refactor-npc #4

Open
xin wants to merge 3 commits from refactor-npc into master
41 changed files with 2997 additions and 903 deletions

View file

@ -5,7 +5,7 @@ jobs:
npc-build:
strategy:
matrix:
package: [ "flow", "flow-simlib"]
package: [ "flow", "flow-simlib" ]
runs-on: nix
defaults:
run:

4
.gitignore vendored
View file

@ -6,3 +6,7 @@ difftest/
**/result
/.pre-commit-config.yaml
**/.vscode/
*.sock
logs/
**/.envrc
**/.gdbinit

File diff suppressed because it is too large Load diff

View file

@ -13,10 +13,23 @@
};
diffu.url = "git+https://git.xinyang.life/xin/diffu.git";
am-kernels.url = "git+https://git.xinyang.life/xin/am-kernels.git?ref=dev";
spike-diff.url = "git+https://git.xinyang.life/xin/spike-diff.git";
};
outputs = { self, flake-utils, nixpkgs, nixpkgs-circt162, pre-commit-hooks, nur-xin, diffu, am-kernels }@inputs:
flake-utils.lib.eachDefaultSystem (system:
outputs =
{
self,
flake-utils,
nixpkgs,
nixpkgs-circt162,
pre-commit-hooks,
nur-xin,
diffu,
am-kernels,
spike-diff,
}@inputs:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs {
inherit system;
@ -39,6 +52,8 @@
};
};
};
am-kernels-nemu = am-kernels.packages.${system}.rv32Cross.am-kernels-nemu;
in
{
checks = {
@ -50,7 +65,10 @@
cmake-format.enable = true;
clang-format = {
enable = true;
types_or = pkgs.lib.mkForce [ "c" "c++" ];
types_or = pkgs.lib.mkForce [
"c"
"c++"
];
};
scalafmt = {
enable = true;
@ -63,13 +81,19 @@
};
};
packages = rec {
packages = {
abstract-machine = pkgs.callPackage ./abstract-machine { isa = "native"; };
nemu = pkgs.callPackage ./nemu { };
nemu-lib = pkgs.callPackage ./nemu { };
nemu = pkgs.callPackage ./nemu { am-kernels = am-kernels-nemu; };
nemu-lib = pkgs.callPackage ./nemu { defconfig = "libdefconfig"; };
rv32Cross = rec {
abstract-machine = rv32CrossConfig.callPackage ./abstract-machine { isa = "riscv"; platform = [ "nemu" "npc" ]; };
rv32Cross = {
abstract-machine = rv32CrossConfig.callPackage ./abstract-machine {
isa = "riscv";
platform = [
"nemu"
"npc"
];
};
};
};
@ -92,7 +116,7 @@
self.packages.${system}.nemu
];
NEMU_HOME = "/home/xin/repo/ysyx-workbench/nemu";
# NEMU_IMAGES_PATH = self.packages.${system}.rv32Cross.am-kernels-nemu + "/share/am-kernels";
NEMU_IMAGES_PATH = am-kernels.packages.${system}.rv32Cross.am-kernels-nemu + "/share/am-kernels";
};
devShells.npc = pkgs.mkShell.override { stdenv = pkgs.ccacheStdenv; } {
@ -125,23 +149,36 @@
verilator
];
buildInputs = with pkgs; [
spdlog
nvboard
openssl
libllvm
libxml2
readline
mini-gdbstub
] ++ self.checks.${system}.pre-commit-check.enabledPackages;
buildInputs =
with pkgs;
[
spdlog
nvboard
openssl
libllvm
libxml2
readline
mini-gdbstub
]
++ self.checks.${system}.pre-commit-check.enabledPackages;
};
devShells.difftest = pkgs.mkShell {
devShells.default = pkgs.mkShell {
inherit (self.checks.${system}.pre-commit-check) shellHook;
buildInputs = self.checks.${system}.pre-commit-check.enabledPackages;
packages = [
diffu.packages.${system}.default
am-kernels.packages.${system}.rv32Cross.am-kernels-npc
self.packages.${system}.nemu-lib
spike-diff.packages.${system}.default
pkgs.gef
pkgs.gtkwave
];
DIFFU_IMAGES_PATH = "${am-kernels.packages.${system}.rv32Cross.am-kernels-npc}";
NEMU_SO = "${self.packages.${system}.nemu-lib}/lib/riscv32-nemu-interpreter-so";
NPC_SO = "/home/xin/repo/ysyx-workbench/npc/build/csrc/Flow/libFlow.so";
};
}
);

View file

@ -1 +1 @@
use flake ".?submodules=1#npc"
use flake

2
npc/.gitignore vendored
View file

@ -17,3 +17,5 @@ hs_err_pid*
compile_commands.json
*.vcd
*.gtkw
*.sock

28
npc/CMakePresets.json Normal file
View file

@ -0,0 +1,28 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 19,
"patch": 0
},
"configurePresets": [
{
"name": "default",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"ENABLE_YSYX_GIT_TRACKER": "ON",
"BUILD_CHISEL_EMIT_TARGET": "ON",
"TOPMODULE": "Flow",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
}
],
"buildPresets": [
{
"name": "default",
"configurePreset": "default"
}
]
}

View file

