From c0d4e848bae02d3e6a7211094831c0bef16caaf0 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Mon, 27 May 2019 22:53:05 +0000 Subject: [PATCH] WIP --- .gitmodules | 6 + build.sbt | 84 ++++-- common.mk | 12 +- .../firechip/src/main/scala/Generator.scala | 124 +++++++++ .../firechip/src/main/scala/SimConfigs.scala | 54 ++++ .../src/main/scala/TargetConfigs.scala | 205 ++++++++++++++ .../src/main/scala/TargetLandTestSuites.scala | 45 ++++ .../src/main/scala/TargetMixins.scala | 131 +++++++++ .../firechip/src/main/scala/Targets.scala | 176 ++++++++++++ .../src/test/scala/ScalaTestSuite.scala | 138 ++++++++++ generators/icenet | 1 + sims/firesim | 1 + tests/big-blkdev.c | 79 ++++++ tests/nic-loopback.c | 98 +++++++ tests/nic.h | 8 +- tests/pingd.c | 254 ++++++++++++++++++ variables.mk | 12 + 17 files changed, 1399 insertions(+), 29 deletions(-) create mode 100755 generators/firechip/src/main/scala/Generator.scala create mode 100644 generators/firechip/src/main/scala/SimConfigs.scala create mode 100644 generators/firechip/src/main/scala/TargetConfigs.scala create mode 100644 generators/firechip/src/main/scala/TargetLandTestSuites.scala create mode 100644 generators/firechip/src/main/scala/TargetMixins.scala create mode 100755 generators/firechip/src/main/scala/Targets.scala create mode 100644 generators/firechip/src/test/scala/ScalaTestSuite.scala create mode 160000 generators/icenet create mode 160000 sims/firesim create mode 100644 tests/big-blkdev.c create mode 100644 tests/nic-loopback.c create mode 100644 tests/pingd.c diff --git a/.gitmodules b/.gitmodules index 698c53b..a3fa357 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,9 @@ [submodule "generators/hwacha"] path = generators/hwacha url = git@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 diff --git a/build.sbt b/build.sbt index 6a06fcb..6e52c68 100644 --- a/build.sbt +++ b/build.sbt @@ -9,9 +9,9 @@ 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 += "edu.berkeley.cs" %% "firrtl-interpreter" % "1.2-SNAPSHOT", libraryDependencies += "com.github.scopt" %% "scopt" % "3.7.0", addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full), resolvers ++= Seq( @@ -19,18 +19,14 @@ lazy val commonSettings = Seq( Resolver.sonatypeRepo("releases"), Resolver.mavenLocal)) -lazy val rebarFirrtl = (project in file("tools/firrtl")) - .settings(commonSettings) +val rocketChipDir = file("generators/rocket-chip") -lazy val rocketchip = RootProject(file("generators/rocket-chip")) - -lazy val rebarrocketchip = project - .dependsOn(rocketchip) - .settings(commonSettings) - -lazy val testchipip = (project in file("generators/testchipip")) - .dependsOn(rebarrocketchip) - .settings(commonSettings) +lazy val firesimAsLibrary = sys.env.get("FIRESIM_IS_TOP") == None +lazy val firesimDir = if (firesimAsLibrary) { + file("sims/firesim/sim/") +} else { + file("../../sim/") +} // Checks for -DROCKET_USE_MAVEN. // If it's there, use a maven dependency. @@ -45,6 +41,42 @@ def conditionalDependsOn(prj: Project): Project = { } } +// Subproject definitions begin + +// Biancolin: get to the bottom of these +//lazy val rebarFirrtl = (project in file("tools/firrtl")) +// .settings(commonSettings) +// Overlaps with the dependency-injected version +// lazy val rocketchip = RootProject(rocketChipDir) + +// 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 rebarRocketchip = (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(rebarRocketchip) + .settings(commonSettings) + lazy val example = conditionalDependsOn(project in file("generators/example")) .dependsOn(boom, hwacha, sifive_blocks) .settings(commonSettings) @@ -52,26 +84,38 @@ lazy val example = conditionalDependsOn(project in file("generators/example")) lazy val utilities = conditionalDependsOn(project in file("generators/utilities")) .settings(commonSettings) -lazy val hwacha = (project in file ("generators/hwacha")) - .dependsOn(rebarrocketchip) +lazy val icenet = (project in file("generators/icenet")) + .dependsOn(rebarRocketchip, testchipip) + .settings(commonSettings) + +lazy val hwacha = (project in file("generators/hwacha")) + .dependsOn(rebarRocketchip) .settings(commonSettings) lazy val boom = (project in file("generators/boom")) - .dependsOn(rebarrocketchip) + .dependsOn(rebarRocketchip) .settings(commonSettings) lazy val tapeout = conditionalDependsOn(project in file("./tools/barstools/tapeout/")) - .dependsOn(rebarFirrtl) .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, rebarrocketchip, rebarFirrtl) +lazy val barstoolsMacros = (project in file("./tools/barstools/macros/")) + .dependsOn(mdf, rebarRocketchip) .enablePlugins(sbtassembly.AssemblyPlugin) .settings(commonSettings) lazy val sifive_blocks = (project in file("generators/sifive-blocks")) - .dependsOn(rebarrocketchip) + .dependsOn(rebarRocketchip) .settings(commonSettings) + +// 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, midasTargetUtils, midas, firesimLib % "test->test;compile->compile") + .settings(commonSettings) + diff --git a/common.mk b/common.mk index f7e4660..37e2523 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 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, $(REBAR_FIRRTL_DIR)/src/main/scala) $(MAKE) -C $(REBAR_FIRRTL_DIR) SBT="$(SBT)" root_dir=$(REBAR_FIRRTL_DIR) build-scala - mkdir -p $(dir $@) + mkdir -p $(@D) cp -p $(REBAR_FIRRTL_DIR)/utils/bin/firrtl.jar $@ touch $@ diff --git a/generators/firechip/src/main/scala/Generator.scala b/generators/firechip/src/main/scala/Generator.scala new file mode 100755 index 0000000..56ae824 --- /dev/null +++ b/generators/firechip/src/main/scala/Generator.scala @@ -0,0 +1,124 @@ +//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 +} diff --git a/generators/firechip/src/main/scala/SimConfigs.scala b/generators/firechip/src/main/scala/SimConfigs.scala new file mode 100644 index 0000000..1118fe0 --- /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 0000000..5c1cade --- /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 rebarBootROM = new File(s"./generators/testchipip/bootrom/bootrom.rv${site(XLen)}.img") + val firesimBootROM = new File(s"./target-rtl/testchipip/bootrom/bootrom.rv${site(XLen)}.img") + + val bootROMPath = if (rebarBootROM.exists()) { + rebarBootROM.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 0000000..b20fa8d --- /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 0000000..1c38ce2 --- /dev/null +++ b/generators/firechip/src/main/scala/TargetMixins.scala @@ -0,0 +1,131 @@ +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 boom.system.BoomSubsystem + +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 100755 index 0000000..3a565c6 --- /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.{BoomSubsystem, BoomSubsystemModuleImp} +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 BoomSubsystem + 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 BoomSubsystemModuleImp(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 BoomSubsystem + 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 BoomSubsystemModuleImp(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 0000000..588c7c2 --- /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.midasexamples.TestSuiteCommon with HasFireSimGeneratorUtilities { + 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 0000000..bba264d --- /dev/null +++ b/generators/icenet @@ -0,0 +1 @@ +Subproject commit bba264d68d366180f6f9b55061ee9408425d8229 diff --git a/sims/firesim b/sims/firesim new file mode 160000 index 0000000..ccfd85a --- /dev/null +++ b/sims/firesim @@ -0,0 +1 @@ +Subproject commit ccfd85aa287bd158b87ab9160cb599ec7534553b diff --git a/tests/big-blkdev.c b/tests/big-blkdev.c new file mode 100644 index 0000000..d065217 --- /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/nic-loopback.c b/tests/nic-loopback.c new file mode 100644 index 0000000..1d3c7b9 --- /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 55a86a2..bee4c39 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 0000000..9e02caf --- /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 0808003..ea48c81 100644 --- a/variables.mk +++ b/variables.mk @@ -86,6 +86,18 @@ ifeq ($(SUB_PROJECT),hwacha) TB ?= TestDriver TOP ?= ExampleRocketSystem endif +# Stand-in firechip variables +ifeq ($(SUB_PROJECT),firechip) + SBT_PROJECT ?= $(SUB_PROJECT) + MODEL ?= TestHarness + VLOG_MODEL ?= TestHarness + MODEL_PACKAGE ?= firesim.firesim + CONFIG ?= FireSimRocketChipConfig + CONFIG_PACKAGE ?= firesim.firesim + GENERATOR_PACKAGE ?= firesim.firesim + TB ?= TestDriver + TOP ?= ExampleRocketSystem +endif ######################################################################################### # path to rocket-chip and testchipip