From 0f2de5b55489602a57d29181f719ed1bb95f5ba7 Mon Sep 17 00:00:00 2001 From: Yanqi Yang Date: Fri, 24 Feb 2023 16:10:44 +0800 Subject: [PATCH 1/5] add draft --- build.sc | 514 ++++++++++++++++++ flake.nix | 5 + overlay.nix | 49 +- tests/cases/entrance/entrance.S | 9 + tests/cases/smoketest/smoke.S | 19 + tests/cosim/elaborate/src/DUT.scala | 95 ++++ tests/cosim/elaborate/src/Main.scala | 72 +++ tests/cosim/emulator/src/exceptions.h | 17 + .../cosim/emulator/src/glog_exception_safe.h | 35 ++ tests/cosim/emulator/src/main.cc | 32 ++ tests/cosim/emulator/src/simple_sim.h | 79 +++ tests/cosim/emulator/src/spike_event.cc | 241 ++++++++ tests/cosim/emulator/src/spike_event.h | 96 ++++ tests/cosim/emulator/src/tl_interface.h | 42 ++ tests/cosim/emulator/src/util.h | 22 + tests/cosim/emulator/src/vbridge.cc | 19 + tests/cosim/emulator/src/vbridge.h | 23 + tests/cosim/emulator/src/vbridge_config.h | 19 + tests/cosim/emulator/src/vbridge_impl.cc | 474 ++++++++++++++++ tests/cosim/emulator/src/vbridge_impl.h | 116 ++++ tests/cosim/emulator/src/vpi.h | 11 + 21 files changed, 1988 insertions(+), 1 deletion(-) create mode 100644 tests/cases/entrance/entrance.S create mode 100644 tests/cases/smoketest/smoke.S create mode 100644 tests/cosim/elaborate/src/DUT.scala create mode 100644 tests/cosim/elaborate/src/Main.scala create mode 100644 tests/cosim/emulator/src/exceptions.h create mode 100644 tests/cosim/emulator/src/glog_exception_safe.h create mode 100644 tests/cosim/emulator/src/main.cc create mode 100644 tests/cosim/emulator/src/simple_sim.h create mode 100644 tests/cosim/emulator/src/spike_event.cc create mode 100644 tests/cosim/emulator/src/spike_event.h create mode 100644 tests/cosim/emulator/src/tl_interface.h create mode 100644 tests/cosim/emulator/src/util.h create mode 100644 tests/cosim/emulator/src/vbridge.cc create mode 100644 tests/cosim/emulator/src/vbridge.h create mode 100644 tests/cosim/emulator/src/vbridge_config.h create mode 100644 tests/cosim/emulator/src/vbridge_impl.cc create mode 100644 tests/cosim/emulator/src/vbridge_impl.h create mode 100644 tests/cosim/emulator/src/vpi.h diff --git a/build.sc b/build.sc index 44237adcd56..94a3a7bcf3a 100644 --- a/build.sc +++ b/build.sc @@ -1,6 +1,9 @@ import mill._ import mill.scalalib._ import mill.scalalib.publish._ +import mill.modules.Util +import mill.define.{Sources, TaskModule} +import mill.scalalib.scalafmt._ import coursier.maven.MavenRepository import $file.hardfloat.build import $file.cde.common @@ -486,3 +489,514 @@ class ArchTest(top: String, config: String, xlen: String, isa: String) extends M } } } + +object v { + val scala = "2.12.16" + val chisel3 = ivy"edu.berkeley.cs::chisel3:3.6-SNAPSHOT" + val chisel3Plugin = ivy"edu.berkeley.cs::chisel3-plugin:3.6-SNAPSHOT" + val chiseltest = ivy"edu.berkeley.cs::chiseltest:3.6-SNAPSHOT" + val utest = ivy"com.lihaoyi::utest:latest.integration" + val macroParadise = ivy"org.scalamacros:::paradise:2.1.1" + val mainargs = ivy"com.lihaoyi::mainargs:0.3.0" +} +object tests extends Module{ + object cosim extends Module { + object elaborate extends ScalaModule with ScalafmtModule { + def scalaVersion = T { + v.scala + } + + //override def moduleDeps = Seq(diplomatic) + +// override def scalacOptions = T { +// Seq("-Xsource:2.11", s"-Xplugin:${mychisel3.plugin.jar().path}") +// } + + override def ivyDeps = Agg( + v.mainargs + ) + + def elaborate = T.persistent { + mill.modules.Jvm.runSubprocess( + finalMainClass(), + runClasspath().map(_.path), + forkArgs(), + forkEnv(), + Seq( + "--dir", T.dest.toString, + ), + workingDir = forkWorkingDir() + ) + PathRef(T.dest) + } + + def rtls = T.persistent { + os.read(elaborate().path / "filelist.f").split("\n").map(str => + try { + os.Path(str) + } catch { + case e: IllegalArgumentException if e.getMessage.contains("is not an absolute path") => + elaborate().path / str + } + ).filter(p => p.ext == "v" || p.ext == "sv").map(PathRef(_)).toSeq + } + + def annos = T.persistent { + os.walk(elaborate().path).filter(p => p.last.endsWith("anno.json")).map(PathRef(_)) + } + } + + /** build emulator */ + object emulator extends Module { + + def csources = T.source { + millSourcePath / "src" + } + + def csrcDir = T { + PathRef(millSourcePath / "src") + } + + def vsrcs = T.persistent { + elaborate.rtls().filter(p => p.path.ext == "v" || p.path.ext == "sv") + } + + def allCSourceFiles = T { + Lib.findSourceFiles(Seq(csrcDir()), Seq("S", "s", "c", "cpp", "cc")).map(PathRef(_)) + } + + val topName = "V" + + def verilatorConfig = T { + val traceConfigPath = T.dest / "verilator.vlt" + os.write( + traceConfigPath, + "`verilator_config\n" + + ujson.read(cosim.elaborate.annos().collectFirst(f => os.read(f.path)).get).arr.flatMap { + case anno if anno("class").str == "chisel3.experimental.Trace$TraceAnnotation" => + Some(anno("target").str) + case _ => None + }.toSet.map { t: String => + val s = t.split('|').last.split("/").last + val M = s.split(">").head.split(":").last + val S = s.split(">").last + s"""//$t\npublic_flat_rd -module "$M" -var "$S"""" + }.mkString("\n") + ) + PathRef(traceConfigPath) + } + + def CMakeListsString = T { + // format: off + s"""cmake_minimum_required(VERSION 3.20) + |set(CMAKE_CXX_STANDARD 17) + |set(CMAKE_CXX_COMPILER_ID "clang") + |set(CMAKE_C_COMPILER "clang") + |set(CMAKE_CXX_COMPILER "clang++") + | + |project(emulator) + | + |find_package(args REQUIRED) + |find_package(glog REQUIRED) + |find_package(fmt REQUIRED) + |find_package(libspike REQUIRED) + |find_package(verilator REQUIRED) + |find_package(Threads REQUIRED) + |set(THREADS_PREFER_PTHREAD_FLAG ON) + | + |set(CMAKE_CXX_FLAGS "$${CMAKE_CXX_FLAGS} -DVERILATOR") + | + |add_executable(${topName} + |${allCSourceFiles().map(_.path).mkString("\n")} + |) + | + |target_include_directories(${topName} PUBLIC ${csources().path.toString}) + | + |target_link_libraries(${topName} PUBLIC $${CMAKE_THREAD_LIBS_INIT}) + |target_link_libraries(${topName} PUBLIC libspike fmt glog) # note that libargs is header only, nothing to link + | + |verilate(${topName} + | SOURCES + |${vsrcs().map(_.path).mkString("\n")} + |${verilatorConfig().path.toString} + | TRACE_FST + | TOP_MODULE DUT + | PREFIX V${topName} + | OPT_FAST + | THREADS 8 + | VERILATOR_ARGS ${verilatorArgs().mkString(" ")} + |) + |""".stripMargin + // format: on + } + + def verilatorArgs = T.input { + Seq( + // format: off + "-Wno-UNOPTTHREADS", "-Wno-STMTDLY", "-Wno-LATCH", "-Wno-WIDTH", + "--x-assign unique", + "+define+RANDOMIZE_GARBAGE_ASSIGN", + "--output-split 20000", + "--output-split-cfuncs 20000", + "--max-num-width 1048576", + "--vpi" + // format: on + ) + } + + def elf = T.persistent { + val path = T.dest / "CMakeLists.txt" + os.write.over(path, CMakeListsString()) + T.log.info(s"CMake project generated in $path,\nverilating...") + os.proc( + // format: off + "cmake", + "-G", "Ninja", + T.dest.toString + // format: on + ).call(T.dest) + T.log.info("compile rtl to emulator...") + os.proc( + // format: off + "ninja" + // format: on + ).call(T.dest) + val elf = T.dest / topName + T.log.info(s"verilated exe generated: ${elf.toString}") + PathRef(elf) + } + } + } + + /** all the cases: entrance, smoketest and riscv-tests */ + object cases extends Module { + trait Case extends Module { + def name: T[String] = millSourcePath.last + + def sources = T.sources { + millSourcePath + } + + def allSourceFiles = T { + Lib.findSourceFiles(sources(), Seq("S", "s", "c", "cpp")).map(PathRef(_)) + } + + def linkScript: T[PathRef] = T { + os.write(T.ctx.dest / "linker.ld", + s""" + |SECTIONS + |{ + | . = 0x1000; + | .text.start : { *(.text.start) } + |} + |""".stripMargin) + PathRef(T.ctx.dest / "linker.ld") + } + + def compile: T[PathRef] = T { + os.proc(Seq("clang-rv64", "-o", name() + ".elf", "--target=riscv64", "-march=rv64gc", "-mno-relax", s"-T${linkScript().path}") ++ allSourceFiles().map(_.path.toString)).call(T.ctx.dest) + os.proc(Seq("llvm-objcopy", "-O", "binary", "--only-section=.text", name() + ".elf", name())).call(T.ctx.dest) + T.log.info(s"${name()} is generated in ${T.dest},\n") + PathRef(T.ctx.dest / name()) + } + } + + object smoketest extends Case { + override def linkScript: T[PathRef] = T { + os.write(T.ctx.dest / "linker.ld", + s""" + |SECTIONS + |{ + | . = 0x80000000; + | .text.start : { *(.text.start) } + |} + |""".stripMargin) + PathRef(T.ctx.dest / "linker.ld") + } + } + + object entrance extends Case + + object riscvtests extends Module { + + c => + trait Suite extends Module { + def name: T[String] + + def description: T[String] + + def binaries: T[Seq[PathRef]] + } + + object test extends Module { + trait Suite extends c.Suite { + def name = T { + millSourcePath.last + } + + def description = T { + s"test suite ${name} from riscv-tests" + } + + def target = T.persistent { + os.walk(untar().path).filter(p => p.last.startsWith(name())).filterNot(p => p.last.endsWith("dump")).map(PathRef(_)) + } + + def init = T.persistent { + target().map(bin => { + os.proc("cp", bin.path, "./" + bin.path.last + ".elf").call(T.dest) + os.proc("llvm-objcopy", "-O", "binary", bin.path.last + ".elf", bin.path.last).call(T.dest) + }) + PathRef(T.dest) + } + + def test = T { + println("why") + + target.map(a => + println("hello")) + PathRef(T.dest) + } + + def binaries = T { + os.walk(init().path).filter(p => p.last.startsWith(name())).filterNot(p => p.last.endsWith("elf")).map(PathRef(_)) + } + } + + def commit = T.input { + "047314c5b0525b86f7d5bb6ffe608f7a8b33ffdb" + } + + def tgz = T.persistent { + Util.download(s"https://github.com/ZenithalHourlyRate/riscv-tests-release/releases/download/tag-${commit()}/riscv-tests.tgz") + } + + def untar = T.persistent { + mill.modules.Jvm.runSubprocess(Seq("tar", "xzf", tgz().path).map(_.toString), Map[String, String](), T.dest) + PathRef(T.dest) + } + + object `rv32mi-p` extends Suite + + object `rv32mi-p-lh` extends Suite + + object `rv32mi-p-lw` extends Suite + + object `rv32mi-p-sh` extends Suite + + object `rv32mi-p-sw` extends Suite + + object `rv32si-p` extends Suite + + object `rv32ua-p` extends Suite + + object `rv32ua-v` extends Suite + + object `rv32uc-p` extends Suite + + object `rv32uc-v` extends Suite + + object `rv32ud-p` extends Suite + + object `rv32ud-v` extends Suite + + object `rv32uf-p` extends Suite + + object `rv32uf-v` extends Suite + + object `rv32ui-p` extends Suite + + object `rv32ui-v` extends Suite + + object `rv32um-p` extends Suite + + object `rv32um-v` extends Suite + + object `rv32uzfh-p` extends Suite + + object `rv32uzfh-v` extends Suite + + object `rv64mi-p` extends Suite + + object `rv64mi-p-ld` extends Suite + + object `rv64mi-p-lh` extends Suite + + object `rv64mi-p-lw` extends Suite + + object `rv64mi-p-sd` extends Suite + + object `rv64mi-p-sh` extends Suite + + object `rv64mi-p-sw` extends Suite + + object `rv64mzicbo-p` extends Suite + + object `rv64si-p` extends Suite + + object `rv64si-p-icache` extends Suite + + object `rv64ssvnapot-p` extends Suite + + object `rv64ua-p` extends Suite + + object `rv64ua-v` extends Suite + + object `rv64uc-p` extends Suite + + object `rv64uc-v` extends Suite + + object `rv64ud-p` extends Suite + + object `rv64ud-v` extends Suite + + object `rv64uf-p` extends Suite + + object `rv64uf-v` extends Suite + + object `rv64ui-p` extends Suite + + object `rv64ui-v` extends Suite + + object `rv64um-p` extends Suite + + object `rv64um-v` extends Suite + + object `rv64uzfh-p` extends Suite + + object `rv64uzfh-v` extends Suite + + object `rv64` extends Suite { + override def binaries = T { + os.walk(init().path).filter(p => p.last.startsWith(name())).filterNot(p => p.last.endsWith("elf")).filterNot(p => p.last.endsWith("rv64mi-p-csr")).filterNot(p => p.last.endsWith("rv64mi-p-breakpoint")).filterNot(p => p.last.startsWith("rv64um")).map(PathRef(_)) + } + } + } + } + } + + object tests extends Module() { + object smoketest extends Module { + trait Test extends TaskModule { + override def defaultCommandName() = "run" + + def bin: cases.Case + + def run(args: String*) = T.command { + val proc = os.proc(Seq(cosim.emulator.elf().path.toString(), "--entrance", cases.entrance.compile().path.toString(), "--bin", bin.compile().path.toString, "--wave", (T.dest / "wave").toString) ++ args) + T.log.info(s"run test: ${bin.name} with:\n ${proc.command.map(_.value.mkString(" ")).mkString(" ")}") + proc.call() + PathRef(T.dest) + } + } + + object smoketest extends Test { + def bin = cases.smoketest + } + + } + + object riscvtests extends Module { + + trait Test extends TaskModule { + override def defaultCommandName() = "run" + + def bin: T[Seq[PathRef]] + + def run(args: String*) = T.command { + bin().map { c => + val name = c.path.last + val proc = os.proc(Seq(cosim.emulator.elf().path.toString(), "--entrance", cases.entrance.compile().path.toString(), "--bin", c.path.toString, "--wave", (T.dest / "wave").toString) ++ args) + T.log.info(s"run test: ${c.path.last} with:\n ${proc.command.map(_.value.mkString(" ")).mkString(" ")}") + val p = proc.call(stdout = T.dest / s"$name.running.log", mergeErrIntoOut = true) + + PathRef(if (p.exitCode != 0) { + os.move(T.dest / s"$name.running.log", T.dest / s"$name.failed.log") + System.err.println(s"Test $name failed with exit code ${p.exitCode}") + T.dest / s"$name.failed.log" + } else { + os.move(T.dest / s"$name.running.log", T.dest / s"$name.passed.log") + T.dest / s"$name.passed.log" + }) + } + } + + } + + object smoketest extends Test { + def bin = Seq(cases.smoketest.compile()) + } + + object `rv64` extends Test { + def bin = cases.riscvtests.test.`rv64`.binaries + } + + object `rv64si-p` extends Test { + def bin = cases.riscvtests.test.`rv64si-p`.binaries + } + + + object `rv64mi-p` extends Test { + def bin = cases.riscvtests.test.`rv64mi-p`.binaries + } + + object `rv64ua-p` extends Test { + def bin = cases.riscvtests.test.`rv64ua-p`.binaries + } + + object `rv64ua-v` extends Test { + def bin = cases.riscvtests.test.`rv64ua-v`.binaries + } + + object `rv64uc-p` extends Test { + def bin = cases.riscvtests.test.`rv64uc-p`.binaries + } + + object `rv64uc-v` extends Test { + def bin = cases.riscvtests.test.`rv64uc-v`.binaries + } + + object `rv64ud-p` extends Test { + def bin = cases.riscvtests.test.`rv64ud-p`.binaries + } + + object `rv64ud-v` extends Test { + def bin = cases.riscvtests.test.`rv64ud-v`.binaries + } + + object `rv64uf-p` extends Test { + def bin = cases.riscvtests.test.`rv64uf-p`.binaries + } + + object `rv64uf-v` extends Test { + def bin = cases.riscvtests.test.`rv64uf-v`.binaries + } + + object `rv64ui-p` extends Test { + def bin = cases.riscvtests.test.`rv64ui-p`.binaries + } + + object `rv64ui-v` extends Test { + def bin = cases.riscvtests.test.`rv64ui-v`.binaries + } + + object `rv64uzfh-p` extends Test { + def bin = cases.riscvtests.test.`rv64uzfh-p`.binaries + } + + object `rv64uzfh-v` extends Test { + def bin = cases.riscvtests.test.`rv64uzfh-p`.binaries + } + + object `rv64um-p` extends Test { + def bin = cases.riscvtests.test.`rv64um-p`.binaries + } + + object `rv64um-v` extends Test { + def bin = cases.riscvtests.test.`rv64um-v`.binaries + } + + } + + } +} diff --git a/flake.nix b/flake.nix index 4d1bd74f526..1823b749f71 100644 --- a/flake.nix +++ b/flake.nix @@ -22,6 +22,11 @@ verilator cmake ninja python3 python3Packages.bootstrapped-pip pkgsCross.riscv64-embedded.buildPackages.gcc + + libargs glog fmt zlib + gnused coreutils gnugrep which + parallel protobuf antlr4 numactl + circt spike riscvTests diff --git a/overlay.nix b/overlay.nix index 289f851f0cf..5db83ff07f0 100644 --- a/overlay.nix +++ b/overlay.nix @@ -1,4 +1,5 @@ -final: prev: { +final: prev: +let riscvTests = final.pkgsCross.riscv64-embedded.stdenv.mkDerivation rec { pname = "riscv-tests"; version = "55bbcc8c06637a31cc01970881ba8072838a9121"; @@ -17,4 +18,50 @@ final: prev: { ]; buildPhase = "make RISCV_PREFIX=riscv64-none-elf-"; }; + + libspike = let + version = "1.1.0"; + pname = "libspike"; + cmakeConfig = '' + add_library(libspike STATIC IMPORTED GLOBAL) + set_target_properties(libspike PROPERTIES + IMPORTED_LOCATION "${placeholder "out"}/lib/libriscv.so") + target_include_directories(libspike INTERFACE + "${placeholder "out"}/include" + "${placeholder "out"}/include/riscv" + "${placeholder "out"}/include/fesvr" + "${placeholder "out"}/include/softfloat" + ) + ''; + in + final.stdenv.mkDerivation { + inherit version pname cmakeConfig; + enableParallelBuilding = true; + nativeBuildInputs = [ final.dtc ]; + src = final.fetchFromGitHub { + owner = "riscv"; + repo = "riscv-isa-sim"; + rev = "ab3225a3ff687fda8b4180f9e4e0949a400d1247"; + sha256 = "sha256-2cC2goTmxWnkTm3Tq08R8YkkuI2Fj8fRvpEPVZ5JvUI="; + }; + configureFlags = [ + "--enable-commitlog" + ]; + installPhase = '' + runHook preInstall + mkdir -p $out/include/{riscv,fesvr,softfloat} $out/lib $out/lib/cmake/libspike + cp riscv/*.h $out/include/riscv + cp fesvr/*.h $out/include/fesvr + cp softfloat/*.h $out/include/softfloat + cp config.h $out/include + cp *.so $out/lib + echo "$cmakeConfig" > $out/lib/cmake/libspike/libspike-config.cmake + runHook postInstall + ''; + }; +in +{ + inherit libspike riscvTests; + + } diff --git a/tests/cases/entrance/entrance.S b/tests/cases/entrance/entrance.S new file mode 100644 index 00000000000..9ddd961a08d --- /dev/null +++ b/tests/cases/entrance/entrance.S @@ -0,0 +1,9 @@ +.global _start +_start: + auipc t1, 0x0 + addi a1, t1, 32 + csrr a0, mhartid + lui t0, 0x80000 + slli t0,t0,32 + srli t0,t0,32 + jr t0 \ No newline at end of file diff --git a/tests/cases/smoketest/smoke.S b/tests/cases/smoketest/smoke.S new file mode 100644 index 00000000000..bcba4ee74e0 --- /dev/null +++ b/tests/cases/smoketest/smoke.S @@ -0,0 +1,19 @@ +.global _start +_start: + add t0,t0,x0 + addi t0, x0, 0x1 + addi t1, x0, 0x0 + addi t3, x0, 0xa +loop: + add t1, t1, t0 + add t0, t0, 0x1 + add t3, t3, -1 + bne t3, x0, loop + sd t1, 0x10(x0) + ld t2, 0x10(x0) + addi x20, x0, 0x1 + addi x21, x0, 0x1 + addi x22, x0, 0x1 + addi x23, x0, 0x1 + addi x24, x0, 0x1 + addi x25, x0, 0x1 \ No newline at end of file diff --git a/tests/cosim/elaborate/src/DUT.scala b/tests/cosim/elaborate/src/DUT.scala new file mode 100644 index 00000000000..c59aed6110a --- /dev/null +++ b/tests/cosim/elaborate/src/DUT.scala @@ -0,0 +1,95 @@ +package cosim.elabotate + +import chisel3._ +import freechips.rocketchip.diplomacy.{AddressSet, BundleBridgeSource, InModuleBody, LazyModule, RegionType, SimpleLazyModule, TransferSizes} +import freechips.rocketchip.interrupts.{IntSinkNode, IntSinkPortSimple, IntSourceNode, IntSourcePortSimple} +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.tilelink.{TLManagerNode, TLSlaveParameters, TLSlavePortParameters} +import org.chipsalliance.cde.config.Parameters +import freechips.rocketchip.tile.{NMI, PriorityMuxHartIdFromSeq, RocketTile} + +class DUT(p: Parameters) extends Module { + implicit val implicitP = p + val tileParams = p(RocketTileParamsKey) + val ldut = LazyModule(new SimpleLazyModule { + implicit val implicitP = p + val rocketTile = LazyModule(new RocketTile(tileParams, RocketCrossingParams(), PriorityMuxHartIdFromSeq(Seq(tileParams)))) + val masterNode = TLManagerNode(Seq(TLSlavePortParameters.v1( + Seq(TLSlaveParameters.v1( + address = List(AddressSet(0x0, 0xffffffffL)), + regionType = RegionType.UNCACHED, + executable = true, + supportsGet = TransferSizes(1, 64), + supportsAcquireT = TransferSizes(1, 64), + supportsAcquireB = TransferSizes(1, 64), + supportsPutPartial = TransferSizes(1, 64), + supportsPutFull = TransferSizes(1, 64), + supportsLogical = TransferSizes(1, 64), + supportsArithmetic = TransferSizes(1, 64), + fifoId = Some(0))), + beatBytes = 8, + endSinkId = 4, + minLatency = 1 + ))) + masterNode :=* rocketTile.masterNode + val memory = InModuleBody { + masterNode.makeIOs() + } + + val intNode = IntSourceNode(IntSourcePortSimple()) + rocketTile.intInwardNode :=* intNode + val intIn = InModuleBody { + intNode.makeIOs() + } + + val haltNode = IntSinkNode(IntSinkPortSimple()) + haltNode :=* rocketTile.haltNode + val haltOut = InModuleBody { + haltNode.makeIOs() + } + + val ceaseNode = IntSinkNode(IntSinkPortSimple()) + ceaseNode :=* rocketTile.ceaseNode + val ceaseOut = InModuleBody { + ceaseNode.makeIOs() + } + + val wfiNode = IntSinkNode(IntSinkPortSimple()) + wfiNode :=* rocketTile.wfiNode + val wfiOut = InModuleBody { + wfiNode.makeIOs() + } + val resetVectorNode = BundleBridgeSource(() => UInt(32.W)) + rocketTile.resetVectorNode := resetVectorNode + val resetVector = InModuleBody { + resetVectorNode.makeIO() + } + val hartidNode = BundleBridgeSource(() => UInt(4.W)) + rocketTile.hartIdNode := hartidNode + InModuleBody { + hartidNode.bundle := 0.U + } + val nmiNode = BundleBridgeSource(Some(() => new NMI(32))) + rocketTile.nmiNode := nmiNode + val nmi = InModuleBody { + nmiNode.makeIO() + } + }) + chisel3.experimental.DataMirror.fullModulePorts( + // instantiate the LazyModule + Module(ldut.module) + ).filterNot(_._2.isInstanceOf[Aggregate]).foreach { case (name, ele) => + if (!(name == "clock" || name == "reset")) { + chisel3.experimental.DataMirror.directionOf(ele) match { + case ActualDirection.Output => + val io = IO(Output(chiselTypeOf(ele))).suggestName(name) + println(s"output $name") + io := ele + case ActualDirection.Input => + val io = IO(Input(chiselTypeOf(ele))).suggestName(name) + println(s"input $name") + ele := io + } + } + } +} diff --git a/tests/cosim/elaborate/src/Main.scala b/tests/cosim/elaborate/src/Main.scala new file mode 100644 index 00000000000..fc44c1cd599 --- /dev/null +++ b/tests/cosim/elaborate/src/Main.scala @@ -0,0 +1,72 @@ +package cosim.elabotate + +import chisel3.aop.Select +import chisel3.aop.injecting.InjectingAspect +import chisel3.stage.ChiselGeneratorAnnotation +import circt.stage.{CIRCTTarget, CIRCTTargetAnnotation, ChiselStage, FirtoolOption} +import firrtl.options.TargetDirAnnotation +import firrtl.{AnnotationSeq, ChirrtlEmitter, EmitAllModulesAnnotation} +import freechips.rocketchip.devices.debug.DebugModuleKey +import freechips.rocketchip.diplomacy.MonitorsEnabled +import freechips.rocketchip.subsystem.{CacheBlockBytes, SystemBusKey, SystemBusParams} +import mainargs._ +import org.chipsalliance.cde.config.{Config, Field} +import freechips.rocketchip.rocket.{DCacheParams, FrontendModule, ICacheModule, ICacheParams, MulDivParams, Rocket, RocketCoreParams} +import freechips.rocketchip.tile.RocketTileParams + + + +object RocketTileParamsKey extends Field[RocketTileParams] + +object Main { + @main + def elaborate( + @arg(name = "dir") dir: String, + ): Unit = { + (new ChiselStage).transform(AnnotationSeq(Seq( + TargetDirAnnotation(dir), + new ChiselGeneratorAnnotation(() => { + new DUT( + new Config((site, here, up) => { + case MonitorsEnabled => false + case freechips.rocketchip.tile.XLen => 64 + case freechips.rocketchip.tile.XLen => 64 + case freechips.rocketchip.tile.MaxHartIdBits => 4 + case freechips.rocketchip.tile.MaxHartIdBits => 4 + case freechips.rocketchip.rocket.PgLevels => if (site(freechips.rocketchip.tile.XLen) == 64) 3 else 2 + case freechips.rocketchip.rocket.PgLevels => if (site(freechips.rocketchip.tile.XLen) == 64) 3 else 2 + case RocketTileParamsKey => RocketTileParams( + core = RocketCoreParams(mulDiv = Some(MulDivParams( + mulUnroll = 8, + mulEarlyOut = true, + divEarlyOut = true))), + dcache = Some(DCacheParams( + rowBits = site(SystemBusKey).beatBits, + nMSHRs = 0, + blockBytes = site(CacheBlockBytes))), + icache = Some(ICacheParams( + rowBits = site(SystemBusKey).beatBits, + blockBytes = site(CacheBlockBytes)))) + case SystemBusKey => SystemBusParams( + beatBytes = site(freechips.rocketchip.tile.XLen) / 8, + blockBytes = site(CacheBlockBytes)) + case DebugModuleKey => None + }) + ) + }), + firrtl.passes.memlib.InferReadWriteAnnotation, + CIRCTTargetAnnotation(CIRCTTarget.Verilog), + EmitAllModulesAnnotation(classOf[ChirrtlEmitter]), + FirtoolOption("--disable-annotation-unknown"), + InjectingAspect( + { dut: DUT => Select.collectDeep(dut){ case icache : ICacheModule => icache } }, + { + icache : ICacheModule => + chisel3.experimental.Trace.traceName(icache.s2_miss) + } + ) + ))) + } + + def main(args: Array[String]): Unit = ParserForMethods(this).runOrExit(args) +} diff --git a/tests/cosim/emulator/src/exceptions.h b/tests/cosim/emulator/src/exceptions.h new file mode 100644 index 00000000000..62fa382cf2f --- /dev/null +++ b/tests/cosim/emulator/src/exceptions.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +class CosimException : public std::runtime_error { +public: + explicit CosimException(const char *what) : runtime_error(what) {} +}; + +class TimeoutException : CosimException { +public: + TimeoutException() : CosimException("timeout") {} +}; + +#define CHECK_S(condition) \ + LOG_IF(FATAL_S, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ + << "Check failed: " #condition " " diff --git a/tests/cosim/emulator/src/glog_exception_safe.h b/tests/cosim/emulator/src/glog_exception_safe.h new file mode 100644 index 00000000000..4db570d2491 --- /dev/null +++ b/tests/cosim/emulator/src/glog_exception_safe.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace google { + + class CheckFailedException : public std::runtime_error { + public: + explicit CheckFailedException() : std::runtime_error("check failed") {} + }; + + class LogMessageFatal_S : public LogMessage { + public: + LogMessageFatal_S(const char *file, int line) : LogMessage(file, line, GLOG_ERROR){}; + LogMessageFatal_S(const char *file, int line, const CheckOpString &result) : LogMessage(file, line, GLOG_ERROR) { + stream() << "Check failed: " << (*result.str_) << " "; + }; + ~LogMessageFatal_S() noexcept(false) { + Flush(); + throw CheckFailedException(); + }; + }; +}// namespace google + +#define CHECK_OP_S(name, op, val1, val2) \ + CHECK_OP_LOG(name, op, val1, val2, google::LogMessageFatal_S) + +#define COMPACT_GOOGLE_LOG_FATAL_S google::LogMessageFatal_S(__FILE__, __LINE__) + +#define CHECK_EQ_S(val1, val2) CHECK_OP_S(_EQ, ==, val1, val2) +#define CHECK_NE_S(val1, val2) CHECK_OP_S(_NE, !=, val1, val2) +#define CHECK_LE_S(val1, val2) CHECK_OP_S(_LE, <=, val1, val2) +#define CHECK_LT_S(val1, val2) CHECK_OP_S(_LT, <, val1, val2) +#define CHECK_GE_S(val1, val2) CHECK_OP_S(_GE, >=, val1, val2) +#define CHECK_GT_S(val1, val2) CHECK_OP_S(_GT, >, val1, val2) diff --git a/tests/cosim/emulator/src/main.cc b/tests/cosim/emulator/src/main.cc new file mode 100644 index 00000000000..6b9fb27b304 --- /dev/null +++ b/tests/cosim/emulator/src/main.cc @@ -0,0 +1,32 @@ +#include +#include +#include + +#include "exceptions.h" +#include "glog_exception_safe.h" +#include "vbridge.h" + +int main(int argc, char **argv) { + FLAGS_logtostderr = 1; + google::InitGoogleLogging(argv[0]); + google::InstallFailureSignalHandler(); + + args::ArgumentParser parser("Rocket"); + args::ValueFlag bin(parser, "bin", "test case path", {"bin"}); + args::ValueFlag entrance(parser, "entrance", "entrance", {"entrance"}); + args::ValueFlag wave(parser, "wave", "wave output path(in fst)", {"wave"}); + args::ValueFlag reset_vector(parser, "reset_vector", "set reset vector", {"reset-vector"}, 0x80000000); + args::ValueFlag cycles(parser, "cycles", "set simulation cycles", {"cycles"}, 1000); + parser.ParseCLI(argc, argv); + + try { + VBridge vb; + vb.configure_simulator(argc, argv); + vb.setup(bin.Get(), entrance.Get(), wave.Get() + ".fst", reset_vector.Get(), cycles.Get()); + vb.loop(); + } catch (TimeoutException &e) { + return 0; + } catch (google::CheckFailedException &e) { + return 1; + } +} diff --git a/tests/cosim/emulator/src/simple_sim.h b/tests/cosim/emulator/src/simple_sim.h new file mode 100644 index 00000000000..79d0283012a --- /dev/null +++ b/tests/cosim/emulator/src/simple_sim.h @@ -0,0 +1,79 @@ +#pragma once +#include "mmu.h" +#include "simif.h" +#include +#include +#include + +class simple_sim : public simif_t { +private: + char *mem; + uint64_t size; + +public: + explicit simple_sim(size_t mem_size) { + mem = new char[(mem_size * 4)]; + size = mem_size * 4; + LOG(INFO) << fmt::format("mem size = {:08X}", mem_size * 4); + } + + ~simple_sim() override { + delete[] mem; + } + + void load(const std::string &fname, const std::string &ename, size_t reset_vector) { + std::ifstream fs(fname, std::ifstream::binary); + assert(fs.is_open()); + size_t offset = reset_vector; + while (!fs.eof()) { + fs.read(&mem[offset], 1024); + offset += fs.gcount(); + } + + std::ifstream fs_init(ename, std::ifstream::binary); + assert(fs_init.is_open()); + size_t cnt = 0x1000; + while (!fs_init.eof()) { + fs_init.read(&mem[cnt], 1024); + } + + //print mem + uint32_t addr = 0x80002000; + for (int i = 0; i < 4; i++) { + uint32_t insn = 0; + for (int j = 0; j < 4; j++) { + insn += (uint64_t) *addr_to_mem(addr + j + i * 4) << (j * 8); + } + LOG(INFO) << fmt::format("scan mem: {:08X} , at:{:08X}", insn, addr + i * 4); + } + } + + // should return NULL for MMIO addresses + // todo: for more oversize mem access return mem[0] + char *addr_to_mem(reg_t addr) override { + if (!paddr_ok(addr)) + return NULL; + return &mem[addr]; + } + // Do not use mmio;return false for Instruction access fault + bool mmio_load(reg_t addr, size_t len, uint8_t *bytes) override { + return false; + } + + bool mmio_store(reg_t addr, size_t len, const uint8_t *bytes) override { + assert(false && "not implemented"); + } + + // Callback for processors to let the simulation know they were reset. + void proc_reset(unsigned id) override { + // maybe nothing to do + } + + const char *get_symbol(uint64_t addr) override { + LOG(FATAL) << "not implemented"; + } + + static bool paddr_ok(reg_t addr) { + return (addr >> MAX_PADDR_BITS) == 0; + } +}; diff --git a/tests/cosim/emulator/src/spike_event.cc b/tests/cosim/emulator/src/spike_event.cc new file mode 100644 index 00000000000..a21da0b0f09 --- /dev/null +++ b/tests/cosim/emulator/src/spike_event.cc @@ -0,0 +1,241 @@ +#include +#include + +#include "disasm.h" +#include "exceptions.h" +#include "glog_exception_safe.h" +#include "spike_event.h" +#include "tl_interface.h" +#include "util.h" + +std::string SpikeEvent::describe_insn() const { + return fmt::format("pc={:08X}, bits={:08X}, disasm='{}'", pc, inst_bits, proc.get_disassembler()->disassemble(inst_bits)); +} + +void SpikeEvent::pre_log_arch_changes() { + if (is_store | is_load | is_amo) { + uint64_t address = target_mem; + uint64_t addr_align = address & 0xFFFFFFC0; + // record mem block for cache + for (int i = 0; i < 8; i++) { + uint64_t data = 0; + //scan 8 bytes to data + for (int j = 0; j < 8; ++j) { + data += (uint64_t) impl->load(addr_align + j + i * 8) << (j * 8); + } + block.blocks[i] = data; + block.addr = addr_align; + block.remaining = true; + } + LOG(INFO) << fmt::format("spike pre_log mem access on:{:08X} ; block_addr={:08X}", address, addr_align); + } +} + +void SpikeEvent::log_arch_changes() { + + state_t *state = proc.get_state(); + + for (auto [write_idx, data]: state->log_reg_write) { + // in spike, log_reg_write is arrange: + // xx0000 <- x + // xx0001 <- f + // xx0010 <- vreg + // xx0011 <- vec + // xx0100 <- csr + + if ((write_idx & 0xf) == 0b0000) {// scalar rf + uint64_t rd_should_be_bits = proc.get_state()->XPR[rd_idx]; + if (rd_new_bits != rd_should_be_bits) { + rd_new_bits = rd_should_be_bits; + is_rd_written = true; + LOG(INFO) << fmt::format("Log Spike {:08X} with scalar rf change: x[{}] from {:08X} to {:08X}", pc, rd_idx, rd_old_bits, rd_new_bits); + } + } + } + + for (auto mem_write: state->log_mem_write) { + uint64_t address = std::get<0>(mem_write); + if (address != target_mem) { + LOG(FATAL) << fmt::format("Error! spike detect mem_write at= {:08X}; target mem = {:08X}", address, target_mem); + } + uint64_t value = std::get<1>(mem_write); + // Byte size_bytes + uint8_t size_by_byte = std::get<2>(mem_write); + LOG(INFO) << fmt::format("spike detect mem write {:08X} on mem:{:08X} with size={}byte", value, address, size_by_byte); + mem_access_record.all_writes[address] = {.size_by_byte = size_by_byte, .val = value}; + } + // since log_mem_read doesn't record mem data, we need to load manually + for (auto mem_read: state->log_mem_read) { + uint64_t address = std::get<0>(mem_read); + if (address != target_mem) { + LOG(FATAL) << fmt::format("Error! spike detect mem_read at= {:08X}; target mem = {:08X}", address, target_mem); + } + // Byte size_bytes + uint8_t size_by_byte = std::get<2>(mem_read); + uint64_t value = 0; + // record mem target + for (int i = 0; i < size_by_byte; ++i) { + value += (uint64_t) impl->load(address + i) << (i * 8); + } + LOG(INFO) << fmt::format("spike detect mem read {:08X} on mem:{:08X} with size={}byte", value, address, size_by_byte); + mem_access_record.all_reads[address] = {.size_by_byte = size_by_byte, .val = value}; + } + + // record root page table + if (satp_mode == 0x8 && block.addr == -1) { + uint64_t root_addr = satp_ppn << 12; + for (int i = 0; i < 8; i++) { + uint64_t data = 0; + //scan 8 bytes to data + for (int j = 0; j < 8; ++j) { + data += (uint64_t) impl->load(root_addr + j + i * 8) << (j * 8); + } + block.blocks[i] = data; + block.addr = root_addr; + block.remaining = true; + } + } + + state->log_reg_write.clear(); + state->log_mem_read.clear(); + state->log_mem_write.clear(); +} + +SpikeEvent::SpikeEvent(processor_t &proc, insn_fetch_t &fetch, VBridgeImpl *impl) : proc(proc), impl(impl) { + auto &xr = proc.get_state()->XPR; + pc = proc.get_state()->pc; + inst_bits = fetch.insn.bits(); + target_mem = -1; + is_committed = false;// j insn should be committed immediately cause it doesn't have wb stage. + + // extension depending parameter + is_compress = false; + if (fetch.insn.length() == 2) { + is_compress = true; + } + if (!is_compress) { + rs1_bits = xr[fetch.insn.rs1()]; + rs2_bits = xr[fetch.insn.rs2()]; + rd_idx = fetch.insn.rd(); + opcode = clip(inst_bits, 0, 6); + // for j insn for x0; + if (opcode == 0b1101111 && rd_idx == 0) { + is_committed = true; + } + // for integer/double load/store + is_load = (opcode == 0b11) || (opcode == 0b0000111); + is_store = opcode == 0b100011 || (opcode == 0b0100111); + is_amo = opcode == 0b0101111; + + if (is_load) { + target_mem = rs1_bits + fetch.insn.i_imm(); + } + if (is_store) target_mem = rs1_bits + fetch.insn.s_imm(); + if (is_amo) target_mem = rs1_bits; + } else { + uint8_t op = inst_bits & 0b11; + uint8_t func3 = (inst_bits & 0xE000) >> 13; + uint64_t rs1s_bits = xr[fetch.insn.rvc_rs1s()]; + uint64_t sp_bits = xr[2]; + switch (op) { + case 0: + rd_idx = 8 + ((inst_bits & 0b11100) >> 2);// todo: rd_idx for store insn? + if (func3 >= 5) { + is_store = true; + switch (func3) { + case 5:// C.FSD + target_mem = rs1s_bits + fetch.insn.rvc_ld_imm(); + break; + case 6:// C.SW + target_mem = rs1s_bits + fetch.insn.rvc_lw_imm(); + break; + case 7:// C.SD + target_mem = rs1s_bits + fetch.insn.rvc_ld_imm(); + break; + default: + LOG(FATAL) << fmt::format("unknown compress func3"); + } + } else if (func3 >= 1 && func3 <= 3) {// include all 0-> the illegal insn + is_load = true; + switch (func3) { + case 1:// C.FLD + target_mem = rs1s_bits + fetch.insn.rvc_ld_imm(); + break; + case 2:// C.LW + target_mem = rs1s_bits + fetch.insn.rvc_lw_imm(); + break; + case 3:// C.LD + target_mem = rs1s_bits + fetch.insn.rvc_ld_imm(); + break; + default: + LOG(FATAL) << fmt::format("unknown compress func3"); + } + } else { + } + // for store insn, no matter rd_idx + break; + case 1:// no load/store insn; 2 types rd_idx format + if (func3 <= 3) rd_idx = fetch.insn.rd(); + else { + rd_idx = fetch.insn.rvc_rs1s(); + } + break; + case 2: + if (func3 >= 5) { + is_store = true; + rd_idx = 0;// todo: set rd_idx to 0 + switch (func3) { + case 5:// C.FSDSP + target_mem = sp_bits + fetch.insn.rvc_sdsp_imm(); + break; + case 6:// C.SWSP + target_mem = sp_bits + fetch.insn.rvc_swsp_imm(); + break; + case 7:// C.SDSP + target_mem = sp_bits + fetch.insn.rvc_sdsp_imm(); + break; + default: + LOG(FATAL) << fmt::format("unknown compress func3"); + } + } else if (func3 >= 1 && func3 <= 3) {// for C.LWSP etc. + is_load = true; + rd_idx = fetch.insn.rd(); + switch (func3) { + case 1:// C.FLDSP + target_mem = sp_bits + fetch.insn.rvc_ldsp_imm(); + break; + case 2:// C.LWSP + target_mem = sp_bits + fetch.insn.rvc_lwsp_imm(); + break; + case 3:// C.LDSP + target_mem = sp_bits + fetch.insn.rvc_ldsp_imm(); + break; + default: + LOG(FATAL) << fmt::format("unknown compress func3"); + } + + } else if (func3 == 4 && (((inst_bits & 0x1000) >> 12) == 1) && (fetch.insn.rvc_rs1() != 0) && (fetch.insn.rvc_rs2() == 0)) { + // C.JALR + LOG(INFO) << fmt::format("find C.JALR"); + rd_idx = 1;// write x2 + + } else { + rd_idx = fetch.insn.rd(); + } + break; + default: + LOG(FATAL) << fmt::format("unknown compress opcode"); + } + } + rd_old_bits = proc.get_state()->XPR[rd_idx]; + is_csr = opcode == 0b1110011; + is_issued = false; + is_trap = false; + + satp = proc.get_state()->satp->read(); + satp_ppn = satp & 0xFFFFFFFFFFF; + satp_mode = clip(satp, 60, 63); + + block.addr = -1; + disasm = proc.get_disassembler()->disassemble(fetch.insn); +} diff --git a/tests/cosim/emulator/src/spike_event.h b/tests/cosim/emulator/src/spike_event.h new file mode 100644 index 00000000000..c3064050374 --- /dev/null +++ b/tests/cosim/emulator/src/spike_event.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include + +#include "mmu.h" +#include "processor.h" + +#include "VV.h" +#include "verilated_fst_c.h" + +#include "simple_sim.h" +#include "vbridge_config.h" +#include "vbridge_impl.h" + +class VBridgeImpl; + +struct Cacheblock { + uint64_t addr; + uint64_t blocks[8]; + bool remaining; +}; + +struct SpikeEvent { + SpikeEvent(processor_t &proc, insn_fetch_t &fetch, VBridgeImpl *impl); + + [[nodiscard]] std::string describe_insn() const; + + void pre_log_arch_changes(); + void log_arch_changes(); + + commit_log_mem_t mem_read_info; + + struct mem_log { + uint64_t addr; + uint64_t value; + uint8_t size; + }; + std::vector log_mem_queue; + + processor_t &proc; + VBridgeImpl *impl; + + bool is_issued; + bool is_committed; + + uint8_t opcode; + bool is_load; + bool is_store; + bool is_csr; + bool is_amo; + + std::string disasm; + + + uint64_t pc; + uint32_t inst_bits; + bool is_compress; + + // scalar to vector interface(used for driver) + uint32_t rs1_bits; + uint32_t rs2_bits; + // rd idx and bits before insn + uint32_t rd_idx; + uint64_t rd_old_bits; + // rd idx and bits after insn + uint64_t rd_new_bits; + bool is_rd_written; + + //csr + uint64_t satp; + uint64_t satp_ppn; + uint8_t satp_mode; + + bool is_trap; + + Cacheblock block; + + uint64_t target_mem; + std::list cache_queue; + + struct { + struct single_mem_write { + uint32_t size_by_byte; + reg_t val; + bool executed = false;// set to true when rtl execute this mem access + }; + struct single_mem_read { + uint16_t size_by_byte; + reg_t val; + bool executed = false;// set to true when rtl execute this mem access + }; + std::map all_writes; + std::map all_reads; + } mem_access_record; +}; diff --git a/tests/cosim/emulator/src/tl_interface.h b/tests/cosim/emulator/src/tl_interface.h new file mode 100644 index 00000000000..b28024d138d --- /dev/null +++ b/tests/cosim/emulator/src/tl_interface.h @@ -0,0 +1,42 @@ +#include + +namespace TlOpcode { + constexpr int + AcquireBlock = 6, + Get = 4, + AccessAckData = 1, + PutFullData = 0, + PutPartialData = 1, + AccessAck = 4; +} + +// the following macro helps us to access tl interface by dynamic index +#define TL_INTERFACE(type, name) \ + [[nodiscard]] inline type &get_tl_##name(VV &top) { \ + return top.memory_0_##name; \ + } + + +TL_INTERFACE(CData, a_ready); +TL_INTERFACE(CData, a_valid); +TL_INTERFACE(CData, a_bits_opcode); +TL_INTERFACE(CData, a_bits_param); +TL_INTERFACE(CData, a_bits_size); +TL_INTERFACE(CData, a_bits_mask); +TL_INTERFACE(CData, a_bits_corrupt); +TL_INTERFACE(CData, a_bits_source); +TL_INTERFACE(IData, a_bits_address); +TL_INTERFACE(QData, a_bits_data); + +TL_INTERFACE(CData, d_ready); +TL_INTERFACE(CData, d_valid); +TL_INTERFACE(CData, d_bits_opcode); +TL_INTERFACE(CData, d_bits_param); +TL_INTERFACE(CData, d_bits_size); +TL_INTERFACE(CData, d_bits_denied); +TL_INTERFACE(CData, d_bits_corrupt); +TL_INTERFACE(CData, d_bits_source); +TL_INTERFACE(CData, d_bits_sink); +TL_INTERFACE(QData, d_bits_data); + +#undef TL_INTERFACE diff --git a/tests/cosim/emulator/src/util.h b/tests/cosim/emulator/src/util.h new file mode 100644 index 00000000000..74d01a25f26 --- /dev/null +++ b/tests/cosim/emulator/src/util.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +/// @return: binary[a, b] +inline uint64_t clip(uint64_t binary, int a, int b) { return (binary >> a) & ((1 << (b - a + 1)) - 1); } + +/// back-port of `std::erase_if` in C++ 20. +/// refer to https://en.cppreference.com/w/cpp/container/map/erase_if +template +typename std::map::size_type +erase_if(std::map &c, Pred pred) { + auto old_size = c.size(); + for (auto i = c.begin(), last = c.end(); i != last;) { + if (pred(*i)) { + i = c.erase(i); + } else { + ++i; + } + } + return old_size - c.size(); +}; \ No newline at end of file diff --git a/tests/cosim/emulator/src/vbridge.cc b/tests/cosim/emulator/src/vbridge.cc new file mode 100644 index 00000000000..b876aedfed8 --- /dev/null +++ b/tests/cosim/emulator/src/vbridge.cc @@ -0,0 +1,19 @@ +#include "vbridge.h" +#include "vbridge_impl.h" + +void VBridge::setup(const std::string &bin, const std::string &ebin, const std::string &wave, uint64_t reset_vector, uint64_t cycles) const { + impl->setup(bin, ebin, wave, reset_vector, cycles); +} + +VBridge::VBridge() : impl(std::make_unique()) {} + +VBridge::~VBridge() { +} + +void VBridge::loop() const { + impl->run(); +} + +void VBridge::configure_simulator(int argc, char **argv) const { + impl->configure_simulator(argc, argv); +} diff --git a/tests/cosim/emulator/src/vbridge.h b/tests/cosim/emulator/src/vbridge.h new file mode 100644 index 00000000000..fee1459c129 --- /dev/null +++ b/tests/cosim/emulator/src/vbridge.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +struct VBridgeImpl; + +class VBridge { +public: + VBridge(); + + ~VBridge(); + + void setup(const std::string &bin, const std::string &ebin, const std::string &vcd, uint64_t reset_vector, uint64_t cycles) const; + + void loop() const; + + void configure_simulator(int argc, char **argv) const; + +private: + std::unique_ptr impl; +}; diff --git a/tests/cosim/emulator/src/vbridge_config.h b/tests/cosim/emulator/src/vbridge_config.h new file mode 100644 index 00000000000..e6dcf8d12b5 --- /dev/null +++ b/tests/cosim/emulator/src/vbridge_config.h @@ -0,0 +1,19 @@ +#pragma once + +namespace consts { + + // simulation arch config + constexpr int vlen_in_bits = 1024; + constexpr int vlen_in_bytes = vlen_in_bits / 8; + constexpr int elen = 32; + constexpr int numVRF = 32; + + // const as default value + constexpr int lsuIdxDefault = 255; + + // rtl parameters + constexpr int numTL = 2; + constexpr int numMSHR = 3; + constexpr int numLanes = 8; + +}// namespace consts diff --git a/tests/cosim/emulator/src/vbridge_impl.cc b/tests/cosim/emulator/src/vbridge_impl.cc new file mode 100644 index 00000000000..15d3261193d --- /dev/null +++ b/tests/cosim/emulator/src/vbridge_impl.cc @@ -0,0 +1,474 @@ +#include +#include + +#include "disasm.h" + +#include "verilated.h" + +#include "exceptions.h" +#include "simple_sim.h" +#include "tl_interface.h" +#include "util.h" +#include "vbridge_impl.h" +#include "vpi.h" + +#include "glog_exception_safe.h" + +/// convert TL style size to size_by_bytes +inline uint32_t decode_size(uint32_t encoded_size) { + return 1 << encoded_size; +} + +VBridgeImpl::VBridgeImpl() : sim(1 << 30), + isa("rv64gc", "msu"), + _cycles(100), + proc( + /*isa*/ &isa, + /*varch*/ fmt::format("").c_str(), + /*sim*/ &sim, + /*id*/ 0, + /*halt on reset*/ true, + /* endianness*/ memif_endianness_little, + /*log_file_t*/ nullptr, + /*sout*/ std::cerr) { +} + +void VBridgeImpl::setup(const std::string &_bin, const std::string &_ebin, const std::string &_wave, uint64_t _reset_vector, uint64_t cycles) { + this->bin = _bin; + this->ebin = _ebin; + this->wave = _wave; + this->reset_vector = _reset_vector; + this->timeout = cycles; +} + + +void VBridgeImpl::reset() { + top.clock = 0; + top.reset = 1; + top.resetVector = 0x1000; + top.eval(); + tfp.dump(0); + + // posedge + top.clock = 1; + top.eval(); + tfp.dump(1); + + + // negedge + top.reset = 0; + top.clock = 0; + top.eval(); + tfp.dump(2); + // posedge + LOG(INFO) << fmt::format("Reset compeleted, now we start"); + top.reset = 0; + top.clock = 1; + top.eval(); + tfp.dump(3); + ctx.time(2); +} + +VBridgeImpl::~VBridgeImpl() { + terminate_simulator(); +} + +void VBridgeImpl::configure_simulator(int argc, char **argv) { + ctx.commandArgs(argc, argv); +} + +void VBridgeImpl::init_spike() { + auto state = proc.get_state(); + LOG(INFO) << fmt::format("Spike reset misa={:08X}", state->misa->read()); + LOG(INFO) << fmt::format("Spike reset mstatus={:08X}", state->mstatus->read()); + // load binary to reset_vector + sim.load(bin, ebin, reset_vector); +} + +SpikeEvent *find_se_to_issue(); +void VBridgeImpl::init_simulator() { + Verilated::traceEverOn(true); + top.trace(&tfp, 99); + tfp.open(wave.c_str()); + _cycles = timeout; +} + +void VBridgeImpl::terminate_simulator() { + if (tfp.isOpen()) { + tfp.close(); + } + top.final(); +} + +uint64_t VBridgeImpl::get_t() { + return ctx.time(); +} + +uint8_t VBridgeImpl::load(uint64_t address) { + return *sim.addr_to_mem(address); +} + +/* For TL Acquire + * Record mem info in se.block when init spike event; + * receive tl Acquire, find corresponding SE in queue. store se.block info in AcquireBanks;set AcquireBanks.remaining as true; + * return: drive D channel with AcquireBanks + * */ +void VBridgeImpl::run() { + + init_spike(); + // sim.load(bin, reset_vector); + init_simulator(); + reset(); + + // start loop + // queue size must > for trap insn to pop correct + while (true) { + loop_until_se_queue_full(); + while (to_rtl_queue.size() > 1) { + return_fetch_response(); + + top.clock = 1; + top.eval(); + + top.memory_0_a_ready = 1; + top.memory_0_c_ready = 1; + top.memory_0_e_ready = 1; + // negedge + + receive_tl_req(); + top.clock = 0; + top.eval(); + tfp.dump(2 * ctx.time()); + ctx.timeInc(1); + + + // posedge, update registers + top.clock = 1; + top.eval(); + tfp.dump(2 * ctx.time() - 1); + + + // when RTL write back + if (top.rootp->DUT__DOT__ldut__DOT__tile__DOT__core__DOT__wb_valid) { + uint64_t pc = top.rootp->DUT__DOT__ldut__DOT__tile__DOT__core__DOT__wb_reg_pc; + LOG(INFO) << fmt::format("RTL write back insn {:08X} ", pc); + // Check rf write + // todo: use rf_valid + if (top.rootp->DUT__DOT__ldut__DOT__tile__DOT__core__DOT____Vcellinp__rf_ext__W0_en) { + record_rf_access(); + } + // commit spike event + bool commit = false; + + for (auto se_iter = to_rtl_queue.rbegin(); se_iter != to_rtl_queue.rend(); se_iter++) { + if (se_iter->pc == pc) { + // mechanism to the insn which causes trap. + // trapped insn will commit with the first insn after trap(0x80000004). + // It demands the trap insn not to be the last one in the queue. + if (se_iter->pc == 0x80000004) { + for (auto se_it = to_rtl_queue.rbegin(); se_it != to_rtl_queue.rend(); se_it++) { + if (se_it->is_trap) se_it->is_committed = true; + } + } + commit = true; + se_iter->is_committed = true; + LOG(INFO) << fmt::format("Set spike {:08X} as committed", se_iter->pc); + break; + } + } + if (!commit) LOG(INFO) << fmt::format("RTL wb without se in pc = {:08X}", pc); + // pop the committed Event from the queue + for (int i = 0; i < to_rtl_queue_size; i++) { + if (to_rtl_queue.back().is_committed) { + LOG(INFO) << fmt::format("Pop SE pc = {:08X} ", to_rtl_queue.back().pc); + to_rtl_queue.pop_back(); + } + } + } + if (get_t() >= timeout) { + throw TimeoutException(); + } + } + } +} + +void VBridgeImpl::loop_until_se_queue_full() { + LOG(INFO) << fmt::format("Refilling Spike queue"); + while (to_rtl_queue.size() < to_rtl_queue_size) { + try { + std::optional spike_event = spike_step(); + if (spike_event.has_value()) { + SpikeEvent &se = spike_event.value(); + to_rtl_queue.push_front(std::move(se)); + } + } catch (trap_t &trap) { + LOG(FATAL) << fmt::format("spike trapped with {}", trap.name()); + } + } + LOG(INFO) << fmt::format("to_rtl_queue is full now, start to simulate."); + for (auto se_iter = to_rtl_queue.rbegin(); se_iter != to_rtl_queue.rend(); se_iter++) { + LOG(INFO) << fmt::format("List: spike pc = {:08X}, write reg({}) from {:08x} to {:08X}, is commit:{}", + se_iter->pc, se_iter->rd_idx, se_iter->rd_old_bits, se_iter->rd_new_bits, se_iter->is_committed); + } +} +// don't creat spike event for csr insn +// todo: haven't created spike event for insn which traps during fetch stage; +// dealing with trap: +// most traps are dealt by Spike when [proc.step(1)]; +// traps during fetch stage [fetch = proc.get_mmu()->load_insn(state->pc)] are dealt manually using try-catch block below. +std::optional VBridgeImpl::spike_step() { + auto state = proc.get_state(); + // to use pro.state, set some csr + state->dcsr->halt = false; + // record pc before execute + auto pc_before = state->pc; + try { + auto fetch = proc.get_mmu()->load_insn(state->pc); + auto event = create_spike_event(fetch); + auto &xr = proc.get_state()->XPR; + LOG(INFO) << fmt::format("Spike start to execute pc=[{:08X}] insn = {:08X} DISASM:{}", + pc_before, fetch.insn.bits(), proc.get_disassembler()->disassemble(fetch.insn)); + auto &se = event.value(); + se.pre_log_arch_changes(); + proc.step(1); + se.log_arch_changes(); + // todo: detect exactly the trap + // if a insn_after_pc = 0x80000004,set it as committed + // set insn which traps as committed in case the queue stalls + if (state->pc == 0x80000004) { + se.is_trap = true; + LOG(INFO) << fmt::format("Trap happens at pc = {:08X} ", pc_before); + } + LOG(INFO) << fmt::format("Spike after execute pc={:08X} ", state->pc); + return event; + } catch (trap_t &trap) { + LOG(INFO) << fmt::format("spike fetch trapped with {}", trap.name()); + proc.step(1); + LOG(INFO) << fmt::format("Spike mcause={:08X}", state->mcause->read()); + return {}; + } catch (triggers::matched_t &t) { + LOG(INFO) << fmt::format("spike fetch triggers "); + proc.step(1); + LOG(INFO) << fmt::format("Spike mcause={:08X}", state->mcause->read()); + return {}; + } +} +// now we take all the instruction as spike event except csr insn +std::optional VBridgeImpl::create_spike_event(insn_fetch_t fetch) { + return SpikeEvent{proc, fetch, this}; +} + +// For every RTL commit event, finds the corresponding Spike event and checks their results. +// two types of failure: +// 1: can't find spike event in queue +// 2: results mismatch +void VBridgeImpl::record_rf_access() { + + // peek rtl rf access + uint32_t waddr = top.rootp->DUT__DOT__ldut__DOT__tile__DOT__core__DOT__rf_waddr; + uint64_t wdata = top.rootp->DUT__DOT__ldut__DOT__tile__DOT__core__DOT__rf_wdata; + uint64_t pc = top.rootp->DUT__DOT__ldut__DOT__tile__DOT__core__DOT__wb_reg_pc; + uint64_t insn = top.rootp->DUT__DOT__ldut__DOT__tile__DOT__core__DOT__wb_reg_inst; + + uint8_t opcode = clip(insn, 0, 6); + bool rtl_csr = opcode == 0b1110011; + + // exclude those rtl reg_write from csr insn + if (!rtl_csr) { + LOG(INFO) << fmt::format("RTL wirte reg({}) = {:08X}, pc = {:08X}", waddr, wdata, pc); + // find corresponding spike event + SpikeEvent *se = nullptr; + for (auto se_iter = to_rtl_queue.rbegin(); se_iter != to_rtl_queue.rend(); se_iter++) { + if ((se_iter->pc == pc) && (se_iter->rd_idx == waddr) && (!se_iter->is_committed)) { + se = &(*se_iter); + break; + } + } + if (se == nullptr) { + for (auto se_iter = to_rtl_queue.rbegin(); se_iter != to_rtl_queue.rend(); se_iter++) { + // LOG(INFO) << fmt::format("se pc = {:08X}, rd_idx = {:08X}",se_iter->pc,se_iter->rd_idx); + LOG(INFO) << fmt::format("List: spike pc = {:08X}, write reg({}) from {:08x} to {:08X}, is commit:{}", + se_iter->pc, se_iter->rd_idx, se_iter->rd_old_bits, se_iter->rd_new_bits, se_iter->is_committed); + } + LOG(FATAL) << fmt::format("RTL rf_write Cannot find se ; pc = {:08X} , insn={:08X}, waddr={:08X}", pc, insn, waddr); + } + // start to check RTL rf_write with spike event + // for non-store ins. check rf write + // todo: why exclude store insn? store insn shouldn't write regfile. + if (!(se->is_store)) { + CHECK_EQ_S(wdata, se->rd_new_bits) << fmt::format("\n RTL write Reg({})={:08X} but Spike write={:08X}", waddr, wdata, se->rd_new_bits); + } else { + LOG(INFO) << fmt::format("Find Store insn"); + } + } else { + LOG(INFO) << fmt::format("RTL csr insn wirte reg({}) = {:08X}, pc = {:08X}", waddr, wdata, pc); + } +} + + +void VBridgeImpl::receive_tl_req() { +#define TL(name) (get_tl_##name(top)) + uint64_t pc = top.rootp->DUT__DOT__ldut__DOT__tile__DOT__core__DOT__ex_reg_pc; + int miss = top.rootp->DUT__DOT__ldut__DOT__tile__DOT__frontend__DOT__icache__DOT__s2_miss; + int valid = TL(a_valid); + if (!TL(a_valid)) return; + // store A channel req + uint8_t opcode = TL(a_bits_opcode); + uint32_t addr = TL(a_bits_address); + uint8_t size = TL(a_bits_size); + uint8_t src = TL(a_bits_source);// TODO: be returned in D channel + uint8_t param = TL(a_bits_param); + // find icache refill request, fill fetch_banks + if (miss) { + switch (opcode) { + case TlOpcode::Get: { + for (int i = 0; i < 8; i++) { + uint64_t insn = 0; + for (int j = 0; j < 8; ++j) { + insn += (uint64_t) load(addr + j + i * 8) << (j * 8); + } + fetch_banks[i].data = insn; + fetch_banks[i].source = src; + fetch_banks[i].remaining = true; + } + return; + } + // fetch + case TlOpcode::AcquireBlock: { + cnt = 1; + LOG(INFO) << fmt::format("acquire fetch here "); + for (int i = 0; i < 8; i++) { + uint64_t data = 0; + for (int j = 0; j < 8; ++j) { + data += (uint64_t) load(addr + j + i * 8) << (j * 8); + } + aquire_banks[i].data = data; + aquire_banks[i].param = param; + aquire_banks[i].source = src; + aquire_banks[i].remaining = true; + } + } + default: + LOG(FATAL) << fmt::format("unknown tl opcode {}", opcode); + } + } + // find corresponding SpikeEvent with addr + SpikeEvent *se = nullptr; + for (auto se_iter = to_rtl_queue.rbegin(); se_iter != to_rtl_queue.rend(); se_iter++) { + if (addr == se_iter->block.addr) { + se = &(*se_iter); + LOG(INFO) << fmt::format("success find acqure Spike pc = {:08X}", se_iter->pc); + break; + } + } + // list the queue if error + if (se == nullptr) { + for (auto se_iter = to_rtl_queue.rbegin(); se_iter != to_rtl_queue.rend(); se_iter++) { + LOG(INFO) << fmt::format("List: spike pc = {:08X}, write reg({}) from {:08x} to {:08X}, is commit:{}", + se_iter->pc, se_iter->rd_idx, se_iter->rd_old_bits, se_iter->rd_new_bits, se_iter->is_committed); + LOG(INFO) << fmt::format("List:spike block.addr = {:08X}", se_iter->block.addr); + } + LOG(FATAL) << fmt::format("cannot find spike_event for tl_request; addr = {:08X}, pc = {:08X} , opcode = {}", addr, pc, opcode); + } + + switch (opcode) { + + case TlOpcode::Get: { + auto mem_read = se->mem_access_record.all_reads.find(addr); + CHECK_S(mem_read != se->mem_access_record.all_reads.end()) + << fmt::format(": [{}] cannot find mem read of addr {:08X}", get_t(), addr); + CHECK_EQ_S(mem_read->second.size_by_byte, decode_size(size)) << fmt::format( + ": [{}] expect mem read of size {}, actual size {} (addr={:08X}, {})", + get_t(), mem_read->second.size_by_byte, 1 << decode_size(size), addr, se->describe_insn()); + + uint64_t data = mem_read->second.val; + LOG(INFO) << fmt::format("[{}] receive rtl mem get req (addr={}, size={}byte), should return data {}", + get_t(), addr, decode_size(size), data); + tl_banks.emplace(std::make_pair(addr, TLReqRecord{ + data, 1u << size, src, TLReqRecord::opType::Get, get_mem_req_cycles()})); + mem_read->second.executed = true; + break; + } + + case TlOpcode::PutFullData: { + uint32_t data = TL(a_bits_data); + LOG(INFO) << fmt::format("[{}] receive rtl mem put req (addr={:08X}, size={}byte, data={})", + addr, decode_size(size), data); + auto mem_write = se->mem_access_record.all_writes.find(addr); + + CHECK_S(mem_write != se->mem_access_record.all_writes.end()) + << fmt::format(": [{}] cannot find mem write of addr={:08X}", get_t(), addr); + CHECK_EQ_S(mem_write->second.size_by_byte, decode_size(size)) << fmt::format( + ": [{}] expect mem write of size {}, actual size {} (addr={:08X}, insn='{}')", + get_t(), mem_write->second.size_by_byte, 1 << decode_size(size), addr, se->describe_insn()); + CHECK_EQ_S(mem_write->second.val, data) << fmt::format( + ": [{}] expect mem write of data {}, actual data {} (addr={:08X}, insn='{}')", + get_t(), mem_write->second.size_by_byte, 1 << decode_size(size), addr, se->describe_insn()); + + tl_banks.emplace(std::make_pair(addr, TLReqRecord{ + data, 1u << size, src, TLReqRecord::opType::PutFullData, get_mem_req_cycles()})); + mem_write->second.executed = true; + break; + } + + case TlOpcode::AcquireBlock: { + cnt = 1; + LOG(INFO) << fmt::format("Find AcquireBlock for mem = {:08X}", addr); + for (int i = 0; i < 8; i++) { + uint64_t data = 0; + for (int j = 0; j < 8; ++j) { + data += (uint64_t) load(addr + j + i * 8) << (j * 8); + } + aquire_banks[i].data = se->block.blocks[i]; + aquire_banks[i].param = param; + aquire_banks[i].source = src; + aquire_banks[i].remaining = true; + } + break; + } + +#undef TL + } +} + + +void VBridgeImpl::return_fetch_response() { +#define TL(name) (get_tl_##name(top)) + bool fetch_valid = false; + bool aqu_valid = false; + uint8_t size = 0; + uint16_t source = 0; + uint16_t param = 0; + for (auto &fetch_bank: fetch_banks) { + if (fetch_bank.remaining) { + fetch_bank.remaining = false; + TL(d_bits_opcode) = 1; + TL(d_bits_data) = fetch_bank.data; + source = fetch_bank.source; + size = 6; + fetch_valid = true; + break; + } + } + // todo: source for acquire? + for (auto &aquire_bank: aquire_banks) { + if (cnt) { + cnt = 0; + break; + } + if (aquire_bank.remaining) { + aquire_bank.remaining = false; + TL(d_bits_opcode) = 5; + TL(d_bits_data) = aquire_bank.data; + TL(d_bits_param) = 0; + source = aquire_bank.source; + size = 6; + aqu_valid = true; + } + } +output: + TL(d_bits_source) = source; + TL(d_bits_size) = size; + TL(d_valid) = fetch_valid | aqu_valid; +#undef TL +} diff --git a/tests/cosim/emulator/src/vbridge_impl.h b/tests/cosim/emulator/src/vbridge_impl.h new file mode 100644 index 00000000000..4681b7a9617 --- /dev/null +++ b/tests/cosim/emulator/src/vbridge_impl.h @@ -0,0 +1,116 @@ +#pragma once + +#include +#include + +#include "mmu.h" + +#include "VV.h" +#include "VV___024root.h" +#include "verilated_fst_c.h" + +#include "simple_sim.h" +#include "spike_event.h" +#include "vbridge_config.h" + + +class SpikeEvent; + +struct TLReqRecord { + uint64_t data; + uint32_t size_by_byte; + uint16_t source; + + /// when opType set to nil, it means this record is already sent back + enum class opType { + Nil, + Get, + PutFullData + } op; + int remaining_cycles; + + TLReqRecord(uint64_t data, uint32_t size_by_byte, uint16_t source, opType op, int cycles) : data(data), size_by_byte(size_by_byte), source(source), op(op), remaining_cycles(cycles){}; +}; +struct FetchRecord { + uint64_t data; + uint16_t source; + bool remaining; +}; +struct AquireRecord { + uint64_t data; + uint16_t param; + uint16_t source; + bool remaining; +}; + + +class VBridgeImpl { +public: + explicit VBridgeImpl(); + + ~VBridgeImpl(); + + void setup(const std::string &bin, const std::string &ebin, const std::string &wave, uint64_t reset_vector, uint64_t cycles); + // todo remove this. + void configure_simulator(int argc, char **argv); + + void run(); + + uint8_t load(uint64_t address); + +private: + // verilator context + VerilatedContext ctx; + VV top; + VerilatedFstC tfp; + // mem + simple_sim sim; + // to init spike + isa_parser_t isa; + processor_t proc; + // parameter used in verilator + uint64_t _cycles; + // file path of executeable binary file, which will be executed. + std::string bin; + // file path of entrance binary file + std::string ebin; + // generated waveform path. + std::string wave; + // reset vector + uint64_t reset_vector{}; + // RTL timeout cycles + // note: this is not the real system cycles, scalar instructions is evaulated via spike, which is not recorded. + uint64_t timeout{}; + + // spike + const size_t to_rtl_queue_size = 5; + std::list to_rtl_queue; + + std::map tl_banks; + FetchRecord fetch_banks[8]; + AquireRecord aquire_banks[8]; + + inline void reset(); + + void init_spike(); + void loop_until_se_queue_full(); + std::optional spike_step(); + std::optional create_spike_event(insn_fetch_t fetch); + SpikeEvent *find_se_to_issue(); + + void init_simulator(); + void terminate_simulator(); + uint64_t get_t(); + + // methods for TL channel + void return_tl_response(); + void receive_tl_req(); + void return_fetch_response(); + void return_acquire_response(); + int cnt; + + void record_rf_access(); + int get_mem_req_cycles() { + return 1; + }; +}; diff --git a/tests/cosim/emulator/src/vpi.h b/tests/cosim/emulator/src/vpi.h new file mode 100644 index 00000000000..8222665eba0 --- /dev/null +++ b/tests/cosim/emulator/src/vpi.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +inline auto vpi_get_integer(const char *name) { + vpiHandle handle = vpi_handle_by_name((PLI_BYTE8 *) name, nullptr); + s_vpi_value val; + val.format = vpiIntVal; + vpi_get_value(handle, &val); + return val.value.integer; +} From 8670dc0ba30c36d36bed6730cb5d9b56aa86f54c Mon Sep 17 00:00:00 2001 From: Yanqi Yang Date: Fri, 24 Feb 2023 19:09:59 +0800 Subject: [PATCH 2/5] delete ResourceBinding in tile --- src/main/scala/tile/Interrupts.scala | 27 ++--------------- src/main/scala/tile/RocketTile.scala | 45 ++++++++++------------------ 2 files changed, 18 insertions(+), 54 deletions(-) diff --git a/src/main/scala/tile/Interrupts.scala b/src/main/scala/tile/Interrupts.scala index 92d8b3cedce..6b2012baad9 100644 --- a/src/main/scala/tile/Interrupts.scala +++ b/src/main/scala/tile/Interrupts.scala @@ -1,10 +1,10 @@ // See LICENSE.SiFive for license details. -package freechips.rocketchip.tile +package tile import Chisel._ -import org.chipsalliance.cde.config.Parameters +import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.interrupts._ import freechips.rocketchip.util._ @@ -32,29 +32,6 @@ trait SinksExternalInterrupts { this: BaseTile => protected val intSinkNode = IntSinkNode(IntSinkPortSimple()) intSinkNode := intXbar.intnode - def cpuDevice: Device - val intcDevice = new DeviceSnippet { - override def parent = Some(cpuDevice) - def describe(): Description = { - Description("interrupt-controller", Map( - "compatible" -> "riscv,cpu-intc".asProperty, - "interrupt-controller" -> Nil, - "#interrupt-cells" -> 1.asProperty)) - } - } - - ResourceBinding { - intSinkNode.edges.in.flatMap(_.source.sources).map { case s => - for (i <- s.range.start until s.range.end) { - csrIntMap.lift(i).foreach { j => - s.resources.foreach { r => - r.bind(intcDevice, ResourceInt(j)) - } - } - } - } - } - // TODO: the order of the following two functions must match, and // also match the order which things are connected to the // per-tile crossbar in subsystem.HasTiles.connectInterrupts diff --git a/src/main/scala/tile/RocketTile.scala b/src/main/scala/tile/RocketTile.scala index b308c0c767f..2691fb21031 100644 --- a/src/main/scala/tile/RocketTile.scala +++ b/src/main/scala/tile/RocketTile.scala @@ -1,10 +1,10 @@ // See LICENSE.SiFive for license details. // See LICENSE.Berkeley for license details. -package freechips.rocketchip.tile +package tile import Chisel._ -import org.chipsalliance.cde.config._ +import freechips.rocketchip.config._ import freechips.rocketchip.devices.tilelink._ import freechips.rocketchip.diplomacy._ import freechips.rocketchip.interrupts._ @@ -36,11 +36,11 @@ case class RocketTileParams( } class RocketTile private( - val rocketParams: RocketTileParams, - crossing: ClockCrossingType, - lookup: LookupByHartIdImpl, - q: Parameters) - extends BaseTile(rocketParams, crossing, lookup, q) + val rocketParams: RocketTileParams, + crossing: ClockCrossingType, + lookup: LookupByHartIdImpl, + q: Parameters) + extends BaseTile(rocketParams, crossing, lookup, q) with SinksExternalInterrupts with SourcesExternalNotifications with HasLazyRoCC // implies CanHaveSharedFPU with CanHavePTW with HasHellaCache @@ -87,20 +87,7 @@ class RocketTile private( val itimProperty = frontend.icache.itimProperty.toSeq.flatMap(p => Map("sifive,itim" -> p)) val beuProperty = bus_error_unit.map(d => Map( - "sifive,buserror" -> d.device.asProperty)).getOrElse(Nil) - - val cpuDevice: SimpleDevice = new SimpleDevice("cpu", Seq("sifive,rocket0", "riscv")) { - override def parent = Some(ResourceAnchors.cpus) - override def describe(resources: ResourceBindings): Description = { - val Description(name, mapping) = super.describe(resources) - Description(name, mapping ++ cpuProperties ++ nextLevelCacheProperty - ++ tileProperties ++ dtimProperty ++ itimProperty ++ beuProperty) - } - } - - ResourceBinding { - Resource(cpuDevice, "reg").bind(ResourceAddress(staticIdForMetadataUseOnly)) - } + "sifive,buserror" -> d.device.asProperty)).getOrElse(Nil) override lazy val module = new RocketTileModuleImp(this) @@ -120,9 +107,9 @@ class RocketTile private( } class RocketTileModuleImp(outer: RocketTile) extends BaseTileModuleImp(outer) - with HasFpuOpt - with HasLazyRoCCModule - with HasICacheFrontendModule { + with HasFpuOpt + with HasLazyRoCCModule + with HasICacheFrontendModule { Annotated.params(this, outer.rocketParams) val core = Module(new Rocket(outer)(outer.p)) @@ -133,9 +120,9 @@ class RocketTileModuleImp(outer: RocketTile) extends BaseTileModuleImp(outer) // Report when the tile has ceased to retire instructions; for now the only cause is clock gating outer.reportCease(outer.rocketParams.core.clockGate.option( !outer.dcache.module.io.cpu.clock_enabled && - !outer.frontend.module.io.cpu.clock_enabled && - !ptw.io.dpath.clock_enabled && - core.io.cease)) + !outer.frontend.module.io.cpu.clock_enabled && + !ptw.io.dpath.clock_enabled && + core.io.cease)) outer.reportWFI(Some(core.io.wfi)) @@ -182,8 +169,8 @@ class RocketTileModuleImp(outer: RocketTile) extends BaseTileModuleImp(outer) require(h == c, s"port list size was $h, core expected $c") require(h == o, s"port list size was $h, outer counted $o") // TODO figure out how to move the below into their respective mix-ins - dcacheArb.io.requestor <> dcachePorts.toSeq - ptw.io.requestor <> ptwPorts.toSeq + dcacheArb.io.requestor <> dcachePorts + ptw.io.requestor <> ptwPorts } trait HasFpuOpt { this: RocketTileModuleImp => From ce190c960ef20579265f8bc6a6b29b9585d3bac9 Mon Sep 17 00:00:00 2001 From: Yanqi Yang Date: Fri, 24 Feb 2023 19:12:43 +0800 Subject: [PATCH 3/5] update elaborate src --- tests/cosim/elaborate/src/DUT.scala | 2 + tests/cosim/elaborate/src/Main.scala | 126 ++++++++++++++++----------- 2 files changed, 76 insertions(+), 52 deletions(-) diff --git a/tests/cosim/elaborate/src/DUT.scala b/tests/cosim/elaborate/src/DUT.scala index c59aed6110a..3a99d330602 100644 --- a/tests/cosim/elaborate/src/DUT.scala +++ b/tests/cosim/elaborate/src/DUT.scala @@ -8,6 +8,8 @@ import freechips.rocketchip.tilelink.{TLManagerNode, TLSlaveParameters, TLSlaveP import org.chipsalliance.cde.config.Parameters import freechips.rocketchip.tile.{NMI, PriorityMuxHartIdFromSeq, RocketTile} +import org.chipsalliance.cde.config.{Config, Field} + class DUT(p: Parameters) extends Module { implicit val implicitP = p val tileParams = p(RocketTileParamsKey) diff --git a/tests/cosim/elaborate/src/Main.scala b/tests/cosim/elaborate/src/Main.scala index fc44c1cd599..0042e34bff0 100644 --- a/tests/cosim/elaborate/src/Main.scala +++ b/tests/cosim/elaborate/src/Main.scala @@ -1,5 +1,6 @@ package cosim.elabotate + import chisel3.aop.Select import chisel3.aop.injecting.InjectingAspect import chisel3.stage.ChiselGeneratorAnnotation @@ -14,59 +15,80 @@ import org.chipsalliance.cde.config.{Config, Field} import freechips.rocketchip.rocket.{DCacheParams, FrontendModule, ICacheModule, ICacheParams, MulDivParams, Rocket, RocketCoreParams} import freechips.rocketchip.tile.RocketTileParams +import freechips.rocketchip.system._ +import chisel3.stage.{ChiselCli, ChiselStage} +import firrtl.AnnotationSeq +import firrtl.options.PhaseManager.PhaseDependency +import firrtl.options.{Dependency, Phase, PhaseManager, PreservesAll, Shell, Stage, StageMain} +import firrtl.stage.FirrtlCli +import freechips.rocketchip.stage.RocketChipCli +import scala.collection.immutable.Seq -object RocketTileParamsKey extends Field[RocketTileParams] +/** Modified ChiselStage that includes the GenerateROMs phase */ +private[freechips] final class RocketChiselStage extends ChiselStage { + + override val targets = Seq( + Dependency[chisel3.stage.phases.Checks], + Dependency[chisel3.stage.phases.Elaborate], + Dependency[freechips.rocketchip.stage.phases.GenerateROMs], + Dependency[chisel3.stage.phases.AddImplicitOutputFile], + Dependency[chisel3.stage.phases.AddImplicitOutputAnnotationFile], + Dependency[chisel3.stage.phases.MaybeAspectPhase], + Dependency[chisel3.stage.phases.Emitter], + Dependency[chisel3.stage.phases.Convert] + ) -object Main { - @main - def elaborate( - @arg(name = "dir") dir: String, - ): Unit = { - (new ChiselStage).transform(AnnotationSeq(Seq( - TargetDirAnnotation(dir), - new ChiselGeneratorAnnotation(() => { - new DUT( - new Config((site, here, up) => { - case MonitorsEnabled => false - case freechips.rocketchip.tile.XLen => 64 - case freechips.rocketchip.tile.XLen => 64 - case freechips.rocketchip.tile.MaxHartIdBits => 4 - case freechips.rocketchip.tile.MaxHartIdBits => 4 - case freechips.rocketchip.rocket.PgLevels => if (site(freechips.rocketchip.tile.XLen) == 64) 3 else 2 - case freechips.rocketchip.rocket.PgLevels => if (site(freechips.rocketchip.tile.XLen) == 64) 3 else 2 - case RocketTileParamsKey => RocketTileParams( - core = RocketCoreParams(mulDiv = Some(MulDivParams( - mulUnroll = 8, - mulEarlyOut = true, - divEarlyOut = true))), - dcache = Some(DCacheParams( - rowBits = site(SystemBusKey).beatBits, - nMSHRs = 0, - blockBytes = site(CacheBlockBytes))), - icache = Some(ICacheParams( - rowBits = site(SystemBusKey).beatBits, - blockBytes = site(CacheBlockBytes)))) - case SystemBusKey => SystemBusParams( - beatBytes = site(freechips.rocketchip.tile.XLen) / 8, - blockBytes = site(CacheBlockBytes)) - case DebugModuleKey => None - }) - ) - }), - firrtl.passes.memlib.InferReadWriteAnnotation, - CIRCTTargetAnnotation(CIRCTTarget.Verilog), - EmitAllModulesAnnotation(classOf[ChirrtlEmitter]), - FirtoolOption("--disable-annotation-unknown"), - InjectingAspect( - { dut: DUT => Select.collectDeep(dut){ case icache : ICacheModule => icache } }, - { - icache : ICacheModule => - chisel3.experimental.Trace.traceName(icache.s2_miss) - } - ) - ))) - } - - def main(args: Array[String]): Unit = ParserForMethods(this).runOrExit(args) } + +class RocketChipStage extends Stage with PreservesAll[Phase] { + + override val shell = new Shell("rocket-chip") with RocketChipCli with ChiselCli with FirrtlCli + val targets: Seq[PhaseDependency] = Seq( + Dependency[freechips.rocketchip.stage.phases.Checks], + Dependency[freechips.rocketchip.stage.phases.TransformAnnotations], + Dependency[freechips.rocketchip.stage.phases.PreElaboration], + Dependency[RocketChiselStage], + Dependency[freechips.rocketchip.stage.phases.GenerateFirrtlAnnos], + Dependency[freechips.rocketchip.stage.phases.AddDefaultTests], + Dependency[freechips.rocketchip.stage.phases.GenerateTestSuiteMakefrags], + Dependency[freechips.rocketchip.stage.phases.GenerateArtefacts] + ) + + private val pm = new PhaseManager(targets) + + override def run(annotations: AnnotationSeq): AnnotationSeq = pm.transform(annotations) + +} + +object Generator extends StageMain(new RocketChipStage) + + +object RocketTileParamsKey extends Field[RocketTileParams] + +class cosimConfig extends Config((site, here, up) => { + case MonitorsEnabled => false + case freechips.rocketchip.tile.XLen => 64 + case freechips.rocketchip.tile.XLen => 64 + case freechips.rocketchip.tile.MaxHartIdBits => 4 + case freechips.rocketchip.tile.MaxHartIdBits => 4 + case freechips.rocketchip.rocket.PgLevels => if (site(freechips.rocketchip.tile.XLen) == 64) 3 else 2 + case freechips.rocketchip.rocket.PgLevels => if (site(freechips.rocketchip.tile.XLen) == 64) 3 else 2 + case RocketTileParamsKey => RocketTileParams( + core = RocketCoreParams(mulDiv = Some(MulDivParams( + mulUnroll = 8, + mulEarlyOut = true, + divEarlyOut = true))), + dcache = Some(DCacheParams( + rowBits = site(SystemBusKey).beatBits, + nMSHRs = 0, + blockBytes = site(CacheBlockBytes))), + icache = Some(ICacheParams( + rowBits = site(SystemBusKey).beatBits, + blockBytes = site(CacheBlockBytes)))) + case SystemBusKey => SystemBusParams( + beatBytes = site(freechips.rocketchip.tile.XLen) / 8, + blockBytes = site(CacheBlockBytes)) + case DebugModuleKey => None +}) + From dc2a4d6a88002ef72d302860e8451516214164e7 Mon Sep 17 00:00:00 2001 From: Yanqi Yang Date: Fri, 24 Feb 2023 19:40:22 +0800 Subject: [PATCH 4/5] update myelaborate src --- build.sc | 73 ++++++++++++++++++ tests/cosim/elaborate/src/DUT.scala | 2 +- tests/cosim/elaborate/src/Main.scala | 2 +- tests/cosim/myelaborate/src/DUT.scala | 97 ++++++++++++++++++++++++ tests/cosim/myelaborate/src/Main.scala | 44 +++++++++++ tests/cosim/myelaborate/src/config.scala | 45 +++++++++++ 6 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 tests/cosim/myelaborate/src/DUT.scala create mode 100644 tests/cosim/myelaborate/src/Main.scala create mode 100644 tests/cosim/myelaborate/src/config.scala diff --git a/build.sc b/build.sc index 94a3a7bcf3a..87656c3939d 100644 --- a/build.sc +++ b/build.sc @@ -546,6 +546,79 @@ object tests extends Module{ } } + object myelaborate extends ScalaModule with ScalafmtModule { + def scalaVersion = T { + v.scala + } + + //override def moduleDeps = Seq(diplomatic) + + // override def scalacOptions = T { + // Seq("-Xsource:2.11", s"-Xplugin:${mychisel3.plugin.jar().path}") + // } + + override def ivyDeps = Agg( + v.mainargs + ) + + def elaborate = T { + mill.modules.Jvm.runSubprocess( + finalMainClass(), + runClasspath().map(_.path), + forkArgs(), + forkEnv(), + Seq( + "--dir", T.dest.toString, + "--top", "tests.cosim.elaborate.DUT", + "--config", "tests.cosim.elaborate.cosimConfig" + ), + workingDir = forkWorkingDir(), + ) + PathRef(T.dest) + } + + def chiselAnno = T { + os.walk(elaborate().path).collectFirst { case p if p.last.endsWith("anno.json") => p }.map(PathRef(_)).get + } + + def chirrtl = T { + os.walk(elaborate().path).collectFirst { case p if p.last.endsWith("fir") => p }.map(PathRef(_)).get + } + + def firtool = T { + os.proc("firtool", + chirrtl().path, + s"--annotation-file=${chiselAnno().path}", + "-disable-infer-rw", + "--disable-annotation-unknown", + "-dedup", + "-O=debug", + "--split-verilog", + "--preserve-values=named", + "--output-annotation-file=mfc.anno.json", + s"-o=${T.dest}" + ).call(T.dest) + PathRef(T.dest) + } + + def rtls = T { + val verilogs = os.read(firtool().path / "filelist.f").split("\n").map(str => + try { + os.Path(str) + } catch { + case e: IllegalArgumentException if e.getMessage.contains("is not an absolute path") => + firtool().path / str.stripPrefix("./") + } + ).filter(p => p.ext == "v" || p.ext == "sv").map(PathRef(_)).toSeq + T.log.info(s"RTL generated:\n${verilogs.map(_.path).mkString("\n")}") + verilogs + } + + def annotations = T { + os.walk(firtool().path).filter(p => p.last.endsWith("mfc.anno.json")).map(PathRef(_)) + } + } + /** build emulator */ object emulator extends Module { diff --git a/tests/cosim/elaborate/src/DUT.scala b/tests/cosim/elaborate/src/DUT.scala index 3a99d330602..68859789f38 100644 --- a/tests/cosim/elaborate/src/DUT.scala +++ b/tests/cosim/elaborate/src/DUT.scala @@ -1,4 +1,4 @@ -package cosim.elabotate +package tests.cosim.elabotate import chisel3._ import freechips.rocketchip.diplomacy.{AddressSet, BundleBridgeSource, InModuleBody, LazyModule, RegionType, SimpleLazyModule, TransferSizes} diff --git a/tests/cosim/elaborate/src/Main.scala b/tests/cosim/elaborate/src/Main.scala index 0042e34bff0..c9e32e60b64 100644 --- a/tests/cosim/elaborate/src/Main.scala +++ b/tests/cosim/elaborate/src/Main.scala @@ -1,4 +1,4 @@ -package cosim.elabotate +package tests.cosim.elabotate import chisel3.aop.Select diff --git a/tests/cosim/myelaborate/src/DUT.scala b/tests/cosim/myelaborate/src/DUT.scala new file mode 100644 index 00000000000..e8082b155ae --- /dev/null +++ b/tests/cosim/myelaborate/src/DUT.scala @@ -0,0 +1,97 @@ +package tests.cosim.myelaborate + +import chisel3._ +import freechips.rocketchip.diplomacy.{AddressSet, BundleBridgeSource, InModuleBody, LazyModule, RegionType, SimpleLazyModule, TransferSizes} +import freechips.rocketchip.interrupts.{IntSinkNode, IntSinkPortSimple, IntSourceNode, IntSourcePortSimple} +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.tilelink.{TLManagerNode, TLSlaveParameters, TLSlavePortParameters} +import org.chipsalliance.cde.config.Parameters +import freechips.rocketchip.tile.{NMI, PriorityMuxHartIdFromSeq, RocketTile} + +import org.chipsalliance.cde.config.{Config, Field} + +class DUT(p: Parameters) extends Module { + implicit val implicitP = p + val tileParams = p(RocketTileParamsKey) + val ldut = LazyModule(new SimpleLazyModule { + implicit val implicitP = p + val rocketTile = LazyModule(new RocketTile(tileParams, RocketCrossingParams(), PriorityMuxHartIdFromSeq(Seq(tileParams)))) + val masterNode = TLManagerNode(Seq(TLSlavePortParameters.v1( + Seq(TLSlaveParameters.v1( + address = List(AddressSet(0x0, 0xffffffffL)), + regionType = RegionType.UNCACHED, + executable = true, + supportsGet = TransferSizes(1, 64), + supportsAcquireT = TransferSizes(1, 64), + supportsAcquireB = TransferSizes(1, 64), + supportsPutPartial = TransferSizes(1, 64), + supportsPutFull = TransferSizes(1, 64), + supportsLogical = TransferSizes(1, 64), + supportsArithmetic = TransferSizes(1, 64), + fifoId = Some(0))), + beatBytes = 8, + endSinkId = 4, + minLatency = 1 + ))) + masterNode :=* rocketTile.masterNode + val memory = InModuleBody { + masterNode.makeIOs() + } + + val intNode = IntSourceNode(IntSourcePortSimple()) + rocketTile.intInwardNode :=* intNode + val intIn = InModuleBody { + intNode.makeIOs() + } + + val haltNode = IntSinkNode(IntSinkPortSimple()) + haltNode :=* rocketTile.haltNode + val haltOut = InModuleBody { + haltNode.makeIOs() + } + + val ceaseNode = IntSinkNode(IntSinkPortSimple()) + ceaseNode :=* rocketTile.ceaseNode + val ceaseOut = InModuleBody { + ceaseNode.makeIOs() + } + + val wfiNode = IntSinkNode(IntSinkPortSimple()) + wfiNode :=* rocketTile.wfiNode + val wfiOut = InModuleBody { + wfiNode.makeIOs() + } + val resetVectorNode = BundleBridgeSource(() => UInt(32.W)) + rocketTile.resetVectorNode := resetVectorNode + val resetVector = InModuleBody { + resetVectorNode.makeIO() + } + val hartidNode = BundleBridgeSource(() => UInt(4.W)) + rocketTile.hartIdNode := hartidNode + InModuleBody { + hartidNode.bundle := 0.U + } + val nmiNode = BundleBridgeSource(Some(() => new NMI(32))) + rocketTile.nmiNode := nmiNode + val nmi = InModuleBody { + nmiNode.makeIO() + } + }) + chisel3.experimental.DataMirror.fullModulePorts( + // instantiate the LazyModule + Module(ldut.module) + ).filterNot(_._2.isInstanceOf[Aggregate]).foreach { case (name, ele) => + if (!(name == "clock" || name == "reset")) { + chisel3.experimental.DataMirror.directionOf(ele) match { + case ActualDirection.Output => + val io = IO(Output(chiselTypeOf(ele))).suggestName(name) + println(s"output $name") + io := ele + case ActualDirection.Input => + val io = IO(Input(chiselTypeOf(ele))).suggestName(name) + println(s"input $name") + ele := io + } + } + } +} diff --git a/tests/cosim/myelaborate/src/Main.scala b/tests/cosim/myelaborate/src/Main.scala new file mode 100644 index 00000000000..79b0b078a61 --- /dev/null +++ b/tests/cosim/myelaborate/src/Main.scala @@ -0,0 +1,44 @@ +package tests.cosim.myelaborate + +import mainargs._ +import chisel3._ +import chisel3.aop.Select +import chisel3.aop.injecting.InjectingAspect +import chisel3.experimental.{ChiselAnnotation, annotate} +import chisel3.stage.ChiselGeneratorAnnotation +import firrtl.annotations.Annotation +import firrtl.options.TargetDirAnnotation +import firrtl.stage.{FirrtlStage, OutputFileAnnotation, RunFirrtlTransformAnnotation} +import firrtl.transforms.TopWiring.TopWiringTransform +import firrtl.{AnnotationSeq, VerilogEmitter} +import freechips.rocketchip.devices.debug.DebugModuleKey +import freechips.rocketchip.diplomacy.MonitorsEnabled +import freechips.rocketchip.rocket.{DCacheParams, ICacheParams, MulDivParams, RocketCoreParams} +import os.Path +import freechips.rocketchip.stage._ +import freechips.rocketchip.subsystem.{CacheBlockBytes, SystemBusKey, SystemBusParams} +import freechips.rocketchip.system.RocketChipStage +import freechips.rocketchip.tile.RocketTileParams +import org.chipsalliance.cde.config.{Config, Field} + +object Main { + @main def elaborate( + @arg(name="dir") dir: String, + @arg(name="top") top: String, + @arg(name="config") config: String + ) = { + val annotations = Seq( + new RocketChipStage, + ).foldLeft( + Seq( + TargetDirAnnotation(dir), + new TopModuleAnnotation(Class.forName(top)), + new ConfigsAnnotation(Seq(config)), + ): AnnotationSeq + ) { case (annos, stage) => stage.transform(annos) } + } + def main(args: Array[String]): Unit = ParserForMethods(this).runOrExit(args) +} + + + diff --git a/tests/cosim/myelaborate/src/config.scala b/tests/cosim/myelaborate/src/config.scala new file mode 100644 index 00000000000..f4875a4b9e8 --- /dev/null +++ b/tests/cosim/myelaborate/src/config.scala @@ -0,0 +1,45 @@ +package tests.cosim.myelaborate + +import chisel3.aop.Select +import chisel3.aop.injecting.InjectingAspect +import chisel3.stage.ChiselGeneratorAnnotation +import circt.stage.{CIRCTTarget, CIRCTTargetAnnotation, ChiselStage, FirtoolOption} +import firrtl.options.TargetDirAnnotation +import firrtl.{AnnotationSeq, ChirrtlEmitter, EmitAllModulesAnnotation} +import freechips.rocketchip.devices.debug.DebugModuleKey +import freechips.rocketchip.diplomacy.MonitorsEnabled +import freechips.rocketchip.subsystem.{CacheBlockBytes, SystemBusKey, SystemBusParams} +import mainargs._ +import org.chipsalliance.cde.config.{Config, Field} +import freechips.rocketchip.rocket.{DCacheParams, FrontendModule, ICacheModule, ICacheParams, MulDivParams, Rocket, RocketCoreParams} +import freechips.rocketchip.tile.RocketTileParams + + + +object RocketTileParamsKey extends Field[RocketTileParams] + +class cosimConfig extends Config((site, here, up) => { + case MonitorsEnabled => false + case freechips.rocketchip.tile.XLen => 64 + case freechips.rocketchip.tile.XLen => 64 + case freechips.rocketchip.tile.MaxHartIdBits => 4 + case freechips.rocketchip.tile.MaxHartIdBits => 4 + case freechips.rocketchip.rocket.PgLevels => if (site(freechips.rocketchip.tile.XLen) == 64) 3 else 2 + case freechips.rocketchip.rocket.PgLevels => if (site(freechips.rocketchip.tile.XLen) == 64) 3 else 2 + case RocketTileParamsKey => RocketTileParams( + core = RocketCoreParams(mulDiv = Some(MulDivParams( + mulUnroll = 8, + mulEarlyOut = true, + divEarlyOut = true))), + dcache = Some(DCacheParams( + rowBits = site(SystemBusKey).beatBits, + nMSHRs = 0, + blockBytes = site(CacheBlockBytes))), + icache = Some(ICacheParams( + rowBits = site(SystemBusKey).beatBits, + blockBytes = site(CacheBlockBytes)))) + case SystemBusKey => SystemBusParams( + beatBytes = site(freechips.rocketchip.tile.XLen) / 8, + blockBytes = site(CacheBlockBytes)) + case DebugModuleKey => None +}) From 6dc7f5fee84484c737e32f5dd3377403f52d034c Mon Sep 17 00:00:00 2001 From: Yanqi Yang Date: Fri, 24 Feb 2023 20:11:20 +0800 Subject: [PATCH 5/5] restore rocket tile rtl --- src/main/scala/tile/Interrupts.scala | 27 +++++++++++++++-- src/main/scala/tile/RocketTile.scala | 45 ++++++++++++++++++---------- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/main/scala/tile/Interrupts.scala b/src/main/scala/tile/Interrupts.scala index 6b2012baad9..92d8b3cedce 100644 --- a/src/main/scala/tile/Interrupts.scala +++ b/src/main/scala/tile/Interrupts.scala @@ -1,10 +1,10 @@ // See LICENSE.SiFive for license details. -package tile +package freechips.rocketchip.tile import Chisel._ -import freechips.rocketchip.config.Parameters +import org.chipsalliance.cde.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.interrupts._ import freechips.rocketchip.util._ @@ -32,6 +32,29 @@ trait SinksExternalInterrupts { this: BaseTile => protected val intSinkNode = IntSinkNode(IntSinkPortSimple()) intSinkNode := intXbar.intnode + def cpuDevice: Device + val intcDevice = new DeviceSnippet { + override def parent = Some(cpuDevice) + def describe(): Description = { + Description("interrupt-controller", Map( + "compatible" -> "riscv,cpu-intc".asProperty, + "interrupt-controller" -> Nil, + "#interrupt-cells" -> 1.asProperty)) + } + } + + ResourceBinding { + intSinkNode.edges.in.flatMap(_.source.sources).map { case s => + for (i <- s.range.start until s.range.end) { + csrIntMap.lift(i).foreach { j => + s.resources.foreach { r => + r.bind(intcDevice, ResourceInt(j)) + } + } + } + } + } + // TODO: the order of the following two functions must match, and // also match the order which things are connected to the // per-tile crossbar in subsystem.HasTiles.connectInterrupts diff --git a/src/main/scala/tile/RocketTile.scala b/src/main/scala/tile/RocketTile.scala index 2691fb21031..b308c0c767f 100644 --- a/src/main/scala/tile/RocketTile.scala +++ b/src/main/scala/tile/RocketTile.scala @@ -1,10 +1,10 @@ // See LICENSE.SiFive for license details. // See LICENSE.Berkeley for license details. -package tile +package freechips.rocketchip.tile import Chisel._ -import freechips.rocketchip.config._ +import org.chipsalliance.cde.config._ import freechips.rocketchip.devices.tilelink._ import freechips.rocketchip.diplomacy._ import freechips.rocketchip.interrupts._ @@ -36,11 +36,11 @@ case class RocketTileParams( } class RocketTile private( - val rocketParams: RocketTileParams, - crossing: ClockCrossingType, - lookup: LookupByHartIdImpl, - q: Parameters) - extends BaseTile(rocketParams, crossing, lookup, q) + val rocketParams: RocketTileParams, + crossing: ClockCrossingType, + lookup: LookupByHartIdImpl, + q: Parameters) + extends BaseTile(rocketParams, crossing, lookup, q) with SinksExternalInterrupts with SourcesExternalNotifications with HasLazyRoCC // implies CanHaveSharedFPU with CanHavePTW with HasHellaCache @@ -87,7 +87,20 @@ class RocketTile private( val itimProperty = frontend.icache.itimProperty.toSeq.flatMap(p => Map("sifive,itim" -> p)) val beuProperty = bus_error_unit.map(d => Map( - "sifive,buserror" -> d.device.asProperty)).getOrElse(Nil) + "sifive,buserror" -> d.device.asProperty)).getOrElse(Nil) + + val cpuDevice: SimpleDevice = new SimpleDevice("cpu", Seq("sifive,rocket0", "riscv")) { + override def parent = Some(ResourceAnchors.cpus) + override def describe(resources: ResourceBindings): Description = { + val Description(name, mapping) = super.describe(resources) + Description(name, mapping ++ cpuProperties ++ nextLevelCacheProperty + ++ tileProperties ++ dtimProperty ++ itimProperty ++ beuProperty) + } + } + + ResourceBinding { + Resource(cpuDevice, "reg").bind(ResourceAddress(staticIdForMetadataUseOnly)) + } override lazy val module = new RocketTileModuleImp(this) @@ -107,9 +120,9 @@ class RocketTile private( } class RocketTileModuleImp(outer: RocketTile) extends BaseTileModuleImp(outer) - with HasFpuOpt - with HasLazyRoCCModule - with HasICacheFrontendModule { + with HasFpuOpt + with HasLazyRoCCModule + with HasICacheFrontendModule { Annotated.params(this, outer.rocketParams) val core = Module(new Rocket(outer)(outer.p)) @@ -120,9 +133,9 @@ class RocketTileModuleImp(outer: RocketTile) extends BaseTileModuleImp(outer) // Report when the tile has ceased to retire instructions; for now the only cause is clock gating outer.reportCease(outer.rocketParams.core.clockGate.option( !outer.dcache.module.io.cpu.clock_enabled && - !outer.frontend.module.io.cpu.clock_enabled && - !ptw.io.dpath.clock_enabled && - core.io.cease)) + !outer.frontend.module.io.cpu.clock_enabled && + !ptw.io.dpath.clock_enabled && + core.io.cease)) outer.reportWFI(Some(core.io.wfi)) @@ -169,8 +182,8 @@ class RocketTileModuleImp(outer: RocketTile) extends BaseTileModuleImp(outer) require(h == c, s"port list size was $h, core expected $c") require(h == o, s"port list size was $h, outer counted $o") // TODO figure out how to move the below into their respective mix-ins - dcacheArb.io.requestor <> dcachePorts - ptw.io.requestor <> ptwPorts + dcacheArb.io.requestor <> dcachePorts.toSeq + ptw.io.requestor <> ptwPorts.toSeq } trait HasFpuOpt { this: RocketTileModuleImp =>