Support FireSim diplomatic multiclock

This commit is contained in:
Jerry Zhao
2020-07-07 17:18:10 -07:00
parent c023cf0688
commit 56e1aeb400
12 changed files with 273 additions and 260 deletions

View File

@@ -58,7 +58,7 @@ class WithBlockDeviceBridge extends OverrideIOBinder({
class WithFASEDBridge extends OverrideIOBinder({
(system: CanHaveMasterAXI4MemPort) => {
implicit val p: Parameters = GetSystemParameters(system)
(system.mem_axi4 zip system.memAXI4Node.in).foreach({ case (axi4, (_, edge)) =>
(system.mem_axi4 zip system.memAXI4Node.edges.in).foreach({ case (axi4, edge) =>
val nastiKey = NastiParameters(axi4.r.bits.data.getWidth,
axi4.ar.bits.addr.getWidth,
axi4.ar.bits.id.getWidth)

View File

@@ -3,13 +3,17 @@
package firesim.firesim
import chisel3._
import chisel3.experimental.{IO}
import freechips.rocketchip.prci._
import freechips.rocketchip.subsystem.{BaseSubsystem, SubsystemDriveAsyncClockGroupsKey}
import freechips.rocketchip.config.{Field, Config, Parameters}
import freechips.rocketchip.diplomacy.{LazyModule}
import freechips.rocketchip.diplomacy.{LazyModule, InModuleBody}
import freechips.rocketchip.util.{ResetCatchAndSync}
import midas.widgets.{Bridge, PeekPokeBridge, RationalClockBridge}
import midas.widgets.{Bridge, PeekPokeBridge, RationalClockBridge, RationalClock}
import chipyard.{BuildSystem}
import chipyard.{BuildSystem, BuildTop, HasHarnessUtils, ChipyardSubsystem, ChipyardClockKey, ChipTop}
import chipyard.iobinders.{IOBinders}
// Determines the number of times to instantiate the DUT in the harness.
@@ -20,6 +24,16 @@ class WithNumNodes(n: Int) extends Config((pname, site, here) => {
case NumNodes => n
})
// 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()))
// Hacky: Set before each node is generated. Ideally we'd give IO binders
// accesses to the the Harness's parameters instance. We could then alter that.
object NodeIdx {
@@ -28,33 +42,108 @@ object NodeIdx {
def apply(): Int = idx
}
class FireSim(implicit val p: Parameters) extends RawModule {
freechips.rocketchip.util.property.cover.setPropLib(new midas.passes.FireSimPropertyLibrary())
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)) {
// It's not a RC bump without some hacks...
// Copy the AsyncClockGroupsKey to generate a fresh node on each
// instantiation of the dut, otherwise the initial instance will be
// reused across each node
import freechips.rocketchip.subsystem.AsyncClockGroupsKey
val lazyModule = p(BuildSystem)(p.alterPartial({
case AsyncClockGroupsKey => p(AsyncClockGroupsKey).copy
}))
(lazyModule, Module(lazyModule.module))
}
class WithFireSimSimpleClocks extends Config((site, here, up) => {
case ChipyardClockKey => { chiptop: ChipTop =>
implicit val p = chiptop.p
val simpleClockGroupSourceNode = ClockGroupSourceNode(Seq(ClockGroupSourceParameters()))
val clockAggregator = LazyModule(new ClockGroupAggregator("clocks"))
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 LazyModule class instance
//
// Apply each partial function to each DUT instance
for ((lazyModule, module) <- targets) {
p(IOBinders).values.foreach(f => f(lazyModule) ++ f(module))
NodeIdx.increment()
// Aggregate all 3 possible clock groups with the clockAggregator
chiptop.systemClockGroup.node := clockAggregator.node
if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) {
chiptop.lSystem match { case l: BaseSubsystem => l.asyncClockGroupsNode := clockAggregator.node }
}
chiptop.lSystem match { case l: ChipyardSubsystem => l.tileClockGroupNode := clockAggregator.node }
clockAggregator.node := simpleClockGroupSourceNode
InModuleBody {
val clock = IO(Input(Clock())).suggestName("clock")
val reset = IO(Input(Reset())).suggestName("reset")
simpleClockGroupSourceNode.out.unzip._1.flatMap(_.member).map { o =>
o.clock := clock
o.reset := reset
}
chiptop.harnessFunctions += ((th: HasHarnessUtils) => {
clock := th.harnessClock
reset := th.harnessReset
Nil
})
}
}
})
class WithFireSimRationalTileDomain(multiplier: Int, divisor: Int) extends Config((site, here, up) => {
case FireSimClockKey => FireSimClockParameters(Seq(RationalClock("TileDomain", multiplier, divisor)))
case ChipyardClockKey => { chiptop: ChipTop =>
implicit val p = chiptop.p
val simpleClockGroupSourceNode = ClockGroupSourceNode(Seq(ClockGroupSourceParameters(), ClockGroupSourceParameters()))
val uncoreClockAggregator = LazyModule(new ClockGroupAggregator("uncore_clocks"))
// Aggregate only the uncoreclocks
chiptop.systemClockGroup.node := uncoreClockAggregator.node
if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) {
chiptop.lSystem match { case l: BaseSubsystem => l.asyncClockGroupsNode := uncoreClockAggregator.node }
}
uncoreClockAggregator.node := simpleClockGroupSourceNode
chiptop.lSystem match {
case l: ChipyardSubsystem => l.tileClockGroupNode := simpleClockGroupSourceNode
case _ => throw new Exception("MultiClock assumes ChipyardSystem")
}
InModuleBody {
val uncore_clock = IO(Input(Clock())).suggestName("uncore_clock")
val tile_clock = IO(Input(Clock())).suggestName("tile_clock")
val reset = IO(Input(Reset())).suggestName("reset")
simpleClockGroupSourceNode.out(0)._1.member.map { o =>
o.clock := uncore_clock
o.reset := reset
}
simpleClockGroupSourceNode.out(1)._1.member.map { o =>
o.clock := tile_clock
o.reset := ResetCatchAndSync(tile_clock, reset.asBool)
}
chiptop.harnessFunctions += ((th: HasHarnessUtils) => {
uncore_clock := th.harnessClock
reset := th.harnessReset
th match {
case f: FireSim => tile_clock := f.additionalClocks(0)
case _ => throw new Exception("FireSimMultiClock must be used with FireSim")
}
Nil
})
}
}
})
class FireSim(implicit val p: Parameters) extends RawModule with HasHarnessUtils {
freechips.rocketchip.util.property.cover.setPropLib(new midas.passes.FireSimPropertyLibrary())
val clockBridge = Module(new RationalClockBridge(p(FireSimClockKey).additionalClocks:_*))
val harnessClock = clockBridge.io.clocks.head // This is the reference clock
val additionalClocks = clockBridge.io.clocks.tail
val harnessReset = WireInit(false.B)
val peekPokeBridge = PeekPokeBridge(harnessClock, harnessReset)
val dutReset = false.B // unused (if used, its a bug)
val success = false.B // unused (if used, its a bug)
// Instantiate multiple instances of the DUT to implement supernode
for (i <- 0 until p(NumNodes)) {
// It's not a RC bump without some hacks...
// Copy the AsyncClockGroupsKey to generate a fresh node on each
// instantiation of the dut, otherwise the initial instance will be
// reused across each node
import freechips.rocketchip.subsystem.AsyncClockGroupsKey
val lazyModule = LazyModule(p(BuildTop)(p.alterPartial({
case AsyncClockGroupsKey => p(AsyncClockGroupsKey).copy
})))
val module = Module(lazyModule.module)
require(lazyModule.harnessFunctions.size == 1, "There should only be 1 harness function to connect clock+reset")
lazyModule.harnessFunctions.foreach(_(this))
NodeIdx.increment()
}
}

View File

@@ -1,107 +0,0 @@
//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 midas.widgets.{Bridge, PeekPokeBridge, RationalClockBridge, RationalClock}
import firesim.configs._
import boom.common.{WithRationalBoomTiles}
import chipyard.{BuildSystem, DigitalTop, DigitalTopModule}
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(
new WithRationalRocketTiles ++
new WithRationalBoomTiles ++
new Config((site, here, up) => {
case FireSimClockKey => FireSimClockParameters(Seq(RationalClock("TileDomain", multiplier, divisor)))
})
)
class HalfRateUncore extends WithSingleRationalTileDomain(2,1)
class WithFiresimMulticlockTop extends Config((site, here, up) => {
case BuildSystem => (p: Parameters) => LazyModule(new FiresimMulticlockTop()(p)).suggestName("system")
})
// Complete Config
class FireSimQuadRocketMulticlockConfig extends Config(
new HalfRateUncore ++
new WithFiresimMulticlockTop ++
new FireSimQuadRocketConfig)
// Top Definition
class FiresimMulticlockTop(implicit p: Parameters) extends chipyard.DigitalTop
{
override lazy val module = new FiresimMulticlockTopModule(this)
}
class FiresimMulticlockTopModule[+L <: DigitalTop](l: L) extends chipyard.DigitalTopModule(l) with HasFireSimClockingImp
// Harness Definition
class FireSimMulticlockPOC(implicit val p: Parameters) extends RawModule {
freechips.rocketchip.util.property.cover.setPropLib(new midas.passes.FireSimPropertyLibrary())
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)) {
val lazyModule = p(BuildSystem)(p)
(lazyModule, Module(lazyModule.module))
}
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 ((lazyModule, module) <- targets) {
p(IOBinders).values.foreach(f => f(lazyModule) ++ f(module))
}
targets.collect({ case (_, t: HasAdditionalClocks) => t.clocks := clockBridge.io.clocks })
}
}

View File

@@ -22,7 +22,6 @@ import testchipip.WithRingSystemBus
import firesim.bridges._
import firesim.configs._
import chipyard.config.ConfigValName._
class WithBootROM extends Config((site, here, up) => {
case BootROMParams => {
@@ -67,6 +66,8 @@ class WithNVDLASmall extends nvidia.blocks.dla.WithNVDLA("small")
// Tweaks that are generally applied to all firesim configs
class WithFireSimConfigTweaks extends Config(
// Required*: Uses FireSim ClockBridge and PeekPokeBridge to drive the system with a single clock/reset
new WithFireSimSimpleClocks ++
// 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
@@ -170,3 +171,12 @@ class FireSimArianeConfig extends Config(
new WithDefaultMemModel ++
new WithFireSimConfigTweaks ++
new chipyard.ArianeConfig)
class FireSimMulticlockRocketConfig extends Config(
new WithFireSimRationalTileDomain(2, 1) ++
new WithDefaultFireSimBridges ++
new WithDefaultMemModel ++
new WithFireSimConfigTweaks ++
new chipyard.MultiClockRocketConfig)

View File

@@ -106,8 +106,8 @@ class BoomF1Tests extends FireSimTestSuite("FireSim", "DDR3FRFCFSLLC4MB_FireSimL
class RocketNICF1Tests extends FireSimTestSuite("FireSim", "WithNIC_DDR3FRFCFSLLC4MB_FireSimRocketConfig", "BaseF1Config")
// Multiclock tests
class RocketMulticlockF1Tests extends FireSimTestSuite(
"FireSimMulticlockPOC",
"FireSimQuadRocketMulticlockConfig",
"FireSim",
"FireSimMulticlockRocketConfig",
"WithSynthAsserts_BaseF1Config")
class ArianeF1Tests extends FireSimTestSuite("FireSim", "WithNIC_DDR3FRFCFSLLC4MB_FireSimArianeConfig", "BaseF1Config")