Compare commits

..

2 commits

Author SHA1 Message Date
24aeabee4f
npc,fix: bugs of new arch in cpu-tests
All checks were successful
Build npc tests / npc-build (flow) (push) Successful in 3m7s
Build npc tests / npc-build (flow-simlib) (push) Successful in 3m13s
Build abstract machine with nix / build-packages (nemu) (pull_request) Successful in 25s
Build abstract machine with nix / build-packages (nemu-lib) (pull_request) Successful in 19s
Build abstract machine with nix / build-packages (rv32Cross.abstract-machine) (pull_request) Successful in 7s
Build abstract machine with nix / build-packages (abstract-machine) (pull_request) Successful in 1m23s
2024-09-06 14:57:11 +08:00
fd1aae7c33 npc,wip: group components into stages 2024-09-03 11:29:22 +08:00
41 changed files with 3295 additions and 739 deletions

View file

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

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,23 +149,36 @@
verilator verilator
]; ];
buildInputs = with pkgs; [ buildInputs =
spdlog with pkgs;
nvboard [
openssl spdlog
libllvm nvboard
libxml2 openssl
readline libllvm
mini-gdbstub libxml2
] ++ self.checks.${system}.pre-commit-check.enabledPackages; 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 = [ 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

29
npc/CMakePresets.json Normal file
View file

@ -0,0 +1,29 @@
{
"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",
"BUILD_USE_BLOOP": "ON"
}
}
],
"buildPresets": [
{
"name": "default",
"configurePreset": "default"
}
]
}

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,21 +18,27 @@ 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
} }
val op = Input(OpSelect()) class ALUControlInterface extends Bundle {
val srcASelect = Input(SrcASelect()) val op = Input(OpSelect())
val srcBSelect = Input(SrcBSelect()) val srcASelect = Input(SrcASelect())
val signExt = Input(Bool()) val srcBSelect = Input(SrcBSelect())
val signExt = Input(Bool())
def ctrlBindPorts = { def ctrlBindPorts = {
op :: srcASelect :: srcBSelect :: signExt :: HNil op :: srcASelect :: srcBSelect :: signExt :: HNil
}
} }
def apply() = new ALUControlInterface;
} }
class ALU[T <: UInt](tpe: T) extends Module { class ALU[T <: UInt](tpe: T) extends Module {
val control = IO(new ALUControlInterface) 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, tpe))
val b = Input(Vec(control.SrcBSelect.all.length, tpe)) val b = Input(Vec(SrcBSelect.all.length, tpe))
}) })
val out = IO(new Bundle { val out = IO(new Bundle {
val eq = Output(Bool()) val eq = Output(Bool())
@ -53,13 +62,12 @@ class ALU[T <: UInt](tpe: T) extends Module {
val sra = a.asSInt >> b(log2Ceil(tpe.getWidth), 0) val sra = a.asSInt >> b(log2Ceil(tpe.getWidth), 0)
out.eq := a === b out.eq := a === b
import control.OpSelect._ import ALUControlInterface.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,
@ -77,3 +85,140 @@ object ALU {
Module(new ALU(tpe)) Module(new ALU(tpe))
} }
} }
class newALU(implicit p: Params) extends Module {
import ALUControlInterface._
val control = IO(ALUControlInterface())
val in = IO(new Bundle {
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 isEqual = Output(Bool())
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
val not = ~a
val or = a | b
val xor = a ^ b
val slt = a.asSInt < b.asSInt
val sltu = a < b
val sll = a << b(5, 0)
val srl = a >> b(5, 0)
val sra = a.asSInt >> b(5, 0)
out.isEqual := a === b
import OpSelect._
out.result := MuxLookup(control.op, 0.U)(
Seq(
aOpAdd -> add,
aOpSub -> sub,
aOpAnd -> and,
aOpOr -> or,
aOpXor -> xor,
aOpSlt -> slt,
aOpSltu -> sltu,
aOpSll -> sll,
aOpSrl -> srl,
aOpSra -> sra.asUInt
)
)
}
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.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
} }
val readEnable = Input(csrRead()) class CSRControlInterface extends Bundle {
val writeEnable = Input(csrWrite()) val readEnable = Input(csrRead())
val writeEnable = Input(csrWrite())
def ctrlBindPorts = { def ctrlBindPorts = {
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,8 +1,14 @@
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 { class PcControlInterface extends Bundle {
object SrcSelect extends ChiselEnum { object SrcSelect extends ChiselEnum {
@ -46,3 +52,68 @@ object ProgramCounter {
pc pc
} }
} }
object newPcControlInterface {
object SrcSelect extends ChiselEnum {
val pStatic, pJmp, pBR = Value
}
class newPcControlInterface extends Bundle {
val srcSelect = Input(SrcSelect())
def ctrlBindPorts = {
srcSelect :: HNil
}
}
def apply() = new newPcControlInterface;
}
class newProgramCounter(implicit p: Params) extends Module {
val control = IO(newPcControlInterface())
import newPcControlInterface.SrcSelect._
val in = IO(new Bundle {
val brOffset = Input(UInt(p.XLEN))
val jAddr = Input(UInt(p.XLEN))
})
val out = IO(Output(UInt(p.XLEN)))
private val pcReg = RegInit(p.resetVector.U)
out := pcReg
private val npc = MuxLookup(control.srcSelect, 4.U)(
Seq(
pStatic -> (pcReg + 4.U),
pJmp -> in.jAddr,
pBR -> (pcReg + in.brOffset)
)
)
pcReg := npc
}
class PcController(implicit p: Params) extends Module {
val in = IO(new Bundle {
val inst = Input(UInt(p.instWidth))
})
val out = IO(Flipped(newPcControlInterface()))
import RV32Inst._
import newPcControlInterface.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 := newPcControlInterface.SrcSelect
.safe(
(
decoder(in.inst, mapping)
)
)
._1
}

View file

@ -4,31 +4,38 @@ 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
import flow.components.util._
import flow.components.RV32Inst._
class RegControl extends Bundle { 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 writeSelect = Input(WriteSelect())
val writeEnable = Input(Bool()) def ctrlBindPorts = {
val writeSelect = Input(WriteSelect()) writeEnable :: writeSelect :: HNil
}
def ctrlBindPorts = {
writeEnable :: writeSelect :: HNil
} }
traceName(writeEnable)
def apply() = new RegControl
} }
class RegisterFile[T <: Data](tpe: T, regCount: Int, numReadPorts: Int) class RegisterFile[T <: Data](tpe: T, regCount: Int, numReadPorts: Int)
extends Module { extends Module {
require(numReadPorts >= 0) require(numReadPorts >= 0)
val control = IO(new RegControl) val control = IO(RegControl())
val dataAddrWidth = log2Ceil(regCount).W val dataAddrWidth = log2Ceil(regCount).W
val in = IO(new Bundle { val in = IO(new Bundle {
val writeAddr = Input(UInt(dataAddrWidth)) val writeAddr = Input(UInt(dataAddrWidth))
val writeData = Input(Vec(control.WriteSelect.all.length, tpe)) val writeData = Input(Vec(RegControl.WriteSelect.all.length, tpe))
val rs = Input(Vec(numReadPorts, UInt(dataAddrWidth))) val rs = Input(Vec(numReadPorts, UInt(dataAddrWidth)))
}) })
val out = IO(new Bundle { val out = IO(new Bundle {
@ -51,6 +58,90 @@ class RegisterFile[T <: Data](tpe: T, regCount: Int, numReadPorts: Int)
for (port <- 0 until numReadPorts) { for (port <- 0 until numReadPorts) {
out.src(port) := regFile(in.rs(port).asUInt) out.src(port) := regFile(in.rs(port).asUInt)
} }
}
class newRegisterFile(implicit p: Params) extends Module {
val in = IO(new Bundle {
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 src1 = Output(UInt(p.XLEN))
val src2 = Output(UInt(p.XLEN))
})
val control = IO(RegControl())
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
out.src1 := regFile(in.rs1)
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")
)
println(writeEnableMapping)
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.newALU
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 newALU)
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.newPcControlInterface.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 newRegisterFile 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.newProgramCounter
import flow.components.newPcControlInterface
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 newProgramCounter)
// 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(newPcControlInterface())
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(newPcControlInterface())
}
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,11 @@ 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)
) )

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

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

View file

@ -4,44 +4,28 @@ import chisel3._
import chiseltest._ import chiseltest._
import org.scalatest.freespec.AnyFreeSpec import org.scalatest.freespec.AnyFreeSpec
import chiseltest.simulator.WriteVcdAnnotation import chiseltest.simulator.WriteVcdAnnotation
import flow.stages._
import flow.Params
import flow.Flow import flow.Flow
import flow.tests.defaultParams
import flow.stages.messages._
class RV32CPUSpec extends AnyFreeSpec with ChiselScalatestTester { class RV32CPUSpec extends AnyFreeSpec with ChiselScalatestTester {
"MemoryFile" - { "IF" - {
"correctly load" in { implicit val p: Params = defaultParams()
import chisel3.util.{SRAM, SRAMInterface, HexMemoryFile} class TestIF extends Module {
class UserMem extends Module { val IF = Module(new IF)
val io = IO(new SRAMInterface(1024, UInt(32.W), 1, 1, 0)) val io = IO(new Bundle {
val memoryFile = HexMemoryFile("../resource/addi.txt") val out = Output(new IF2ID)
io :<>= SRAM( })
size = 1024, io.out := IF.msgio.out
tpe = UInt(32.W), IF.msgio.out.ready := DontCare
numReadPorts = 1, IF.io.fromRam := DontCare
numWritePorts = 1, IF.io.fromEx := DontCare
numReadwritePorts = 0, }
memoryFile = memoryFile "should compile" in {
) test(new TestIF) { c => }
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

@ -0,0 +1,24 @@
package flow.tests
import chisel3._
import chiseltest._
import org.scalatest.freespec.AnyFreeSpec
import flow.components._
class ProgramCounterSpec extends AnyFreeSpec with ChiselScalatestTester {
implicit val p: flow.Params = defaultParams()
"should compile" in {
test(new newProgramCounter) { c =>
c.clock.step(1)
}
}
"Static next pc" in {
test(new newProgramCounter) { c =>
import flow.components.newPcControlInterface.SrcSelect._
c.control.srcSelect.poke(pStatic)
c.clock.step(1)
c.out.expect(p.resetVector + 4)
}
}
}

View file

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

View file

@ -0,0 +1,70 @@
package flow.tests
import chisel3._
import chiseltest._
import org.scalatest.freespec.AnyFreeSpec
import chiseltest.simulator.WriteVcdAnnotation
import flow.tests.defaultParams
import flow.stages.utils._
import flow.stages.DecoupledMsgIO
import chisel3.util.Decoupled
class StageConnect extends AnyFreeSpec with ChiselScalatestTester {
"should compile" in {
implicit val p: flow.Params = defaultParams().copy(arch = "single")
class stage1 extends Module {
val io = DecoupledMsgIO(out = (new Bundle {
val data = UInt(12.W)
}).S)
io.out.valid := true.B
io.out.bits.data := 1.U
}
class stage2 extends Module {
val io = DecoupledMsgIO(Some(new Bundle {
val data = UInt(12.W)
}))
io.in.ready := true.B
}
class stage3 extends Module {
val wireOut = DecoupledMsgIO(
out = Some(new Bundle {
val data = UInt(12.W)
}),
isIO = false
)
val wireIn = DecoupledMsgIO(
Some(new Bundle {
val data = UInt(12.W)
}),
isIO = false
)
wireOut connect [Nothing] wireIn
wireOut.out.valid := true.B
wireOut.out.bits.data := 1.U
wireIn.in.ready := true.B
}
class stage extends Module {
val s1 = Module(new stage1)
val s2 = Module(new stage2)
s1.io connect [Nothing] s2.io
}
import circt.stage.ChiselStage
println(ChiselStage.emitSystemVerilog(new stage1))
test(new stage) { c =>
println(c)
}
test(new stage3) { c =>
println(c)
}
}
}

View file

@ -4,5 +4,5 @@ import chisel3._
import flow.Params import flow.Params
object defaultParams { object defaultParams {
def apply(): Params = new Params(XLEN = 32.W) def apply(): Params = new Params(XLEN = 32.W, arch = "single")
} }

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,29 @@
inherit flow; inherit flow;
}; };
}; };
devShells.default = pkgs.mkShell.override { stdenv = pkgs.ccacheStdenv; } {
nativeBuildInputs = with pkgs; [
cmake
ninja
flex
bison
nvboard
flow
espresso
bloop
coursier
sbt
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, 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) {
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); } 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())