diff --git a/generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv b/generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv new file mode 100644 index 0000000000..4d940d06ff --- /dev/null +++ b/generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv @@ -0,0 +1,42 @@ +// See LICENSE for license details. + +/** + * An unsynthesizable divide-by-N clock divider. + * Duty cycle is 100 * (ceil(DIV / 2)) / DIV. + */ + +module ClockDividerN #(parameter DIV)(output logic clk_out = 1'b0, input clk_in); + + localparam CWIDTH = $clog2(DIV); + localparam LOW_CYCLES = DIV / 2; + localparam HIGH_TRANSITION = LOW_CYCLES - 1; + localparam LOW_TRANSITION = DIV - 1; + + generate + if (DIV == 1) begin + // This needs to be procedural because of the assignment on declaration + always @(clk_in) begin + clk_out = clk_in; + end + end else begin + reg [CWIDTH - 1: 0] count = HIGH_TRANSITION[CWIDTH-1:0]; + // The blocking assignment to clock out is used to conform what was done + // in RC's clock dividers. + // It should have the effect of preventing registers in the divided clock + // domain latching register updates launched by the fast clock-domain edge + // that occurs at the same simulated time (as the divided clock edge). + always @(posedge clk_in) begin + if (count == LOW_TRANSITION[CWIDTH-1:0]) begin + clk_out = 1'b0; + count <= '0; + end + else begin + if (count == HIGH_TRANSITION[CWIDTH-1:0]) begin + clk_out = 1'b1; + end + count <= count + 1'b1; + end + end + end + endgenerate +endmodule // ClockDividerN diff --git a/generators/chipyard/src/main/scala/ChipTop.scala b/generators/chipyard/src/main/scala/ChipTop.scala index dfe08780ff..1cef2180ee 100644 --- a/generators/chipyard/src/main/scala/ChipTop.scala +++ b/generators/chipyard/src/main/scala/ChipTop.scala @@ -31,7 +31,7 @@ class ChipTop(implicit p: Parameters) extends LazyModule with HasTestHarnessFunc val lazySystem = LazyModule(p(BuildSystem)(p)).suggestName("system") // The implicitClockSinkNode provides the implicit clock and reset for the System - val implicitClockSinkNode = ClockSinkNode(Seq(ClockSinkParameters())) + val implicitClockSinkNode = ClockSinkNode(Seq(ClockSinkParameters(name = Some("implicit_clock")))) // Generate Clocks and Reset p(ClockingSchemeKey)(this) diff --git a/generators/chipyard/src/main/scala/Clocks.scala b/generators/chipyard/src/main/scala/Clocks.scala index 38ab105ace..554e990560 100644 --- a/generators/chipyard/src/main/scala/Clocks.scala +++ b/generators/chipyard/src/main/scala/Clocks.scala @@ -6,12 +6,14 @@ import scala.collection.mutable.{ArrayBuffer} import freechips.rocketchip.prci._ import freechips.rocketchip.subsystem.{BaseSubsystem, SubsystemDriveAsyncClockGroupsKey} -import freechips.rocketchip.config.{Parameters, Field} +import freechips.rocketchip.config.{Parameters, Field, Config} import freechips.rocketchip.diplomacy.{OutwardNodeHandle, InModuleBody, LazyModule} import freechips.rocketchip.util.{ResetCatchAndSync, Pow2ClockDivider} import barstools.iocell.chisel._ +import chipyard.clocking.{DividerOnlyClockGenerator, ClockGroupNamePrefixer, ClockGroupFrequencySpecifier} + /** * Chipyard provides three baseline, top-level reset schemes, set using the * [[GlobalResetSchemeKey]] in a Parameters instance. These are: @@ -77,99 +79,61 @@ object GenerateReset { } -case object ClockingSchemeKey extends Field[ChipTop => Unit](ClockingSchemeGenerators.harnessClock) +case object ClockingSchemeKey extends Field[ChipTop => Unit](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 { - // A simple clock provider, for testing - val harnessClock: ChipTop => Unit = { chiptop => + val dividerOnlyClockGenerator: ChipTop => Unit = { chiptop => implicit val p = chiptop.p - val implicitClockSourceNode = ClockSourceNode(Seq(ClockSourceParameters())) - chiptop.implicitClockSinkNode := implicitClockSourceNode - - // Drive the diplomaticclock graph of the DigitalTop (if present) - val simpleClockGroupSourceNode = chiptop.lazySystem match { - case l: BaseSubsystem if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) => { - val n = ClockGroupSourceNode(Seq(ClockGroupSourceParameters())) - l.asyncClockGroupsNode := n - Some(n) - } - case _ => None + // Requires existence of undriven asyncClockGroups in subsystem + val systemAsyncClockGroup = chiptop.lazySystem match { + case l: BaseSubsystem if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) => + l.asyncClockGroupsNode } - InModuleBody { - //this needs directionality so generateIOFromSignal works - val clock_wire = Wire(Input(Clock())) - val reset_wire = GenerateReset(chiptop, clock_wire) - val (clock_io, clockIOCell) = IOCell.generateIOFromSignal(clock_wire, "clock") - chiptop.iocells ++= clockIOCell + val aggregator = LazyModule(new ClockGroupAggregator("allClocks")).node + chiptop.implicitClockSinkNode := ClockGroup() := aggregator + systemAsyncClockGroup := ClockGroupNamePrefixer() := aggregator - implicitClockSourceNode.out.unzip._1.map { o => - o.clock := clock_wire - o.reset := reset_wire - } + val referenceClockSource = ClockSourceNode(Seq(ClockSourceParameters())) + (aggregator + := ClockGroupFrequencySpecifier(p(ClockFrequencyAssignersKey), p(DefaultClockFrequencyKey)) + := DividerOnlyClockGenerator() + := referenceClockSource) - simpleClockGroupSourceNode.map { n => n.out.unzip._1.map { out: ClockGroupBundle => - out.member.data.foreach { o => - o.clock := clock_wire - o.reset := reset_wire - } - }} - - chiptop.harnessFunctions += ((th: HasHarnessSignalReferences) => { - clock_io := th.harnessClock - Nil - }) - } - - } - - - val harnessDividedClock: ChipTop => Unit = { chiptop => - implicit val p = chiptop.p - - require(false, "Divided clock is broken until we fix passing onchip clocks to TestHarness objects") - - val implicitClockSourceNode = ClockSourceNode(Seq(ClockSourceParameters())) - chiptop.implicitClockSinkNode := implicitClockSourceNode - - val simpleClockGroupSourceNode = chiptop.lazySystem match { - case l: BaseSubsystem if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) => { - val n = ClockGroupSourceNode(Seq(ClockGroupSourceParameters())) - l.asyncClockGroupsNode := n - Some(n) - } - case _ => throw new Exception("Harness multiclock assumes BaseSubsystem") - } InModuleBody { - // this needs directionality so generateIOFromSignal works val clock_wire = Wire(Input(Clock())) val reset_wire = GenerateReset(chiptop, clock_wire) val (clock_io, clockIOCell) = IOCell.generateIOFromSignal(clock_wire, "clock") chiptop.iocells ++= clockIOCell - val div_clock = Pow2ClockDivider(clock_wire, 2) - implicitClockSourceNode.out.unzip._1.map { o => - o.clock := div_clock + referenceClockSource.out.unzip._1.map { o => + o.clock := clock_wire o.reset := reset_wire } - simpleClockGroupSourceNode.map { n => n.out.unzip._1.map { out: ClockGroupBundle => - out.member.elements.map { case (name, data) => - // This is mega hacks, how are you actually supposed to do this? - data.clock := (if (name.contains("core")) clock_wire else div_clock) - data.reset := reset_wire - } - }} - chiptop.harnessFunctions += ((th: HasHarnessSignalReferences) => { clock_io := th.harnessClock - Nil - }) + Nil }) } - } } diff --git a/generators/chipyard/src/main/scala/ConfigFragments.scala b/generators/chipyard/src/main/scala/ConfigFragments.scala index cfa465e72f..d7becac6ad 100644 --- a/generators/chipyard/src/main/scala/ConfigFragments.scala +++ b/generators/chipyard/src/main/scala/ConfigFragments.scala @@ -26,8 +26,7 @@ import sifive.blocks.devices.gpio._ import sifive.blocks.devices.uart._ import sifive.blocks.devices.spi._ -import chipyard.{BuildTop, BuildSystem, ClockingSchemeGenerators, ClockingSchemeKey, TestSuitesKey, TestSuiteHelper} - +import chipyard._ // ----------------------- // Common Config Fragments @@ -159,10 +158,6 @@ class WithNoSubsystemDrivenClocks extends Config((site, here, up) => { case SubsystemDriveAsyncClockGroupsKey => None }) -class WithTileDividedClock extends Config((site, here, up) => { - case ClockingSchemeKey => ClockingSchemeGenerators.harnessDividedClock -}) - class WithDMIDTM extends Config((site, here, up) => { case ExportDebug => up(ExportDebug, site).copy(protocols = Set(DMI)) }) @@ -170,3 +165,10 @@ class WithDMIDTM extends Config((site, here, up) => { class WithNoDebug extends Config((site, here, up) => { case DebugModuleKey => None }) + +class WithTileFrequency(fMHz: Double) extends ClockNameContainsAssignment("core", fMHz) + +class WithPeripheryBusFrequencyAsDefault extends Config((site, here, up) => { + case DefaultClockFrequencyKey => (site(PeripheryBusKey).dtsFrequency.get / (1000 * 1000)).toDouble +}) + diff --git a/generators/chipyard/src/main/scala/clocking/ClockDividerN.scala b/generators/chipyard/src/main/scala/clocking/ClockDividerN.scala new file mode 100644 index 0000000000..06ea2661f0 --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/ClockDividerN.scala @@ -0,0 +1,23 @@ +// See LICENSE for license details. + +package chipyard.clocking + +import chisel3._ +import chisel3.util._ + +class ClockDividerN(div: Int) extends BlackBox(Map("DIV" -> div)) with HasBlackBoxResource { + require(div > 0); + val io = IO(new Bundle { + val clk_out = Output(Clock()) + val clk_in = Input(Clock()) + }) + addResource("/vsrc/ClockDividerN.sv") +} + +object ClockDivideByN { + def apply(clockIn: Clock, div: Int): Clock = { + val clockDivider = Module(new ClockDividerN(div)) + clockDivider.io.clk_in := clockIn + clockDivider.io.clk_out + } +} diff --git a/generators/chipyard/src/main/scala/clocking/ClockGroupNamePrefixer.scala b/generators/chipyard/src/main/scala/clocking/ClockGroupNamePrefixer.scala new file mode 100644 index 0000000000..a5618c2dd6 --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/ClockGroupNamePrefixer.scala @@ -0,0 +1,70 @@ +package chipyard.clocking + +import chisel3._ + +import freechips.rocketchip.config.{Parameters} +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.prci._ + +/** + * 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). + */ +class ClockGroupParameterModifier( + sourceFn: ClockGroupSourceParameters => ClockGroupSourceParameters = { m => m }, + sinkFn: ClockGroupSinkParameters => ClockGroupSinkParameters = { s => s })( + implicit p: Parameters, v: ValName) extends LazyModule { + val node = ClockGroupAdapterNode(sourceFn, sinkFn) + lazy val module = new LazyRawModuleImp(this) { + (node.out zip node.in).map { case ((o, _), (i, _)) => + (o.member.data zip i.member.data).foreach { case (oD, iD) => oD := iD } + } + } +} + +/** + * Pushes the ClockGroup's name into each member's name field as a prefix. This is + * intended to be used before a ClockGroupAggregator so that sources from + * different aggregated ClockGroups can be disambiguated by their names. + */ +object ClockGroupNamePrefixer { + def apply()(implicit p: Parameters, valName: ValName): ClockGroupAdapterNode = + LazyModule(new ClockGroupParameterModifier(sinkFn = { s => s.copy(members = s.members.zipWithIndex.map { case (m, idx) => + m.copy(name = m.name match { + // This matches what the chisel would do if the names were not modified + case Some(clockName) => Some(s"${s.name}_${clockName}") + case None => Some(s"${s.name}_${idx}") + }) + })})).node +} + +/** + * [Word from on high is that Strings are in...] + * Overrides the take field of all clocks in a group, by attempting to apply a + * series of assignment functions: + * (name: String) => freq-in-MHz: Option[Double] + * to each sink. Later functions that return non-empty values take priority. + * The default if all functions return None. + */ +object ClockGroupFrequencySpecifier { + def apply( + assigners: Seq[(String) => Option[Double]], + defaultFreq: Double)( + implicit p: Parameters, valName: ValName): ClockGroupAdapterNode = { + + def lookupFrequencyForName(clock: ClockSinkParameters): ClockSinkParameters = { + require(clock.name.nonEmpty, "All clocks in clock group must have an assigned name") + val clockFreq = assigners.foldLeft(defaultFreq)( + (currentFreq, candidateFunc) => candidateFunc(clock.name.get).getOrElse(currentFreq)) + + clock.copy(take = clock.take match { + case Some(cp) => + println(s"Clock ${clock.name.get}: using diplomatically specified frequency of ${cp.freqMHz}.") + Some(cp) + case None => Some(ClockParameters(clockFreq)) + }) + } + + LazyModule(new ClockGroupParameterModifier(sinkFn = { s => s.copy(members = s.members.map(lookupFrequencyForName)) })).node + } +} diff --git a/generators/chipyard/src/main/scala/clocking/DividerOnlyClockGenerator.scala b/generators/chipyard/src/main/scala/clocking/DividerOnlyClockGenerator.scala new file mode 100644 index 0000000000..4355fc7115 --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/DividerOnlyClockGenerator.scala @@ -0,0 +1,97 @@ +package chipyard.clocking + +import chisel3._ + +import freechips.rocketchip.config.{Parameters} +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.prci._ +import freechips.rocketchip.util.ElaborationArtefacts + +import scala.collection.mutable +import scala.collection.immutable.ListMap + +/** + * TODO: figure out how much division is acceptable in our simulators and redefine this. + */ +object FrequencyUtils { + def computeReferenceFrequencyMHz( + requestedOutputs: Seq[ClockParameters], + maximumAllowableDivisor: Int = 0xFFFF): ClockParameters = { + require(requestedOutputs.nonEmpty) + require(!requestedOutputs.contains(0.0)) + val freqs = requestedOutputs.map(f => BigInt(Math.round(f.freqMHz * 1000 * 1000))) + val refFreq = freqs.reduce((a, b) => a * b / a.gcd(b)).toDouble / (1000 * 1000) + assert((refFreq / freqs.min.toDouble) < maximumAllowableDivisor.toDouble) + ClockParameters(refFreq) + } +} + +class SimplePllConfiguration(name: String, val sinks: Seq[ClockSinkParameters]) { + val referenceFreqMHz = FrequencyUtils.computeReferenceFrequencyMHz(sinks.flatMap(_.take)).freqMHz + val sinkDividerMap = ListMap((sinks.map({s => (s, Math.round(referenceFreqMHz / s.take.get.freqMHz).toInt) })):_*) + + private val preamble = s""" + |${name} Frequency Summary + | Input Reference Frequency: ${referenceFreqMHz} MHz\n""".stripMargin + private val outputSummaries = sinkDividerMap.map { case (sink, division) => + val requested = sink.take.get.freqMHz + val actual = referenceFreqMHz / division.toDouble + s" Output clock ${sink.name.get}, requested: ${requested} MHz, actual: ${actual} MHz (division of ${division})" + } + + val summaryString = preamble + outputSummaries.mkString("\n") + ElaborationArtefacts.add(s"${name}.freq-summary", summaryString) + println(summaryString) +} + +case class DividerOnlyClockGeneratorNode(pllName: String)(implicit valName: ValName) + extends MixedNexusNode(ClockImp, ClockGroupImp)( + dFn = { _ => ClockGroupSourceParameters() }, + uFn = { u => + require(u.size == 1) + require(!u.head.members.contains(None), + "All output clocks in group must set their take parameters. Use a ClockGroupDealiaser") + ClockSinkParameters( + name = Some(s"${pllName}_reference_input"), + take = Some(FrequencyUtils.computeReferenceFrequencyMHz(u.head.members.flatMap(_.take)))) } + ) + +/** + * Generates a digital-divider-only PLL model that verilator can simulate. + * Inspects all take-specified frequencies in the output ClockGroup, calculates a + * fast reference clock (roughly LCM(requested frequencies)) which is passed up the + * diplomatic graph, and then generates dividers for each unique requested + * frequency. + */ + +class DividerOnlyClockGenerator(pllName: String)(implicit p: Parameters, valName: ValName) extends LazyModule { + val node = DividerOnlyClockGeneratorNode(pllName) + + lazy val module = new LazyRawModuleImp(this) { + require(node.out.size == 1, "Idealized PLL expects to generate a single output clock group. Use a ClockGroupAggregator") + val (refClock, ClockEdgeParameters(_, refSinkParam, _, _)) = node.in.head + val (outClocks, ClockGroupEdgeParameters(_, outSinkParams, _, _)) = node.out.head + + val referenceFreq = refSinkParam.take.get.freqMHz + val pllConfig = new SimplePllConfiguration(pllName, outSinkParams.members) + + val dividedClocks = mutable.HashMap[Int, Clock]() + def instantiateDivider(div: Int): Clock = { + val divider = Module(new ClockDividerN(div)) + divider.suggestName(s"ClockDivideBy${div}") + divider.io.clk_in := refClock.clock + dividedClocks(div) = divider.io.clk_out + divider.io.clk_out + } + + for (((sinkBName, sinkB), sinkP) <- outClocks.member.elements.zip(outSinkParams.members)) { + val div = pllConfig.sinkDividerMap(sinkP) + sinkB.clock := dividedClocks.getOrElse(div, instantiateDivider(div)) + sinkB.reset := refClock.reset + } + } +} + +object DividerOnlyClockGenerator { + def apply()(implicit p: Parameters, valName: ValName) = LazyModule(new DividerOnlyClockGenerator(valName.name)).node +} diff --git a/generators/chipyard/src/main/scala/config/AbstractConfig.scala b/generators/chipyard/src/main/scala/config/AbstractConfig.scala index 5b356c740b..9b04788c45 100644 --- a/generators/chipyard/src/main/scala/config/AbstractConfig.scala +++ b/generators/chipyard/src/main/scala/config/AbstractConfig.scala @@ -43,6 +43,7 @@ class AbstractConfig extends Config( new chipyard.config.WithUART ++ // add a UART new chipyard.config.WithL2TLBs(1024) ++ // use L2 TLBs new chipyard.config.WithNoSubsystemDrivenClocks ++ // drive the subsystem diplomatic clocks from ChipTop instead of using implicit clocks + new chipyard.config.WithPeripheryBusFrequencyAsDefault ++ // Unspecified clocks will match the frequency specified by the pbus dtsFrequency parameter new freechips.rocketchip.subsystem.WithJtagDTM ++ // set the debug module to expose a JTAG port new freechips.rocketchip.subsystem.WithNoMMIOPort ++ // no top-level MMIO master port (overrides default set in rocketchip) new freechips.rocketchip.subsystem.WithNoSlavePort ++ // no top-level MMIO slave port (overrides default set in rocketchip) diff --git a/generators/chipyard/src/main/scala/config/RocketConfigs.scala b/generators/chipyard/src/main/scala/config/RocketConfigs.scala index 17a51662d7..11399a62e2 100644 --- a/generators/chipyard/src/main/scala/config/RocketConfigs.scala +++ b/generators/chipyard/src/main/scala/config/RocketConfigs.scala @@ -174,10 +174,8 @@ class MMIORocketConfig extends Config( new freechips.rocketchip.subsystem.WithNBigCores(1) ++ new chipyard.config.AbstractConfig) -// NOTE: This config doesn't work yet because SimWidgets in the TestHarness -// always get the TestHarness clock. The Tiles and Uncore receive the correct clocks class DividedClockRocketConfig extends Config( - new chipyard.config.WithTileDividedClock ++ // Put the Tile on its own clock domain + new chipyard.config.WithTileFrequency(200.0) ++ new freechips.rocketchip.subsystem.WithRationalRocketTiles ++ // Add rational crossings between RocketTile and uncore new freechips.rocketchip.subsystem.WithNBigCores(1) ++ new chipyard.config.AbstractConfig) diff --git a/generators/chipyard/src/main/scala/config/TracegenConfigs.scala b/generators/chipyard/src/main/scala/config/TracegenConfigs.scala index 78cb6851b5..f9980bf671 100644 --- a/generators/chipyard/src/main/scala/config/TracegenConfigs.scala +++ b/generators/chipyard/src/main/scala/config/TracegenConfigs.scala @@ -10,6 +10,7 @@ class AbstractTraceGenConfig extends Config( new chipyard.iobinders.WithTraceGenSuccessPunchthrough ++ new chipyard.config.WithTracegenSystem ++ new chipyard.config.WithNoSubsystemDrivenClocks ++ + new chipyard.config.WithPeripheryBusFrequencyAsDefault ++ new freechips.rocketchip.subsystem.WithCoherentBusTopology ++ new freechips.rocketchip.groundtest.GroundTestBaseConfig) diff --git a/generators/firechip/src/main/scala/BridgeBinders.scala b/generators/firechip/src/main/scala/BridgeBinders.scala index 4943e13033..653f8026e3 100644 --- a/generators/firechip/src/main/scala/BridgeBinders.scala +++ b/generators/firechip/src/main/scala/BridgeBinders.scala @@ -69,9 +69,7 @@ class WithSerialBridge extends OverrideHarnessBinder({ (system: CanHavePeripheryTLSerial, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[SerialIO]]) => { ports.map { p => val ram = SerialAdapter.connectHarnessRAM(system.serdesser.get, p, th.harnessReset) - withClockAndReset(p.clock, th.harnessReset) { - SerialBridge(p.clock, ram.module.io.tsi_ser, MainMemoryConsts.globalName)(GetSystemParameters(system)) - } + SerialBridge(p.clock, ram.module.io.tsi_ser, MainMemoryConsts.globalName)(GetSystemParameters(system)) } Nil } @@ -80,7 +78,7 @@ class WithSerialBridge extends OverrideHarnessBinder({ class WithNICBridge extends OverrideHarnessBinder({ (system: CanHavePeripheryIceNIC, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[NICIOvonly]]) => { val p: Parameters = GetSystemParameters(system) - ports.map { n => withClockAndReset(n.clock, th.harnessReset) { NICBridge(n.clock, n.bits)(p) } } + ports.map { n => NICBridge(n.clock, n.bits)(p) } Nil } }) @@ -120,11 +118,7 @@ class WithFASEDBridge extends OverrideHarnessBinder({ class WithTracerVBridge extends ComposeHarnessBinder({ (system: CanHaveTraceIOModuleImp, th: HasHarnessSignalReferences, ports: Seq[TraceOutputTop]) => { - ports.map { p => - p.traces.map( - tileTrace => withClockAndReset(tileTrace.clock, tileTrace.reset) { TracerVBridge(tileTrace)(system.p) } - ) - } + ports.map { p => p.traces.map(tileTrace => TracerVBridge(tileTrace)(system.p)) } Nil } }) diff --git a/generators/firechip/src/main/scala/FireSim.scala b/generators/firechip/src/main/scala/FireSim.scala index d2ef4e60b9..90fd473a87 100644 --- a/generators/firechip/src/main/scala/FireSim.scala +++ b/generators/firechip/src/main/scala/FireSim.scala @@ -8,14 +8,15 @@ 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} -import freechips.rocketchip.util.{ResetCatchAndSync} +import freechips.rocketchip.diplomacy.{LazyModule, InModuleBody, ValName} +import freechips.rocketchip.util.{ResetCatchAndSync, RecordMap} import midas.widgets.{Bridge, PeekPokeBridge, RationalClockBridge, RationalClock} import chipyard._ import chipyard.harness._ import chipyard.iobinders._ +import chipyard.clocking.{FrequencyUtils, ClockGroupNamePrefixer, ClockGroupFrequencySpecifier, SimplePllConfiguration} // Determines the number of times to instantiate the DUT in the harness. // Subsumes legacy supernode support @@ -25,16 +26,6 @@ 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 { @@ -43,107 +34,113 @@ object NodeIdx { def apply(): Int = idx } -class WithFireSimSimpleClocks extends Config((site, here, up) => { - case ClockingSchemeKey => { chiptop: ChipTop => - implicit val p = chiptop.p - val implicitClockSourceNode = ClockSourceNode(Seq(ClockSourceParameters())) - chiptop.implicitClockSinkNode := implicitClockSourceNode +/** + * Under FireSim's current multiclock implementation there can be only a + * single clock bridge. This requires, therefore, that it be instantiated in + * the harness and reused across all supernode instances. This class attempts to + * memoize its instantiation such that it can be referenced from within a ClockScheme function. + */ +class ClockBridgeInstantiator { + private var _clockRecord: Option[RecordMap[Clock]] = None - // Drive the diplomaticclock graph of the DigitalTop (if present) - val simpleClockGroupSourceNode = chiptop.lazySystem match { - case l: BaseSubsystem if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) => { - val n = ClockGroupSourceNode(Seq(ClockGroupSourceParameters())) - l.asyncClockGroupsNode := n - Some(n) - } - case _ => None - } + def getClockRecord: RecordMap[Clock] = _clockRecord.get - InModuleBody { - val clock = IO(Input(Clock())).suggestName("clock") - val reset = IO(Input(Reset())).suggestName("reset") + def getClockRecordOrInstantiate(allClocks: Seq[RationalClock], baseClockName: String): RecordMap[Clock] = { + if (_clockRecord.isEmpty) { + require(allClocks.exists(_.name == baseClockName), + s"Provided base-clock name, ${baseClockName}, does not match a defined clock. Available clocks:\n " + + allClocks.map(_.name).mkString("\n ")) - implicitClockSourceNode.out.unzip._1.map { o => - o.clock := clock - o.reset := reset + val baseClock = allClocks.find(_.name == baseClockName).get + val simplified = allClocks.map { c => + c.copy(multiplier = c.multiplier * baseClock.divisor, divisor = c.divisor * baseClock.multiplier) + .simplify } - simpleClockGroupSourceNode.map { n => n.out.unzip._1.map { out: ClockGroupBundle => - out.member.data.foreach { o => - o.clock := clock - o.reset := reset - } - }} + /** + * Removes clocks that have the same frequency before instantiating the + * clock bridge to avoid unnecessary BUFGCE use. + */ + val distinct = simplified.foldLeft(Seq(RationalClock(baseClockName, 1, 1))) { case (list, candidate) => + if (list.exists { clock => clock.equalFrequency(candidate) }) list else list :+ candidate + } - chiptop.harnessFunctions += ((th: HasHarnessSignalReferences) => { - clock := th.harnessClock - reset := th.harnessReset - Nil - }) + val clockBridge = Module(new RationalClockBridge(distinct)) + val cbVecTuples = distinct.zip(clockBridge.io.clocks) + val outputWire = Wire(RecordMap(simplified.map { c => (c.name, Clock()) }:_*)) + for (parameter <- simplified) { + val (_, cbClockField) = cbVecTuples.find(_._1.equalFrequency(parameter)).get + outputWire(parameter.name).get := cbClockField + } + _clockRecord = Some(outputWire) } + getClockRecord } -}) +} -class WithFireSimRationalTileDomain(multiplier: Int, divisor: Int) extends Config((site, here, up) => { - case FireSimClockKey => FireSimClockParameters(Seq(RationalClock("TileDomain", multiplier, divisor))) +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 + // Figure out what provides this in the chipyard scheme + implicit val valName = ValName("FireSimClocking") - val implicitClockSourceNode = ClockSourceNode(Seq(ClockSourceParameters())) - chiptop.implicitClockSinkNode := implicitClockSourceNode - - // Drive the diplomaticclock graph of the DigitalTop (if present) - val simpleClockGroupSourceNode = chiptop.lazySystem match { - case l: BaseSubsystem if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) => { - val n = ClockGroupSourceNode(Seq(ClockGroupSourceParameters())) - l.asyncClockGroupsNode := n - Some(n) - } - case _ => None + // Requires existence of undriven asyncClockGroups in subsystem + val systemAsyncClockGroup = chiptop.lazySystem match { + case l: BaseSubsystem if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) => + l.asyncClockGroupsNode } + val aggregator = LazyModule(new ClockGroupAggregator("allClocks")).node + (chiptop.implicitClockSinkNode := ClockGroup() := aggregator) + (systemAsyncClockGroup := ClockGroupNamePrefixer() := aggregator) + + val inputClockSource = ClockGroupSourceNode(Seq(ClockGroupSourceParameters())) + + (aggregator + := ClockGroupFrequencySpecifier(p(ClockFrequencyAssignersKey), p(DefaultClockFrequencyKey)) + := inputClockSource) + + 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") + val (clockGroupBundle, clockGroupEdge) = inputClockSource.out.head + val input_clocks = IO(Input(RecordMap((clockGroupEdge.sink.members.map { m => (m.name.get, Clock()) }):_* ))) + .suggestName("clocks") + val reset = IO(Input(Reset())).suggestName("reset") - implicitClockSourceNode.out.unzip._1.map { o => - o.clock := uncore_clock - o.reset := reset + (clockGroupBundle.member.data zip input_clocks.data).foreach { case (clockBundle, inputClock) => + clockBundle.clock := inputClock } - simpleClockGroupSourceNode.map { n => n.out.unzip._1.map { out: ClockGroupBundle => - out.member.elements.map { case (name, data) => - // This is mega hacks, how are you actually supposed to do this? - if (name.contains("core")) { - data.clock := tile_clock - data.reset := ResetCatchAndSync(tile_clock, reset.asBool) - } else { - data.clock := uncore_clock - data.reset := reset - } + // Assign resets. The synchronization scheme is still WIP. + for ((name, clockBundle) <- clockGroupBundle.member.elements) { + if (name.contains("core")) { + clockBundle.reset := ResetCatchAndSync(clockBundle.clock, reset.asBool) + } else { + clockBundle.reset := reset } - }} + } + + val pllConfig = new SimplePllConfiguration("FireSim RationalClockBridge", clockGroupEdge.sink.members) + val rationalClockSpecs = for ((sinkP, division) <- pllConfig.sinkDividerMap) yield { + RationalClock(sinkP.name.get, 1, division) + } chiptop.harnessFunctions += ((th: HasHarnessSignalReferences) => { - 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 - }) + reset := th.harnessReset + input_clocks := p(ClockBridgeInstantiatorKey) + .getClockRecordOrInstantiate(rationalClockSpecs.toSeq, p(FireSimBaseClockNameKey)) + Nil }) } } }) class FireSim(implicit val p: Parameters) extends RawModule with HasHarnessSignalReferences { 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 harnessClock = Wire(Clock()) val harnessReset = WireInit(false.B) val peekPokeBridge = PeekPokeBridge(harnessClock, harnessReset) def dutReset = { require(false, "dutReset should not be used in Firesim"); false.B } @@ -165,8 +162,7 @@ class FireSim(implicit val p: Parameters) extends RawModule with HasHarnessSigna d.harnessFunctions.foreach(_(this)) ApplyHarnessBinders(this, d.lazySystem, p(HarnessBinders), d.portMap.toMap) } - - NodeIdx.increment() } + harnessClock := p(ClockBridgeInstantiatorKey).getClockRecord("implicit_clock").get } diff --git a/generators/firechip/src/main/scala/TargetConfigs.scala b/generators/firechip/src/main/scala/TargetConfigs.scala index 678afccfb5..b70ef64706 100644 --- a/generators/firechip/src/main/scala/TargetConfigs.scala +++ b/generators/firechip/src/main/scala/TargetConfigs.scala @@ -194,7 +194,7 @@ class FireSimArianeConfig extends Config( //* Multiclock Configurations //*********************************************************************************/ class FireSimMulticlockRocketConfig extends Config( - new WithFireSimRationalTileDomain(2, 1) ++ + new chipyard.config.WithTileFrequency(6400.0) ++ //lol new WithDefaultFireSimBridges ++ new WithDefaultMemModel ++ new WithFireSimConfigTweaks ++ diff --git a/sims/firesim b/sims/firesim index 3dbe8aee3f..801baeb901 160000 --- a/sims/firesim +++ b/sims/firesim @@ -1 +1 @@ -Subproject commit 3dbe8aee3f917079e7319391bac5d23d2ba5e6de +Subproject commit 801baeb901c207beb0511311e09ae10e0dbb8b5f