refactor-npc #4
41 changed files with 2997 additions and 903 deletions
|
@ -5,7 +5,7 @@ jobs:
|
|||
npc-build:
|
||||
strategy:
|
||||
matrix:
|
||||
package: [ "flow", "flow-simlib"]
|
||||
package: [ "flow", "flow-simlib" ]
|
||||
runs-on: nix
|
||||
defaults:
|
||||
run:
|
||||
|
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -6,3 +6,7 @@ difftest/
|
|||
**/result
|
||||
/.pre-commit-config.yaml
|
||||
**/.vscode/
|
||||
*.sock
|
||||
logs/
|
||||
**/.envrc
|
||||
**/.gdbinit
|
||||
|
|
870
flake.lock
870
flake.lock
File diff suppressed because it is too large
Load diff
75
flake.nix
75
flake.nix
|
@ -13,10 +13,23 @@
|
|||
};
|
||||
diffu.url = "git+https://git.xinyang.life/xin/diffu.git";
|
||||
am-kernels.url = "git+https://git.xinyang.life/xin/am-kernels.git?ref=dev";
|
||||
spike-diff.url = "git+https://git.xinyang.life/xin/spike-diff.git";
|
||||
};
|
||||
|
||||
outputs = { self, flake-utils, nixpkgs, nixpkgs-circt162, pre-commit-hooks, nur-xin, diffu, am-kernels }@inputs:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
flake-utils,
|
||||
nixpkgs,
|
||||
nixpkgs-circt162,
|
||||
pre-commit-hooks,
|
||||
nur-xin,
|
||||
diffu,
|
||||
am-kernels,
|
||||
spike-diff,
|
||||
}@inputs:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
|
@ -39,6 +52,8 @@
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
am-kernels-nemu = am-kernels.packages.${system}.rv32Cross.am-kernels-nemu;
|
||||
in
|
||||
{
|
||||
checks = {
|
||||
|
@ -50,7 +65,10 @@
|
|||
cmake-format.enable = true;
|
||||
clang-format = {
|
||||
enable = true;
|
||||
types_or = pkgs.lib.mkForce [ "c" "c++" ];
|
||||
types_or = pkgs.lib.mkForce [
|
||||
"c"
|
||||
"c++"
|
||||
];
|
||||
};
|
||||
scalafmt = {
|
||||
enable = true;
|
||||
|
@ -63,13 +81,19 @@
|
|||
};
|
||||
};
|
||||
|
||||
packages = rec {
|
||||
packages = {
|
||||
abstract-machine = pkgs.callPackage ./abstract-machine { isa = "native"; };
|
||||
nemu = pkgs.callPackage ./nemu { };
|
||||
nemu-lib = pkgs.callPackage ./nemu { };
|
||||
nemu = pkgs.callPackage ./nemu { am-kernels = am-kernels-nemu; };
|
||||
nemu-lib = pkgs.callPackage ./nemu { defconfig = "libdefconfig"; };
|
||||
|
||||
rv32Cross = rec {
|
||||
abstract-machine = rv32CrossConfig.callPackage ./abstract-machine { isa = "riscv"; platform = [ "nemu" "npc" ]; };
|
||||
rv32Cross = {
|
||||
abstract-machine = rv32CrossConfig.callPackage ./abstract-machine {
|
||||
isa = "riscv";
|
||||
platform = [
|
||||
"nemu"
|
||||
"npc"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -92,7 +116,7 @@
|
|||
self.packages.${system}.nemu
|
||||
];
|
||||
NEMU_HOME = "/home/xin/repo/ysyx-workbench/nemu";
|
||||
# NEMU_IMAGES_PATH = self.packages.${system}.rv32Cross.am-kernels-nemu + "/share/am-kernels";
|
||||
NEMU_IMAGES_PATH = am-kernels.packages.${system}.rv32Cross.am-kernels-nemu + "/share/am-kernels";
|
||||
};
|
||||
|
||||
devShells.npc = pkgs.mkShell.override { stdenv = pkgs.ccacheStdenv; } {
|
||||
|
@ -125,23 +149,36 @@
|
|||
verilator
|
||||
];
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
spdlog
|
||||
nvboard
|
||||
openssl
|
||||
libllvm
|
||||
libxml2
|
||||
readline
|
||||
mini-gdbstub
|
||||
] ++ self.checks.${system}.pre-commit-check.enabledPackages;
|
||||
buildInputs =
|
||||
with pkgs;
|
||||
[
|
||||
spdlog
|
||||
nvboard
|
||||
openssl
|
||||
libllvm
|
||||
libxml2
|
||||
readline
|
||||
mini-gdbstub
|
||||
]
|
||||
++ self.checks.${system}.pre-commit-check.enabledPackages;
|
||||
};
|
||||
|
||||
devShells.difftest = pkgs.mkShell {
|
||||
devShells.default = pkgs.mkShell {
|
||||
inherit (self.checks.${system}.pre-commit-check) shellHook;
|
||||
buildInputs = self.checks.${system}.pre-commit-check.enabledPackages;
|
||||
|
||||
packages = [
|
||||
diffu.packages.${system}.default
|
||||
am-kernels.packages.${system}.rv32Cross.am-kernels-npc
|
||||
self.packages.${system}.nemu-lib
|
||||
spike-diff.packages.${system}.default
|
||||
pkgs.gef
|
||||
pkgs.gtkwave
|
||||
];
|
||||
|
||||
DIFFU_IMAGES_PATH = "${am-kernels.packages.${system}.rv32Cross.am-kernels-npc}";
|
||||
NEMU_SO = "${self.packages.${system}.nemu-lib}/lib/riscv32-nemu-interpreter-so";
|
||||
NPC_SO = "/home/xin/repo/ysyx-workbench/npc/build/csrc/Flow/libFlow.so";
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1 +1 @@
|
|||
use flake ".?submodules=1#npc"
|
||||
use flake
|
||||
|
|
2
npc/.gitignore
vendored
2
npc/.gitignore
vendored
|
@ -17,3 +17,5 @@ hs_err_pid*
|
|||
compile_commands.json
|
||||
|
||||
*.vcd
|
||||
*.gtkw
|
||||
*.sock
|
||||
|
|
28
npc/CMakePresets.json
Normal file
28
npc/CMakePresets.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -32,7 +32,9 @@ else()
|
|||
set(CHISEL_TEST_TARGET sbt_${TOPMODULE}_test)
|
||||
add_custom_command(
|
||||
OUTPUT ${CHISEL_OUTPUT_TOPMODULE} ${CHISEL_OUTPUT_VERILATOR_CONF}
|
||||
COMMAND sbt "run ${CHISEL_EMIT_ARGS}"
|
||||
# Try to use native sbt to increase performance when possible
|
||||
COMMAND ${CMAKE_COMMAND} -E env SBT_NATIVE_CLIENT=true sbt
|
||||
"run ${CHISEL_EMIT_ARGS}"
|
||||
WORKING_DIRECTORY ${SCALA_CORE}
|
||||
DEPENDS ${CHISEL_DEPENDENCY}
|
||||
VERBATIM
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ThisBuild / scalaVersion := "2.13.12"
|
||||
ThisBuild / version := "0.1.0"
|
||||
ThisBuild / version := "0.1.1"
|
||||
|
||||
val chiselVersion = "6.2.0"
|
||||
val chiselVersion = "6.5.0"
|
||||
val circeVersion = "0.14.1"
|
||||
|
||||
lazy val root = (project in file("."))
|
||||
|
|
|
@ -1 +1 @@
|
|||
sbt.version=1.9.9
|
||||
sbt.version=1.10.1
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import "DPI-C" function int pmem_read(input int addr);
|
||||
import "DPI-C" function int pmem_read(input int addr, input byte rmask);
|
||||
import "DPI-C" function void pmem_write(input int waddr, input int wdata, input byte wmask);
|
||||
|
||||
module RamDpi (
|
||||
|
@ -9,6 +9,7 @@ module RamDpi (
|
|||
input [31:0] writeAddr,
|
||||
input [31:0] writeData,
|
||||
input [3:0] writeMask,
|
||||
input [3:0] readMask,
|
||||
input reg [31:0] readAddr,
|
||||
output reg [31:0] readData,
|
||||
input reg [31:0] pc,
|
||||
|
@ -16,7 +17,7 @@ module RamDpi (
|
|||
);
|
||||
always @(posedge clock) begin
|
||||
if (valid) begin // 有读写请求时
|
||||
readData = pmem_read(readAddr);
|
||||
readData = pmem_read(readAddr, { 4'h0, readMask });
|
||||
if (writeEnable) begin // 有写请求时
|
||||
pmem_write(writeAddr, writeData, { 4'h0, writeMask });
|
||||
end
|
||||
|
@ -25,5 +26,5 @@ module RamDpi (
|
|||
readData = 32'h80000000;
|
||||
end
|
||||
end
|
||||
assign inst = pmem_read(pc);
|
||||
assign inst = pmem_read(pc, 8'hF);
|
||||
endmodule
|
||||
|
|
|
@ -3,11 +3,14 @@ package flow.components
|
|||
import chisel3._
|
||||
import chisel3.util._
|
||||
import shapeless.{HNil, ::}
|
||||
import flow.Params
|
||||
import flow.components.util._
|
||||
import chisel3.util.experimental.decode.{decoder, TruthTable}
|
||||
|
||||
class ALUControlInterface extends Bundle {
|
||||
object ALUControlInterface {
|
||||
object OpSelect extends ChiselEnum {
|
||||
val aOpAdd, aOpSub, aOpNot, aOpAnd, aOpOr, aOpXor, aOpSlt, aOpSltu, aOpSll,
|
||||
aOpSrl, aOpSra = Value
|
||||
val aOpAdd, aOpSub, aOpAnd, aOpOr, aOpXor, aOpSlt, aOpSltu, aOpSll, aOpSrl,
|
||||
aOpSra = Value
|
||||
}
|
||||
object SrcASelect extends ChiselEnum {
|
||||
val aSrcARs1, aSrcAPc, aSrcAZero = Value
|
||||
|
@ -15,31 +18,34 @@ class ALUControlInterface extends Bundle {
|
|||
object SrcBSelect extends ChiselEnum {
|
||||
val aSrcBRs2, aSrcBImmI, aSrcBImmJ, aSrcBImmS, aSrcBImmU = Value
|
||||
}
|
||||
val op = Input(OpSelect())
|
||||
val srcASelect = Input(SrcASelect())
|
||||
val srcBSelect = Input(SrcBSelect())
|
||||
val signExt = Input(Bool())
|
||||
class ALUControlInterface extends Bundle {
|
||||
val op = Input(OpSelect())
|
||||
val srcASelect = Input(SrcASelect())
|
||||
val srcBSelect = Input(SrcBSelect())
|
||||
val signExt = Input(Bool())
|
||||
|
||||
def ctrlBindPorts = {
|
||||
op :: srcASelect :: srcBSelect :: signExt :: HNil
|
||||
def ctrlBindPorts = {
|
||||
op :: srcASelect :: srcBSelect :: signExt :: HNil
|
||||
}
|
||||
}
|
||||
|
||||
def apply() = new ALUControlInterface;
|
||||
}
|
||||
|
||||
class ALU[T <: UInt](tpe: T) extends Module {
|
||||
val control = IO(new ALUControlInterface)
|
||||
class ALU(implicit p: Params) extends Module {
|
||||
import ALUControlInterface._
|
||||
val control = IO(ALUControlInterface())
|
||||
val in = IO(new Bundle {
|
||||
val a = Input(Vec(control.SrcASelect.all.length, tpe))
|
||||
val b = Input(Vec(control.SrcBSelect.all.length, tpe))
|
||||
val a = Input(Vec(SrcASelect.all.length, UInt(p.XLEN)))
|
||||
val b = Input(Vec(SrcBSelect.all.length, UInt(p.XLEN)))
|
||||
})
|
||||
val out = IO(new Bundle {
|
||||
val eq = Output(Bool())
|
||||
val result = Output(tpe)
|
||||
val result = Output(UInt(p.XLEN))
|
||||
})
|
||||
|
||||
val a = in.a(control.srcASelect.asUInt)
|
||||
val b = in.b(control.srcBSelect.asUInt)
|
||||
|
||||
// val adder_b = (Fill(tpe.getWidth, io.op(0)) ^ io.b) + io.op(0) // take (-b) if sub
|
||||
val add = a + b
|
||||
val sub = a - b
|
||||
val and = a & b
|
||||
|
@ -48,18 +54,16 @@ class ALU[T <: UInt](tpe: T) extends Module {
|
|||
val xor = a ^ b
|
||||
val slt = a.asSInt < b.asSInt
|
||||
val sltu = a < b
|
||||
val sll = a << b(log2Ceil(tpe.getWidth), 0)
|
||||
val srl = a >> b(log2Ceil(tpe.getWidth), 0)
|
||||
val sra = a.asSInt >> b(log2Ceil(tpe.getWidth), 0)
|
||||
out.eq := a === b
|
||||
val sll = a << b(5, 0)
|
||||
val srl = a >> b(5, 0)
|
||||
val sra = a.asSInt >> b(5, 0)
|
||||
|
||||
import control.OpSelect._
|
||||
import OpSelect._
|
||||
|
||||
out.result := MuxLookup(control.op, 0.U)(
|
||||
Seq(
|
||||
aOpAdd -> add,
|
||||
aOpSub -> sub,
|
||||
aOpNot -> not,
|
||||
aOpAnd -> and,
|
||||
aOpOr -> or,
|
||||
aOpXor -> xor,
|
||||
|
@ -72,8 +76,92 @@ class ALU[T <: UInt](tpe: T) extends Module {
|
|||
)
|
||||
}
|
||||
|
||||
object ALU {
|
||||
def apply[T <: UInt](tpe: T): ALU[T] = {
|
||||
Module(new ALU(tpe))
|
||||
}
|
||||
class AluController(implicit p: Params) extends Module {
|
||||
val out = IO(Flipped(ALUControlInterface()))
|
||||
val in = IO(new Bundle {
|
||||
val inst = Input(UInt(p.XLEN))
|
||||
})
|
||||
import flow.components.RV32Inst._
|
||||
import ALUControlInterface._
|
||||
// OpSelect
|
||||
// format: off
|
||||
val aOpAdd = Array(
|
||||
lui, auipc,
|
||||
jal, jalr,
|
||||
lb, lbu, lh, lhu, lw,
|
||||
sb, sh, sw,
|
||||
addi, add
|
||||
).map(_ -> OpSelect.aOpAdd.BP)
|
||||
// format: on
|
||||
|
||||
val aOpAnd = Array(and, andi).map(_ -> OpSelect.aOpAnd.BP)
|
||||
|
||||
val aOpSub = Array(sub).map(_ -> OpSelect.aOpSub.BP)
|
||||
|
||||
val aOpOr = Array(ori, or).map(_ -> OpSelect.aOpOr.BP)
|
||||
|
||||
val aOpXor = Array(xori, xor).map(_ -> OpSelect.aOpXor.BP)
|
||||
|
||||
val aOpSlt = Array(beq, bne, blt, bge, slti, slt).map(_ -> OpSelect.aOpSlt.BP)
|
||||
|
||||
val aOpSltu = Array(sltiu, sltu, bltu, bgeu).map(_ -> OpSelect.aOpSltu.BP)
|
||||
|
||||
val aOpSll = Array(slli, sll).map(_ -> OpSelect.aOpSll.BP)
|
||||
|
||||
val aOpSrl = Array(srli, srl).map(_ -> OpSelect.aOpSrl.BP)
|
||||
|
||||
val aOpSra = Array(
|
||||
sra,
|
||||
srai
|
||||
).map(_ -> OpSelect.aOpSra.BP)
|
||||
|
||||
val OpCodeMapping = TruthTable(
|
||||
aOpAdd ++ aOpAnd ++ aOpSub ++ aOpOr ++ aOpXor ++ aOpSlt ++ aOpSltu ++ aOpSll ++ aOpSrl ++ aOpSra,
|
||||
BitPat.dontCare(OpSelect.getWidth)
|
||||
)
|
||||
|
||||
val aSrcAZero = Array(
|
||||
lui
|
||||
).map(_ -> SrcASelect.aSrcAZero.BP)
|
||||
|
||||
val aSrcAPc = Array(
|
||||
auipc,
|
||||
jal
|
||||
).map(_ -> SrcASelect.aSrcAPc.BP)
|
||||
|
||||
val srcAMapping = TruthTable(
|
||||
aSrcAZero ++ aSrcAPc,
|
||||
SrcASelect.aSrcARs1.BP
|
||||
)
|
||||
val aSrcBImmS = Array(sb, sh, sw).map(_ -> SrcBSelect.aSrcBImmS.BP)
|
||||
|
||||
val aSrcBImmU = Array(lui, auipc).map(_ -> SrcBSelect.aSrcBImmU.BP)
|
||||
|
||||
val aSrcBImmJ = Array(jal).map(_ -> SrcBSelect.aSrcBImmJ.BP)
|
||||
|
||||
// format: off
|
||||
val aSrcBImmI = Array(
|
||||
jalr,
|
||||
lb, lbu, lh, lhu, lw,
|
||||
addi, slti, sltiu, xori, ori, andi, slli, srli, srai
|
||||
).map(_ -> SrcBSelect.aSrcBImmI.BP)
|
||||
// format: on
|
||||
|
||||
val srcBMapping = TruthTable(
|
||||
aSrcBImmI ++ aSrcBImmJ ++ aSrcBImmU ++ aSrcBImmS,
|
||||
SrcBSelect.aSrcBRs2.BP
|
||||
)
|
||||
|
||||
val doSignExt = Array(blt, bge, slt, slti).map(_ -> BitPat(true.B))
|
||||
|
||||
val noSignExt = Array(lui, auipc, jal, jalr, bltu, bgeu, sltiu, sltu).map(
|
||||
_ -> BitPat(false.B)
|
||||
)
|
||||
|
||||
val signExtMapping = TruthTable(doSignExt ++ noSignExt, BitPat.dontCare(1))
|
||||
|
||||
out.op := OpSelect.safe(decoder(in.inst, OpCodeMapping))._1
|
||||
out.srcASelect := SrcASelect.safe(decoder(in.inst, srcAMapping))._1
|
||||
out.srcBSelect := SrcBSelect.safe(decoder(in.inst, srcBMapping))._1
|
||||
out.signExt := decoder(in.inst, signExtMapping)
|
||||
}
|
||||
|
|
|
@ -11,25 +11,28 @@ import flow.Params
|
|||
import chisel3.util.BitPat
|
||||
import chisel3.util.Fill
|
||||
|
||||
class CSRControlInterface extends Bundle {
|
||||
object CSRControlInterface extends Bundle {
|
||||
object csrRead extends ChiselEnum {
|
||||
val csrReadDisabled, csrReadEnabled = Value
|
||||
}
|
||||
|
||||
object csrWrite extends ChiselEnum {
|
||||
val csrWriteDisabled, csrWriteEnabled = Value
|
||||
val csrWriteDisabled, csrWriteData, csrSetBit, csrClearBit = Value
|
||||
}
|
||||
|
||||
val readEnable = Input(csrRead())
|
||||
val writeEnable = Input(csrWrite())
|
||||
class CSRControlInterface extends Bundle {
|
||||
val readEnable = Input(csrRead())
|
||||
val writeEnable = Input(csrWrite())
|
||||
|
||||
def ctrlBindPorts = {
|
||||
readEnable :: writeEnable :: HNil
|
||||
def ctrlBindPorts = {
|
||||
readEnable :: writeEnable :: HNil
|
||||
}
|
||||
}
|
||||
def apply() = new CSRControlInterface
|
||||
}
|
||||
|
||||
class CSRCore(implicit val p: Params) extends Module {
|
||||
val control = IO(new CSRControlInterface)
|
||||
val control = IO(CSRControlInterface())
|
||||
|
||||
val in = IO(new Bundle {
|
||||
val csrAddr = Input(UInt(p.csrAddrWidth))
|
||||
|
|
|
@ -5,38 +5,81 @@ import chisel3.experimental.noPrefix
|
|||
import chisel3.util.HasBlackBoxPath
|
||||
import chisel3.util.HasBlackBoxResource
|
||||
import chisel3.util.log2Ceil
|
||||
import chisel3.util.BitPat
|
||||
import flow.components
|
||||
import flow.Params
|
||||
import shapeless.::
|
||||
import shapeless.HNil
|
||||
|
||||
import scala.collection.SeqMap
|
||||
import chisel3.util.experimental.decode.{decoder, TruthTable}
|
||||
import flow.components.util._
|
||||
import flow.components.RV32Inst._
|
||||
|
||||
class RamControlInterface(addrWidth: Int) extends Bundle {
|
||||
val valid = Input(Bool())
|
||||
val writeMask = Input(UInt((addrWidth / 8).W))
|
||||
val writeEnable = Input(Bool())
|
||||
def ctrlBindPorts = {
|
||||
valid :: writeMask :: writeEnable :: HNil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* FIXME: Extends here might not be the best solution.
|
||||
* We need a way to merge two bundles together
|
||||
*/
|
||||
class RamInterface[T <: Data](tpe: T, addrWidth: Int)
|
||||
extends RamControlInterface(addrWidth) {
|
||||
val clock = Input(Clock())
|
||||
val reset = Input(Reset())
|
||||
val writeAddr = Input(UInt(addrWidth.W))
|
||||
val writeData = Input(tpe)
|
||||
val readAddr = Input(UInt(addrWidth.W))
|
||||
val readData = Output(tpe)
|
||||
val pc = Input(UInt(addrWidth.W))
|
||||
val inst = Output(tpe)
|
||||
}
|
||||
|
||||
class RamDpi extends BlackBox with HasBlackBoxResource {
|
||||
val io = IO((new RamInterface(UInt(32.W), 32)))
|
||||
class RamDpi(implicit p: Params) extends BlackBox with HasBlackBoxResource {
|
||||
val io = IO(new DpiRamInterface)
|
||||
addResource("/RamDpi.v")
|
||||
}
|
||||
|
||||
class DpiRamControlInterface(implicit p: Params) extends Bundle {
|
||||
val valid = Input(Bool())
|
||||
val readMask = Input(UInt((p.XLEN.get / 8).W))
|
||||
val writeMask = Input(UInt((p.XLEN.get / 8).W))
|
||||
val writeEnable = Input(Bool())
|
||||
}
|
||||
|
||||
class DpiRamInterface(implicit p: Params) extends DpiRamControlInterface {
|
||||
val clock = Input(Clock())
|
||||
val reset = Input(Reset())
|
||||
|
||||
// Data
|
||||
val writeAddr = Input(UInt(p.XLEN))
|
||||
val writeData = Input(UInt(p.XLEN))
|
||||
val readAddr = Input(UInt(p.XLEN))
|
||||
val readData = Output(UInt(p.XLEN))
|
||||
val pc = Input(UInt(p.XLEN))
|
||||
val inst = Output(UInt(p.XLEN))
|
||||
}
|
||||
|
||||
class RamController(implicit p: Params) extends Module {
|
||||
val in = new Bundle {
|
||||
val inst = IO(Flipped(UInt(p.XLEN)))
|
||||
}
|
||||
val out = IO(Flipped(new DpiRamControlInterface))
|
||||
private val validMapping = TruthTable(
|
||||
Array(sb, sh, sw, lb, lbu, lh, lhu, lw).map(_ -> BitPat("b1")),
|
||||
BitPat("b0")
|
||||
)
|
||||
|
||||
private val writeMaskMapping = TruthTable(
|
||||
Array(
|
||||
sb -> BitPat("b0001"),
|
||||
sh -> BitPat("b0011"),
|
||||
sw -> BitPat("b1111")
|
||||
),
|
||||
BitPat.dontCare(out.writeMask.getWidth)
|
||||
)
|
||||
|
||||
private val readMaskMapping = TruthTable(
|
||||
Array(
|
||||
lb -> BitPat("b0001"),
|
||||
lh -> BitPat("b0011"),
|
||||
lw -> BitPat("b1111")
|
||||
),
|
||||
BitPat.dontCare(out.readMask.getWidth)
|
||||
)
|
||||
|
||||
private val writeEnableMapping = TruthTable(
|
||||
Array(
|
||||
sb -> BitPat("b1"),
|
||||
sh -> BitPat("b1"),
|
||||
sw -> BitPat("b1")
|
||||
),
|
||||
BitPat("b0")
|
||||
)
|
||||
|
||||
out.valid := decoder(in.inst, validMapping)
|
||||
out.writeEnable := decoder(in.inst, writeEnableMapping)
|
||||
out.writeMask := decoder(in.inst, writeMaskMapping)
|
||||
out.readMask := decoder(in.inst, readMaskMapping)
|
||||
}
|
||||
|
|
|
@ -1,48 +1,76 @@
|
|||
package flow.components
|
||||
import chisel3._
|
||||
import chisel3.util.experimental.decode.{decoder, TruthTable}
|
||||
import chisel3.util.{Valid, log2Ceil}
|
||||
import chisel3.util.MuxLookup
|
||||
import chisel3.util.BitPat
|
||||
import shapeless.{HNil, ::}
|
||||
import shapeless.HList
|
||||
import flow.Params
|
||||
import RV32InstSubfields._
|
||||
import flow.components.util._
|
||||
|
||||
class PcControlInterface extends Bundle {
|
||||
object PcControlInterface {
|
||||
object SrcSelect extends ChiselEnum {
|
||||
val pStaticNpc, pExeOut = Value
|
||||
val pStatic, pJmp, pBR = Value
|
||||
}
|
||||
|
||||
val useImmB = Input(Bool())
|
||||
val srcSelect = Input(SrcSelect())
|
||||
class newPcControlInterface extends Bundle {
|
||||
val srcSelect = Input(SrcSelect())
|
||||
|
||||
def ctrlBindPorts = {
|
||||
useImmB :: srcSelect :: HNil
|
||||
def ctrlBindPorts = {
|
||||
srcSelect :: HNil
|
||||
}
|
||||
}
|
||||
def apply() = new newPcControlInterface;
|
||||
}
|
||||
|
||||
class ProgramCounter[T <: UInt](tpe: T) extends Module {
|
||||
|
||||
val control = IO(new PcControlInterface)
|
||||
class ProgramCounter(implicit p: Params) extends Module {
|
||||
val control = IO(PcControlInterface())
|
||||
import PcControlInterface.SrcSelect._
|
||||
val in = IO(new Bundle {
|
||||
val immB = Input(tpe)
|
||||
val exeOut = Input(tpe)
|
||||
val brOffset = Input(UInt(p.XLEN))
|
||||
val jAddr = Input(UInt(p.XLEN))
|
||||
})
|
||||
val out = IO(Output(tpe))
|
||||
val out = IO(Output(UInt(p.XLEN)))
|
||||
|
||||
private val pc_reg = RegInit(0x80000000L.U)
|
||||
private val pcReg = RegInit(p.resetVector.U)
|
||||
out := pcReg
|
||||
|
||||
// pc := in.pcSrcs(control.srcSelect.asUInt)
|
||||
import control.SrcSelect._
|
||||
when(control.useImmB === true.B) {
|
||||
pc_reg := pc_reg + in.immB
|
||||
}.elsewhen(control.srcSelect === pStaticNpc) {
|
||||
pc_reg := pc_reg + 4.U
|
||||
}.elsewhen(control.srcSelect === pExeOut) {
|
||||
pc_reg := in.exeOut
|
||||
}
|
||||
out := pc_reg
|
||||
private val npc = MuxLookup(control.srcSelect, 4.U)(
|
||||
Seq(
|
||||
pStatic -> (pcReg + 4.U),
|
||||
pJmp -> in.jAddr,
|
||||
pBR -> (pcReg + in.brOffset)
|
||||
)
|
||||
)
|
||||
|
||||
pcReg := npc
|
||||
}
|
||||
|
||||
object ProgramCounter {
|
||||
def apply[T <: UInt](tpe: T): ProgramCounter[T] = {
|
||||
val pc = Module(new ProgramCounter(tpe))
|
||||
pc
|
||||
}
|
||||
class PcController(implicit p: Params) extends Module {
|
||||
val in = IO(new Bundle {
|
||||
val inst = Input(UInt(p.instWidth))
|
||||
})
|
||||
|
||||
val out = IO(Flipped(PcControlInterface()))
|
||||
|
||||
import RV32Inst._
|
||||
import PcControlInterface.SrcSelect._
|
||||
private val _jmpMapping = Array(jal, jalr).map(_ -> pJmp.BP)
|
||||
private val _brMapping =
|
||||
Array(beq, bne, blt, bge, bltu, bgeu).map(_ -> pBR.BP)
|
||||
|
||||
val mapping = TruthTable(
|
||||
_jmpMapping ++ _brMapping,
|
||||
pStatic.BP
|
||||
)
|
||||
|
||||
out.srcSelect := PcControlInterface.SrcSelect
|
||||
.safe(
|
||||
(
|
||||
decoder(in.inst, mapping)
|
||||
)
|
||||
)
|
||||
._1
|
||||
}
|
||||
|
|
|
@ -4,53 +4,109 @@ import chisel3._
|
|||
import chisel3.util.log2Ceil
|
||||
import chisel3.util.UIntToOH
|
||||
import chisel3.util.MuxLookup
|
||||
import chisel3.util.BitPat
|
||||
import chisel3.util.experimental.decode.{decoder, TruthTable}
|
||||
import chisel3.experimental.Trace._
|
||||
import shapeless.{HList, HNil, ::}
|
||||
|
||||
class RegControl extends Bundle {
|
||||
import flow.Params
|
||||
import flow.components.util._
|
||||
import flow.components.RV32Inst._
|
||||
object RegControl {
|
||||
object WriteSelect extends ChiselEnum {
|
||||
val rAluOut, rMemOut, rNpc = Value
|
||||
}
|
||||
class RegControl extends Bundle {
|
||||
val writeEnable = Input(Bool())
|
||||
val writeSelect = Input(WriteSelect())
|
||||
|
||||
val writeEnable = Input(Bool())
|
||||
val writeSelect = Input(WriteSelect())
|
||||
|
||||
def ctrlBindPorts = {
|
||||
writeEnable :: writeSelect :: HNil
|
||||
def ctrlBindPorts = {
|
||||
writeEnable :: writeSelect :: HNil
|
||||
}
|
||||
}
|
||||
traceName(writeEnable)
|
||||
|
||||
def apply() = new RegControl
|
||||
}
|
||||
|
||||
class RegisterFile[T <: Data](tpe: T, regCount: Int, numReadPorts: Int)
|
||||
extends Module {
|
||||
require(numReadPorts >= 0)
|
||||
val control = IO(new RegControl)
|
||||
val dataAddrWidth = log2Ceil(regCount).W
|
||||
class RegisterFile(implicit p: Params) extends Module {
|
||||
val in = IO(new Bundle {
|
||||
val writeAddr = Input(UInt(dataAddrWidth))
|
||||
val writeData = Input(Vec(control.WriteSelect.all.length, tpe))
|
||||
val rs = Input(Vec(numReadPorts, UInt(dataAddrWidth)))
|
||||
val rd = Input(UInt(p.regsAddrWidth))
|
||||
val writeData = Input(Vec(RegControl.WriteSelect.all.length, UInt(p.XLEN)))
|
||||
val rs1 = Input(UInt(p.regsAddrWidth))
|
||||
val rs2 = Input(UInt(p.regsAddrWidth))
|
||||
})
|
||||
|
||||
val out = IO(new Bundle {
|
||||
val src = Output(Vec(numReadPorts, tpe))
|
||||
val src1 = Output(UInt(p.XLEN))
|
||||
val src2 = Output(UInt(p.XLEN))
|
||||
})
|
||||
|
||||
val regResetValue = 0.U(tpe.getWidth.W)
|
||||
val regFile = RegInit(VecInit(Seq.fill(regCount)(regResetValue)))
|
||||
val writeAddrOH = UIntToOH(in.writeAddr)
|
||||
val control = IO(RegControl())
|
||||
|
||||
for ((reg, i) <- regFile.zipWithIndex.tail) {
|
||||
reg := Mux(
|
||||
writeAddrOH(i.U(log2Ceil(regCount).W)) && control.writeEnable,
|
||||
in.writeData(control.writeSelect.asUInt),
|
||||
reg
|
||||
)
|
||||
}
|
||||
val regFile = RegInit(
|
||||
VecInit(Seq.fill(p.regsCount)(p.regsResetValue.U(p.XLEN)))
|
||||
)
|
||||
|
||||
// 0.U -> writeData(0), ...
|
||||
val writeDataMux =
|
||||
MuxBindCtrlSrc(control.writeSelect, in.writeData, regFile(in.rd))
|
||||
|
||||
regFile(in.rd) := Mux(
|
||||
control.writeEnable,
|
||||
writeDataMux,
|
||||
regFile(in.rd)
|
||||
)
|
||||
regFile(0) := 0.U
|
||||
|
||||
for (port <- 0 until numReadPorts) {
|
||||
out.src(port) := regFile(in.rs(port).asUInt)
|
||||
}
|
||||
out.src1 := regFile(in.rs1)
|
||||
out.src2 := regFile(in.rs2)
|
||||
traceName(regFile)
|
||||
dontTouch(regFile)
|
||||
}
|
||||
|
||||
class RegisterFileController(implicit p: Params) extends Module {
|
||||
val in = IO(new Bundle {
|
||||
val inst = Input(UInt(p.XLEN))
|
||||
})
|
||||
|
||||
val out = IO(Flipped(RegControl()))
|
||||
|
||||
// format: off
|
||||
import RegControl._
|
||||
private val aluOutMapping =
|
||||
Array(
|
||||
lui, auipc,
|
||||
addi, slti, sltiu, xori, ori, andi, slli, srli, srai,
|
||||
add, sub, sll, slt, sltu, xor, srl, sra, or, and,
|
||||
).map(_ -> WriteSelect.rAluOut.BP)
|
||||
|
||||
private val memOutMapping =
|
||||
Array(
|
||||
lb, lbu, lh, lhu, lw
|
||||
).map(_ -> WriteSelect.rMemOut.BP)
|
||||
|
||||
private val npcMapping =
|
||||
Array(
|
||||
jal, jalr
|
||||
).map(_ -> WriteSelect.rNpc.BP)
|
||||
// format: on
|
||||
|
||||
private val writeSelectMapping = TruthTable(
|
||||
aluOutMapping ++ memOutMapping ++ npcMapping,
|
||||
BitPat.dontCare(out.writeSelect.getWidth)
|
||||
)
|
||||
|
||||
// enable write if instruction belongs to any mapping above
|
||||
private val writeEnableMapping = TruthTable(
|
||||
(aluOutMapping ++ memOutMapping ++ npcMapping).map(x =>
|
||||
(x._1 -> BitPat("b1"))
|
||||
),
|
||||
BitPat("b0")
|
||||
)
|
||||
|
||||
out.writeSelect := RegControl.WriteSelect
|
||||
.safe(
|
||||
decoder(in.inst, writeSelectMapping)
|
||||
)
|
||||
._1
|
||||
out.writeEnable := decoder(in.inst, writeEnableMapping)
|
||||
}
|
||||
|
|
28
npc/core/src/main/scala/components/util.scala
Normal file
28
npc/core/src/main/scala/components/util.scala
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
74
npc/core/src/main/scala/stages/Base.scala
Normal file
74
npc/core/src/main/scala/stages/Base.scala
Normal 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)
|
||||
}
|
||||
}
|
75
npc/core/src/main/scala/stages/EX.scala
Normal file
75
npc/core/src/main/scala/stages/EX.scala
Normal 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
|
||||
}
|
64
npc/core/src/main/scala/stages/ID.scala
Normal file
64
npc/core/src/main/scala/stages/ID.scala
Normal 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
|
||||
}
|
46
npc/core/src/main/scala/stages/IF.scala
Normal file
46
npc/core/src/main/scala/stages/IF.scala
Normal 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
|
||||
}
|
53
npc/core/src/main/scala/stages/LS.scala
Normal file
53
npc/core/src/main/scala/stages/LS.scala
Normal 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
|
||||
}
|
73
npc/core/src/main/scala/stages/Messages.scala
Normal file
73
npc/core/src/main/scala/stages/Messages.scala
Normal 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)
|
||||
}
|
32
npc/core/src/main/scala/stages/WB.scala
Normal file
32
npc/core/src/main/scala/stages/WB.scala
Normal 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)
|
||||
}
|
|
@ -18,6 +18,20 @@ import io.circe.generic.JsonCodec
|
|||
import chisel3._
|
||||
case class Params(
|
||||
XLEN: Width,
|
||||
instWidth: Width = 32.W,
|
||||
regsCount: Int = 32,
|
||||
regsAddrWidth: Width = 5.W,
|
||||
regsResetValue: BigInt = 0L,
|
||||
arch: String,
|
||||
csrAddrWidth: Width = 12.W
|
||||
csrAddrWidth: Width = 12.W,
|
||||
resetVector: BigInt = BigInt(0x80000000L),
|
||||
csrNameToAddr: Map[String, Int] = Map(
|
||||
"mstatus" -> 0x300,
|
||||
"mtvec" -> 0x305,
|
||||
"mie" -> 0x304,
|
||||
"mepc" -> 0x341,
|
||||
"mcause" -> 0x342,
|
||||
"mtval" -> 0x343,
|
||||
"mip" -> 0x344
|
||||
)
|
||||
)
|
||||
|
|
|
@ -13,454 +13,35 @@ import chisel3.util.{BinaryMemoryFile, HexMemoryFile}
|
|||
import chisel3.experimental.Trace
|
||||
import scala.collection.IndexedSeqView
|
||||
import shapeless.Poly1
|
||||
import flow.components.RamControlInterface
|
||||
import flow.components.DpiRamControlInterface
|
||||
import flow.components.RV32Inst
|
||||
import flow.components.RV32InstSubfields._
|
||||
import flow.components.util._
|
||||
|
||||
import flow.components.{RegControl, PcControlInterface, ALUControlInterface}
|
||||
class Control(width: Int) extends RawModule {
|
||||
// Helpers
|
||||
class WrapList[T](vl: T) { type Type = T; val v = vl }
|
||||
object wrap extends Poly1 {
|
||||
implicit def default[A] = at[A](Right(_).withLeft[Int])
|
||||
}
|
||||
def lit(x: Element) = { x.litValue.toInt }
|
||||
def toBits(t: dst.Type): BitPat = {
|
||||
val list = t.toList
|
||||
list
|
||||
.map(e =>
|
||||
e match {
|
||||
case Right(x) => BitPat(lit(x).U(x.getWidth.W))
|
||||
case Left(x) => BitPat.dontCare(x)
|
||||
}
|
||||
)
|
||||
.reduceLeft(_ ## _)
|
||||
}
|
||||
val r = Right
|
||||
def l[T <: Any](x: T) = x match {
|
||||
case x: ChiselEnum => Left(log2Ceil(x.all.length))
|
||||
case x: Data => Left(x.getWidth)
|
||||
case _ => throw new IllegalArgumentException
|
||||
}
|
||||
|
||||
val inst = IO(Input(UInt(width.W)))
|
||||
import flow.stages._
|
||||
import chisel3.experimental.annotate
|
||||
import chisel3.experimental.ChiselAnnotation
|
||||
|
||||
val reg = IO(Flipped(new RegControl))
|
||||
val pc = IO(Flipped(new PcControlInterface))
|
||||
val alu = IO(Flipped(new ALUControlInterface))
|
||||
val ram = IO(Flipped(new RamControlInterface(32)))
|
||||
import chisel3.Data
|
||||
import chisel3.experimental.{annotate, requireIsAnnotatable, ChiselAnnotation}
|
||||
|
||||
val dst = new WrapList(
|
||||
(reg.ctrlBindPorts ++
|
||||
pc.ctrlBindPorts ++
|
||||
alu.ctrlBindPorts ++
|
||||
ram.ctrlBindPorts).map(wrap)
|
||||
)
|
||||
|
||||
val dstList = dst.v.toList
|
||||
val controlWidth = dstList.map(_.toOption.get.getWidth).reduce(_ + _)
|
||||
val reversePrefixSum = dstList.scanLeft(0)(_ + _.toOption.get.getWidth)
|
||||
val sliceIndex = reversePrefixSum.map(controlWidth - _)
|
||||
val slices = sliceIndex.map(_ - 1).zip(sliceIndex.tail)
|
||||
|
||||
import reg.WriteSelect._
|
||||
import reg._
|
||||
import pc.SrcSelect._
|
||||
import pc._
|
||||
import alu.OpSelect._
|
||||
import alu.SrcASelect._
|
||||
import alu.SrcBSelect._
|
||||
import pc._
|
||||
import RV32Inst._
|
||||
// format: off
|
||||
val ControlMapping: Array[(BitPat, dst.Type)] = Array(
|
||||
// Regs | writeEnable :: writeSelect :: HNil
|
||||
// PC | useImmB :: srcSelect :: HNil
|
||||
// Exe | op :: srcASelect :: srcBSelect :: signExt :: HNil
|
||||
// Mem | valid :: writeMask :: writeEnable :: HNil
|
||||
|
||||
(lui , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc)::
|
||||
r(aOpAdd) :: r(aSrcAZero) :: r(aSrcBImmU) :: r(false.B) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(auipc , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc)::
|
||||
r(aOpAdd) :: r(aSrcAPc) :: r(aSrcBImmU) :: r(false.B) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
// ---- Control Transfer Instructions ----
|
||||
(jal , (
|
||||
r(true.B) :: r(rNpc) ::
|
||||
r(false.B) :: r(pExeOut) ::
|
||||
r(aOpAdd) :: r(aSrcAPc) :: r(aSrcBImmJ) :: r(false.B) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(jalr , (
|
||||
r(true.B) :: r(rNpc) ::
|
||||
r(false.B) :: r(pExeOut) ::
|
||||
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: r(false.B) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(beq , (
|
||||
r(false.B) :: l(WriteSelect) ::
|
||||
r(true.B) :: r(pStaticNpc) ::
|
||||
r(aOpSlt) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(bne , (
|
||||
r(false.B) :: l(WriteSelect) ::
|
||||
r(true.B) :: r(pStaticNpc) ::
|
||||
r(aOpSlt) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(blt , (
|
||||
r(false.B) :: l(WriteSelect) ::
|
||||
r(true.B) :: r(pStaticNpc) ::
|
||||
r(aOpSlt) :: r(aSrcARs1) :: r(aSrcBRs2) :: r(true.B) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(bge , (
|
||||
r(false.B) :: l(WriteSelect) ::
|
||||
r(true.B) :: r(pStaticNpc) ::
|
||||
r(aOpSlt) :: r(aSrcARs1) :: r(aSrcBRs2) :: r(true.B) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(bltu , (
|
||||
r(false.B) :: l(WriteSelect)::
|
||||
r(true.B) :: r(pStaticNpc) ::
|
||||
r(aOpSltu) :: r(aSrcARs1) :: r(aSrcBRs2) :: r(false.B) ::
|
||||
r(false.B) :: l(UInt(4.W)) :: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(bgeu , (
|
||||
r(false.B) :: l(WriteSelect)::
|
||||
r(true.B) :: r(pStaticNpc) ::
|
||||
r(aOpSltu) :: r(aSrcARs1) :: r(aSrcBRs2) :: r(false.B) ::
|
||||
r(false.B) :: l(UInt(4.W)) :: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
// ---- Memory Access Instructions ----
|
||||
|
||||
(lb , (
|
||||
r(true.B) :: r(rMemOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
|
||||
r(true.B) :: r(1.U(4.W)) :: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(lbu , (
|
||||
r(true.B) :: r(rMemOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
|
||||
r(true.B) :: r(0.U(4.W)) :: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(lh , (
|
||||
r(true.B) :: r(rMemOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
|
||||
r(true.B) :: r(3.U(4.W)) :: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(lhu , (
|
||||
r(true.B) :: r(rMemOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
|
||||
r(true.B) :: r(2.U(4.W)) :: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(lw , (
|
||||
r(true.B) :: r(rMemOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
|
||||
r(true.B) :: r(14.U(4.W)) :: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(sb , (
|
||||
r(false.B) :: l(WriteSelect)::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmS) :: l(Bool()) ::
|
||||
r(true.B) :: r(1.U(4.W)) :: r(true.B) :: HNil
|
||||
)),
|
||||
|
||||
(sh , (
|
||||
r(false.B) :: l(WriteSelect)::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmS) :: l(Bool()) ::
|
||||
r(true.B) :: r(3.U(4.W)) :: r(true.B) :: HNil
|
||||
)),
|
||||
|
||||
(sw , (
|
||||
r(false.B) :: l(WriteSelect)::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmS) :: l(Bool()) ::
|
||||
r(true.B) :: r(15.U(4.W)) :: r(true.B) :: HNil
|
||||
)),
|
||||
|
||||
// ---- Integer Computational Instructions ---
|
||||
|
||||
(addi , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(slti , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpSlt) :: r(aSrcARs1) :: r(aSrcBImmI) :: r(true.B) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(sltiu , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpSltu) :: r(aSrcARs1) :: r(aSrcBImmI) :: r(false.B) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(xori , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpXor) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(ori , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpOr) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(andi , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpAnd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(slli , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpSll) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(srli , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpSrl) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(srai , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpSra) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(add , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(sub , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpSub) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(sll , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpSll) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(slt , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpSlt) :: r(aSrcARs1) :: r(aSrcBRs2) :: r(true.B) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(sltu , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpSltu) :: r(aSrcARs1) :: r(aSrcBRs2) :: r(false.B) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(xor , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpXor) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(srl , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpSrl) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(sra , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpSra) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(or , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpOr) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
|
||||
(and , (
|
||||
r(true.B) :: r(rAluOut) ::
|
||||
r(false.B) :: r(pStaticNpc) ::
|
||||
r(aOpAnd) :: r(aSrcARs1) :: r(aSrcBRs2) :: l(Bool()) ::
|
||||
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil
|
||||
)),
|
||||
)
|
||||
// format: on
|
||||
|
||||
val default = BitPat(0.U(controlWidth.W))
|
||||
|
||||
// println(s"ControlMapping = ${ControlMapping.map(it => (it._1 -> toBits(it._2))).foreach(x => println(x._2))}\n")
|
||||
val out = decoder(
|
||||
inst,
|
||||
TruthTable(ControlMapping.map(it => (it._1 -> toBits(it._2))), default)
|
||||
)
|
||||
val srcList = slices.map(s => out(s._1, s._2))
|
||||
|
||||
assert(out != default)
|
||||
println(s"out = $out, default = $default\n")
|
||||
println(s"dstList = ${dstList}\n")
|
||||
println(s"srcList = ${srcList}\n")
|
||||
srcList
|
||||
.zip(dstList)
|
||||
.foreach({ case (src, dst) =>
|
||||
dst.toOption.get := src.asTypeOf(dst.toOption.get)
|
||||
})
|
||||
}
|
||||
|
||||
import flow.components.{RegisterFile, ProgramCounter, ALU, RamDpi}
|
||||
import chisel3.util.experimental.loadMemoryFromFileInline
|
||||
class Flow extends Module {
|
||||
def lit(x: Data) = { x.litValue.toInt }
|
||||
implicit val p: Params = new Params(XLEN = 32.W, arch = "single")
|
||||
val IF = Module(new IF)
|
||||
val ID = Module(new ID)
|
||||
val EX = Module(new EX)
|
||||
val LS = Module(new LS)
|
||||
val WB = Module(new WB)
|
||||
// IF.msgio connect ID.msgio connect EX.msgio connect LS.msgio connect [Nothing] WB.msgio
|
||||
ID.msgio.in :<>= IF.msgio.out
|
||||
EX.msgio.in :<>= ID.msgio.out
|
||||
LS.msgio.in :<>= EX.msgio.out
|
||||
WB.msgio.in :<>= LS.msgio.out
|
||||
|
||||
val dataType = UInt(32.W)
|
||||
val ram = Module(new RamDpi)
|
||||
val control = Module(new Control(32))
|
||||
val reg = Module(new RegisterFile(dataType, 32, 2))
|
||||
val pc = Module(new ProgramCounter(dataType))
|
||||
val alu = Module(new ALU(dataType))
|
||||
|
||||
// TODO: Switch to Decoupled and Arbiter later
|
||||
ram.io.pc := pc.out
|
||||
val inst = ram.io.inst
|
||||
|
||||
dontTouch(reg.control.writeEnable)
|
||||
|
||||
import control.pc.SrcSelect._
|
||||
|
||||
val npc = Wire(dataType)
|
||||
npc := pc.out + 4.U
|
||||
pc.in.exeOut := alu.out.result
|
||||
pc.in.immB := inst.immB
|
||||
|
||||
control.inst := inst
|
||||
reg.control <> control.reg
|
||||
// FIXME: Probably optimizable with bulk connection
|
||||
pc.control <> control.pc
|
||||
pc.control.useImmB := control.pc.useImmB
|
||||
alu.control <> control.alu
|
||||
val branchUseSlt = Wire(Bool())
|
||||
val branchInvertResult = Wire(Bool())
|
||||
branchUseSlt := inst(14)
|
||||
branchInvertResult := inst(12)
|
||||
val _branchResult = Mux(branchUseSlt, alu.out.result(0), alu.out.eq)
|
||||
val branchResult = Mux(branchInvertResult, !_branchResult, _branchResult)
|
||||
pc.control.useImmB := control.pc.useImmB && branchResult
|
||||
// printf(cf"_branchResult = ${_branchResult}, branchResult = ${branchResult}\n")
|
||||
// printf(cf"pcin.useImmB = ${pc.control.useImmB}, control.out.useImmB = ${control.pc.useImmB} \n")
|
||||
|
||||
import control.reg.WriteSelect._
|
||||
reg.in.writeData(lit(rAluOut)) := alu.out.result
|
||||
val maskedData = ram.io.readData & Cat(
|
||||
Fill(8, ram.io.writeMask(3)),
|
||||
Fill(8, ram.io.writeMask(2)),
|
||||
Fill(8, ram.io.writeMask(1)),
|
||||
"b11111111".U
|
||||
)
|
||||
|
||||
val doSignExt = control.ram.writeMask(0)
|
||||
val signExt16 = control.ram.writeMask(1)
|
||||
when(!doSignExt) {
|
||||
reg.in.writeData(lit(rMemOut)) := maskedData
|
||||
// printf(cf"!doSignExt\n")
|
||||
}.elsewhen(signExt16) {
|
||||
reg.in.writeData(lit(rMemOut)) := Cat(
|
||||
Fill(16, maskedData(15)),
|
||||
maskedData(15, 0)
|
||||
)
|
||||
// printf(cf"elsewhen\n")
|
||||
}.otherwise {
|
||||
reg.in
|
||||
.writeData(lit(rMemOut)) := Cat(Fill(24, maskedData(7)), maskedData(7, 0))
|
||||
// printf(cf"otherwise\n")
|
||||
}
|
||||
// printf(cf"maskedData = ${maskedData}, writeData = ${reg.in.writeData(lit(rMemOut))}\n")
|
||||
reg.in.writeData(lit(rNpc)) := npc
|
||||
|
||||
reg.in.writeAddr := inst.rd
|
||||
reg.in.rs(0) := inst.rs1
|
||||
reg.in.rs(1) := inst.rs2
|
||||
|
||||
// TODO: Bulk connection here
|
||||
ram.io.clock := clock
|
||||
ram.io.reset := reset
|
||||
ram.io.writeAddr := alu.out.result
|
||||
ram.io.writeData := reg.out.src(1)
|
||||
ram.io.writeMask := control.ram.writeMask
|
||||
ram.io.writeEnable := control.ram.writeEnable
|
||||
ram.io.valid := control.ram.valid
|
||||
ram.io.readAddr := alu.out.result
|
||||
|
||||
import control.alu.SrcASelect._
|
||||
import control.alu.SrcBSelect._
|
||||
alu.in.a(lit(aSrcARs1)) := reg.out.src(0)
|
||||
alu.in.a(lit(aSrcAPc)) := pc.out
|
||||
alu.in.a(lit(aSrcAZero)) := 0.U
|
||||
|
||||
alu.in.b(lit(aSrcBRs2)) := reg.out.src(1)
|
||||
// alu.in.b(lit(aSrcBImmI)) := inst(31, 20).pad(aSrcBImmI.getWidth)
|
||||
alu.in.b(lit(aSrcBImmI)) := inst.immI
|
||||
alu.in.b(lit(aSrcBImmJ)) := inst.immJ
|
||||
alu.in.b(lit(aSrcBImmS)) := inst.immS
|
||||
alu.in.b(lit(aSrcBImmU)) := inst.immU
|
||||
|
||||
Trace.traceName(pc.out)
|
||||
dontTouch(control.out)
|
||||
IF.io.fromRam := LS.io.toIF
|
||||
IF.io.fromEx := EX.io.toIF
|
||||
LS.io.fromIF := IF.io.toRam
|
||||
ID.io.fromWB := WB.io.toID
|
||||
}
|
||||
|
|
|
@ -7,11 +7,10 @@ import chisel3.experimental.Trace._
|
|||
import chisel3.stage.{ChiselGeneratorAnnotation, DesignAnnotation}
|
||||
import chisel3.util.experimental.InlineInstance
|
||||
import circt.stage.ChiselStage
|
||||
import firrtl.AnnotationSeq
|
||||
import firrtl.annotations.TargetToken.{Instance, OfModule, Ref}
|
||||
import java.io.PrintWriter
|
||||
import scala.io.Source
|
||||
import java.io.File
|
||||
import firrtl.annotations.TargetToken.{OfModule, Instance, Ref}
|
||||
|
||||
// TODO: Generate verilator config file
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package flow.tests
|
||||
|
||||
import chisel3._
|
||||
import flow.Params
|
||||
|
||||
object defaultParams {
|
||||
def apply(): Params = new Params(XLEN = 32.W)
|
||||
}
|
|
@ -19,4 +19,4 @@ void Config::cli_parse(int argc, char **argv) {
|
|||
}
|
||||
}
|
||||
|
||||
Config config;
|
||||
Config config{.wavefile = "flowwave.vcd"};
|
||||
|
|
|
@ -7,6 +7,7 @@ extern "C" {
|
|||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <devices.hpp>
|
||||
#include <string>
|
||||
#include <types.h>
|
||||
#include <vl_wrapper.hpp>
|
||||
#include <vpi_user.h>
|
||||
|
@ -39,21 +40,19 @@ void *pmem_get() {
|
|||
return pmem;
|
||||
}
|
||||
|
||||
int pmem_read(int raddr) {
|
||||
int pmem_read(int raddr, int rmask) {
|
||||
void *pmem = pmem_get();
|
||||
auto mem = static_cast<MMap *>(pmem);
|
||||
// TODO: Do memory difftest at memory read and write to diagnose at a finer
|
||||
// granularity
|
||||
mem->trace(raddr, true, regs->get_pc());
|
||||
if (g_skip_memcheck)
|
||||
return mem->read(PMEM_START);
|
||||
return mem->read(raddr);
|
||||
return mem->read(PMEM_START, rmask);
|
||||
return mem->read(raddr, rmask);
|
||||
}
|
||||
|
||||
void pmem_write(int waddr, int wdata, char wmask) {
|
||||
void *pmem = pmem_get();
|
||||
auto mem = static_cast<MMap *>(pmem);
|
||||
mem->trace((std::size_t)waddr, false, regs->get_pc(), wdata);
|
||||
return mem->write((std::size_t)waddr, wdata, wmask);
|
||||
}
|
||||
|
||||
|
@ -146,13 +145,37 @@ void npc_init(void *args) {
|
|||
DbgState *dbg = (DbgState *)args;
|
||||
void *mem = pmem_get();
|
||||
dbg->bp = new std::vector<Breakpoint>;
|
||||
dbg->cmd_return_buf = new std::string;
|
||||
|
||||
top = new VlModule;
|
||||
regs = new Registers("TOP.Flow.reg_0.regFile_", "TOP.Flow.pc.out");
|
||||
regs = new Registers("TOP.Flow.ID.regs_regFile_",
|
||||
"TOP.Flow.WB.msgio_in_bits_pc");
|
||||
top->setup(config.wavefile, regs);
|
||||
top->reset_eval(10);
|
||||
}
|
||||
|
||||
char *npc_monitor(void *args, char **argv, int argc) {
|
||||
DbgState *dbg = (DbgState *)args;
|
||||
std::string *cmd_return_buf = dbg->cmd_return_buf;
|
||||
|
||||
if (strncmp(argv[0], "trace", 5) == 0) {
|
||||
if (strncmp(argv[1], "on", 2) == 0) {
|
||||
*cmd_return_buf = "Tracing turned on\n";
|
||||
if (!top->start_trace())
|
||||
*cmd_return_buf = "Failed to turn on tracing\n";
|
||||
return cmd_return_buf->data();
|
||||
} else if (strncmp(argv[1], "off", 3) == 0) {
|
||||
*cmd_return_buf = "Tracing turned off\n";
|
||||
if (!top->end_trace())
|
||||
*cmd_return_buf = "Failed to turn on tracing\n";
|
||||
return cmd_return_buf->data();
|
||||
}
|
||||
}
|
||||
|
||||
*cmd_return_buf = "Command not found\n";
|
||||
return cmd_return_buf->data();
|
||||
}
|
||||
|
||||
bool npc_do_difftest = true;
|
||||
|
||||
static gdbstub_t gdbstub_priv;
|
||||
|
|
1045
npc/flake.lock
1045
npc/flake.lock
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-24.05";
|
||||
nixpkgs-circt162.url = "github:NixOS/nixpkgs/7995cae3ad60e3d6931283d650d7f43d31aaa5c7";
|
||||
nur-xin = {
|
||||
url = "git+https://git.xinyang.life/xin/nur.git";
|
||||
|
@ -11,11 +12,18 @@
|
|||
url = "github:zaninime/sbt-derivation";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
am-kernels.url = "git+https://git.xinyang.life/xin/am-kernels.git?ref=dev";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils, nur-xin, nixpkgs-circt162, sbt-derivation }:
|
||||
outputs = { self, nixpkgs, nixpkgs-stable, flake-utils, nur-xin, nixpkgs-circt162, sbt-derivation, am-kernels }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
stablePkgs = import nixpkgs-stable {
|
||||
inherit system;
|
||||
config.allowUnfree = true;
|
||||
overlays = [
|
||||
];
|
||||
};
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
config.allowUnfree = true;
|
||||
|
@ -37,5 +45,28 @@
|
|||
inherit flow;
|
||||
};
|
||||
};
|
||||
|
||||
devShells.default = pkgs.mkShell.override { stdenv = pkgs.ccacheStdenv; } {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
cmake
|
||||
ninja
|
||||
flex
|
||||
bison
|
||||
nvboard
|
||||
flow
|
||||
espresso
|
||||
coursier
|
||||
sbt-with-scala-native
|
||||
gef
|
||||
] ++ [stablePkgs.verilator];
|
||||
CHISEL_FIRTOOL_PATH = "${nixpkgs-circt162.legacyPackages.${system}.circt}/bin";
|
||||
NPC_IMAGES_PATH = "${am-kernels.packages.${system}.rv32Cross.am-kernels-npc}/share";
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
cli11
|
||||
spdlog
|
||||
mini-gdbstub
|
||||
];
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -31,7 +31,9 @@ public:
|
|||
Memory(std::filesystem::path filepath, bool is_binary, paddr_t pmem_start,
|
||||
paddr_t pmem_end)
|
||||
: pmem_start(pmem_start), pmem_end(pmem_end) {
|
||||
read_memory(filepath, is_binary);
|
||||
if (!filepath.empty()) {
|
||||
read_memory(filepath, is_binary);
|
||||
}
|
||||
}
|
||||
|
||||
const word_t &operator[](std::size_t addr) { return this->read(addr); }
|
||||
|
@ -86,14 +88,18 @@ public:
|
|||
ram->transfer(waddr, (uint8_t *)&wdata, len, true);
|
||||
} else if (devices->handle(waddr, (uint8_t *)&wdata, len, true)) {
|
||||
}
|
||||
logger->trace("[W] 0x{:x}: 0x{:x}", waddr, wdata);
|
||||
}
|
||||
|
||||
word_t read(paddr_t raddr) const {
|
||||
word_t read(paddr_t raddr, int rmask) const {
|
||||
word_t res = 0;
|
||||
size_t len = (rmask & 1) + ((rmask & 2) >> 1) + ((rmask & 4) >> 2) +
|
||||
((rmask & 8) >> 3);
|
||||
if (ram->in_pmem(raddr)) {
|
||||
ram->transfer(raddr, (uint8_t *)&res, 4, false);
|
||||
ram->transfer(raddr, (uint8_t *)&res, len, false);
|
||||
} else if (devices->handle(raddr, (uint8_t *)&res, 4, false)) {
|
||||
}
|
||||
logger->trace("[R] 0x{:x}: 0x{:x}", raddr, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -118,7 +124,7 @@ public:
|
|||
void *get_pmem() { return ram->mem.data(); }
|
||||
|
||||
void trace(paddr_t addr, bool is_read, word_t pc = 0, word_t value = 0) {
|
||||
logger->trace("[{}] 0x{:x}", is_read ? 'R' : 'W', this->read(addr));
|
||||
logger->trace("[{}] 0x{:x}", is_read ? 'R' : 'W', this->read(addr, value));
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef _NPC_TYPES_H__
|
||||
#define _NPC_TYPES_H__
|
||||
#ifdef __cplusplus
|
||||
#include <string>
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <gdbstub.h>
|
||||
|
@ -32,6 +33,7 @@ struct Breakpoint {
|
|||
|
||||
struct DbgState {
|
||||
std::vector<Breakpoint> *bp;
|
||||
std::string *cmd_return_buf;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ template <typename T, typename R> class VlModuleInterfaceCommon : public T {
|
|||
uint64_t sim_time = 0;
|
||||
uint64_t posedge_cnt = 0;
|
||||
std::unique_ptr<Tracer<T>> tracer;
|
||||
std::filesystem::path wavefile;
|
||||
|
||||
public:
|
||||
const R *registers;
|
||||
|
@ -43,8 +44,7 @@ public:
|
|||
}
|
||||
|
||||
void setup(std::filesystem::path wavefile, const R *r) {
|
||||
if (!wavefile.empty())
|
||||
tracer = std::make_unique<Tracer<T>>(this, wavefile);
|
||||
wavefile = "wave.vcd";
|
||||
registers = r;
|
||||
}
|
||||
|
||||
|
@ -89,10 +89,30 @@ public:
|
|||
this->reset = 0;
|
||||
g_skip_memcheck = false;
|
||||
}
|
||||
|
||||
bool is_posedge() {
|
||||
// Will be posedge when eval is called
|
||||
return T::clock == 0;
|
||||
}
|
||||
|
||||
bool start_trace() { return init_tracer(wavefile); }
|
||||
|
||||
bool end_trace() {
|
||||
tracer.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool init_tracer(std::filesystem::path wavefile) {
|
||||
fmt::print("wavefile: {}", wavefile.string());
|
||||
std::filesystem::path wav = "wave.vcd";
|
||||
if (!wav.empty()) {
|
||||
// Creating of tracer must happen after this class fully initialized
|
||||
tracer = std::make_unique<Tracer<T>>(this, wav);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,18 +27,23 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
static vpiHandle get_handle(const std::string name) {
|
||||
vpiHandle hdl = vpi_handle_by_name((PLI_BYTE8 *)name.c_str(), nullptr);
|
||||
if (hdl == nullptr) {
|
||||
SPDLOG_ERROR("VPI Handle {} not found", name);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
SPDLOG_INFO("Found VPI handle {} at {}", name, (void *)hdl);
|
||||
}
|
||||
return hdl;
|
||||
}
|
||||
|
||||
void init_handlers(const std::string regs_prefix, const std::string pcname) {
|
||||
pc_handle = get_handle(pcname);
|
||||
for (int i = 0; i < nr; i++) {
|
||||
std::string regname = regs_prefix + std::to_string(i);
|
||||
vpiHandle vh = vpi_handle_by_name((PLI_BYTE8 *)regname.c_str(), nullptr);
|
||||
if (vh == nullptr) {
|
||||
std::cerr << "vpiHandle " << regname.c_str() << " not found"
|
||||
<< std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
reg_handles[i] = vh;
|
||||
reg_handles[i] = get_handle(regname);
|
||||
}
|
||||
pc_handle = vpi_handle_by_name((PLI_BYTE8 *)pcname.c_str(), nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
86
scripts/difftests.py
Normal file
86
scripts/difftests.py
Normal 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())
|
Loading…
Reference in a new issue