From 84c8de84618a555904095f3bfbd2f0d1c01fe319 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Fri, 29 Mar 2024 10:35:49 +0800 Subject: [PATCH 01/10] npc: reg file access through vpi --- npc/CMakeLists.txt | 161 ++++++++++++------ npc/core/build.sbt | 12 +- .../main/scala/{Main.scala => FlowMain.scala} | 13 +- npc/core/src/main/scala/Mem.scala | 1 + npc/core/src/main/scala/RegisterFile.scala | 74 +++----- npc/core/src/main/scala/top/ArgParse.scala | 47 +++++ npc/core/src/main/scala/top/Config.scala | 16 ++ npc/core/src/main/scala/top/Main.scala | 72 ++++++++ npc/core/src/main/scala/utils/DPI.scala | 11 ++ npc/csrc/Flow/main.cpp | 113 ++++++++---- npc/flake.nix | 3 +- 11 files changed, 378 insertions(+), 145 deletions(-) rename npc/core/src/main/scala/{Main.scala => FlowMain.scala} (93%) create mode 100644 npc/core/src/main/scala/Mem.scala create mode 100644 npc/core/src/main/scala/top/ArgParse.scala create mode 100644 npc/core/src/main/scala/top/Config.scala create mode 100644 npc/core/src/main/scala/top/Main.scala create mode 100644 npc/core/src/main/scala/utils/DPI.scala diff --git a/npc/CMakeLists.txt b/npc/CMakeLists.txt index db9d0a1..bd56708 100644 --- a/npc/CMakeLists.txt +++ b/npc/CMakeLists.txt @@ -1,91 +1,157 @@ -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.26) project(flow) -set (CMAKE_CXX_STANDARD 11) +set (CMAKE_CXX_STANDARD 14) cmake_policy(SET CMP0144 NEW) -execute_process( - COMMAND ${CMAKE_SOURCE_DIR}/../git_commit.sh "configure(npc)" - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/.. -) +include(CMakeDependentOption) +enable_testing() -find_package(SDL2 REQUIRED) -find_package(SDL2_image REQUIRED) +# -- Build options +option(BUILD_USE_BLOOP "Whether to use bloop to spped up elaborate" ON) +option(BUILD_SIM_TARGET "Whether to build verilator simulation binary" ON) +cmake_dependent_option(BUILD_SIM_NVBOARD_TARGET "Whether to build nvboard target" OFF "BUILD_SIM_TARGET" OFF) +option(ENABLE_YSYX_GIT_TRACKER "Ysyx tracker support" OFF) +set(TOPMODULE "Flow" CACHE STRING "Topmodule name in chisel") -find_package(verilator REQUIRED) +# -- Ysyx tracker, configure +if(ENABLE_YSYX_GIT_TRACKER) + execute_process( + COMMAND ${CMAKE_SOURCE_DIR}/../git_commit.sh "configure(npc)" + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/.. + ) +endif() + +# -- Check dependencies +if(BUILD_SIM_TARGET) + find_package(verilator REQUIRED) +endif() +if(BUILD_SIM_NVBOARD_TARGET) + find_package(SDL2 REQUIRED) + find_package(SDL2_image REQUIRED) +endif() find_library(NVBOARD_LIBRARY NAMES nvboard) find_path(NVBOARD_INCLUDE_DIR NAMES nvboard.h) -set(TOPMODULES "Flow") +# FIXME: all scala source file are tracked here, cause all files to rebuild +# after a source update. +set(SCALA_CORE "${CMAKE_CURRENT_SOURCE_DIR}/core") +set(CHISEL_MODULE_CLASS "${CMAKE_PROJECT_NAME}.${TOPMODULE}") -foreach(TOPMODULE IN LISTS TOPMODULES) - - # FIXME: all scala source file are tracked here, cause all files to rebuild - # after a source update. - set(SCALA_CORE "${CMAKE_CURRENT_SOURCE_DIR}/core") - set(CHISEL_MODULE_CLASS "${CMAKE_PROJECT_NAME}.${TOPMODULE}") - file(GLOB_RECURSE SCALA_CORE_SOURCES "${SCALA_CORE}/src/main/scala/*.scala") - file(GLOB_RECURSE SCALA_CORE_TEST_SOURCES "${SCALA_CORE}/src/test/scala/*.scala") +# Verilog files are generted in CHISEL_OUTPUT_TMP_DIR and copy to +# CHISEL_OUTPUT_DIR if content changes +set(CHISEL_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc/) +set(CHISEL_OUTPUT_TMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc_tmp/) - # Configure time verilog source generation for verilator - execute_process( - COMMAND sbt "runMain circt.stage.ChiselMain --target-dir ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc --module ${CHISEL_MODULE_CLASS} --target verilog" - WORKING_DIRECTORY ${SCALA_CORE} - ) +set(CHISEL_OUTPUT_VERILATOR_CONF ${CHISEL_OUTPUT_DIR}/conf.vlt) +set(CHISEL_OUTPUT_TOPMODULE ${CHISEL_OUTPUT_DIR}/${TOPMODULE}.sv) +set(CHISEL_EMIT_ARGS "--target-dir ${CHISEL_OUTPUT_TMP_DIR}") +# -- Add an always run target to generate verilog files with sbt/bloop, +# as we don't know if the result files will be different from cmake +# NOTE: Must reconfigure if we add new files in SCALA_CORE directory +file(GLOB_RECURSE SCALA_CORE_SOURCES "${SCALA_CORE}/src/main/scala/*.scala") +file(GLOB_RECURSE SCALA_CORE_RESOURCES "${SCALA_CORE}/src/resource/*") +set(CHISEL_DEPENDENCY ${SCALA_CORE_SOURCES} ${SCALA_CORE_RESOURCE} ${SCALA_CORE}/build.sbt) + +if(BUILD_USE_BLOOP) + set(CHISEL_TARGET bloop_${TOPMODULE}) + set(CHISEL_TEST_TARGET bloop_${TOPMODULE}_test) + # Export sbt build config to bloop + if(NOT EXISTS ${SCALA_CORE}/.bloop) + execute_process( + COMMAND sbt bloopInstall + WORKING_DIRECTORY ${SCALA_CORE} + ) + endif() + string(REPLACE " " ";" CHISEL_EMIT_ARGS_LIST ${CHISEL_EMIT_ARGS}) + list(TRANSFORM CHISEL_EMIT_ARGS_LIST PREPEND "--args;") add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc/${TOPMODULE}.v - COMMAND sbt "runMain circt.stage.ChiselMain --target-dir ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc --module ${CHISEL_MODULE_CLASS} --target verilog" + OUTPUT ${CHISEL_OUTPUT_TOPMODULE} + COMMAND bloop run root ${CHISEL_EMIT_ARGS_LIST} + COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} WORKING_DIRECTORY ${SCALA_CORE} - DEPENDS ${SCALA_CORE_SOURCES} + DEPENDS ${CHISEL_DEPENDENCY} + COMMAND_EXPAND_LISTS ) - - add_custom_target( - ChiselBuild_${TOPMODULE} - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc/${TOPMODULE}.v + add_test( + NAME bloop_${TOPMODULE}_test + COMMAND bloop test + WORKING_DIRECTORY ${SCALA_CORE} ) +else() + set(CHISEL_TARGET sbt_${TOPMODULE}) + set(CHISEL_TEST_TARGET sbt_${TOPMODULE}_test) + add_custom_command( + OUTPUT ${CHISEL_OUTPUT_TOPMODULE} + COMMAND sbt "run ${CHISEL_EMIT_ARGS}" + COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} + WORKING_DIRECTORY ${SCALA_CORE} + DEPENDS ${CHISEL_DEPENDENCY} + VERBATIM + ) + add_test( + NAME sbt_${TOPMODULE}_test + COMMAND sbt test + WORKING_DIRECTORY ${SCALA_CORE} + ) +endif() - # -- Build NVBoard executable +if(NOT EXISTS ${CHISEL_OUTPUT_TOPMODULE}) + # Probably cold build, generate verilog at configure time to produce top module file + execute_process( + COMMAND sbt "run ${CHISEL_EMIT_ARGS}" + WORKING_DIRECTORY ${SCALA_CORE} + ) + execute_process( + COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} + ) +endif() +# -- Build NVBoard executable +if(BUILD_SIM_NVBOARD_TARGET) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp COMMAND auto_pin_bind ${CMAKE_SOURCE_DIR}/constr/${TOPMODULE}.nxdc ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp DEPENDS ${CMAKE_SOURCE_DIR}/constr/${TOPMODULE}.nxdc ) - unset(SOURCES) file(GLOB_RECURSE SOURCES csrc_nvboard/${TOPMODULE}/*.cpp) add_executable(V${TOPMODULE}_nvboard ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp) verilate(V${TOPMODULE}_nvboard TRACE THREADS TOP_MODULE ${TOPMODULE} PREFIX V${TOPMODULE} - SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc/${TOPMODULE}.v) + SOURCES ${CHISEL_OUTPUT_TOPMODULE} + INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc) add_dependencies(V${TOPMODULE}_nvboard ChiselBuild_${TOPMODULE}) target_include_directories(V${TOPMODULE}_nvboard PRIVATE ${NVBOARD_INCLUDE_DIR} ${SDL2_INCLUDE_DIRS}) target_link_libraries(V${TOPMODULE}_nvboard PRIVATE ${NVBOARD_LIBRARY} SDL2::SDL2 SDL2_image::SDL2_image) install(TARGETS V${TOPMODULE}_nvboard) +endif() - # -- Build Verilator executable and add to test +# -- Build Verilator executable and add to test +file(GLOB_RECURSE SOURCES csrc/${TOPMODULE}/*.cpp) +add_executable(V${TOPMODULE} ${SOURCES}) - unset(SOURCES) - file(GLOB_RECURSE SOURCES csrc/${TOPMODULE}/*.cpp) - add_executable(V${TOPMODULE} ${SOURCES}) +verilate(V${TOPMODULE} TRACE COVERAGE THREADS + TOP_MODULE ${TOPMODULE} + PREFIX V${TOPMODULE} + SOURCES ${CHISEL_OUTPUT_TOPMODULE} ${CHISEL_OUTPUT_VERILATOR_CONF} + INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc + VERILATOR_ARGS + "--vpi" # Enable VPI + +) - verilate(V${TOPMODULE} TRACE COVERAGE THREADS - TOP_MODULE ${TOPMODULE} - PREFIX V${TOPMODULE} - SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc/${TOPMODULE}.v) - add_dependencies(V${TOPMODULE} ChiselBuild_${TOPMODULE}) +add_test(NAME V${TOPMODULE} COMMAND V${TOPMODULE}) - enable_testing() - add_test(NAME V${TOPMODULE} COMMAND V${TOPMODULE}) - - # -- Add build tracking +# -- Add build tracking +if(ENABLE_YSYX_GIT_TRACKER) add_custom_command( TARGET V${TOPMODULE}_nvboard PRE_BUILD COMMAND ${CMAKE_SOURCE_DIR}/../git_commit.sh "build_${CMAKE_PROJECT_NAME}_V${TOPMODULE}_nvboard" @@ -97,5 +163,4 @@ foreach(TOPMODULE IN LISTS TOPMODULES) COMMAND ${CMAKE_SOURCE_DIR}/../git_commit.sh "build_${CMAKE_PROJECT_NAME}_V${TOPMODULE}" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/.. ) - -endforeach() +endif() diff --git a/npc/core/build.sbt b/npc/core/build.sbt index e04b603..3d0f4f8 100644 --- a/npc/core/build.sbt +++ b/npc/core/build.sbt @@ -3,6 +3,7 @@ ThisBuild / version := "0.1.0" val chiselVersion = "6.2.0" +val circeVersion = "0.14.1" lazy val root = (project in file(".")) .settings( @@ -10,8 +11,13 @@ lazy val root = (project in file(".")) libraryDependencies ++= Seq( "org.chipsalliance" %% "chisel" % chiselVersion, "edu.berkeley.cs" %% "chiseltest" % "6.0.0" % "test", - "com.chuusai" %% "shapeless" % "2.3.3" - ), + "com.chuusai" %% "shapeless" % "2.3.3", + "com.github.scopt" %% "scopt" % "4.1.0", + ) ++ Seq( + "io.circe" %% "circe-core", + "io.circe" %% "circe-generic", + "io.circe" %% "circe-parser" + ).map(_ % circeVersion), scalacOptions ++= Seq( "-language:reflectiveCalls", "-deprecation", @@ -20,4 +26,4 @@ lazy val root = (project in file(".")) "-Ymacro-annotations", ), addCompilerPlugin("org.chipsalliance" % "chisel-plugin" % chiselVersion cross CrossVersion.full), - ) + ) \ No newline at end of file diff --git a/npc/core/src/main/scala/Main.scala b/npc/core/src/main/scala/FlowMain.scala similarity index 93% rename from npc/core/src/main/scala/Main.scala rename to npc/core/src/main/scala/FlowMain.scala index 1ede7e1..ae39e24 100644 --- a/npc/core/src/main/scala/Main.scala +++ b/npc/core/src/main/scala/FlowMain.scala @@ -5,16 +5,17 @@ import chisel3._ import chisel3.util.{MuxLookup, Fill, Decoupled, Counter, Queue, Reverse} import chisel3.util.{SRAM} import chisel3.util.experimental.decode.{decoder, TruthTable} -import chisel3.stage.ChiselOption import chisel3.util.log2Ceil import chisel3.util.BitPat import chisel3.util.Enum -import chisel3.experimental.prefix +import chisel3.experimental.Trace._ import shapeless.{HNil, ::} import shapeless.HList import shapeless.ops.coproduct.Prepend import chisel3.util.{ BinaryMemoryFile, HexMemoryFile } +import chisel3.experimental.Trace + object RV32Inst { private val bp = BitPat val addi = this.bp("b???????_?????_?????_000_?????_00100_11") @@ -73,7 +74,7 @@ class Control(width: Int) extends Module { }) } -import flow.components.{RegisterFile, RegFileInterface, ProgramCounter, ALU} +import flow.components.{RegisterFile, ProgramCounter, ALU} import chisel3.util.experimental.loadMemoryFromFileInline class Flow extends Module { val dataType = UInt(32.W) @@ -87,7 +88,7 @@ class Flow extends Module { memoryFile = HexMemoryFile("./resource/addi.txt") ) val control = Module(new Control(32)) - val reg = RegisterFile(32, dataType, 2, 2) + val reg = Module(new RegisterFile(dataType, 32, 2)) val pc = Module(new ProgramCounter(dataType)) val alu = Module(new ALU(dataType)) @@ -95,6 +96,9 @@ class Flow extends Module { ram.readPorts(0).address := pc.out - 0x80000000L.U val inst = ram.readPorts(0).data + Trace.traceName(reg.control.writeEnable) + dontTouch(reg.control.writeEnable) + import control.pc.SrcSelect._ pc.in.pcSrcs(pStaticNpc.litValue.toInt) := pc.out + 4.U @@ -125,5 +129,6 @@ class Flow extends Module { alu.in.a(aSrcImm.litValue.toInt) := inst(31, 20) alu.in.b := reg.out.src(1) + dontTouch(control.out) } diff --git a/npc/core/src/main/scala/Mem.scala b/npc/core/src/main/scala/Mem.scala new file mode 100644 index 0000000..2a39fce --- /dev/null +++ b/npc/core/src/main/scala/Mem.scala @@ -0,0 +1 @@ +package flow.components \ No newline at end of file diff --git a/npc/core/src/main/scala/RegisterFile.scala b/npc/core/src/main/scala/RegisterFile.scala index 509ceaa..28ec02c 100644 --- a/npc/core/src/main/scala/RegisterFile.scala +++ b/npc/core/src/main/scala/RegisterFile.scala @@ -4,6 +4,7 @@ import chisel3._ import chisel3.util.log2Ceil import chisel3.util.UIntToOH import chisel3.util.MuxLookup +import chisel3.experimental.Trace._ import shapeless.{ HNil, :: } class RegControl extends Bundle { @@ -18,69 +19,34 @@ class RegControl extends Bundle { def ctrlBindPorts: CtrlTypes = { writeEnable :: writeSelect :: HNil } + traceName(writeEnable) } -class RegFileData[T <: Data](size:Int, tpe: T, numReadPorts: Int, numWritePorts: Int) extends Bundle { - val write = new Bundle { - val addr = Input(UInt(size.W)) - val data = Vec(numWritePorts, Input(tpe)) - } - val read = Vec(numReadPorts, new Bundle { - val rs = Input(UInt(size.W)) - val src = Output(tpe) - }) -} - -class RegFileInterface[T <: Data](size: Int, tpe: T, numReadPorts: Int, numWritePorts: Int) extends Bundle { - val control = new RegControl - // val data = new RegFileData(size, tpe, numReadPorts, numWritePorts) - val in = new Bundle { - val writeAddr = Input(UInt(size.W)) - val writeData = Input(Vec(numWritePorts, tpe)) - val rs = Input(Vec(numReadPorts, UInt(size.W))) - } - val out = new Bundle { - val src = Output(Vec(numReadPorts, tpe)) - } -} - -class RegisterFileCore[T <: Data](size: Int, tpe: T, numReadPorts: Int) extends Module { +class RegisterFile[T <: Data](tpe: T, regCount: Int, numReadPorts: Int) extends Module { require(numReadPorts >= 0) - val writePort = IO(new Bundle { - val enable = Input(Bool()) - val addr = Input(UInt(log2Ceil(size).W)) - val data = Input(tpe) + val control = IO(new RegControl) + val dataAddrWidth = log2Ceil(tpe.getWidth).W + 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 out = IO(new Bundle { + val src = Output(Vec(numReadPorts, tpe)) }) - val readPorts = IO(Vec(numReadPorts, new Bundle { - val addr = Input(UInt(log2Ceil(size).W)) - val data = Output(tpe) - })) - val regFile = RegInit(VecInit(Seq.fill(size)(0.U(tpe.getWidth.W)))) - val writeAddrOH = UIntToOH(writePort.addr) + val regResetValue = 0.U(tpe.getWidth.W) + val regFile = RegInit(VecInit(Seq.fill(regCount)(regResetValue))) + val writeAddrOH = UIntToOH(in.writeAddr) + for ((reg, i) <- regFile.zipWithIndex.tail) { - reg := Mux(writeAddrOH(i) && writePort.enable, writePort.data, reg) + reg := Mux(writeAddrOH(i.U(log2Ceil(regCount).W)) && control.writeEnable, in.writeData(control.writeSelect.asUInt), reg) } regFile(0) := 0.U - for (readPort <- readPorts) { - readPort.data := regFile(readPort.addr) + for (port <- 0 until numReadPorts) { + out.src(port) := regFile(in.rs(port).asUInt) } + traceName(regFile) dontTouch(regFile) } - -object RegisterFile { - def apply[T <: Data](size: Int, tpe: T, numReadPorts: Int, numWritePorts: Int): RegFileInterface[T] = { - val core = Module(new RegisterFileCore(size, tpe, numReadPorts)) - val _out = Wire(new RegFileInterface(size, tpe, numReadPorts, numWritePorts)) - val clock = core.clock - for (i <- 0 until numReadPorts) { - core.readPorts(i).addr := _out.in.rs(i) - _out.out.src(i) := core.readPorts(i).data - } - core.writePort.addr := _out.in.writeAddr - core.writePort.data := _out.in.writeData(_out.control.writeSelect.asUInt) - core.writePort.enable := _out.control.writeEnable - _out - } -} diff --git a/npc/core/src/main/scala/top/ArgParse.scala b/npc/core/src/main/scala/top/ArgParse.scala new file mode 100644 index 0000000..b430a70 --- /dev/null +++ b/npc/core/src/main/scala/top/ArgParse.scala @@ -0,0 +1,47 @@ +package flow + +import scopt.{ OParser, DefaultOEffectSetup } +import java.io.File + +case class CliOptions( + targetDir: File = new File("."), + configFile: Option[File] = None, + argsFile: Option[File] = None, + verilatorConfigFileOut: File = new File("conf.vlt"), +) { + val builder = OParser.builder[CliOptions] + val parser = { + import builder._ + OParser.sequence( + programName("flow"), + help("help"), + opt[Option[File]]('c', "config") + .action((x, c) => c.copy(configFile = x)) + .text("JSON Configuration file for generation"), + opt[Option[File]]("args-file") + .action((x, c) => c.copy(argsFile = x)) + .text("Passing file content as args when emit"), + opt[File]('o', "target-dir") + .action((x, c) => c.copy(targetDir = x)) + .text("Output files relative to this path"), + opt[File]("out-verilator-conf") + .action((x, c) => c.copy(verilatorConfigFileOut = x)) + .text("Options needed when simulating with verilator") + ) + } + + def parse(args: Array[String]): CliOptions = { + OParser.runParser(parser, args, CliOptions()) match { + case (result, effects) => + OParser.runEffects(effects, new DefaultOEffectSetup { + // ignore terminate + override def terminate(exitState: Either[String, Unit]): Unit = () + }) + + result match { + case Some(cliOptions: CliOptions) => { return cliOptions } + case _ => { throw new IllegalArgumentException("Wrong command line argument") } + } + } + } +} diff --git a/npc/core/src/main/scala/top/Config.scala b/npc/core/src/main/scala/top/Config.scala new file mode 100644 index 0000000..aa58107 --- /dev/null +++ b/npc/core/src/main/scala/top/Config.scala @@ -0,0 +1,16 @@ +package flow + +import io.circe.generic.JsonCodec + +// Which group of signals to trace +@JsonCodec case class TraceConfig ( + enable: Boolean = false, + registers: Array[Int] = Array(), + mem: Array[(Int, Int)] = Array(), +) + +@JsonCodec case class Config( + // Whether to enable Difftest + enableDifftest: Boolean = true, + traceConfig: TraceConfig = TraceConfig(), +) diff --git a/npc/core/src/main/scala/top/Main.scala b/npc/core/src/main/scala/top/Main.scala new file mode 100644 index 0000000..3c02ff3 --- /dev/null +++ b/npc/core/src/main/scala/top/Main.scala @@ -0,0 +1,72 @@ +package flow + +import flow._ + +import chisel3._ +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 + + +// TODO: Generate verilator config file + +object VerilogMain extends App { + val opt = CliOptions().parse(args) + val topName = "Flow" + + val config: Config = opt.configFile match { + case Some(f) => { + val source = Source.fromFile(f) + val jsonString = source.mkString + source.close() + io.circe.parser.decode[Config](jsonString) match { + case Right(x) => x + case Left(e) => throw e + } + } + case None => Config(traceConfig = TraceConfig(enable = true)) + } + + val annos = (new ChiselStage).execute( + Array("--target-dir", opt.targetDir.toString, "--target", "systemverilog", "--split-verilog"), + Seq( + + ) ++ (if(config.traceConfig.enable) Seq(ChiselGeneratorAnnotation(() => new Flow)) else Seq()) + ) + + if(config.traceConfig.enable) { + val dut = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[Flow] + + val verilatorConfigSeq = finalTargetMap(annos) + .values + .flatten + .map(ct => + s"""public_flat_rd -module "${ + ct.tokens.collectFirst { case OfModule(m) => m }.get + }" -var "${ct.tokens.collectFirst { case Ref(r) => r }.get}"""") + finalTargetMap(annos) + .values + .flatten + .foreach( + ct => println(s"""TOP.${ct.circuit}.${ct.path.map { case (Instance(i), _) => i }.mkString(".")}.${ct.tokens.collectFirst { + case Ref(r) => r + }.get}""") + ) + + val verilatorConfigWriter = new PrintWriter(new File(opt.targetDir, opt.verilatorConfigFileOut.toString())) + verilatorConfigWriter.write("`verilator_config\n") + try { + for(ct <- verilatorConfigSeq) { + verilatorConfigWriter.println(ct) + } + } finally { + verilatorConfigWriter.close() + } + } +} \ No newline at end of file diff --git a/npc/core/src/main/scala/utils/DPI.scala b/npc/core/src/main/scala/utils/DPI.scala new file mode 100644 index 0000000..2c83be5 --- /dev/null +++ b/npc/core/src/main/scala/utils/DPI.scala @@ -0,0 +1,11 @@ +package flow.utils + +import chisel3._ +import chisel3.util.HasBlackBoxResource + +// class DiffTester extends BlackBox with HasBlackBoxResource { +// val io = IO(new Bundle { +// val regs = +// }) +// addResource("difftest.v"); +// } diff --git a/npc/csrc/Flow/main.cpp b/npc/csrc/Flow/main.cpp index c708610..465e1f0 100644 --- a/npc/csrc/Flow/main.cpp +++ b/npc/csrc/Flow/main.cpp @@ -1,43 +1,86 @@ +#include +#include #include -#include -#include +#include +#include #include #include -#include +#include +#include #define MAX_SIM_TIME 100 #define VERILATOR_TRACE -int main(int argc, char **argv, char **env) { - int sim_time = 0; - Verilated::commandArgs(argc, argv); +std::vector regsHandle; +int regs[32]; - VFlow *top = new VFlow; - - Verilated::traceEverOn(true); - VerilatedVcdC *m_trace = new VerilatedVcdC; -#ifdef VERILATOR_TRACE - top->trace(m_trace, 5); - m_trace->open("waveform.vcd"); -#endif - for (sim_time = 0; sim_time < 10; sim_time++) { - top->eval(); - top->clock = !top->clock; - top->reset = 1; -#ifdef VERILATOR_TRACE - m_trace->dump(sim_time); -#endif - } - top->reset = 0; - for (sim_time = 10; sim_time < MAX_SIM_TIME; sim_time++) { - top->eval(); - top->clock = !top->clock; -#ifdef VERILATOR_TRACE - m_trace->dump(sim_time); -#endif - } -#ifdef VERILATOR_TRACE - m_trace->close(); -#endif - delete top; - exit(EXIT_SUCCESS); +static void init_vpi_regs() { + std::string regfile = "TOP.Flow.reg_0.regFile_"; + for(int i = 0; i < 32; i++) { + std::string regname = regfile + std::to_string(i); + vpiHandle vh = vpi_handle_by_name((PLI_BYTE8 *)regname.c_str(), NULL); + regsHandle.push_back(vh); + } +} + +static void init_vpi() { + init_vpi_regs(); +} + +static int vpi_get_int(vpiHandle vh) { + s_vpi_value v; + v.format = vpiIntVal; + vpi_get_value(vh, &v); + return v.value.integer; +} + +static void update_regs() { + for(int i = 0; i < 32; i++) { + regs[i] = vpi_get_int(regsHandle[i]); + } +} + +static void print_regs() { + for(int i = 0; i < 32; i++) { + printf("%d: %d\t", i, regs[i]); + if(i % 8 == 7) putchar('\n'); + } + putchar('\n'); +} + +static int sim_time = 0; + +int main(int argc, char **argv, char **env) { + int sim_time = 0; + int posedge_cnt = 0; + Verilated::commandArgs(argc, argv); + + std::unique_ptr top{new VFlow}; + Verilated::traceEverOn(true); + VerilatedVcdC *m_trace = new VerilatedVcdC; +#ifdef VERILATOR_TRACE + top->trace(m_trace, 5); + m_trace->open("waveform.vcd"); +#endif + + init_vpi(); + + top->reset = 0; + for (sim_time = 10; sim_time < MAX_SIM_TIME; sim_time++) { + top->eval(); + top->clock = !top->clock; + if(top->clock == 1) { + // Posedge + ++posedge_cnt; + update_regs(); + print_regs(); + } + +#ifdef VERILATOR_TRACE + m_trace->dump(sim_time); +#endif + } +#ifdef VERILATOR_TRACE + m_trace->close(); +#endif + exit(EXIT_SUCCESS); } diff --git a/npc/flake.nix b/npc/flake.nix index 15b910f..00a98ba 100644 --- a/npc/flake.nix +++ b/npc/flake.nix @@ -20,9 +20,10 @@ CHISEL_FIRTOOL_PATH = "${nixpkgs-circt162.legacyPackages.${system}.circt}/bin"; packages = [ clang-tools - # rnix-lsp + cmake coursier espresso + bloop gdb jre From db660212483c87b225fc76a610f53f5821703062 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Tue, 2 Apr 2024 13:35:29 +0800 Subject: [PATCH 02/10] Class wrapper for regiters --- nemu/src/cpu/cpu-exec.c | 4 +- npc/CMakeLists.txt | 2 +- npc/csrc/Flow/main.cpp | 140 ++++++++++++++++++++++++++-------------- 3 files changed, 95 insertions(+), 51 deletions(-) diff --git a/nemu/src/cpu/cpu-exec.c b/nemu/src/cpu/cpu-exec.c index d2b1f98..097c02f 100644 --- a/nemu/src/cpu/cpu-exec.c +++ b/nemu/src/cpu/cpu-exec.c @@ -83,7 +83,7 @@ static void execute(uint64_t n) { g_nr_guest_inst ++; trace_and_difftest(&s, cpu.pc); if (wp_eval_all()) { - puts(logbuf[logbuf_rear]); + IFDEF(CONFIG_ITRACE, puts(logbuf[logbuf_rear])); break; } if (nemu_state.state != NEMU_RUNNING) break; @@ -132,7 +132,7 @@ void cpu_exec(uint64_t n) { ANSI_FMT("HIT BAD TRAP", ANSI_FG_RED))), nemu_state.halt_pc); if(nemu_state.halt_ret != 0) { - log_itrace_print(); + IFDEF(CONFIG_ITRACE, log_itrace_print()); } } // fall through case NEMU_QUIT: statistic(); diff --git a/npc/CMakeLists.txt b/npc/CMakeLists.txt index bd56708..b40fa44 100644 --- a/npc/CMakeLists.txt +++ b/npc/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.26) project(flow) -set (CMAKE_CXX_STANDARD 14) +set (CMAKE_CXX_STANDARD 17) cmake_policy(SET CMP0144 NEW) include(CMakeDependentOption) diff --git a/npc/csrc/Flow/main.cpp b/npc/csrc/Flow/main.cpp index 465e1f0..3fd0129 100644 --- a/npc/csrc/Flow/main.cpp +++ b/npc/csrc/Flow/main.cpp @@ -1,3 +1,8 @@ +#include "VFlow___024root.h" +#include "tracer.h" +#include +#include +#include #include #include #include @@ -13,40 +18,90 @@ std::vector regsHandle; int regs[32]; -static void init_vpi_regs() { - std::string regfile = "TOP.Flow.reg_0.regFile_"; - for(int i = 0; i < 32; i++) { - std::string regname = regfile + std::to_string(i); - vpiHandle vh = vpi_handle_by_name((PLI_BYTE8 *)regname.c_str(), NULL); - regsHandle.push_back(vh); +template +class Tracer { +#ifdef VERILATOR_TRACE + std::shared_ptr top; + std::unique_ptr m_trace; + uint64_t time = 0; +#endif + public: + Tracer(std::shared_ptr top, std::filesystem::path wavefile) { +#ifdef VERILATOR_TRACE + top = top; + Verilated::traceEverOn(true); + m_trace = std::make_unique(); + top->trace(m_trace.get(), 5); + m_trace->open(wavefile.c_str()); +#endif + } + ~Tracer() { +#ifdef VERILATOR_TRACE + m_trace->close(); +#endif + } + + /** + * Dump signals to waveform file. Must be called once after every top->eval() call. + */ + void update() { +#ifdef VERILATOR_TRACE + m_trace->dump(time++); +#endif + } +}; +template +class _RegistersBase { + std::array regs; + virtual T fetch_reg(size_t id); + public: + void update() { + for(int i = 0; i < regs.size(); i++) { + regs[i] = fetch_reg(i); + } + } + void print_regs() { + for(int i = 0; i < regs.size(); i++) { + printf("%d: %d\t", i, regs[i]); + if(i % 8 == 7) putchar('\n'); + } + putchar('\n'); + } +}; + +template +class _RegistersVPI : public _RegistersBase { + std::array reg_handles; + T fetch_reg(size_t id) { + s_vpi_value v; + v.format = vpiIntVal; + vpi_get_value(reg_handles[id], &v); + return v.value.integer; } -} + public: + _RegistersVPI(const std::string regs_prefix) { + 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(), NULL); + reg_handles[i] = vh; + } + } +}; -static void init_vpi() { - init_vpi_regs(); -} - -static int vpi_get_int(vpiHandle vh) { - s_vpi_value v; - v.format = vpiIntVal; - vpi_get_value(vh, &v); - return v.value.integer; -} - -static void update_regs() { - for(int i = 0; i < 32; i++) { - regs[i] = vpi_get_int(regsHandle[i]); +template +class Memory { + std::array mem; + size_t addr_to_index(size_t addr) { + // Linear mapping + return addr - 0x80000000; } -} - -static void print_regs() { - for(int i = 0; i < 32; i++) { - printf("%d: %d\t", i, regs[i]); - if(i % 8 == 7) putchar('\n'); - } - putchar('\n'); -} + public: + const T& operator[](size_t addr) { + return mem[addr_to_index(index)]; + } +}; +typedef _RegistersVPI Registers; static int sim_time = 0; int main(int argc, char **argv, char **env) { @@ -54,33 +109,22 @@ int main(int argc, char **argv, char **env) { int posedge_cnt = 0; Verilated::commandArgs(argc, argv); - std::unique_ptr top{new VFlow}; - Verilated::traceEverOn(true); - VerilatedVcdC *m_trace = new VerilatedVcdC; -#ifdef VERILATOR_TRACE - top->trace(m_trace, 5); - m_trace->open("waveform.vcd"); -#endif + auto top = std::make_shared(); + auto top_tracer = std::make_unique>(top, "waveform.vcd"); - init_vpi(); + Registers regs("TOP.Flow.reg_0.regFile_"); top->reset = 0; + top->eval(); for (sim_time = 10; sim_time < MAX_SIM_TIME; sim_time++) { - top->eval(); top->clock = !top->clock; if(top->clock == 1) { // Posedge ++posedge_cnt; - update_regs(); - print_regs(); + regs.update(); + regs.print_regs(); } - -#ifdef VERILATOR_TRACE - m_trace->dump(sim_time); -#endif + top->eval(); } -#ifdef VERILATOR_TRACE - m_trace->close(); -#endif exit(EXIT_SUCCESS); } From a91adb3a7dc636ee61a714aad97bdff424d94569 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Tue, 2 Apr 2024 14:30:14 +0800 Subject: [PATCH 03/10] wrap verilated model --- npc/csrc/Flow/main.cpp | 69 ++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/npc/csrc/Flow/main.cpp b/npc/csrc/Flow/main.cpp index 3fd0129..536716d 100644 --- a/npc/csrc/Flow/main.cpp +++ b/npc/csrc/Flow/main.cpp @@ -1,12 +1,11 @@ -#include "VFlow___024root.h" -#include "tracer.h" #include #include +#include #include +#include #include #include #include -#include #include #include #include @@ -15,18 +14,15 @@ #define MAX_SIM_TIME 100 #define VERILATOR_TRACE -std::vector regsHandle; -int regs[32]; - template class Tracer { #ifdef VERILATOR_TRACE std::shared_ptr top; std::unique_ptr m_trace; - uint64_t time = 0; + uint64_t cycle = 0; #endif public: - Tracer(std::shared_ptr top, std::filesystem::path wavefile) { + Tracer(T *top, std::filesystem::path wavefile) { #ifdef VERILATOR_TRACE top = top; Verilated::traceEverOn(true); @@ -46,7 +42,7 @@ class Tracer { */ void update() { #ifdef VERILATOR_TRACE - m_trace->dump(time++); + m_trace->dump(cycle++); #endif } }; @@ -88,6 +84,8 @@ class _RegistersVPI : public _RegistersBase { } }; +typedef _RegistersVPI Registers; + template class Memory { std::array mem; @@ -101,30 +99,57 @@ class Memory { } }; -typedef _RegistersVPI Registers; -static int sim_time = 0; +template +class VlModuleInterfaceCommon : public T { + uint64_t sim_time = 0; + uint64_t posedge_cnt = 0; + std::unique_ptr> tracer; + public: + VlModuleInterfaceCommon(bool do_trace, std::filesystem::path wavefile = "waveform.vcd") { + if(do_trace) tracer = std::make_unique>(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; + } +}; + +typedef VlModuleInterfaceCommon VlModule; int main(int argc, char **argv, char **env) { - int sim_time = 0; - int posedge_cnt = 0; Verilated::commandArgs(argc, argv); - auto top = std::make_shared(); - auto top_tracer = std::make_unique>(top, "waveform.vcd"); + auto top = std::make_shared(false, "waveform.vcd"); Registers regs("TOP.Flow.reg_0.regFile_"); - top->reset = 0; - top->eval(); - for (sim_time = 10; sim_time < MAX_SIM_TIME; sim_time++) { - top->clock = !top->clock; - if(top->clock == 1) { + top->reset_eval(10); + for (int i = 0; i < MAX_SIM_TIME; i++) { + if(top->is_posedge()) { // Posedge - ++posedge_cnt; regs.update(); regs.print_regs(); } top->eval(); } - exit(EXIT_SUCCESS); + return 0; } From a478ef76391e96557ce87a900505efb9598fdf37 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Tue, 2 Apr 2024 16:15:16 +0800 Subject: [PATCH 04/10] export memory read and write functions --- npc/csrc/Flow/main.cpp | 45 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/npc/csrc/Flow/main.cpp b/npc/csrc/Flow/main.cpp index 536716d..ef02605 100644 --- a/npc/csrc/Flow/main.cpp +++ b/npc/csrc/Flow/main.cpp @@ -85,20 +85,59 @@ class _RegistersVPI : public _RegistersBase { }; typedef _RegistersVPI Registers; - template class Memory { std::array mem; size_t addr_to_index(size_t addr) { // Linear mapping - return addr - 0x80000000; + return (addr >> 2) - 0x20000000; + } + uint32_t expand_bits(uint8_t bits) { + uint32_t x = bits; + x = (x | (x << 7) | (x << 14) | (x << 21)) & 0x01010101; + x = x * 0xFF; + // printf("expand: %hhx->%x\n", bits, x); + return x; } public: const T& operator[](size_t addr) { - return mem[addr_to_index(index)]; + return this->read(addr); + } + /** + * Always reads and returns 4 bytes from the address raddr & ~0x3u. + */ + T read(int raddr) { + return mem[addr_to_index(raddr)]; + } + /** + * Always writes to the 4 bytes at the address `waddr` & ~0x3u. + * Each bit in `wmask` represents a mask for one byte in wdata. + * For example, wmask = 0x3 means only the lowest 2 bytes are written, + * and the other bytes in memory remain unchanged. + */ + void write(int waddr, T wdata, char wmask) { + mem[addr_to_index((uint32_t)waddr)] = expand_bits(wmask) & wdata; } }; +extern "C" { + void *pmem_get() { + static auto pmem = new Memory; + return pmem; + } + int pmem_read(int raddr) { + void *pmem = pmem_get(); + auto mem = static_cast*>(pmem); + return mem->read(raddr); + } + + void pmem_write(int waddr, int wdata, char wmask) { + void *pmem = pmem_get(); + auto mem = static_cast*>(pmem); + return mem->write((size_t)waddr, wdata, wmask); + } +} + template class VlModuleInterfaceCommon : public T { uint64_t sim_time = 0; From 97cf418c86d555507d052ea9aa8177e4a4413b1d Mon Sep 17 00:00:00 2001 From: xinyangli Date: Wed, 3 Apr 2024 22:39:33 +0800 Subject: [PATCH 05/10] refactor: move to dpi module for memory access --- .gitignore | 1 + npc/.clang-format | 2 + npc/.gitignore | 1 + npc/CMakeLists.txt | 16 +- npc/core/src/main/resources/RamDpi.v | 24 +++ npc/core/src/main/scala/FlowMain.scala | 31 ++-- npc/core/src/main/scala/Mem.scala | 25 ++- npc/core/src/main/scala/RegisterFile.scala | 4 +- npc/csrc/Flow/components.hpp | 101 +++++++++++ npc/csrc/Flow/config.cpp | 27 +++ npc/csrc/Flow/config.hpp | 19 ++ npc/csrc/Flow/main.cpp | 200 +++------------------ npc/csrc/Flow/vl_wrapper.hpp | 65 +++++++ npc/flake.lock | 113 +++++++++++- npc/flake.nix | 21 ++- 15 files changed, 443 insertions(+), 207 deletions(-) create mode 100644 npc/.clang-format create mode 100644 npc/core/src/main/resources/RamDpi.v create mode 100644 npc/csrc/Flow/components.hpp create mode 100644 npc/csrc/Flow/config.cpp create mode 100644 npc/csrc/Flow/config.hpp create mode 100644 npc/csrc/Flow/vl_wrapper.hpp diff --git a/.gitignore b/.gitignore index 44a51ce..7a06b53 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ /nvboard **/.cache **/result +/.pre-commit-config.yaml diff --git a/npc/.clang-format b/npc/.clang-format new file mode 100644 index 0000000..f6b8fdf --- /dev/null +++ b/npc/.clang-format @@ -0,0 +1,2 @@ +--- +BasedOnStyle: LLVM diff --git a/npc/.gitignore b/npc/.gitignore index fd7efb6..e0793d8 100644 --- a/npc/.gitignore +++ b/npc/.gitignore @@ -15,3 +15,4 @@ hs_err_pid* .vscode/ .direnv/ compile_commands.json + diff --git a/npc/CMakeLists.txt b/npc/CMakeLists.txt index b40fa44..76fcc5e 100644 --- a/npc/CMakeLists.txt +++ b/npc/CMakeLists.txt @@ -30,16 +30,17 @@ if(BUILD_SIM_NVBOARD_TARGET) find_package(SDL2 REQUIRED) find_package(SDL2_image REQUIRED) endif() +find_package(CLI11 CONFIG REQUIRED) find_library(NVBOARD_LIBRARY NAMES nvboard) find_path(NVBOARD_INCLUDE_DIR NAMES nvboard.h) -# FIXME: all scala source file are tracked here, cause all files to rebuild -# after a source update. +# FIXME: all scala source file are tracked here, cause all files to rebuild +# after a source update. set(SCALA_CORE "${CMAKE_CURRENT_SOURCE_DIR}/core") set(CHISEL_MODULE_CLASS "${CMAKE_PROJECT_NAME}.${TOPMODULE}") -# Verilog files are generted in CHISEL_OUTPUT_TMP_DIR and copy to +# Verilog files are generted in CHISEL_OUTPUT_TMP_DIR and copy to # CHISEL_OUTPUT_DIR if content changes set(CHISEL_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc/) set(CHISEL_OUTPUT_TMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc_tmp/) @@ -52,8 +53,8 @@ set(CHISEL_EMIT_ARGS "--target-dir ${CHISEL_OUTPUT_TMP_DIR}") # as we don't know if the result files will be different from cmake # NOTE: Must reconfigure if we add new files in SCALA_CORE directory file(GLOB_RECURSE SCALA_CORE_SOURCES "${SCALA_CORE}/src/main/scala/*.scala") -file(GLOB_RECURSE SCALA_CORE_RESOURCES "${SCALA_CORE}/src/resource/*") -set(CHISEL_DEPENDENCY ${SCALA_CORE_SOURCES} ${SCALA_CORE_RESOURCE} ${SCALA_CORE}/build.sbt) +file(GLOB_RECURSE SCALA_CORE_RESOURCES "${SCALA_CORE}/src/main/resource/*") +set(CHISEL_DEPENDENCY ${SCALA_CORE_SOURCES} ${SCALA_CORE_RESOURCES} ${SCALA_CORE}/build.sbt) if(BUILD_USE_BLOOP) set(CHISEL_TARGET bloop_${TOPMODULE}) @@ -105,7 +106,7 @@ if(NOT EXISTS ${CHISEL_OUTPUT_TOPMODULE}) WORKING_DIRECTORY ${SCALA_CORE} ) execute_process( - COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} ) endif() @@ -114,7 +115,7 @@ if(BUILD_SIM_NVBOARD_TARGET) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp COMMAND auto_pin_bind ${CMAKE_SOURCE_DIR}/constr/${TOPMODULE}.nxdc ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp - DEPENDS ${CMAKE_SOURCE_DIR}/constr/${TOPMODULE}.nxdc + DEPENDS ${CMAKE_SOURCE_DIR}/constr/${TOPMODULE}.nxdc ) file(GLOB_RECURSE SOURCES csrc_nvboard/${TOPMODULE}/*.cpp) @@ -144,7 +145,6 @@ verilate(V${TOPMODULE} TRACE COVERAGE THREADS INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc VERILATOR_ARGS "--vpi" # Enable VPI - ) diff --git a/npc/core/src/main/resources/RamDpi.v b/npc/core/src/main/resources/RamDpi.v new file mode 100644 index 0000000..d6e3c98 --- /dev/null +++ b/npc/core/src/main/resources/RamDpi.v @@ -0,0 +1,24 @@ +import "DPI-C" function int pmem_read(input int addr); +import "DPI-C" function void pmem_write(input int waddr, input int wdata, input byte wmask); + +module RamDpi ( + input writeEnable, + input valid, + input [31:0] writeAddr, + input [31:0] writeData, + input [3:0] writeMask, + input reg [31:0] readAddr, + output reg [31:0] readData +); + always @(*) begin + if (valid) begin // 有读写请求时 + readData = pmem_read(readAddr); + if (writeEnable) begin // 有写请求时 + pmem_write(writeAddr, writeData, { 4'h0, writeMask }); + end + end + else begin + readData = 0; + end + end +endmodule \ No newline at end of file diff --git a/npc/core/src/main/scala/FlowMain.scala b/npc/core/src/main/scala/FlowMain.scala index ae39e24..9f1cf4e 100644 --- a/npc/core/src/main/scala/FlowMain.scala +++ b/npc/core/src/main/scala/FlowMain.scala @@ -74,27 +74,18 @@ class Control(width: Int) extends Module { }) } -import flow.components.{RegisterFile, ProgramCounter, ALU} +import flow.components.{RegisterFile, ProgramCounter, ALU, RamDpi} import chisel3.util.experimental.loadMemoryFromFileInline class Flow extends Module { val dataType = UInt(32.W) - - val ram = SRAM( - size = 1024, - tpe = dataType, - numReadPorts = 2, - numWritePorts = 1, - numReadwritePorts = 0, - memoryFile = HexMemoryFile("./resource/addi.txt") - ) + 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)) - ram.readPorts(0).enable := true.B - ram.readPorts(0).address := pc.out - 0x80000000L.U - val inst = ram.readPorts(0).data + ram.io.readAddr := pc.out + val inst = ram.io.readData Trace.traceName(reg.control.writeEnable) dontTouch(reg.control.writeEnable) @@ -111,18 +102,20 @@ class Flow extends Module { import control.reg.WriteSelect._ reg.in.writeData(rAluOut.litValue.toInt) := alu.out.result + // TODO: Read address in load command goes here - ram.readPorts(1).enable := false.B - ram.readPorts(1).address := 0.U - reg.in.writeData(rMemOut.litValue.toInt) := ram.readPorts(1).data + reg.in.writeData(rMemOut.litValue.toInt) := DontCare + reg.in.writeAddr := inst(11, 7) reg.in.rs(0) := inst(19, 15) reg.in.rs(1) := inst(24, 20) // TODO: Memory write goes here - ram.writePorts(0).address := 1.U - ram.writePorts(0).data := 1.U - ram.writePorts(0).enable := false.B + ram.io.writeAddr := DontCare + ram.io.writeData := DontCare + ram.io.writeMask := DontCare + ram.io.writeEnable := false.B + ram.io.valid := true.B import control.alu.SrcSelect._ alu.in.a(aSrcRs1.litValue.toInt) := reg.out.src(0) diff --git a/npc/core/src/main/scala/Mem.scala b/npc/core/src/main/scala/Mem.scala index 2a39fce..6634edb 100644 --- a/npc/core/src/main/scala/Mem.scala +++ b/npc/core/src/main/scala/Mem.scala @@ -1 +1,24 @@ -package flow.components \ No newline at end of file +package flow.components + +import chisel3._ +import chisel3.util.HasBlackBoxPath +import chisel3.util.HasBlackBoxResource +import chisel3.util.log2Ceil +import chisel3.experimental.noPrefix +import scala.collection.SeqMap +import javax.swing.plaf.synth.SynthRadioButtonMenuItemUI + +class RamInterface[T <: Data](tpe: T, addrWidth: Int) extends Bundle { + val valid = Input(Bool()) + val writeEnable = Input(Bool()) + val writeAddr = Input(UInt(addrWidth.W)) + val writeData = Input(tpe) + val writeMask = Input(UInt((addrWidth / 8).W)) + val readAddr = Input(UInt(addrWidth.W)) + val readData = Output(tpe) +} + +class RamDpi extends BlackBox with HasBlackBoxResource { + val io = IO(new RamInterface(UInt(32.W), 32)) + addResource("/RamDpi.v") +} diff --git a/npc/core/src/main/scala/RegisterFile.scala b/npc/core/src/main/scala/RegisterFile.scala index 28ec02c..9b50375 100644 --- a/npc/core/src/main/scala/RegisterFile.scala +++ b/npc/core/src/main/scala/RegisterFile.scala @@ -12,7 +12,7 @@ class RegControl extends Bundle { val rAluOut, rMemOut = Value } - val writeEnable = Input(Bool()) + val writeEnable = Input(Bool()) val writeSelect = Input(WriteSelect()) type CtrlTypes = Bool :: WriteSelect.Type :: HNil @@ -25,7 +25,7 @@ class RegControl extends Bundle { class RegisterFile[T <: Data](tpe: T, regCount: Int, numReadPorts: Int) extends Module { require(numReadPorts >= 0) val control = IO(new RegControl) - val dataAddrWidth = log2Ceil(tpe.getWidth).W + val dataAddrWidth = log2Ceil(regCount).W val in = IO(new Bundle { val writeAddr = Input(UInt(dataAddrWidth)) val writeData = Input(Vec(control.WriteSelect.all.length, tpe)) diff --git a/npc/csrc/Flow/components.hpp b/npc/csrc/Flow/components.hpp new file mode 100644 index 0000000..e0a2186 --- /dev/null +++ b/npc/csrc/Flow/components.hpp @@ -0,0 +1,101 @@ + +#ifndef _NPC_COMPONENTS_H_ +#define _NPC_COMPONENTS_H_ +#include +#include +#include +#include + +template class _RegistersBase { + std::array regs; + virtual T fetch_reg(std::size_t id); + +public: + void update() { + for (int i = 0; i < regs.size(); i++) { + regs[i] = fetch_reg(i); + } + } + void print_regs() { + for (int i = 0; i < regs.size(); i++) { + printf("%d: %d\t", i, regs[i]); + if (i % 8 == 7) + putchar('\n'); + } + putchar('\n'); + } +}; + +template +class _RegistersVPI : public _RegistersBase { + std::array reg_handles; + T fetch_reg(std::size_t id) { + s_vpi_value v; + v.format = vpiIntVal; + vpi_get_value(reg_handles[id], &v); + return v.value.integer; + } + +public: + _RegistersVPI(const std::string regs_prefix) { + 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(), NULL); + reg_handles[i] = vh; + } + } +}; + +template class Memory { + std::array mem; + std::size_t addr_to_index(std::size_t addr) { + if (addr < 0x80000000) { + return 0; + } + // Linear mapping + return (addr >> 2) - 0x20000000; + } + uint32_t expand_bits(uint8_t bits) { + uint32_t x = bits; + x = (x | (x << 7) | (x << 14) | (x << 21)) & 0x01010101; + x = x * 0xFF; + // printf("expand: %hhx->%x\n", bits, x); + return x; + } + +public: + Memory(std::filesystem::path filepath, bool is_binary = true) { + assert(std::filesystem::exists(filepath)); + if (is_binary) { + std::ifstream file(filepath, std::ios::binary); + char *pmem = reinterpret_cast(mem.data()); + file.read(pmem, mem.size() / sizeof(mem[0])); + } else { + std::string line; + std::ifstream file(filepath); + int i = 0; + while (std::getline(file, line)) { + mem[i++] = std::stoul(line, 0, 16); + } + } + } + const T &operator[](std::size_t addr) { return this->read(addr); } + /** + * Always reads and returns 4 bytes from the address raddr & ~0x3u. + */ + T read(int raddr) { + printf("raddr: 0x%x\n", raddr); + return mem[addr_to_index((uint32_t)raddr)]; + } + /** + * Always writes to the 4 bytes at the address `waddr` & ~0x3u. + * Each bit in `wmask` represents a mask for one byte in wdata. + * For example, wmask = 0x3 means only the lowest 2 bytes are written, + * and the other bytes in memory remain unchanged. + */ + void write(int waddr, T wdata, char wmask) { + printf("waddr: 0x%x\n", waddr); + mem[addr_to_index((uint32_t)waddr)] = expand_bits(wmask) & wdata; + } +}; +#endif diff --git a/npc/csrc/Flow/config.cpp b/npc/csrc/Flow/config.cpp new file mode 100644 index 0000000..16b0136 --- /dev/null +++ b/npc/csrc/Flow/config.cpp @@ -0,0 +1,27 @@ +#include "config.hpp" + +void Config::cli_parse(int argc, char **argv) { + CLI::App app; + app.add_option("-m,--memory", memory_file, "Content of memory") + ->required() + ->check(CLI::ExistingFile); + app.add_flag("!--no-bin", memory_file_binary, + "Memory file is in text format"); + app.add_flag("--trace", do_trace, "Enable tracing"); + app.add_option("--wav", wavefile, "output .vcd file path") + ->check([this](const std::string &) { + if (!this->do_trace) + throw CLI::ValidationError( + "dependency", "You must turn on trace before specify wave file"); + return std::string(); + }); + app.add_option("-t", max_sim_time, "Max simulation timestep"); + + try { + app.parse(argc, argv); + } catch (const CLI::ParseError &e) { + exit((app).exit(e)); + } +} + +Config config; diff --git a/npc/csrc/Flow/config.hpp b/npc/csrc/Flow/config.hpp new file mode 100644 index 0000000..3cd4274 --- /dev/null +++ b/npc/csrc/Flow/config.hpp @@ -0,0 +1,19 @@ +#ifndef _NPC_CONFIG_H_ +#define _NPC_CONFIG_H_ +#include +#include +#include +#include + +struct Config { + std::filesystem::path wavefile; + std::filesystem::path memory_file; + uint64_t max_sim_time = 1000; + bool memory_file_binary = {true}; + bool do_trace{false}; + void cli_parse(int argc, char **argv); +}; + +extern Config config; + +#endif \ No newline at end of file diff --git a/npc/csrc/Flow/main.cpp b/npc/csrc/Flow/main.cpp index ef02605..60e66bf 100644 --- a/npc/csrc/Flow/main.cpp +++ b/npc/csrc/Flow/main.cpp @@ -1,189 +1,39 @@ -#include -#include -#include -#include -#include -#include +#include "components.hpp" +#include "config.hpp" +#include "vl_wrapper.hpp" #include -#include -#include -#include -#include -#include -#include -#define MAX_SIM_TIME 100 -#define VERILATOR_TRACE -template -class Tracer { -#ifdef VERILATOR_TRACE - std::shared_ptr top; - std::unique_ptr m_trace; - uint64_t cycle = 0; -#endif - public: - Tracer(T *top, std::filesystem::path wavefile) { -#ifdef VERILATOR_TRACE - top = top; - Verilated::traceEverOn(true); - m_trace = std::make_unique(); - top->trace(m_trace.get(), 5); - m_trace->open(wavefile.c_str()); -#endif - } - ~Tracer() { -#ifdef VERILATOR_TRACE - m_trace->close(); -#endif - } - - /** - * Dump signals to waveform file. Must be called once after every top->eval() call. - */ - void update() { -#ifdef VERILATOR_TRACE - m_trace->dump(cycle++); -#endif - } -}; -template -class _RegistersBase { - std::array regs; - virtual T fetch_reg(size_t id); - public: - void update() { - for(int i = 0; i < regs.size(); i++) { - regs[i] = fetch_reg(i); - } - } - void print_regs() { - for(int i = 0; i < regs.size(); i++) { - printf("%d: %d\t", i, regs[i]); - if(i % 8 == 7) putchar('\n'); - } - putchar('\n'); - } -}; - -template -class _RegistersVPI : public _RegistersBase { - std::array reg_handles; - T fetch_reg(size_t id) { - s_vpi_value v; - v.format = vpiIntVal; - vpi_get_value(reg_handles[id], &v); - return v.value.integer; - } - public: - _RegistersVPI(const std::string regs_prefix) { - 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(), NULL); - reg_handles[i] = vh; - } - } -}; - -typedef _RegistersVPI Registers; -template -class Memory { - std::array mem; - size_t addr_to_index(size_t addr) { - // Linear mapping - return (addr >> 2) - 0x20000000; - } - uint32_t expand_bits(uint8_t bits) { - uint32_t x = bits; - x = (x | (x << 7) | (x << 14) | (x << 21)) & 0x01010101; - x = x * 0xFF; - // printf("expand: %hhx->%x\n", bits, x); - return x; - } - public: - const T& operator[](size_t addr) { - return this->read(addr); - } - /** - * Always reads and returns 4 bytes from the address raddr & ~0x3u. - */ - T read(int raddr) { - return mem[addr_to_index(raddr)]; - } - /** - * Always writes to the 4 bytes at the address `waddr` & ~0x3u. - * Each bit in `wmask` represents a mask for one byte in wdata. - * For example, wmask = 0x3 means only the lowest 2 bytes are written, - * and the other bytes in memory remain unchanged. - */ - void write(int waddr, T wdata, char wmask) { - mem[addr_to_index((uint32_t)waddr)] = expand_bits(wmask) & wdata; - } -}; +using VlModule = VlModuleInterfaceCommon; +using Registers = _RegistersVPI; extern "C" { - void *pmem_get() { - static auto pmem = new Memory; - return pmem; - } - int pmem_read(int raddr) { - void *pmem = pmem_get(); - auto mem = static_cast*>(pmem); - return mem->read(raddr); - } +void *pmem_get() { + static auto pmem = new Memory(config.memory_file, + config.memory_file_binary); + return pmem; +} - void pmem_write(int waddr, int wdata, char wmask) { - void *pmem = pmem_get(); - auto mem = static_cast*>(pmem); - return mem->write((size_t)waddr, wdata, wmask); - } -} +int pmem_read(int raddr) { + void *pmem = pmem_get(); + auto mem = static_cast *>(pmem); + return mem->read(raddr); +} -template -class VlModuleInterfaceCommon : public T { - uint64_t sim_time = 0; - uint64_t posedge_cnt = 0; - std::unique_ptr> tracer; - public: - VlModuleInterfaceCommon(bool do_trace, std::filesystem::path wavefile = "waveform.vcd") { - if(do_trace) tracer = std::make_unique>(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; - } -}; - -typedef VlModuleInterfaceCommon VlModule; +void pmem_write(int waddr, int wdata, char wmask) { + void *pmem = pmem_get(); + auto mem = static_cast *>(pmem); + return mem->write((std::size_t)waddr, wdata, wmask); +} +} int main(int argc, char **argv, char **env) { - Verilated::commandArgs(argc, argv); - - auto top = std::make_shared(false, "waveform.vcd"); - + config.cli_parse(argc, argv); + auto top = std::make_shared(config.do_trace, config.wavefile); Registers regs("TOP.Flow.reg_0.regFile_"); top->reset_eval(10); - for (int i = 0; i < MAX_SIM_TIME; i++) { - if(top->is_posedge()) { + for (int i = 0; i < config.max_sim_time; i++) { + if (top->is_posedge()) { // Posedge regs.update(); regs.print_regs(); diff --git a/npc/csrc/Flow/vl_wrapper.hpp b/npc/csrc/Flow/vl_wrapper.hpp new file mode 100644 index 0000000..4d42b55 --- /dev/null +++ b/npc/csrc/Flow/vl_wrapper.hpp @@ -0,0 +1,65 @@ +#ifndef _NPC_TRACER_H_ +#define _NPC_TRACER_H_ +#include +#include + +template class Tracer { + std::shared_ptr top; + std::unique_ptr m_trace; + uint64_t cycle = 0; + +public: + Tracer(T *top, std::filesystem::path wavefile) { + top = top; + Verilated::traceEverOn(true); + m_trace = std::make_unique(); + 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 class VlModuleInterfaceCommon : public T { + uint64_t sim_time = 0; + uint64_t posedge_cnt = 0; + std::unique_ptr> tracer; + +public: + VlModuleInterfaceCommon(bool do_trace, + std::filesystem::path wavefile = "waveform.vcd") { + if (do_trace) + tracer = std::make_unique>(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 diff --git a/npc/flake.lock b/npc/flake.lock index 76de6c7..f37f6be 100644 --- a/npc/flake.lock +++ b/npc/flake.lock @@ -1,5 +1,21 @@ { "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -18,6 +34,45 @@ "type": "github" } }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1709961763, @@ -50,6 +105,22 @@ "type": "github" } }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1710695816, + "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "614b4613980a522ba49f0d194531beddbb7220d3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, "nur-xin": { "inputs": { "nixpkgs": [ @@ -70,12 +141,37 @@ "url": "https://git.xinyang.life/xin/nur.git" } }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils_2", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1712055707, + "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", "nixpkgs-circt162": "nixpkgs-circt162", - "nur-xin": "nur-xin" + "nur-xin": "nur-xin", + "pre-commit-hooks": "pre-commit-hooks" } }, "systems": { @@ -92,6 +188,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/npc/flake.nix b/npc/flake.nix index 00a98ba..c9eb07d 100644 --- a/npc/flake.nix +++ b/npc/flake.nix @@ -3,6 +3,10 @@ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs-circt162.url = "github:NixOS/nixpkgs/7995cae3ad60e3d6931283d650d7f43d31aaa5c7"; flake-utils.url = "github:numtide/flake-utils"; + pre-commit-hooks = { + url = "github:cachix/pre-commit-hooks.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; nur-xin = { url = "git+https://git.xinyang.life/xin/nur.git"; inputs.nixpkgs.follows = "nixpkgs"; @@ -16,7 +20,21 @@ { nur.xin = nur-xin.legacyPackages.${system}; }; in { + checks = { + pre-commit-check = pre-commit-hooks.lib.${system}.run { + src = ./.; + hooks = { + trim-trailing-whitespace.enable = true; + clang-format = { + enable = true; + types_or = pkgs.lib.mkForce [ "c" "c++" ]; + }; + }; + }; + }; devShells.default = with pkgs; mkShell { + inherit (self.checks.${system}.pre-commit-check) shellHook; + buildInputs = self.checks.${system}.pre-commit-check.enabledPackages; CHISEL_FIRTOOL_PATH = "${nixpkgs-circt162.legacyPackages.${system}.circt}/bin"; packages = [ clang-tools @@ -43,6 +61,7 @@ nur.xin.nvboard nixpkgs-circt162.legacyPackages.${system}.circt yosys + cli11 ]; buildInputs = [ verilator @@ -51,7 +70,7 @@ NEMU_HOME="/home/xin/repo/ysyx-workbench/nemu"; }; - + # This version (1.43.0) of circt does not exist in nixpkgs # and Chisel 5.1.0 specifically build against it, so here we are. # Ref: https://github.com/NixOS/nixpkgs/blob/b6465c8/pkgs/development/compilers/circt/default.nix From 849f2bb5f3d7f31c84051be97bf5020e774a05d5 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Fri, 5 Apr 2024 00:16:58 +0800 Subject: [PATCH 06/10] feat: difftest framework --- .clang-format | 4 + nemu/.clang-format | 3 - nemu/Kconfig | 20 ++++ nemu/include/debug.h | 66 ++++++++----- nemu/src/cpu/difftest/ref.c | 25 +++-- npc/.clang-format | 2 - npc/CMakeLists.txt | 2 + npc/core/src/main/scala/ALU.scala | 4 +- npc/core/src/main/scala/FlowMain.scala | 11 +-- npc/csrc/Flow/components.hpp | 41 ++++---- npc/csrc/Flow/config.cpp | 7 +- npc/csrc/Flow/config.hpp | 3 +- npc/csrc/Flow/main.cpp | 61 ++++++++++-- npc/include/difftest.hpp | 126 +++++++++++++++++++++++++ npc/resource/addi.txt | 27 ++++-- 15 files changed, 318 insertions(+), 84 deletions(-) create mode 100644 .clang-format delete mode 100644 nemu/.clang-format delete mode 100644 npc/.clang-format create mode 100644 npc/include/difftest.hpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..3ba9598 --- /dev/null +++ b/.clang-format @@ -0,0 +1,4 @@ +--- +Language: Cpp +BasedOnStyle: LLVM +ReflowComments: false diff --git a/nemu/.clang-format b/nemu/.clang-format deleted file mode 100644 index 0515c02..0000000 --- a/nemu/.clang-format +++ /dev/null @@ -1,3 +0,0 @@ ---- -Language: Cpp -BasedOnStyle: LLVM \ No newline at end of file diff --git a/nemu/Kconfig b/nemu/Kconfig index 2b316a5..85074bd 100644 --- a/nemu/Kconfig +++ b/nemu/Kconfig @@ -126,6 +126,26 @@ endmenu menu "Testing and Debugging" +choice + prompt "Choose log level" + default LOG_TRACE +config LOG_TRACE + bool "trace" +config LOG_INFO + bool "info" +config LOG_WARNING + bool "warning" +config LOG_ERROR + bool "error" +endchoice + +config LOG_LEVEL + int + default 1 if LOG_ERROR + default 2 if LOG_WARNING + default 3 if LOG_INFO + default 4 if LOG_TRACE + default 0 config TRACE bool "Enable tracer" diff --git a/nemu/include/debug.h b/nemu/include/debug.h index 329c64a..c0f4952 100644 --- a/nemu/include/debug.h +++ b/nemu/include/debug.h @@ -16,41 +16,59 @@ #ifndef __DEBUG_H__ #define __DEBUG_H__ +#include #include #include -#include IFDEF(CONFIG_ITRACE, void log_itrace_print()); -#define Trace(format, ...) \ - _Log("[TRACE] " format "\n", ## __VA_ARGS__) +#if (CONFIG_LOG_LEVEL >= 4) +#define Trace(format, ...) _Log("[TRACE] " format "\n", ##__VA_ARGS__) +#else +#define Trace(format, ...) +#endif -#define Log(format, ...) \ - _Log(ANSI_FMT("[INFO] %s:%d %s() ", ANSI_FG_BLUE) format "\n", \ - __FILE__, __LINE__, __func__, ## __VA_ARGS__) +#if (CONFIG_LOG_LEVEL >= 3) +#define Log(format, ...) \ + _Log(ANSI_FMT("[INFO] %s:%d %s() ", ANSI_FG_BLUE) format "\n", __FILE__, \ + __LINE__, __func__, ##__VA_ARGS__) +#else +#define Log(format, ...) +#endif -#define Warning(format, ...) \ - _Log(ANSI_FMT("[WARNING] %s:%d %s() ", ANSI_FG_YELLOW) format "\n", \ - __FILE__, __LINE__, __func__, ## __VA_ARGS__) +#if (CONFIG_LOG_LEVEL >= 2) +#define Warning(format, ...) \ + _Log(ANSI_FMT("[WARNING] %s:%d %s() ", ANSI_FG_YELLOW) format "\n", \ + __FILE__, __LINE__, __func__, ##__VA_ARGS__) +#else +#define Warning(format, ...) +#endif -#define Error(format, ...) \ - _Log(ANSI_FMT("[ERROR] %s:%d %s() ", ANSI_FG_RED) format "\n", \ - __FILE__, __LINE__, __func__, ## __VA_ARGS__) +#if (CONFIG_LOG_LEVEL >= 1) +#define Error(format, ...) \ + _Log(ANSI_FMT("[ERROR] %s:%d %s() ", ANSI_FG_RED) format "\n", __FILE__, \ + __LINE__, __func__, ##__VA_ARGS__) +#else +#define Error(format, ...) +#endif -#define Assert(cond, format, ...) \ - do { \ - if (!(cond)) { \ - MUXDEF(CONFIG_TARGET_AM, printf(ANSI_FMT(format, ANSI_FG_RED) "\n", ## __VA_ARGS__), \ - (fflush(stdout), fprintf(stderr, ANSI_FMT(format, ANSI_FG_RED) "\n", ## __VA_ARGS__))); \ - IFNDEF(CONFIG_TARGET_AM, extern FILE* log_fp; fflush(log_fp)); \ - IFDEF(CONFIG_ITRACE, log_itrace_print()); \ - extern void assert_fail_msg(); \ - assert_fail_msg(); \ - assert(cond); \ - } \ +#define Assert(cond, format, ...) \ + do { \ + if (!(cond)) { \ + MUXDEF( \ + CONFIG_TARGET_AM, \ + printf(ANSI_FMT(format, ANSI_FG_RED) "\n", ##__VA_ARGS__), \ + (fflush(stdout), fprintf(stderr, ANSI_FMT(format, ANSI_FG_RED) "\n", \ + ##__VA_ARGS__))); \ + IFNDEF(CONFIG_TARGET_AM, extern FILE *log_fp; fflush(log_fp)); \ + IFDEF(CONFIG_ITRACE, log_itrace_print()); \ + extern void assert_fail_msg(); \ + assert_fail_msg(); \ + assert(cond); \ + } \ } while (0) -#define panic(format, ...) Assert(0, format, ## __VA_ARGS__) +#define panic(format, ...) Assert(0, format, ##__VA_ARGS__) #define TODO() panic("please implement me") diff --git a/nemu/src/cpu/difftest/ref.c b/nemu/src/cpu/difftest/ref.c index ef89a61..7d59b4a 100644 --- a/nemu/src/cpu/difftest/ref.c +++ b/nemu/src/cpu/difftest/ref.c @@ -13,25 +13,34 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ -#include +#include "types.h" #include +#include #include +#include #include -__EXPORT void difftest_memcpy(paddr_t addr, void *buf, size_t n, bool direction) { - assert(0); +__EXPORT void difftest_memcpy(paddr_t addr, void *buf, size_t n, + bool direction) { + if (direction == DIFFTEST_TO_REF) { + memcpy(guest_to_host(addr), buf, n); + } else { + assert(0); + } } __EXPORT void difftest_regcpy(void *dut, bool direction) { - assert(0); + // assert(0); + if (direction == DIFFTEST_TO_DUT) + memcpy(dut, &cpu, sizeof(CPU_state)); + else + memcpy(&cpu, dut, sizeof(CPU_state)); } -__EXPORT void difftest_exec(uint64_t n) { - assert(0); -} +__EXPORT void difftest_exec(uint64_t n) { cpu_exec(n); } __EXPORT void difftest_raise_intr(word_t NO) { - assert(0); + // assert(0); } __EXPORT void difftest_init(int port) { diff --git a/npc/.clang-format b/npc/.clang-format deleted file mode 100644 index f6b8fdf..0000000 --- a/npc/.clang-format +++ /dev/null @@ -1,2 +0,0 @@ ---- -BasedOnStyle: LLVM diff --git a/npc/CMakeLists.txt b/npc/CMakeLists.txt index 76fcc5e..0bc466b 100644 --- a/npc/CMakeLists.txt +++ b/npc/CMakeLists.txt @@ -135,6 +135,8 @@ if(BUILD_SIM_NVBOARD_TARGET) endif() # -- Build Verilator executable and add to test +include_directories(include) + file(GLOB_RECURSE SOURCES csrc/${TOPMODULE}/*.cpp) add_executable(V${TOPMODULE} ${SOURCES}) diff --git a/npc/core/src/main/scala/ALU.scala b/npc/core/src/main/scala/ALU.scala index 70f0fea..9a0b0f9 100644 --- a/npc/core/src/main/scala/ALU.scala +++ b/npc/core/src/main/scala/ALU.scala @@ -9,7 +9,7 @@ class ALUControlInterface extends Bundle { val aOpAdd, aOpSub, aOpNot, aOpAnd, aOpOr, aOpXor, aOpSlt, aOpEq, aOpNop = Value } object SrcSelect extends ChiselEnum { - val aSrcRs1, aSrcImm = Value + val aSrcRs2, aSrcImm = Value } val op = Input(OpSelect()) val src = Input(SrcSelect()) @@ -33,7 +33,7 @@ class ALU[T <: UInt](tpe: T) extends Module { val a = in.a(control.src.asUInt) // 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 + in.b val sub = a - in.b val and = a & in.b val not = ~a diff --git a/npc/core/src/main/scala/FlowMain.scala b/npc/core/src/main/scala/FlowMain.scala index 9f1cf4e..73df9ed 100644 --- a/npc/core/src/main/scala/FlowMain.scala +++ b/npc/core/src/main/scala/FlowMain.scala @@ -87,7 +87,6 @@ class Flow extends Module { ram.io.readAddr := pc.out val inst = ram.io.readData - Trace.traceName(reg.control.writeEnable) dontTouch(reg.control.writeEnable) import control.pc.SrcSelect._ @@ -107,8 +106,8 @@ class Flow extends Module { reg.in.writeData(rMemOut.litValue.toInt) := DontCare reg.in.writeAddr := inst(11, 7) - reg.in.rs(0) := inst(19, 15) - reg.in.rs(1) := inst(24, 20) + reg.in.rs(0) := inst(19, 15) // rs1 + reg.in.rs(1) := inst(24, 20) // rs2 // TODO: Memory write goes here ram.io.writeAddr := DontCare @@ -118,10 +117,10 @@ class Flow extends Module { ram.io.valid := true.B import control.alu.SrcSelect._ - alu.in.a(aSrcRs1.litValue.toInt) := reg.out.src(0) + alu.in.a(aSrcRs2.litValue.toInt) := reg.out.src(1) alu.in.a(aSrcImm.litValue.toInt) := inst(31, 20) - alu.in.b := reg.out.src(1) - + alu.in.b := reg.out.src(0) + Trace.traceName(pc.out); dontTouch(control.out) } diff --git a/npc/csrc/Flow/components.hpp b/npc/csrc/Flow/components.hpp index e0a2186..a84309a 100644 --- a/npc/csrc/Flow/components.hpp +++ b/npc/csrc/Flow/components.hpp @@ -1,53 +1,61 @@ #ifndef _NPC_COMPONENTS_H_ #define _NPC_COMPONENTS_H_ +#include "vpi_user.h" #include +#include #include #include +#include #include template class _RegistersBase { std::array regs; + T pc; + virtual T fetch_pc(); virtual T fetch_reg(std::size_t id); public: + T operator[](size_t id) { return fetch_reg(id); } + T get_pc() { return fetch_pc(); } void update() { for (int i = 0; i < regs.size(); i++) { regs[i] = fetch_reg(i); } } - void print_regs() { - for (int i = 0; i < regs.size(); i++) { - printf("%d: %d\t", i, regs[i]); - if (i % 8 == 7) - putchar('\n'); - } - putchar('\n'); - } }; template class _RegistersVPI : public _RegistersBase { std::array reg_handles; - T fetch_reg(std::size_t id) { + vpiHandle pc_handle; + T vpi_get(vpiHandle vh) { s_vpi_value v; v.format = vpiIntVal; - vpi_get_value(reg_handles[id], &v); + 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(const std::string regs_prefix) { + _RegistersVPI(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(), NULL); + 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 class Memory { - std::array mem; std::size_t addr_to_index(std::size_t addr) { if (addr < 0x80000000) { return 0; @@ -64,12 +72,13 @@ template class Memory { } public: + std::array mem; Memory(std::filesystem::path filepath, bool is_binary = true) { assert(std::filesystem::exists(filepath)); if (is_binary) { std::ifstream file(filepath, std::ios::binary); char *pmem = reinterpret_cast(mem.data()); - file.read(pmem, mem.size() / sizeof(mem[0])); + file.read(pmem, mem.size() * sizeof(mem[0])); } else { std::string line; std::ifstream file(filepath); @@ -84,7 +93,7 @@ public: * Always reads and returns 4 bytes from the address raddr & ~0x3u. */ T read(int raddr) { - printf("raddr: 0x%x\n", raddr); + // printf("raddr: 0x%x\n", raddr); return mem[addr_to_index((uint32_t)raddr)]; } /** @@ -94,7 +103,7 @@ public: * and the other bytes in memory remain unchanged. */ void write(int waddr, T wdata, char wmask) { - printf("waddr: 0x%x\n", waddr); + // printf("waddr: 0x%x\n", waddr); mem[addr_to_index((uint32_t)waddr)] = expand_bits(wmask) & wdata; } }; diff --git a/npc/csrc/Flow/config.cpp b/npc/csrc/Flow/config.cpp index 16b0136..4d8338b 100644 --- a/npc/csrc/Flow/config.cpp +++ b/npc/csrc/Flow/config.cpp @@ -9,13 +9,16 @@ void Config::cli_parse(int argc, char **argv) { "Memory file is in text format"); app.add_flag("--trace", do_trace, "Enable tracing"); app.add_option("--wav", wavefile, "output .vcd file path") - ->check([this](const std::string &) { - if (!this->do_trace) + ->check([=](const std::string &) { + if (!do_trace) throw CLI::ValidationError( "dependency", "You must turn on trace before specify wave file"); return std::string(); }); app.add_option("-t", max_sim_time, "Max simulation timestep"); + app.add_option("--diff-lib", lib_ref, + "Dynamic library file of difftest reference") + ->check(CLI::ExistingFile); try { app.parse(argc, argv); diff --git a/npc/csrc/Flow/config.hpp b/npc/csrc/Flow/config.hpp index 3cd4274..9f5873c 100644 --- a/npc/csrc/Flow/config.hpp +++ b/npc/csrc/Flow/config.hpp @@ -6,11 +6,12 @@ #include struct Config { - std::filesystem::path wavefile; 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); }; diff --git a/npc/csrc/Flow/main.cpp b/npc/csrc/Flow/main.cpp index 60e66bf..50eeeeb 100644 --- a/npc/csrc/Flow/main.cpp +++ b/npc/csrc/Flow/main.cpp @@ -1,7 +1,10 @@ #include "components.hpp" #include "config.hpp" #include "vl_wrapper.hpp" +#include "vpi_user.h" #include +#include +#include using VlModule = VlModuleInterfaceCommon; using Registers = _RegistersVPI; @@ -16,6 +19,8 @@ void *pmem_get() { int pmem_read(int raddr) { void *pmem = pmem_get(); auto mem = static_cast *>(pmem); + // TODO: Do memory difftest at memory read and write to diagnose at a finer + // granularity return mem->read(raddr); } @@ -26,19 +31,55 @@ void pmem_write(int waddr, int wdata, char wmask) { } } +VlModule *top; +Registers *regs; +using CPUState = CPUStateBase; +vpiHandle pc = nullptr; +void difftest_memcpy(paddr_t, void *, size_t, bool){}; + +void difftest_regcpy(void *p, bool direction) { + + if (direction == DIFFTEST_FROM_REF) { + ((CPUState *)p)->pc = regs->get_pc(); + for (int i = 0; i < 32; i++) { + ((CPUState *)p)->reg[i] = (*regs)[i]; + } + } +} + +void difftest_exec(uint64_t n) { + while (n--) { + for (int i = 0; i < 2; i++) { + if (top->is_posedge()) { + // Posedge + regs->update(); + } + top->eval(); + } + } +} +void difftest_init(int port) { + // top = std::make_unique(config.do_trace, config.wavefile); + top = new VlModule{config.do_trace, config.wavefile}; + regs = new Registers("TOP.Flow.reg_0.regFile_", "TOP.Flow.pc.out"); + top->reset_eval(10); +} + int main(int argc, char **argv, char **env) { config.cli_parse(argc, argv); - auto top = std::make_shared(config.do_trace, config.wavefile); - Registers regs("TOP.Flow.reg_0.regFile_"); - top->reset_eval(10); - for (int i = 0; i < config.max_sim_time; i++) { - if (top->is_posedge()) { - // Posedge - regs.update(); - regs.print_regs(); - } - top->eval(); + /* -- Difftest -- */ + std::filesystem::path ref{config.lib_ref}; + DifftestInterface dut_interface = DifftestInterface{ + &difftest_memcpy, &difftest_regcpy, &difftest_exec, &difftest_init}; + DifftestInterface ref_interface = DifftestInterface{ref}; + + Difftest> diff{dut_interface, ref_interface, + pmem_get(), 128}; + int t = 8; + while (t--) { + diff.step(1); } + return 0; } diff --git a/npc/include/difftest.hpp b/npc/include/difftest.hpp new file mode 100644 index 0000000..dd690b9 --- /dev/null +++ b/npc/include/difftest.hpp @@ -0,0 +1,126 @@ +#ifndef _DIFFTEST_DIFFTEST_H_ +#define _DIFFTEST_DIFFTEST_H_ +#include +#include +#include +#include +#include +#include +#include +#include + +using paddr_t = uint32_t; +enum { DIFFTEST_FROM_REF, DIFFTEST_TO_REF }; + +struct DifftestInterface { + using memcpy_t = void (*)(paddr_t, void *, size_t, bool); + using regcpy_t = void (*)(void *, bool); + using exec_t = void (*)(uint64_t); + using init_t = void (*)(int); + std::function memcpy; + std::function regcpy; + std::function exec; + std::function init; + + DifftestInterface(memcpy_t memcpy, regcpy_t regcpy, exec_t exec, init_t init) + : memcpy(memcpy), regcpy(regcpy), exec(exec), init(init){}; + + // using fs = std::filesystem::path; + DifftestInterface(std::filesystem::path lib_file) { + void *handle = dlopen(lib_file.c_str(), RTLD_LAZY); + assert(handle != nullptr); + memcpy = (memcpy_t)dlsym(handle, "difftest_memcpy"); + assert(memcpy); + regcpy = (regcpy_t)dlsym(handle, "difftest_regcpy"); + assert(regcpy); + exec = (exec_t)dlsym(handle, "difftest_exec"); + assert(exec); + init = (init_t)dlsym(handle, "difftest_init"); + assert(init); + } +}; + +template class Difftest { + const DifftestInterface &ref; + std::unique_ptr ref_state; + const DifftestInterface &dut; + std::unique_ptr dut_state; + +public: + Difftest(const DifftestInterface &dut, const DifftestInterface &ref, + void *mem, size_t n, std::unique_ptr ref_state = nullptr, + std::unique_ptr dut_state = nullptr) + : ref(ref), dut(dut), ref_state(std::move(ref_state)), + dut_state(std::move(dut_state)) { + if (ref_state == nullptr) + this->ref_state = std::make_unique(); + if (dut_state == nullptr) + this->dut_state = std::make_unique(); + ref.init(0); + dut.init(0); + fetch_state(); + paddr_t reset_vector = 0x80000000; + ref.memcpy(reset_vector, mem, n, DIFFTEST_TO_REF); + dut.memcpy(reset_vector, mem, n, DIFFTEST_TO_REF); + }; + + void fetch_state() { + ref.regcpy(ref_state.get(), DIFFTEST_FROM_REF); + dut.regcpy(dut_state.get(), DIFFTEST_FROM_REF); + } + + void step(uint64_t n) { + ref.exec(n); + dut.exec(n); + fetch_state(); + if (*ref_state != *dut_state) { + std::cout << *this; + exit(EXIT_FAILURE); + } + } + + friend std::ostream &operator<<(std::ostream &os, const Difftest &d) { + os << "REF state:\n" + << *d.ref_state << "DUT state:\n" + << *d.dut_state << std::endl; + return os; + } +}; + +template struct CPUStateBase { + R reg[nr_reg] = {0}; + paddr_t pc = 0x80000000; + CPUStateBase() { + for (int i = 0; i < nr_reg; i++) + reg[i] = 0; + } + bool operator==(const CPUStateBase &other) const { + if (pc != other.pc) + return false; + for (int i = 0; i < nr_reg; ++i) { + if (reg[i] != other.reg[i]) + return false; + } + return true; + } + bool operator!=(const CPUStateBase &other) const { + return !(*this == other); // Reuse the == operator for != implementation + } +}; + +template +std::ostream &operator<<(std::ostream &os, const CPUStateBase &cpu) { + os << "PC: " << std::hex << cpu.pc << std::endl; + for (int i = 0; i < nr_reg; i++) { + os << "reg " << std::dec << std::setw(2) << i << ":" << std::hex + << std::setw(10) << cpu.reg[i]; + if (i % 4 == 3) { + os << std::endl; + } else { + os << " | "; + } + } + return os; +} + +#endif \ No newline at end of file diff --git a/npc/resource/addi.txt b/npc/resource/addi.txt index 8eb11e4..6fb3e74 100644 --- a/npc/resource/addi.txt +++ b/npc/resource/addi.txt @@ -1,10 +1,17 @@ -00114113 -00114113 -00114113 -00114113 -00114113 -00114113 -00114113 -00114113 -00114113 -00114113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 +00110113 From f02d5eb2f1f57d4b0218122be29ba1b85bc6b663 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Fri, 5 Apr 2024 02:12:30 +0800 Subject: [PATCH 07/10] build: split CMakeLists - Put add_custom_command into seperate ChiselBuild module. Otherwise final target cannot correctly depends on scala sources. --- npc/CMakeLists.txt | 109 +++------------------------ npc/cmake/ChiselBuild.cmake | 62 +++++++++++++++ npc/csrc/CMakeLists.txt | 1 + npc/csrc/Flow/CMakeLists.txt | 11 +++ npc/csrc_nvboard/Flow/CMakeLists.txt | 23 ++++++ npc/flake.nix | 1 + 6 files changed, 110 insertions(+), 97 deletions(-) create mode 100644 npc/cmake/ChiselBuild.cmake create mode 100644 npc/csrc/CMakeLists.txt create mode 100644 npc/csrc/Flow/CMakeLists.txt create mode 100644 npc/csrc_nvboard/Flow/CMakeLists.txt diff --git a/npc/CMakeLists.txt b/npc/CMakeLists.txt index 0bc466b..4874780 100644 --- a/npc/CMakeLists.txt +++ b/npc/CMakeLists.txt @@ -5,10 +5,12 @@ set (CMAKE_CXX_STANDARD 17) cmake_policy(SET CMP0144 NEW) include(CMakeDependentOption) +include(CTest) enable_testing() +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) # -- Build options -option(BUILD_USE_BLOOP "Whether to use bloop to spped up elaborate" ON) +option(BUILD_USE_BLOOP "Whether to use bloop to speed up elaborate" ON) option(BUILD_SIM_TARGET "Whether to build verilator simulation binary" ON) cmake_dependent_option(BUILD_SIM_NVBOARD_TARGET "Whether to build nvboard target" OFF "BUILD_SIM_TARGET" OFF) option(ENABLE_YSYX_GIT_TRACKER "Ysyx tracker support" OFF) @@ -42,115 +44,28 @@ set(CHISEL_MODULE_CLASS "${CMAKE_PROJECT_NAME}.${TOPMODULE}") # Verilog files are generted in CHISEL_OUTPUT_TMP_DIR and copy to # CHISEL_OUTPUT_DIR if content changes -set(CHISEL_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc/) -set(CHISEL_OUTPUT_TMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc_tmp/) +set(CHISEL_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc) +set(CHISEL_OUTPUT_TMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc_tmp) set(CHISEL_OUTPUT_VERILATOR_CONF ${CHISEL_OUTPUT_DIR}/conf.vlt) set(CHISEL_OUTPUT_TOPMODULE ${CHISEL_OUTPUT_DIR}/${TOPMODULE}.sv) set(CHISEL_EMIT_ARGS "--target-dir ${CHISEL_OUTPUT_TMP_DIR}") -# -- Add an always run target to generate verilog files with sbt/bloop, -# as we don't know if the result files will be different from cmake -# NOTE: Must reconfigure if we add new files in SCALA_CORE directory -file(GLOB_RECURSE SCALA_CORE_SOURCES "${SCALA_CORE}/src/main/scala/*.scala") -file(GLOB_RECURSE SCALA_CORE_RESOURCES "${SCALA_CORE}/src/main/resource/*") -set(CHISEL_DEPENDENCY ${SCALA_CORE_SOURCES} ${SCALA_CORE_RESOURCES} ${SCALA_CORE}/build.sbt) - -if(BUILD_USE_BLOOP) - set(CHISEL_TARGET bloop_${TOPMODULE}) - set(CHISEL_TEST_TARGET bloop_${TOPMODULE}_test) - # Export sbt build config to bloop - if(NOT EXISTS ${SCALA_CORE}/.bloop) - execute_process( - COMMAND sbt bloopInstall - WORKING_DIRECTORY ${SCALA_CORE} - ) - endif() - string(REPLACE " " ";" CHISEL_EMIT_ARGS_LIST ${CHISEL_EMIT_ARGS}) - list(TRANSFORM CHISEL_EMIT_ARGS_LIST PREPEND "--args;") - add_custom_command( - OUTPUT ${CHISEL_OUTPUT_TOPMODULE} - COMMAND bloop run root ${CHISEL_EMIT_ARGS_LIST} - COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} - WORKING_DIRECTORY ${SCALA_CORE} - DEPENDS ${CHISEL_DEPENDENCY} - COMMAND_EXPAND_LISTS - ) - add_test( - NAME bloop_${TOPMODULE}_test - COMMAND bloop test - WORKING_DIRECTORY ${SCALA_CORE} - ) -else() - set(CHISEL_TARGET sbt_${TOPMODULE}) - set(CHISEL_TEST_TARGET sbt_${TOPMODULE}_test) - add_custom_command( - OUTPUT ${CHISEL_OUTPUT_TOPMODULE} - COMMAND sbt "run ${CHISEL_EMIT_ARGS}" - COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} - WORKING_DIRECTORY ${SCALA_CORE} - DEPENDS ${CHISEL_DEPENDENCY} - VERBATIM - ) - add_test( - NAME sbt_${TOPMODULE}_test - COMMAND sbt test - WORKING_DIRECTORY ${SCALA_CORE} - ) -endif() - -if(NOT EXISTS ${CHISEL_OUTPUT_TOPMODULE}) - # Probably cold build, generate verilog at configure time to produce top module file - execute_process( - COMMAND sbt "run ${CHISEL_EMIT_ARGS}" - WORKING_DIRECTORY ${SCALA_CORE} - ) - execute_process( - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} - ) -endif() - # -- Build NVBoard executable if(BUILD_SIM_NVBOARD_TARGET) - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp - COMMAND auto_pin_bind ${CMAKE_SOURCE_DIR}/constr/${TOPMODULE}.nxdc ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp - DEPENDS ${CMAKE_SOURCE_DIR}/constr/${TOPMODULE}.nxdc - ) - - file(GLOB_RECURSE SOURCES csrc_nvboard/${TOPMODULE}/*.cpp) - add_executable(V${TOPMODULE}_nvboard ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp) - - verilate(V${TOPMODULE}_nvboard TRACE THREADS - TOP_MODULE ${TOPMODULE} - PREFIX V${TOPMODULE} - SOURCES ${CHISEL_OUTPUT_TOPMODULE} - INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc) - - add_dependencies(V${TOPMODULE}_nvboard ChiselBuild_${TOPMODULE}) - target_include_directories(V${TOPMODULE}_nvboard PRIVATE ${NVBOARD_INCLUDE_DIR} ${SDL2_INCLUDE_DIRS}) - target_link_libraries(V${TOPMODULE}_nvboard PRIVATE ${NVBOARD_LIBRARY} SDL2::SDL2 SDL2_image::SDL2_image) - - install(TARGETS V${TOPMODULE}_nvboard) + add_subdirectory(csrc_nvboard) endif() # -- Build Verilator executable and add to test include_directories(include) -file(GLOB_RECURSE SOURCES csrc/${TOPMODULE}/*.cpp) -add_executable(V${TOPMODULE} ${SOURCES}) +add_subdirectory(csrc) -verilate(V${TOPMODULE} TRACE COVERAGE THREADS - TOP_MODULE ${TOPMODULE} - PREFIX V${TOPMODULE} - SOURCES ${CHISEL_OUTPUT_TOPMODULE} ${CHISEL_OUTPUT_VERILATOR_CONF} - INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/vsrc - VERILATOR_ARGS - "--vpi" # Enable VPI -) - - -add_test(NAME V${TOPMODULE} COMMAND V${TOPMODULE}) +add_test( + NAME V${TOPMODULE} + COMMAND V${TOPMODULE} + --no-bin -m ${PROJECT_SOURCE_DIR}/resource/addi.txt + --diff-lib /home/xin/repo/ysyx-workbench/nemu/build/riscv32-nemu-interpreter-so) # -- Add build tracking if(ENABLE_YSYX_GIT_TRACKER) diff --git a/npc/cmake/ChiselBuild.cmake b/npc/cmake/ChiselBuild.cmake new file mode 100644 index 0000000..aa830f7 --- /dev/null +++ b/npc/cmake/ChiselBuild.cmake @@ -0,0 +1,62 @@ +# -- Add an always run target to generate verilog files with sbt/bloop, +# as we don't know if the result files will be different from cmake +# NOTE: Must reconfigure if we add new files in SCALA_CORE directory +file(GLOB_RECURSE SCALA_CORE_SOURCES "${SCALA_CORE}/src/main/scala/*.scala") +file(GLOB_RECURSE SCALA_CORE_RESOURCES "${SCALA_CORE}/src/main/resources/*") +set(CHISEL_DEPENDENCY ${SCALA_CORE_SOURCES} ${SCALA_CORE_RESOURCES} ${SCALA_CORE}/build.sbt) + +if(BUILD_USE_BLOOP) + set(CHISEL_TARGET bloop_${TOPMODULE}) + set(CHISEL_TEST_TARGET bloop_${TOPMODULE}_test) + # Export sbt build config to bloop + if(NOT EXISTS ${SCALA_CORE}/.bloop) + execute_process( + COMMAND sbt bloopInstall + WORKING_DIRECTORY ${SCALA_CORE} + ) + endif() + string(REPLACE " " ";" CHISEL_EMIT_ARGS_LIST ${CHISEL_EMIT_ARGS}) + list(TRANSFORM CHISEL_EMIT_ARGS_LIST PREPEND "--args;") + add_custom_command( + OUTPUT ${CHISEL_OUTPUT_TOPMODULE} + COMMAND bloop run root ${CHISEL_EMIT_ARGS_LIST} + COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} + WORKING_DIRECTORY ${SCALA_CORE} + DEPENDS ${CHISEL_DEPENDENCY} + COMMAND_EXPAND_LISTS + COMMENT "Run bloop from CMake" + ) +# add_test( +# NAME bloop_${TOPMODULE}_test +# COMMAND bloop test +# WORKING_DIRECTORY ${SCALA_CORE} +# ) +else() + set(CHISEL_TARGET sbt_${TOPMODULE}) + set(CHISEL_TEST_TARGET sbt_${TOPMODULE}_test) + add_custom_command( + OUTPUT ${CHISEL_OUTPUT_TOPMODULE} + COMMAND sbt "run ${CHISEL_EMIT_ARGS}" + COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} + WORKING_DIRECTORY ${SCALA_CORE} + # DEPENDS ${CHISEL_DEPENDENCY} test.scala + VERBATIM + COMMENT "Run sbt from CMake" + ) + add_test( + NAME sbt_${TOPMODULE}_test + COMMAND sbt test + WORKING_DIRECTORY ${SCALA_CORE} + ) +endif() + +if(NOT EXISTS ${CHISEL_OUTPUT_DIR}) + # Probably cold build, generate verilog at configure time to produce top module file + execute_process( + COMMAND sbt "run ${CHISEL_EMIT_ARGS}" + WORKING_DIRECTORY ${SCALA_CORE} + ) + execute_process( + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CHISEL_OUTPUT_TMP_DIR} ${CHISEL_OUTPUT_DIR} + ) +endif() diff --git a/npc/csrc/CMakeLists.txt b/npc/csrc/CMakeLists.txt new file mode 100644 index 0000000..3f645a7 --- /dev/null +++ b/npc/csrc/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(${TOPMODULE}) \ No newline at end of file diff --git a/npc/csrc/Flow/CMakeLists.txt b/npc/csrc/Flow/CMakeLists.txt new file mode 100644 index 0000000..0764ea6 --- /dev/null +++ b/npc/csrc/Flow/CMakeLists.txt @@ -0,0 +1,11 @@ +include(ChiselBuild) +add_executable(V${TOPMODULE} config.cpp main.cpp) + +verilate(V${TOPMODULE} TRACE COVERAGE THREADS + TOP_MODULE ${TOPMODULE} + PREFIX V${TOPMODULE} + SOURCES ${CHISEL_OUTPUT_TOPMODULE} ${CHISEL_OUTPUT_VERILATOR_CONF} + INCLUDE_DIRS ${CHISEL_OUTPUT_DIR} + VERILATOR_ARGS + "--vpi" # Enable VPI +) diff --git a/npc/csrc_nvboard/Flow/CMakeLists.txt b/npc/csrc_nvboard/Flow/CMakeLists.txt new file mode 100644 index 0000000..82143bc --- /dev/null +++ b/npc/csrc_nvboard/Flow/CMakeLists.txt @@ -0,0 +1,23 @@ +include(ChiselBuild) + +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp + COMMAND auto_pin_bind ${CMAKE_SOURCE_DIR}/constr/${TOPMODULE}.nxdc ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp + DEPENDS ${CMAKE_SOURCE_DIR}/constr/${TOPMODULE}.nxdc +) + +add_executable(V${TOPMODULE}_nvboard + ${CMAKE_CURRENT_BINARY_DIR}/${TOPMODULE}/auto_bind.cpp + main.cpp +) + +verilate(V${TOPMODULE}_nvboard TRACE THREADS + TOP_MODULE ${TOPMODULE} + PREFIX V${TOPMODULE} + SOURCES ${CHISEL_OUTPUT_TOPMODULE} + INCLUDE_DIRS ${CHISEL_OUTPUT_DIR}) + +target_include_directories(V${TOPMODULE}_nvboard PRIVATE ${NVBOARD_INCLUDE_DIR} ${SDL2_INCLUDE_DIRS}) +target_link_libraries(V${TOPMODULE}_nvboard PRIVATE ${NVBOARD_LIBRARY} SDL2::SDL2 SDL2_image::SDL2_image) + +install(TARGETS V${TOPMODULE}_nvboard) \ No newline at end of file diff --git a/npc/flake.nix b/npc/flake.nix index c9eb07d..a0bd61e 100644 --- a/npc/flake.nix +++ b/npc/flake.nix @@ -39,6 +39,7 @@ packages = [ clang-tools cmake + ninja coursier espresso bloop From c242e8c507db0817ba962b38b77fcd84f5832d14 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Fri, 5 Apr 2024 10:23:28 +0800 Subject: [PATCH 08/10] chore: move npc devshell to root directory --- flake.lock | 151 ++++++++++++++++++++++++++++++++++- flake.nix | 67 +++++++++++++++- npc/.envrc | 2 +- npc/flake.lock | 210 ------------------------------------------------- npc/flake.nix | 137 -------------------------------- 5 files changed, 215 insertions(+), 352 deletions(-) delete mode 100644 npc/flake.lock delete mode 100644 npc/flake.nix diff --git a/flake.lock b/flake.lock index 36b9a39..247c2b9 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,21 @@ { "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -18,6 +34,45 @@ "type": "github" } }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1709237383, @@ -34,10 +89,89 @@ "type": "github" } }, + "nixpkgs-circt162": { + "locked": { + "lastModified": 1705645507, + "narHash": "sha256-tX3vipIAmNDBA8WNWG4oY4KyTfnm2YieTHO2BhG8ISA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7995cae3ad60e3d6931283d650d7f43d31aaa5c7", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7995cae3ad60e3d6931283d650d7f43d31aaa5c7", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1710695816, + "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "614b4613980a522ba49f0d194531beddbb7220d3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nur-xin": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1710242657, + "narHash": "sha256-tgaILHKZH6vC8P8N8NBMNQ/XIpTY72zMojlGLPLPLZk=", + "ref": "refs/heads/master", + "rev": "6a499c8371c6a14d11a8c2fcc9f233e7b8e688a0", + "revCount": 148, + "type": "git", + "url": "https://git.xinyang.life/xin/nur.git" + }, + "original": { + "type": "git", + "url": "https://git.xinyang.life/xin/nur.git" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils_2", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1712055707, + "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "nixpkgs-circt162": "nixpkgs-circt162", + "nur-xin": "nur-xin", + "pre-commit-hooks": "pre-commit-hooks" } }, "systems": { @@ -54,6 +188,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 3a07d79..d3a27fc 100644 --- a/flake.nix +++ b/flake.nix @@ -1,13 +1,30 @@ { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs-circt162.url = "github:NixOS/nixpkgs/7995cae3ad60e3d6931283d650d7f43d31aaa5c7"; flake-utils.url = "github:numtide/flake-utils"; + pre-commit-hooks = { + url = "github:cachix/pre-commit-hooks.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nur-xin = { + url = "git+https://git.xinyang.life/xin/nur.git"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { self, ... }@inputs: with inputs; flake-utils.lib.eachDefaultSystem (system: let - pkgs = nixpkgs.legacyPackages.${system}; + pkgs = import nixpkgs { + inherit system; + config.allowUnfree = true; + overlays = [ + (self: super: { + nvboard = nur-xin.legacyPackages.${system}.nvboard; + }) + ]; + }; crossPkgs = import nixpkgs { localSystem = system; crossSystem = { @@ -20,6 +37,18 @@ }; in { + checks = { + pre-commit-check = pre-commit-hooks.lib.${system}.run { + src = ./.; + hooks = { + trim-trailing-whitespace.enable = true; + clang-format = { + enable = true; + types_or = pkgs.lib.mkForce [ "c" "c++" ]; + }; + }; + }; + }; packages.nemu = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; }; packages.abstract-machine = crossPkgs.callPackage ./abstract-machine { isa = "riscv"; platform = "nemu"; }; @@ -27,7 +56,7 @@ pname = "am-kernels-cmake"; version = "2024.02.18"; - src = ./am-kernels; + src = ./am-kernels; nativeBuildInputs = [ pkgs.cmake @@ -44,7 +73,7 @@ self.packages.${system}.abstract-machine ]; }; - + devShells.nemu = pkgs.mkShell { packages = with pkgs; [ clang-tools @@ -54,6 +83,38 @@ self.packages.${system}.nemu ]; }; + + devShells.npc = with pkgs; mkShell { + inherit (self.checks.${system}.pre-commit-check) shellHook; + CHISEL_FIRTOOL_PATH = "${nixpkgs-circt162.legacyPackages.${system}.circt}/bin"; + packages = [ + clang-tools + cmake + ninja + coursier + espresso + bloop + + gdb + jre + + gtkwave + ]; + + nativeBuildInputs = [ + cmake + sbt + nvboard + nixpkgs-circt162.legacyPackages.${system}.circt + yosys + cli11 + ]; + + buildInputs = [ + verilator + nvboard + ] ++ self.checks.${system}.pre-commit-check.enabledPackages; + }; } ); } diff --git a/npc/.envrc b/npc/.envrc index 3550a30..3f3af37 100644 --- a/npc/.envrc +++ b/npc/.envrc @@ -1 +1 @@ -use flake +use flake ..#npc diff --git a/npc/flake.lock b/npc/flake.lock deleted file mode 100644 index f37f6be..0000000 --- a/npc/flake.lock +++ /dev/null @@ -1,210 +0,0 @@ -{ - "nodes": { - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "pre-commit-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1709961763, - "narHash": "sha256-6H95HGJHhEZtyYA3rIQpvamMKAGoa8Yh2rFV29QnuGw=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "3030f185ba6a4bf4f18b87f345f104e6a6961f34", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-circt162": { - "locked": { - "lastModified": 1705645507, - "narHash": "sha256-tX3vipIAmNDBA8WNWG4oY4KyTfnm2YieTHO2BhG8ISA=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "7995cae3ad60e3d6931283d650d7f43d31aaa5c7", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "7995cae3ad60e3d6931283d650d7f43d31aaa5c7", - "type": "github" - } - }, - "nixpkgs-stable": { - "locked": { - "lastModified": 1710695816, - "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "614b4613980a522ba49f0d194531beddbb7220d3", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-23.11", - "repo": "nixpkgs", - "type": "github" - } - }, - "nur-xin": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1707020873, - "narHash": "sha256-+dNltc7tjgTIyle/I/5siQ5IvPwu+R5Uf6e24CmjLNk=", - "ref": "refs/heads/master", - "rev": "8142717e7154dbaadee0679f0224fe75cebb1735", - "revCount": 147, - "type": "git", - "url": "https://git.xinyang.life/xin/nur.git" - }, - "original": { - "type": "git", - "url": "https://git.xinyang.life/xin/nur.git" - } - }, - "pre-commit-hooks": { - "inputs": { - "flake-compat": "flake-compat", - "flake-utils": "flake-utils_2", - "gitignore": "gitignore", - "nixpkgs": [ - "nixpkgs" - ], - "nixpkgs-stable": "nixpkgs-stable" - }, - "locked": { - "lastModified": 1712055707, - "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs", - "nixpkgs-circt162": "nixpkgs-circt162", - "nur-xin": "nur-xin", - "pre-commit-hooks": "pre-commit-hooks" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/npc/flake.nix b/npc/flake.nix deleted file mode 100644 index a0bd61e..0000000 --- a/npc/flake.nix +++ /dev/null @@ -1,137 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - nixpkgs-circt162.url = "github:NixOS/nixpkgs/7995cae3ad60e3d6931283d650d7f43d31aaa5c7"; - flake-utils.url = "github:numtide/flake-utils"; - pre-commit-hooks = { - url = "github:cachix/pre-commit-hooks.nix"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - nur-xin = { - url = "git+https://git.xinyang.life/xin/nur.git"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = { self, ... }@inputs: with inputs; - flake-utils.lib.eachDefaultSystem (system: - let - pkgs = import nixpkgs { inherit system; config.allowUnfree = true; }// - { nur.xin = nur-xin.legacyPackages.${system}; }; - in - { - checks = { - pre-commit-check = pre-commit-hooks.lib.${system}.run { - src = ./.; - hooks = { - trim-trailing-whitespace.enable = true; - clang-format = { - enable = true; - types_or = pkgs.lib.mkForce [ "c" "c++" ]; - }; - }; - }; - }; - devShells.default = with pkgs; mkShell { - inherit (self.checks.${system}.pre-commit-check) shellHook; - buildInputs = self.checks.${system}.pre-commit-check.enabledPackages; - CHISEL_FIRTOOL_PATH = "${nixpkgs-circt162.legacyPackages.${system}.circt}/bin"; - packages = [ - clang-tools - cmake - ninja - coursier - espresso - bloop - - gdb - jre - - gtkwave - ]; - - inputsFrom = [ self.packages.${system}.default ]; - }; - packages.default = with pkgs; clangStdenv.mkDerivation { - name = "npc"; - version = "0.0.1"; - src = ./.; - nativeBuildInputs = [ - cmake - sbt - nur.xin.nvboard - nixpkgs-circt162.legacyPackages.${system}.circt - yosys - cli11 - ]; - buildInputs = [ - verilator - nur.xin.nvboard - ]; - - NEMU_HOME="/home/xin/repo/ysyx-workbench/nemu"; - }; - - # This version (1.43.0) of circt does not exist in nixpkgs - # and Chisel 5.1.0 specifically build against it, so here we are. - # Ref: https://github.com/NixOS/nixpkgs/blob/b6465c8/pkgs/development/compilers/circt/default.nix - packages.circt = - with pkgs; - let - pythonEnv = python3.withPackages (ps: [ ps.psutil ]); - in - stdenv.mkDerivation rec { - pname = "circt"; - version = "1.43.0"; - src = fetchFromGitHub { - owner = "llvm"; - repo = "circt"; - rev = "firtool-${version}"; - sha256 = "sha256-RkjigboswLkLgLkgOGahQLIygCkC3Q9rbVw3LqIzREY="; - fetchSubmodules = true; - }; - - requiredSystemFeatures = [ "big-parallel" ]; - - nativeBuildInputs = [ cmake ninja git pythonEnv ]; - - cmakeDir = "../llvm/llvm"; - cmakeFlags = [ - "-DLLVM_ENABLE_BINDINGS=OFF" - "-DLLVM_ENABLE_OCAMLDOC=OFF" - "-DLLVM_BUILD_EXAMPLES=OFF" - "-DLLVM_OPTIMIZED_TABLEGEN=ON" - "-DLLVM_ENABLE_PROJECTS=mlir" - "-DLLVM_EXTERNAL_PROJECTS=circt" - "-DLLVM_EXTERNAL_CIRCT_SOURCE_DIR=.." - "-DCIRCT_LLHD_SIM_ENABLED=OFF" - ]; - - LIT_FILTER_OUT = if stdenv.cc.isClang then "CIRCT :: Target/ExportSystemC/.*\.mlir" else null; - - preConfigure = '' - find ./test -name '*.mlir' -exec sed -i 's|/usr/bin/env|${coreutils}/bin/env|g' {} \; - ''; - - installPhase = '' - runHook preInstall - mkdir -p $out/bin - mv bin/{{fir,hls}tool,circt-{as,dis,lsp-server,opt,reduce,translate}} $out/bin - runHook postInstall - ''; - - doCheck = true; - checkTarget = "check-circt check-circt-integration"; - - meta = { - description = "Circuit IR compilers and tools"; - homepage = "https://circt.org/"; - license = lib.licenses.asl20; - maintainers = with lib.maintainers; [ sharzy ]; - platforms = lib.platforms.all; - }; - }; - } - ); -} - From 6f7d7ba383e6ab494a78456e1f562fed24bbcddb Mon Sep 17 00:00:00 2001 From: xinyangli Date: Fri, 5 Apr 2024 10:40:55 +0800 Subject: [PATCH 09/10] chore: move components to seperate folder --- npc/core/src/main/scala/{ => components}/ALU.scala | 0 npc/core/src/main/scala/{ => components}/Mem.scala | 0 npc/core/src/main/scala/{ => components}/ProgramCounter.scala | 0 npc/core/src/main/scala/{ => components}/RegisterFile.scala | 0 npc/core/src/main/scala/{ => top}/FlowMain.scala | 0 npc/core/src/main/scala/{ => utils}/Keyboard.scala | 0 npc/core/src/main/scala/{ => utils}/SegControllerGenerator.scala | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename npc/core/src/main/scala/{ => components}/ALU.scala (100%) rename npc/core/src/main/scala/{ => components}/Mem.scala (100%) rename npc/core/src/main/scala/{ => components}/ProgramCounter.scala (100%) rename npc/core/src/main/scala/{ => components}/RegisterFile.scala (100%) rename npc/core/src/main/scala/{ => top}/FlowMain.scala (100%) rename npc/core/src/main/scala/{ => utils}/Keyboard.scala (100%) rename npc/core/src/main/scala/{ => utils}/SegControllerGenerator.scala (100%) diff --git a/npc/core/src/main/scala/ALU.scala b/npc/core/src/main/scala/components/ALU.scala similarity index 100% rename from npc/core/src/main/scala/ALU.scala rename to npc/core/src/main/scala/components/ALU.scala diff --git a/npc/core/src/main/scala/Mem.scala b/npc/core/src/main/scala/components/Mem.scala similarity index 100% rename from npc/core/src/main/scala/Mem.scala rename to npc/core/src/main/scala/components/Mem.scala diff --git a/npc/core/src/main/scala/ProgramCounter.scala b/npc/core/src/main/scala/components/ProgramCounter.scala similarity index 100% rename from npc/core/src/main/scala/ProgramCounter.scala rename to npc/core/src/main/scala/components/ProgramCounter.scala diff --git a/npc/core/src/main/scala/RegisterFile.scala b/npc/core/src/main/scala/components/RegisterFile.scala similarity index 100% rename from npc/core/src/main/scala/RegisterFile.scala rename to npc/core/src/main/scala/components/RegisterFile.scala diff --git a/npc/core/src/main/scala/FlowMain.scala b/npc/core/src/main/scala/top/FlowMain.scala similarity index 100% rename from npc/core/src/main/scala/FlowMain.scala rename to npc/core/src/main/scala/top/FlowMain.scala diff --git a/npc/core/src/main/scala/Keyboard.scala b/npc/core/src/main/scala/utils/Keyboard.scala similarity index 100% rename from npc/core/src/main/scala/Keyboard.scala rename to npc/core/src/main/scala/utils/Keyboard.scala diff --git a/npc/core/src/main/scala/SegControllerGenerator.scala b/npc/core/src/main/scala/utils/SegControllerGenerator.scala similarity index 100% rename from npc/core/src/main/scala/SegControllerGenerator.scala rename to npc/core/src/main/scala/utils/SegControllerGenerator.scala From e1438e25ed420fc9a5f44bd6b0bc07d83c40dcf9 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Fri, 5 Apr 2024 11:30:52 +0800 Subject: [PATCH 10/10] ci: add npc difftest --- .gitea/workflows/npc-test.yml | 45 +++++++++++++++++++ flake.nix | 7 +++ nemu/configs/riscv32-lib_defconfig | 72 ++++++++++++++++++++++++++++++ nemu/default.nix | 13 +++--- nemu/scripts/build.mk | 5 +++ npc/CMakeLists.txt | 7 +-- npc/csrc/Flow/CMakeLists.txt | 6 +++ 7 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 .gitea/workflows/npc-test.yml create mode 100644 nemu/configs/riscv32-lib_defconfig diff --git a/.gitea/workflows/npc-test.yml b/.gitea/workflows/npc-test.yml new file mode 100644 index 0000000..a5652e4 --- /dev/null +++ b/.gitea/workflows/npc-test.yml @@ -0,0 +1,45 @@ +name: Run CTests within npc +on: [push] + +jobs: + npc-test: + runs-on: nix + steps: + - uses: https://github.com/cachix/cachix-action@v14 + with: + name: ysyx + authToken: '${{ secrets.CACHIX_SIGNING_KEY }}' + - uses: actions/checkout@v4 + with: + submodules: true + - name: Cache develop environment + id: cache-nix-develop + uses: actions/cache@v4 + with: + path: /nix/store + key: nix-develop-${{ hashFiles('flake.*') }} + - name: Fetch nix store + if: steps.cache-nix-develop.outputs.cache-hit != 'true' + run: nix develop .#npc + - name: Use develop environment + uses: https://git.xinyang.life/xin/nix-develop@main + with: + arguments: .#npc + - name: Cache sbt dependencies + id: cache-sbt-dependency + uses: actions/cache@v4 + with: + path: npc/core + key: core-${{ hashFiles('npc/core/build.sbt') }} + - name: Fetch sbt dependencies + if: steps.cache-sbt-dependency.outputs.cache-hit != 'true' + run: | + cd npc/core + sbt update + - name: Run difftests + run: | + mkdir -p npc/build + cd npc/build + cmake $cmakeFlags ../ + make -j8 + ctest -V diff --git a/flake.nix b/flake.nix index d3a27fc..c5db1e6 100644 --- a/flake.nix +++ b/flake.nix @@ -50,6 +50,7 @@ }; }; packages.nemu = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; }; + packages.nemu-lib = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; defconfig = "riscv32-lib_defconfig"; }; packages.abstract-machine = crossPkgs.callPackage ./abstract-machine { isa = "riscv"; platform = "nemu"; }; packages.am-kernels = crossPkgs.stdenv.mkDerivation rec { @@ -82,6 +83,7 @@ inputsFrom = [ self.packages.${system}.nemu ]; + IMAGES_PATH = "${self.packages.${system}.am-kernels}/share/binary"; }; devShells.npc = with pkgs; mkShell { @@ -113,7 +115,12 @@ buildInputs = [ verilator nvboard + openssl ] ++ self.checks.${system}.pre-commit-check.enabledPackages; + + cmakeFlags = [ + "-DDIFFTEST_LIB:string=${self.packages.${system}.nemu-lib}/lib/riscv32-nemu-interpreter-so" + ]; }; } ); diff --git a/nemu/configs/riscv32-lib_defconfig b/nemu/configs/riscv32-lib_defconfig new file mode 100644 index 0000000..15d8758 --- /dev/null +++ b/nemu/configs/riscv32-lib_defconfig @@ -0,0 +1,72 @@ +# +# Automatically generated file; DO NOT EDIT. +# NEMU Configuration Menu +# +# CONFIG_ISA_x86 is not set +# CONFIG_ISA_mips32 is not set +CONFIG_ISA_riscv=y +# CONFIG_ISA_loongarch32r is not set +CONFIG_ISA="riscv32" + +# +# ISA-dependent Options for riscv +# +# CONFIG_RV64 is not set +# CONFIG_RVE is not set +# end of ISA-dependent Options for riscv + +CONFIG_ENGINE_INTERPRETER=y +CONFIG_ENGINE="interpreter" +CONFIG_MODE_SYSTEM=y +# CONFIG_TARGET_NATIVE_ELF is not set +CONFIG_TARGET_SHARE=y +# CONFIG_TARGET_AM is not set + +# +# Build Options +# +CONFIG_CC_GCC=y +# CONFIG_CC_GPP is not set +# CONFIG_CC_CLANG is not set +CONFIG_CC="gcc" +# CONFIG_CC_O0 is not set +# CONFIG_CC_O1 is not set +CONFIG_CC_O2=y +# CONFIG_CC_O3 is not set +CONFIG_CC_OPT="-O2" +CONFIG_CC_LTO=y +CONFIG_CC_DEBUG=y +# CONFIG_CC_ASAN is not set +# end of Build Options + +# +# Testing and Debugging +# +CONFIG_LOG_TRACE=y +# CONFIG_LOG_INFO is not set +# CONFIG_LOG_WARNING is not set +# CONFIG_LOG_ERROR is not set +CONFIG_LOG_LEVEL=4 +# CONFIG_TRACE is not set +CONFIG_DIFFTEST_REF_PATH="none" +CONFIG_DIFFTEST_REF_NAME="none" +# end of Testing and Debugging + +# +# Memory Configuration +# +CONFIG_MBASE=0x80000000 +CONFIG_MSIZE=0x8000000 +CONFIG_PC_RESET_OFFSET=0 +# CONFIG_PMEM_MALLOC is not set +CONFIG_PMEM_GARRAY=y +CONFIG_MEM_RANDOM=y +# end of Memory Configuration + +# +# Miscellaneous +# +CONFIG_TIMER_GETTIMEOFDAY=y +# CONFIG_TIMER_CLOCK_GETTIME is not set +CONFIG_RT_CHECK=y +# end of Miscellaneous diff --git a/nemu/default.nix b/nemu/default.nix index d3d5a70..052b4a7 100644 --- a/nemu/default.nix +++ b/nemu/default.nix @@ -2,7 +2,8 @@ lib, stdenv, am-kernels, - dtc + dtc, + defconfig ? "alldefconfig", }: stdenv.mkDerivation rec { @@ -31,27 +32,27 @@ stdenv.mkDerivation rec { configurePhase = '' export NEMU_HOME=$(pwd) - make alldefconfig + make ${defconfig} ''; buildPhase = '' make ''; - doCheck = true; - checkPhase = '' + doCheck = (defconfig == "alldefconfig"); + checkPhase = if doCheck then '' export IMAGES_PATH=${am-kernels}/share/binary make test - ''; + '' else ""; installPhase = '' mkdir -p $out/bin + mkdir -p $out/lib make PREFIX=$out install ''; shellHook = '' export NEMU_HOME=$(pwd) - export IMAGES_PATH=${am-kernels}/share/binary ''; meta = with lib; { diff --git a/nemu/scripts/build.mk b/nemu/scripts/build.mk index f050197..a3b71d2 100644 --- a/nemu/scripts/build.mk +++ b/nemu/scripts/build.mk @@ -76,8 +76,13 @@ $(BINARY):: $(OBJS) $(ARCHIVES) @$(LD) -o $@ $(OBJS) $(LDFLAGS) $(ARCHIVES) $(LIBS) install: $(BINARY) +ifeq ($(SHARE),1) + @mkdir -p $(PREFIX)/lib + @cp $(BINARY) $(PREFIX)/lib/ +else @mkdir -p $(PREFIX)/bin @cp $(BINARY) $(PREFIX)/bin/ +endif clean: -rm -rf $(BUILD_DIR) diff --git a/npc/CMakeLists.txt b/npc/CMakeLists.txt index 4874780..78dbfe5 100644 --- a/npc/CMakeLists.txt +++ b/npc/CMakeLists.txt @@ -15,6 +15,7 @@ option(BUILD_SIM_TARGET "Whether to build verilator simulation binary" ON) cmake_dependent_option(BUILD_SIM_NVBOARD_TARGET "Whether to build nvboard target" OFF "BUILD_SIM_TARGET" OFF) option(ENABLE_YSYX_GIT_TRACKER "Ysyx tracker support" OFF) set(TOPMODULE "Flow" CACHE STRING "Topmodule name in chisel") +set(DIFFTEST_LIB "" CACHE STRING "Dynamic library file used as difftest reference") # -- Ysyx tracker, configure if(ENABLE_YSYX_GIT_TRACKER) @@ -61,12 +62,6 @@ include_directories(include) add_subdirectory(csrc) -add_test( - NAME V${TOPMODULE} - COMMAND V${TOPMODULE} - --no-bin -m ${PROJECT_SOURCE_DIR}/resource/addi.txt - --diff-lib /home/xin/repo/ysyx-workbench/nemu/build/riscv32-nemu-interpreter-so) - # -- Add build tracking if(ENABLE_YSYX_GIT_TRACKER) add_custom_command( diff --git a/npc/csrc/Flow/CMakeLists.txt b/npc/csrc/Flow/CMakeLists.txt index 0764ea6..606fd73 100644 --- a/npc/csrc/Flow/CMakeLists.txt +++ b/npc/csrc/Flow/CMakeLists.txt @@ -9,3 +9,9 @@ verilate(V${TOPMODULE} TRACE COVERAGE THREADS VERILATOR_ARGS "--vpi" # Enable VPI ) + +add_test( + NAME V${TOPMODULE} + COMMAND V${TOPMODULE} + --no-bin -m ${PROJECT_SOURCE_DIR}/resource/addi.txt + --diff-lib ${DIFFTEST_LIB})