Merge pull request #468 from ucb-bar/firesim-multiclock
Target-Facing Support for Multiclock Simulation in FireSim
This commit is contained in:
@@ -204,5 +204,6 @@ lazy val firechip = conditionalDependsOn(project in file("generators/firechip"))
|
||||
.dependsOn(chipyard, midasTargetUtils, midas, firesimLib % "test->test;compile->compile")
|
||||
.settings(
|
||||
commonSettings,
|
||||
testGrouping in Test := isolateAllTests( (definedTests in Test).value )
|
||||
testGrouping in Test := isolateAllTests( (definedTests in Test).value ),
|
||||
testOptions in Test += Tests.Argument("-oF")
|
||||
)
|
||||
|
||||
@@ -29,19 +29,23 @@ import chipyard.iobinders.{IOBinders, OverrideIOBinder, ComposeIOBinder}
|
||||
import chipyard.HasChipyardTilesModuleImp
|
||||
|
||||
class WithSerialBridge extends OverrideIOBinder({
|
||||
(c, r, s, target: CanHavePeripherySerialModuleImp) => target.serial.map(s => SerialBridge(s)(target.p)).toSeq
|
||||
(c, r, s, target: CanHavePeripherySerialModuleImp) =>
|
||||
target.serial.map(s => SerialBridge(target.clock, s)(target.p)).toSeq
|
||||
})
|
||||
|
||||
class WithNICBridge extends OverrideIOBinder({
|
||||
(c, r, s, target: CanHavePeripheryIceNICModuleImp) => target.net.map(n => NICBridge(n)(target.p)).toSeq
|
||||
(c, r, s, target: CanHavePeripheryIceNICModuleImp) =>
|
||||
target.net.map(n => NICBridge(target.clock, n)(target.p)).toSeq
|
||||
})
|
||||
|
||||
class WithUARTBridge extends OverrideIOBinder({
|
||||
(c, r, s, target: HasPeripheryUARTModuleImp) => target.uart.map(u => UARTBridge(u)(target.p)).toSeq
|
||||
(c, r, s, target: HasPeripheryUARTModuleImp) =>
|
||||
target.uart.map(u => UARTBridge(target.clock, u)(target.p)).toSeq
|
||||
})
|
||||
|
||||
class WithBlockDeviceBridge extends OverrideIOBinder({
|
||||
(c, r, s, target: CanHavePeripheryBlockDeviceModuleImp) => target.bdev.map(b => BlockDevBridge(b, target.reset.toBool)(target.p)).toSeq
|
||||
(c, r, s, target: CanHavePeripheryBlockDeviceModuleImp) =>
|
||||
target.bdev.map(b => BlockDevBridge(target.clock, b, target.reset.toBool)(target.p)).toSeq
|
||||
})
|
||||
|
||||
class WithFASEDBridge extends OverrideIOBinder({
|
||||
@@ -52,7 +56,7 @@ class WithFASEDBridge extends OverrideIOBinder({
|
||||
val nastiKey = NastiParameters(axi4Bundle.r.bits.data.getWidth,
|
||||
axi4Bundle.ar.bits.addr.getWidth,
|
||||
axi4Bundle.ar.bits.id.getWidth)
|
||||
FASEDBridge(axi4Bundle, t.reset.toBool,
|
||||
FASEDBridge(t.clock, axi4Bundle, t.reset.toBool,
|
||||
CompleteConfig(p(firesim.configs.MemModelKey), nastiKey, Some(AXI4EdgeSummary(edge))))
|
||||
})
|
||||
}).toSeq
|
||||
@@ -60,11 +64,16 @@ class WithFASEDBridge extends OverrideIOBinder({
|
||||
})
|
||||
|
||||
class WithTracerVBridge extends OverrideIOBinder({
|
||||
(c, r, s, target: CanHaveTraceIOModuleImp) => target.traceIO.map(t => TracerVBridge(t)(target.p)).toSeq
|
||||
(c, r, s, target: CanHaveTraceIOModuleImp) => target.traceIO match {
|
||||
case Some(t) => t.traces.map(tileTrace => TracerVBridge(tileTrace)(target.p))
|
||||
case None => Nil
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
class WithTraceGenBridge extends OverrideIOBinder({
|
||||
(c, r, s, target: HasTraceGenTilesModuleImp) => Seq(GroundTestBridge(target.success)(target.p))
|
||||
(c, r, s, target: HasTraceGenTilesModuleImp) =>
|
||||
Seq(GroundTestBridge(target.clock, target.success)(target.p))
|
||||
})
|
||||
|
||||
class WithFireSimMultiCycleRegfile extends ComposeIOBinder({
|
||||
|
||||
@@ -7,7 +7,7 @@ import chisel3._
|
||||
import freechips.rocketchip.config.{Field, Config, Parameters}
|
||||
import freechips.rocketchip.diplomacy.{LazyModule}
|
||||
|
||||
import midas.widgets.{Bridge, PeekPokeBridge}
|
||||
import midas.widgets.{Bridge, PeekPokeBridge, RationalClockBridge}
|
||||
|
||||
import chipyard.{BuildTop}
|
||||
import chipyard.iobinders.{IOBinders}
|
||||
@@ -21,12 +21,13 @@ class WithNumNodes(n: Int) extends Config((pname, site, here) => {
|
||||
})
|
||||
|
||||
class FireSim(implicit val p: Parameters) extends RawModule {
|
||||
val clock = IO(Input(Clock()))
|
||||
val clockBridge = Module(new RationalClockBridge)
|
||||
val clock = clockBridge.io.clocks.head
|
||||
val reset = WireInit(false.B)
|
||||
withClockAndReset(clock, reset) {
|
||||
// Instantiate multiple instances of the DUT to implement supernode
|
||||
val targets = Seq.fill(p(NumNodes))(p(BuildTop)(p))
|
||||
val peekPokeBridge = PeekPokeBridge(reset)
|
||||
val peekPokeBridge = PeekPokeBridge(clock, reset)
|
||||
// A Seq of partial functions that will instantiate the right bridge only
|
||||
// if that Mixin trait is present in the target's class instance
|
||||
//
|
||||
|
||||
104
generators/firechip/src/main/scala/FireSimMulticlockPOC.scala
Normal file
104
generators/firechip/src/main/scala/FireSimMulticlockPOC.scala
Normal file
@@ -0,0 +1,104 @@
|
||||
//See LICENSE for license details.
|
||||
|
||||
package firesim.firesim
|
||||
|
||||
import chisel3._
|
||||
|
||||
import freechips.rocketchip.config.{Field, Config, Parameters}
|
||||
import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImp, RationalCrossing}
|
||||
import freechips.rocketchip.subsystem._
|
||||
import freechips.rocketchip.util.{ResetCatchAndSync}
|
||||
|
||||
import boom.common.{BoomTilesKey, BoomCrossingKey}
|
||||
|
||||
import midas.widgets.{Bridge, PeekPokeBridge, RationalClockBridge, RationalClock}
|
||||
import firesim.configs._
|
||||
|
||||
import chipyard.{BuildTop, Top, TopModule}
|
||||
import chipyard.config.ConfigValName._
|
||||
import chipyard.iobinders.{IOBinders}
|
||||
|
||||
// WIP! This file is a sketch of one means of defining a multiclock target-design
|
||||
// that can be simulated in FireSim, pending a canonicalized form in Chipyard.
|
||||
//
|
||||
// Note, the main prerequisite for supporting an additional clock domain in a
|
||||
// FireSim simulation is to supply an additional clock parameter
|
||||
// (RationalClock) to the clock bridge (RationalClockBridge). The bridge
|
||||
// produces a vector of clocks, based on the provided parameter list, which you
|
||||
// may use freely without further modifications to your target design.
|
||||
|
||||
case class FireSimClockParameters(additionalClocks: Seq[RationalClock]) {
|
||||
def numClocks(): Int = additionalClocks.size + 1
|
||||
}
|
||||
case object FireSimClockKey extends Field[FireSimClockParameters](FireSimClockParameters(Seq()))
|
||||
|
||||
trait HasAdditionalClocks extends LazyModuleImp {
|
||||
val clocks = IO(Vec(p(FireSimClockKey).numClocks, Input(Clock())))
|
||||
}
|
||||
|
||||
// Presupposes only 1 or 2 clocks.
|
||||
trait HasFireSimClockingImp extends HasAdditionalClocks {
|
||||
val outer: HasTiles
|
||||
val (tileClock, tileReset) = p(FireSimClockKey).additionalClocks.headOption match {
|
||||
case Some(RationalClock(_, numer, denom)) if numer != denom => (clocks(1), ResetCatchAndSync(clocks(1), reset.toBool))
|
||||
case None => (clocks.head, reset)
|
||||
}
|
||||
|
||||
outer.tiles.foreach({ case tile =>
|
||||
tile.module.clock := tileClock
|
||||
tile.module.reset := tileReset
|
||||
})
|
||||
}
|
||||
|
||||
// Config Fragment
|
||||
class WithSingleRationalTileDomain(multiplier: Int, divisor: Int) extends Config((site, here, up) => {
|
||||
case FireSimClockKey => FireSimClockParameters(Seq(RationalClock("TileDomain", multiplier, divisor)))
|
||||
case RocketCrossingKey => up(RocketCrossingKey, site) map { r =>
|
||||
r.copy(crossingType = RationalCrossing())
|
||||
}
|
||||
case BoomCrossingKey => up(BoomCrossingKey, site) map { r =>
|
||||
r.copy(crossingType = RationalCrossing())
|
||||
}
|
||||
})
|
||||
|
||||
class HalfRateUncore extends WithSingleRationalTileDomain(2,1)
|
||||
|
||||
class WithFiresimMulticlockTop extends Config((site, here, up) => {
|
||||
case BuildTop => (p: Parameters) => Module(LazyModule(new FiresimMulticlockTop()(p)).suggestName("Top").module)
|
||||
})
|
||||
|
||||
// Complete Config
|
||||
class FireSimQuadRocketMulticlockConfig extends Config(
|
||||
new HalfRateUncore ++
|
||||
new WithFiresimMulticlockTop ++
|
||||
new FireSimQuadRocketConfig)
|
||||
|
||||
// Top Definition
|
||||
class FiresimMulticlockTop(implicit p: Parameters) extends chipyard.Top
|
||||
{
|
||||
override lazy val module = new FiresimMulticlockTopModule(this)
|
||||
}
|
||||
|
||||
class FiresimMulticlockTopModule[+L <: Top](l: L) extends chipyard.TopModule(l) with HasFireSimClockingImp
|
||||
|
||||
// Harness Definition
|
||||
class FireSimMulticlockPOC(implicit val p: Parameters) extends RawModule {
|
||||
val clockBridge = Module(new RationalClockBridge(p(FireSimClockKey).additionalClocks:_*))
|
||||
val refClock = clockBridge.io.clocks.head
|
||||
val reset = WireInit(false.B)
|
||||
withClockAndReset(refClock, reset) {
|
||||
// Instantiate multiple instances of the DUT to implement supernode
|
||||
val targets = Seq.fill(p(NumNodes))(p(BuildTop)(p))
|
||||
val peekPokeBridge = PeekPokeBridge(refClock, reset)
|
||||
// A Seq of partial functions that will instantiate the right bridge only
|
||||
// if that Mixin trait is present in the target's class instance
|
||||
//
|
||||
// Apply each partial function to each DUT instance
|
||||
for ((target) <- targets) {
|
||||
p(IOBinders).values.map(fn => fn(refClock, reset.asBool, false.B, target))
|
||||
}
|
||||
targets.collect({ case t: HasAdditionalClocks => t.clocks := clockBridge.io.clocks })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,13 +84,25 @@ class WithTraceIO extends Config((site, here, up) => {
|
||||
|
||||
// Tweaks that are generally applied to all firesim configs
|
||||
class WithFireSimConfigTweaks extends Config(
|
||||
new WithBootROM ++ // needed to support FireSim-as-top
|
||||
new WithPeripheryBusFrequency(BigInt(3200000000L)) ++ // 3.2 GHz
|
||||
// Required*: When using FireSim-as-top to provide a correct path to the target bootrom source
|
||||
new WithBootROM ++
|
||||
// Optional*: Removing this will require target-software changes to properly capture UART output
|
||||
new WithPeripheryBusFrequency(BigInt(3200000000L)) ++
|
||||
// Required: Existing FAME-1 transform cannot handle black-box clock gates
|
||||
new WithoutClockGating ++
|
||||
// Required*: Removes thousands of assertions that would be synthesized (* pending PriorityMux bugfix)
|
||||
new WithoutTLMonitors ++
|
||||
// Optional: Adds IO to attach tracerV bridges
|
||||
new WithTraceIO ++
|
||||
new freechips.rocketchip.subsystem.WithExtMemSize((1 << 30) * 16L) ++ // 16 GB
|
||||
// Optional: Request 16 GiB of target-DRAM by default (can safely request up to 32 GiB on F1)
|
||||
new freechips.rocketchip.subsystem.WithExtMemSize((1 << 30) * 16L) ++
|
||||
// Required: Adds IO to attach SerialBridge. The SerialBridges is responsible
|
||||
// for signalling simulation termination under simulation success. This fragment can
|
||||
// be removed if you supply an auxiliary bridge that signals simulation termination
|
||||
new testchipip.WithTSI ++
|
||||
// Optional: Removing this will require using an initramfs under linux
|
||||
new testchipip.WithBlockDevice ++
|
||||
// Required*:
|
||||
new chipyard.config.WithUART
|
||||
)
|
||||
|
||||
|
||||
@@ -106,14 +106,19 @@ abstract class FireSimTestSuite(
|
||||
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 = 51
|
||||
val verilatedOutput = getLines(new File(outDir, s"/${verilatedLog}"))
|
||||
val synthPrintOutput = getLines(new File(genDir, s"/TRACEFILE"), resetLength)
|
||||
assert(math.abs(verilatedOutput.size - synthPrintOutput.size) <= 1, "Outputs differ in length")
|
||||
def getLines(file: File): Seq[String] = Source.fromFile(file).getLines.toList
|
||||
|
||||
val printfPrefix = "TRACEPORT 0: "
|
||||
val verilatedOutput = getLines(new File(outDir, s"/${verilatedLog}")).collect({
|
||||
case line if line.startsWith(printfPrefix) => line.stripPrefix(printfPrefix) })
|
||||
|
||||
// Last bit indicates the core was under reset; reject those tokens
|
||||
// Tail to drop the first token which is initialized in the channel
|
||||
val synthPrintOutput = getLines(new File(genDir, s"/TRACEFILE")).tail.filter(line =>
|
||||
(line.last.toInt & 1) == 0)
|
||||
|
||||
assert(math.abs(verilatedOutput.size - synthPrintOutput.size) <= 1,
|
||||
s"\nPrintf Length: ${verilatedOutput.size}, Trace Length: ${synthPrintOutput.size}")
|
||||
assert(verilatedOutput.nonEmpty)
|
||||
for ( (vPrint, sPrint) <- verilatedOutput.zip(synthPrintOutput) ) {
|
||||
assert(vPrint == sPrint)
|
||||
@@ -125,51 +130,59 @@ abstract class FireSimTestSuite(
|
||||
mkdirs
|
||||
elaborate
|
||||
generateTestSuiteMakefrags
|
||||
runTest("verilator", "rv64ui-p-simple", false, Seq(s"""EXTRA_SIM_ARGS=+trace-test-output0"""))
|
||||
diffTracelog("rv64ui-p-simple.out")
|
||||
runTest("verilator", "rv64ui-p-simple", false, Seq(s"""EXTRA_SIM_ARGS=+trace-humanreadable0"""))
|
||||
//diffTracelog("rv64ui-p-simple.out")
|
||||
runSuite("verilator")(benchmarks)
|
||||
runSuite("verilator")(FastBlockdevTests)
|
||||
}
|
||||
|
||||
class RocketF1Tests extends FireSimTestSuite("FireSimNoNIC", "DDR3FRFCFSLLC4MB_FireSimRocketChipQuadCoreConfig", "BaseF1Config")
|
||||
class BoomF1Tests extends FireSimTestSuite("FireSimNoNIC", "DDR3FRFCFSLLC4MB_FireSimBoomConfig", "BaseF1Config")
|
||||
class RocketNICF1Tests extends FireSimTestSuite("FireSim", "DDR3FRFCFSLLC4MB_FireSimRocketChipConfig", "BaseF1Config") {
|
||||
class RocketF1Tests extends FireSimTestSuite("FireSim", "DDR3FRFCFSLLC4MB_FireSimQuadRocketConfig", "WithSynthAsserts_BaseF1Config")
|
||||
class BoomF1Tests extends FireSimTestSuite("FireSim", "DDR3FRFCFSLLC4MB_FireSimBoomConfig", "BaseF1Config")
|
||||
class RocketNICF1Tests extends FireSimTestSuite("FireSim", "WithNIC_DDR3FRFCFSLLC4MB_FireSimRocketConfig", "BaseF1Config") {
|
||||
runSuite("verilator")(NICLoopbackTests)
|
||||
}
|
||||
class RamModelRocketF1Tests extends FireSimTestSuite("FireSimNoNIC", "FireSimRocketChipDualCoreConfig", "BaseF1Config_MCRams")
|
||||
class RamModelBoomF1Tests extends FireSimTestSuite("FireSimNoNIC", "FireSimBoomConfig", "BaseF1Config_MCRams")
|
||||
// Disabled until RAM optimizations re-enabled in multiclock
|
||||
//class RamModelRocketF1Tests extends FireSimTestSuite("FireSim", "FireSimDualRocketConfig", "BaseF1Config_MCRams")
|
||||
//class RamModelBoomF1Tests extends FireSimTestSuite("FireSim", "FireSimBoomConfig", "BaseF1Config_MCRams")
|
||||
|
||||
abstract class FireSimTraceGenTest(targetConfig: String, platformConfig: String)
|
||||
extends firesim.TestSuiteCommon with IsFireSimGeneratorLike {
|
||||
val longName = names.topModuleProject + "." + names.topModuleClass + "." + names.configs
|
||||
// Multiclock tests
|
||||
class RocketMulticlockF1Tests extends FireSimTestSuite(
|
||||
"FireSimMulticlockPOC",
|
||||
"FireSimQuadRocketMulticlockConfig",
|
||||
"WithSynthAsserts_BaseF1Config")
|
||||
|
||||
lazy val generatorArgs = GeneratorArgs(
|
||||
midasFlowKind = "midas",
|
||||
targetDir = "generated-src",
|
||||
topModuleProject = "firesim.firesim",
|
||||
topModuleClass = "FireSimTraceGen",
|
||||
targetConfigProject = "firesim.firesim",
|
||||
targetConfigs = targetConfig ++ "_WithScalaTestFeatures",
|
||||
platformConfigProject = "firesim.firesim",
|
||||
platformConfigs = platformConfig)
|
||||
|
||||
// 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}")
|
||||
|
||||
it should "pass" in {
|
||||
assert(make("fsim-tracegen") == 0)
|
||||
}
|
||||
}
|
||||
|
||||
class FireSimLLCTraceGenTest extends FireSimTraceGenTest(
|
||||
"DDR3FRFCFSLLC4MB_FireSimTraceGenConfig", "BaseF1Config")
|
||||
|
||||
class FireSimL2TraceGenTest extends FireSimTraceGenTest(
|
||||
"DDR3FRFCFS_FireSimTraceGenL2Config", "BaseF1Config")
|
||||
// Jerry broke these -- damn it Jerry.
|
||||
//abstract class FireSimTraceGenTest(targetConfig: String, platformConfig: String)
|
||||
// extends firesim.TestSuiteCommon with IsFireSimGeneratorLike {
|
||||
// val longName = names.topModuleProject + "." + names.topModuleClass + "." + names.configs
|
||||
//
|
||||
// lazy val generatorArgs = GeneratorArgs(
|
||||
// midasFlowKind = "midas",
|
||||
// targetDir = "generated-src",
|
||||
// topModuleProject = "firesim.firesim",
|
||||
// topModuleClass = "FireSimTraceGen",
|
||||
// targetConfigProject = "firesim.firesim",
|
||||
// targetConfigs = targetConfig ++ "_WithScalaTestFeatures",
|
||||
// platformConfigProject = "firesim.firesim",
|
||||
// platformConfigs = platformConfig)
|
||||
//
|
||||
// // 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}")
|
||||
//
|
||||
// it should "pass" in {
|
||||
// assert(make("fsim-tracegen") == 0)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//class FireSimLLCTraceGenTest extends FireSimTraceGenTest(
|
||||
// "DDR3FRFCFSLLC4MB_FireSimTraceGenConfig", "BaseF1Config")
|
||||
//
|
||||
//class FireSimL2TraceGenTest extends FireSimTraceGenTest(
|
||||
// "DDR3FRFCFS_FireSimTraceGenL2Config", "BaseF1Config")
|
||||
|
||||
Submodule generators/testchipip updated: 3a83cd0a40...30d44252e8
Submodule sims/firesim updated: ce0d05a10a...3cf0d45e07
Reference in New Issue
Block a user