@ -32,7 +32,9 @@ else()
set(CHISEL_TEST_TARGET sbt_${TOPMODULE}_test)
add_custom_command(
OUTPUT ${CHISEL_OUTPUT_TOPMODULE} ${CHISEL_OUTPUT_VERILATOR_CONF}
COMMAND sbt "run ${CHISEL_EMIT_ARGS}"
# Try to use native sbt to increase performance when possible
COMMAND ${CMAKE_COMMAND} -E env SBT_NATIVE_CLIENT=true sbt
"run ${CHISEL_EMIT_ARGS}"
WORKING_DIRECTORY ${SCALA_CORE}
DEPENDS ${CHISEL_DEPENDENCY}
VERBATIM

View file

@ -1,7 +1,7 @@
ThisBuild / scalaVersion := "2.13.12"
ThisBuild / version := "0.1.0"
ThisBuild / version := "0.1.1"
val chiselVersion = "6.2.0"
val chiselVersion = "6.5.0"
val circeVersion = "0.14.1"
lazy val root = (project in file("."))

View file

@ -1 +1 @@
sbt.version=1.9.9
sbt.version=1.10.1

View file

@ -1,4 +1,4 @@
import "DPI-C" function int pmem_read(input int addr);
import "DPI-C" function int pmem_read(input int addr, input byte rmask);
import "DPI-C" function void pmem_write(input int waddr, input int wdata, input byte wmask);
module RamDpi (
@ -9,6 +9,7 @@ module RamDpi (
input [31:0] writeAddr,
input [31:0] writeData,
input [3:0] writeMask,
input [3:0] readMask,
input reg [31:0] readAddr,
output reg [31:0] readData,
input reg [31:0] pc,
@ -16,7 +17,7 @@ module RamDpi (
);
always @(posedge clock) begin
if (valid) begin // 有读写请求时
readData = pmem_read(readAddr);
readData = pmem_read(readAddr, { 4'h0, readMask });
if (writeEnable) begin // 有写请求时
pmem_write(writeAddr, writeData, { 4'h0, writeMask });
end
@ -25,5 +26,5 @@ module RamDpi (
readData = 32'h80000000;
end
end
assign inst = pmem_read(pc);
assign inst = pmem_read(pc, 8'hF);
endmodule

View file

@ -3,11 +3,14 @@ package flow.components
import chisel3._
import chisel3.util._
import shapeless.{HNil, ::}
import flow.Params
import flow.components.util._
import chisel3.util.experimental.decode.{decoder, TruthTable}
class ALUControlInterface extends Bundle {
object ALUControlInterface {
object OpSelect extends ChiselEnum {
val aOpAdd, aOpSub, aOpNot, aOpAnd, aOpOr, aOpXor, aOpSlt, aOpSltu, aOpSll,
aOpSrl, aOpSra = Value
val aOpAdd, aOpSub, aOpAnd, aOpOr, aOpXor, aOpSlt, aOpSltu, aOpSll, aOpSrl,
aOpSra = Value
}
object SrcASelect extends ChiselEnum {
val aSrcARs1, aSrcAPc, aSrcAZero = Value
@ -15,31 +18,34 @@ class ALUControlInterface extends Bundle {
object SrcBSelect extends ChiselEnum {
val aSrcBRs2, aSrcBImmI, aSrcBImmJ, aSrcBImmS, aSrcBImmU = Value
}
val op = Input(OpSelect())
val srcASelect = Input(SrcASelect())
val srcBSelect = Input(SrcBSelect())
val signExt = Input(Bool())
class ALUControlInterface extends Bundle {
val op = Input(OpSelect())
val srcASelect = Input(SrcASelect())
val srcBSelect = Input(SrcBSelect())
val signExt = Input(Bool())
def ctrlBindPorts = {
op :: srcASelect :: srcBSelect :: signExt :: HNil
def ctrlBindPorts = {
op :: srcASelect :: srcBSelect :: signExt :: HNil
}
}
def apply() = new ALUControlInterface;
}
class ALU[T <: UInt](tpe: T) extends Module {
val control = IO(new ALUControlInterface)
class ALU(implicit p: Params) extends Module {
import ALUControlInterface._
val control = IO(ALUControlInterface())
val in = IO(new Bundle {
val a = Input(Vec(control.SrcASelect.all.length, tpe))
val b = Input(Vec(control.SrcBSelect.all.length, tpe))
val a = Input(Vec(SrcASelect.all.length, UInt(p.XLEN)))
val b = Input(Vec(SrcBSelect.all.length, UInt(p.XLEN)))
})
val out = IO(new Bundle {
val eq = Output(Bool())
val result = Output(tpe)
val result = Output(UInt(p.XLEN))
})
val a = in.a(control.srcASelect.asUInt)
val b = in.b(control.srcBSelect.asUInt)
// val adder_b = (Fill(tpe.getWidth, io.op(0)) ^ io.b) + io.op(0) // take (-b) if sub
val add = a + b
val sub = a - b
val and = a & b
@ -48,18 +54,16 @@ class ALU[T <: UInt](tpe: T) extends Module {
val xor = a ^ b
val slt = a.asSInt < b.asSInt
val sltu = a < b
val sll = a << b(log2Ceil(tpe.getWidth), 0)
val srl = a >> b(log2Ceil(tpe.getWidth), 0)
val sra = a.asSInt >> b(log2Ceil(tpe.getWidth), 0)
out.eq := a === b
val sll = a << b(5, 0)
val srl = a >> b(5, 0)
val sra = a.asSInt >> b(5, 0)
import control.OpSelect._
import OpSelect._
out.result := MuxLookup(control.op, 0.U)(
Seq(
aOpAdd -> add,
aOpSub -> sub,
aOpNot -> not,
aOpAnd -> and,
aOpOr -> or,
aOpXor -> xor,
@ -72,8 +76,92 @@ class ALU[T <: UInt](tpe: T) extends Module {
)
}
object ALU {
def apply[T <: UInt](tpe: T): ALU[T] = {
Module(new ALU(tpe))
}
class AluController(implicit p: Params) extends Module {
val out = IO(Flipped(ALUControlInterface()))
val in = IO(new Bundle {
val inst = Input(UInt(p.XLEN))
})
import flow.components.RV32Inst._
import ALUControlInterface._
// OpSelect
// format: off
val aOpAdd = Array(
lui, auipc,
jal, jalr,
lb, lbu, lh, lhu, lw,
sb, sh, sw,
addi, add
).map(_ -> OpSelect.aOpAdd.BP)
// format: on
val aOpAnd = Array(and, andi).map(_ -> OpSelect.aOpAnd.BP)
val aOpSub = Array(sub).map(_ -> OpSelect.aOpSub.BP)
val aOpOr = Array(ori, or).map(_ -> OpSelect.aOpOr.BP)
val aOpXor = Array(xori, xor).map(_ -> OpSelect.aOpXor.BP)
val aOpSlt = Array(beq, bne, blt, bge, slti, slt).map(_ -> OpSelect.aOpSlt.BP)
val aOpSltu = Array(sltiu, sltu, bltu, bgeu).map(_ -> OpSelect.aOpSltu.BP)
val aOpSll = Array(slli, sll).map(_ -> OpSelect.aOpSll.BP)
val aOpSrl = Array(srli, srl).map(_ -> OpSelect.aOpSrl.BP)
val aOpSra = Array(
sra,
srai
).map(_ -> OpSelect.aOpSra.BP)
val OpCodeMapping = TruthTable(
aOpAdd ++ aOpAnd ++ aOpSub ++ aOpOr ++ aOpXor ++ aOpSlt ++ aOpSltu ++ aOpSll ++ aOpSrl ++ aOpSra,
BitPat.dontCare(OpSelect.getWidth)
)
val aSrcAZero = Array(
lui
).map(_ -> SrcASelect.aSrcAZero.BP)
val aSrcAPc = Array(
auipc,
jal
).map(_ -> SrcASelect.aSrcAPc.BP)
val srcAMapping = TruthTable(
aSrcAZero ++ aSrcAPc,
SrcASelect.aSrcARs1.BP
)
val aSrcBImmS = Array(sb, sh, sw).map(_ -> SrcBSelect.aSrcBImmS.BP)
val aSrcBImmU = Array(lui, auipc).map(_ -> SrcBSelect.aSrcBImmU.BP)
val aSrcBImmJ = Array(jal).map(_ -> SrcBSelect.aSrcBImmJ.BP)
// format: off
val aSrcBImmI = Array(
jalr,
lb, lbu, lh, lhu, lw,
addi, slti, sltiu, xori, ori, andi, slli, srli, srai
).map(_ -> SrcBSelect.aSrcBImmI.BP)
// format: on
val srcBMapping = TruthTable(
aSrcBImmI ++ aSrcBImmJ ++ aSrcBImmU ++ aSrcBImmS,
SrcBSelect.aSrcBRs2.BP
)
val doSignExt = Array(blt, bge, slt, slti).map(_ -> BitPat(true.B))
val noSignExt = Array(lui, auipc, jal, jalr, bltu, bgeu, sltiu, sltu).map(
_ -> BitPat(false.B)
)
val signExtMapping = TruthTable(doSignExt ++ noSignExt, BitPat.dontCare(1))
out.op := OpSelect.safe(decoder(in.inst, OpCodeMapping))._1
out.srcASelect := SrcASelect.safe(decoder(in.inst, srcAMapping))._1
out.srcBSelect := SrcBSelect.safe(decoder(in.inst, srcBMapping))._1
out.signExt := decoder(in.inst, signExtMapping)
}

View file

@ -11,25 +11,28 @@ import flow.Params
import chisel3.util.BitPat
import chisel3.util.Fill
class CSRControlInterface extends Bundle {
object CSRControlInterface extends Bundle {
object csrRead extends ChiselEnum {
val csrReadDisabled, csrReadEnabled = Value
}
object csrWrite extends ChiselEnum {
val csrWriteDisabled, csrWriteEnabled = Value
val csrWriteDisabled, csrWriteData, csrSetBit, csrClearBit = Value
}
val readEnable = Input(csrRead())
val writeEnable = Input(csrWrite())
class CSRControlInterface extends Bundle {
val readEnable = Input(csrRead())
val writeEnable = Input(csrWrite())
def ctrlBindPorts = {
readEnable :: writeEnable :: HNil
def ctrlBindPorts = {
readEnable :: writeEnable :: HNil
}
}
def apply() = new CSRControlInterface
}
class CSRCore(implicit val p: Params) extends Module {
val control = IO(new CSRControlInterface)
val control = IO(CSRControlInterface())
val in = IO(new Bundle {
val csrAddr = Input(UInt(p.csrAddrWidth))

View file

@ -5,38 +5,81 @@ import chisel3.experimental.noPrefix
import chisel3.util.HasBlackBoxPath
import chisel3.util.HasBlackBoxResource
import chisel3.util.log2Ceil
import chisel3.util.BitPat
import flow.components
import flow.Params
import shapeless.::
import shapeless.HNil
import scala.collection.SeqMap
import chisel3.util.experimental.decode.{decoder, TruthTable}
import flow.components.util._
import flow.components.RV32Inst._
class RamControlInterface(addrWidth: Int) extends Bundle {
val valid = Input(Bool())
val writeMask = Input(UInt((addrWidth / 8).W))
val writeEnable = Input(Bool())
def ctrlBindPorts = {
valid :: writeMask :: writeEnable :: HNil
}
}
/* FIXME: Extends here might not be the best solution.
* We need a way to merge two bundles together
*/
class RamInterface[T <: Data](tpe: T, addrWidth: Int)
extends RamControlInterface(addrWidth) {
val clock = Input(Clock())
val reset = Input(Reset())
val writeAddr = Input(UInt(addrWidth.W))
val writeData = Input(tpe)
val readAddr = Input(UInt(addrWidth.W))
val readData = Output(tpe)
val pc = Input(UInt(addrWidth.W))
val inst = Output(tpe)
}
class RamDpi extends BlackBox with HasBlackBoxResource {
val io = IO((new RamInterface(UInt(32.W), 32)))
class RamDpi(implicit p: Params) extends BlackBox with HasBlackBoxResource {
val io = IO(new DpiRamInterface)
addResource("/RamDpi.v")
}
class DpiRamControlInterface(implicit p: Params) extends Bundle {
val valid = Input(Bool())
val readMask = Input(UInt((p.XLEN.get / 8).W))
val writeMask = Input(UInt((p.XLEN.get / 8).W))
val writeEnable = Input(Bool())
}
class DpiRamInterface(implicit p: Params) extends DpiRamControlInterface {
val clock = Input(Clock())
val reset = Input(Reset())
// Data
val writeAddr = Input(UInt(p.XLEN))
val writeData = Input(UInt(p.XLEN))
val readAddr = Input(UInt(p.XLEN))
val readData = Output(UInt(p.XLEN))
val pc = Input(UInt(p.XLEN))
val inst = Output(UInt(p.XLEN))
}
class RamController(implicit p: Params) extends Module {
val in = new Bundle {
val inst = IO(Flipped(UInt(p.XLEN)))
}
val out = IO(Flipped(new DpiRamControlInterface))
private val validMapping = TruthTable(
Array(sb, sh, sw, lb, lbu, lh, lhu, lw).map(_ -> BitPat("b1")),
BitPat("b0")
)
private val writeMaskMapping = TruthTable(
Array(
sb -> BitPat("b0001"),
sh -> BitPat("b0011"),
sw -> BitPat("b1111")
),
BitPat.dontCare(out.writeMask.getWidth)
)
private val readMaskMapping = TruthTable(
Array(
lb -> BitPat("b0001"),
lh -> BitPat("b0011"),
lw -> BitPat("b1111")
),
BitPat.dontCare(out.readMask.getWidth)
)
private val writeEnableMapping = TruthTable(
Array(
sb -> BitPat("b1"),
sh -> BitPat("b1"),
sw -> BitPat("b1")
),
BitPat("b0")
)
out.valid := decoder(in.inst, validMapping)
out.writeEnable := decoder(in.inst, writeEnableMapping)
out.writeMask := decoder(in.inst, writeMaskMapping)
out.readMask := decoder(in.inst, readMaskMapping)
}

View file

@ -1,48 +1,76 @@
package flow.components
import chisel3._
import chisel3.util.experimental.decode.{decoder, TruthTable}
import chisel3.util.{Valid, log2Ceil}
import chisel3.util.MuxLookup
import chisel3.util.BitPat
import shapeless.{HNil, ::}
import shapeless.HList
import flow.Params
import RV32InstSubfields._
import flow.components.util._
class PcControlInterface extends Bundle {
object PcControlInterface {
object SrcSelect extends ChiselEnum {
val pStaticNpc, pExeOut = Value
val pStatic, pJmp, pBR = Value
}
val useImmB = Input(Bool())
val srcSelect = Input(SrcSelect())
class newPcControlInterface extends Bundle {
val srcSelect = Input(SrcSelect())
def ctrlBindPorts = {
useImmB :: srcSelect :: HNil
def ctrlBindPorts = {
srcSelect :: HNil
}
}
def apply() = new newPcControlInterface;
}
class ProgramCounter[T <: UInt](tpe: T) extends Module {
val control = IO(new PcControlInterface)
class ProgramCounter(implicit p: Params) extends Module {
val control = IO(PcControlInterface())
import PcControlInterface.SrcSelect._
val in = IO(new Bundle {
val immB = Input(tpe)
val exeOut = Input(tpe)
val brOffset = Input(UInt(p.XLEN))
val jAddr = Input(UInt(p.XLEN))
})
val out = IO(Output(tpe))
val out = IO(Output(UInt(p.XLEN)))
private val pc_reg = RegInit(0x80000000L.U)
private val pcReg = RegInit(p.resetVector.U)
out := pcReg
// pc := in.pcSrcs(control.srcSelect.asUInt)
import control.SrcSelect._
when(control.useImmB === true.B) {
pc_reg := pc_reg + in.immB
}.elsewhen(control.srcSelect === pStaticNpc) {
pc_reg := pc_reg + 4.U
}.elsewhen(control.srcSelect === pExeOut) {
pc_reg := in.exeOut
}
out := pc_reg
private val npc = MuxLookup(control.srcSelect, 4.U)(
Seq(
pStatic -> (pcReg + 4.U),
pJmp -> in.jAddr,
pBR -> (pcReg + in.brOffset)
)
)
pcReg := npc
}
object ProgramCounter {
def apply[T <: UInt](tpe: T): ProgramCounter[T] = {
val pc = Module(new ProgramCounter(tpe))
pc
}
class PcController(implicit p: Params) extends Module {
val in = IO(new Bundle {
val inst = Input(UInt(p.instWidth))
})
val out = IO(Flipped(PcControlInterface()))
import RV32Inst._
import PcControlInterface.SrcSelect._
private val _jmpMapping = Array(jal, jalr).map(_ -> pJmp.BP)
private val _brMapping =
Array(beq, bne, blt, bge, bltu, bgeu).map(_ -> pBR.BP)
val mapping = TruthTable(
_jmpMapping ++ _brMapping,
pStatic.BP
)
out.srcSelect := PcControlInterface.SrcSelect
.safe(
(
decoder(in.inst, mapping)
)
)
._1
}

View file

@ -4,53 +4,109 @@ import chisel3._
import chisel3.util.log2Ceil
import chisel3.util.UIntToOH
import chisel3.util.MuxLookup
import chisel3.util.BitPat
import chisel3.util.experimental.decode.{decoder, TruthTable}
import chisel3.experimental.Trace._
import shapeless.{HList, HNil, ::}
class RegControl extends Bundle {
import flow.Params
import flow.components.util._
import flow.components.RV32Inst._
object RegControl {
object WriteSelect extends ChiselEnum {
val rAluOut, rMemOut, rNpc = Value
}
class RegControl extends Bundle {
val writeEnable = Input(Bool())
val writeSelect = Input(WriteSelect())
val writeEnable = Input(Bool())
val writeSelect = Input(WriteSelect())
def ctrlBindPorts = {
writeEnable :: writeSelect :: HNil
def ctrlBindPorts = {
writeEnable :: writeSelect :: HNil
}
}
traceName(writeEnable)
def apply() = new RegControl
}
class RegisterFile[T <: Data](tpe: T, regCount: Int, numReadPorts: Int)
extends Module {
require(numReadPorts >= 0)
val control = IO(new RegControl)
val dataAddrWidth = log2Ceil(regCount).W
class RegisterFile(implicit p: Params) extends Module {
val in = IO(new Bundle {
val writeAddr = Input(UInt(dataAddrWidth))
val writeData = Input(Vec(control.WriteSelect.all.length, tpe))
val rs = Input(Vec(numReadPorts, UInt(dataAddrWidth)))
val rd = Input(UInt(p.regsAddrWidth))
val writeData = Input(Vec(RegControl.WriteSelect.all.length, UInt(p.XLEN)))
val rs1 = Input(UInt(p.regsAddrWidth))
val rs2 = Input(UInt(p.regsAddrWidth))
})
val out = IO(new Bundle {
val src = Output(Vec(numReadPorts, tpe))
val src1 = Output(UInt(p.XLEN))
val src2 = Output(UInt(p.XLEN))
})
val regResetValue = 0.U(tpe.getWidth.W)
val regFile = RegInit(VecInit(Seq.fill(regCount)(regResetValue)))
val writeAddrOH = UIntToOH(in.writeAddr)
val control = IO(RegControl())
for ((reg, i) <- regFile.zipWithIndex.tail) {
reg := Mux(
writeAddrOH(i.U(log2Ceil(regCount).W)) && control.writeEnable,
in.writeData(control.writeSelect.asUInt),
reg
)
}
val regFile = RegInit(
VecInit(Seq.fill(p.regsCount)(p.regsResetValue.U(p.XLEN)))
)
// 0.U -> writeData(0), ...
val writeDataMux =
MuxBindCtrlSrc(control.writeSelect, in.writeData, regFile(in.rd))
regFile(in.rd) := Mux(
control.writeEnable,
writeDataMux,
regFile(in.rd)
)
regFile(0) := 0.U
for (port <- 0 until numReadPorts) {
out.src(port) := regFile(in.rs(port).asUInt)
}
out.src1 := regFile(in.rs1)
out.src2 := regFile(in.rs2)
traceName(regFile)
dontTouch(regFile)
}
class RegisterFileController(implicit p: Params) extends Module {
val in = IO(new Bundle {
val inst = Input(UInt(p.XLEN))
})
val out = IO(Flipped(RegControl()))
// format: off
import RegControl._
private val aluOutMapping =
Array(
lui, auipc,
addi, slti, sltiu, xori, ori, andi, slli, srli, srai,
add, sub, sll, slt, sltu, xor, srl, sra, or, and,
).map(_ -> WriteSelect.rAluOut.BP)
private val memOutMapping =
Array(
lb, lbu, lh, lhu, lw
).map(_ -> WriteSelect.rMemOut.BP)
private val npcMapping =
Array(
jal, jalr
).map(_ -> WriteSelect.rNpc.BP)
// format: on
private val writeSelectMapping = TruthTable(
aluOutMapping ++ memOutMapping ++ npcMapping,
BitPat.dontCare(out.writeSelect.getWidth)
)
// enable write if instruction belongs to any mapping above
private val writeEnableMapping = TruthTable(
(aluOutMapping ++ memOutMapping ++ npcMapping).map(x =>
(x._1 -> BitPat("b1"))
),
BitPat("b0")
)
out.writeSelect := RegControl.WriteSelect
.safe(
decoder(in.inst, writeSelectMapping)
)
._1
out.writeEnable := decoder(in.inst, writeEnableMapping)
}

View file

@ -0,0 +1,28 @@
package flow.components
import chisel3._
import chisel3.util.BitPat
import chisel3.util.MuxLookup
object util {
implicit class enumToUInt[T <: EnumType](e: T) {
def U = {
e.asUInt
}
def BP = {
BitPat(e.litValue.toInt.U(e.getWidth.W))
}
}
import scala.language.implicitConversions
implicit def chiselEnumAsInt[T <: EnumType](e: T): Int = {
e.litValue.toInt
}
object MuxBindCtrlSrc {
def apply[E <: EnumType, T <: Data](ctrl: E, src: Vec[T], default: T) = {
val map = (0 until src.length).map(_.U).zip(src)
MuxLookup(ctrl.U, default)(map)
}
}
}

View file

@ -0,0 +1,74 @@
package flow.stages
import chisel3._
import flow.Params
import chisel3.util.Decoupled
import chisel3.util.DecoupledIO
import chisel3.util.ReadyValidIO
class DecoupledMsgIO[Tin <: Data, Tout <: Data](
_in: => Option[DecoupledIO[Tin]],
_out: => Option[DecoupledIO[Tout]]
)(implicit
p: Params
) extends Bundle {
implicit class optionWithGetThrow[T](o: Option[T]) {
def getOrThrow[E <: Exception](e: E): T = o match {
case Some(x) => x
case None => throw e
}
}
lazy val in = _in.getOrThrow(new Exception(s"No input port at $this"))
lazy val out = _out.getOrThrow(new Exception(s"No output port at $this"))
def connect[Tout_ <: Data](
other: DecoupledMsgIO[Tout, Tout_]
) = {
if (p.arch == "single") {
// out.ready := DontCare
// out.valid := DontCare
// other.in.valid := DontCare
// other.in.ready := DontCare
other.in.bits := out.bits
} else if (p.arch == "multi") {
out :<>= other.in
} else {
throw new Exception("Unknown architecture")
}
other
}
}
object DecoupledMsgIO {
import scala.language.implicitConversions
/** Wrap input and output messages in ReadValidIO
*/
def apply[Tin <: Data, Tout <: Data](
in: Option[Tin] = None,
out: Option[Tout] = None,
isIO: Boolean = true
)(implicit
p: Params
) = {
val _in = in match {
case Some(d) =>
if (isIO) Some(Flipped(Decoupled(d)))
else Some(Wire(Flipped(Decoupled(d))))
case None => None
}
val _out = out match {
case Some(d) =>
if (isIO) Some(Decoupled(d)) else Some(Decoupled(d))
case None => None
}
new DecoupledMsgIO(_in, _out)
}
}
object utils {
implicit class dataWrapBySome[T <: Data](d: T) {
def S = Some(d)
}
}

View file

@ -0,0 +1,75 @@
package flow.stages
import chisel3._
import chisel3.util.Decoupled
import chisel3.util.DecoupledIO
import flow.Params
import flow.stages.utils._
import flow.components.ALU
import flow.components.ALUControlInterface
import flow.stages.messages._
import flow.components.RV32InstSubfields._
class EX(implicit val p: Params) extends Module {
// val msgio = IO(DecoupledMsgIO((new ID2EX).S, (new EX2LS).S))
val msgio = IO(new Bundle {
val in = Flipped(DecoupledIO(new ID2EX))
val out = DecoupledIO(new EX2LS)
})
msgio.in.ready := DontCare
msgio.out.valid := DontCare
val io = IO(new Bundle {
val toIF = Output(new EX2IF)
})
private val _toIF = io.toIF
private val _in = msgio.in.bits
private val _out = msgio.out.bits
val alu = Module(new ALU)
alu.control := _in.aluCtrl;
{
import flow.components.ALUControlInterface.SrcASelect._
import flow.components.util.chiselEnumAsInt
alu.in.a(aSrcARs1) := _in.src1
alu.in.a(aSrcAPc) := _in.pc
alu.in.a(aSrcAZero) := 0.U
import flow.components.ALUControlInterface.SrcBSelect._
alu.in.b(aSrcBRs2) := _in.src2
alu.in.b(aSrcBImmI) := _in.inst.immI
alu.in.b(aSrcBImmJ) := _in.inst.immJ
alu.in.b(aSrcBImmU) := _in.inst.immU
alu.in.b(aSrcBImmS) := _in.inst.immS
}
_out.exeOut := alu.out.result
_toIF.jAddr := alu.out.result
_toIF.brOffset := _in.inst.immB
_toIF.pc := _in.pc
import flow.components.PcControlInterface.SrcSelect._
val regSrcEq = Wire(Bool());
regSrcEq := (_in.src1 === _in.src2);
when(_in.pcCtrl.srcSelect === pBR) {
val branchUseSlt = _in.inst(14)
val branchInvertResult = _in.inst(12)
val _branchResult =
Mux(branchUseSlt, alu.out.result(0), regSrcEq)
val branchResult = Mux(branchInvertResult, !_branchResult, _branchResult)
_toIF.pcCtrl.srcSelect := Mux(branchResult, pBR, pStatic)
}.otherwise {
_toIF.pcCtrl.srcSelect := _in.pcCtrl.srcSelect
}
_out.ramCtrl := _in.ramCtrl
_out.pc := _in.pc
_out.rd := _in.inst.rd
_out.src1 := _in.src1
_out.src2 := _in.src2
_out.regCtrl := _in.regCtrl
}

View file

@ -0,0 +1,64 @@
package flow.stages
import chisel3._
import flow.Params
import flow.stages.utils._
import flow.stages.messages._
import flow.components._
import flow.components.RV32InstSubfields._
import chisel3.util.DecoupledIO
import chisel3.experimental.Trace._
import chisel3.util.experimental.InlineInstance
class ID(implicit val p: Params) extends Module {
val io = IO(new Bundle {
val fromWB = Input(new WB2ID)
})
// val msgio = IO(DecoupledMsgIO((new IF2ID).S, (new ID2EX).S))
val msgio = IO(new Bundle {
val in = Flipped(DecoupledIO(new IF2ID))
val out = DecoupledIO(new ID2EX)
})
msgio.in.ready := DontCare
msgio.out.valid := DontCare
val _in = msgio.in.bits
val _out = msgio.out.bits
val _fromWB = io.fromWB
val regs = Module(new RegisterFile with InlineInstance)
// Controllers
val pcController = Module(new PcController)
val regFileController = Module(new RegisterFileController)
val aluController = Module(new AluController)
val ramController = Module(new RamController)
pcController.in.inst := _in.inst
regFileController.in.inst := _in.inst
aluController.in.inst := _in.inst
ramController.in.inst := _in.inst
regs.in.rs1 := _in.inst.rs1
regs.in.rs2 := _in.inst.rs2
_out.src1 := regs.out.src1
_out.src2 := regs.out.src2
_out.pc := _in.pc
_out.pcCtrl := pcController.out
_out.regCtrl := regFileController.out
_out.aluCtrl := aluController.out
_out.ramCtrl := ramController.out
_out.inst := _in.inst
// register file write back
import flow.components.RegControl.WriteSelect._
import flow.components.util.chiselEnumAsInt
regs.control := _fromWB.regCtrl
regs.in.rd := _fromWB.rd
regs.in.writeData(rAluOut) := _fromWB.exeOut
regs.in.writeData(rMemOut) := _fromWB.memOut
regs.in.writeData(rNpc) := _fromWB.npc
}

View file

@ -0,0 +1,46 @@
package flow.stages
import chisel3._
import flow.Params
import flow.components.ProgramCounter
import flow.components.PcControlInterface
import flow.stages.utils._
import flow.stages.messages._
import chisel3.util.DecoupledIO
class IF(implicit val p: Params) extends Module {
import flow.components.RV32InstSubfields._
// Use DPI-C for instant instruction fetch for now
val io = IO(new Bundle {
val fromRam = Input(new Ram2IF)
val toRam = Output(new IF2Ram)
val fromEx = Input(new EX2IF)
})
val msgio = IO(new Bundle {
val out = DecoupledIO(new IF2ID)
})
msgio.out.valid := DontCare
// val msgio = IO(DecoupledMsgIO(out = (new IF2ID).S))
val _out = msgio.out.bits
val _fromRam = io.fromRam
val _toRam = io.toRam
val _fromEx = io.fromEx
// Program Counter
private val pc = Module(new ProgramCounter)
// PC update
pc.in.brOffset := _fromEx.brOffset
pc.in.jAddr := _fromEx.jAddr
pc.control := _fromEx.pcCtrl
// Instruction fetch
_toRam.pc := pc.out
_out.inst := _fromRam.inst
_out.pc := pc.out
}

View file

@ -0,0 +1,53 @@
package flow.stages
import chisel3._
import chisel3.util.Decoupled
import chisel3.util.DecoupledIO
import flow.Params
import flow.stages.utils._
import flow.stages.messages._
import flow.components.RamDpi
class LS(implicit val p: Params) extends Module {
val io = IO(new Bundle {
val toIF = Output(new Ram2IF)
val fromIF = Input(new IF2Ram)
})
// val msgio = IO(DecoupledMsgIO((new EX2LS).S, (new LS2WB).S))
val msgio = IO(new Bundle {
val in = Flipped(DecoupledIO(new EX2LS))
val out = DecoupledIO(new LS2WB)
})
msgio.in.ready := DontCare
msgio.out.valid := DontCare
private val _in = msgio.in.bits
private val _out = msgio.out.bits
private val _toIF = io.toIF
private val _fromIF = io.fromIF
val ram = Module(new RamDpi)
ram.io.clock := clock
ram.io.reset := reset
ram.io.writeAddr := _in.exeOut
ram.io.writeData := _in.src2
ram.io.writeMask := _in.ramCtrl.writeMask
ram.io.readMask := _in.ramCtrl.readMask
ram.io.writeEnable := _in.ramCtrl.writeEnable
ram.io.valid := _in.ramCtrl.valid // TODO: change to a better name
ram.io.readAddr := _in.exeOut
// TODO: Change to icache, and move to IF stage.
// Change to arbitor here
ram.io.pc := _fromIF.pc
_toIF.inst := ram.io.inst
_out.memOut := ram.io.readData
_out.exeOut := _in.exeOut
// passthrough
_out.regCtrl := _in.regCtrl
_out.rd := _in.rd
_out.pc := _in.pc
}

View file

@ -0,0 +1,73 @@
package flow.stages.messages
import chisel3._
import flow.Params
import flow.components._
class IF2ID(implicit p: Params) extends Bundle {
val inst = UInt(p.instWidth)
val pc = UInt(p.XLEN)
}
class ID2EX(implicit p: Params) extends Bundle {
// Data
val pc = UInt(p.XLEN)
val inst = UInt(p.XLEN)
val src1 = UInt(p.XLEN)
val src2 = UInt(p.XLEN)
// Control
val aluCtrl = Flipped(ALUControlInterface())
val ramCtrl = Flipped(new DpiRamControlInterface)
val pcCtrl = Flipped(PcControlInterface())
val regCtrl = Flipped(RegControl())
}
class EX2LS(implicit p: Params) extends Bundle {
val pc = UInt(p.XLEN)
val rd = UInt(p.regsAddrWidth)
val src1 = UInt(p.XLEN)
val src2 = UInt(p.XLEN)
val exeOut = UInt(p.XLEN)
// Control
val ramCtrl = Flipped(new DpiRamControlInterface)
val regCtrl = Flipped(RegControl())
}
class LS2WB(implicit p: Params) extends Bundle {
val pc = UInt(p.XLEN)
val rd = UInt(p.regsAddrWidth)
val memOut = UInt(p.XLEN)
val exeOut = UInt(p.XLEN)
val regCtrl = Flipped(RegControl())
}
class EX2IF(implicit p: Params) extends Bundle {
val pc = UInt(p.XLEN)
val brOffset = UInt(p.XLEN)
val jAddr = UInt(p.XLEN)
// Control
val pcCtrl = Flipped(PcControlInterface())
}
class IF2Ram(implicit p: Params) extends Bundle {
val pc = UInt(p.XLEN)
}
class Ram2IF(implicit p: Params) extends Bundle {
val inst = UInt(p.XLEN)
}
/** Commit result back to registers
*/
class WB2ID(implicit p: Params) extends Bundle {
val regCtrl = Flipped(RegControl())
val exeOut = UInt(p.XLEN)
val memOut = UInt(p.XLEN)
val rd = UInt(p.regsAddrWidth)
val npc = UInt(p.XLEN)
}

View file

@ -0,0 +1,32 @@
package flow.stages
import chisel3._
import chisel3.util.Decoupled
import chisel3.util.DecoupledIO
import flow.Params
import flow.stages.utils._
import flow.stages.messages._
import chisel3.experimental.Trace._
class WB(implicit val p: Params) extends Module {
// val msgio = IO(DecoupledMsgIO(in = (new LS2WB).S))
val io = IO(new Bundle {
val toID = Output(new WB2ID)
})
val msgio = IO(new Bundle {
val in = Flipped(DecoupledIO(new LS2WB))
})
msgio.in.ready := DontCare
private val _in = msgio.in.bits
private val _toID = io.toID
_toID.exeOut := _in.exeOut;
_toID.memOut := _in.memOut;
_toID.regCtrl := _in.regCtrl;
_toID.rd := _in.rd
_toID.npc := _in.pc + 4.U
traceName(_in.pc)
traceName(_in.rd)
}

View file

@ -18,6 +18,20 @@ import io.circe.generic.JsonCodec
import chisel3._
case class Params(
XLEN: Width,
instWidth: Width = 32.W,
regsCount: Int = 32,
regsAddrWidth: Width = 5.W,
regsResetValue: BigInt = 0L,
arch: String,
csrAddrWidth: Width = 12.W
csrAddrWidth: Width = 12.W,
resetVector: BigInt = BigInt(0x80000000L),
csrNameToAddr: Map[String, Int] = Map(
"mstatus" -> 0x300,
"mtvec" -> 0x305,
"mie" -> 0x304,
"mepc" -> 0x341,
"mcause" -> 0x342,
"mtval" -> 0x343,
"mip" -> 0x344
)
)

View file

@ -13,454 +13,35 @@ import chisel3.util.{BinaryMemoryFile, HexMemoryFile}
import chisel3.experimental.Trace
import scala.collection.IndexedSeqView
import shapeless.Poly1
import flow.components.RamControlInterface
import flow.components.DpiRamControlInterface
import flow.components.RV32Inst
import flow.components.RV32InstSubfields._
import flow.components.util._
import flow.components.{RegControl, PcControlInterface, ALUControlInterface}
class Control(width: Int) extends RawModule {
// Helpers
class WrapList[T](vl: T) { type Type = T; val v = vl }
object wrap extends Poly1 {
implicit def default[A] = at[A](Right(_).withLeft[Int])
}
def lit(x: Element) = { x.litValue.toInt }
def toBits(t: dst.Type): BitPat = {
val list = t.toList
list
.map(e =>
e match {
case Right(x) => BitPat(lit(x).U(x.getWidth.W))
case Left(x) => BitPat.dontCare(x)
}
)
.reduceLeft(_ ## _)
}
val r = Right
def l[T <: Any](x: T) = x match {
case x: ChiselEnum => Left(log2Ceil(x.all.length))
case x: Data => Left(x.getWidth)
case _ => throw new IllegalArgumentException
}
val inst = IO(Input(UInt(width.W)))
import flow.stages._
import chisel3.experimental.annotate
import chisel3.experimental.ChiselAnnotation
val reg = IO(Flipped(new RegControl))
val pc = IO(Flipped(new PcControlInterface))
val alu = IO(Flipped(new ALUControlInterface))
val ram = IO(Flipped(new RamControlInterface(32)))
import chisel3.Data
import chisel3.experimental.{annotate, requireIsAnnotatable, ChiselAnnotation}
val dst = new WrapList(
(reg.ctrlBindPorts ++
pc.ctrlBindPorts ++
alu.ctrlBindPorts ++
ram.ctrlBindPorts).map(wrap)
)
val dstList = dst.v.toList
val controlWidth = dstList.map(_.toOption.get.getWidth).reduce(_ + _)
val reversePrefixSum = dstList.scanLeft(0)(_ + _.toOption.get.getWidth)
val sliceIndex = reversePrefixSum.map(controlWidth - _)
val slices = sliceIndex.map(_ - 1).zip(sliceIndex.tail)
import reg.WriteSelect._
import reg._
import pc.SrcSelect._
import pc._
import alu.OpSelect._
import alu.SrcASelect._
import alu.SrcBSelect._
import pc._
import RV32Inst._
// format: off
val ControlMapping: Array[(BitPat, dst.Type)] = Array(
// Regs | writeEnable :: writeSelect :: HNil
// PC | useImmB :: srcSelect :: HNil
// Exe | op :: srcASelect :: srcBSelect :: signExt :: HNil
// Mem | valid :: writeMask :: writeEnable :: HNil
(lui , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc)::
r(aOpAdd) :: r(aSrcAZero) :: r(aSrcBImmU) :: r(false.B) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(auipc , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc)::
r(aOpAdd) :: r(aSrcAPc) :: r(aSrcBImmU) :: r(false.B) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
// ---- Control Transfer Instructions ----
(jal , (
r(true.B) :: r(rNpc) ::
r(false.B) :: r(pExeOut) ::
r(aOpAdd) :: r(aSrcAPc) :: r(aSrcBImmJ) :: r(false.B) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(jalr , (
r(true.B) :: r(rNpc) ::
r(false.B) :: r(pExeOut) ::
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: r(false.B) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(beq , (
r(false.B) :: l(WriteSelect) ::
r(true.B) :: r(pStaticNpc) ::
r(aOpSlt) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(bne , (
r(false.B) :: l(WriteSelect) ::
r(true.B) :: r(pStaticNpc) ::
r(aOpSlt) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(blt , (
r(false.B) :: l(WriteSelect) ::
r(true.B) :: r(pStaticNpc) ::
r(aOpSlt) :: r(aSrcARs1) :: r(aSrcBRs2) :: r(true.B) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(bge , (
r(false.B) :: l(WriteSelect) ::
r(true.B) :: r(pStaticNpc) ::
r(aOpSlt) :: r(aSrcARs1) :: r(aSrcBRs2) :: r(true.B) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(bltu , (
r(false.B) :: l(WriteSelect)::
r(true.B) :: r(pStaticNpc) ::
r(aOpSltu) :: r(aSrcARs1) :: r(aSrcBRs2) :: r(false.B) ::
r(false.B) :: l(UInt(4.W)) :: r(false.B) :: HNil
)),
(bgeu , (
r(false.B) :: l(WriteSelect)::
r(true.B) :: r(pStaticNpc) ::
r(aOpSltu) :: r(aSrcARs1) :: r(aSrcBRs2) :: r(false.B) ::
r(false.B) :: l(UInt(4.W)) :: r(false.B) :: HNil
)),
// ---- Memory Access Instructions ----
(lb , (
r(true.B) :: r(rMemOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
r(true.B) :: r(1.U(4.W)) :: r(false.B) :: HNil
)),
(lbu , (
r(true.B) :: r(rMemOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
r(true.B) :: r(0.U(4.W)) :: r(false.B) :: HNil
)),
(lh , (
r(true.B) :: r(rMemOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
r(true.B) :: r(3.U(4.W)) :: r(false.B) :: HNil
)),
(lhu , (
r(true.B) :: r(rMemOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
r(true.B) :: r(2.U(4.W)) :: r(false.B) :: HNil
)),
(lw , (
r(true.B) :: r(rMemOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
r(true.B) :: r(14.U(4.W)) :: r(false.B) :: HNil
)),
(sb , (
r(false.B) :: l(WriteSelect)::
r(false.B) :: r(pStaticNpc) ::
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmS) :: l(Bool()) ::
r(true.B) :: r(1.U(4.W)) :: r(true.B) :: HNil
)),
(sh , (
r(false.B) :: l(WriteSelect)::
r(false.B) :: r(pStaticNpc) ::
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmS) :: l(Bool()) ::
r(true.B) :: r(3.U(4.W)) :: r(true.B) :: HNil
)),
(sw , (
r(false.B) :: l(WriteSelect)::
r(false.B) :: r(pStaticNpc) ::
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmS) :: l(Bool()) ::
r(true.B) :: r(15.U(4.W)) :: r(true.B) :: HNil
)),
// ---- Integer Computational Instructions ---
(addi , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(slti , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpSlt) :: r(aSrcARs1) :: r(aSrcBImmI) :: r(true.B) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(sltiu , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpSltu) :: r(aSrcARs1) :: r(aSrcBImmI) :: r(false.B) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(xori , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpXor) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(ori , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpOr) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(andi , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpAnd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(slli , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpSll) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(srli , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpSrl) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(srai , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpSra) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(add , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(sub , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpSub) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(sll , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpSll) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(slt , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpSlt) :: r(aSrcARs1) :: r(aSrcBRs2) :: r(true.B) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(sltu , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpSltu) :: r(aSrcARs1) :: r(aSrcBRs2) :: r(false.B) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(xor , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpXor) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(srl , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpSrl) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(sra , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpSra) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(or , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpOr) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
(and , (
r(true.B) :: r(rAluOut) ::
r(false.B) :: r(pStaticNpc) ::
r(aOpAnd) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
)),
)
// format: on
val default = BitPat(0.U(controlWidth.W))
// println(s"ControlMapping = ${ControlMapping.map(it => (it._1 -> toBits(it._2))).foreach(x => println(x._2))}\n")
val out = decoder(
inst,
TruthTable(ControlMapping.map(it => (it._1 -> toBits(it._2))), default)
)
val srcList = slices.map(s => out(s._1, s._2))
assert(out != default)
println(s"out = $out, default = $default\n")
println(s"dstList = ${dstList}\n")
println(s"srcList = ${srcList}\n")
srcList
.zip(dstList)
.foreach({ case (src, dst) =>
dst.toOption.get := src.asTypeOf(dst.toOption.get)
})
}
import flow.components.{RegisterFile, ProgramCounter, ALU, RamDpi}
import chisel3.util.experimental.loadMemoryFromFileInline
class Flow extends Module {
def lit(x: Data) = { x.litValue.toInt }
implicit val p: Params = new Params(XLEN = 32.W, arch = "single")
val IF = Module(new IF)
val ID = Module(new ID)
val EX = Module(new EX)
val LS = Module(new LS)
val WB = Module(new WB)
// IF.msgio connect ID.msgio connect EX.msgio connect LS.msgio connect [Nothing] WB.msgio
ID.msgio.in :<>= IF.msgio.out
EX.msgio.in :<>= ID.msgio.out
LS.msgio.in :<>= EX.msgio.out
WB.msgio.in :<>= LS.msgio.out
val dataType = UInt(32.W)
val ram = Module(new RamDpi)
val control = Module(new Control(32))
val reg = Module(new RegisterFile(dataType, 32, 2))
val pc = Module(new ProgramCounter(dataType))
val alu = Module(new ALU(dataType))
// TODO: Switch to Decoupled and Arbiter later
ram.io.pc := pc.out
val inst = ram.io.inst
dontTouch(reg.control.writeEnable)
import control.pc.SrcSelect._
val npc = Wire(dataType)
npc := pc.out + 4.U
pc.in.exeOut := alu.out.result
pc.in.immB := inst.immB
control.inst := inst
reg.control <> control.reg
// FIXME: Probably optimizable with bulk connection
pc.control <> control.pc
pc.control.useImmB := control.pc.useImmB
alu.control <> control.alu
val branchUseSlt = Wire(Bool())
val branchInvertResult = Wire(Bool())
branchUseSlt := inst(14)
branchInvertResult := inst(12)
val _branchResult = Mux(branchUseSlt, alu.out.result(0), alu.out.eq)
val branchResult = Mux(branchInvertResult, !_branchResult, _branchResult)
pc.control.useImmB := control.pc.useImmB && branchResult
// printf(cf"_branchResult = ${_branchResult}, branchResult = ${branchResult}\n")
// printf(cf"pcin.useImmB = ${pc.control.useImmB}, control.out.useImmB = ${control.pc.useImmB} \n")
import control.reg.WriteSelect._
reg.in.writeData(lit(rAluOut)) := alu.out.result
val maskedData = ram.io.readData & Cat(
Fill(8, ram.io.writeMask(3)),
Fill(8, ram.io.writeMask(2)),
Fill(8, ram.io.writeMask(1)),
"b11111111".U
)
val doSignExt = control.ram.writeMask(0)
val signExt16 = control.ram.writeMask(1)
when(!doSignExt) {
reg.in.writeData(lit(rMemOut)) := maskedData
// printf(cf"!doSignExt\n")
}.elsewhen(signExt16) {
reg.in.writeData(lit(rMemOut)) := Cat(
Fill(16, maskedData(15)),
maskedData(15, 0)
)
// printf(cf"elsewhen\n")
}.otherwise {
reg.in
.writeData(lit(rMemOut)) := Cat(Fill(24, maskedData(7)), maskedData(7, 0))
// printf(cf"otherwise\n")
}
// printf(cf"maskedData = ${maskedData}, writeData = ${reg.in.writeData(lit(rMemOut))}\n")
reg.in.writeData(lit(rNpc)) := npc
reg.in.writeAddr := inst.rd
reg.in.rs(0) := inst.rs1
reg.in.rs(1) := inst.rs2
// TODO: Bulk connection here
ram.io.clock := clock
ram.io.reset := reset
ram.io.writeAddr := alu.out.result
ram.io.writeData := reg.out.src(1)
ram.io.writeMask := control.ram.writeMask
ram.io.writeEnable := control.ram.writeEnable
ram.io.valid := control.ram.valid
ram.io.readAddr := alu.out.result
import control.alu.SrcASelect._
import control.alu.SrcBSelect._
alu.in.a(lit(aSrcARs1)) := reg.out.src(0)
alu.in.a(lit(aSrcAPc)) := pc.out
alu.in.a(lit(aSrcAZero)) := 0.U
alu.in.b(lit(aSrcBRs2)) := reg.out.src(1)
// alu.in.b(lit(aSrcBImmI)) := inst(31, 20).pad(aSrcBImmI.getWidth)
alu.in.b(lit(aSrcBImmI)) := inst.immI
alu.in.b(lit(aSrcBImmJ)) := inst.immJ
alu.in.b(lit(aSrcBImmS)) := inst.immS
alu.in.b(lit(aSrcBImmU)) := inst.immU
Trace.traceName(pc.out)
dontTouch(control.out)
IF.io.fromRam := LS.io.toIF
IF.io.fromEx := EX.io.toIF
LS.io.fromIF := IF.io.toRam
ID.io.fromWB := WB.io.toID
}

View file

@ -7,11 +7,10 @@ import chisel3.experimental.Trace._
import chisel3.stage.{ChiselGeneratorAnnotation, DesignAnnotation}
import chisel3.util.experimental.InlineInstance
import circt.stage.ChiselStage
import firrtl.AnnotationSeq
import firrtl.annotations.TargetToken.{Instance, OfModule, Ref}
import java.io.PrintWriter
import scala.io.Source
import java.io.File
import firrtl.annotations.TargetToken.{OfModule, Instance, Ref}
// TODO: Generate verilator config file

View file

@ -1,59 +0,0 @@
package flow.tests
import chisel3._
import chiseltest._
import org.scalatest.freespec.AnyFreeSpec
import chiseltest.simulator.WriteVcdAnnotation
import flow.components.CSRCore
import flow.tests.defaultParams
class CSRSpec extends AnyFreeSpec with ChiselScalatestTester {
implicit val p: flow.Params = defaultParams()
"should compile" in {
test(new CSRCore) { c =>
c.clock.step(1)
}
}
"Write" - {
"delayed" in {
test(new CSRCore) { c =>
val tv = BigInt("deadbeef", 16)
c.in.csrAddr.poke(c.nameToAddr("mstatus"))
c.in.writeData.poke(tv)
c.control.writeEnable.poke(c.control.csrWrite.csrWriteEnabled)
c.clock.step(1)
c.control.readEnable.poke(c.control.csrRead.csrReadEnabled)
c.out.readData.expect(0)
c.out.readValid.expect(1)
c.clock.step(1)
c.out.readValid.expect(1)
c.out.readData.expect(tv)
}
}
}
"Read" - {
"controlled by readEnable" in {
test(new CSRCore) { c =>
val tv = BigInt("deadbeef", 16)
c.in.csrAddr.poke(c.nameToAddr("mstatus"))
c.in.writeData.poke(tv)
c.control.readEnable.poke(c.control.csrRead.csrReadEnabled)
c.control.writeEnable.poke(c.control.csrWrite.csrWriteEnabled)
c.clock.step(1)
c.control.readEnable.poke(c.control.csrRead.csrReadDisabled)
c.out.readData.expect(0)
c.out.readValid.expect(0)
c.clock.step(1)
c.out.readData.expect(0)
c.out.readValid.expect(0)
}
}
}
}

View file

@ -1,62 +0,0 @@
package npc.keyboard
import chisel3._
import chiseltest._
import org.scalatest.freespec.AnyFreeSpec
import chiseltest.simulator.WriteVcdAnnotation
import npc.util._
class KeyboardControllerSpec extends AnyFreeSpec with ChiselScalatestTester {
def transfer(keycode: Int, clock: Clock, ps2: PS2Port) : Unit = {
require(keycode >= 0 && keycode < 0xFF)
var cycle = 0
var keycode_remain = keycode << 1 // Shift 1 to do nothing at cycle 0
var keycode_collect = 0
ps2.data.poke(1)
ps2.clk.poke(true)
clock.step(1)
for (cycle <- 0 until 9) {
val last_digit = keycode_remain & 1
ps2.clk.poke(true)
ps2.data.poke(last_digit)
clock.step(32)
keycode_collect = keycode_collect | (last_digit << cycle)
keycode_remain = keycode_remain >> 1
ps2.clk.poke(false)
clock.step(32)
}
for (_ <- 9 until 11) {
ps2.clk.poke(true)
clock.step(32)
ps2.clk.poke(false)
clock.step(32)
}
assert(keycode_collect >> 1 == keycode)
clock.step(32)
}
"Simple test" in {
test(new KeyboardController).withAnnotations(Seq(WriteVcdAnnotation)) { c =>
val data = Array(0xE4, 0xD4, 0xC4, 0xA9)
data.foreach(d => {
transfer(d, c.clock, c.io.ps2)
c.io.out.valid.expect(1.U)
c.io.out.bits.expect(d)
c.io.out.ready.poke(1)
c.clock.step(1)
c.io.out.ready.poke(0)
})
data.foreach(d => {
transfer(d, c.clock, c.io.ps2)
})
data.foreach(d => {
c.io.out.valid.expect(1.U)
c.io.out.bits.expect(d)
c.io.out.ready.poke(1)
c.clock.step(1)
c.io.out.ready.poke(0)
})
}
}
}

View file

@ -1,47 +0,0 @@
package flow
import chisel3._
import chiseltest._
import org.scalatest.freespec.AnyFreeSpec
import chiseltest.simulator.WriteVcdAnnotation
import flow.Flow
class RV32CPUSpec extends AnyFreeSpec with ChiselScalatestTester {
"MemoryFile" - {
"correctly load" in {
import chisel3.util.{SRAM, SRAMInterface, HexMemoryFile}
class UserMem extends Module {
val io = IO(new SRAMInterface(1024, UInt(32.W), 1, 1, 0))
val memoryFile = HexMemoryFile("../resource/addi.txt")
io :<>= SRAM(
size = 1024,
tpe = UInt(32.W),
numReadPorts = 1,
numWritePorts = 1,
numReadwritePorts = 0,
memoryFile = memoryFile
)
val read = io.readPorts(0).data
printf(cf"memoryFile=$memoryFile, readPort=$read%x\n")
}
test(new UserMem).withAnnotations(Seq(WriteVcdAnnotation)) { c =>
c.io.readPorts(0).enable.poke(true.B)
c.io.writePorts(0).enable.poke(false.B)
c.io.writePorts(0).address.poke(0.U)
c.io.writePorts(0).data.poke(0.U)
for (i <- 0 until 32) {
c.io.readPorts(0).address.poke(i.U)
c.clock.step(1)
}
}
}
}
"should compile" in {
test(new Flow) { c =>
c.clock.step(1)
}
}
}

View file

@ -1,81 +0,0 @@
package flow
import chisel3._
import chiseltest._
import org.scalatest.freespec.AnyFreeSpec
import chiseltest.simulator.WriteVcdAnnotation
import flow.components._
class RegisterFileSpec extends AnyFreeSpec with ChiselScalatestTester {
"RegisterFileCore" - {
"register 0 is always 0" in {
test(new RegisterFileCore(32, UInt(32.W), 2)) { c =>
c.readPorts(0).addr.poke(0)
c.readPorts(1).addr.poke(0)
c.writePort.enable.poke(true)
c.writePort.addr.poke(0)
c.writePort.data.poke(0x1234)
c.readPorts(0).data.expect(0)
c.readPorts(1).data.expect(0)
c.clock.step(2)
c.readPorts(0).data.expect(0)
c.readPorts(1).data.expect(0)
}
}
"register other than 0 can be written" in {
test(new RegisterFileCore(32, UInt(32.W), 2)) { c =>
import scala.util.Random
val r = new Random()
for (i <- 1 until 32) {
val v = r.nextLong() & 0xFFFFFFFFL
c.readPorts(0).addr.poke(i)
c.writePort.enable.poke(true)
c.writePort.addr.poke(i)
c.writePort.data.poke(v)
c.clock.step(1)
c.readPorts(0).data.expect(v)
}
}
}
}
"RegisterInterface" - {
class Top extends Module {
val io = IO(new RegFileInterface(32, UInt(32.W), 2, 2))
val rf = RegisterFile(32, UInt(32.W), 2, 2)
io :<>= rf
}
"write" in {
test(new Top).withAnnotations(Seq(WriteVcdAnnotation)) { c =>
import c.io.control.WriteSelect._
val writePort = rAluOut.litValue.toInt
c.io.control.writeEnable.poke(true)
c.io.control.writeSelect.poke(rAluOut)
c.io.in.writeAddr.poke(5)
c.io.in.writeData(writePort).poke(0xcdef)
c.io.in.rs(0).poke(5)
c.clock.step(1)
c.io.out.src(0).expect(0xcdef)
}
}
"no data is written when not enabled" in {
test(new Top).withAnnotations(Seq(WriteVcdAnnotation)) { c =>
import c.io.control.WriteSelect._
val writePort = rAluOut.litValue.toInt
c.io.control.writeEnable.poke(true)
c.io.control.writeSelect.poke(rAluOut)
c.io.in.writeAddr.poke(5)
c.io.in.writeData(writePort).poke(0xcdef)
c.io.in.rs(0).poke(5)
c.clock.step(1)
c.io.control.writeEnable.poke(false)
c.io.in.writeData(writePort).poke(0x1234)
c.clock.step(1)
c.io.out.src(0).expect(0xcdef)
}
}
}
}

View file

@ -1,8 +0,0 @@
package flow.tests
import chisel3._
import flow.Params
object defaultParams {
def apply(): Params = new Params(XLEN = 32.W)
}

View file

@ -19,4 +19,4 @@ void Config::cli_parse(int argc, char **argv) {
}
}
Config config;
Config config{.wavefile = "flowwave.vcd"};

View file

@ -7,6 +7,7 @@ extern "C" {
#include <cstdint>
#include <cstdlib>
#include <devices.hpp>
#include <string>
#include <types.h>
#include <vl_wrapper.hpp>
#include <vpi_user.h>
@ -39,21 +40,19 @@ void *pmem_get() {
return pmem;
}
int pmem_read(int raddr) {
int pmem_read(int raddr, int rmask) {
void *pmem = pmem_get();
auto mem = static_cast<MMap *>(pmem);
// TODO: Do memory difftest at memory read and write to diagnose at a finer
// granularity
mem->trace(raddr, true, regs->get_pc());
if (g_skip_memcheck)
return mem->read(PMEM_START);
return mem->read(raddr);
return mem->read(PMEM_START, rmask);
return mem->read(raddr, rmask);
}
void pmem_write(int waddr, int wdata, char wmask) {
void *pmem = pmem_get();
auto mem = static_cast<MMap *>(pmem);
mem->trace((std::size_t)waddr, false, regs->get_pc(), wdata);
return mem->write((std::size_t)waddr, wdata, wmask);
}
@ -146,13 +145,37 @@ void npc_init(void *args) {
DbgState *dbg = (DbgState *)args;
void *mem = pmem_get();
dbg->bp = new std::vector<Breakpoint>;
dbg->cmd_return_buf = new std::string;
top = new VlModule;
regs = new Registers("TOP.Flow.reg_0.regFile_", "TOP.Flow.pc.out");
regs = new Registers("TOP.Flow.ID.regs_regFile_",
"TOP.Flow.WB.msgio_in_bits_pc");
top->setup(config.wavefile, regs);
top->reset_eval(10);
}
char *npc_monitor(void *args, char **argv, int argc) {
DbgState *dbg = (DbgState *)args;
std::string *cmd_return_buf = dbg->cmd_return_buf;
if (strncmp(argv[0], "trace", 5) == 0) {
if (strncmp(argv[1], "on", 2) == 0) {
*cmd_return_buf = "Tracing turned on\n";
if (!top->start_trace())
*cmd_return_buf = "Failed to turn on tracing\n";
return cmd_return_buf->data();
} else if (strncmp(argv[1], "off", 3) == 0) {
*cmd_return_buf = "Tracing turned off\n";
if (!top->end_trace())
*cmd_return_buf = "Failed to turn on tracing\n";
return cmd_return_buf->data();
}
}
*cmd_return_buf = "Command not found\n";
return cmd_return_buf->data();
}
bool npc_do_difftest = true;
static gdbstub_t gdbstub_priv;

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-24.05";
nixpkgs-circt162.url = "github:NixOS/nixpkgs/7995cae3ad60e3d6931283d650d7f43d31aaa5c7";
nur-xin = {
url = "git+https://git.xinyang.life/xin/nur.git";
@ -11,11 +12,18 @@
url = "github:zaninime/sbt-derivation";
inputs.nixpkgs.follows = "nixpkgs";
};
am-kernels.url = "git+https://git.xinyang.life/xin/am-kernels.git?ref=dev";
};
outputs = { self, nixpkgs, flake-utils, nur-xin, nixpkgs-circt162, sbt-derivation }:
outputs = { self, nixpkgs, nixpkgs-stable, flake-utils, nur-xin, nixpkgs-circt162, sbt-derivation, am-kernels }:
flake-utils.lib.eachDefaultSystem (system:
let
stablePkgs = import nixpkgs-stable {
inherit system;
config.allowUnfree = true;
overlays = [
];
};
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
@ -37,5 +45,28 @@
inherit flow;
};
};
devShells.default = pkgs.mkShell.override { stdenv = pkgs.ccacheStdenv; } {
nativeBuildInputs = with pkgs; [
cmake
ninja
flex
bison
nvboard
flow
espresso
coursier
sbt-with-scala-native
gef
] ++ [stablePkgs.verilator];
CHISEL_FIRTOOL_PATH = "${nixpkgs-circt162.legacyPackages.${system}.circt}/bin";
NPC_IMAGES_PATH = "${am-kernels.packages.${system}.rv32Cross.am-kernels-npc}/share";
buildInputs = with pkgs; [
cli11
spdlog
mini-gdbstub
];
};
});
}

View file

@ -31,7 +31,9 @@ public:
Memory(std::filesystem::path filepath, bool is_binary, paddr_t pmem_start,
paddr_t pmem_end)
: pmem_start(pmem_start), pmem_end(pmem_end) {
read_memory(filepath, is_binary);
if (!filepath.empty()) {
read_memory(filepath, is_binary);
}
}
const word_t &operator[](std::size_t addr) { return this->read(addr); }
@ -86,14 +88,18 @@ public:
ram->transfer(waddr, (uint8_t *)&wdata, len, true);
} else if (devices->handle(waddr, (uint8_t *)&wdata, len, true)) {
}
logger->trace("[W] 0x{:x}: 0x{:x}", waddr, wdata);
}
word_t read(paddr_t raddr) const {
word_t read(paddr_t raddr, int rmask) const {
word_t res = 0;
size_t len = (rmask & 1) + ((rmask & 2) >> 1) + ((rmask & 4) >> 2) +
((rmask & 8) >> 3);
if (ram->in_pmem(raddr)) {
ram->transfer(raddr, (uint8_t *)&res, 4, false);
ram->transfer(raddr, (uint8_t *)&res, len, false);
} else if (devices->handle(raddr, (uint8_t *)&res, 4, false)) {
}
logger->trace("[R] 0x{:x}: 0x{:x}", raddr, res);
return res;
}
@ -118,7 +124,7 @@ public:
void *get_pmem() { return ram->mem.data(); }
void trace(paddr_t addr, bool is_read, word_t pc = 0, word_t value = 0) {
logger->trace("[{}] 0x{:x}", is_read ? 'R' : 'W', this->read(addr));
logger->trace("[{}] 0x{:x}", is_read ? 'R' : 'W', this->read(addr, value));
}
private:

View file

@ -1,6 +1,7 @@
#ifndef _NPC_TYPES_H__
#define _NPC_TYPES_H__
#ifdef __cplusplus
#include <string>
extern "C" {
#endif
#include <gdbstub.h>
@ -32,6 +33,7 @@ struct Breakpoint {
struct DbgState {
std::vector<Breakpoint> *bp;
std::string *cmd_return_buf;
};
#endif

View file

@ -34,6 +34,7 @@ template <typename T, typename R> class VlModuleInterfaceCommon : public T {
uint64_t sim_time = 0;
uint64_t posedge_cnt = 0;
std::unique_ptr<Tracer<T>> tracer;
std::filesystem::path wavefile;
public:
const R *registers;
@ -43,8 +44,7 @@ public:
}
void setup(std::filesystem::path wavefile, const R *r) {
if (!wavefile.empty())
tracer = std::make_unique<Tracer<T>>(this, wavefile);
wavefile = "wave.vcd";
registers = r;
}
@ -89,10 +89,30 @@ public:
this->reset = 0;
g_skip_memcheck = false;
}
bool is_posedge() {
// Will be posedge when eval is called
return T::clock == 0;
}
bool start_trace() { return init_tracer(wavefile); }
bool end_trace() {
tracer.reset();
return true;
}
private:
bool init_tracer(std::filesystem::path wavefile) {
fmt::print("wavefile: {}", wavefile.string());
std::filesystem::path wav = "wave.vcd";
if (!wav.empty()) {
// Creating of tracer must happen after this class fully initialized
tracer = std::make_unique<Tracer<T>>(this, wav);
return true;
}
return false;
}
};
#endif

View file

@ -27,18 +27,23 @@ public:
}
private:
static vpiHandle get_handle(const std::string name) {
vpiHandle hdl = vpi_handle_by_name((PLI_BYTE8 *)name.c_str(), nullptr);
if (hdl == nullptr) {
SPDLOG_ERROR("VPI Handle {} not found", name);
exit(EXIT_FAILURE);
} else {
SPDLOG_INFO("Found VPI handle {} at {}", name, (void *)hdl);
}
return hdl;
}
void init_handlers(const std::string regs_prefix, const std::string pcname) {
pc_handle = get_handle(pcname);
for (int i = 0; i < nr; i++) {
std::string regname = regs_prefix + std::to_string(i);
vpiHandle vh = vpi_handle_by_name((PLI_BYTE8 *)regname.c_str(), nullptr);
if (vh == nullptr) {
std::cerr << "vpiHandle " << regname.c_str() << " not found"
<< std::endl;
exit(EXIT_FAILURE);
}
reg_handles[i] = vh;
reg_handles[i] = get_handle(regname);
}
pc_handle = vpi_handle_by_name((PLI_BYTE8 *)pcname.c_str(), nullptr);
}
};

86
scripts/difftests.py Normal file
View file

@ -0,0 +1,86 @@
#/usr/bin/env python
"""
This script is used to provide a wrapper to difftest, so that they can be easily
deployed on ci environment.
"""
import os
import os.path as osp
import sys
from multiprocessing import Pool, Process
from functools import partial
def find_all_test_images(path):
tests = []
for root, dirs, files in os.walk(path):
for file in files:
# Get file extensions and select files with .bin
ext = osp.splitext(file)[1]
if ext == ".bin":
tests.append(osp.join(root, file))
return tests
def run_test(test_image, ref, ref_prefix, dut, dut_prefix):
diffu = "diffu"
args = [
"--images-path", "/",
"-m", test_image,
"--ref", ref,
"--ref-prefix", ref_prefix,
"--dut", dut,
"--dut-prefix", dut_prefix,
"> logs/" + osp.basename(test_image) + ".log",
"2>&1"
]
status = os.system(diffu + " " + " ".join(args))
exitcode = os.waitstatus_to_exitcode(status)
image_shortname = osp.basename(test_image)
print(f"{ 'FAILED' if exitcode else 'PASSED' } {image_shortname}")
if exitcode:
print(f"cmd: {diffu + ' ' + ' '.join(args)}")
print(f"exitcode: {exitcode}")
sys.exit(exitcode)
def print_statistics(results):
pass
def main():
DIFFU_IMAGES_PATH = os.environ["DIFFU_IMAGES_PATH"]
print(DIFFU_IMAGES_PATH)
assert(osp.isdir(DIFFU_IMAGES_PATH))
os.makedirs("logs", exist_ok = True)
# Run tests in a multiprocess pool
tests = find_all_test_images(DIFFU_IMAGES_PATH)
ref, ref_prefix, dut, dut_prefix = sys.argv[1:]
ref_shortname = osp.basename(ref)
dut_shortname = osp.basename(dut)
print(f"[{ref_shortname}, {dut_shortname}]")
procs = []
for test in tests:
image_shortname = osp.basename(test)
p = Process(target=run_test, args=(test, ref, ref_prefix, dut, dut_prefix), name=image_shortname, daemon=True)
procs.append(p)
p.start()
timeout = 0
for p in procs:
p.join(5)
if p.exitcode is None:
print(f"{ 'TIMEOUT' } {p.name}")
p.terminate()
p.join()
timeout += 1
not_success = sum((1 for p in procs if p.exitcode != 0))
failed = not_success - timeout
print("==========")
print(f"TOTAL {len(procs)}\tFAILED: {failed}\tTIMEOUT: {timeout}")
return not_success
if __name__ == "__main__":
sys.exit(main())