refactor-npc #4

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

4
.gitignore vendored
View file

@ -6,3 +6,7 @@ difftest/
**/result **/result
/.pre-commit-config.yaml /.pre-commit-config.yaml
**/.vscode/ **/.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"; diffu.url = "git+https://git.xinyang.life/xin/diffu.git";
am-kernels.url = "git+https://git.xinyang.life/xin/am-kernels.git?ref=dev"; 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: outputs =
flake-utils.lib.eachDefaultSystem (system: {
self,
flake-utils,
nixpkgs,
nixpkgs-circt162,
pre-commit-hooks,
nur-xin,
diffu,
am-kernels,
spike-diff,
}@inputs:
flake-utils.lib.eachDefaultSystem (
system:
let let
pkgs = import nixpkgs { pkgs = import nixpkgs {
inherit system; inherit system;
@ -39,6 +52,8 @@
}; };
}; };
}; };
am-kernels-nemu = am-kernels.packages.${system}.rv32Cross.am-kernels-nemu;
in in
{ {
checks = { checks = {
@ -50,7 +65,10 @@
cmake-format.enable = true; cmake-format.enable = true;
clang-format = { clang-format = {
enable = true; enable = true;
types_or = pkgs.lib.mkForce [ "c" "c++" ]; types_or = pkgs.lib.mkForce [
"c"
"c++"
];
}; };
scalafmt = { scalafmt = {
enable = true; enable = true;
@ -63,13 +81,19 @@
}; };
}; };
packages = rec { packages = {
abstract-machine = pkgs.callPackage ./abstract-machine { isa = "native"; }; abstract-machine = pkgs.callPackage ./abstract-machine { isa = "native"; };
nemu = pkgs.callPackage ./nemu { }; nemu = pkgs.callPackage ./nemu { am-kernels = am-kernels-nemu; };
nemu-lib = pkgs.callPackage ./nemu { }; nemu-lib = pkgs.callPackage ./nemu { defconfig = "libdefconfig"; };
rv32Cross = rec { rv32Cross = {
abstract-machine = rv32CrossConfig.callPackage ./abstract-machine { isa = "riscv"; platform = [ "nemu" "npc" ]; }; abstract-machine = rv32CrossConfig.callPackage ./abstract-machine {
isa = "riscv";
platform = [
"nemu"
"npc"
];
};
}; };
}; };
@ -92,7 +116,7 @@
self.packages.${system}.nemu self.packages.${system}.nemu
]; ];
NEMU_HOME = "/home/xin/repo/ysyx-workbench/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; } { devShells.npc = pkgs.mkShell.override { stdenv = pkgs.ccacheStdenv; } {
@ -125,7 +149,9 @@
verilator verilator
]; ];
buildInputs = with pkgs; [ buildInputs =
with pkgs;
[
spdlog spdlog
nvboard nvboard
openssl openssl
@ -133,15 +159,26 @@
libxml2 libxml2
readline readline
mini-gdbstub mini-gdbstub
] ++ self.checks.${system}.pre-commit-check.enabledPackages; ]
++ 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 = [ packages = [
diffu.packages.${system}.default diffu.packages.${system}.default
am-kernels.packages.${system}.rv32Cross.am-kernels-npc am-kernels.packages.${system}.rv32Cross.am-kernels-npc
self.packages.${system}.nemu-lib 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 compile_commands.json
*.vcd *.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) set(CHISEL_TEST_TARGET sbt_${TOPMODULE}_test)
add_custom_command( add_custom_command(
OUTPUT ${CHISEL_OUTPUT_TOPMODULE} ${CHISEL_OUTPUT_VERILATOR_CONF} 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} WORKING_DIRECTORY ${SCALA_CORE}
DEPENDS ${CHISEL_DEPENDENCY} DEPENDS ${CHISEL_DEPENDENCY}
VERBATIM VERBATIM

View file

@ -1,7 +1,7 @@
ThisBuild / scalaVersion := "2.13.12" 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" val circeVersion = "0.14.1"
lazy val root = (project in file(".")) 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); import "DPI-C" function void pmem_write(input int waddr, input int wdata, input byte wmask);
module RamDpi ( module RamDpi (
@ -9,6 +9,7 @@ module RamDpi (
input [31:0] writeAddr, input [31:0] writeAddr,
input [31:0] writeData, input [31:0] writeData,
input [3:0] writeMask, input [3:0] writeMask,
input [3:0] readMask,
input reg [31:0] readAddr, input reg [31:0] readAddr,
output reg [31:0] readData, output reg [31:0] readData,
input reg [31:0] pc, input reg [31:0] pc,
@ -16,7 +17,7 @@ module RamDpi (
); );
always @(posedge clock) begin always @(posedge clock) begin
if (valid) begin // 有读写请求时 if (valid) begin // 有读写请求时
readData = pmem_read(readAddr); readData = pmem_read(readAddr, { 4'h0, readMask });
if (writeEnable) begin // 有写请求时 if (writeEnable) begin // 有写请求时
pmem_write(writeAddr, writeData, { 4'h0, writeMask }); pmem_write(writeAddr, writeData, { 4'h0, writeMask });
end end
@ -25,5 +26,5 @@ module RamDpi (
readData = 32'h80000000; readData = 32'h80000000;
end end
end end
assign inst = pmem_read(pc); assign inst = pmem_read(pc, 8'hF);
endmodule endmodule

View file

@ -3,11 +3,14 @@ package flow.components
import chisel3._ import chisel3._
import chisel3.util._ import chisel3.util._
import shapeless.{HNil, ::} 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 { object OpSelect extends ChiselEnum {
val aOpAdd, aOpSub, aOpNot, aOpAnd, aOpOr, aOpXor, aOpSlt, aOpSltu, aOpSll, val aOpAdd, aOpSub, aOpAnd, aOpOr, aOpXor, aOpSlt, aOpSltu, aOpSll, aOpSrl,
aOpSrl, aOpSra = Value aOpSra = Value
} }
object SrcASelect extends ChiselEnum { object SrcASelect extends ChiselEnum {
val aSrcARs1, aSrcAPc, aSrcAZero = Value val aSrcARs1, aSrcAPc, aSrcAZero = Value
@ -15,6 +18,7 @@ class ALUControlInterface extends Bundle {
object SrcBSelect extends ChiselEnum { object SrcBSelect extends ChiselEnum {
val aSrcBRs2, aSrcBImmI, aSrcBImmJ, aSrcBImmS, aSrcBImmU = Value val aSrcBRs2, aSrcBImmI, aSrcBImmJ, aSrcBImmS, aSrcBImmU = Value
} }
class ALUControlInterface extends Bundle {
val op = Input(OpSelect()) val op = Input(OpSelect())
val srcASelect = Input(SrcASelect()) val srcASelect = Input(SrcASelect())
val srcBSelect = Input(SrcBSelect()) val srcBSelect = Input(SrcBSelect())
@ -25,21 +29,23 @@ class ALUControlInterface extends Bundle {
} }
} }
class ALU[T <: UInt](tpe: T) extends Module { def apply() = new ALUControlInterface;
val control = IO(new ALUControlInterface) }
class ALU(implicit p: Params) extends Module {
import ALUControlInterface._
val control = IO(ALUControlInterface())
val in = IO(new Bundle { val in = IO(new Bundle {
val a = Input(Vec(control.SrcASelect.all.length, tpe)) val a = Input(Vec(SrcASelect.all.length, UInt(p.XLEN)))
val b = Input(Vec(control.SrcBSelect.all.length, tpe)) val b = Input(Vec(SrcBSelect.all.length, UInt(p.XLEN)))
}) })
val out = IO(new Bundle { val out = IO(new Bundle {
val eq = Output(Bool()) val result = Output(UInt(p.XLEN))
val result = Output(tpe)
}) })
val a = in.a(control.srcASelect.asUInt) val a = in.a(control.srcASelect.asUInt)
val b = in.b(control.srcBSelect.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 add = a + b
val sub = a - b val sub = a - b
val and = a & b val and = a & b
@ -48,18 +54,16 @@ class ALU[T <: UInt](tpe: T) extends Module {
val xor = a ^ b val xor = a ^ b
val slt = a.asSInt < b.asSInt val slt = a.asSInt < b.asSInt
val sltu = a < b val sltu = a < b
val sll = a << b(log2Ceil(tpe.getWidth), 0) val sll = a << b(5, 0)
val srl = a >> b(log2Ceil(tpe.getWidth), 0) val srl = a >> b(5, 0)
val sra = a.asSInt >> b(log2Ceil(tpe.getWidth), 0) val sra = a.asSInt >> b(5, 0)
out.eq := a === b
import control.OpSelect._ import OpSelect._
out.result := MuxLookup(control.op, 0.U)( out.result := MuxLookup(control.op, 0.U)(
Seq( Seq(
aOpAdd -> add, aOpAdd -> add,
aOpSub -> sub, aOpSub -> sub,
aOpNot -> not,
aOpAnd -> and, aOpAnd -> and,
aOpOr -> or, aOpOr -> or,
aOpXor -> xor, aOpXor -> xor,
@ -72,8 +76,92 @@ class ALU[T <: UInt](tpe: T) extends Module {
) )
} }
object ALU { class AluController(implicit p: Params) extends Module {
def apply[T <: UInt](tpe: T): ALU[T] = { val out = IO(Flipped(ALUControlInterface()))
Module(new ALU(tpe)) 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,15 +11,16 @@ import flow.Params
import chisel3.util.BitPat import chisel3.util.BitPat
import chisel3.util.Fill import chisel3.util.Fill
class CSRControlInterface extends Bundle { object CSRControlInterface extends Bundle {
object csrRead extends ChiselEnum { object csrRead extends ChiselEnum {
val csrReadDisabled, csrReadEnabled = Value val csrReadDisabled, csrReadEnabled = Value
} }
object csrWrite extends ChiselEnum { object csrWrite extends ChiselEnum {
val csrWriteDisabled, csrWriteEnabled = Value val csrWriteDisabled, csrWriteData, csrSetBit, csrClearBit = Value
} }
class CSRControlInterface extends Bundle {
val readEnable = Input(csrRead()) val readEnable = Input(csrRead())
val writeEnable = Input(csrWrite()) val writeEnable = Input(csrWrite())
@ -27,9 +28,11 @@ class CSRControlInterface extends Bundle {
readEnable :: writeEnable :: HNil readEnable :: writeEnable :: HNil
} }
} }
def apply() = new CSRControlInterface
}
class CSRCore(implicit val p: Params) extends Module { class CSRCore(implicit val p: Params) extends Module {
val control = IO(new CSRControlInterface) val control = IO(CSRControlInterface())
val in = IO(new Bundle { val in = IO(new Bundle {
val csrAddr = Input(UInt(p.csrAddrWidth)) val csrAddr = Input(UInt(p.csrAddrWidth))

View file

@ -5,38 +5,81 @@ import chisel3.experimental.noPrefix
import chisel3.util.HasBlackBoxPath import chisel3.util.HasBlackBoxPath
import chisel3.util.HasBlackBoxResource import chisel3.util.HasBlackBoxResource
import chisel3.util.log2Ceil import chisel3.util.log2Ceil
import chisel3.util.BitPat
import flow.components import flow.components
import flow.Params
import shapeless.:: import shapeless.::
import shapeless.HNil import shapeless.HNil
import scala.collection.SeqMap 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 { class RamDpi(implicit p: Params) extends BlackBox with HasBlackBoxResource {
val valid = Input(Bool()) val io = IO(new DpiRamInterface)
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)))
addResource("/RamDpi.v") 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 package flow.components
import chisel3._ import chisel3._
import chisel3.util.experimental.decode.{decoder, TruthTable}
import chisel3.util.{Valid, log2Ceil} import chisel3.util.{Valid, log2Ceil}
import chisel3.util.MuxLookup import chisel3.util.MuxLookup
import chisel3.util.BitPat
import shapeless.{HNil, ::} 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 { object SrcSelect extends ChiselEnum {
val pStaticNpc, pExeOut = Value val pStatic, pJmp, pBR = Value
} }
val useImmB = Input(Bool()) class newPcControlInterface extends Bundle {
val srcSelect = Input(SrcSelect()) val srcSelect = Input(SrcSelect())
def ctrlBindPorts = { def ctrlBindPorts = {
useImmB :: srcSelect :: HNil srcSelect :: HNil
} }
} }
def apply() = new newPcControlInterface;
}
class ProgramCounter[T <: UInt](tpe: T) extends Module { class ProgramCounter(implicit p: Params) extends Module {
val control = IO(PcControlInterface())
val control = IO(new PcControlInterface) import PcControlInterface.SrcSelect._
val in = IO(new Bundle { val in = IO(new Bundle {
val immB = Input(tpe) val brOffset = Input(UInt(p.XLEN))
val exeOut = Input(tpe) 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) private val npc = MuxLookup(control.srcSelect, 4.U)(
import control.SrcSelect._ Seq(
when(control.useImmB === true.B) { pStatic -> (pcReg + 4.U),
pc_reg := pc_reg + in.immB pJmp -> in.jAddr,
}.elsewhen(control.srcSelect === pStaticNpc) { pBR -> (pcReg + in.brOffset)
pc_reg := pc_reg + 4.U )
}.elsewhen(control.srcSelect === pExeOut) { )
pc_reg := in.exeOut
} pcReg := npc
out := pc_reg
} }
object ProgramCounter { class PcController(implicit p: Params) extends Module {
def apply[T <: UInt](tpe: T): ProgramCounter[T] = { val in = IO(new Bundle {
val pc = Module(new ProgramCounter(tpe)) val inst = Input(UInt(p.instWidth))
pc })
}
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.log2Ceil
import chisel3.util.UIntToOH import chisel3.util.UIntToOH
import chisel3.util.MuxLookup import chisel3.util.MuxLookup
import chisel3.util.BitPat
import chisel3.util.experimental.decode.{decoder, TruthTable}
import chisel3.experimental.Trace._ import chisel3.experimental.Trace._
import shapeless.{HList, HNil, ::} import shapeless.{HList, HNil, ::}
import flow.Params
class RegControl extends Bundle { import flow.components.util._
import flow.components.RV32Inst._
object RegControl {
object WriteSelect extends ChiselEnum { object WriteSelect extends ChiselEnum {
val rAluOut, rMemOut, rNpc = Value val rAluOut, rMemOut, rNpc = Value
} }
class RegControl extends Bundle {
val writeEnable = Input(Bool()) val writeEnable = Input(Bool())
val writeSelect = Input(WriteSelect()) val writeSelect = Input(WriteSelect())
def ctrlBindPorts = { def ctrlBindPorts = {
writeEnable :: writeSelect :: HNil writeEnable :: writeSelect :: HNil
} }
traceName(writeEnable)
} }
class RegisterFile[T <: Data](tpe: T, regCount: Int, numReadPorts: Int) def apply() = new RegControl
extends Module { }
require(numReadPorts >= 0)
val control = IO(new RegControl) class RegisterFile(implicit p: Params) extends Module {
val dataAddrWidth = log2Ceil(regCount).W
val in = IO(new Bundle { val in = IO(new Bundle {
val writeAddr = Input(UInt(dataAddrWidth)) val rd = Input(UInt(p.regsAddrWidth))
val writeData = Input(Vec(control.WriteSelect.all.length, tpe)) val writeData = Input(Vec(RegControl.WriteSelect.all.length, UInt(p.XLEN)))
val rs = Input(Vec(numReadPorts, UInt(dataAddrWidth))) val rs1 = Input(UInt(p.regsAddrWidth))
val rs2 = Input(UInt(p.regsAddrWidth))
}) })
val out = IO(new Bundle { 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 control = IO(RegControl())
val regFile = RegInit(VecInit(Seq.fill(regCount)(regResetValue)))
val writeAddrOH = UIntToOH(in.writeAddr)
for ((reg, i) <- regFile.zipWithIndex.tail) { val regFile = RegInit(
reg := Mux( VecInit(Seq.fill(p.regsCount)(p.regsResetValue.U(p.XLEN)))
writeAddrOH(i.U(log2Ceil(regCount).W)) && control.writeEnable, )
in.writeData(control.writeSelect.asUInt),
reg // 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 regFile(0) := 0.U
for (port <- 0 until numReadPorts) { out.src1 := regFile(in.rs1)
out.src(port) := regFile(in.rs(port).asUInt) out.src2 := regFile(in.rs2)
}
traceName(regFile) traceName(regFile)
dontTouch(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._ import chisel3._
case class Params( case class Params(
XLEN: Width, XLEN: Width,
instWidth: Width = 32.W,
regsCount: Int = 32,
regsAddrWidth: Width = 5.W,
regsResetValue: BigInt = 0L,
arch: String, 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 chisel3.experimental.Trace
import scala.collection.IndexedSeqView import scala.collection.IndexedSeqView
import shapeless.Poly1 import shapeless.Poly1
import flow.components.RamControlInterface import flow.components.DpiRamControlInterface
import flow.components.RV32Inst import flow.components.RV32Inst
import flow.components.RV32InstSubfields._ import flow.components.RV32InstSubfields._
import flow.components.util._
import flow.components.{RegControl, PcControlInterface, ALUControlInterface} 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)) import chisel3.Data
val pc = IO(Flipped(new PcControlInterface)) import chisel3.experimental.{annotate, requireIsAnnotatable, ChiselAnnotation}
val alu = IO(Flipped(new ALUControlInterface))
val ram = IO(Flipped(new RamControlInterface(32)))
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 { 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) IF.io.fromRam := LS.io.toIF
val ram = Module(new RamDpi) IF.io.fromEx := EX.io.toIF
val control = Module(new Control(32)) LS.io.fromIF := IF.io.toRam
val reg = Module(new RegisterFile(dataType, 32, 2)) ID.io.fromWB := WB.io.toID
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)
} }

View file

@ -7,11 +7,10 @@ import chisel3.experimental.Trace._
import chisel3.stage.{ChiselGeneratorAnnotation, DesignAnnotation} import chisel3.stage.{ChiselGeneratorAnnotation, DesignAnnotation}
import chisel3.util.experimental.InlineInstance import chisel3.util.experimental.InlineInstance
import circt.stage.ChiselStage import circt.stage.ChiselStage
import firrtl.AnnotationSeq
import firrtl.annotations.TargetToken.{Instance, OfModule, Ref}
import java.io.PrintWriter import java.io.PrintWriter
import scala.io.Source import scala.io.Source
import java.io.File import java.io.File
import firrtl.annotations.TargetToken.{OfModule, Instance, Ref}
// TODO: Generate verilator config file // 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 <cstdint>
#include <cstdlib> #include <cstdlib>
#include <devices.hpp> #include <devices.hpp>
#include <string>
#include <types.h> #include <types.h>
#include <vl_wrapper.hpp> #include <vl_wrapper.hpp>
#include <vpi_user.h> #include <vpi_user.h>
@ -39,21 +40,19 @@ void *pmem_get() {
return pmem; return pmem;
} }
int pmem_read(int raddr) { int pmem_read(int raddr, int rmask) {
void *pmem = pmem_get(); void *pmem = pmem_get();
auto mem = static_cast<MMap *>(pmem); auto mem = static_cast<MMap *>(pmem);
// TODO: Do memory difftest at memory read and write to diagnose at a finer // TODO: Do memory difftest at memory read and write to diagnose at a finer
// granularity // granularity
mem->trace(raddr, true, regs->get_pc());
if (g_skip_memcheck) if (g_skip_memcheck)
return mem->read(PMEM_START); return mem->read(PMEM_START, rmask);
return mem->read(raddr); return mem->read(raddr, rmask);
} }
void pmem_write(int waddr, int wdata, char wmask) { void pmem_write(int waddr, int wdata, char wmask) {
void *pmem = pmem_get(); void *pmem = pmem_get();
auto mem = static_cast<MMap *>(pmem); 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); return mem->write((std::size_t)waddr, wdata, wmask);
} }
@ -146,13 +145,37 @@ void npc_init(void *args) {
DbgState *dbg = (DbgState *)args; DbgState *dbg = (DbgState *)args;
void *mem = pmem_get(); void *mem = pmem_get();
dbg->bp = new std::vector<Breakpoint>; dbg->bp = new std::vector<Breakpoint>;
dbg->cmd_return_buf = new std::string;
top = new VlModule; 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->setup(config.wavefile, regs);
top->reset_eval(10); 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; bool npc_do_difftest = true;
static gdbstub_t gdbstub_priv; static gdbstub_t gdbstub_priv;

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
{ {
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-24.05";
nixpkgs-circt162.url = "github:NixOS/nixpkgs/7995cae3ad60e3d6931283d650d7f43d31aaa5c7"; nixpkgs-circt162.url = "github:NixOS/nixpkgs/7995cae3ad60e3d6931283d650d7f43d31aaa5c7";
nur-xin = { nur-xin = {
url = "git+https://git.xinyang.life/xin/nur.git"; url = "git+https://git.xinyang.life/xin/nur.git";
@ -11,11 +12,18 @@
url = "github:zaninime/sbt-derivation"; url = "github:zaninime/sbt-derivation";
inputs.nixpkgs.follows = "nixpkgs"; 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: flake-utils.lib.eachDefaultSystem (system:
let let
stablePkgs = import nixpkgs-stable {
inherit system;
config.allowUnfree = true;
overlays = [
];
};
pkgs = import nixpkgs { pkgs = import nixpkgs {
inherit system; inherit system;
config.allowUnfree = true; config.allowUnfree = true;
@ -37,5 +45,28 @@
inherit flow; 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,8 +31,10 @@ public:
Memory(std::filesystem::path filepath, bool is_binary, paddr_t pmem_start, Memory(std::filesystem::path filepath, bool is_binary, paddr_t pmem_start,
paddr_t pmem_end) paddr_t pmem_end)
: pmem_start(pmem_start), pmem_end(pmem_end) { : pmem_start(pmem_start), pmem_end(pmem_end) {
if (!filepath.empty()) {
read_memory(filepath, is_binary); read_memory(filepath, is_binary);
} }
}
const word_t &operator[](std::size_t addr) { return this->read(addr); } 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); ram->transfer(waddr, (uint8_t *)&wdata, len, true);
} else if (devices->handle(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; word_t res = 0;
size_t len = (rmask & 1) + ((rmask & 2) >> 1) + ((rmask & 4) >> 2) +
((rmask & 8) >> 3);
if (ram->in_pmem(raddr)) { 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)) { } else if (devices->handle(raddr, (uint8_t *)&res, 4, false)) {
} }
logger->trace("[R] 0x{:x}: 0x{:x}", raddr, res);
return res; return res;
} }
@ -118,7 +124,7 @@ public:
void *get_pmem() { return ram->mem.data(); } void *get_pmem() { return ram->mem.data(); }
void trace(paddr_t addr, bool is_read, word_t pc = 0, word_t value = 0) { 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: private:

View file

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

View file

@ -34,6 +34,7 @@ template <typename T, typename R> class VlModuleInterfaceCommon : public T {
uint64_t sim_time = 0; uint64_t sim_time = 0;
uint64_t posedge_cnt = 0; uint64_t posedge_cnt = 0;
std::unique_ptr<Tracer<T>> tracer; std::unique_ptr<Tracer<T>> tracer;
std::filesystem::path wavefile;
public: public:
const R *registers; const R *registers;
@ -43,8 +44,7 @@ public:
} }
void setup(std::filesystem::path wavefile, const R *r) { void setup(std::filesystem::path wavefile, const R *r) {
if (!wavefile.empty()) wavefile = "wave.vcd";
tracer = std::make_unique<Tracer<T>>(this, wavefile);
registers = r; registers = r;
} }
@ -89,10 +89,30 @@ public:
this->reset = 0; this->reset = 0;
g_skip_memcheck = false; g_skip_memcheck = false;
} }
bool is_posedge() { bool is_posedge() {
// Will be posedge when eval is called // Will be posedge when eval is called
return T::clock == 0; 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 #endif

View file

@ -27,18 +27,23 @@ public:
} }
private: 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) { void init_handlers(const std::string regs_prefix, const std::string pcname) {
pc_handle = get_handle(pcname);
for (int i = 0; i < nr; i++) { for (int i = 0; i < nr; i++) {
std::string regname = regs_prefix + std::to_string(i); std::string regname = regs_prefix + std::to_string(i);
vpiHandle vh = vpi_handle_by_name((PLI_BYTE8 *)regname.c_str(), nullptr); reg_handles[i] = get_handle(regname);
if (vh == nullptr) {
std::cerr << "vpiHandle " << regname.c_str() << " not found"
<< std::endl;
exit(EXIT_FAILURE);
} }
reg_handles[i] = vh;
}
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())