diff --git a/fpga/src/main/scala/arty/TestHarness.scala b/fpga/src/main/scala/arty/TestHarness.scala index db7ddc01..fdb91a47 100644 --- a/fpga/src/main/scala/arty/TestHarness.scala +++ b/fpga/src/main/scala/arty/TestHarness.scala @@ -7,7 +7,7 @@ import freechips.rocketchip.config.{Parameters} import sifive.fpgashells.shell.xilinx.artyshell.{ArtyShell} -import chipyard.{BuildTop, HasHarnessSignalReferences, HasTestHarnessFunctions} +import chipyard.{BuildTop, HasHarnessSignalReferences} import chipyard.harness.{ApplyHarnessBinders} import chipyard.iobinders.{HasIOBinders} @@ -34,9 +34,6 @@ class ArtyFPGATestHarness(override implicit val p: Parameters) extends ArtyShell val dutReset = dReset // must be after HasHarnessSignalReferences assignments - lazyDut match { case d: HasTestHarnessFunctions => - d.harnessFunctions.foreach(_(this)) - } lazyDut match { case d: HasIOBinders => ApplyHarnessBinders(this, d.lazySystem, d.portMap) } diff --git a/fpga/src/main/scala/vcu118/TestHarness.scala b/fpga/src/main/scala/vcu118/TestHarness.scala index 33161b68..ab6897c9 100644 --- a/fpga/src/main/scala/vcu118/TestHarness.scala +++ b/fpga/src/main/scala/vcu118/TestHarness.scala @@ -17,7 +17,7 @@ import sifive.blocks.devices.uart._ import sifive.blocks.devices.spi._ import sifive.blocks.devices.gpio._ -import chipyard.{HasHarnessSignalReferences, HasTestHarnessFunctions, BuildTop, ChipTop, ExtTLMem, CanHaveMasterTLMemPort, DefaultClockFrequencyKey, HasReferenceClockFreq} +import chipyard.{HasHarnessSignalReferences, BuildTop, ChipTop, ExtTLMem, CanHaveMasterTLMemPort, DefaultClockFrequencyKey} import chipyard.iobinders.{HasIOBinders} import chipyard.harness.{ApplyHarnessBinders} @@ -129,17 +129,11 @@ class VCU118FPGATestHarnessImp(_outer: VCU118FPGATestHarness) extends LazyRawMod childReset := buildtopReset // harness binders are non-lazy - _outer.topDesign match { case d: HasTestHarnessFunctions => - d.harnessFunctions.foreach(_(this)) - } _outer.topDesign match { case d: HasIOBinders => ApplyHarnessBinders(this, d.lazySystem, d.portMap) } // check the top-level reference clock is equal to the default // non-exhaustive since you need all ChipTop clocks to equal the default - _outer.topDesign match { - case d: HasReferenceClockFreq => require(d.refClockFreqMHz == p(DefaultClockFrequencyKey)) - case _ => - } + require(getRefClockFreq == p(DefaultClockFrequencyKey)) } diff --git a/generators/chipyard/src/main/scala/ChipTop.scala b/generators/chipyard/src/main/scala/ChipTop.scala index 8d05d10e..96e5de17 100644 --- a/generators/chipyard/src/main/scala/ChipTop.scala +++ b/generators/chipyard/src/main/scala/ChipTop.scala @@ -15,10 +15,6 @@ import barstools.iocell.chisel._ case object BuildSystem extends Field[Parameters => LazyModule]((p: Parameters) => new DigitalTop()(p)) -trait HasReferenceClockFreq { - def refClockFreqMHz: Double -} - /** * The base class used for building chips. This constructor instantiates a module specified by the BuildSystem parameter, * named "system", which is an instance of DigitalTop by default. The diplomatic clocks of System, as well as its implicit clock, @@ -27,31 +23,14 @@ trait HasReferenceClockFreq { */ class ChipTop(implicit p: Parameters) extends LazyModule with BindingScope - with HasTestHarnessFunctions with HasReferenceClockFreq with HasIOBinders { + with HasIOBinders { // The system module specified by BuildSystem lazy val lazySystem = LazyModule(p(BuildSystem)(p)).suggestName("system") - // The implicitClockSinkNode provides the implicit clock and reset for the system (connected by clocking scheme) - val implicitClockSinkNode = ClockSinkNode(Seq(ClockSinkParameters(name = Some("implicit_clock")))) - - // Generate Clocks and Reset - val mvRefClkFreq = p(ClockingSchemeKey)(this) - def refClockFreqMHz: Double = mvRefClkFreq.getWrappedValue - // NOTE: Making this a LazyRawModule is moderately dangerous, as anonymous children // of ChipTop (ex: ClockGroup) do not receive clock or reset. // However. anonymous children of ChipTop should not need an implicit Clock or Reset // anyways, they probably need to be explicitly clocked. - lazy val module: LazyModuleImpLike = new LazyRawModuleImp(this) { - // These become the implicit clock and reset to the System - val implicit_clock = implicitClockSinkNode.in.head._1.clock - val implicit_reset = implicitClockSinkNode.in.head._1.reset - - // Connect the implicit clock/reset, if present - lazySystem.module match { case l: LazyModuleImp => { - l.clock := implicit_clock - l.reset := implicit_reset - }} - } + lazy val module: LazyModuleImpLike = new LazyRawModuleImp(this) { } } diff --git a/generators/chipyard/src/main/scala/Clocks.scala b/generators/chipyard/src/main/scala/Clocks.scala deleted file mode 100644 index b5553347..00000000 --- a/generators/chipyard/src/main/scala/Clocks.scala +++ /dev/null @@ -1,128 +0,0 @@ -package chipyard - -import chisel3._ - -import scala.collection.mutable.{ArrayBuffer} - -import freechips.rocketchip.prci._ -import freechips.rocketchip.subsystem.{BaseSubsystem, SubsystemDriveAsyncClockGroupsKey, InstantiatesTiles} -import freechips.rocketchip.config.{Parameters, Field, Config} -import freechips.rocketchip.diplomacy.{ModuleValue, OutwardNodeHandle, InModuleBody, LazyModule} -import freechips.rocketchip.util.{ResetCatchAndSync} - -import barstools.iocell.chisel._ -import testchipip.{TLTileResetCtrl} - -import chipyard.clocking._ -import chipyard.iobinders._ - -/** - * A simple reset implementation that punches out reset ports - * for standard Module classes. The ChipTop reset pin is Async. - * Synchronization is performed in the ClockGroupResetSynchronizer - */ -object GenerateReset { - def apply(chiptop: ChipTop, clock: Clock): Reset = { - implicit val p = chiptop.p - // this needs directionality so generateIOFromSignal works - val async_reset_wire = Wire(Input(AsyncReset())) - val (reset_io, resetIOCell) = IOCell.generateIOFromSignal(async_reset_wire, "reset", p(IOCellKey), - abstractResetAsAsync = true) - - chiptop.iocells ++= resetIOCell - chiptop.harnessFunctions += ((th: HasHarnessSignalReferences) => { - reset_io := th.dutReset - Nil - }) - async_reset_wire - } -} - - -case object ClockingSchemeKey extends Field[ChipTop => ModuleValue[Double]](ClockingSchemeGenerators.dividerOnlyClockGenerator) -/* - * This is a Seq of assignment functions, that accept a clock name and return an optional frequency. - * Functions that appear later in this seq have higher precedence that earlier ones. - * If no function returns a non-empty value, the value specified in - * [[DefaultClockFrequencyKey]] will be used. - */ -case object ClockFrequencyAssignersKey extends Field[Seq[(String) => Option[Double]]](Seq.empty) -case object DefaultClockFrequencyKey extends Field[Double]() - -class ClockNameMatchesAssignment(name: String, fMHz: Double) extends Config((site, here, up) => { - case ClockFrequencyAssignersKey => up(ClockFrequencyAssignersKey, site) ++ - Seq((cName: String) => if (cName == name) Some(fMHz) else None) -}) - -class ClockNameContainsAssignment(name: String, fMHz: Double) extends Config((site, here, up) => { - case ClockFrequencyAssignersKey => up(ClockFrequencyAssignersKey, site) ++ - Seq((cName: String) => if (cName.contains(name)) Some(fMHz) else None) -}) - -object ClockingSchemeGenerators { - val dividerOnlyClockGenerator: ChipTop => ModuleValue[Double] = { chiptop => - implicit val p = chiptop.p - - // Requires existence of undriven asyncClockGroups in subsystem - val systemAsyncClockGroup = chiptop.lazySystem match { - case l: BaseSubsystem if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) => - l.asyncClockGroupsNode - } - - // Add a control register for each tile's reset - val resetSetter = chiptop.lazySystem match { - case sys: BaseSubsystem with InstantiatesTiles => Some(TLTileResetCtrl(sys)) - case _ => None - } - val resetSetterResetProvider = resetSetter.map(_.tileResetProviderNode).getOrElse(ClockGroupEphemeralNode()) - - val aggregator = LazyModule(new ClockGroupAggregator("allClocks")).node - // provides the implicit clock to the system - (chiptop.implicitClockSinkNode - := ClockGroup() - := aggregator) - // provides the system clock (ex. the bus clocks) - (systemAsyncClockGroup - :*= ClockGroupNamePrefixer() - :*= aggregator) - - val referenceClockSource = ClockSourceNode(Seq(ClockSourceParameters())) - val dividerOnlyClkGenerator = LazyModule(new DividerOnlyClockGenerator("buildTopClockGenerator")) - // provides all the divided clocks (from the top-level clock) - (aggregator - := ClockGroupFrequencySpecifier(p(ClockFrequencyAssignersKey), p(DefaultClockFrequencyKey)) - := ClockGroupResetSynchronizer() - := resetSetterResetProvider - := dividerOnlyClkGenerator.node - := referenceClockSource) - - val asyncResetBroadcast = FixedClockBroadcast(None) - resetSetter.foreach(_.asyncResetSinkNode := asyncResetBroadcast) - val asyncResetSource = ClockSourceNode(Seq(ClockSourceParameters())) - asyncResetBroadcast := asyncResetSource - - InModuleBody { - val clock_wire = Wire(Input(Clock())) - val reset_wire = GenerateReset(chiptop, clock_wire) - val (clock_io, clockIOCell) = IOCell.generateIOFromSignal(clock_wire, "clock", p(IOCellKey)) - chiptop.iocells ++= clockIOCell - - referenceClockSource.out.unzip._1.map { o => - o.clock := clock_wire - o.reset := reset_wire - } - - asyncResetSource.out.unzip._1.map { o => - o.clock := false.B.asClock // async reset broadcast network does not provide a clock - o.reset := reset_wire - } - - chiptop.harnessFunctions += ((th: HasHarnessSignalReferences) => { - clock_io := th.buildtopClock - Nil }) - - // return the reference frequency - dividerOnlyClkGenerator.module.referenceFreq - } - } -} diff --git a/generators/chipyard/src/main/scala/ConfigFragments.scala b/generators/chipyard/src/main/scala/ConfigFragments.scala index 5d9dc81e..a41bfae0 100644 --- a/generators/chipyard/src/main/scala/ConfigFragments.scala +++ b/generators/chipyard/src/main/scala/ConfigFragments.scala @@ -31,6 +31,7 @@ import sifive.blocks.devices.uart._ import sifive.blocks.devices.spi._ import chipyard._ +import chipyard.clocking._ // ----------------------- // Common Config Fragments @@ -68,8 +69,10 @@ class WithL2TLBs(entries: Int) extends Config((site, here, up) => { } }) +class TraceGenTop(implicit p: Parameters) extends TraceGenSystem + with HasChipyardPRCI class WithTracegenSystem extends Config((site, here, up) => { - case BuildSystem => (p: Parameters) => new TraceGenSystem()(p) + case BuildSystem => (p: Parameters) => new TraceGenTop()(p) }) /** diff --git a/generators/chipyard/src/main/scala/DigitalTop.scala b/generators/chipyard/src/main/scala/DigitalTop.scala index a91fa479..ba094163 100644 --- a/generators/chipyard/src/main/scala/DigitalTop.scala +++ b/generators/chipyard/src/main/scala/DigitalTop.scala @@ -31,6 +31,7 @@ class DigitalTop(implicit p: Parameters) extends ChipyardSystem with chipyard.example.CanHavePeripheryStreamingFIR // Enables optionally adding the DSPTools FIR example widget with chipyard.example.CanHavePeripheryStreamingPassthrough // Enables optionally adding the DSPTools streaming-passthrough example widget with nvidia.blocks.dla.CanHavePeripheryNVDLA // Enables optionally having an NVDLA + with chipyard.clocking.HasChipyardPRCI // Use Chipyard reset/clock distribution { override lazy val module = new DigitalTopModule(this) } diff --git a/generators/chipyard/src/main/scala/HarnessBinders.scala b/generators/chipyard/src/main/scala/HarnessBinders.scala index c1ec2bda..1cc138f3 100644 --- a/generators/chipyard/src/main/scala/HarnessBinders.scala +++ b/generators/chipyard/src/main/scala/HarnessBinders.scala @@ -22,7 +22,8 @@ import barstools.iocell.chisel._ import testchipip._ import chipyard.{HasHarnessSignalReferences, HarnessClockInstantiatorKey} -import chipyard.iobinders.{GetSystemParameters, JTAGChipIO} +import chipyard.clocking.{HasChipyardPRCI} +import chipyard.iobinders.{GetSystemParameters, JTAGChipIO, ClockWithFreq} import tracegen.{TraceGenSystemModuleImp} import icenet.{CanHavePeripheryIceNIC, SimNetwork, NicLoopback, NICKey, NICIOvonly} @@ -322,8 +323,23 @@ class WithSimDromajoBridge extends ComposeHarnessBinder({ } }) -class WithTieOffCustomBootPin extends OverrideHarnessBinder({ +class WithCustomBootPinPlusArg extends OverrideHarnessBinder({ (system: CanHavePeripheryCustomBootPin, th: HasHarnessSignalReferences, ports: Seq[Bool]) => { - ports.foreach(_ := false.B) + val pin = PlusArg("custom_boot_pin", width=1) + ports.foreach(_ := pin) + } +}) + + +class WithClockAndResetFromHarness extends OverrideHarnessBinder({ + (system: HasChipyardPRCI, th: HasHarnessSignalReferences, ports: Seq[Data]) => { + implicit val p = GetSystemParameters(system) + ports.map ({ + case c: ClockWithFreq => { + th.setRefClockFreq(c.freqMHz) + c.clock := th.buildtopClock + } + case r: AsyncReset => r := th.buildtopReset.asAsyncReset + }) } }) diff --git a/generators/chipyard/src/main/scala/IOBinders.scala b/generators/chipyard/src/main/scala/IOBinders.scala index 448d4b72..65c8754f 100644 --- a/generators/chipyard/src/main/scala/IOBinders.scala +++ b/generators/chipyard/src/main/scala/IOBinders.scala @@ -11,7 +11,7 @@ import freechips.rocketchip.subsystem._ import freechips.rocketchip.system.{SimAXIMem} import freechips.rocketchip.amba.axi4.{AXI4Bundle, AXI4SlaveNode, AXI4MasterNode, AXI4EdgeParameters} import freechips.rocketchip.util._ -import freechips.rocketchip.prci.{ClockSinkNode, ClockSinkParameters} +import freechips.rocketchip.prci._ import freechips.rocketchip.groundtest.{GroundTestSubsystemModuleImp, GroundTestSubsystem} import sifive.blocks.devices.gpio._ @@ -23,6 +23,7 @@ import barstools.iocell.chisel._ import testchipip._ import icenet.{CanHavePeripheryIceNIC, SimNetwork, NicLoopback, NICKey, NICIOvonly} +import chipyard.clocking.{HasChipyardPRCI, DividerOnlyClockGenerator} import scala.reflect.{ClassTag} @@ -384,3 +385,45 @@ class WithDontTouchPorts extends OverrideIOBinder({ (system: DontTouch) => system.dontTouchPorts(); (Nil, Nil) }) +class ClockWithFreq(val freqMHz: Double) extends Bundle { + val clock = Clock() +} + +class WithDividerOnlyClockGenerator extends OverrideLazyIOBinder({ + (system: HasChipyardPRCI) => { + // Connect the implicit clock + implicit val p = GetSystemParameters(system) + val implicitClockSinkNode = ClockSinkNode(Seq(ClockSinkParameters(name = Some("implicit_clock")))) + system.connectImplicitClockSinkNode(implicitClockSinkNode) + InModuleBody { + val implicit_clock = implicitClockSinkNode.in.head._1.clock + val implicit_reset = implicitClockSinkNode.in.head._1.reset + system.asInstanceOf[BaseSubsystem].module match { case l: LazyModuleImp => { + l.clock := implicit_clock + l.reset := implicit_reset + }} + } + + // Connect all other requested clocks + val referenceClockSource = ClockSourceNode(Seq(ClockSourceParameters())) + val dividerOnlyClockGen = LazyModule(new DividerOnlyClockGenerator("buildTopClockGenerator")) + + (system.allClockGroupsNode + := dividerOnlyClockGen.node + := referenceClockSource) + + InModuleBody { + val clock_wire = Wire(Input(new ClockWithFreq(dividerOnlyClockGen.module.referenceFreq))) + val reset_wire = Wire(Input(AsyncReset())) + val (clock_io, clockIOCell) = IOCell.generateIOFromSignal(clock_wire, "clock", p(IOCellKey)) + val (reset_io, resetIOCell) = IOCell.generateIOFromSignal(reset_wire, "reset", p(IOCellKey)) + + referenceClockSource.out.unzip._1.map { o => + o.clock := clock_wire.clock + o.reset := reset_wire + } + + (Seq(clock_io, reset_io), clockIOCell ++ resetIOCell) + } + } +}) diff --git a/generators/chipyard/src/main/scala/TestHarness.scala b/generators/chipyard/src/main/scala/TestHarness.scala index ba09e6dc..e83c9fec 100644 --- a/generators/chipyard/src/main/scala/TestHarness.scala +++ b/generators/chipyard/src/main/scala/TestHarness.scala @@ -17,13 +17,14 @@ import chipyard.clocking.{SimplePllConfiguration, ClockDividerN} // ------------------------------- case object BuildTop extends Field[Parameters => LazyModule]((p: Parameters) => new ChipTop()(p)) - -trait HasTestHarnessFunctions { - val harnessFunctions = ArrayBuffer.empty[HasHarnessSignalReferences => Seq[Any]] -} +case object DefaultClockFrequencyKey extends Field[Double](100.0) // MHz trait HasHarnessSignalReferences { + implicit val p: Parameters // clock/reset of the chiptop reference clock (can be different than the implicit harness clock/reset) + var refClockFreq: Double = p(DefaultClockFrequencyKey) + def setRefClockFreq(freqMHz: Double) = { refClockFreq = freqMHz } + def getRefClockFreq: Double = refClockFreq def buildtopClock: Clock def buildtopReset: Reset def dutReset: Reset @@ -90,25 +91,18 @@ class TestHarness(implicit val p: Parameters) extends Module with HasHarnessSign io.success := false.B - val freqMHz = lazyDut match { - case d: HasReferenceClockFreq => d.refClockFreqMHz - case _ => p(DefaultClockFrequencyKey) - } - val refClkBundle = p(HarnessClockInstantiatorKey).requestClockBundle("buildtop_reference_clock", freqMHz * (1000 * 1000)) - - buildtopClock := refClkBundle.clock - buildtopReset := WireInit(refClkBundle.reset) - val dutReset = refClkBundle.reset.asAsyncReset - + val dutReset = buildtopReset.asAsyncReset val success = io.success - lazyDut match { case d: HasTestHarnessFunctions => - d.harnessFunctions.foreach(_(this)) - } lazyDut match { case d: HasIOBinders => ApplyHarnessBinders(this, d.lazySystem, d.portMap) } + val refClkBundle = p(HarnessClockInstantiatorKey).requestClockBundle("buildtop_reference_clock", getRefClockFreq * (1000 * 1000)) + + buildtopClock := refClkBundle.clock + buildtopReset := WireInit(refClkBundle.reset) + val implicitHarnessClockBundle = Wire(new ClockBundle(ClockBundleParameters())) implicitHarnessClockBundle.clock := clock implicitHarnessClockBundle.reset := reset diff --git a/generators/chipyard/src/main/scala/clocking/ClockGroupCombiner.scala b/generators/chipyard/src/main/scala/clocking/ClockGroupCombiner.scala new file mode 100644 index 00000000..07ae536b --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/ClockGroupCombiner.scala @@ -0,0 +1,81 @@ +package chipyard.clocking + +import chisel3._ +import chisel3.util._ +import chisel3.experimental.{Analog, IO} + +import freechips.rocketchip.config._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.prci._ +import freechips.rocketchip.util._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.devices.tilelink._ +import freechips.rocketchip.regmapper._ +import freechips.rocketchip.subsystem._ + +object ClockGroupCombiner { + def apply()(implicit p: Parameters, valName: ValName): ClockGroupAdapterNode = { + LazyModule(new ClockGroupCombiner()).node + } +} + +case object ClockGroupCombinerKey extends Field[Seq[(String, ClockSinkParameters => Boolean)]](Nil) + +// All clock groups with a name containing any substring in names will be combined into a single clock group +class WithClockGroupsCombinedByName(grouped_name: String, names: String*) extends Config((site, here, up) => { + case ClockGroupCombinerKey => { + val combiner: ClockSinkParameters => Boolean = { m => names.map(n => m.name.get.contains(n)).reduce(_||_) } + up(ClockGroupCombinerKey) ++ Seq((grouped_name, combiner)) + } +}) + +/** This node combines sets of clock groups according to functions provided in the ClockGroupCombinerKey + * The ClockGroupCombinersKey contains a list of tuples of: + * - The name of the combined group + * - A function on the ClockSinkParameters, returning True if the associated clock group should be grouped by this node + * This node will fail if + * - Multiple grouping functions match a single clock group + * - A grouping function matches zero clock groups + * - A grouping function matches clock groups with different requested frequncies + */ +class ClockGroupCombiner(implicit p: Parameters, v: ValName) extends LazyModule { + val combiners = p(ClockGroupCombinerKey) + val sourceFn: ClockGroupSourceParameters => ClockGroupSourceParameters = { m => m } + val sinkFn: ClockGroupSinkParameters => ClockGroupSinkParameters = { u => + var i = 0 + val (grouped, rest) = combiners.map(_._2).foldLeft((Seq[ClockSinkParameters](), u.members)) { case ((grouped, rest), c) => + val (g, r) = rest.partition(c(_)) + val name = combiners(i)._1 + i = i + 1 + require(g.size >= 1) + require(g.forall(_.take.get == g.head.take.get)) + (grouped ++ Seq(ClockSinkParameters(take = g.head.take, name = Some(name))), r) + } + ClockGroupSinkParameters( + name = u.name, + members = grouped ++ rest + ) + } + + + val node = ClockGroupAdapterNode(sourceFn, sinkFn) + lazy val module = new LazyRawModuleImp(this) { + (node.out zip node.in).map { case ((o, oe), (i, ie)) => + { + val inMap = (i.member.data zip ie.sink.members).map { case (id, im) => + im.name.get -> id + }.toMap + (o.member.data zip oe.sink.members).map { case (od, om) => + val matches = combiners.filter(c => c._2(om)) + require(matches.size <= 1) + if (matches.size == 0) { + od := inMap(om.name.get) + } else { + od := inMap(matches(0)._1) + } + } + } + } + } +} diff --git a/generators/chipyard/src/main/scala/clocking/ClockGroupNamePrefixer.scala b/generators/chipyard/src/main/scala/clocking/ClockGroupNamePrefixer.scala index a5618c2d..965beed3 100644 --- a/generators/chipyard/src/main/scala/clocking/ClockGroupNamePrefixer.scala +++ b/generators/chipyard/src/main/scala/clocking/ClockGroupNamePrefixer.scala @@ -2,10 +2,23 @@ package chipyard.clocking import chisel3._ -import freechips.rocketchip.config.{Parameters} +import freechips.rocketchip.config.{Parameters, Config, Field} import freechips.rocketchip.diplomacy._ import freechips.rocketchip.prci._ +case object ClockFrequencyAssignersKey extends Field[Seq[(String) => Option[Double]]](Seq.empty) + +class ClockNameMatchesAssignment(name: String, fMHz: Double) extends Config((site, here, up) => { + case ClockFrequencyAssignersKey => up(ClockFrequencyAssignersKey, site) ++ + Seq((cName: String) => if (cName == name) Some(fMHz) else None) +}) + +class ClockNameContainsAssignment(name: String, fMHz: Double) extends Config((site, here, up) => { + case ClockFrequencyAssignersKey => up(ClockFrequencyAssignersKey, site) ++ + Seq((cName: String) => if (cName.contains(name)) Some(fMHz) else None) +}) + + /** * This sort of node can be used when it is a connectivity passthrough, but modifies * the flow of parameters (which may result in changing the names of the underlying signals). diff --git a/generators/chipyard/src/main/scala/clocking/HasChipyardPRCI.scala b/generators/chipyard/src/main/scala/clocking/HasChipyardPRCI.scala new file mode 100644 index 00000000..30a29bcb --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/HasChipyardPRCI.scala @@ -0,0 +1,79 @@ +package chipyard.clocking + +import chisel3._ + +import scala.collection.mutable.{ArrayBuffer} + +import freechips.rocketchip.config.{Parameters, Field, Config} +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.devices.tilelink._ +import freechips.rocketchip.regmapper._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.util._ +import freechips.rocketchip.tile._ +import freechips.rocketchip.prci._ + +import testchipip.{TLTileResetCtrl} +import chipyard.{DefaultClockFrequencyKey} + +case class ChipyardPRCIControlParams( + slaveWhere: TLBusWrapperLocation = CBUS, + baseAddress: BigInt = 0x100000, + enableTileClockGating: Boolean = true +) + + +case object ChipyardPRCIControlKey extends Field[ChipyardPRCIControlParams](ChipyardPRCIControlParams()) + +trait HasChipyardPRCI { this: BaseSubsystem with InstantiatesTiles => + require(p(SubsystemDriveAsyncClockGroupsKey).isEmpty, "Subsystem asyncClockGroups must be undriven") + + implicit val n = ValName("chipyardPRCI") + + val prciParams = p(ChipyardPRCIControlKey) + + // Set up clock domain + private val tlbus = locateTLBusWrapper(prciParams.slaveWhere) + val prci_ctrl_domain = LazyModule(new ClockSinkDomain(name=Some("chipyard-prci-control"))) + prci_ctrl_domain.clockNode := tlbus.fixedClockNode + + // Aggregate all the clock groups into a single node + val aggregator = LazyModule(new ClockGroupAggregator("allClocks")).node + val allClockGroupsNode = ClockGroupEphemeralNode() + + // There are two "sets" of clocks which must be dealt with + + // 1. The implicit clock from the subsystem. RC is moving away from depending on this + // clock, but some modules still use it. Since the implicit clock sink node + // is created in the ChipTop (the hierarchy wrapping the subsystem), this function + // is provided to allow connecting that clock to the clock aggregator. This function + // should be called in the ChipTop context + def connectImplicitClockSinkNode(sink: ClockSinkNode) = + (sink + := ClockGroup() + := aggregator) + + // 2. The rest of the diplomatic clocks in the subsystem are routed to this asyncClockGroupsNode + (asyncClockGroupsNode + :*= ClockGroupNamePrefixer() + :*= aggregator) + + + // Once all the clocks are gathered in the aggregator node, several steps remain + // 1. Assign frequencies to any clock groups which did not specify a frequency. + // 2. Combine duplicated clock groups (clock groups which physically should be in the same clock domain) + // 3. Synchronize reset to each clock group + // 4. Clock gate the clock groups corresponding to Tiles (if desired). + // 5. Add reset control registers to the tiles (if desired) + // The final clock group here contains physically distinct clock domains, which some PRCI node in a + // diplomatic IOBinder should drive + (aggregator + := ClockGroupFrequencySpecifier(p(ClockFrequencyAssignersKey), p(DefaultClockFrequencyKey)) + := ClockGroupCombiner() + := ClockGroupResetSynchronizer() + := TileClockGater(prciParams.baseAddress + 0x00000, tlbus, prciParams.enableTileClockGating) + := TileResetSetter(prciParams.baseAddress + 0x10000, tlbus, tile_prci_domains.map(_.tile_reset_domain.clockNode.portParams(0).name.get), Nil) + := allClockGroupsNode) +} + diff --git a/generators/chipyard/src/main/scala/clocking/TileClockGater.scala b/generators/chipyard/src/main/scala/clocking/TileClockGater.scala new file mode 100644 index 00000000..9247bc81 --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/TileClockGater.scala @@ -0,0 +1,55 @@ +package chipyard.clocking + +import chisel3._ +import chisel3.util._ +import chisel3.experimental.{Analog, IO} + +import freechips.rocketchip.config._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.prci._ +import freechips.rocketchip.util._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.devices.tilelink._ +import freechips.rocketchip.regmapper._ +import freechips.rocketchip.subsystem._ + +/** This node adds clock gating control registers. + * If deploying on a platform which does not support clock gating, deasserting the enable + * flag will generate the registers, preserving the same memory map and behavior, but will not + * generate any gaters + */ +class TileClockGater(address: BigInt, beatBytes: Int, enable: Boolean)(implicit p: Parameters, valName: ValName) extends LazyModule +{ + val device = new SimpleDevice(s"clock-gater", Nil) + val clockNode = ClockGroupIdentityNode() + val tlNode = TLRegisterNode(Seq(AddressSet(address, 4096-1)), device, "reg/control", beatBytes=beatBytes) + lazy val module = new LazyModuleImp(this) { + val sources = clockNode.in.head._1.member.data.toSeq + val sinks = clockNode.out.head._1.member.elements.toSeq + val nSinks = sinks.size + val regs = (0 until nSinks).map({i => + val sinkName = sinks(i)._1 + val reg = withReset(sources(i).reset) { Module(new AsyncResetRegVec(w=1, init=1)) } + if (sinkName.contains("tile") && enable) { + println(s"ClockGate for ${sinkName} regmapped at ${(address+i*4).toString(16)}") + sinks(i)._2.clock := ClockGate(sources(i).clock, reg.io.q.asBool) + sinks(i)._2.reset := sources(i).reset + } else { + sinks(i)._2 := sources(i) + } + reg + }) + tlNode.regmap((0 until nSinks).map({i => + i*4 -> Seq(RegField.rwReg(1, regs(i).io)) + }): _*) + } +} + +object TileClockGater { + def apply(address: BigInt, tlbus: TLBusWrapper, enable: Boolean)(implicit p: Parameters, v: ValName) = { + val gater = LazyModule(new TileClockGater(address, tlbus.beatBytes, enable)) + tlbus.toVariableWidthSlave(Some("clock-gater")) { gater.tlNode := TLBuffer() } + gater.clockNode + } +} diff --git a/generators/chipyard/src/main/scala/clocking/TileResetSetter.scala b/generators/chipyard/src/main/scala/clocking/TileResetSetter.scala new file mode 100644 index 00000000..41f6d317 --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/TileResetSetter.scala @@ -0,0 +1,72 @@ +package chipyard.clocking + +import chisel3._ +import chisel3.util._ +import chisel3.experimental.{Analog, IO} + +import freechips.rocketchip.config._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.prci._ +import freechips.rocketchip.util._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.devices.tilelink._ +import freechips.rocketchip.regmapper._ +import freechips.rocketchip.subsystem._ + +// Currently only works if all tiles are already driven by independent clock groups +// TODO: After https://github.com/chipsalliance/rocket-chip/pull/2842 is merged, we should +// always put all tiles on independent clock groups +class TileResetSetter(address: BigInt, beatBytes: Int, tileNames: Seq[String], initResetHarts: Seq[Int])(implicit p: Parameters) + extends LazyModule { + val device = new SimpleDevice("tile-reset-setter", Nil) + val tlNode = TLRegisterNode(Seq(AddressSet(address, 4096-1)), device, "reg/control", beatBytes=beatBytes) + val clockNode = ClockGroupIdentityNode() + + lazy val module = new LazyModuleImp(this) { + val nTiles = p(TilesLocated(InSubsystem)).size + require (nTiles <= 4096 / 4) + val tile_async_resets = Wire(Vec(nTiles, Reset())) + val r_tile_resets = (0 until nTiles).map({ i => + tile_async_resets(i) := true.B.asAsyncReset // Remove this line after https://github.com/chipsalliance/rocket-chip/pull/2842 + withReset (tile_async_resets(i)) { + Module(new AsyncResetRegVec(w=1, init=(if (initResetHarts.contains(i)) 1 else 0))) + } + }) + tlNode.regmap((0 until nTiles).map({ i => + i * 4 -> Seq(RegField.rwReg(1, r_tile_resets(i).io)), + }): _*) + + val tileMap = tileNames.zipWithIndex.map({ case (n, i) => + n -> (tile_async_resets(i), r_tile_resets(i).io.q) + }) + + (clockNode.out zip clockNode.in).map { case ((o, _), (i, _)) => + (o.member.elements zip i.member.elements).foreach { case ((name, oD), (_, iD)) => + oD.clock := iD.clock + oD.reset := iD.reset + for ((n, (rIn, rOut)) <- tileMap) { + if (name.contains(n)) { + println(name, n) + // Async because the reset coming out of the AsyncResetRegVec is + // clocked to the bus this is attached to, not the clock in this + // clock bundle. We expect a ClockGroupResetSynchronizer downstream + // to synchronize the resets + // Also, this or enforces that the tiles come out of reset after the reset of the system + oD.reset := (rOut.asBool || iD.reset.asBool).asAsyncReset + rIn := iD.reset + } + } + } + } + } +} + + +object TileResetSetter { + def apply(address: BigInt, tlbus: TLBusWrapper, tileNames: Seq[String], initResetHarts: Seq[Int])(implicit p: Parameters, v: ValName) = { + val setter = LazyModule(new TileResetSetter(address, tlbus.beatBytes, tileNames, initResetHarts)) + tlbus.toVariableWidthSlave(Some("tile-reset-setter")) { setter.tlNode := TLBuffer() } + setter.clockNode + } +} diff --git a/generators/chipyard/src/main/scala/config/AbstractConfig.scala b/generators/chipyard/src/main/scala/config/AbstractConfig.scala index 6660d159..209bcf1f 100644 --- a/generators/chipyard/src/main/scala/config/AbstractConfig.scala +++ b/generators/chipyard/src/main/scala/config/AbstractConfig.scala @@ -21,7 +21,8 @@ class AbstractConfig extends Config( new chipyard.harness.WithSimAXIMMIO ++ // add SimAXIMem for axi4 mmio port, if enabled new chipyard.harness.WithTieOffInterrupts ++ // tie-off interrupt ports, if present new chipyard.harness.WithTieOffL2FBusAXI ++ // tie-off external AXI4 master, if present - new chipyard.harness.WithTieOffCustomBootPin ++ + new chipyard.harness.WithCustomBootPinPlusArg ++ + new chipyard.harness.WithClockAndResetFromHarness ++ // The IOBinders instantiate ChipTop IOs to match desired digital IOs // IOCells are generated for "Chip-like" IOs, while simulation-only IOs are directly punched through @@ -39,6 +40,7 @@ class AbstractConfig extends Config( new chipyard.iobinders.WithTraceIOPunchthrough ++ new chipyard.iobinders.WithExtInterruptIOCells ++ new chipyard.iobinders.WithCustomBootPin ++ + new chipyard.iobinders.WithDividerOnlyClockGenerator ++ new testchipip.WithDefaultSerialTL ++ // use serialized tilelink port to external serialadapter/harnessRAM new chipyard.config.WithBootROM ++ // use default bootrom diff --git a/generators/chipyard/src/main/scala/config/TracegenConfigs.scala b/generators/chipyard/src/main/scala/config/TracegenConfigs.scala index 3f9e27d1..4ab51994 100644 --- a/generators/chipyard/src/main/scala/config/TracegenConfigs.scala +++ b/generators/chipyard/src/main/scala/config/TracegenConfigs.scala @@ -6,8 +6,10 @@ import freechips.rocketchip.rocket.{DCacheParams} class AbstractTraceGenConfig extends Config( new chipyard.harness.WithBlackBoxSimMem ++ new chipyard.harness.WithTraceGenSuccess ++ + new chipyard.harness.WithClockAndResetFromHarness ++ new chipyard.iobinders.WithAXI4MemPunchthrough ++ new chipyard.iobinders.WithTraceGenSuccessPunchthrough ++ + new chipyard.iobinders.WithDividerOnlyClockGenerator ++ new chipyard.config.WithTracegenSystem ++ new chipyard.config.WithNoSubsystemDrivenClocks ++ new chipyard.config.WithPeripheryBusFrequencyAsDefault ++ diff --git a/generators/firechip/src/main/scala/FireSim.scala b/generators/firechip/src/main/scala/FireSim.scala index 661ddb99..3839bf14 100644 --- a/generators/firechip/src/main/scala/FireSim.scala +++ b/generators/firechip/src/main/scala/FireSim.scala @@ -10,7 +10,7 @@ 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, InModuleBody, ValName} +import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImp, InModuleBody, ValName} import freechips.rocketchip.util.{ResetCatchAndSync, RecordMap} import midas.widgets.{Bridge, PeekPokeBridge, RationalClockBridge, RationalClock, ResetPulseBridge, ResetPulseBridgeParameters} @@ -166,59 +166,69 @@ class ClockBridgeInstantiator { case object ClockBridgeInstantiatorKey extends Field[ClockBridgeInstantiator](new ClockBridgeInstantiator) case object FireSimBaseClockNameKey extends Field[String]("implicit_clock") -class WithFireSimSimpleClocks extends Config((site, here, up) => { - case ClockingSchemeKey => { chiptop: ChipTop => - implicit val p = chiptop.p +class ClocksWithSinkParams(val params: Seq[ClockSinkParameters]) extends Bundle { + val clocks = Vec(params.size, Clock()) +} + +class WithFireSimSimpleClocks extends OverrideLazyIOBinder({ + (system: HasChipyardPRCI) => { + implicit val p = GetSystemParameters(system) // Figure out what provides this in the chipyard scheme implicit val valName = ValName("FireSimClocking") - // Requires existence of undriven asyncClockGroups in subsystem - val systemAsyncClockGroup = chiptop.lazySystem match { - case l: BaseSubsystem if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) => - l.asyncClockGroupsNode + val implicitClockSinkNode = ClockSinkNode(Seq(ClockSinkParameters(name = Some("implicit_clock")))) + system.connectImplicitClockSinkNode(implicitClockSinkNode) + InModuleBody { + val implicit_clock = implicitClockSinkNode.in.head._1.clock + val implicit_reset = implicitClockSinkNode.in.head._1.reset + system.asInstanceOf[BaseSubsystem].module match { case l: LazyModuleImp => { + l.clock := implicit_clock + l.reset := implicit_reset + }} } - val aggregator = LazyModule(new ClockGroupAggregator("allClocks")).node - (chiptop.implicitClockSinkNode := ClockGroup() := aggregator) - (systemAsyncClockGroup :*= ClockGroupNamePrefixer() :*= aggregator) - val inputClockSource = ClockGroupSourceNode(Seq(ClockGroupSourceParameters())) - - (aggregator - := ClockGroupResetSynchronizer() - := ClockGroupFrequencySpecifier(p(ClockFrequencyAssignersKey), p(DefaultClockFrequencyKey)) - := inputClockSource) - + system.allClockGroupsNode := inputClockSource InModuleBody { val (clockGroupBundle, clockGroupEdge) = inputClockSource.out.head - val input_clocks = IO(Input(RecordMap((clockGroupEdge.sink.members.map { m => (m.name.get, Clock()) }):_* ))) + val reset_io = IO(Input(AsyncReset())).suggestName("async_reset") + + val input_clocks = IO(Input(new ClocksWithSinkParams(clockGroupEdge.sink.members))) .suggestName("clocks") - val reset = IO(Input(Reset())).suggestName("reset") - (clockGroupBundle.member.data zip input_clocks.data).foreach { case (clockBundle, inputClock) => + (clockGroupBundle.member.data zip input_clocks.clocks).foreach { case (clockBundle, inputClock) => clockBundle.clock := inputClock - clockBundle.reset := reset + clockBundle.reset := reset_io } - val pllConfig = new SimplePllConfiguration("firesimBuildTopClockGenerator", clockGroupEdge.sink.members) - pllConfig.emitSummaries - val rationalClockSpecs = for ((sinkP, division) <- pllConfig.sinkDividerMap) yield { - RationalClock(sinkP.name.get, 1, division) - } + (Seq(reset_io, input_clocks), Nil) + } + } +}) - chiptop.harnessFunctions += ((th: HasHarnessSignalReferences) => { - reset := th.buildtopReset - input_clocks := p(ClockBridgeInstantiatorKey) - .requestClockRecordMap(BuildTopClockParameters( +class WithFireSimHarnessClockBinder extends OverrideHarnessBinder({ + (system: HasChipyardPRCI, th: FireSim, ports: Seq[Data]) => { + implicit val p = th.p + ports.map ({ + case c: ClocksWithSinkParams => { + val pllConfig = new SimplePllConfiguration("firesimBuildTopClockGenerator", c.params) + pllConfig.emitSummaries + th.setRefClockFreq(pllConfig.referenceFreqMHz) + val rationalClockSpecs = for ((sinkP, division) <- pllConfig.sinkDividerMap) yield { + RationalClock(sinkP.name.get, 1, division) + } + val input_clocks: RecordMap[Clock] = p(ClockBridgeInstantiatorKey).requestClockRecordMap( + BuildTopClockParameters( rationalClockSpecs.toSeq, p(FireSimBaseClockNameKey), pllConfig.referenceFreqMHz * (1000 * 1000))) - Nil }) - - // return the reference frequency - pllConfig.referenceFreqMHz - } + (c.clocks zip c.params) map ({ case (clock, param) => + clock := input_clocks(param.name.get).get + }) + } + case r: Reset => r := th.buildtopReset.asAsyncReset + }) } }) @@ -245,8 +255,6 @@ class FireSim(implicit val p: Parameters) extends RawModule with HasHarnessSigna def dutReset = { require(false, "dutReset should not be used in Firesim"); false.B } def success = { require(false, "success should not be used in Firesim"); false.B } - var btFreqMHz: Option[Double] = None - // Instantiate multiple instances of the DUT to implement supernode for (i <- 0 until p(NumNodes)) { // It's not a RC bump without some hacks... @@ -259,22 +267,13 @@ class FireSim(implicit val p: Parameters) extends RawModule with HasHarnessSigna }))) val module = Module(lazyModule.module) - btFreqMHz = Some(lazyModule match { - case d: HasReferenceClockFreq => d.refClockFreqMHz - case _ => p(DefaultClockFrequencyKey) - }) - - lazyModule match { case d: HasTestHarnessFunctions => - require(d.harnessFunctions.size == 1, "There should only be 1 harness function to connect clock+reset") - d.harnessFunctions.foreach(_(this)) - } lazyModule match { case d: HasIOBinders => ApplyHarnessBinders(this, d.lazySystem, d.portMap) } NodeIdx.increment() } - buildtopClock := p(ClockBridgeInstantiatorKey).requestClock("buildtop_reference_clock", btFreqMHz.get * (1000 * 1000)) + buildtopClock := p(ClockBridgeInstantiatorKey).requestClock("buildtop_reference_clock", getRefClockFreq * (1000 * 1000)) p(ClockBridgeInstantiatorKey).instantiateFireSimClockBridge } diff --git a/generators/firechip/src/main/scala/TargetConfigs.scala b/generators/firechip/src/main/scala/TargetConfigs.scala index 691ef15d..0c612cba 100644 --- a/generators/firechip/src/main/scala/TargetConfigs.scala +++ b/generators/firechip/src/main/scala/TargetConfigs.scala @@ -17,6 +17,7 @@ import testchipip.{BlockDeviceKey, BlockDeviceConfig, TracePortKey, TracePortPar import sifive.blocks.devices.uart.{PeripheryUARTKey, UARTParams} import scala.math.{min, max} +import chipyard.clocking.{ChipyardPRCIControlKey} import icenet._ import testchipip.WithRingSystemBus @@ -40,6 +41,7 @@ class WithBootROM extends Config((site, here, up) => { // Disables clock-gating; doesn't play nice with our FAME-1 pass class WithoutClockGating extends Config((site, here, up) => { case DebugModuleKey => up(DebugModuleKey, site).map(_.copy(clockGate = false)) + case ChipyardPRCIControlKey => up(ChipyardPRCIControlKey, site).copy(enableTileClockGating = false) }) // Testing configurations @@ -65,6 +67,7 @@ class WithFireSimDesignTweaks extends Config( // Required: Bake in the default FASED memory model new WithDefaultMemModel ++ // Required*: Uses FireSim ClockBridge and PeekPokeBridge to drive the system with a single clock/reset + new WithFireSimHarnessClockBinder ++ new WithFireSimSimpleClocks ++ // Required*: When using FireSim-as-top to provide a correct path to the target bootrom source new WithBootROM ++ diff --git a/sims/firesim b/sims/firesim index a2a6b3bc..cd13db4f 160000 --- a/sims/firesim +++ b/sims/firesim @@ -1 +1 @@ -Subproject commit a2a6b3bc27ee049d7bf5048287700d65ac66d126 +Subproject commit cd13db4f2006d47bf71c593b0255b32c6a0bbec5