diff --git a/.gitignore b/.gitignore index 551a8e97..8450e9b8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ target *.swp .idea .DS_Store +env.sh +riscv-tools-install tags diff --git a/.gitmodules b/.gitmodules index 698c53b9..ca80d661 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,13 +21,22 @@ url = https://github.com/ucb-bar/esp-tools.git [submodule "tools/torture"] path = tools/torture - url = git@github.com:ucb-bar/riscv-torture.git + url = https://github.com/ucb-bar/riscv-torture.git [submodule "generators/boom"] path = generators/boom - url = git@github.com:riscv-boom/riscv-boom.git + url = https://github.com/riscv-boom/riscv-boom.git [submodule "generators/sifive-blocks"] path = generators/sifive-blocks - url = git@github.com:sifive/sifive-blocks.git + url = https://github.com/sifive/sifive-blocks.git [submodule "generators/hwacha"] path = generators/hwacha - url = git@github.com:ucb-bar/hwacha.git + url = https://github.com/ucb-bar/hwacha.git +[submodule "sims/firesim"] + path = sims/firesim + url = https://github.com/firesim/firesim.git +[submodule "generators/icenet"] + path = generators/icenet + url = https://github.com/firesim/icenet.git +[submodule "generators/block-inclusivecache-sifive"] + path = generators/sifive-cache + url = https://github.com/sifive/block-inclusivecache-sifive.git diff --git a/build.sbt b/build.sbt index 09c78b34..cb3a0b69 100644 --- a/build.sbt +++ b/build.sbt @@ -1,3 +1,5 @@ +import Tests._ + lazy val commonSettings = Seq( organization := "edu.berkeley.cs", version := "1.0", @@ -9,7 +11,7 @@ lazy val commonSettings = Seq( case _ => MergeStrategy.first}}, scalacOptions ++= Seq("-deprecation","-unchecked","-Xsource:2.11"), libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % "test", - libraryDependencies += "org.json4s" %% "json4s-native" % "3.6.1", + libraryDependencies += "org.json4s" %% "json4s-jackson" % "3.6.1", libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value, libraryDependencies += "edu.berkeley.cs" %% "firrtl-interpreter" % "1.2-SNAPSHOT", libraryDependencies += "com.github.scopt" %% "scopt" % "3.7.0", @@ -19,18 +21,14 @@ lazy val commonSettings = Seq( Resolver.sonatypeRepo("releases"), Resolver.mavenLocal)) -lazy val chipyardFirrtl = (project in file("tools/firrtl")) - .settings(commonSettings) +val rocketChipDir = file("generators/rocket-chip") -lazy val rocketchip = RootProject(file("generators/rocket-chip")) - -lazy val chipyardrocketchip = project - .dependsOn(rocketchip) - .settings(commonSettings) - -lazy val testchipip = (project in file("generators/testchipip")) - .dependsOn(chipyardrocketchip) - .settings(commonSettings) +lazy val firesimAsLibrary = sys.env.get("FIRESIM_STANDALONE") == None +lazy val firesimDir = if (firesimAsLibrary) { + file("sims/firesim/sim/") +} else { + file("../../") +} // Checks for -DROCKET_USE_MAVEN. // If it's there, use a maven dependency. @@ -45,33 +43,90 @@ def conditionalDependsOn(prj: Project): Project = { } } +// Fork each scala test for now, to work around persistent mutable state +// in Rocket-Chip based generators +def isolateAllTests(tests: Seq[TestDefinition]) = tests map { test => + val options = ForkOptions() + new Group(test.name, Seq(test), SubProcess(options)) + } toSeq + +// Subproject definitions begin + +// NB: FIRRTL dependency is unmanaged (and dropped in sim/lib) +lazy val chisel = (project in rocketChipDir / "chisel3") + + // Contains annotations & firrtl passes you may wish to use in rocket-chip without +// introducing a circular dependency between RC and MIDAS +lazy val midasTargetUtils = ProjectRef(firesimDir, "targetutils") + + // Rocket-chip dependencies (subsumes making RC a RootProject) +lazy val hardfloat = (project in rocketChipDir / "hardfloat") + .settings(commonSettings).dependsOn(midasTargetUtils) + +lazy val rocketMacros = (project in rocketChipDir / "macros") + .settings(commonSettings) + +// HACK: I'm strugging to override settings in rocket-chip's build.sbt (i want +// the subproject to register a new library dependendency on midas's targetutils library) +// So instead, avoid the existing build.sbt altogether and specify the project's root at src/ +lazy val rocketchip = (project in rocketChipDir / "src") + .settings( + commonSettings, + scalaSource in Compile := baseDirectory.value / "main" / "scala", + resourceDirectory in Compile := baseDirectory.value / "main" / "resources") + .dependsOn(chisel, hardfloat, rocketMacros) + +lazy val testchipip = (project in file("generators/testchipip")) + .dependsOn(rocketchip) + .settings(commonSettings) + lazy val example = conditionalDependsOn(project in file("generators/example")) - .dependsOn(boom, hwacha, sifive_blocks) + .dependsOn(boom, hwacha, sifive_blocks, sifive_cache) .settings(commonSettings) lazy val utilities = conditionalDependsOn(project in file("generators/utilities")) .settings(commonSettings) -lazy val hwacha = (project in file ("generators/hwacha")) - .dependsOn(chipyardrocketchip) +lazy val icenet = (project in file("generators/icenet")) + .dependsOn(rocketchip, testchipip) + .settings(commonSettings) + +lazy val hwacha = (project in file("generators/hwacha")) + .dependsOn(rocketchip) .settings(commonSettings) lazy val boom = (project in file("generators/boom")) - .dependsOn(chipyardrocketchip) + .dependsOn(rocketchip) .settings(commonSettings) lazy val tapeout = conditionalDependsOn(project in file("./tools/barstools/tapeout/")) - .dependsOn(chipyardFirrtl) .settings(commonSettings) lazy val mdf = (project in file("./tools/barstools/mdf/scalalib/")) .settings(commonSettings) -lazy val `barstools-macros` = (project in file("./tools/barstools/macros/")) - .dependsOn(mdf, chipyardrocketchip, chipyardFirrtl) +lazy val barstoolsMacros = (project in file("./tools/barstools/macros/")) + .dependsOn(mdf, rocketchip) .enablePlugins(sbtassembly.AssemblyPlugin) .settings(commonSettings) lazy val sifive_blocks = (project in file("generators/sifive-blocks")) - .dependsOn(chipyardrocketchip) + .dependsOn(rocketchip) .settings(commonSettings) + +lazy val sifive_cache = (project in file("generators/sifive-cache")).settings( + commonSettings, + scalaSource in Compile := baseDirectory.value / "craft" + ).dependsOn(rocketchip) + +// Library components of FireSim +lazy val midas = ProjectRef(firesimDir, "midas") +lazy val firesimLib = ProjectRef(firesimDir, "firesimLib") + +lazy val firechip = (project in file("generators/firechip")) + .dependsOn(boom, icenet, testchipip, sifive_blocks, sifive_cache, midasTargetUtils, midas, firesimLib % "test->test;compile->compile") + .settings( + commonSettings, + testGrouping in Test := isolateAllTests( (definedTests in Test).value ) + ) + diff --git a/common.mk b/common.mk index bd61184b..310da07c 100644 --- a/common.mk +++ b/common.mk @@ -8,23 +8,25 @@ SHELL=/bin/bash ######################################################################################### lookup_scala_srcs = $(shell find -L $(1)/ -iname "*.scala" 2> /dev/null) -PACKAGES=rocket-chip testchipip boom hwacha sifive-blocks example -SCALA_SOURCES=$(foreach pkg,$(PACKAGES),$(call lookup_scala_srcs,$(base_dir)/generators/$(pkg)/src/main/scala)) +PACKAGES=$(addprefix generators/, rocket-chip testchipip boom hwacha sifive-blocks sifive-cache example) \ + $(addprefix sims/firesim/sim/, . firesim-lib midas midas/targetutils) +SCALA_SOURCES=$(foreach pkg,$(PACKAGES),$(call lookup_scala_srcs,$(base_dir)/$(pkg)/src/main/scala)) ######################################################################################### # rocket and testchipip classes ######################################################################################### -ROCKET_CLASSES ?= "$(ROCKETCHIP_DIR)/target/scala-$(SCALA_VERSION_MAJOR)/classes:$(ROCKETCHIP_DIR)/chisel3/target/scala-$(SCALA_VERSION_MAJOR)/*" +# NB: target/ lives under source ----V , due to how we're handling midas dependency injection +ROCKET_CLASSES ?= "$(ROCKETCHIP_DIR)/src/target/scala-$(SCALA_VERSION_MAJOR)/classes:$(ROCKETCHIP_DIR)/chisel3/target/scala-$(SCALA_VERSION_MAJOR)/*" TESTCHIPIP_CLASSES ?= "$(TESTCHIP_DIR)/target/scala-$(SCALA_VERSION_MAJOR)/classes" ######################################################################################### # jar creation variables and rules ######################################################################################### -FIRRTL_JAR ?= $(ROCKETCHIP_DIR)/lib/firrtl.jar +FIRRTL_JAR := $(base_dir)/lib/firrtl.jar $(FIRRTL_JAR): $(call lookup_scala_srcs, $(CHIPYARD_FIRRTL_DIR)/src/main/scala) $(MAKE) -C $(CHIPYARD_FIRRTL_DIR) SBT="$(SBT)" root_dir=$(CHIPYARD_FIRRTL_DIR) build-scala - mkdir -p $(dir $@) + mkdir -p $(@D) cp -p $(CHIPYARD_FIRRTL_DIR)/utils/bin/firrtl.jar $@ touch $@ @@ -58,11 +60,11 @@ $(HARNESS_FILE) $(HARNESS_ANNO) $(HARNESS_FIR) $(sim_harness_blackboxes): $(FIRR # This file is for simulation only. VLSI flows should replace this file with one containing hard SRAMs MACROCOMPILER_MODE ?= --mode synflops $(SMEMS_FILE) $(SMEMS_FIR): $(SMEMS_CONF) - cd $(base_dir) && $(SBT) "project barstools-macros" "runMain barstools.macros.MacroCompiler -n $(SMEMS_CONF) -v $(SMEMS_FILE) -f $(SMEMS_FIR) $(MACROCOMPILER_MODE)" + cd $(base_dir) && $(SBT) "project barstoolsMacros" "runMain barstools.macros.MacroCompiler -n $(SMEMS_CONF) -v $(SMEMS_FILE) -f $(SMEMS_FIR) $(MACROCOMPILER_MODE)" HARNESS_MACROCOMPILER_MODE = --mode synflops $(HARNESS_SMEMS_FILE) $(HARNESS_SMEMS_FIR): $(HARNESS_SMEMS_CONF) - cd $(base_dir) && $(SBT) "project barstools-macros" "runMain barstools.macros.MacroCompiler -n $(HARNESS_SMEMS_CONF) -v $(HARNESS_SMEMS_FILE) -f $(HARNESS_SMEMS_FIR) $(HARNESS_MACROCOMPILER_MODE)" + cd $(base_dir) && $(SBT) "project barstoolsMacros" "runMain barstools.macros.MacroCompiler -n $(HARNESS_SMEMS_CONF) -v $(HARNESS_SMEMS_FILE) -f $(HARNESS_SMEMS_FIR) $(HARNESS_MACROCOMPILER_MODE)" ######################################################################################### # helper rule to just make verilog files diff --git a/docs/Advanced-Usage/Heterogeneous-SoCs.rst b/docs/Advanced-Usage/Heterogeneous-SoCs.rst new file mode 100644 index 00000000..96a7e40e --- /dev/null +++ b/docs/Advanced-Usage/Heterogeneous-SoCs.rst @@ -0,0 +1,126 @@ +Heterogeneous SoCs +=============================== + +The Chipyard framework involves multiple cores and accelerators that can be composed in arbitrary ways. +This discussion will focus on how you combine Rocket, BOOM and Hwacha in particular ways to create a unique SoC. + +Creating a Rocket and BOOM System +------------------------------------------- + +Instantiating an SoC with Rocket and BOOM cores is all done with the configuration system and two specific mixins. +Both BOOM and Rocket have mixins labelled ``WithNBoomCores(X)`` and ``WithNBigCores(X)`` that automatically create ``X`` copies of the core. +When used together you can create a heterogeneous system. +The following example shows a dual core BOOM with a single core Rocket. + +.. code-block:: scala + + class DualBoomAndOneRocketConfig extends Config( + new WithNormalBoomRocketTop ++ + new WithBootROM ++ + new boom.system.WithRenumberHarts ++ + new boom.common.WithRVC ++ + new boom.common.DefaultBoomConfig ++ + new boom.system.WithNBoomCores(2) ++ + new freechips.rocketchip.subsystem.WithoutTLMonitors ++ + new freechips.rocketchip.subsystem.WithNBigCores(1) ++ + new freechips.rocketchip.system.BaseConfig) + +In this example, the ``WithNBoomCores`` and ``WithNBigCores`` mixins set up the default parameters for the multiple BOOM and Rocket cores, respectively. +However, for BOOM, an extra mixin called ``DefaultBoomConfig`` is added to override the default parameters with a different set of more common default parameters. +This mixin applies to all BOOM cores in the system and changes the parameters for each. + +Great! Now you have a heterogeneous setup with BOOMs and Rockets. +The final thing you need to make this system work is to renumber the ``hartId``'s of the cores so that each core has a unique ``hartId`` (a ``hartId`` is the hardware thread id of the core). +This is done with ``WithRenumberHarts`` (which can label the Rocket cores first or the BOOM cores first). +The reason this is needed is because by default the ``WithN...Cores(X)`` mixin assumes that there are only BOOM or only Rocket cores in the system. +Thus, without the ``WithRenumberHarts`` mixin, each set of cores is labeled starting from zero causing multiple cores to be assigned the same ``hartId``. + +Another alternative option to create a multi heterogeneous core system is to override the parameters yourself so you can specify the core parameters per core. +The mixin to add to your system would look something like the following. + +.. code-block:: scala + + // create 6 cores (4 boom and 2 rocket) + class WithHeterCoresSetup extends Config((site, here, up) => { + case BoomTilesKey => { + val boomTile0 = BoomTileParams(...) // params for boom core 0 + val boomTile1 = BoomTileParams(...) // params for boom core 1 + val boomTile2 = BoomTileParams(...) // params for boom core 2 + val boomTile3 = BoomTileParams(...) // params for boom core 3 + boomTile0 ++ boomTile1 ++ boomTile2 ++ boomTile3 + } + + case RocketTilesKey => { + val rocketTile0 = RocketTileParams(...) // params for rocket core 0 + val rocketTile1 = RocketTileParams(...) // params for rocket core 1 + rocketTile0 ++ rocketTile1 + } + }) + +Then you could use this new mixin like the following. + +.. code-block:: scala + + class SixCoreConfig extends Config( + new WithNormalBoomRocketTop ++ + new WithBootROM ++ + new WithHeterCoresSetup ++ + new freechips.rocketchip.system.BaseConfig) + +Note, in this setup you need to specify the ``hartId`` of each core in the "TileParams", where each ``hartId`` is unique. + +Adding Hwachas +------------------------------------------- + +Adding a Hwacha accelerator is as easy as adding the ``DefaultHwachaConfig`` so that it can setup the Hwacha parameters and add itself to the ``BuildRoCC`` parameter. +An example of adding a Hwacha to all tiles in the system is below. + +.. code-block:: scala + + class DualBoomAndRocketWithHwachasConfig extends Config( + new WithNormalBoomRocketTop ++ + new WithBootROM ++ + new hwacha.DefaultHwachaConfig ++ + new boom.system.WithRenumberHarts ++ + new boom.common.WithRVC ++ + new boom.common.DefaultBoomConfig ++ + new boom.system.WithNBoomCores(2) ++ + new freechips.rocketchip.subsystem.WithoutTLMonitors ++ + new freechips.rocketchip.subsystem.WithNBigCores(1) ++ + new freechips.rocketchip.system.BaseConfig) + +In this example, Hwachas are added to both BOOM tiles and to the Rocket tile. +All with the same Hwacha parameters. + +Assigning Accelerators to Specific Tiles with MultiRoCC +------------------------------------------------------- + +Located in ``generators/example/src/main/scala/ConfigMixins.scala`` is a mixin that provides support for adding RoCC accelerators to specific tiles in your SoC. +Named ``MultiRoCCKey``, this key allows you to attach RoCC accelerators based on the ``hartId`` of the tile. +For example, using this allows you to create a 8 tile system with a RoCC accelerator on only a subset of the tiles. +An example is shown below with two BOOM cores, and one Rocket tile with a RoCC accelerator (Hwacha) attached. + +.. code-block:: scala + + class DualBoomAndOneHwachaRocketConfig extends Config( + new WithNormalBoomRocketTop ++ + new WithBootROM ++ + new WithMultiRoCC ++ + new WithMultiRoCCHwacha(0) ++ // put Hwacha just on hart0 which was renumbered to Rocket + new boom.system.WithRenumberHarts(rocketFirst = true) ++ + new hwacha.DefaultHwachaConfig ++ + new boom.common.WithRVC ++ + new boom.common.DefaultBoomConfig ++ + new boom.system.WithNBoomCores(2) ++ + new freechips.rocketchip.subsystem.WithoutTLMonitors ++ + new freechips.rocketchip.subsystem.WithNBigCores(1) ++ + new freechips.rocketchip.system.BaseConfig) + +In this example, the ``WithRenumberHarts`` relabels the ``hartId``'s of all the BOOM/Rocket cores. +Then after that is applied to the parameters, the ``WithMultiRoCCHwacha(0)`` is used to assign to ``hartId`` zero a Hwacha (in this case ``hartId`` zero is Rocket). +Finally, the ``WithMultiRoCC`` mixin is called. +This mixin sets the ``BuildRoCC`` key to use the ``MultiRoCCKey`` instead of the default. +This must be used after all the RoCC parameters are set because it needs to override the ``BuildRoCC`` parameter. +If this is used earlier in the configuration sequence, then MultiRoCC does not work. + +This mixin can be changed to put more accelerators on more cores by changing the arguments to cover more ``hartId``'s (i.e. ``WithMultiRoCCHwacha(0,1,3,6,...)``). diff --git a/docs/Advanced-Usage/index.rst b/docs/Advanced-Usage/index.rst new file mode 100644 index 00000000..62dd11aa --- /dev/null +++ b/docs/Advanced-Usage/index.rst @@ -0,0 +1,11 @@ +Advanced Usage +================================ + +The following sections are advanced topics about how to use Chipyard and special features of the framework. +They expect you to know about Chisel, Parameters, Configs, etc. + +.. toctree:: + :maxdepth: 2 + :caption: Getting Started: + + Heterogeneous-SoCs diff --git a/docs/index.rst b/docs/index.rst index ce083f2c..019a6991 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -37,6 +37,10 @@ New to Chipyard? Jump to the :ref:`Getting Started` page for more info. :numbered: VLSI/index + :maxdepth: 3 + :caption: Advanced Usage: + :numbered: + Advanced-Usage/index Indices and tables ================== diff --git a/generators/boom b/generators/boom index 22c5f9ec..80a68074 160000 --- a/generators/boom +++ b/generators/boom @@ -1 +1 @@ -Subproject commit 22c5f9ec3f0325b9bdbf1a98b5752b6ac843eb19 +Subproject commit 80a680745c27dfe54bdd217e8de9c24c1c96b553 diff --git a/generators/example/src/main/scala/Configs.scala b/generators/example/src/main/scala/Configs.scala index d6ff3432..c2334ca8 100644 --- a/generators/example/src/main/scala/Configs.scala +++ b/generators/example/src/main/scala/Configs.scala @@ -3,7 +3,7 @@ package example import chisel3._ import freechips.rocketchip.config.{Config} -import freechips.rocketchip.subsystem.{WithRoccExample, WithNMemoryChannels, WithNBigCores, WithRV32, WithExtMemSize, WithNBanks} +import freechips.rocketchip.subsystem.{WithRoccExample, WithNMemoryChannels, WithNBigCores, WithRV32, WithExtMemSize, WithNBanks, WithInclusiveCache} import testchipip._ @@ -62,13 +62,16 @@ class GB1MemoryConfig extends Config( new WithExtMemSize((1<<30) * 1L) ++ new DefaultRocketConfig) +class RocketL2Config extends Config( + new WithInclusiveCache ++ new DefaultRocketConfig) + // ------------ // BOOM Configs // ------------ class BaseBoomConfig extends Config( new WithBootROM ++ - new boom.system.BoomConfig) + new boom.system.LargeBoomConfig) class SmallBaseBoomConfig extends Config( new WithBootROM ++ @@ -121,7 +124,8 @@ class DualCoreBoomConfig extends Config( new WithNormalBoomRocketTop ++ new WithBootROM ++ new boom.common.WithRVC ++ - new boom.common.DefaultBoomConfig ++ + new boom.common.WithLargeBooms ++ + new boom.common.BaseBoomConfig ++ new boom.system.WithNBoomCores(2) ++ new freechips.rocketchip.subsystem.WithoutTLMonitors ++ new freechips.rocketchip.system.BaseConfig) @@ -131,7 +135,7 @@ class DualCoreSmallBoomConfig extends Config( new WithBootROM ++ new boom.common.WithRVC ++ new boom.common.WithSmallBooms ++ - new boom.common.DefaultBoomConfig ++ + new boom.common.BaseBoomConfig ++ new boom.system.WithNBoomCores(2) ++ new freechips.rocketchip.subsystem.WithoutTLMonitors ++ new freechips.rocketchip.system.BaseConfig) @@ -141,6 +145,9 @@ class RV32UnifiedBoomConfig extends Config( new WithBootROM ++ new boom.system.SmallRV32UnifiedBoomConfig) +class BoomL2Config extends Config( + new WithInclusiveCache ++ new SmallDefaultBoomConfig) + // --------------------- // BOOM and Rocket Configs // --------------------- @@ -149,7 +156,8 @@ class BaseBoomAndRocketConfig extends Config( new WithBootROM ++ new boom.system.WithRenumberHarts ++ new boom.common.WithRVC ++ - new boom.common.DefaultBoomConfig ++ + new boom.common.WithLargeBooms ++ + new boom.common.BaseBoomConfig ++ new boom.system.WithNBoomCores(1) ++ new freechips.rocketchip.subsystem.WithoutTLMonitors ++ new freechips.rocketchip.subsystem.WithNBigCores(1) ++ @@ -160,7 +168,7 @@ class SmallBaseBoomAndRocketConfig extends Config( new boom.system.WithRenumberHarts ++ new boom.common.WithRVC ++ new boom.common.WithSmallBooms ++ - new boom.common.DefaultBoomConfig ++ + new boom.common.BaseBoomConfig ++ new boom.system.WithNBoomCores(1) ++ new freechips.rocketchip.subsystem.WithoutTLMonitors ++ new freechips.rocketchip.subsystem.WithNBigCores(1) ++ @@ -210,21 +218,23 @@ class DualCoreBoomAndOneRocketConfig extends Config( new WithBootROM ++ new boom.system.WithRenumberHarts ++ new boom.common.WithRVC ++ - new boom.common.DefaultBoomConfig ++ + new boom.common.WithLargeBooms ++ + new boom.common.BaseBoomConfig ++ new boom.system.WithNBoomCores(2) ++ new freechips.rocketchip.subsystem.WithoutTLMonitors ++ new freechips.rocketchip.subsystem.WithNBigCores(1) ++ new freechips.rocketchip.system.BaseConfig) -class DualCoreBoomAndOneHwachaRocketConfig extends Config( +class DualBoomAndOneHwachaRocketConfig extends Config( new WithNormalBoomRocketTop ++ new WithBootROM ++ new WithMultiRoCC ++ new WithMultiRoCCHwacha(0) ++ // put Hwacha just on hart0 which was renumbered to Rocket - new boom.system.WithRenumberHarts ++ + new boom.system.WithRenumberHarts(rocketFirst = true) ++ new hwacha.DefaultHwachaConfig ++ new boom.common.WithRVC ++ - new boom.common.DefaultBoomConfig ++ + new boom.common.WithLargeBooms ++ + new boom.common.BaseBoomConfig ++ new boom.system.WithNBoomCores(2) ++ new freechips.rocketchip.subsystem.WithoutTLMonitors ++ new freechips.rocketchip.subsystem.WithNBigCores(1) ++ @@ -236,9 +246,13 @@ class RV32BoomAndRocketConfig extends Config( new boom.system.WithRenumberHarts ++ new boom.common.WithBoomRV32 ++ new boom.common.WithRVC ++ - new boom.common.DefaultBoomConfig ++ + new boom.common.WithLargeBooms ++ + new boom.common.BaseBoomConfig ++ new boom.system.WithNBoomCores(1) ++ new freechips.rocketchip.subsystem.WithoutTLMonitors ++ - new WithRV32 ++ + new freechips.rocketchip.subsystem.WithRV32 ++ new freechips.rocketchip.subsystem.WithNBigCores(1) ++ new freechips.rocketchip.system.BaseConfig) + +class DualCoreRocketL2Config extends Config( + new WithInclusiveCache ++ new DualCoreRocketConfig) diff --git a/generators/example/src/main/scala/Generator.scala b/generators/example/src/main/scala/Generator.scala index 684b2834..92658038 100644 --- a/generators/example/src/main/scala/Generator.scala +++ b/generators/example/src/main/scala/Generator.scala @@ -1,140 +1,24 @@ package example -import scala.collection.mutable.LinkedHashSet - import chisel3._ -import chisel3.experimental._ -import firrtl.transforms.{BlackBoxResourceAnno, BlackBoxSourceHelper} - -import freechips.rocketchip.subsystem.{RocketTilesKey} -import freechips.rocketchip.diplomacy.{LazyModule} -import freechips.rocketchip.config.{Field, Parameters} +import freechips.rocketchip.config.{Parameters} import freechips.rocketchip.util.{GeneratorApp} -import freechips.rocketchip.tile.{XLen} -import freechips.rocketchip.system.{TestGeneration, RegressionTestSuite} -import boom.system.{BoomTilesKey, BoomTestSuites} +import boom.system.{BoomTilesKey, TestSuiteHelper} object Generator extends GeneratorApp { - val rv64RegrTestNames = LinkedHashSet( - "rv64ud-v-fcvt", - "rv64ud-p-fdiv", - "rv64ud-v-fadd", - "rv64uf-v-fadd", - "rv64um-v-mul", - "rv64mi-p-breakpoint", - "rv64uc-v-rvc", - "rv64ud-v-structural", - "rv64si-p-wfi", - "rv64um-v-divw", - "rv64ua-v-lrsc", - "rv64ui-v-fence_i", - "rv64ud-v-fcvt_w", - "rv64uf-v-fmin", - "rv64ui-v-sb", - "rv64ua-v-amomax_d", - "rv64ud-v-move", - "rv64ud-v-fclass", - "rv64ua-v-amoand_d", - "rv64ua-v-amoxor_d", - "rv64si-p-sbreak", - "rv64ud-v-fmadd", - "rv64uf-v-ldst", - "rv64um-v-mulh", - "rv64si-p-dirty") - - val rv32RegrTestNames = LinkedHashSet( - "rv32mi-p-ma_addr", - "rv32mi-p-csr", - "rv32ui-p-sh", - "rv32ui-p-lh", - "rv32uc-p-rvc", - "rv32mi-p-sbreak", - "rv32ui-p-sll") - + // add unique test suites override def addTestSuites { - import freechips.rocketchip.system.DefaultTestSuites._ - val xlen = params(XLen) - - // TODO: support heterogenous systems? - // TODO: generate tests for hart0 of the system since asm tests run on hart0 by default - - // rocket specific tests - params(RocketTilesKey).find(_.hartId == 0).map { tileParams => - val coreParams = tileParams.core - val vm = coreParams.useVM - val env = if (vm) List("p","v") else List("p") - coreParams.fpu foreach { case cfg => - if (xlen == 32) { - TestGeneration.addSuites(env.map(rv32uf)) - if (cfg.fLen >= 64) - TestGeneration.addSuites(env.map(rv32ud)) - } else { - TestGeneration.addSuite(rv32udBenchmarks) - TestGeneration.addSuites(env.map(rv64uf)) - if (cfg.fLen >= 64) - TestGeneration.addSuites(env.map(rv64ud)) - } - } - if (coreParams.useAtomics) { - if (tileParams.dcache.flatMap(_.scratch).isEmpty) - TestGeneration.addSuites(env.map(if (xlen == 64) rv64ua else rv32ua)) - else - TestGeneration.addSuites(env.map(if (xlen == 64) rv64uaSansLRSC else rv32uaSansLRSC)) - } - if (coreParams.useCompressed) TestGeneration.addSuites(env.map(if (xlen == 64) rv64uc else rv32uc)) - val (rvi, rvu) = - if (xlen == 64) ((if (vm) rv64i else rv64pi), rv64u) - else ((if (vm) rv32i else rv32pi), rv32u) - - TestGeneration.addSuites(rvi.map(_("p"))) - TestGeneration.addSuites((if (vm) List("v") else List()).flatMap(env => rvu.map(_(env)))) - TestGeneration.addSuite(benchmarks) - TestGeneration.addSuite(new RegressionTestSuite(if (xlen == 64) rv64RegrTestNames else rv32RegrTestNames)) - } - - // boom specific tests - params(BoomTilesKey).find(_.hartId == 0).map { tileParams => - val coreParams = tileParams.core - val vm = coreParams.useVM - val env = if (vm) List("p","v") else List("p") - coreParams.fpu foreach { case cfg => - if (xlen == 32) { - TestGeneration.addSuites(env.map(rv32uf)) - if (cfg.fLen >= 64) { - TestGeneration.addSuites(env.map(rv32ud)) - } - } else if (cfg.fLen >= 64) { - TestGeneration.addSuites(env.map(rv64ud)) - TestGeneration.addSuites(env.map(rv64uf)) - TestGeneration.addSuite(rv32udBenchmarks) - } - } - if (coreParams.useAtomics) { - if (tileParams.dcache.flatMap(_.scratch).isEmpty) { - TestGeneration.addSuites(env.map(if (xlen == 64) rv64ua else rv32ua)) - } else { - TestGeneration.addSuites(env.map(if (xlen == 64) rv64uaSansLRSC else rv32uaSansLRSC)) - } - } - if (coreParams.useCompressed) TestGeneration.addSuites(env.map(if (xlen == 64) rv64uc else rv32uc)) - - // Include our BOOM-specific overrides. - val (rvi, rvu) = - if (xlen == 64) ((if (vm) BoomTestSuites.rv64i else BoomTestSuites.rv64pi), rv64u) - else ((if (vm) rv32i else rv32pi), rv32u) - - TestGeneration.addSuites(rvi.map(_("p"))) - TestGeneration.addSuites(rvu.map(_("p"))) - TestGeneration.addSuites((if (vm) List("v") else List()).flatMap(env => rvu.map(_(env)))) - TestGeneration.addSuite(benchmarks) - rv64RegrTestNames -= "rv64mi-p-breakpoint" // TODO: breakpoints not implemented yet - TestGeneration.addSuite(new RegressionTestSuite(if (xlen == 64) rv64RegrTestNames else rv32RegrTestNames)) - } + implicit val p: Parameters = params + TestSuiteHelper.addRocketTestSuites + TestSuiteHelper.addBoomTestSuites } + // specify the name that the generator outputs files as val longName = names.topModuleProject + "." + names.topModuleClass + "." + names.configs + + // generate files generateFirrtl generateAnno generateTestSuiteMakefrags diff --git a/generators/firechip/src/main/scala/Generator.scala b/generators/firechip/src/main/scala/Generator.scala new file mode 100644 index 00000000..06febd3a --- /dev/null +++ b/generators/firechip/src/main/scala/Generator.scala @@ -0,0 +1,133 @@ +//See LICENSE for license details. + +package firesim.firesim + +import java.io.{File} + +import chisel3.experimental.RawModule +import chisel3.internal.firrtl.{Circuit, Port} + +import freechips.rocketchip.diplomacy.{ValName, AutoBundle} +import freechips.rocketchip.devices.debug.DebugIO +import freechips.rocketchip.util.{HasGeneratorUtilities, ParsedInputNames, ElaborationArtefacts} +import freechips.rocketchip.system.DefaultTestSuites._ +import freechips.rocketchip.system.{TestGeneration, RegressionTestSuite} +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.subsystem.RocketTilesKey +import freechips.rocketchip.tile.XLen + +import boom.system.{BoomTilesKey, BoomTestSuites} + +import firesim.util.{GeneratorArgs, HasTargetAgnosticUtilites, HasFireSimGeneratorUtilities} + +trait HasTestSuites { + val rv64RegrTestNames = collection.mutable.LinkedHashSet( + "rv64ud-v-fcvt", + "rv64ud-p-fdiv", + "rv64ud-v-fadd", + "rv64uf-v-fadd", + "rv64um-v-mul", + // "rv64mi-p-breakpoint", // Not implemented in BOOM + // "rv64uc-v-rvc", // Not implemented in BOOM + "rv64ud-v-structural", + "rv64si-p-wfi", + "rv64um-v-divw", + "rv64ua-v-lrsc", + "rv64ui-v-fence_i", + "rv64ud-v-fcvt_w", + "rv64uf-v-fmin", + "rv64ui-v-sb", + "rv64ua-v-amomax_d", + "rv64ud-v-move", + "rv64ud-v-fclass", + "rv64ua-v-amoand_d", + "rv64ua-v-amoxor_d", + "rv64si-p-sbreak", + "rv64ud-v-fmadd", + "rv64uf-v-ldst", + "rv64um-v-mulh", + "rv64si-p-dirty") + + val rv32RegrTestNames = collection.mutable.LinkedHashSet( + "rv32mi-p-ma_addr", + "rv32mi-p-csr", + "rv32ui-p-sh", + "rv32ui-p-lh", + "rv32uc-p-rvc", + "rv32mi-p-sbreak", + "rv32ui-p-sll") + + def addTestSuites(targetName: String, params: Parameters) { + val coreParams = + if (params(RocketTilesKey).nonEmpty) { + params(RocketTilesKey).head.core + } else { + params(BoomTilesKey).head.core + } + val xlen = params(XLen) + val vm = coreParams.useVM + val env = if (vm) List("p","v") else List("p") + coreParams.fpu foreach { case cfg => + if (xlen == 32) { + TestGeneration.addSuites(env.map(rv32uf)) + if (cfg.fLen >= 64) + TestGeneration.addSuites(env.map(rv32ud)) + } else { + TestGeneration.addSuite(rv32udBenchmarks) + TestGeneration.addSuites(env.map(rv64uf)) + if (cfg.fLen >= 64) + TestGeneration.addSuites(env.map(rv64ud)) + } + } + if (coreParams.useAtomics) TestGeneration.addSuites(env.map(if (xlen == 64) rv64ua else rv32ua)) + if (coreParams.useCompressed) TestGeneration.addSuites(env.map(if (xlen == 64) rv64uc else rv32uc)) + val (rvi, rvu) = + if (params(BoomTilesKey).nonEmpty) ((if (vm) BoomTestSuites.rv64i else BoomTestSuites.rv64pi), rv64u) + else if (xlen == 64) ((if (vm) rv64i else rv64pi), rv64u) + else ((if (vm) rv32i else rv32pi), rv32u) + + TestGeneration.addSuites(rvi.map(_("p"))) + TestGeneration.addSuites((if (vm) List("v") else List()).flatMap(env => rvu.map(_(env)))) + TestGeneration.addSuite(benchmarks) + TestGeneration.addSuite(new RegressionTestSuite(if (xlen == 64) rv64RegrTestNames else rv32RegrTestNames)) + TestGeneration.addSuite(FastBlockdevTests) + TestGeneration.addSuite(SlowBlockdevTests) + if (!targetName.contains("NoNIC")) + TestGeneration.addSuite(NICLoopbackTests) + } +} + +// Mixed into an App or into a TestSuite +trait IsFireSimGeneratorLike extends HasFireSimGeneratorUtilities with HasTestSuites { + /** Output software test Makefrags, which provide targets for integration testing. */ + def generateTestSuiteMakefrags { + addTestSuites(names.topModuleClass, targetParams) + writeOutputFile(s"$longName.d", TestGeneration.generateMakefrag) // Subsystem-specific test suites + } + + // Output miscellaneous files produced as a side-effect of elaboration + def generateArtefacts { + ElaborationArtefacts.files.foreach { case (extension, contents) => + writeOutputFile(s"${longName}.${extension}", contents ()) + } + } +} + +object FireSimGenerator extends App with IsFireSimGeneratorLike { + lazy val generatorArgs = GeneratorArgs(args) + lazy val genDir = new File(names.targetDir) + elaborateAndCompileWithMidas + generateTestSuiteMakefrags + generateHostVerilogHeader + generateArtefacts + generateTclEnvFile +} + +// For now, provide a separate generator app when not specifically building for FireSim +object Generator extends freechips.rocketchip.util.GeneratorApp with HasTestSuites { + val longName = names.topModuleProject + "." + names.topModuleClass + "." + names.configs + generateFirrtl + generateAnno + generateTestSuiteMakefrags + generateArtefacts +} diff --git a/generators/firechip/src/main/scala/SimConfigs.scala b/generators/firechip/src/main/scala/SimConfigs.scala new file mode 100644 index 00000000..1118fe07 --- /dev/null +++ b/generators/firechip/src/main/scala/SimConfigs.scala @@ -0,0 +1,54 @@ +//See LICENSE for license details. +package firesim.firesim + +import freechips.rocketchip.config.{Parameters, Config, Field} + +import midas.{EndpointKey} +import midas.widgets.{EndpointMap} +import midas.models._ + +import firesim.endpoints._ +import firesim.configs._ + +/******************************************************************************* +* Full PLATFORM_CONFIG Configurations. These set simulator parameters. +* +* In general, if you're adding or removing features from any of these, you +* should CREATE A NEW ONE, WITH A NEW NAME. This is because the manager +* will store this name as part of the tags for the AGFI, so that later you can +* reconstruct what is in a particular AGFI. These tags are also used to +* determine which driver to build. +*******************************************************************************/ +class FireSimConfig extends Config( + new WithSerialWidget ++ + new WithUARTWidget ++ + new WithSimpleNICWidget ++ + new WithBlockDevWidget ++ + new WithDefaultMemModel ++ + new WithTracerVWidget ++ + new BasePlatformConfig) + +class FireSimClockDivConfig extends Config( + new WithDefaultMemModel(clockDivision = 2) ++ + new FireSimConfig) + +class FireSimDDR3Config extends Config( + new FCFS16GBQuadRank ++ + new FireSimConfig) + +class FireSimDDR3LLC4MBConfig extends Config( + new FCFS16GBQuadRankLLC4MB ++ + new FireSimConfig) + +class FireSimDDR3FRFCFSConfig extends Config( + new FRFCFS16GBQuadRank ++ + new FireSimConfig) + +class FireSimDDR3FRFCFSLLC4MBConfig extends Config( + new FRFCFS16GBQuadRankLLC4MB ++ + new FireSimConfig) + +class FireSimDDR3FRFCFSLLC4MB3ClockDivConfig extends Config( + new FRFCFS16GBQuadRankLLC4MB3Div ++ + new FireSimConfig) + diff --git a/generators/firechip/src/main/scala/TargetConfigs.scala b/generators/firechip/src/main/scala/TargetConfigs.scala new file mode 100644 index 00000000..9be95d89 --- /dev/null +++ b/generators/firechip/src/main/scala/TargetConfigs.scala @@ -0,0 +1,205 @@ +package firesim.firesim + +import java.io.File + +import chisel3.util.{log2Up} +import freechips.rocketchip.config.{Parameters, Config} +import freechips.rocketchip.tile._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.devices.tilelink.BootROMParams +import freechips.rocketchip.devices.debug.DebugModuleParams +import boom.system.BoomTilesKey +import testchipip.{WithBlockDevice, BlockDeviceKey, BlockDeviceConfig} +import sifive.blocks.devices.uart.{PeripheryUARTKey, UARTParams} +import icenet._ + +class WithBootROM extends Config((site, here, up) => { + case BootROMParams => { + val chipyardBootROM = new File(s"./generators/testchipip/bootrom/bootrom.rv${site(XLen)}.img") + val firesimBootROM = new File(s"./target-rtl/chipyard/generators/testchipip/bootrom/bootrom.rv${site(XLen)}.img") + + val bootROMPath = if (chipyardBootROM.exists()) { + chipyardBootROM.getAbsolutePath() + } else { + firesimBootROM.getAbsolutePath() + } + BootROMParams(contentFileName = bootROMPath) + } +}) + +class WithPeripheryBusFrequency(freq: BigInt) extends Config((site, here, up) => { + case PeripheryBusKey => up(PeripheryBusKey).copy(frequency=freq) +}) + +class WithUARTKey extends Config((site, here, up) => { + case PeripheryUARTKey => List(UARTParams( + address = BigInt(0x54000000L), + nTxEntries = 256, + nRxEntries = 256)) +}) + +class WithNICKey extends Config((site, here, up) => { + case NICKey => NICConfig( + inBufFlits = 8192, + ctrlQueueDepth = 64) +}) + +class WithRocketL2TLBs(entries: Int) extends Config((site, here, up) => { + case RocketTilesKey => up(RocketTilesKey) map (tile => tile.copy( + core = tile.core.copy( + nL2TLBEntries = entries + ) + )) +}) + +class WithPerfCounters extends Config((site, here, up) => { + case RocketTilesKey => up(RocketTilesKey) map (tile => tile.copy( + core = tile.core.copy(nPerfCounters = 29) + )) +}) + +class WithBoomL2TLBs(entries: Int) extends Config((site, here, up) => { + case BoomTilesKey => up(BoomTilesKey) map (tile => tile.copy( + core = tile.core.copy(nL2TLBEntries = entries) + )) +}) + +// Disables clock-gating; doesn't play nice with our FAME-1 pass +class WithoutClockGating extends Config((site, here, up) => { + case DebugModuleParams => up(DebugModuleParams, site).copy(clockGate = false) +}) + +// Testing configurations +// This enables printfs used in testing +class WithScalaTestFeatures extends Config((site, here, up) => { + case PrintTracePort => true +}) + +/******************************************************************************* +* Full TARGET_CONFIG configurations. These set parameters of the target being +* simulated. +* +* In general, if you're adding or removing features from any of these, you +* should CREATE A NEW ONE, WITH A NEW NAME. This is because the manager +* will store this name as part of the tags for the AGFI, so that later you can +* reconstruct what is in a particular AGFI. These tags are also used to +* determine which driver to build. +*******************************************************************************/ +class FireSimRocketChipConfig extends Config( + new WithBootROM ++ + new WithPeripheryBusFrequency(BigInt(3200000000L)) ++ + new WithExtMemSize(0x400000000L) ++ // 16GB + new WithoutTLMonitors ++ + new WithUARTKey ++ + new WithNICKey ++ + new WithBlockDevice ++ + new WithRocketL2TLBs(1024) ++ + new WithPerfCounters ++ + new WithoutClockGating ++ + new freechips.rocketchip.system.DefaultConfig) + +class WithNDuplicatedRocketCores(n: Int) extends Config((site, here, up) => { + case RocketTilesKey => List.tabulate(n)(i => up(RocketTilesKey).head.copy(hartId = i)) +}) + +// single core config +class FireSimRocketChipSingleCoreConfig extends Config(new FireSimRocketChipConfig) + +// dual core config +class FireSimRocketChipDualCoreConfig extends Config( + new WithNDuplicatedRocketCores(2) ++ + new FireSimRocketChipSingleCoreConfig) + +// quad core config +class FireSimRocketChipQuadCoreConfig extends Config( + new WithNDuplicatedRocketCores(4) ++ + new FireSimRocketChipSingleCoreConfig) + +// hexa core config +class FireSimRocketChipHexaCoreConfig extends Config( + new WithNDuplicatedRocketCores(6) ++ + new FireSimRocketChipSingleCoreConfig) + +// octa core config +class FireSimRocketChipOctaCoreConfig extends Config( + new WithNDuplicatedRocketCores(8) ++ + new FireSimRocketChipSingleCoreConfig) + +class FireSimBoomConfig extends Config( + new WithBootROM ++ + new WithPeripheryBusFrequency(BigInt(3200000000L)) ++ + new WithExtMemSize(0x400000000L) ++ // 16GB + new WithoutTLMonitors ++ + new WithUARTKey ++ + new WithNICKey ++ + new WithBlockDevice ++ + new WithBoomL2TLBs(1024) ++ + new WithoutClockGating ++ + // Using a small config because it has 64-bit system bus, and compiles quickly + new boom.system.SmallBoomConfig) + +// A safer implementation than the one in BOOM in that it +// duplicates whatever BOOMTileKey.head is present N times. This prevents +// accidentally (and silently) blowing away configurations that may change the +// tile in the "up" view +class WithNDuplicatedBoomCores(n: Int) extends Config((site, here, up) => { + case BoomTilesKey => List.tabulate(n)(i => up(BoomTilesKey).head.copy(hartId = i)) + case MaxHartIdBits => log2Up(site(BoomTilesKey).size) +}) + +class FireSimBoomDualCoreConfig extends Config( + new WithNDuplicatedBoomCores(2) ++ + new FireSimBoomConfig) + +class FireSimBoomQuadCoreConfig extends Config( + new WithNDuplicatedBoomCores(4) ++ + new FireSimBoomConfig) + +//********************************************************************************** +//* Supernode Configurations +//*********************************************************************************/ +class WithNumNodes(n: Int) extends Config((pname, site, here) => { + case NumNodes => n +}) + +class SupernodeFireSimRocketChipConfig extends Config( + new WithNumNodes(4) ++ + new WithExtMemSize(0x200000000L) ++ // 8GB + new FireSimRocketChipConfig) + +class SupernodeFireSimRocketChipSingleCoreConfig extends Config( + new WithNumNodes(4) ++ + new WithExtMemSize(0x200000000L) ++ // 8GB + new FireSimRocketChipSingleCoreConfig) + +class SupernodeSixNodeFireSimRocketChipSingleCoreConfig extends Config( + new WithNumNodes(6) ++ + new WithExtMemSize(0x40000000L) ++ // 1GB + new FireSimRocketChipSingleCoreConfig) + +class SupernodeEightNodeFireSimRocketChipSingleCoreConfig extends Config( + new WithNumNodes(8) ++ + new WithExtMemSize(0x40000000L) ++ // 1GB + new FireSimRocketChipSingleCoreConfig) + +class SupernodeFireSimRocketChipDualCoreConfig extends Config( + new WithNumNodes(4) ++ + new WithExtMemSize(0x200000000L) ++ // 8GB + new FireSimRocketChipDualCoreConfig) + +class SupernodeFireSimRocketChipQuadCoreConfig extends Config( + new WithNumNodes(4) ++ + new WithExtMemSize(0x200000000L) ++ // 8GB + new FireSimRocketChipQuadCoreConfig) + +class SupernodeFireSimRocketChipHexaCoreConfig extends Config( + new WithNumNodes(4) ++ + new WithExtMemSize(0x200000000L) ++ // 8GB + new FireSimRocketChipHexaCoreConfig) + +class SupernodeFireSimRocketChipOctaCoreConfig extends Config( + new WithNumNodes(4) ++ + new WithExtMemSize(0x200000000L) ++ // 8GB + new FireSimRocketChipOctaCoreConfig) + diff --git a/generators/firechip/src/main/scala/TargetLandTestSuites.scala b/generators/firechip/src/main/scala/TargetLandTestSuites.scala new file mode 100644 index 00000000..b20fa8dd --- /dev/null +++ b/generators/firechip/src/main/scala/TargetLandTestSuites.scala @@ -0,0 +1,45 @@ +//See LICENSE for license details. +package firesim.firesim + +import scala.collection.mutable.LinkedHashSet + +import freechips.rocketchip.system.{TestGeneration, RocketTestSuite} + +/* This imports tests from FireChip to test devices that aren't natively + * tested by the riscv assembly tests. + * Firesim's target-specific makefrag gives the recipes for building the + * binaries. + */ + +class BlockdevTestSuite(prefix: String, val names: LinkedHashSet[String]) extends RocketTestSuite { + val envName = "" + // fc_test_dir is is defined in firesim's Makefrag + val dir = "$(fc_test_dir)" + val makeTargetName = prefix + "-blkdev-tests" + def kind = "blockdev" + // Blockdev tests need an image, which complicates this + def additionalArgs = "+blkdev-in-mem0=128 +nic-loopback0" + override def toString = s"$makeTargetName = \\\n" + + // Make variable with the binaries of the suite + names.map(n => s"\t$n.riscv").mkString(" \\\n") + "\n\n" + + // Variables with binary specific arguments + names.map(n => s"$n.riscv_ARGS=$additionalArgs").mkString(" \n") + + postScript +} + +object FastBlockdevTests extends BlockdevTestSuite("fast", LinkedHashSet("blkdev")) +object SlowBlockdevTests extends BlockdevTestSuite("slow", LinkedHashSet("big-blkdev")) + +class NICTestSuite(prefix: String, val names: LinkedHashSet[String]) extends RocketTestSuite { + val envName = "" + val dir = "$(fc_test_dir)" + val makeTargetName = prefix + "-nic-tests" + def kind = "nic" + def additionalArgs = "+netbw0=100 +linklatency0=6405 +netburst0=8 +slotid=0 +nic-loopback0" + override def toString = s"$makeTargetName = \\\n" + + names.map(n => s"\t$n.riscv").mkString(" \\\n") + "\n\n" + + names.map(n => s"$n.riscv_ARGS=$additionalArgs").mkString(" \n") + + postScript +} + +object NICLoopbackTests extends NICTestSuite("loopback", LinkedHashSet("nic-loopback")) diff --git a/generators/firechip/src/main/scala/TargetMixins.scala b/generators/firechip/src/main/scala/TargetMixins.scala new file mode 100644 index 00000000..5544376f --- /dev/null +++ b/generators/firechip/src/main/scala/TargetMixins.scala @@ -0,0 +1,130 @@ +package firesim.firesim + +import chisel3._ +import freechips.rocketchip.config.{Field, Parameters} +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.amba.axi4._ +import freechips.rocketchip.util._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.rocket.TracedInstruction +import firesim.endpoints.{TraceOutputTop, DeclockedTracedInstruction} + +import midas.models.AXI4BundleWithEdge +import midas.targetutils.ExcludeInstanceAsserts + +/** Ties together Subsystem buses in the same fashion done in the example top of Rocket Chip */ +trait HasDefaultBusConfiguration { + this: BaseSubsystem => + // The sbus masters the cbus; here we convert TL-UH -> TL-UL + sbus.crossToBus(cbus, NoCrossing) + + // The cbus masters the pbus; which might be clocked slower + cbus.crossToBus(pbus, SynchronousCrossing()) + + // The fbus masters the sbus; both are TL-UH or TL-C + FlipRendering { implicit p => + sbus.crossFromBus(fbus, SynchronousCrossing()) + } + + // The sbus masters the mbus; here we convert TL-C -> TL-UH + private val BankedL2Params(nBanks, coherenceManager) = p(BankedL2Key) + private val (in, out, halt) = coherenceManager(this) + if (nBanks != 0) { + sbus.coupleTo("coherence_manager") { in :*= _ } + mbus.coupleFrom("coherence_manager") { _ :=* BankBinder(mbus.blockBytes * (nBanks-1)) :*= out } + } +} + + +/** Copied from RC and modified to change the IO type of the Imp to include the Diplomatic edges + * associated with each port. This drives FASED functional model sizing + */ +trait CanHaveFASEDOptimizedMasterAXI4MemPort { this: BaseSubsystem => + val module: CanHaveFASEDOptimizedMasterAXI4MemPortModuleImp + + val memAXI4Node = p(ExtMem).map { case MemoryPortParams(memPortParams, nMemoryChannels) => + val portName = "axi4" + val device = new MemoryDevice + + val memAXI4Node = AXI4SlaveNode(Seq.tabulate(nMemoryChannels) { channel => + val base = AddressSet.misaligned(memPortParams.base, memPortParams.size) + val filter = AddressSet(channel * mbus.blockBytes, ~((nMemoryChannels-1) * mbus.blockBytes)) + + AXI4SlavePortParameters( + slaves = Seq(AXI4SlaveParameters( + address = base.flatMap(_.intersect(filter)), + resources = device.reg, + regionType = RegionType.UNCACHED, // cacheable + executable = true, + supportsWrite = TransferSizes(1, mbus.blockBytes), + supportsRead = TransferSizes(1, mbus.blockBytes), + interleavedId = Some(0))), // slave does not interleave read responses + beatBytes = memPortParams.beatBytes) + }) + + memAXI4Node := mbus.toDRAMController(Some(portName)) { + AXI4UserYanker() := AXI4IdIndexer(memPortParams.idBits) := TLToAXI4() + } + + memAXI4Node + } +} + +/** Actually generates the corresponding IO in the concrete Module */ +trait CanHaveFASEDOptimizedMasterAXI4MemPortModuleImp extends LazyModuleImp { + val outer: CanHaveFASEDOptimizedMasterAXI4MemPort + + val mem_axi4 = outer.memAXI4Node.map(x => IO(HeterogeneousBag(AXI4BundleWithEdge.fromNode(x.in)))) + (mem_axi4 zip outer.memAXI4Node) foreach { case (io, node) => + (io zip node.in).foreach { case (io, (bundle, _)) => io <> bundle } + } + + def connectSimAXIMem() { + (mem_axi4 zip outer.memAXI4Node).foreach { case (io, node) => + (io zip node.in).foreach { case (io, (_, edge)) => + val mem = LazyModule(new SimAXIMem(edge, size = p(ExtMem).get.master.size)) + Module(mem.module).io.axi4.head <> io + } + } + } +} + +/* Wires out tile trace ports to the top; and wraps them in a Bundle that the + * TracerV endpoint can match on. + */ +object PrintTracePort extends Field[Boolean](false) + +trait HasTraceIO { + this: HasTiles => + val module: HasTraceIOImp + + // Bind all the trace nodes to a BB; we'll use this to generate the IO in the imp + val traceNexus = BundleBridgeNexus[Vec[TracedInstruction]] + val tileTraceNodes = tiles.map(tile => tile.traceNode) + tileTraceNodes foreach { traceNexus := _ } +} + +trait HasTraceIOImp extends LazyModuleImp { + val outer: HasTraceIO + + val traceIO = IO(Output(new TraceOutputTop( + DeclockedTracedInstruction.fromNode(outer.traceNexus.in)))) + (traceIO.traces zip outer.traceNexus.in).foreach({ case (port, (tileTrace, _)) => + port := DeclockedTracedInstruction.fromVec(tileTrace) + }) + + // Enabled to test TracerV trace capture + if (p(PrintTracePort)) { + val traceprint = Wire(UInt(512.W)) + traceprint := traceIO.asUInt + printf("TRACEPORT: %x\n", traceprint) + } +} + +// Prevent MIDAS from synthesizing assertions in the dummy TLB included in BOOM +trait ExcludeInvalidBoomAssertions extends LazyModuleImp { + ExcludeInstanceAsserts(("NonBlockingDCache", "dtlb")) +} + diff --git a/generators/firechip/src/main/scala/Targets.scala b/generators/firechip/src/main/scala/Targets.scala new file mode 100644 index 00000000..c9077044 --- /dev/null +++ b/generators/firechip/src/main/scala/Targets.scala @@ -0,0 +1,176 @@ +package firesim.firesim + +import chisel3._ +import freechips.rocketchip._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.devices.tilelink._ +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.util.{HeterogeneousBag} +import freechips.rocketchip.amba.axi4.AXI4Bundle +import freechips.rocketchip.config.{Field, Parameters} +import freechips.rocketchip.diplomacy.LazyModule +import boom.system.{BoomRocketSubsystem, BoomRocketSubsystemModuleImp} +import icenet._ +import testchipip._ +import testchipip.SerialAdapter.SERIAL_IF_WIDTH +import sifive.blocks.devices.uart._ +import midas.models.AXI4BundleWithEdge +import java.io.File + +/******************************************************************************* +* Top level DESIGN configurations. These describe the basic instantiations of +* the designs being simulated. +* +* In general, if you're adding or removing features from any of these, you +* should CREATE A NEW ONE, WITH A NEW NAME. This is because the manager +* will store this name as part of the tags for the AGFI, so that later you can +* reconstruct what is in a particular AGFI. These tags are also used to +* determine which driver to build. +*******************************************************************************/ + +class FireSim(implicit p: Parameters) extends RocketSubsystem + with HasDefaultBusConfiguration + with CanHaveFASEDOptimizedMasterAXI4MemPort + with HasPeripheryBootROM + with HasNoDebug + with HasPeripherySerial + with HasPeripheryUART + with HasPeripheryIceNIC + with HasPeripheryBlockDevice + with HasTraceIO +{ + override lazy val module = new FireSimModuleImp(this) +} + +class FireSimModuleImp[+L <: FireSim](l: L) extends RocketSubsystemModuleImp(l) + with HasRTCModuleImp + with CanHaveFASEDOptimizedMasterAXI4MemPortModuleImp + with HasPeripheryBootROMModuleImp + with HasNoDebugModuleImp + with HasPeripherySerialModuleImp + with HasPeripheryUARTModuleImp + with HasPeripheryIceNICModuleImpValidOnly + with HasPeripheryBlockDeviceModuleImp + with HasTraceIOImp + + +class FireSimNoNIC(implicit p: Parameters) extends RocketSubsystem + with HasDefaultBusConfiguration + with CanHaveFASEDOptimizedMasterAXI4MemPort + with HasPeripheryBootROM + with HasNoDebug + with HasPeripherySerial + with HasPeripheryUART + with HasPeripheryBlockDevice + with HasTraceIO +{ + override lazy val module = new FireSimNoNICModuleImp(this) +} + +class FireSimNoNICModuleImp[+L <: FireSimNoNIC](l: L) extends RocketSubsystemModuleImp(l) + with HasRTCModuleImp + with CanHaveFASEDOptimizedMasterAXI4MemPortModuleImp + with HasPeripheryBootROMModuleImp + with HasNoDebugModuleImp + with HasPeripherySerialModuleImp + with HasPeripheryUARTModuleImp + with HasPeripheryBlockDeviceModuleImp + with HasTraceIOImp + + +class FireBoom(implicit p: Parameters) extends BoomRocketSubsystem + with HasDefaultBusConfiguration + with CanHaveFASEDOptimizedMasterAXI4MemPort + with HasPeripheryBootROM + with HasNoDebug + with HasPeripherySerial + with HasPeripheryUART + with HasPeripheryIceNIC + with HasPeripheryBlockDevice + with HasTraceIO +{ + override lazy val module = new FireBoomModuleImp(this) +} + +class FireBoomModuleImp[+L <: FireBoom](l: L) extends BoomRocketSubsystemModuleImp(l) + with HasRTCModuleImp + with CanHaveFASEDOptimizedMasterAXI4MemPortModuleImp + with HasPeripheryBootROMModuleImp + with HasNoDebugModuleImp + with HasPeripherySerialModuleImp + with HasPeripheryUARTModuleImp + with HasPeripheryIceNICModuleImpValidOnly + with HasPeripheryBlockDeviceModuleImp + with HasTraceIOImp + with ExcludeInvalidBoomAssertions + +class FireBoomNoNIC(implicit p: Parameters) extends BoomRocketSubsystem + with HasDefaultBusConfiguration + with CanHaveFASEDOptimizedMasterAXI4MemPort + with HasPeripheryBootROM + with HasNoDebug + with HasPeripherySerial + with HasPeripheryUART + with HasPeripheryBlockDevice + with HasTraceIO +{ + override lazy val module = new FireBoomNoNICModuleImp(this) +} + +class FireBoomNoNICModuleImp[+L <: FireBoomNoNIC](l: L) extends BoomRocketSubsystemModuleImp(l) + with HasRTCModuleImp + with CanHaveFASEDOptimizedMasterAXI4MemPortModuleImp + with HasPeripheryBootROMModuleImp + with HasNoDebugModuleImp + with HasPeripherySerialModuleImp + with HasPeripheryUARTModuleImp + with HasPeripheryBlockDeviceModuleImp + with HasTraceIOImp + with ExcludeInvalidBoomAssertions + +case object NumNodes extends Field[Int] + +class SupernodeIO( + nNodes: Int, + serialWidth: Int, + bagPrototype: HeterogeneousBag[AXI4BundleWithEdge])(implicit p: Parameters) + extends Bundle { + + val serial = Vec(nNodes, new SerialIO(serialWidth)) + val mem_axi = Vec(nNodes, bagPrototype.cloneType) + val bdev = Vec(nNodes, new BlockDeviceIO) + val net = Vec(nNodes, new NICIOvonly) + val uart = Vec(nNodes, new UARTPortIO) + + override def cloneType = new SupernodeIO(nNodes, serialWidth, bagPrototype).asInstanceOf[this.type] +} + + +class FireSimSupernode(implicit p: Parameters) extends Module { + val nNodes = p(NumNodes) + val nodes = Seq.fill(nNodes) { + Module(LazyModule(new FireSim).module) + } + + val io = IO(new SupernodeIO(nNodes, SERIAL_IF_WIDTH, nodes(0).mem_axi4.get)) + + io.mem_axi.zip(nodes.map(_.mem_axi4)).foreach { + case (out, mem_axi4) => out <> mem_axi4.get + } + io.serial <> nodes.map(_.serial) + io.bdev <> nodes.map(_.bdev) + io.net <> nodes.map(_.net) + io.uart <> nodes.map(_.uart(0)) + nodes.foreach{ case n => { + n.debug.clockeddmi.get.dmi.req.valid := false.B + n.debug.clockeddmi.get.dmi.resp.ready := false.B + n.debug.clockeddmi.get.dmiClock := clock + n.debug.clockeddmi.get.dmiReset := reset.toBool + n.debug.clockeddmi.get.dmi.req.bits.data := DontCare + n.debug.clockeddmi.get.dmi.req.bits.addr := DontCare + n.debug.clockeddmi.get.dmi.req.bits.op := DontCare + } } +} + diff --git a/generators/firechip/src/test/scala/ScalaTestSuite.scala b/generators/firechip/src/test/scala/ScalaTestSuite.scala new file mode 100644 index 00000000..54848ac8 --- /dev/null +++ b/generators/firechip/src/test/scala/ScalaTestSuite.scala @@ -0,0 +1,138 @@ +//See LICENSE for license details. +package firesim.firesim + +import java.io.File + +import scala.concurrent.{Future, Await, ExecutionContext} +import scala.sys.process.{stringSeqToProcess, ProcessLogger} +import scala.io.Source + +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.system.{RocketTestSuite, BenchmarkTestSuite} +import freechips.rocketchip.system.TestGeneration._ +import freechips.rocketchip.system.DefaultTestSuites._ + +import firesim.util.GeneratorArgs + +abstract class FireSimTestSuite( + topModuleClass: String, + targetConfigs: String, + platformConfigs: String, + N: Int = 8 + ) extends firesim.TestSuiteCommon with IsFireSimGeneratorLike { + import scala.concurrent.duration._ + import ExecutionContext.Implicits.global + + lazy val generatorArgs = GeneratorArgs( + midasFlowKind = "midas", + targetDir = "generated-src", + topModuleProject = "firesim.firesim", + topModuleClass = topModuleClass, + targetConfigProject = "firesim.firesim", + targetConfigs = targetConfigs ++ "_WithScalaTestFeatures", + platformConfigProject = "firesim.firesim", + platformConfigs = platformConfigs) + + // From HasFireSimGeneratorUtilities + // For the firesim utilities to use the same directory as the test suite + override lazy val testDir = genDir + + // From TestSuiteCommon + val targetTuple = generatorArgs.tupleName + val commonMakeArgs = Seq(s"DESIGN=${generatorArgs.topModuleClass}", + s"TARGET_CONFIG=${generatorArgs.targetConfigs}", + s"PLATFORM_CONFIG=${generatorArgs.platformConfigs}") + override lazy val platform = hostParams(midas.Platform) + + def invokeMlSimulator(backend: String, name: String, debug: Boolean, additionalArgs: Seq[String] = Nil) = { + make((Seq(s"${outDir.getAbsolutePath}/${name}.%s".format(if (debug) "vpd" else "out"), + s"EMUL=${backend}") + ++ additionalArgs):_*) + } + + def runTest(backend: String, name: String, debug: Boolean, additionalArgs: Seq[String] = Nil) = { + behavior of s"${name} running on ${backend} in MIDAS-level simulation" + compileMlSimulator(backend, debug) + if (isCmdAvailable(backend)) { + it should s"pass" in { + assert(invokeMlSimulator(backend, name, debug, additionalArgs) == 0) + } + } + } + + //def runReplay(backend: String, replayBackend: String, name: String) = { + // val dir = (new File(outDir, backend)).getAbsolutePath + // (Seq("make", s"replay-$replayBackend", + // s"SAMPLE=${dir}/${name}.sample", s"output_dir=$dir") ++ makeArgs).! + //} + + def runSuite(backend: String, debug: Boolean = false)(suite: RocketTestSuite) { + // compile emulators + behavior of s"${suite.makeTargetName} running on $backend" + if (isCmdAvailable(backend)) { + val postfix = suite match { + case _: BenchmarkTestSuite | _: BlockdevTestSuite | _: NICTestSuite => ".riscv" + case _ => "" + } + val results = suite.names.toSeq sliding (N, N) map { t => + val subresults = t map (name => + Future(name -> invokeMlSimulator(backend, s"$name$postfix", debug))) + Await result (Future sequence subresults, Duration.Inf) + } + results.flatten foreach { case (name, exitcode) => + it should s"pass $name" in { assert(exitcode == 0) } + } + //replayBackends foreach { replayBackend => + // if (platformParams(midas.EnableSnapshot) && isCmdAvailable("vcs")) { + // assert((Seq("make", s"vcs-$replayBackend") ++ makeArgs).! == 0) // compile vcs + // suite.names foreach { name => + // it should s"replay $name in $replayBackend" in { + // assert(runReplay(backend, replayBackend, s"$name$postfix") == 0) + // } + // } + // } else { + // suite.names foreach { name => + // ignore should s"replay $name in $backend" + // } + // } + //} + } else { + ignore should s"pass $backend" + } + } + + // Checks the collected trace log matches the behavior of a chisel printf + def diffTracelog(verilatedLog: String) { + behavior of "captured instruction trace" + it should s"match the chisel printf in ${verilatedLog}" in { + def getLines(file: File, dropLines: Int = 0): Seq[String] = { + val lines = Source.fromFile(file).getLines.toList + lines.filter(_.startsWith("TRACEPORT")).drop(dropLines) + } + val resetLength = 50 + val verilatedOutput = getLines(new File(outDir, s"/${verilatedLog}")) + val synthPrintOutput = getLines(new File(genDir, s"/TRACEFILE"), resetLength + 1) + assert(verilatedOutput.size == synthPrintOutput.size, "Outputs differ in length") + assert(verilatedOutput.nonEmpty) + for ( (vPrint, sPrint) <- verilatedOutput.zip(synthPrintOutput) ) { + assert(vPrint == sPrint) + } + } + } + + clean + mkdirs + elaborateAndCompileWithMidas + generateTestSuiteMakefrags + runTest("verilator", "rv64ui-p-simple", false, Seq(s"""EXTRA_SIM_ARGS=+trace-test-output0""")) + diffTracelog("rv64ui-p-simple.out") + runSuite("verilator")(benchmarks) + runSuite("verilator")(FastBlockdevTests) +} + +class RocketF1Tests extends FireSimTestSuite("FireSimNoNIC", "FireSimRocketChipConfig", "FireSimConfig") +class RocketF1ClockDivTests extends FireSimTestSuite("FireSimNoNIC", "FireSimRocketChipConfig", "FireSimClockDivConfig") +class BoomF1Tests extends FireSimTestSuite("FireBoomNoNIC", "FireSimBoomConfig", "FireSimConfig") +class RocketNICF1Tests extends FireSimTestSuite("FireSim", "FireSimRocketChipConfig", "FireSimConfig") { + runSuite("verilator")(NICLoopbackTests) +} diff --git a/generators/icenet b/generators/icenet new file mode 160000 index 00000000..bba264d6 --- /dev/null +++ b/generators/icenet @@ -0,0 +1 @@ +Subproject commit bba264d68d366180f6f9b55061ee9408425d8229 diff --git a/generators/sifive-cache b/generators/sifive-cache new file mode 160000 index 00000000..13d0c2f1 --- /dev/null +++ b/generators/sifive-cache @@ -0,0 +1 @@ +Subproject commit 13d0c2f17853a658ae86eae793718c71ac82dddf diff --git a/generators/testchipip b/generators/testchipip index 855a4dd4..532d4a42 160000 --- a/generators/testchipip +++ b/generators/testchipip @@ -1 +1 @@ -Subproject commit 855a4dd4822cee1693fe1e0bf7d0727f074ceae5 +Subproject commit 532d4a42914286548179f3893ab6349a0aedb5e3 diff --git a/generators/utilities/src/main/resources/csrc/emulator.cc b/generators/utilities/src/main/resources/csrc/emulator.cc index 1a4f4dd2..b4f2e093 100644 --- a/generators/utilities/src/main/resources/csrc/emulator.cc +++ b/generators/utilities/src/main/resources/csrc/emulator.cc @@ -121,7 +121,6 @@ int main(int argc, char** argv) FILE * vcdfile = NULL; uint64_t start = 0; #endif - char ** htif_argv = NULL; int verilog_plusargs_legal = 1; while (1) { @@ -243,10 +242,6 @@ done_processing: usage(argv[0]); return 1; } - int htif_argc = 1 + argc - optind; - htif_argv = (char **) malloc((htif_argc) * sizeof (char *)); - htif_argv[0] = argv[0]; - for (int i = 1; optind < argc;) htif_argv[i++] = argv[optind++]; if (verbose) fprintf(stderr, "using random seed %u\n", random_seed); @@ -269,8 +264,8 @@ done_processing: #endif jtag = new remote_bitbang_t(rbb_port); - dtm = new dtm_t(htif_argc, htif_argv); - tsi = new tsi_t(htif_argc, htif_argv); + dtm = new dtm_t(argc, argv); + tsi = new tsi_t(argc, argv); signal(SIGTERM, handle_sigterm); @@ -351,6 +346,5 @@ done_processing: if (tsi) delete tsi; if (jtag) delete jtag; if (tile) delete tile; - if (htif_argv) free(htif_argv); return ret; } diff --git a/scripts/firesim-setup.sh b/scripts/firesim-setup.sh new file mode 100755 index 00000000..53dee5ab --- /dev/null +++ b/scripts/firesim-setup.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# Sets up FireSim for use as a library within REBAR + +set -e +set -o pipefail + +RDIR=$(pwd) +scripts_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +sims_dir=$scripts_dir/../sims/ + +# Reenable the FireSim submodule +git config --unset submodule.sims/firesim.update || true +cd $sims_dir +git submodule update --init firesim +cd firesim +./build-setup.sh $@ --library +cd $RDIR diff --git a/scripts/init-submodules-no-riscv-tools.sh b/scripts/init-submodules-no-riscv-tools.sh index 27005cbb..64452d40 100755 --- a/scripts/init-submodules-no-riscv-tools.sh +++ b/scripts/init-submodules-no-riscv-tools.sh @@ -6,6 +6,7 @@ set -o pipefail unamestr=$(uname) RDIR=$(pwd) +scripts_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # ignore riscv-tools for submodule init recursive # you must do this globally (otherwise riscv-tools deep @@ -13,7 +14,19 @@ RDIR=$(pwd) git config --global submodule.riscv-tools.update none git config --global submodule.esp-tools.update none git config --global submodule.experimental-blocks.update none +# Disable updates to the FireSim submodule until explicitly requested +git config submodule.sims/firesim.update none git submodule update --init --recursive #--jobs 8 # unignore riscv-tools,catapult-shell2 globally git config --global --unset submodule.riscv-tools.update git config --global --unset submodule.experimental-blocks.update + +# Renable firesim and init only the required submodules to provide +# all required scala deps, without doing a full build-setup +git config --unset submodule.sims/firesim.update +cd $scripts_dir/../sims/ +git submodule update --init firesim +cd firesim/sim +git submodule update --init midas +cd $RDIR +git config submodule.sims/firesim.update none diff --git a/sims/firesim b/sims/firesim new file mode 160000 index 00000000..7f8152e5 --- /dev/null +++ b/sims/firesim @@ -0,0 +1 @@ +Subproject commit 7f8152e5114c7f7793df54a41a53a9e6a04fa8cd diff --git a/tests/Makefile b/tests/Makefile index edabb264..80142ad7 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -3,7 +3,7 @@ OBJDUMP=riscv64-unknown-elf-objdump CFLAGS=-mcmodel=medany -std=gnu99 -O2 -fno-common -fno-builtin-printf -Wall LDFLAGS=-static -nostdlib -nostartfiles -lgcc -PROGRAMS = pwm blkdev accum charcount +PROGRAMS = pwm blkdev accum charcount nic-loopback big-blkdev pingd default: $(addsuffix .riscv,$(PROGRAMS)) diff --git a/tests/big-blkdev.c b/tests/big-blkdev.c new file mode 100644 index 00000000..d065217a --- /dev/null +++ b/tests/big-blkdev.c @@ -0,0 +1,79 @@ +#include +#include + +#include "mmio.h" +#include "blkdev.h" + +#define SECTOR_WORDS (BLKDEV_SECTOR_SIZE / sizeof(uint64_t)) +#define TEST_SECTORS 128 + +unsigned long sector_buf[SECTOR_WORDS]; + +void write_sector(unsigned int secnum) +{ + int req_tag, resp_tag; + + for (int i = 0; i < SECTOR_WORDS; i++) + sector_buf[i] = (secnum << 6) | i; + + while (reg_read8(BLKDEV_NREQUEST) == 0); + req_tag = blkdev_send_request((unsigned long) sector_buf, secnum, 1, 1); + while (reg_read8(BLKDEV_NCOMPLETE) == 0); + resp_tag = reg_read8(BLKDEV_COMPLETE); + + if (req_tag != resp_tag) { + printf("Response tag %d does not match request tag %d\n", + req_tag, resp_tag); + exit(EXIT_FAILURE); + } +} + +void check_sector(unsigned int secnum) +{ + int req_tag, resp_tag; + + while (reg_read8(BLKDEV_NREQUEST) == 0); + req_tag = blkdev_send_request((unsigned long) sector_buf, secnum, 1, 0); + while (reg_read8(BLKDEV_NCOMPLETE) == 0); + resp_tag = reg_read8(BLKDEV_COMPLETE); + + if (req_tag != resp_tag) { + printf("Response tag %d does not match request tag %d\n", + req_tag, resp_tag); + exit(EXIT_FAILURE); + } + + for (int i = 0; i < SECTOR_WORDS; i++) { + unsigned long expected = (secnum << 6) | i; + unsigned long actual = sector_buf[i]; + if (actual != expected) { + printf("Word %d in sector %x does not match expected\n", + i, secnum); + printf("Expected %lx, got %lx\n", + expected, actual); + exit(EXIT_FAILURE); + } + } +} + +int main(void) +{ + unsigned int nsectors = blkdev_nsectors(); + unsigned int stride = nsectors / TEST_SECTORS; + + printf("Writing %u of %u sectors\n", TEST_SECTORS, nsectors); + + for (int i = 0; i < TEST_SECTORS; i++) { + int sector = i * stride; + write_sector(sector); + } + + printf("Checking sectors\n", nsectors); + + for (int i = 0; i < TEST_SECTORS; i++) { + int sector = i * stride; + check_sector(sector); + } + + return 0; +} diff --git a/tests/blkdev.c b/tests/blkdev.c index 7716b46a..994a341b 100644 --- a/tests/blkdev.c +++ b/tests/blkdev.c @@ -2,30 +2,7 @@ #include #include "mmio.h" - -#define BLKDEV_BASE 0x10015000 -#define BLKDEV_ADDR BLKDEV_BASE -#define BLKDEV_OFFSET (BLKDEV_BASE + 8) -#define BLKDEV_LEN (BLKDEV_BASE + 12) -#define BLKDEV_WRITE (BLKDEV_BASE + 16) -#define BLKDEV_REQUEST (BLKDEV_BASE + 17) -#define BLKDEV_NREQUEST (BLKDEV_BASE + 18) -#define BLKDEV_COMPLETE (BLKDEV_BASE + 19) -#define BLKDEV_NCOMPLETE (BLKDEV_BASE + 20) -#define BLKDEV_NSECTORS (BLKDEV_BASE + 24) -#define BLKDEV_MAX_REQUEST_LENGTH (BLKDEV_BASE + 28) -#define BLKDEV_SECTOR_SIZE 512 -#define BLKDEV_SECTOR_SHIFT 9 - -size_t blkdev_nsectors(void) -{ - return reg_read32(BLKDEV_NSECTORS); -} - -size_t blkdev_max_req_len(void) -{ - return reg_read32(BLKDEV_MAX_REQUEST_LENGTH); -} +#include "blkdev.h" void blkdev_read(void *addr, unsigned long offset, size_t nsectors) { @@ -38,12 +15,9 @@ void blkdev_read(void *addr, unsigned long offset, size_t nsectors) printf("sending %d reads\n", ntags); for (i = 0; i < ntags; i++) { - reg_write64(BLKDEV_ADDR, (unsigned long) addr); - reg_write32(BLKDEV_OFFSET, offset); - reg_write32(BLKDEV_LEN, nsectors_per_tag); - reg_write8(BLKDEV_WRITE, 0); - - req_tag = reg_read8(BLKDEV_REQUEST); + req_tag = blkdev_send_request( + (unsigned long) addr, offset, + nsectors_per_tag, 0); addr += (nsectors_per_tag << BLKDEV_SECTOR_SHIFT); offset += nsectors_per_tag; } @@ -67,12 +41,9 @@ void blkdev_write(unsigned long offset, void *addr, size_t nsectors) printf("sending %d writes\n", ntags); for (i = 0; i < ntags; i++) { - reg_write64(BLKDEV_ADDR, (unsigned long) addr); - reg_write32(BLKDEV_OFFSET, offset); - reg_write32(BLKDEV_LEN, nsectors_per_tag); - reg_write8(BLKDEV_WRITE, 1); - - req_tag = reg_read8(BLKDEV_REQUEST); + req_tag = blkdev_send_request( + (unsigned long) addr, offset, + nsectors_per_tag, 1); addr += (nsectors_per_tag << BLKDEV_SECTOR_SHIFT); offset += nsectors_per_tag; } diff --git a/tests/blkdev.h b/tests/blkdev.h new file mode 100644 index 00000000..be7ec064 --- /dev/null +++ b/tests/blkdev.h @@ -0,0 +1,38 @@ +#define BLKDEV_BASE 0x10015000 +#define BLKDEV_ADDR BLKDEV_BASE +#define BLKDEV_OFFSET (BLKDEV_BASE + 8) +#define BLKDEV_LEN (BLKDEV_BASE + 12) +#define BLKDEV_WRITE (BLKDEV_BASE + 16) +#define BLKDEV_REQUEST (BLKDEV_BASE + 17) +#define BLKDEV_NREQUEST (BLKDEV_BASE + 18) +#define BLKDEV_COMPLETE (BLKDEV_BASE + 19) +#define BLKDEV_NCOMPLETE (BLKDEV_BASE + 20) +#define BLKDEV_NSECTORS (BLKDEV_BASE + 24) +#define BLKDEV_MAX_REQUEST_LENGTH (BLKDEV_BASE + 28) +#define BLKDEV_SECTOR_SIZE 512 +#define BLKDEV_SECTOR_SHIFT 9 + +static inline size_t blkdev_nsectors(void) +{ + return reg_read32(BLKDEV_NSECTORS); +} + +static inline size_t blkdev_max_req_len(void) +{ + return reg_read32(BLKDEV_MAX_REQUEST_LENGTH); +} + +static inline unsigned int blkdev_send_request( + unsigned long addr, + unsigned int offset, + unsigned int len, + unsigned char write) +{ + reg_write64(BLKDEV_ADDR, addr); + reg_write32(BLKDEV_OFFSET, offset); + reg_write32(BLKDEV_LEN, len); + reg_write8(BLKDEV_WRITE, write); + + asm volatile ("fence"); + return reg_read8(BLKDEV_REQUEST); +} diff --git a/tests/nic-loopback.c b/tests/nic-loopback.c new file mode 100644 index 00000000..1d3c7b96 --- /dev/null +++ b/tests/nic-loopback.c @@ -0,0 +1,98 @@ +#include "mmio.h" +#include +#include +#include + +#include "nic.h" +#include "encoding.h" + +#define NPACKETS 10 +#define TEST_OFFSET 3 +#define TEST_LEN 356 +#define ARRAY_LEN 360 +#define NTRIALS 3 + +uint32_t src[NPACKETS][ARRAY_LEN]; +uint32_t dst[NPACKETS][ARRAY_LEN]; +uint64_t lengths[NPACKETS]; + +static inline void send_recv() +{ + uint64_t send_packet, recv_addr; + int ncomps, send_comps_left = NPACKETS, recv_comps_left = NPACKETS; + int recv_idx = 0; + + for (int i = 0; i < NPACKETS; i++) { + uint64_t pkt_size = TEST_LEN * sizeof(uint32_t); + uint64_t src_addr = (uint64_t) &src[i][TEST_OFFSET]; + send_packet = (pkt_size << 48) | src_addr; + recv_addr = (uint64_t) dst[i]; + reg_write64(SIMPLENIC_SEND_REQ, send_packet); + reg_write64(SIMPLENIC_RECV_REQ, recv_addr); + } + + while (send_comps_left > 0 || recv_comps_left > 0) { + ncomps = nic_send_comp_avail(); + asm volatile ("fence"); + for (int i = 0; i < ncomps; i++) + reg_read16(SIMPLENIC_SEND_COMP); + send_comps_left -= ncomps; + + ncomps = nic_recv_comp_avail(); + asm volatile ("fence"); + for (int i = 0; i < ncomps; i++) { + lengths[recv_idx] = reg_read16(SIMPLENIC_RECV_COMP); + recv_idx++; + } + recv_comps_left -= ncomps; + } +} + +void run_test(void) +{ + unsigned long start, end; + int i, j; + + memset(dst, 0, sizeof(dst)); + asm volatile ("fence"); + + start = rdcycle(); + send_recv(); + end = rdcycle(); + + printf("send/recv %lu cycles\n", end - start); + + for (i = 0; i < NPACKETS; i++) { + if (lengths[i] != TEST_LEN * sizeof(uint32_t)) { + printf("recv got wrong # bytes\n"); + exit(EXIT_FAILURE); + } + + for (j = 0; j < TEST_LEN; j++) { + if (dst[i][j] != src[i][j + TEST_OFFSET]) { + printf("Data mismatch @ %d, %d: %x != %x\n", + i, j, dst[i][j], src[i][j + TEST_OFFSET]); + exit(EXIT_FAILURE); + } + } + } +} + +int main(void) +{ + int i, j; + + for (i = 0; i < NPACKETS; i++) { + for (j = 0; j < ARRAY_LEN; j++) + src[i][j] = i * ARRAY_LEN + j; + } + + for (i = 0; i < NTRIALS; i++) { + printf("Trial %d\n", i); + run_test(); + } + + printf("All correct\n"); + + return 0; +} diff --git a/tests/nic.h b/tests/nic.h index 55a86a2d..bee4c395 100644 --- a/tests/nic.h +++ b/tests/nic.h @@ -8,22 +8,22 @@ static inline int nic_send_req_avail(void) { - return reg_read16(SIMPLENIC_COUNTS) & 0xf; + return reg_read32(SIMPLENIC_COUNTS) & 0xff; } static inline int nic_recv_req_avail(void) { - return (reg_read16(SIMPLENIC_COUNTS) >> 4) & 0xf; + return (reg_read32(SIMPLENIC_COUNTS) >> 8) & 0xff; } static inline int nic_send_comp_avail(void) { - return (reg_read16(SIMPLENIC_COUNTS) >> 8) & 0xf; + return (reg_read32(SIMPLENIC_COUNTS) >> 16) & 0xff; } static inline int nic_recv_comp_avail(void) { - return (reg_read16(SIMPLENIC_COUNTS) >> 12) & 0xf; + return (reg_read32(SIMPLENIC_COUNTS) >> 24) & 0xff; } static void nic_send(void *data, unsigned long len) diff --git a/tests/pingd.c b/tests/pingd.c new file mode 100644 index 00000000..9e02cafe --- /dev/null +++ b/tests/pingd.c @@ -0,0 +1,254 @@ +#include "mmio.h" +#include "nic.h" + +#include +#include +#include +#include + +#define ETH_MAX_WORDS 190 +#define NET_IP_ALIGN 2 +#define ETH_HEADER_SIZE 14 +#define MAC_ADDR_SIZE 6 +#define IP_ADDR_SIZE 4 + +#define IPV4_ETHTYPE 0x0800 +#define ARP_ETHTYPE 0x0806 +#define ICMP_PROT 1 +#define ECHO_REPLY 0 +#define ECHO_REQUEST 8 +#define ARP_REQUEST 1 +#define ARP_REPLY 2 +#define HTYPE_ETH 1 + +static inline uint16_t ntohs(uint16_t nint) +{ + return ((nint & 0xff) << 8) | ((nint >> 8) & 0xff); +} + +static inline uint16_t htons(uint16_t nint) +{ + return ntohs(nint); +} + +struct eth_header { + uint8_t padding[NET_IP_ALIGN]; + uint8_t dst_mac[MAC_ADDR_SIZE]; + uint8_t src_mac[MAC_ADDR_SIZE]; + uint16_t ethtype; +}; + +struct arp_header { + uint16_t htype; + uint16_t ptype; + uint8_t hlen; + uint8_t plen; + uint16_t oper; + uint8_t sha[MAC_ADDR_SIZE]; + uint8_t spa[IP_ADDR_SIZE]; + uint8_t tha[MAC_ADDR_SIZE]; + uint8_t tpa[IP_ADDR_SIZE]; +}; + +struct ipv4_header { + uint8_t ver_ihl; + uint8_t dscp_ecn; + uint16_t length; + uint16_t ident; + uint16_t flags_frag_off; + uint8_t ttl; + uint8_t prot; + uint16_t cksum; + uint32_t src_addr; + uint32_t dst_addr; +}; + +struct icmp_header { + uint8_t type; + uint8_t code; + uint16_t cksum; + uint32_t rest; +}; + +static int checksum(uint16_t *data, int len) +{ + int i; + uint32_t sum = 0; + + for (i = 0; i < len; i++) + sum += ntohs(data[i]); + + while ((sum >> 16) != 0) + sum = (sum & 0xffff) + (sum >> 16); + + sum = ~sum & 0xffff; + + return sum; +} + +#define ceil_div(n, d) (((n) - 1) / (d) + 1) + +static int process_arp(void *buf, uint8_t *mac) +{ + struct eth_header *eth = buf; + struct arp_header *arp; + size_t size = ETH_HEADER_SIZE + sizeof(*arp); + uint8_t tmp_addr[IP_ADDR_SIZE]; + + // Verify arp packet + arp = buf + sizeof(*eth); + if (ntohs(arp->oper) != ARP_REQUEST) { + printf("Wrong arp operation: %d\n", ntohs(arp->oper)); + return -1; + } + + if (ntohs(arp->htype) != HTYPE_ETH) { + printf("Wrong ARP HTYPE\n"); + return -1; + } + + if (ntohs(arp->ptype) != IPV4_ETHTYPE) { + printf("Wrong ARP PTYPE\n"); + return -1; + } + + if (arp->hlen != 6) { + printf("Wrong ARP HLEN: %d\n", arp->hlen); + return -1; + } + + if (arp->plen != 4) { + printf("Wrong ARP PLEN: %d\n", arp->plen); + return -1; + } + + // Make the source the destination, and add our mac address + memcpy(eth->dst_mac, eth->src_mac, MAC_ADDR_SIZE); + memcpy(eth->src_mac, mac, MAC_ADDR_SIZE); + + // create ARP reply + arp->oper = htons(ARP_REPLY); + + // Make tha the sha, and fill in sha with actual mac address + memcpy(arp->tha, arp->sha, MAC_ADDR_SIZE); + memcpy(arp->sha, mac, MAC_ADDR_SIZE); + + // Swap spa and tpa in arp packet + memcpy(tmp_addr, arp->tpa, IP_ADDR_SIZE); + memcpy(arp->tpa, arp->spa, IP_ADDR_SIZE); + memcpy(arp->spa, tmp_addr, IP_ADDR_SIZE); + + size = ceil_div(size + NET_IP_ALIGN, 8) * 8; + nic_send(buf, size); + + return 0; +} +static int process_icmp(void *buf, uint8_t *mac) +{ + struct eth_header *eth = buf; + struct ipv4_header *ipv4; + struct icmp_header *icmp; + int ihl, icmp_size; + ssize_t size; + uint32_t tmp_addr; + + // verify IPv4 + ipv4 = buf + sizeof(*eth); + ihl = ipv4->ver_ihl & 0xf; + + if (checksum((uint16_t *) ipv4, ihl << 1) != 0) { + printf("Bad IP header checksum %04x\n", ipv4->cksum); + return -1; + } + + if (ipv4->prot != ICMP_PROT) { + printf("Wrong IP protocol %d\n", ipv4->prot); + return -1; + } + + // verify ICMP + icmp = (buf + sizeof(*eth) + (ihl << 2)); + + if (icmp->type != ECHO_REQUEST) { + printf("Wrong ICMP type %d\n", icmp->type); + return -1; + } + + if (icmp->code != 0) { + printf("Wrong ICMP code %d\n", icmp->code); + return -1; + } + + icmp_size = ntohs(ipv4->length) - (ihl << 2); + if (checksum((uint16_t *) icmp, icmp_size >> 1) != 0) { + printf("Bad ICMP checksum %04x\n", icmp->cksum); + return -1; + } + + // Set the destination and source MACs + memcpy(eth->dst_mac, eth->src_mac, MAC_ADDR_SIZE); + memcpy(eth->src_mac, mac, MAC_ADDR_SIZE); + + // Swap the source and destination IP addresses + tmp_addr = ipv4->dst_addr; + ipv4->dst_addr = ipv4->src_addr; + ipv4->src_addr = tmp_addr; + + // compute the IPv4 header checksum + ipv4->cksum = 0; + ipv4->cksum = htons(checksum((uint16_t *) ipv4, ihl << 1)); + + // set the ICMP type to reply and compute checksum + icmp->cksum = 0; + icmp->type = ECHO_REPLY; + icmp->cksum = htons(checksum((uint16_t *) icmp, icmp_size >> 1)); + size = ntohs(ipv4->length) + ETH_HEADER_SIZE; + + size = ceil_div(size + NET_IP_ALIGN, 8) * 8; + nic_send(buf, size); + + return 0; +} + +static int process_packet(void *buf, uint8_t *mac) +{ + struct eth_header *eth; + + // read the ICMP request + nic_recv(buf); + eth = buf; + printf("Got packet: [ethtype=%04x]\n", ntohs(eth->ethtype)); + // Check ethernet type + switch (ntohs(eth->ethtype)) { + case IPV4_ETHTYPE: + return process_icmp(buf, mac); + case ARP_ETHTYPE: + return process_arp(buf, mac); + default: + printf("Wrong ethtype %x\n", ntohs(eth->ethtype)); + return -1; + } +} + +uint64_t buffer[ETH_MAX_WORDS]; + +int main(void) +{ + uint64_t macaddr_long; + uint8_t *macaddr; + + macaddr_long = nic_macaddr(); + macaddr = (uint8_t *) &macaddr_long; + + printf("macaddr - %02x", macaddr[0]); + for (int i = 1; i < MAC_ADDR_SIZE; i++) + printf(":%02x", macaddr[i]); + printf("\n"); + + for (;;) { + if (process_packet(buffer, macaddr)) + return -1; + } + + return 0; +} diff --git a/variables.mk b/variables.mk index 601afb03..d9ce7781 100644 --- a/variables.mk +++ b/variables.mk @@ -44,7 +44,7 @@ ifeq ($(SUB_PROJECT),boom) MODEL ?= TestHarness VLOG_MODEL ?= TestHarness MODEL_PACKAGE ?= boom.system - CONFIG ?= BoomConfig + CONFIG ?= LargeBoomConfig CONFIG_PACKAGE ?= boom.system GENERATOR_PACKAGE ?= boom.system TB ?= TestDriver @@ -52,7 +52,7 @@ ifeq ($(SUB_PROJECT),boom) endif # for Rocket-chip developers ifeq ($(SUB_PROJECT),rocketchip) - SBT_PROJECT ?= chipyardrocketchip + SBT_PROJECT ?= rocketchip MODEL ?= TestHarness VLOG_MODEL ?= TestHarness MODEL_PACKAGE ?= freechips.rocketchip.system @@ -74,6 +74,19 @@ ifeq ($(SUB_PROJECT),hwacha) TB ?= TestDriver TOP ?= ExampleRocketSystem endif +# Stand-in firechip variables: +# TODO: need a seperate generator and test harnesses for each target +#ifeq ($(SUB_PROJECT),firechip) +# SBT_PROJECT ?= $(SUB_PROJECT) +# MODEL ?= TestHarness +# VLOG_MODEL ?= TestHarness +# MODEL_PACKAGE ?= freechips.rocketchip.system +# CONFIG ?= FireSimRocketChipConfig +# CONFIG_PACKAGE ?= firesim.firesim +# GENERATOR_PACKAGE ?= firesim.firesim +# TB ?= TestDriver +# TOP ?= FireSimNoNIC +#endif ######################################################################################### # path to rocket-chip and testchipip