Compare commits
2 commits
50f7d4d7b9
...
e828e140cd
Author | SHA1 | Date | |
---|---|---|---|
e828e140cd | |||
8500df8a6e |
29 changed files with 1381 additions and 135 deletions
|
@ -111,12 +111,17 @@
|
||||||
nixpkgs-circt162.legacyPackages.${system}.circt
|
nixpkgs-circt162.legacyPackages.${system}.circt
|
||||||
yosys
|
yosys
|
||||||
cli11
|
cli11
|
||||||
|
flex
|
||||||
|
bison
|
||||||
|
verilator
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
verilator
|
|
||||||
nvboard
|
nvboard
|
||||||
openssl
|
openssl
|
||||||
|
libllvm
|
||||||
|
libxml2
|
||||||
|
readline
|
||||||
] ++ self.checks.${system}.pre-commit-check.enabledPackages;
|
] ++ self.checks.${system}.pre-commit-check.enabledPackages;
|
||||||
|
|
||||||
cmakeFlags = [
|
cmakeFlags = [
|
||||||
|
|
|
@ -34,6 +34,10 @@ if(BUILD_SIM_NVBOARD_TARGET)
|
||||||
find_package(SDL2_image REQUIRED)
|
find_package(SDL2_image REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
find_package(CLI11 CONFIG REQUIRED)
|
find_package(CLI11 CONFIG REQUIRED)
|
||||||
|
# TODO: Not required
|
||||||
|
find_package(LLVM CONFIG REQUIRED)
|
||||||
|
|
||||||
|
option(ENABLE_SDB "Enable simple debugger" ON)
|
||||||
|
|
||||||
find_library(NVBOARD_LIBRARY NAMES nvboard)
|
find_library(NVBOARD_LIBRARY NAMES nvboard)
|
||||||
find_path(NVBOARD_INCLUDE_DIR NAMES nvboard.h)
|
find_path(NVBOARD_INCLUDE_DIR NAMES nvboard.h)
|
||||||
|
@ -59,6 +63,7 @@ endif()
|
||||||
|
|
||||||
# -- Build Verilator executable and add to test
|
# -- Build Verilator executable and add to test
|
||||||
include_directories(include)
|
include_directories(include)
|
||||||
|
add_subdirectory(utils)
|
||||||
|
|
||||||
add_subdirectory(csrc)
|
add_subdirectory(csrc)
|
||||||
|
|
||||||
|
|
49
npc/cmake/FindReadline.cmake
Normal file
49
npc/cmake/FindReadline.cmake
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# Code copied from sethhall@github
|
||||||
|
#
|
||||||
|
# - Try to find readline include dirs and libraries
|
||||||
|
#
|
||||||
|
# Usage of this module as follows:
|
||||||
|
#
|
||||||
|
# find_package(Readline)
|
||||||
|
#
|
||||||
|
# Variables used by this module, they can change the default behaviour and need
|
||||||
|
# to be set before calling find_package:
|
||||||
|
#
|
||||||
|
# Readline_ROOT_DIR Set this variable to the root installation of
|
||||||
|
# readline if the module has problems finding the
|
||||||
|
# proper installation path.
|
||||||
|
#
|
||||||
|
# Variables defined by this module:
|
||||||
|
#
|
||||||
|
# READLINE_FOUND System has readline, include and lib dirs found
|
||||||
|
# Readline_INCLUDE_DIR The readline include directories.
|
||||||
|
# Readline_LIBRARY The readline library.
|
||||||
|
|
||||||
|
find_path(Readline_ROOT_DIR
|
||||||
|
NAMES include/readline/readline.h
|
||||||
|
)
|
||||||
|
|
||||||
|
find_path(Readline_INCLUDE_DIR
|
||||||
|
NAMES readline/readline.h
|
||||||
|
HINTS ${Readline_ROOT_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
find_library(Readline_LIBRARY
|
||||||
|
NAMES readline
|
||||||
|
HINTS ${Readline_ROOT_DIR}/lib
|
||||||
|
)
|
||||||
|
|
||||||
|
if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
|
||||||
|
set(READLINE_FOUND TRUE)
|
||||||
|
else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
|
||||||
|
find_library(Readline_LIBRARY NAMES readline)
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY )
|
||||||
|
mark_as_advanced(Readline_INCLUDE_DIR Readline_LIBRARY)
|
||||||
|
endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
|
||||||
|
|
||||||
|
mark_as_advanced(
|
||||||
|
Readline_ROOT_DIR
|
||||||
|
Readline_INCLUDE_DIR
|
||||||
|
Readline_LIBRARY
|
||||||
|
)
|
|
@ -8,7 +8,9 @@ module RamDpi (
|
||||||
input [31:0] writeData,
|
input [31:0] writeData,
|
||||||
input [3:0] writeMask,
|
input [3:0] writeMask,
|
||||||
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,
|
||||||
|
output reg [31:0] inst
|
||||||
);
|
);
|
||||||
always @(*) begin
|
always @(*) begin
|
||||||
if (valid) begin // 有读写请求时
|
if (valid) begin // 有读写请求时
|
||||||
|
@ -20,5 +22,6 @@ module RamDpi (
|
||||||
else begin
|
else begin
|
||||||
readData = 0;
|
readData = 0;
|
||||||
end
|
end
|
||||||
|
inst = pmem_read(pc);
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
|
@ -6,41 +6,50 @@ import shapeless.{HNil, ::}
|
||||||
|
|
||||||
class ALUControlInterface extends Bundle {
|
class ALUControlInterface extends Bundle {
|
||||||
object OpSelect extends ChiselEnum {
|
object OpSelect extends ChiselEnum {
|
||||||
val aOpAdd, aOpSub, aOpNot, aOpAnd, aOpOr, aOpXor, aOpSlt, aOpEq, aOpNop = Value
|
val aOpAdd, aOpSub, aOpNot, aOpAnd, aOpOr, aOpXor, aOpSlt, aOpSll, aOpSrl, aOpSra = Value
|
||||||
}
|
}
|
||||||
object SrcSelect extends ChiselEnum {
|
object SrcASelect extends ChiselEnum {
|
||||||
val aSrcRs2, aSrcImm = Value
|
val aSrcARs1, aSrcAPc, aSrcAZero = Value
|
||||||
|
}
|
||||||
|
object SrcBSelect extends ChiselEnum {
|
||||||
|
val aSrcBRs2, aSrcBImmI, aSrcBImmJ, aSrcBImmB, aSrcBImmS = Value
|
||||||
}
|
}
|
||||||
val op = Input(OpSelect())
|
val op = Input(OpSelect())
|
||||||
val src = Input(SrcSelect())
|
val srcASelect = Input(SrcASelect())
|
||||||
|
val srcBSelect = Input(SrcBSelect())
|
||||||
|
val signExt = Input(Bool())
|
||||||
|
|
||||||
type CtrlTypes = OpSelect.Type :: SrcSelect.Type :: HNil
|
def ctrlBindPorts = {
|
||||||
def ctrlBindPorts: CtrlTypes = {
|
op :: srcASelect :: srcBSelect :: signExt :: HNil
|
||||||
op :: src :: HNil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ALU[T <: UInt](tpe: T) extends Module {
|
class ALU[T <: UInt](tpe: T) extends Module {
|
||||||
val control = IO(new ALUControlInterface)
|
val control = IO(new ALUControlInterface)
|
||||||
val in = IO(new Bundle {
|
val in = IO(new Bundle {
|
||||||
val a = Input(Vec(control.SrcSelect.all.length, tpe))
|
val a = Input(Vec(control.SrcASelect.all.length, tpe))
|
||||||
val b = Input(tpe)
|
val b = Input(Vec(control.SrcBSelect.all.length, tpe))
|
||||||
})
|
})
|
||||||
val out = IO(new Bundle {
|
val out = IO(new Bundle {
|
||||||
|
val eq = Output(Bool())
|
||||||
val result = Output(tpe)
|
val result = Output(tpe)
|
||||||
})
|
})
|
||||||
|
|
||||||
val a = in.a(control.src.asUInt)
|
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 adder_b = (Fill(tpe.getWidth, io.op(0)) ^ io.b) + io.op(0) // take (-b) if sub
|
||||||
val add = a + in.b
|
val add = a + b
|
||||||
val sub = a - in.b
|
val sub = a - b
|
||||||
val and = a & in.b
|
val and = a & b
|
||||||
val not = ~a
|
val not = ~a
|
||||||
val or = a | in.b
|
val or = a | b
|
||||||
val xor = a ^ in.b
|
val xor = a ^ b
|
||||||
val slt = a < in.b
|
val slt = a < b
|
||||||
val eq = a === in.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
|
||||||
|
|
||||||
import control.OpSelect._
|
import control.OpSelect._
|
||||||
|
|
||||||
|
@ -52,7 +61,9 @@ class ALU[T <: UInt](tpe: T) extends Module {
|
||||||
aOpOr -> or,
|
aOpOr -> or,
|
||||||
aOpXor -> xor,
|
aOpXor -> xor,
|
||||||
aOpSlt -> slt,
|
aOpSlt -> slt,
|
||||||
aOpEq -> eq
|
aOpSll -> sll,
|
||||||
|
aOpSrl -> srl,
|
||||||
|
aOpSra -> sra.asUInt
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,19 +6,32 @@ import chisel3.util.HasBlackBoxResource
|
||||||
import chisel3.util.log2Ceil
|
import chisel3.util.log2Ceil
|
||||||
import chisel3.experimental.noPrefix
|
import chisel3.experimental.noPrefix
|
||||||
import scala.collection.SeqMap
|
import scala.collection.SeqMap
|
||||||
import javax.swing.plaf.synth.SynthRadioButtonMenuItemUI
|
import flow.components
|
||||||
|
import shapeless.{HNil, ::}
|
||||||
|
|
||||||
class RamInterface[T <: Data](tpe: T, addrWidth: Int) extends Bundle {
|
class RamControlInterface(addrWidth: Int) extends Bundle {
|
||||||
val valid = Input(Bool())
|
val valid = Input(Bool())
|
||||||
|
val writeMask = Input(UInt((addrWidth / 8).W))
|
||||||
val writeEnable = Input(Bool())
|
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 writeAddr = Input(UInt(addrWidth.W))
|
val writeAddr = Input(UInt(addrWidth.W))
|
||||||
val writeData = Input(tpe)
|
val writeData = Input(tpe)
|
||||||
val writeMask = Input(UInt((addrWidth / 8).W))
|
|
||||||
val readAddr = Input(UInt(addrWidth.W))
|
val readAddr = Input(UInt(addrWidth.W))
|
||||||
val readData = Output(tpe)
|
val readData = Output(tpe)
|
||||||
|
val pc = Input(UInt(addrWidth.W))
|
||||||
|
val inst = Output(tpe)
|
||||||
}
|
}
|
||||||
|
|
||||||
class RamDpi extends BlackBox with HasBlackBoxResource {
|
class RamDpi extends BlackBox with HasBlackBoxResource {
|
||||||
val io = IO(new RamInterface(UInt(32.W), 32))
|
val io = IO((new RamInterface(UInt(32.W), 32)))
|
||||||
addResource("/RamDpi.v")
|
addResource("/RamDpi.v")
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,33 +6,42 @@ import shapeless.{HNil, ::}
|
||||||
|
|
||||||
class PcControlInterface extends Bundle {
|
class PcControlInterface extends Bundle {
|
||||||
object SrcSelect extends ChiselEnum {
|
object SrcSelect extends ChiselEnum {
|
||||||
val pStaticNpc, pBranchResult = Value
|
val pStaticNpc, pExeOut = Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val useImmB = Input(Bool())
|
||||||
val srcSelect = Input(SrcSelect())
|
val srcSelect = Input(SrcSelect())
|
||||||
|
|
||||||
type CtrlTypes = SrcSelect.Type :: HNil
|
def ctrlBindPorts = {
|
||||||
def ctrlBindPorts: CtrlTypes = {
|
useImmB :: srcSelect :: HNil
|
||||||
srcSelect :: HNil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProgramCounter[T <: Data](tpe: T) extends Module {
|
class ProgramCounter[T <: UInt](tpe: T) extends Module {
|
||||||
|
|
||||||
val control = IO(new PcControlInterface)
|
val control = IO(new PcControlInterface)
|
||||||
val in = IO(new Bundle {
|
val in = IO(new Bundle {
|
||||||
val pcSrcs = Input(Vec(control.SrcSelect.all.length, tpe))
|
val immB = Input(tpe)
|
||||||
|
val exeOut = Input(tpe)
|
||||||
})
|
})
|
||||||
val out = IO(Output(tpe))
|
val out = IO(Output(tpe))
|
||||||
|
|
||||||
private val pc = RegInit(0x80000000L.U)
|
private val pc_reg = RegInit(0x80000000L.U)
|
||||||
|
|
||||||
pc := in.pcSrcs(control.srcSelect.asUInt)
|
// pc := in.pcSrcs(control.srcSelect.asUInt)
|
||||||
out := pc
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
object ProgramCounter {
|
object ProgramCounter {
|
||||||
def apply[T <: Data](tpe: T): ProgramCounter[T] = {
|
def apply[T <: UInt](tpe: T): ProgramCounter[T] = {
|
||||||
val pc = Module(new ProgramCounter(tpe))
|
val pc = Module(new ProgramCounter(tpe))
|
||||||
pc
|
pc
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,18 +5,17 @@ import chisel3.util.log2Ceil
|
||||||
import chisel3.util.UIntToOH
|
import chisel3.util.UIntToOH
|
||||||
import chisel3.util.MuxLookup
|
import chisel3.util.MuxLookup
|
||||||
import chisel3.experimental.Trace._
|
import chisel3.experimental.Trace._
|
||||||
import shapeless.{ HNil, :: }
|
import shapeless.{ HList, HNil, :: }
|
||||||
|
|
||||||
class RegControl extends Bundle {
|
class RegControl extends Bundle {
|
||||||
object WriteSelect extends ChiselEnum {
|
object WriteSelect extends ChiselEnum {
|
||||||
val rAluOut, rMemOut = Value
|
val rAluOut, rMemOut, rNpc = Value
|
||||||
}
|
}
|
||||||
|
|
||||||
val writeEnable = Input(Bool())
|
val writeEnable = Input(Bool())
|
||||||
val writeSelect = Input(WriteSelect())
|
val writeSelect = Input(WriteSelect())
|
||||||
|
|
||||||
type CtrlTypes = Bool :: WriteSelect.Type :: HNil
|
def ctrlBindPorts = {
|
||||||
def ctrlBindPorts: CtrlTypes = {
|
|
||||||
writeEnable :: writeSelect :: HNil
|
writeEnable :: writeSelect :: HNil
|
||||||
}
|
}
|
||||||
traceName(writeEnable)
|
traceName(writeEnable)
|
||||||
|
@ -40,7 +39,8 @@ class RegisterFile[T <: Data](tpe: T, regCount: Int, numReadPorts: Int) extends
|
||||||
val writeAddrOH = UIntToOH(in.writeAddr)
|
val writeAddrOH = UIntToOH(in.writeAddr)
|
||||||
|
|
||||||
for ((reg, i) <- regFile.zipWithIndex.tail) {
|
for ((reg, i) <- regFile.zipWithIndex.tail) {
|
||||||
reg := Mux(writeAddrOH(i.U(log2Ceil(regCount).W)) && control.writeEnable, in.writeData(control.writeSelect.asUInt), reg)
|
reg := Mux(
|
||||||
|
writeAddrOH(i.U(log2Ceil(regCount).W)) && control.writeEnable, in.writeData(control.writeSelect.asUInt), reg)
|
||||||
}
|
}
|
||||||
regFile(0) := 0.U
|
regFile(0) := 0.U
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,8 @@ package flow
|
||||||
|
|
||||||
import scala.reflect.runtime.universe._
|
import scala.reflect.runtime.universe._
|
||||||
import chisel3._
|
import chisel3._
|
||||||
import chisel3.util.{MuxLookup, Fill, Decoupled, Counter, Queue, Reverse}
|
|
||||||
import chisel3.util.{SRAM}
|
|
||||||
import chisel3.util.experimental.decode.{decoder, TruthTable}
|
import chisel3.util.experimental.decode.{decoder, TruthTable}
|
||||||
import chisel3.util.log2Ceil
|
import chisel3.util._
|
||||||
import chisel3.util.BitPat
|
|
||||||
import chisel3.util.Enum
|
|
||||||
import chisel3.experimental.Trace._
|
import chisel3.experimental.Trace._
|
||||||
import shapeless.{HNil, ::}
|
import shapeless.{HNil, ::}
|
||||||
import shapeless.HList
|
import shapeless.HList
|
||||||
|
@ -15,68 +11,322 @@ import shapeless.ops.coproduct.Prepend
|
||||||
import chisel3.util.{ BinaryMemoryFile, HexMemoryFile }
|
import chisel3.util.{ BinaryMemoryFile, HexMemoryFile }
|
||||||
|
|
||||||
import chisel3.experimental.Trace
|
import chisel3.experimental.Trace
|
||||||
|
import scala.collection.IndexedSeqView
|
||||||
|
import shapeless.Poly1
|
||||||
|
import flow.components.RamControlInterface
|
||||||
|
|
||||||
object RV32Inst {
|
object RV32Inst {
|
||||||
private val bp = BitPat
|
private val bp = BitPat
|
||||||
|
|
||||||
|
val lui = this.bp("b???????_?????_?????_???_?????_01101_11")
|
||||||
|
val auipc = this.bp("b???????_?????_?????_???_?????_00101_11")
|
||||||
|
|
||||||
|
val jal = this.bp("b???????_?????_?????_???_?????_11011_11")
|
||||||
|
val jalr = this.bp("b???????_?????_?????_???_?????_11001_11")
|
||||||
|
val beq = this.bp("b???????_?????_?????_000_?????_11000_11")
|
||||||
|
val bne = this.bp("b???????_?????_?????_001_?????_11000_11")
|
||||||
|
val blt = this.bp("b???????_?????_?????_100_?????_11000_11")
|
||||||
|
val bge = this.bp("b???????_?????_?????_101_?????_11000_11")
|
||||||
|
val bltu = this.bp("b???????_?????_?????_110_?????_11000_11")
|
||||||
|
val bgeu = this.bp("b???????_?????_?????_111_?????_11000_11")
|
||||||
|
|
||||||
|
val lb = this.bp("b???????_?????_?????_000_?????_00000_11")
|
||||||
|
val lh = this.bp("b???????_?????_?????_001_?????_00000_11")
|
||||||
|
val lw = this.bp("b???????_?????_?????_010_?????_00000_11")
|
||||||
|
val lbu = this.bp("b???????_?????_?????_100_?????_00000_11")
|
||||||
|
val lhu = this.bp("b???????_?????_?????_101_?????_00000_11")
|
||||||
|
val sb = this.bp("b???????_?????_?????_000_?????_01000_11")
|
||||||
|
val sh = this.bp("b???????_?????_?????_001_?????_01000_11")
|
||||||
|
val sw = this.bp("b???????_?????_?????_010_?????_01000_11")
|
||||||
|
|
||||||
val addi = this.bp("b???????_?????_?????_000_?????_00100_11")
|
val addi = this.bp("b???????_?????_?????_000_?????_00100_11")
|
||||||
|
val slti = this.bp("b???????_?????_?????_010_?????_00100_11")
|
||||||
|
val sltiu = this.bp("b???????_?????_?????_011_?????_00100_11")
|
||||||
|
val xori = this.bp("b???????_?????_?????_100_?????_00100_11")
|
||||||
|
val ori = this.bp("b???????_?????_?????_110_?????_00100_11")
|
||||||
|
val andi = this.bp("b???????_?????_?????_111_?????_00100_11")
|
||||||
|
val slli = this.bp("b0000000_?????_?????_001_?????_00100_11")
|
||||||
|
val srli = this.bp("b0000000_?????_?????_101_?????_00100_11")
|
||||||
|
val srai = this.bp("b0100000_?????_?????_101_?????_00100_11")
|
||||||
|
val add = this.bp("b0000000_?????_?????_000_?????_01100_11")
|
||||||
|
val sub = this.bp("b0100000_?????_?????_000_?????_01100_11")
|
||||||
|
val sll = this.bp("b0000000_?????_?????_001_?????_01100_11")
|
||||||
|
val slt = this.bp("b0000000_?????_?????_010_?????_01100_11")
|
||||||
|
val sltu = this.bp("b0000000_?????_?????_011_?????_01100_11")
|
||||||
|
val xor = this.bp("b0000000_?????_?????_100_?????_01100_11")
|
||||||
|
val srl = this.bp("b0000000_?????_?????_101_?????_01100_11")
|
||||||
|
val sra = this.bp("b0100000_?????_?????_101_?????_01100_11")
|
||||||
|
val or = this.bp("b0000000_?????_?????_110_?????_01100_11")
|
||||||
|
val and = this.bp("b0000000_?????_?????_111_?????_01100_11")
|
||||||
|
|
||||||
|
val ebreak = this.bp("b0000000_00001_00000_000_00000_11100_11")
|
||||||
|
|
||||||
|
|
||||||
|
val mul = this.bp("b0000001_?????_?????_000_?????_01100_11")
|
||||||
|
val mulh = this.bp("b0000001_?????_?????_001_?????_01100_11")
|
||||||
|
val mulhsu = this.bp("b0000001_?????_?????_010_?????_01100_11")
|
||||||
|
val mulhu = this.bp("b0000001_?????_?????_011_?????_01100_11")
|
||||||
|
val div = this.bp("b0000001_?????_?????_100_?????_01100_11")
|
||||||
|
val divu = this.bp("b0000001_?????_?????_101_?????_01100_11")
|
||||||
|
val rem = this.bp("b0000001_?????_?????_110_?????_01100_11")
|
||||||
|
val remu = this.bp("b0000001_?????_?????_111_?????_01100_11")
|
||||||
|
|
||||||
val inv = this.bp("b???????_?????_?????_???_?????_?????_??")
|
val inv = this.bp("b???????_?????_?????_???_?????_?????_??")
|
||||||
}
|
}
|
||||||
|
|
||||||
class PcControl(width: Int) extends Bundle {
|
|
||||||
object SrcSelect extends ChiselEnum {
|
|
||||||
val pPC, pExeResult = Value
|
|
||||||
}
|
|
||||||
val srcSelect = Output(SrcSelect())
|
|
||||||
}
|
|
||||||
|
|
||||||
import flow.components.{RegControl, PcControlInterface, ALUControlInterface}
|
import flow.components.{RegControl, PcControlInterface, ALUControlInterface}
|
||||||
class Control(width: Int) extends Module {
|
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)))
|
val inst = IO(Input(UInt(width.W)))
|
||||||
|
|
||||||
val reg = IO(Flipped(new RegControl))
|
val reg = IO(Flipped(new RegControl))
|
||||||
val pc = IO(Flipped(new PcControlInterface))
|
val pc = IO(Flipped(new PcControlInterface))
|
||||||
val alu = IO(Flipped(new ALUControlInterface))
|
val alu = IO(Flipped(new ALUControlInterface))
|
||||||
|
val ram = IO(Flipped(new RamControlInterface(32)))
|
||||||
|
|
||||||
// TODO: Add .ctrlTypes together instead of writing them by hand.
|
val dst = new WrapList((
|
||||||
type T =
|
reg.ctrlBindPorts ++
|
||||||
Bool :: reg.WriteSelect.Type :: pc.SrcSelect.Type :: alu.OpSelect.Type :: alu.SrcSelect.Type :: HNil
|
pc.ctrlBindPorts ++
|
||||||
val dst: T = reg.ctrlBindPorts ++ pc.ctrlBindPorts ++ alu.ctrlBindPorts
|
alu.ctrlBindPorts ++
|
||||||
|
ram.ctrlBindPorts).map(wrap))
|
||||||
|
|
||||||
val dstList = dst.toList
|
val dstList = dst.v.toList
|
||||||
val reversePrefixSum = dstList.scanLeft(0)(_ + _.getWidth).reverse
|
val controlWidth = dstList.map(_.toOption.get.getWidth).reduce(_ + _)
|
||||||
val slices = reversePrefixSum.zip(reversePrefixSum.tail)
|
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.WriteSelect._
|
||||||
|
import reg._
|
||||||
import pc.SrcSelect._
|
import pc.SrcSelect._
|
||||||
|
import pc._
|
||||||
import alu.OpSelect._
|
import alu.OpSelect._
|
||||||
import alu.SrcSelect._
|
import alu.SrcASelect._
|
||||||
|
import alu.SrcBSelect._
|
||||||
|
import pc._
|
||||||
import RV32Inst._
|
import RV32Inst._
|
||||||
val ControlMapping: Array[(BitPat, T)] = Array(
|
val ControlMapping: Array[(BitPat, dst.Type)] = Array(
|
||||||
// Regs :: PC :: Exe
|
// Regs | writeEnable :: writeSelect :: HNil
|
||||||
// writeEnable :: writeSelect :: srcSelect ::
|
// PC | useImmB :: srcSelect :: HNil
|
||||||
(addi, true.B :: rAluOut :: pStaticNpc :: aOpAdd :: aSrcImm :: HNil),
|
// Exe | op :: srcASelect :: srcBSelect :: signExt :: HNil
|
||||||
)
|
// Mem | valid :: writeMask :: writeEnable :: HNil
|
||||||
val default = BitPat.dontCare(dstList.map(_.getWidth).reduce(_ + _))
|
|
||||||
|
|
||||||
def toBits(t: T): BitPat = {
|
// ---- Control Transfer Instructions ----
|
||||||
val list: List[Data] = t.toList
|
(jal , (r(true.B) :: r(rNpc) ::
|
||||||
list.map(x => BitPat(x.litValue.toInt.U(x.getWidth.W))).reduceLeft(_ ## _)
|
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(aSrcBImmJ) :: r(false.B) ::
|
||||||
|
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil)),
|
||||||
|
|
||||||
|
(beq , (r(false.B) :: l(WriteSelect) ::
|
||||||
|
r(true.B) :: r(pExeOut) ::
|
||||||
|
r(aOpSlt) :: r(aSrcAPc) :: r(aSrcBImmB) :: l(Bool()) ::
|
||||||
|
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil)),
|
||||||
|
|
||||||
|
(bne , (r(false.B) :: l(WriteSelect) ::
|
||||||
|
r(true.B) :: r(pExeOut) ::
|
||||||
|
r(aOpSlt) :: r(aSrcAPc) :: r(aSrcBImmB) :: l(Bool()) ::
|
||||||
|
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil)),
|
||||||
|
|
||||||
|
(blt , (r(false.B) :: l(WriteSelect) ::
|
||||||
|
r(true.B) :: r(pExeOut) ::
|
||||||
|
r(aOpSlt) :: r(aSrcAPc) :: r(aSrcBImmB) :: r(true.B) ::
|
||||||
|
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil)),
|
||||||
|
|
||||||
|
(bge , (r(false.B) :: l(WriteSelect) ::
|
||||||
|
r(true.B) :: r(pExeOut) ::
|
||||||
|
r(aOpSlt) :: r(aSrcAPc) :: r(aSrcBImmB) :: r(true.B) ::
|
||||||
|
r(false.B) :: l(UInt(4.W)):: r(false.B) :: HNil)),
|
||||||
|
|
||||||
|
(bltu , (r(false.B) :: l(WriteSelect)::
|
||||||
|
r(true.B) :: r(pExeOut) ::
|
||||||
|
r(aOpSlt) :: r(aSrcAPc) :: r(aSrcBImmB) :: r(false.B) ::
|
||||||
|
r(false.B) :: l(UInt(4.W)) :: r(false.B) :: HNil)),
|
||||||
|
|
||||||
|
(bgeu , (r(false.B) :: l(WriteSelect)::
|
||||||
|
r(true.B) :: r(pExeOut) ::
|
||||||
|
r(aOpSlt) :: r(aSrcAPc) :: r(aSrcBImmB) :: 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) :: l(UInt(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) :: l(UInt(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) :: l(UInt(4.W)) :: r(false.B) :: HNil)),
|
||||||
|
|
||||||
|
(sb , (r(false.B) :: l(WriteSelect)::
|
||||||
|
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)),
|
||||||
|
|
||||||
|
(sh , (r(false.B) :: l(WriteSelect)::
|
||||||
|
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)),
|
||||||
|
|
||||||
|
(sw , (r(false.B) :: l(WriteSelect)::
|
||||||
|
r(false.B) :: r(pStaticNpc) ::
|
||||||
|
r(aOpAdd) :: r(aSrcARs1) :: r(aSrcBImmI) :: l(Bool()) ::
|
||||||
|
r(true.B) :: r(15.U(4.W)) :: r(false.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(aOpSlt) :: 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(aOpOr) :: 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(aOpXor) :: 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(aOpSlt) :: 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)),
|
||||||
|
)
|
||||||
|
|
||||||
|
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(
|
val out = decoder(
|
||||||
inst,
|
inst,
|
||||||
TruthTable(ControlMapping.map(it => (it._1 -> toBits(it._2))), default))
|
TruthTable(ControlMapping.map(it => (it._1 -> toBits(it._2))), default))
|
||||||
val srcList = slices.map(s => out(s._1 - 1, s._2))
|
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
|
srcList
|
||||||
.zip(dstList.reverse)
|
.zip(dstList)
|
||||||
.foreach({ case (src, dst) =>
|
.foreach({ case (src, dst) =>
|
||||||
dst := src.asTypeOf(dst)
|
dst.toOption.get := src.asTypeOf(dst.toOption.get)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
pc.useImmB := DontCare
|
||||||
}
|
}
|
||||||
|
|
||||||
import flow.components.{RegisterFile, ProgramCounter, ALU, RamDpi}
|
import flow.components.{RegisterFile, ProgramCounter, ALU, RamDpi}
|
||||||
import chisel3.util.experimental.loadMemoryFromFileInline
|
import chisel3.util.experimental.loadMemoryFromFileInline
|
||||||
class Flow extends Module {
|
class Flow extends Module {
|
||||||
|
def lit(x: Data) = { x.litValue.toInt }
|
||||||
|
|
||||||
val dataType = UInt(32.W)
|
val dataType = UInt(32.W)
|
||||||
val ram = Module(new RamDpi)
|
val ram = Module(new RamDpi)
|
||||||
val control = Module(new Control(32))
|
val control = Module(new Control(32))
|
||||||
|
@ -84,42 +334,61 @@ class Flow extends Module {
|
||||||
val pc = Module(new ProgramCounter(dataType))
|
val pc = Module(new ProgramCounter(dataType))
|
||||||
val alu = Module(new ALU(dataType))
|
val alu = Module(new ALU(dataType))
|
||||||
|
|
||||||
ram.io.readAddr := pc.out
|
// TODO: Switch to Decoupled and Arbiter later
|
||||||
val inst = ram.io.readData
|
ram.io.pc := pc.out
|
||||||
|
val inst = ram.io.inst
|
||||||
|
|
||||||
dontTouch(reg.control.writeEnable)
|
dontTouch(reg.control.writeEnable)
|
||||||
|
|
||||||
import control.pc.SrcSelect._
|
import control.pc.SrcSelect._
|
||||||
|
|
||||||
pc.in.pcSrcs(pStaticNpc.litValue.toInt) := pc.out + 4.U
|
val npc = Wire(dataType)
|
||||||
pc.in.pcSrcs(pBranchResult.litValue.toInt) := alu.out.result
|
npc := pc.out + 4.U
|
||||||
|
pc.in.exeOut := alu.out.result
|
||||||
|
pc.in.immB := Cat(Fill(20, inst(31)), inst(7), inst(30, 25), inst(11, 8), inst(0))
|
||||||
|
|
||||||
control.inst := inst
|
control.inst := inst
|
||||||
reg.control <> control.reg
|
reg.control <> control.reg
|
||||||
|
// FIXME: Probably optimizable with bulk connection
|
||||||
pc.control <> control.pc
|
pc.control <> control.pc
|
||||||
alu.control <> control.alu
|
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
|
||||||
|
|
||||||
import control.reg.WriteSelect._
|
import control.reg.WriteSelect._
|
||||||
reg.in.writeData(rAluOut.litValue.toInt) := alu.out.result
|
reg.in.writeData(lit(rAluOut)) := alu.out.result
|
||||||
|
|
||||||
// TODO: Read address in load command goes here
|
// TODO: Read address in load command goes here
|
||||||
reg.in.writeData(rMemOut.litValue.toInt) := DontCare
|
reg.in.writeData(lit(rMemOut)) := ram.io.readData
|
||||||
|
reg.in.writeData(lit(rNpc)) := npc
|
||||||
|
|
||||||
reg.in.writeAddr := inst(11, 7)
|
reg.in.writeAddr := inst(11, 7)
|
||||||
reg.in.rs(0) := inst(19, 15) // rs1
|
reg.in.rs(0) := inst(19, 15) // rs1
|
||||||
reg.in.rs(1) := inst(24, 20) // rs2
|
reg.in.rs(1) := inst(24, 20) // rs2
|
||||||
|
|
||||||
// TODO: Memory write goes here
|
// TODO: Memory write goes here
|
||||||
ram.io.writeAddr := DontCare
|
ram.io.writeAddr := alu.out.result
|
||||||
ram.io.writeData := DontCare
|
ram.io.writeData := reg.out.src(1)
|
||||||
ram.io.writeMask := DontCare
|
ram.io.writeMask := control.ram.writeMask
|
||||||
ram.io.writeEnable := false.B
|
ram.io.writeEnable := control.ram.writeEnable
|
||||||
ram.io.valid := true.B
|
ram.io.valid := true.B
|
||||||
|
ram.io.readAddr := alu.out.result
|
||||||
|
|
||||||
import control.alu.SrcSelect._
|
import control.alu.SrcASelect._
|
||||||
alu.in.a(aSrcRs2.litValue.toInt) := reg.out.src(1)
|
import control.alu.SrcBSelect._
|
||||||
alu.in.a(aSrcImm.litValue.toInt) := inst(31, 20)
|
alu.in.a(lit(aSrcARs1)) := reg.out.src(0)
|
||||||
alu.in.b := 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(aSrcBImmJ)) := Cat(Fill(12, inst(31)), inst(19, 12), inst(20), inst(30, 25), inst(24, 21), 0.U(1.W))
|
||||||
|
alu.in.b(lit(aSrcBImmB)) := Cat(Fill(20, inst(31)), inst(7), inst(30, 25), inst(11, 8), inst(0))
|
||||||
|
alu.in.b(lit(aSrcBImmS)) := Cat(inst(31), inst(19, 12), inst(20), inst(30, 25), inst(24, 21), 0.U(1.W)).pad(aSrcBImmS.getWidth)
|
||||||
|
|
||||||
Trace.traceName(pc.out);
|
Trace.traceName(pc.out);
|
||||||
dontTouch(control.out)
|
dontTouch(control.out)
|
||||||
|
|
|
@ -34,7 +34,7 @@ object VerilogMain extends App {
|
||||||
}
|
}
|
||||||
|
|
||||||
val annos = (new ChiselStage).execute(
|
val annos = (new ChiselStage).execute(
|
||||||
Array("--target-dir", opt.targetDir.toString, "--target", "systemverilog", "--split-verilog"),
|
Array("--target-dir", opt.targetDir.toString, "--target", "systemverilog", "--split-verilog", "--full-stacktrace"),
|
||||||
Seq(
|
Seq(
|
||||||
|
|
||||||
) ++ (if(config.traceConfig.enable) Seq(ChiselGeneratorAnnotation(() => new Flow)) else Seq())
|
) ++ (if(config.traceConfig.enable) Seq(ChiselGeneratorAnnotation(() => new Flow)) else Seq())
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
include(ChiselBuild)
|
include(ChiselBuild)
|
||||||
add_executable(V${TOPMODULE} config.cpp main.cpp)
|
add_executable(V${TOPMODULE} config.cpp main.cpp)
|
||||||
|
target_link_libraries(V${TOPMODULE} PRIVATE disasm sdb)
|
||||||
|
|
||||||
verilate(V${TOPMODULE} TRACE COVERAGE THREADS
|
verilate(V${TOPMODULE} TRACE COVERAGE THREADS
|
||||||
TOP_MODULE ${TOPMODULE}
|
TOP_MODULE ${TOPMODULE}
|
||||||
|
@ -8,6 +9,7 @@ verilate(V${TOPMODULE} TRACE COVERAGE THREADS
|
||||||
INCLUDE_DIRS ${CHISEL_OUTPUT_DIR}
|
INCLUDE_DIRS ${CHISEL_OUTPUT_DIR}
|
||||||
VERILATOR_ARGS
|
VERILATOR_ARGS
|
||||||
"--vpi" # Enable VPI
|
"--vpi" # Enable VPI
|
||||||
|
"-Wno-UNOPTFLAT"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_test(
|
add_test(
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
#include "components.hpp"
|
#include "VFlow___024root.h"
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
#include "disasm.hpp"
|
||||||
#include "vl_wrapper.hpp"
|
#include "vl_wrapper.hpp"
|
||||||
#include "vpi_user.h"
|
#include "vpi_user.h"
|
||||||
|
#include "vpi_wrapper.hpp"
|
||||||
#include <VFlow.h>
|
#include <VFlow.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
#include <difftest.hpp>
|
#include <difftest.hpp>
|
||||||
|
#include <sdb.hpp>
|
||||||
|
#include <types.h>
|
||||||
|
|
||||||
using VlModule = VlModuleInterfaceCommon<VFlow>;
|
using VlModule = VlModuleInterfaceCommon<VFlow>;
|
||||||
using Registers = _RegistersVPI<uint32_t, 32>;
|
using Registers = _RegistersVPI<uint32_t, 32>;
|
||||||
|
@ -31,9 +36,10 @@ void pmem_write(int waddr, int wdata, char wmask) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Disassembler d{"riscv32-pc-linux-gnu"};
|
||||||
|
|
||||||
VlModule *top;
|
VlModule *top;
|
||||||
Registers *regs;
|
Registers *regs;
|
||||||
using CPUState = CPUStateBase<uint32_t, 32>;
|
|
||||||
vpiHandle pc = nullptr;
|
vpiHandle pc = nullptr;
|
||||||
void difftest_memcpy(paddr_t, void *, size_t, bool){};
|
void difftest_memcpy(paddr_t, void *, size_t, bool){};
|
||||||
|
|
||||||
|
@ -58,6 +64,8 @@ void difftest_exec(uint64_t n) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// std::cout << d.disassemble(top->rootp->Flow__DOT__pc__DOT__pc_reg, (uint8_t *)&top->rootp->Flow__DOT___ram_inst, 4) << std::endl;
|
||||||
|
|
||||||
void difftest_init(int port) {
|
void difftest_init(int port) {
|
||||||
// top = std::make_unique<VlModule>(config.do_trace, config.wavefile);
|
// top = std::make_unique<VlModule>(config.do_trace, config.wavefile);
|
||||||
top = new VlModule{config.do_trace, config.wavefile};
|
top = new VlModule{config.do_trace, config.wavefile};
|
||||||
|
@ -65,20 +73,34 @@ void difftest_init(int port) {
|
||||||
top->reset_eval(10);
|
top->reset_eval(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DifftestInterface dut_interface = DifftestInterface{
|
||||||
|
&difftest_memcpy, &difftest_regcpy, &difftest_exec, &difftest_init};
|
||||||
|
|
||||||
|
SDB::SDB<dut_interface> sdb_dut;
|
||||||
|
extern "C" {
|
||||||
|
word_t reg_str2val(const char *name, bool *success) {
|
||||||
|
return sdb_dut.reg_str2val(name, success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv, char **env) {
|
int main(int argc, char **argv, char **env) {
|
||||||
config.cli_parse(argc, argv);
|
config.cli_parse(argc, argv);
|
||||||
|
|
||||||
/* -- Difftest -- */
|
/* -- Difftest -- */
|
||||||
std::filesystem::path ref{config.lib_ref};
|
std::filesystem::path ref{config.lib_ref};
|
||||||
DifftestInterface dut_interface = DifftestInterface{
|
|
||||||
&difftest_memcpy, &difftest_regcpy, &difftest_exec, &difftest_init};
|
|
||||||
DifftestInterface ref_interface = DifftestInterface{ref};
|
DifftestInterface ref_interface = DifftestInterface{ref};
|
||||||
|
|
||||||
Difftest<CPUStateBase<uint32_t, 32>> diff{dut_interface, ref_interface,
|
Difftest<CPUStateBase<uint32_t, 32>> diff{dut_interface, ref_interface,
|
||||||
pmem_get(), 128};
|
pmem_get(), 128};
|
||||||
int t = 8;
|
int t = 8;
|
||||||
|
sdb_dut.main_loop();
|
||||||
while (t--) {
|
while (t--) {
|
||||||
diff.step(1);
|
if (!diff.step(1)) {
|
||||||
|
uint32_t pc = regs->get_pc();
|
||||||
|
uint32_t inst = pmem_read(pc);
|
||||||
|
std::cout << diff << d.disassemble(pc, (uint8_t *)&inst, 4) << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
36
npc/csrc/Flow/vpi_wrapper.hpp
Normal file
36
npc/csrc/Flow/vpi_wrapper.hpp
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef _NPC_VPI_WRAPPER_H_
|
||||||
|
#define _NPC_VPI_WRAPPER_H_
|
||||||
|
#include <components.hpp>
|
||||||
|
#include <vpi_user.h>
|
||||||
|
|
||||||
|
template <typename T, std::size_t nr>
|
||||||
|
class _RegistersVPI : public _RegistersBase<T, nr> {
|
||||||
|
std::array<vpiHandle, nr> reg_handles;
|
||||||
|
vpiHandle pc_handle;
|
||||||
|
T vpi_get(vpiHandle vh) {
|
||||||
|
s_vpi_value v;
|
||||||
|
v.format = vpiIntVal;
|
||||||
|
vpi_get_value(vh, &v);
|
||||||
|
return v.value.integer;
|
||||||
|
}
|
||||||
|
T fetch_pc(void) { return vpi_get(pc_handle); }
|
||||||
|
T fetch_reg(std::size_t id) { return vpi_get(reg_handles[id]); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
_RegistersVPI<T, nr>(const std::string regs_prefix,
|
||||||
|
const std::string 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;
|
||||||
|
}
|
||||||
|
pc_handle = vpi_handle_by_name((PLI_BYTE8 *)pcname.c_str(), nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,13 +1,23 @@
|
||||||
|
|
||||||
#ifndef _NPC_COMPONENTS_H_
|
#ifndef _NPC_COMPONENTS_H_
|
||||||
#define _NPC_COMPONENTS_H_
|
#define _NPC_COMPONENTS_H_
|
||||||
#include "vpi_user.h"
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <exception>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <verilated_vpi.h>
|
#include <map>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
const std::map<std::string, int> riscv32_regs_by_name{
|
||||||
|
{"$0", 0}, {"ra", 1}, {"sp", 2}, {"gp", 3}, {"tp", 4}, {"t0", 5},
|
||||||
|
{"t1", 6}, {"t2", 7}, {"s0", 8}, {"s1", 9}, {"a0", 10}, {"a1", 11},
|
||||||
|
{"a2", 12}, {"a3", 13}, {"a4", 14}, {"a5", 15}, {"a6", 16}, {"a7", 17},
|
||||||
|
{"s2", 18}, {"s3", 19}, {"s4", 20}, {"s5", 21}, {"s6", 22}, {"s7", 23},
|
||||||
|
{"s8", 24}, {"s9", 25}, {"s10", 26}, {"s11", 27}, {"t3", 28}, {"t4", 29},
|
||||||
|
{"t5", 30}, {"t6", 31}};
|
||||||
|
|
||||||
template <typename T, std::size_t nr> class _RegistersBase {
|
template <typename T, std::size_t nr> class _RegistersBase {
|
||||||
std::array<T, nr> regs;
|
std::array<T, nr> regs;
|
||||||
|
@ -25,36 +35,6 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, std::size_t nr>
|
|
||||||
class _RegistersVPI : public _RegistersBase<T, nr> {
|
|
||||||
std::array<vpiHandle, nr> reg_handles;
|
|
||||||
vpiHandle pc_handle;
|
|
||||||
T vpi_get(vpiHandle vh) {
|
|
||||||
s_vpi_value v;
|
|
||||||
v.format = vpiIntVal;
|
|
||||||
vpi_get_value(vh, &v);
|
|
||||||
return v.value.integer;
|
|
||||||
}
|
|
||||||
T fetch_pc(void) { return vpi_get(pc_handle); }
|
|
||||||
T fetch_reg(std::size_t id) { return vpi_get(reg_handles[id]); }
|
|
||||||
|
|
||||||
public:
|
|
||||||
_RegistersVPI<T, nr>(const std::string regs_prefix,
|
|
||||||
const std::string 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;
|
|
||||||
}
|
|
||||||
pc_handle = vpi_handle_by_name((PLI_BYTE8 *)pcname.c_str(), nullptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, std::size_t n> class Memory {
|
template <typename T, std::size_t n> class Memory {
|
||||||
std::size_t addr_to_index(std::size_t addr) {
|
std::size_t addr_to_index(std::size_t addr) {
|
||||||
if (addr < 0x80000000) {
|
if (addr < 0x80000000) {
|
||||||
|
@ -74,7 +54,8 @@ template <typename T, std::size_t n> class Memory {
|
||||||
public:
|
public:
|
||||||
std::array<T, n> mem;
|
std::array<T, n> mem;
|
||||||
Memory(std::filesystem::path filepath, bool is_binary = true) {
|
Memory(std::filesystem::path filepath, bool is_binary = true) {
|
||||||
assert(std::filesystem::exists(filepath));
|
if (!std::filesystem::exists(filepath))
|
||||||
|
throw std::runtime_error("Memory file not found");
|
||||||
if (is_binary) {
|
if (is_binary) {
|
||||||
std::ifstream file(filepath, std::ios::binary);
|
std::ifstream file(filepath, std::ios::binary);
|
||||||
char *pmem = reinterpret_cast<char *>(mem.data());
|
char *pmem = reinterpret_cast<char *>(mem.data());
|
20
npc/include/config.hpp
Normal file
20
npc/include/config.hpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef _NPC_CONFIG_H_
|
||||||
|
#define _NPC_CONFIG_H_
|
||||||
|
#include <CLI/App.hpp>
|
||||||
|
#include <CLI/CLI.hpp>
|
||||||
|
#include <CLI/Validators.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
struct Config {
|
||||||
|
std::filesystem::path memory_file;
|
||||||
|
uint64_t max_sim_time = 1000;
|
||||||
|
bool memory_file_binary = {true};
|
||||||
|
bool do_trace{false};
|
||||||
|
std::filesystem::path wavefile;
|
||||||
|
std::filesystem::path lib_ref;
|
||||||
|
void cli_parse(int argc, char **argv);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Config config;
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,12 +1,14 @@
|
||||||
#ifndef _DIFFTEST_DIFFTEST_H_
|
#ifndef _DIFFTEST_DIFFTEST_H_
|
||||||
#define _DIFFTEST_DIFFTEST_H_
|
#define _DIFFTEST_DIFFTEST_H_
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <components.hpp>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
using paddr_t = uint32_t;
|
using paddr_t = uint32_t;
|
||||||
|
@ -69,14 +71,11 @@ public:
|
||||||
dut.regcpy(dut_state.get(), DIFFTEST_FROM_REF);
|
dut.regcpy(dut_state.get(), DIFFTEST_FROM_REF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void step(uint64_t n) {
|
bool step(uint64_t n) {
|
||||||
ref.exec(n);
|
ref.exec(n);
|
||||||
dut.exec(n);
|
dut.exec(n);
|
||||||
fetch_state();
|
fetch_state();
|
||||||
if (*ref_state != *dut_state) {
|
return *ref_state == *dut_state;
|
||||||
std::cout << *this;
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
friend std::ostream &operator<<(std::ostream &os, const Difftest<S> &d) {
|
friend std::ostream &operator<<(std::ostream &os, const Difftest<S> &d) {
|
||||||
|
@ -90,6 +89,8 @@ public:
|
||||||
template <typename R, size_t nr_reg> struct CPUStateBase {
|
template <typename R, size_t nr_reg> struct CPUStateBase {
|
||||||
R reg[nr_reg] = {0};
|
R reg[nr_reg] = {0};
|
||||||
paddr_t pc = 0x80000000;
|
paddr_t pc = 0x80000000;
|
||||||
|
static const std::map<std::string, int> inline regs_by_name =
|
||||||
|
riscv32_regs_by_name;
|
||||||
CPUStateBase() {
|
CPUStateBase() {
|
||||||
for (int i = 0; i < nr_reg; i++)
|
for (int i = 0; i < nr_reg; i++)
|
||||||
reg[i] = 0;
|
reg[i] = 0;
|
||||||
|
@ -106,6 +107,9 @@ template <typename R, size_t nr_reg> struct CPUStateBase {
|
||||||
bool operator!=(const CPUStateBase &other) const {
|
bool operator!=(const CPUStateBase &other) const {
|
||||||
return !(*this == other); // Reuse the == operator for != implementation
|
return !(*this == other); // Reuse the == operator for != implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This does not update the register!!! */
|
||||||
|
R at(std::string name) { return reg[regs_by_name.at(name)]; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename R, size_t nr_reg>
|
template <typename R, size_t nr_reg>
|
||||||
|
|
23
npc/include/types.h
Normal file
23
npc/include/types.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef _NPC_TYPES_H__
|
||||||
|
#define _NPC_TYPES_H__
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
typedef uint32_t word_t;
|
||||||
|
typedef int32_t sword_t;
|
||||||
|
static const word_t WORD_T_MAX = UINT32_MAX;
|
||||||
|
static const sword_t SWORD_T_MAX = INT32_MAX;
|
||||||
|
static const sword_t SWORD_T_MIN = INT32_MIN;
|
||||||
|
#define WORD_BYTES 4
|
||||||
|
|
||||||
|
#define FMT_WORD "0x%08x"
|
||||||
|
typedef uint32_t vaddr_t;
|
||||||
|
typedef uint32_t paddr_t;
|
||||||
|
#define FMT_ADDR "0x%08x"
|
||||||
|
typedef uint16_t ioaddr_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <difftest.hpp>
|
||||||
|
using CPUState = CPUStateBase<word_t, 32>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
65
npc/include/vl_wrapper.hpp
Normal file
65
npc/include/vl_wrapper.hpp
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#ifndef _NPC_TRACER_H_
|
||||||
|
#define _NPC_TRACER_H_
|
||||||
|
#include <filesystem>
|
||||||
|
#include <verilated_vcd_c.h>
|
||||||
|
|
||||||
|
template <class T> class Tracer {
|
||||||
|
std::shared_ptr<T> top;
|
||||||
|
std::unique_ptr<VerilatedVcdC> m_trace;
|
||||||
|
uint64_t cycle = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Tracer(T *top, std::filesystem::path wavefile) {
|
||||||
|
top = top;
|
||||||
|
Verilated::traceEverOn(true);
|
||||||
|
m_trace = std::make_unique<VerilatedVcdC>();
|
||||||
|
top->trace(m_trace.get(), 5);
|
||||||
|
m_trace->open(wavefile.c_str());
|
||||||
|
}
|
||||||
|
~Tracer() { m_trace->close(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump signals to waveform file. Must be called once after every top->eval()
|
||||||
|
* call.
|
||||||
|
*/
|
||||||
|
void update() { m_trace->dump(cycle++); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class VlModuleInterfaceCommon : public T {
|
||||||
|
uint64_t sim_time = 0;
|
||||||
|
uint64_t posedge_cnt = 0;
|
||||||
|
std::unique_ptr<Tracer<T>> tracer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
VlModuleInterfaceCommon<T>(bool do_trace,
|
||||||
|
std::filesystem::path wavefile = "waveform.vcd") {
|
||||||
|
if (do_trace)
|
||||||
|
tracer = std::make_unique<Tracer<T>>(this, wavefile);
|
||||||
|
}
|
||||||
|
void eval() {
|
||||||
|
if (this->is_posedge()) {
|
||||||
|
posedge_cnt++;
|
||||||
|
}
|
||||||
|
T::clock = !T::clock;
|
||||||
|
sim_time++;
|
||||||
|
T::eval();
|
||||||
|
if (tracer)
|
||||||
|
tracer->update();
|
||||||
|
}
|
||||||
|
void eval(int n) {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
this->eval();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void reset_eval(int n) {
|
||||||
|
this->reset = 1;
|
||||||
|
this->eval(n);
|
||||||
|
this->reset = 0;
|
||||||
|
}
|
||||||
|
bool is_posedge() {
|
||||||
|
// Will be posedge when eval is called
|
||||||
|
return T::clock == 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
4
npc/utils/CMakeLists.txt
Normal file
4
npc/utils/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
add_subdirectory(disasm)
|
||||||
|
if (ENABLE_SDB)
|
||||||
|
add_subdirectory(sdb)
|
||||||
|
endif()
|
4
npc/utils/disasm/CMakeLists.txt
Normal file
4
npc/utils/disasm/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
add_library(disasm disasm.cpp)
|
||||||
|
target_include_directories(disasm PUBLIC include)
|
||||||
|
llvm_map_components_to_libnames(LLVM_LIBS ${LLVM_TARGETS_TO_BUILD})
|
||||||
|
target_link_libraries(disasm PUBLIC ${LLVM_LIBS})
|
87
npc/utils/disasm/disasm.cpp
Normal file
87
npc/utils/disasm/disasm.cpp
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
#include <disasm.hpp>
|
||||||
|
#include <llvm/MC/MCAsmInfo.h>
|
||||||
|
#include <llvm/MC/MCContext.h>
|
||||||
|
#include <llvm/MC/MCDisassembler/MCDisassembler.h>
|
||||||
|
#include <llvm/MC/MCInstPrinter.h>
|
||||||
|
#include <llvm/Support/raw_ostream.h>
|
||||||
|
#if LLVM_VERSION_MAJOR >= 14
|
||||||
|
#include <llvm/MC/TargetRegistry.h>
|
||||||
|
#if LLVM_VERSION_MAJOR >= 15
|
||||||
|
#include <llvm/MC/MCSubtargetInfo.h>
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#include <llvm/Support/TargetRegistry.h>
|
||||||
|
#endif
|
||||||
|
#include <iostream>
|
||||||
|
#include <llvm/Support/TargetSelect.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LLVM_VERSION_MAJOR < 11
|
||||||
|
#error Please use LLVM with major version >= 11
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Disassembler::Disassembler(std::string triple) : triple(triple) {
|
||||||
|
llvm::InitializeAllTargetInfos();
|
||||||
|
llvm::InitializeAllTargetMCs();
|
||||||
|
llvm::InitializeAllAsmParsers();
|
||||||
|
llvm::InitializeAllDisassemblers();
|
||||||
|
|
||||||
|
std::string errstr;
|
||||||
|
|
||||||
|
llvm::MCInstrInfo *gMII = nullptr;
|
||||||
|
llvm::MCRegisterInfo *gMRI = nullptr;
|
||||||
|
auto target = llvm::TargetRegistry::lookupTarget(triple, errstr);
|
||||||
|
if (!target) {
|
||||||
|
llvm::errs() << "Can't find target for " << triple << ": " << errstr
|
||||||
|
<< "\n";
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::MCTargetOptions MCOptions;
|
||||||
|
gSTI = target->createMCSubtargetInfo(triple, "", "");
|
||||||
|
std::string isa = target->getName();
|
||||||
|
if (isa == "riscv32" || isa == "riscv64") {
|
||||||
|
gSTI->ApplyFeatureFlag("+m");
|
||||||
|
gSTI->ApplyFeatureFlag("+a");
|
||||||
|
gSTI->ApplyFeatureFlag("+c");
|
||||||
|
gSTI->ApplyFeatureFlag("+f");
|
||||||
|
gSTI->ApplyFeatureFlag("+d");
|
||||||
|
}
|
||||||
|
gMII = target->createMCInstrInfo();
|
||||||
|
gMRI = target->createMCRegInfo(triple);
|
||||||
|
auto AsmInfo = target->createMCAsmInfo(*gMRI, triple, MCOptions);
|
||||||
|
#if LLVM_VERSION_MAJOR >= 13
|
||||||
|
auto llvmTripleTwine = llvm::Twine(triple);
|
||||||
|
auto llvmtriple = llvm::Triple(llvmTripleTwine);
|
||||||
|
auto Ctx = new llvm::MCContext(llvmtriple, AsmInfo, gMRI, nullptr);
|
||||||
|
#else
|
||||||
|
auto Ctx = new llvm::MCContext(AsmInfo, gMRI, nullptr);
|
||||||
|
#endif
|
||||||
|
gDisassembler = target->createMCDisassembler(*gSTI, *Ctx);
|
||||||
|
gIP = target->createMCInstPrinter(llvm::Triple(triple),
|
||||||
|
AsmInfo->getAssemblerDialect(), *AsmInfo,
|
||||||
|
*gMII, *gMRI);
|
||||||
|
gIP->setPrintImmHex(true);
|
||||||
|
gIP->setPrintBranchImmAsAddress(true);
|
||||||
|
if (isa == "riscv32" || isa == "riscv64")
|
||||||
|
gIP->applyTargetSpecificCLOption("no-aliases");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Disassembler::disassemble(uint64_t pc, uint8_t *code, int nbyte) {
|
||||||
|
llvm::MCInst inst;
|
||||||
|
llvm::ArrayRef<uint8_t> arr(code, nbyte);
|
||||||
|
uint64_t dummy_size = 0;
|
||||||
|
gDisassembler->getInstruction(inst, dummy_size, arr, pc, llvm::nulls());
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "0x" << std::hex << pc << ": ";
|
||||||
|
std::string s = ss.str();
|
||||||
|
llvm::raw_string_ostream os{s};
|
||||||
|
gIP->printInst(&inst, pc, "", *gSTI, os);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
17
npc/utils/disasm/include/disasm.hpp
Normal file
17
npc/utils/disasm/include/disasm.hpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef _NPC_UTILS_DISASM_
|
||||||
|
#define _NPC_UTILS_DISASM_
|
||||||
|
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
|
||||||
|
#include "llvm/MC/MCInstPrinter.h"
|
||||||
|
|
||||||
|
class Disassembler {
|
||||||
|
llvm::MCDisassembler *gDisassembler = nullptr;
|
||||||
|
llvm::MCSubtargetInfo *gSTI = nullptr;
|
||||||
|
llvm::MCInstPrinter *gIP = nullptr;
|
||||||
|
std::string triple;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Disassembler(std::string);
|
||||||
|
std::string disassemble(uint64_t pc, uint8_t *code, int nbyte);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
14
npc/utils/sdb/CMakeLists.txt
Normal file
14
npc/utils/sdb/CMakeLists.txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
find_package(Readline REQUIRED)
|
||||||
|
find_package(FLEX REQUIRED)
|
||||||
|
find_package(BISON REQUIRED)
|
||||||
|
set(PARSER_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
set(LEXER_OUT "${PARSER_DIR}/lexer.c")
|
||||||
|
set(PARSER_OUT "${PARSER_DIR}/parser.c")
|
||||||
|
flex_target(LEXER addrexp.l "${LEXER_OUT}" DEFINES_FILE "${PARSER_DIR}/addrexp_lex.h")
|
||||||
|
bison_target(PARSER addrexp.y "${PARSER_OUT}" DEFINES_FILE "${PARSER_DIR}/addrexp.h")
|
||||||
|
add_flex_bison_dependency(LEXER PARSER)
|
||||||
|
|
||||||
|
add_library(sdb sdb.cpp console.cpp "${LEXER_OUT}" "${PARSER_OUT}")
|
||||||
|
target_link_libraries(sdb PRIVATE ${Readline_LIBRARY})
|
||||||
|
target_include_directories(sdb PRIVATE ${Readline_INCLUDE_DIR})
|
||||||
|
target_include_directories(sdb PUBLIC include)
|
26
npc/utils/sdb/addrexp.l
Normal file
26
npc/utils/sdb/addrexp.l
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
%{
|
||||||
|
#include <types.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "addrexp.h"
|
||||||
|
static bool success = false;
|
||||||
|
void yyerror(word_t *result, const char *err);
|
||||||
|
word_t reg_str2val(const char *name, bool*);
|
||||||
|
%}
|
||||||
|
%option noyywrap
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
0[xX][0-9a-fA-F]+ { yylval = strtoul(yytext, NULL, 16); return HEX_NUMBER; }
|
||||||
|
[0-9]+ { yylval = strtoul(yytext, NULL, 10); return NUMBER; }
|
||||||
|
$[asgprt$][0-9pa][0-9]? {
|
||||||
|
yylval = reg_str2val(yytext + 1, &success);
|
||||||
|
if(!success) {
|
||||||
|
yyerror(NULL, "Failed to convert reg to value");
|
||||||
|
return YYerror;
|
||||||
|
}
|
||||||
|
return REGISTER;
|
||||||
|
}
|
||||||
|
[+\-*/<=()] { return *yytext; }
|
||||||
|
[ \t] { }
|
||||||
|
. { printf("Unexpected character: %s\n", yytext); return YYerror; }
|
||||||
|
%%
|
58
npc/utils/sdb/addrexp.y
Normal file
58
npc/utils/sdb/addrexp.y
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
%code requires {
|
||||||
|
#include <types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
extern int yylex(void);
|
||||||
|
}
|
||||||
|
%{
|
||||||
|
#include <types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
void yyerror(word_t *result, const char *err) {
|
||||||
|
fprintf(stderr, "%s", err);
|
||||||
|
}
|
||||||
|
int pmem_read(int raddr);
|
||||||
|
%}
|
||||||
|
|
||||||
|
%token NUMBER HEX_NUMBER
|
||||||
|
%token REGISTER
|
||||||
|
%locations
|
||||||
|
%start input
|
||||||
|
%define api.value.type { word_t }
|
||||||
|
%parse-param { uint32_t *result }
|
||||||
|
%left '-' '+'
|
||||||
|
%left '*' '/'
|
||||||
|
|
||||||
|
%%
|
||||||
|
input
|
||||||
|
: expression { *result = $1; }
|
||||||
|
;
|
||||||
|
|
||||||
|
expression
|
||||||
|
: number { $$ = $1; }
|
||||||
|
| expression '>' '=' expression { $$ = ($1 >= $4); }
|
||||||
|
| expression '<' '=' expression { $$ = ($1 <= $4); }
|
||||||
|
| expression '=' '=' expression { $$ = ($1 == $4); }
|
||||||
|
| expression '!' '=' expression { $$ = ($1 == $4); }
|
||||||
|
| expression '>' expression { $$ = ($1 > $3); }
|
||||||
|
| expression '<' expression { $$ = ($1 < $3); }
|
||||||
|
| expression '+' expression { $$ = $1 + $3; }
|
||||||
|
| expression '-' expression { $$ = $1 - $3; }
|
||||||
|
| expression '*' expression { $$ = $1 * $3; }
|
||||||
|
| expression '/' expression {
|
||||||
|
if($3 == 0) {
|
||||||
|
fprintf(stderr, "Error: divide by zero at" FMT_WORD " / " FMT_WORD "\n", $1, $3);
|
||||||
|
YYABORT;
|
||||||
|
};
|
||||||
|
$$ = $1 / $3;
|
||||||
|
}
|
||||||
|
| '-' number { $$ = -$2; }
|
||||||
|
| '*' expression { $$ = pmem_read($2); }
|
||||||
|
| '(' expression ')' { $$ = $2; }
|
||||||
|
|
||||||
|
number
|
||||||
|
: REGISTER
|
||||||
|
| NUMBER
|
||||||
|
| HEX_NUMBER
|
||||||
|
|
||||||
|
%%
|
215
npc/utils/sdb/console.cpp
Normal file
215
npc/utils/sdb/console.cpp
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
// cpp-readline library
|
||||||
|
//
|
||||||
|
// @author zmij
|
||||||
|
// @date Nov 30, 2015
|
||||||
|
|
||||||
|
#include <console.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fstream>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <sstream>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <readline/history.h>
|
||||||
|
#include <readline/readline.h>
|
||||||
|
|
||||||
|
namespace CppReadline {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
Console *currentConsole = nullptr;
|
||||||
|
HISTORY_STATE *emptyHistory = history_get_history_state();
|
||||||
|
|
||||||
|
} /* namespace */
|
||||||
|
|
||||||
|
struct Console::Impl {
|
||||||
|
using RegisteredCommands =
|
||||||
|
std::unordered_map<std::string, Console::CommandFunction>;
|
||||||
|
|
||||||
|
::std::string greeting_;
|
||||||
|
// These are hardcoded commands. They do not do anything and are catched manually in the executeCommand function.
|
||||||
|
RegisteredCommands commands_;
|
||||||
|
HISTORY_STATE *history_ = nullptr;
|
||||||
|
|
||||||
|
Impl(::std::string const &greeting) : greeting_(greeting), commands_() {}
|
||||||
|
~Impl() { free(history_); }
|
||||||
|
|
||||||
|
Impl(Impl const &) = delete;
|
||||||
|
Impl(Impl &&) = delete;
|
||||||
|
Impl &operator=(Impl const &) = delete;
|
||||||
|
Impl &operator=(Impl &&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Here we set default commands, they do nothing since we quit with them
|
||||||
|
// Quitting behaviour is hardcoded in readLine()
|
||||||
|
Console::Console(std::string const &greeting) : pimpl_{new Impl{greeting}} {
|
||||||
|
// Init readline basics
|
||||||
|
rl_attempted_completion_function = &Console::getCommandCompletions;
|
||||||
|
|
||||||
|
// These are default hardcoded commands.
|
||||||
|
// Help command lists available commands.
|
||||||
|
pimpl_->commands_["help"] = [this](const Arguments &) {
|
||||||
|
auto commands = getRegisteredCommands();
|
||||||
|
std::cout << "Available commands are:\n";
|
||||||
|
for (auto &command : commands)
|
||||||
|
std::cout << "\t" << command << "\n";
|
||||||
|
return ReturnCode::Ok;
|
||||||
|
};
|
||||||
|
// Run command executes all commands in an external file.
|
||||||
|
pimpl_->commands_["run"] = [this](const Arguments &input) {
|
||||||
|
if (input.size() < 2) {
|
||||||
|
std::cout << "Usage: " << input[0] << " script_filename\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return executeFile(input[1]);
|
||||||
|
};
|
||||||
|
// Quit and Exit simply terminate the console.
|
||||||
|
pimpl_->commands_["quit"] = [this](const Arguments &) {
|
||||||
|
return ReturnCode::Quit;
|
||||||
|
};
|
||||||
|
|
||||||
|
pimpl_->commands_["exit"] = [this](const Arguments &) {
|
||||||
|
return ReturnCode::Quit;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Console::~Console() = default;
|
||||||
|
|
||||||
|
void Console::registerCommand(const std::string &s, CommandFunction f) {
|
||||||
|
pimpl_->commands_[s] = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> Console::getRegisteredCommands() const {
|
||||||
|
std::vector<std::string> allCommands;
|
||||||
|
for (auto &pair : pimpl_->commands_)
|
||||||
|
allCommands.push_back(pair.first);
|
||||||
|
|
||||||
|
return allCommands;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::saveState() {
|
||||||
|
free(pimpl_->history_);
|
||||||
|
pimpl_->history_ = history_get_history_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::reserveConsole() {
|
||||||
|
if (currentConsole == this)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Save state of other Console
|
||||||
|
if (currentConsole)
|
||||||
|
currentConsole->saveState();
|
||||||
|
|
||||||
|
// Else we swap state
|
||||||
|
if (!pimpl_->history_)
|
||||||
|
history_set_history_state(emptyHistory);
|
||||||
|
else
|
||||||
|
history_set_history_state(pimpl_->history_);
|
||||||
|
|
||||||
|
// Tell others we are using the console
|
||||||
|
currentConsole = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::setGreeting(const std::string &greeting) {
|
||||||
|
pimpl_->greeting_ = greeting;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Console::getGreeting() const { return pimpl_->greeting_; }
|
||||||
|
|
||||||
|
int Console::executeCommand(const std::string &command) {
|
||||||
|
// Convert input to vector
|
||||||
|
std::vector<std::string> inputs;
|
||||||
|
{
|
||||||
|
std::istringstream iss(command);
|
||||||
|
std::copy(std::istream_iterator<std::string>(iss),
|
||||||
|
std::istream_iterator<std::string>(), std::back_inserter(inputs));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputs.size() == 0)
|
||||||
|
return ReturnCode::Ok;
|
||||||
|
|
||||||
|
Impl::RegisteredCommands::iterator it;
|
||||||
|
if ((it = pimpl_->commands_.find(inputs[0])) != end(pimpl_->commands_)) {
|
||||||
|
return static_cast<int>((it->second)(inputs));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Command '" << inputs[0] << "' not found.\n";
|
||||||
|
return ReturnCode::Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Console::executeFile(const std::string &filename) {
|
||||||
|
std::ifstream input(filename);
|
||||||
|
if (!input) {
|
||||||
|
std::cout << "Could not find the specified file to execute.\n";
|
||||||
|
return ReturnCode::Error;
|
||||||
|
}
|
||||||
|
std::string command;
|
||||||
|
int counter = 0, result;
|
||||||
|
|
||||||
|
while (std::getline(input, command)) {
|
||||||
|
if (command[0] == '#')
|
||||||
|
continue; // Ignore comments
|
||||||
|
// Report what the Console is executing.
|
||||||
|
std::cout << "[" << counter << "] " << command << '\n';
|
||||||
|
if ((result = executeCommand(command)))
|
||||||
|
return result;
|
||||||
|
++counter;
|
||||||
|
std::cout << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we arrived successfully at the end, all is ok
|
||||||
|
return ReturnCode::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Console::readLine() {
|
||||||
|
reserveConsole();
|
||||||
|
|
||||||
|
char *buffer = readline(pimpl_->greeting_.c_str());
|
||||||
|
if (!buffer) {
|
||||||
|
std::cout
|
||||||
|
<< '\n'; // EOF doesn't put last endline so we put that so that it looks uniform.
|
||||||
|
return ReturnCode::Quit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Maybe add commands to history only if succeeded?
|
||||||
|
if (buffer[0] != '\0')
|
||||||
|
add_history(buffer);
|
||||||
|
|
||||||
|
std::string line(buffer);
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
return executeCommand(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
char **Console::getCommandCompletions(const char *text, int start, int) {
|
||||||
|
char **completionList = nullptr;
|
||||||
|
|
||||||
|
if (start == 0)
|
||||||
|
completionList = rl_completion_matches(text, &Console::commandIterator);
|
||||||
|
|
||||||
|
return completionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *Console::commandIterator(const char *text, int state) {
|
||||||
|
static Impl::RegisteredCommands::iterator it;
|
||||||
|
if (!currentConsole)
|
||||||
|
return nullptr;
|
||||||
|
auto &commands = currentConsole->pimpl_->commands_;
|
||||||
|
|
||||||
|
if (state == 0)
|
||||||
|
it = begin(commands);
|
||||||
|
|
||||||
|
while (it != end(commands)) {
|
||||||
|
auto &command = it->first;
|
||||||
|
++it;
|
||||||
|
if (command.find(text) != std::string::npos) {
|
||||||
|
return strdup(command.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} // namespace CppReadline
|
145
npc/utils/sdb/include/console.hpp
Normal file
145
npc/utils/sdb/include/console.hpp
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
// cpp-readline library
|
||||||
|
//
|
||||||
|
// @author zmij
|
||||||
|
// @date Nov 30, 2015
|
||||||
|
|
||||||
|
#ifndef CONSOLE_CONSOLE_HEADER_FILE
|
||||||
|
#define CONSOLE_CONSOLE_HEADER_FILE
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace CppReadline {
|
||||||
|
class Console {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief This is the function type that is used to interface with the Console class.
|
||||||
|
*
|
||||||
|
* These are the functions that are going to get called by Console
|
||||||
|
* when the user types in a message. The vector will hold the
|
||||||
|
* command elements, and the function needs to return its result.
|
||||||
|
* The result can either be Quit (-1), OK (0), or an arbitrary
|
||||||
|
* error (>=1).
|
||||||
|
*/
|
||||||
|
using Arguments = std::vector<std::string>;
|
||||||
|
using CommandFunction = std::function<int(const Arguments &)>;
|
||||||
|
|
||||||
|
enum ReturnCode {
|
||||||
|
Quit = -1,
|
||||||
|
Ok = 0,
|
||||||
|
Error = 1 // Or greater!
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Basic constructor.
|
||||||
|
*
|
||||||
|
* The Console comes with two predefined commands: "quit" and
|
||||||
|
* "exit", which both terminate the console, "help" which prints a
|
||||||
|
* list of all registered commands, and "run" which executes script
|
||||||
|
* files.
|
||||||
|
*
|
||||||
|
* These commands can be overridden or unregistered - but remember
|
||||||
|
* to leave at least one to quit ;).
|
||||||
|
*
|
||||||
|
* @param greeting This represents the prompt of the Console.
|
||||||
|
*/
|
||||||
|
explicit Console(std::string const &greeting);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Basic destructor.
|
||||||
|
*
|
||||||
|
* Frees the history which is been produced by GNU readline.
|
||||||
|
*/
|
||||||
|
~Console();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function registers a new command within the Console.
|
||||||
|
*
|
||||||
|
* If the command already existed, it overwrites the previous entry.
|
||||||
|
*
|
||||||
|
* @param s The name of the command as inserted by the user.
|
||||||
|
* @param f The function that will be called once the user writes the command.
|
||||||
|
*/
|
||||||
|
void registerCommand(const std::string &s, CommandFunction f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function returns a list with the currently available commands.
|
||||||
|
*
|
||||||
|
* @return A vector containing all registered commands names.
|
||||||
|
*/
|
||||||
|
std::vector<std::string> getRegisteredCommands() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the prompt for this Console.
|
||||||
|
*
|
||||||
|
* @param greeting The new greeting.
|
||||||
|
*/
|
||||||
|
void setGreeting(const std::string &greeting);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the current prompt of this Console.
|
||||||
|
*
|
||||||
|
* @return The currently set greeting.
|
||||||
|
*/
|
||||||
|
std::string getGreeting() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function executes an arbitrary string as if it was inserted via stdin.
|
||||||
|
*
|
||||||
|
* @param command The command that needs to be executed.
|
||||||
|
*
|
||||||
|
* @return The result of the operation.
|
||||||
|
*/
|
||||||
|
int executeCommand(const std::string &command);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function calls an external script and executes all commands inside.
|
||||||
|
*
|
||||||
|
* This function stops execution as soon as any single command returns something
|
||||||
|
* different from 0, be it a quit code or an error code.
|
||||||
|
*
|
||||||
|
* @param filename The pathname of the script.
|
||||||
|
*
|
||||||
|
* @return What the last command executed returned.
|
||||||
|
*/
|
||||||
|
int executeFile(const std::string &filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function executes a single command from the user via stdin.
|
||||||
|
*
|
||||||
|
* @return The result of the operation.
|
||||||
|
*/
|
||||||
|
int readLine();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Console(const Console &) = delete;
|
||||||
|
Console(Console &&) = delete;
|
||||||
|
Console &operator=(Console const &) = delete;
|
||||||
|
Console &operator=(Console &&) = delete;
|
||||||
|
|
||||||
|
struct Impl;
|
||||||
|
using PImpl = ::std::unique_ptr<Impl>;
|
||||||
|
PImpl pimpl_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function saves the current state so that some other Console can make use of the GNU readline facilities.
|
||||||
|
*/
|
||||||
|
void saveState();
|
||||||
|
/**
|
||||||
|
* @brief This function reserves the use of the GNU readline facilities to the calling Console instance.
|
||||||
|
*/
|
||||||
|
void reserveConsole();
|
||||||
|
|
||||||
|
// GNU newline interface to our commands.
|
||||||
|
using commandCompleterFunction = char **(const char *text, int start,
|
||||||
|
int end);
|
||||||
|
using commandIteratorFunction = char *(const char *text, int state);
|
||||||
|
|
||||||
|
static commandCompleterFunction getCommandCompletions;
|
||||||
|
static commandIteratorFunction commandIterator;
|
||||||
|
};
|
||||||
|
} // namespace CppReadline
|
||||||
|
|
||||||
|
#endif
|
149
npc/utils/sdb/include/sdb.hpp
Normal file
149
npc/utils/sdb/include/sdb.hpp
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
#ifndef _SDB_SDB_HEADER_FILE_
|
||||||
|
#define _SDB_SDB_HEADER_FILE_
|
||||||
|
|
||||||
|
#include <components.hpp>
|
||||||
|
#include <console.hpp>
|
||||||
|
#include <difftest.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <types.h>
|
||||||
|
|
||||||
|
namespace cr = CppReadline;
|
||||||
|
using ret = cr::Console::ReturnCode;
|
||||||
|
|
||||||
|
namespace SDB {
|
||||||
|
|
||||||
|
enum SDBStatus {
|
||||||
|
SDB_SUCCESS,
|
||||||
|
SDB_WRONG_ARGUMENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Handler {
|
||||||
|
const std::vector<const char *> names;
|
||||||
|
cr::Console::CommandFunction f;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <const DifftestInterface &funcs> class _SDBHandlers {
|
||||||
|
using CPUState = CPUStateBase<uint32_t, 32>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Handler> all_handlers;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static CPUState cpu;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static _SDBHandlers<funcs> *instance;
|
||||||
|
static int cmd_continue(const cr::Console::Arguments &input);
|
||||||
|
static int cmd_step(const std::vector<std::string> &input);
|
||||||
|
static int cmd_info_registers(const std::vector<std::string> &input);
|
||||||
|
static int cmd_print(const std::vector<std::string> &input);
|
||||||
|
_SDBHandlers<funcs>(std::vector<Handler> all_handlers)
|
||||||
|
: all_handlers(all_handlers){};
|
||||||
|
|
||||||
|
public:
|
||||||
|
_SDBHandlers<funcs>(const _SDBHandlers<funcs> &) = delete;
|
||||||
|
_SDBHandlers<funcs> operator=(const _SDBHandlers<funcs> &) = delete;
|
||||||
|
|
||||||
|
static _SDBHandlers<funcs> *getInstance() {
|
||||||
|
if (instance == nullptr) {
|
||||||
|
std::vector<Handler> all_handlers{
|
||||||
|
Handler{{"c", "continue"}, &_SDBHandlers::cmd_continue},
|
||||||
|
Handler{{"si", "step-instruction"}, &_SDBHandlers::cmd_step},
|
||||||
|
};
|
||||||
|
instance = new _SDBHandlers<funcs>(all_handlers);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerHandlers(cr::Console *c);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <const DifftestInterface &funcs>
|
||||||
|
_SDBHandlers<funcs> *_SDBHandlers<funcs>::instance = nullptr;
|
||||||
|
|
||||||
|
template <const DifftestInterface &funcs>
|
||||||
|
CPUState _SDBHandlers<funcs>::cpu = CPUState();
|
||||||
|
|
||||||
|
template <const DifftestInterface &funcs>
|
||||||
|
int _SDBHandlers<funcs>::cmd_continue(const cr::Console::Arguments &input) {
|
||||||
|
if (input.size() > 1)
|
||||||
|
return SDB_WRONG_ARGUMENT;
|
||||||
|
funcs.exec(-1);
|
||||||
|
return SDB_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <const DifftestInterface &funcs>
|
||||||
|
int _SDBHandlers<funcs>::cmd_step(const std::vector<std::string> &input) {
|
||||||
|
if (input.size() > 2) {
|
||||||
|
return SDB_WRONG_ARGUMENT;
|
||||||
|
}
|
||||||
|
uint64_t step_count = input.size() == 2 ? std::stoull(input[1]) : 1;
|
||||||
|
funcs.exec(step_count);
|
||||||
|
return SDB_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <const DifftestInterface &funcs>
|
||||||
|
int _SDBHandlers<funcs>::cmd_info_registers(
|
||||||
|
const std::vector<std::string> &input) {
|
||||||
|
if (input.size() > 1)
|
||||||
|
return SDB_WRONG_ARGUMENT;
|
||||||
|
std::cout << _SDBHandlers<funcs>::getInstance()->cpu << std::endl;
|
||||||
|
return SDB_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <const DifftestInterface &funcs>
|
||||||
|
int _SDBHandlers<funcs>::cmd_print(const std::vector<std::string> &input) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <const DifftestInterface &funcs>
|
||||||
|
void _SDBHandlers<funcs>::registerHandlers(cr::Console *c) {
|
||||||
|
for (auto &h : this->all_handlers) {
|
||||||
|
for (auto &name : h.names) {
|
||||||
|
c->registerCommand(name, h.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <const DifftestInterface &funcs> class SDB {
|
||||||
|
private:
|
||||||
|
std::unique_ptr<CppReadline::Console> c;
|
||||||
|
using SDBHandlers = _SDBHandlers<funcs>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SDB(std::string const &greeting = "\033[1;34m(npc)\033[0m ") {
|
||||||
|
c = std::make_unique<CppReadline::Console>(greeting);
|
||||||
|
SDBHandlers::getInstance()->registerHandlers(c.get());
|
||||||
|
};
|
||||||
|
|
||||||
|
word_t reg_str2val(const char *name, bool *success) {
|
||||||
|
try {
|
||||||
|
*success = true;
|
||||||
|
return SDBHandlers::getInstance()->cpu.at(name);
|
||||||
|
} catch (std::runtime_error) {
|
||||||
|
*success = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main_loop() {
|
||||||
|
int retCode;
|
||||||
|
do {
|
||||||
|
retCode = c->readLine();
|
||||||
|
// We can also change the prompt based on last return value:
|
||||||
|
if (retCode == ret::Ok)
|
||||||
|
c->setGreeting("\033[1;34m(npc)\033[0m ");
|
||||||
|
else
|
||||||
|
c->setGreeting("\033[1;31m(npc)\033[0m ");
|
||||||
|
|
||||||
|
if (retCode == SDB_WRONG_ARGUMENT) {
|
||||||
|
std::cout << "Wrong argument give to command\n";
|
||||||
|
}
|
||||||
|
} while (retCode != ret::Quit);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace SDB
|
||||||
|
|
||||||
|
#endif
|
10
npc/utils/sdb/sdb.cpp
Normal file
10
npc/utils/sdb/sdb.cpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include <components.hpp>
|
||||||
|
#include <console.hpp>
|
||||||
|
#include <difftest.hpp>
|
||||||
|
#include <sdb.hpp>
|
||||||
|
#include <types.h>
|
||||||
|
|
||||||
|
namespace cr = CppReadline;
|
||||||
|
using ret = cr::Console::ReturnCode;
|
||||||
|
|
||||||
|
namespace SDB {}
|
Loading…
Add table
Add a link
Reference in a new issue