From 895bacea9884248493c0e44d3a4d1ad6b91e786d Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Mon, 24 Aug 2020 11:01:29 -0700 Subject: [PATCH 01/19] WIP - Simple divider-only PLL generation flow --- .../src/main/resources/vsrc/ClockDividerN.sv | 40 +++++++++++ .../chipyard/src/main/scala/Clocks.scala | 36 ++++++++++ .../src/main/scala/ConfigFragments.scala | 27 +++++++- .../src/main/scala/GenericAttachParams.scala | 38 +++++++++++ .../main/scala/clocking/ClockDividerN.scala | 23 +++++++ .../scala/clocking/ClockGroupDealiaser.scala | 51 ++++++++++++++ .../scala/clocking/ClockNodeInjectors.scala | 29 ++++++++ .../main/scala/clocking/IdealizedPLL.scala | 68 +++++++++++++++++++ .../src/main/scala/config/RocketConfigs.scala | 9 ++- 9 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv create mode 100644 generators/chipyard/src/main/scala/GenericAttachParams.scala create mode 100644 generators/chipyard/src/main/scala/clocking/ClockDividerN.scala create mode 100644 generators/chipyard/src/main/scala/clocking/ClockGroupDealiaser.scala create mode 100644 generators/chipyard/src/main/scala/clocking/ClockNodeInjectors.scala create mode 100644 generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala 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 00000000..33f7a05b --- /dev/null +++ b/generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv @@ -0,0 +1,40 @@ +// See LICENSE for license details. + +/** + * An unsynthesizable divide-by-N clock divider. + * Duty cycle is 100 * (ceil(DIV / 2)) / 2. + */ + +module ClockDividerN #(parameter DIV)(output logic clk_out = 1'b0, input clk_in); + + localparam DIV_COUNTER_WIDTH = $clog2(DIV); + localparam LOW_CYCLES = DIV / 2; + + 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 [DIV_COUNTER_WIDTH - 1: 0] count = '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 == (DIV - 1)) begin + clk_out = 1'b0; + count <= '0; + end + else begin + if (count == (LOW_CYCLES - 1)) begin + clk_out = 1'b1; + end + count <= count + 1'b1; + end + end + end + endgenerate +endmodule // ClockDividerN diff --git a/generators/chipyard/src/main/scala/Clocks.scala b/generators/chipyard/src/main/scala/Clocks.scala index a1ff1b0f..01d6c828 100644 --- a/generators/chipyard/src/main/scala/Clocks.scala +++ b/generators/chipyard/src/main/scala/Clocks.scala @@ -12,6 +12,8 @@ import freechips.rocketchip.util.{ResetCatchAndSync, Pow2ClockDivider} import barstools.iocell.chisel._ +import chipyard.clocking.{IdealizedPLL, ClockGroupDealiaser} + /** * Chipyard provides three baseline, top-level reset schemes, set using the * [[GlobalResetSchemeKey]] in a Parameters instance. These are: @@ -173,6 +175,40 @@ object ClockingSchemeGenerators { Nil }) } + } + val idealizedPLL: ChipTop => Unit = { chiptop => + implicit val p = chiptop.p + + // Requires existence of undriven asyncClockGroups in subsystem + val systemAsyncClockGroup = chiptop.lSystem match { + case l: BaseSubsystem if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) => + l.asyncClockGroupsNode + } + + val aggregator = ClockGroupAggregator() + chiptop.implicitClockSinkNode := ClockGroup() := aggregator + systemAsyncClockGroup := aggregator + + val referenceClockSource = ClockSourceNode(Seq(ClockSourceParameters())) + aggregator := ClockGroupDealiaser() := IdealizedPLL() := referenceClockSource + + InModuleBody { + + val clock_wire = Wire(Input(Clock())) + val reset_wire = GenerateReset(chiptop, clock_wire) + val (clock_io, clockIOCell) = IOCell.generateIOFromSignal(clock_wire, Some("iocell_clock")) + chiptop.iocells ++= clockIOCell + clock_io.suggestName("clock") + + referenceClockSource.out.unzip._1.map { o => + o.clock := clock_wire + o.reset := reset_wire + } + + chiptop.harnessFunctions += ((th: HasHarnessUtils) => { + clock_io := th.harnessClock + Nil }) + } } } diff --git a/generators/chipyard/src/main/scala/ConfigFragments.scala b/generators/chipyard/src/main/scala/ConfigFragments.scala index cfa465e7..9fb3adb2 100644 --- a/generators/chipyard/src/main/scala/ConfigFragments.scala +++ b/generators/chipyard/src/main/scala/ConfigFragments.scala @@ -28,7 +28,11 @@ import sifive.blocks.devices.spi._ import chipyard.{BuildTop, BuildSystem, ClockingSchemeGenerators, ClockingSchemeKey, TestSuitesKey, TestSuiteHelper} - +// Imports for multiclock sketch +import boom.common.{BoomTile, BoomTileParams} +import ariane.{ArianeTile, ArianeTileParams} +import chipyard.{GenericallyAttachableTile, GenericCrossingParams} +import chipyard.clocking.{ClockNodeInjectionUtils } // ----------------------- // Common Config Fragments // ----------------------- @@ -170,3 +174,24 @@ class WithDMIDTM extends Config((site, here, up) => { class WithNoDebug extends Config((site, here, up) => { case DebugModuleKey => None }) + + +// Multiclock sketch +class WithForcedTileFrequency(fMHz: Double) extends Config((site, here, up) => { + case TilesLocated(InSubsystem) => + val genericAttachParams = up(TilesLocated(InSubsystem), site) map { + case b: BoomTileAttachParams => GenericallyAttachableTile[BoomTile]( + b.tileParams, GenericCrossingParams(b.crossingParams), b.lookup) + case r: RocketTileAttachParams => GenericallyAttachableTile[RocketTile]( + r.tileParams, GenericCrossingParams(r.crossingParams), r.lookup) + case a: ArianeTileAttachParams => GenericallyAttachableTile[ArianeTile]( + a.tileParams, GenericCrossingParams(a.crossingParams), a.lookup) + case g: GenericallyAttachableTile[_] => g + } + genericAttachParams.map(p => p.copy(crossingParams = p.crossingParams.copy( + injectClockNodeFunc = ClockNodeInjectionUtils.forceTakeFrequency(fMHz)))) +}) + +class WithIdealizedPLL extends Config((site, here, up) => { + case ChipyardClockKey => ClockDrivers.idealizedPLL +}) diff --git a/generators/chipyard/src/main/scala/GenericAttachParams.scala b/generators/chipyard/src/main/scala/GenericAttachParams.scala new file mode 100644 index 00000000..f90598af --- /dev/null +++ b/generators/chipyard/src/main/scala/GenericAttachParams.scala @@ -0,0 +1,38 @@ + +package chipyard + +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.config.{Field, Parameters} +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.tile.{LookupByHartIdImpl, TileParams, InstantiableTileParams, BaseTile} + +import chipyard.clocking.ClockNodeInjectionUtils._ + +case class GenericCrossingParams( + crossingType: ClockCrossingType = SynchronousCrossing(), + master: TilePortParamsLike = TileMasterPortParams(), + slave: TilePortParamsLike = TileSlavePortParams(), + mmioBaseAddressPrefixWhere: TLBusWrapperLocation = CBUS, + injectClockNodeFunc: InjectClockNodeFunc = injectIdentityClockNode, + forceSeparateClockReset: Boolean = false) extends TileCrossingParamsLike { + + def injectClockNode(a: Attachable)(implicit p: Parameters) = injectClockNodeFunc(a, p) +} + +object GenericCrossingParams { + def apply(params: TileCrossingParamsLike): GenericCrossingParams = GenericCrossingParams( + params.crossingType, + params.master, + params.slave, + params.mmioBaseAddressPrefixWhere, + (a: Attachable, p: Parameters) => params.injectClockNode(a)(p), + params.forceSeparateClockReset) +} + +case class GenericallyAttachableTile[TT <: BaseTile]( + tileParams: InstantiableTileParams[TT], + crossingParams: GenericCrossingParams, + lookup: LookupByHartIdImpl) extends CanAttachTile { + type TileType = TT +} + 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 00000000..c9513b88 --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/ClockDividerN.scala @@ -0,0 +1,23 @@ +// See LICENSE.SiFive 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/ClockGroupDealiaser.scala b/generators/chipyard/src/main/scala/clocking/ClockGroupDealiaser.scala new file mode 100644 index 00000000..3ffea3c0 --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/ClockGroupDealiaser.scala @@ -0,0 +1,51 @@ +package chipyard.clocking + +import chisel3._ + +import freechips.rocketchip.config.{Parameters} +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.prci._ + +/** + * Somewhat hacky. Since not all clocks in a clock group specify a taken frequency + * current, this LazyModule attempts to dealias them, by finding a specified + * clock whose name has the longest matching prefix. + * + * Perhaps another, simpler solution would be to pass a default. + * + */ + +case class ClockGroupDealiaserNode()(implicit valName: ValName) + extends NexusNode(ClockGroupImp)( + dFn = { _ => ClockGroupSourceParameters() }, + uFn = { u => + require(u.size == 1) + val takenClocks = u.head.members.filter(_.take.nonEmpty) + require(takenClocks.nonEmpty, + "At least one sink clock in clock group must specify its take parameter") + u.head.copy(members = takenClocks) + }) + +class ClockGroupDealiaser(name: String)(implicit p: Parameters) extends LazyModule { + val node = ClockGroupDealiaserNode() + + lazy val module = new LazyRawModuleImp(this) { + require(node.out.size == 1, "Must use a ClockGroupAggregator") + val (outClocks, e @ ClockGroupEdgeParameters(_, outSinkParams, _, _)) = node.out.head + val (inClocks, ClockGroupEdgeParameters(_, inSinkParams, _, _)) = node.in.head + val inMap = inClocks.member.data.zip(inSinkParams.members).map({ case (b, p) => p.name -> b}).toMap + + for (((outBName, outB), outName) <- outClocks.member.elements.zip(outSinkParams.members.map(_.name))) { + val inClock = inMap.getOrElse(outName, throw new Exception(""" + | No clock in input group with name: Option matching ${outName}. At least one clock + | with the same must specify a frequency in its take parameter.""".stripMargin)) + // This will be removed. + dontTouch(outB) + outB := inClock + } + } +} + +object ClockGroupDealiaser { + def apply()(implicit p: Parameters, valName: ValName) = LazyModule(new ClockGroupDealiaser(valName.name)).node +} diff --git a/generators/chipyard/src/main/scala/clocking/ClockNodeInjectors.scala b/generators/chipyard/src/main/scala/clocking/ClockNodeInjectors.scala new file mode 100644 index 00000000..981ed327 --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/ClockNodeInjectors.scala @@ -0,0 +1,29 @@ + +package chipyard.clocking + +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.config.{Parameters} +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.prci.{ClockNode, ClockTempNode, ClockAdapterNode, ClockParameters} +/** + * An adapter node hack c that just throws out the existing sink node + * clock parameters in favor of the provided ones. + */ +class ForceTakeClock(clockParams: Option[ClockParameters])(implicit p: Parameters, v: ValName) extends LazyModule { + val node = ClockAdapterNode(sinkFn = { s => s.copy(take = clockParams) }) + lazy val module = new LazyRawModuleImp(this) { + (node.out zip node.in) map { case ((o, _), (i, _)) => o := i } + } +} + +object ForceTakeClock { + def apply(clockParams: Option[ClockParameters])(implicit p: Parameters, v: ValName): ClockAdapterNode = + LazyModule(new ForceTakeClock(clockParams)).node +} + +object ClockNodeInjectionUtils { + type InjectClockNodeFunc = (Attachable, Parameters) => ClockNode + val injectIdentityClockNode: InjectClockNodeFunc = (a: Attachable, p: Parameters) => ClockTempNode() + def forceTakeFrequency(freqMHz: Double): InjectClockNodeFunc = + (a: Attachable, p: Parameters) => ForceTakeClock(Some(ClockParameters(freqMHz)))(p, ValName("ForcedTakeClock")) +} diff --git a/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala b/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala new file mode 100644 index 00000000..3de99e8e --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala @@ -0,0 +1,68 @@ +package chipyard.clocking + +import chisel3._ + +import freechips.rocketchip.config.{Parameters} +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.prci._ + +import scala.collection.mutable + +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) + } +} + +case class IdealizedPLLNode(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)))) } + ) + +class IdealizedPLL(pllName: String)(implicit p: Parameters, valName: ValName) extends LazyModule { + val node = IdealizedPLLNode(pllName) + + lazy val module = new LazyRawModuleImp(this) { + require(node.out.size == 1, "Must use a ClockGroupAggregator") + val (refClock, ClockEdgeParameters(_, refSinkParam, _, _)) = node.in.head + val (outClocks, ClockGroupEdgeParameters(_, outSinkParams, _, _)) = node.out.head + + val referenceFreq = refSinkParam.take.get.freqMHz + val requestedFreqs = outSinkParams.members.map(m => m.name -> m.take) + + 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 requested = sinkP.take.get.freqMHz + val div = Math.round(referenceFreq / requested).toInt + val actual = referenceFreq / div.toDouble + println(s"Clock ${sinkBName}, requested freq: ${requested} MHz. Actual freq: ${actual} MHz via division of ${div}") + sinkB.clock := dividedClocks.getOrElse(div, instantiateDivider(div)) + } + } +} + +object IdealizedPLL { + def apply()(implicit p: Parameters, valName: ValName) = LazyModule(new IdealizedPLL(valName.name)).node +} diff --git a/generators/chipyard/src/main/scala/config/RocketConfigs.scala b/generators/chipyard/src/main/scala/config/RocketConfigs.scala index 16d298fb..a4e1af72 100644 --- a/generators/chipyard/src/main/scala/config/RocketConfigs.scala +++ b/generators/chipyard/src/main/scala/config/RocketConfigs.scala @@ -182,4 +182,11 @@ class DividedClockRocketConfig extends Config( new freechips.rocketchip.subsystem.WithNBigCores(1) ++ new chipyard.config.AbstractConfig) - +// Multiclock Sketch +class ForcedClockRocketConfig extends Config( + new chipyard.config.WithForcedTileFrequency(200) ++ + new chipyard.config.WithIdealizedPLL ++ // Put the Tile on its own clock domain + //new chipyard.config.WithTileDividedClock ++ // Put the Tile on its own clock domain + new freechips.rocketchip.subsystem.WithRationalRocketTiles ++ // Add rational crossings between RocketTile and uncore + new freechips.rocketchip.subsystem.WithNBigCores(1) ++ + new chipyard.config.AbstractConfig) From 8e4dedcecfe7c0f7a4e1a5a0e3cfdc867479cfcd Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Wed, 16 Sep 2020 16:30:00 -0700 Subject: [PATCH 02/19] Remove require guard on divided configs --- generators/chipyard/src/main/scala/Clocks.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/generators/chipyard/src/main/scala/Clocks.scala b/generators/chipyard/src/main/scala/Clocks.scala index 01d6c828..afe04eaf 100644 --- a/generators/chipyard/src/main/scala/Clocks.scala +++ b/generators/chipyard/src/main/scala/Clocks.scala @@ -134,8 +134,6 @@ object ClockingSchemeGenerators { 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 From b8d3e4a66d6e8a984bc49cc0ca1d11f0d49a87ed Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Wed, 16 Sep 2020 16:30:25 -0700 Subject: [PATCH 03/19] Update Idealized PLL config --- generators/chipyard/src/main/scala/Clocks.scala | 4 ++-- generators/chipyard/src/main/scala/ConfigFragments.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/generators/chipyard/src/main/scala/Clocks.scala b/generators/chipyard/src/main/scala/Clocks.scala index afe04eaf..7c9ade21 100644 --- a/generators/chipyard/src/main/scala/Clocks.scala +++ b/generators/chipyard/src/main/scala/Clocks.scala @@ -179,7 +179,7 @@ object ClockingSchemeGenerators { implicit val p = chiptop.p // Requires existence of undriven asyncClockGroups in subsystem - val systemAsyncClockGroup = chiptop.lSystem match { + val systemAsyncClockGroup = chiptop.lazySystem match { case l: BaseSubsystem if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) => l.asyncClockGroupsNode } @@ -204,7 +204,7 @@ object ClockingSchemeGenerators { o.reset := reset_wire } - chiptop.harnessFunctions += ((th: HasHarnessUtils) => { + chiptop.harnessFunctions += ((th: HasHarnessSignalReferences) => { clock_io := th.harnessClock Nil }) } diff --git a/generators/chipyard/src/main/scala/ConfigFragments.scala b/generators/chipyard/src/main/scala/ConfigFragments.scala index 9fb3adb2..03ccdbca 100644 --- a/generators/chipyard/src/main/scala/ConfigFragments.scala +++ b/generators/chipyard/src/main/scala/ConfigFragments.scala @@ -193,5 +193,5 @@ class WithForcedTileFrequency(fMHz: Double) extends Config((site, here, up) => { }) class WithIdealizedPLL extends Config((site, here, up) => { - case ChipyardClockKey => ClockDrivers.idealizedPLL + case ClockingSchemeKey => ClockingSchemeGenerators.idealizedPLL }) From cfa7e30d95f82acc69811753183ca6b681f8954a Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Thu, 17 Sep 2020 11:32:51 -0700 Subject: [PATCH 04/19] [clocks] Fix comment in ClockDividerN --- generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv b/generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv index 33f7a05b..868b0eee 100644 --- a/generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv +++ b/generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv @@ -2,7 +2,7 @@ /** * An unsynthesizable divide-by-N clock divider. - * Duty cycle is 100 * (ceil(DIV / 2)) / 2. + * Duty cycle is 100 * (ceil(DIV / 2)) / DIV. */ module ClockDividerN #(parameter DIV)(output logic clk_out = 1'b0, input clk_in); From 6a26a350eee9d67032e6710d20ad59822bd51390 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Thu, 17 Sep 2020 11:33:26 -0700 Subject: [PATCH 05/19] [clocks] Update dealiaser based on feedback --- .../src/main/scala/clocking/ClockGroupDealiaser.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/generators/chipyard/src/main/scala/clocking/ClockGroupDealiaser.scala b/generators/chipyard/src/main/scala/clocking/ClockGroupDealiaser.scala index 3ffea3c0..54b384e9 100644 --- a/generators/chipyard/src/main/scala/clocking/ClockGroupDealiaser.scala +++ b/generators/chipyard/src/main/scala/clocking/ClockGroupDealiaser.scala @@ -9,7 +9,7 @@ import freechips.rocketchip.prci._ /** * Somewhat hacky. Since not all clocks in a clock group specify a taken frequency * current, this LazyModule attempts to dealias them, by finding a specified - * clock whose name has the longest matching prefix. + * clock with a matching name. * * Perhaps another, simpler solution would be to pass a default. * @@ -30,17 +30,16 @@ class ClockGroupDealiaser(name: String)(implicit p: Parameters) extends LazyModu val node = ClockGroupDealiaserNode() lazy val module = new LazyRawModuleImp(this) { - require(node.out.size == 1, "Must use a ClockGroupAggregator") + require(node.out.size == 1 && node.in.size == 1, + "ClockGroupDealiaser requires a single ClockGroup, please use a ClockGroupAggregator") val (outClocks, e @ ClockGroupEdgeParameters(_, outSinkParams, _, _)) = node.out.head val (inClocks, ClockGroupEdgeParameters(_, inSinkParams, _, _)) = node.in.head val inMap = inClocks.member.data.zip(inSinkParams.members).map({ case (b, p) => p.name -> b}).toMap for (((outBName, outB), outName) <- outClocks.member.elements.zip(outSinkParams.members.map(_.name))) { val inClock = inMap.getOrElse(outName, throw new Exception(""" - | No clock in input group with name: Option matching ${outName}. At least one clock + | No clock in input group with name option matching ${outName}. At least one clock | with the same must specify a frequency in its take parameter.""".stripMargin)) - // This will be removed. - dontTouch(outB) outB := inClock } } From 0f33ea3999f0c7cf5610e4099178c683ada680f8 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Thu, 17 Sep 2020 11:37:56 -0700 Subject: [PATCH 06/19] [clocks] Stringly specified clock frequencies; DRY out schemes --- .../chipyard/src/main/scala/ChipTop.scala | 2 +- .../chipyard/src/main/scala/Clocks.scala | 122 ++++-------------- .../src/main/scala/ConfigFragments.scala | 39 +++--- .../clocking/ClockGroupNamePrefixer.scala | 65 ++++++++++ .../main/scala/clocking/IdealizedPLL.scala | 20 ++- .../src/main/scala/config/RocketConfigs.scala | 13 +- 6 files changed, 125 insertions(+), 136 deletions(-) create mode 100644 generators/chipyard/src/main/scala/clocking/ClockGroupNamePrefixer.scala diff --git a/generators/chipyard/src/main/scala/ChipTop.scala b/generators/chipyard/src/main/scala/ChipTop.scala index dfe08780..1cef2180 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 7c9ade21..609bf8a0 100644 --- a/generators/chipyard/src/main/scala/Clocks.scala +++ b/generators/chipyard/src/main/scala/Clocks.scala @@ -6,13 +6,13 @@ 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.{IdealizedPLL, ClockGroupDealiaser} +import chipyard.clocking.{IdealizedPLL, ClockGroupDealiaser, ClockGroupNamePrefixer, ClockGroupFrequencySpecifier} /** * Chipyard provides three baseline, top-level reset schemes, set using the @@ -80,101 +80,26 @@ object GenerateReset { } -case object ClockingSchemeKey extends Field[ChipTop => Unit](ClockingSchemeGenerators.harnessClock) +case object ClockingSchemeKey extends Field[ChipTop => Unit](ClockingSchemeGenerators.idealizedPLL) +/** + * This is a dictionary of clock name to clock frequency in MHz. Names + * correspond to the IO coming off digital top. If the map is undefined for the given name, + * it will return a default value -- DFU. + */ +case object ClockFrequencyAssignment extends Field[Seq[(String) => Option[Double]]](Seq.empty) +case object DefaultClockFrequencyKey extends Field[Double](100.0) +class ClockNameMatchesAssignment(name: String, fMHz: Double) extends Config((site, here, up) => { + case ClockFrequencyAssignment => up(ClockFrequencyAssignment, site) ++ + Seq((cName: String) => if (cName == name) Some(fMHz) else None) +}) +class ClockNameContainsAssignment(name: String, fMHz: Double) extends Config((site, here, up) => { + case ClockFrequencyAssignment => up(ClockFrequencyAssignment, 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 => - 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 - } - - 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, Some("iocell_clock")) - chiptop.iocells ++= clockIOCell - clock_io.suggestName("clock") - - implicitClockSourceNode.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.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 - - 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, Some("iocell_clock")) - chiptop.iocells ++= clockIOCell - clock_io.suggestName("clock") - val div_clock = Pow2ClockDivider(clock_wire, 2) - - implicitClockSourceNode.out.unzip._1.map { o => - o.clock := div_clock - 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 - }) - } - } - val idealizedPLL: ChipTop => Unit = { chiptop => implicit val p = chiptop.p @@ -184,15 +109,18 @@ object ClockingSchemeGenerators { l.asyncClockGroupsNode } - val aggregator = ClockGroupAggregator() + val aggregator = LazyModule(new ClockGroupAggregator("allClocks")).node chiptop.implicitClockSinkNode := ClockGroup() := aggregator - systemAsyncClockGroup := aggregator + systemAsyncClockGroup := ClockGroupNamePrefixer() := aggregator val referenceClockSource = ClockSourceNode(Seq(ClockSourceParameters())) - aggregator := ClockGroupDealiaser() := IdealizedPLL() := referenceClockSource + (aggregator + := ClockGroupFrequencySpecifier(p(ClockFrequencyAssignment), p(DefaultClockFrequencyKey)) + := IdealizedPLL() + := referenceClockSource) + InModuleBody { - val clock_wire = Wire(Input(Clock())) val reset_wire = GenerateReset(chiptop, clock_wire) val (clock_io, clockIOCell) = IOCell.generateIOFromSignal(clock_wire, Some("iocell_clock")) diff --git a/generators/chipyard/src/main/scala/ConfigFragments.scala b/generators/chipyard/src/main/scala/ConfigFragments.scala index 03ccdbca..94b6477b 100644 --- a/generators/chipyard/src/main/scala/ConfigFragments.scala +++ b/generators/chipyard/src/main/scala/ConfigFragments.scala @@ -26,7 +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.{BuildTop, BuildSystem, ClockingSchemeGenerators, ClockingSchemeKey, TestSuitesKey, TestSuiteHelper, ClockNameContainsAssignment} // Imports for multiclock sketch import boom.common.{BoomTile, BoomTileParams} @@ -163,10 +163,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)) }) @@ -175,23 +171,20 @@ class WithNoDebug extends Config((site, here, up) => { case DebugModuleKey => None }) - // Multiclock sketch -class WithForcedTileFrequency(fMHz: Double) extends Config((site, here, up) => { - case TilesLocated(InSubsystem) => - val genericAttachParams = up(TilesLocated(InSubsystem), site) map { - case b: BoomTileAttachParams => GenericallyAttachableTile[BoomTile]( - b.tileParams, GenericCrossingParams(b.crossingParams), b.lookup) - case r: RocketTileAttachParams => GenericallyAttachableTile[RocketTile]( - r.tileParams, GenericCrossingParams(r.crossingParams), r.lookup) - case a: ArianeTileAttachParams => GenericallyAttachableTile[ArianeTile]( - a.tileParams, GenericCrossingParams(a.crossingParams), a.lookup) - case g: GenericallyAttachableTile[_] => g - } - genericAttachParams.map(p => p.copy(crossingParams = p.crossingParams.copy( - injectClockNodeFunc = ClockNodeInjectionUtils.forceTakeFrequency(fMHz)))) -}) +//class WithForcedTileFrequency(fMHz: Double) extends Config((site, here, up) => { +// case TilesLocated(InSubsystem) => +// val genericAttachParams = up(TilesLocated(InSubsystem), site) map { +// case b: BoomTileAttachParams => GenericallyAttachableTile[BoomTile]( +// b.tileParams, GenericCrossingParams(b.crossingParams), b.lookup) +// case r: RocketTileAttachParams => GenericallyAttachableTile[RocketTile]( +// r.tileParams, GenericCrossingParams(r.crossingParams), r.lookup) +// case a: ArianeTileAttachParams => GenericallyAttachableTile[ArianeTile]( +// a.tileParams, GenericCrossingParams(a.crossingParams), a.lookup) +// case g: GenericallyAttachableTile[_] => g +// } +// genericAttachParams.map(p => p.copy(crossingParams = p.crossingParams.copy( +// injectClockNodeFunc = ClockNodeInjectionUtils.forceTakeFrequency(fMHz)))) +//}) -class WithIdealizedPLL extends Config((site, here, up) => { - case ClockingSchemeKey => ClockingSchemeGenerators.idealizedPLL -}) +class WithTileFrequency(fMHz: Double) extends ClockNameContainsAssignment("core", fMHz) 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 00000000..bf756003 --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/ClockGroupNamePrefixer.scala @@ -0,0 +1,65 @@ +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.map(_.copy(freqMHz = clockFreq)).orElse(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/IdealizedPLL.scala b/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala index 3de99e8e..6cf03e27 100644 --- a/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala +++ b/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala @@ -8,6 +8,9 @@ import freechips.rocketchip.prci._ import scala.collection.mutable +/** + * TODO: figure out how much division is acceptable in our simulators and redefine this. + */ object FrequencyUtils { def computeReferenceFrequencyMHz( requestedOutputs: Seq[ClockParameters], @@ -33,16 +36,26 @@ case class IdealizedPLLNode(pllName: String)(implicit valName: ValName) take = Some(FrequencyUtils.computeReferenceFrequencyMHz(u.head.members.flatMap(_.take)))) } ) +/** + * Generates a digttal-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 IdealizedPLL(pllName: String)(implicit p: Parameters, valName: ValName) extends LazyModule { val node = IdealizedPLLNode(pllName) lazy val module = new LazyRawModuleImp(this) { - require(node.out.size == 1, "Must use a ClockGroupAggregator") + 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 requestedFreqs = outSinkParams.members.map(m => m.name -> m.take) + println(s"Idealized PLL Frequency Summary") + println(s"-------------------------------") + println(s" Requested Reference Frequency: ${referenceFreq} MHz") val dividedClocks = mutable.HashMap[Int, Clock]() def instantiateDivider(div: Int): Clock = { @@ -57,8 +70,9 @@ class IdealizedPLL(pllName: String)(implicit p: Parameters, valName: ValName) ex val requested = sinkP.take.get.freqMHz val div = Math.round(referenceFreq / requested).toInt val actual = referenceFreq / div.toDouble - println(s"Clock ${sinkBName}, requested freq: ${requested} MHz. Actual freq: ${actual} MHz via division of ${div}") + println(s" Output Clock ${sinkBName}: Requested: ${requested} MHz, Actual: ${actual} MHz (division of ${div})") sinkB.clock := dividedClocks.getOrElse(div, instantiateDivider(div)) + sinkB.reset := refClock.reset } } } diff --git a/generators/chipyard/src/main/scala/config/RocketConfigs.scala b/generators/chipyard/src/main/scala/config/RocketConfigs.scala index a4e1af72..dd186828 100644 --- a/generators/chipyard/src/main/scala/config/RocketConfigs.scala +++ b/generators/chipyard/src/main/scala/config/RocketConfigs.scala @@ -174,19 +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 freechips.rocketchip.subsystem.WithRationalRocketTiles ++ // Add rational crossings between RocketTile and uncore - new freechips.rocketchip.subsystem.WithNBigCores(1) ++ - new chipyard.config.AbstractConfig) - -// Multiclock Sketch -class ForcedClockRocketConfig extends Config( - new chipyard.config.WithForcedTileFrequency(200) ++ - new chipyard.config.WithIdealizedPLL ++ // Put the Tile on its own clock domain - //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) From ad147ec7f227abd1c02935e8c429280f00e45990 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Thu, 17 Sep 2020 11:39:01 -0700 Subject: [PATCH 07/19] [clocks] Remove dealiaser and node injector until they are needed --- .../chipyard/src/main/scala/Clocks.scala | 2 +- .../src/main/scala/ConfigFragments.scala | 20 +------- .../src/main/scala/GenericAttachParams.scala | 38 -------------- .../scala/clocking/ClockGroupDealiaser.scala | 50 ------------------- .../scala/clocking/ClockNodeInjectors.scala | 29 ----------- 5 files changed, 2 insertions(+), 137 deletions(-) delete mode 100644 generators/chipyard/src/main/scala/GenericAttachParams.scala delete mode 100644 generators/chipyard/src/main/scala/clocking/ClockGroupDealiaser.scala delete mode 100644 generators/chipyard/src/main/scala/clocking/ClockNodeInjectors.scala diff --git a/generators/chipyard/src/main/scala/Clocks.scala b/generators/chipyard/src/main/scala/Clocks.scala index 609bf8a0..1d3a981e 100644 --- a/generators/chipyard/src/main/scala/Clocks.scala +++ b/generators/chipyard/src/main/scala/Clocks.scala @@ -12,7 +12,7 @@ import freechips.rocketchip.util.{ResetCatchAndSync, Pow2ClockDivider} import barstools.iocell.chisel._ -import chipyard.clocking.{IdealizedPLL, ClockGroupDealiaser, ClockGroupNamePrefixer, ClockGroupFrequencySpecifier} +import chipyard.clocking.{IdealizedPLL, ClockGroupNamePrefixer, ClockGroupFrequencySpecifier} /** * Chipyard provides three baseline, top-level reset schemes, set using the diff --git a/generators/chipyard/src/main/scala/ConfigFragments.scala b/generators/chipyard/src/main/scala/ConfigFragments.scala index 94b6477b..19fe0630 100644 --- a/generators/chipyard/src/main/scala/ConfigFragments.scala +++ b/generators/chipyard/src/main/scala/ConfigFragments.scala @@ -31,8 +31,6 @@ import chipyard.{BuildTop, BuildSystem, ClockingSchemeGenerators, ClockingScheme // Imports for multiclock sketch import boom.common.{BoomTile, BoomTileParams} import ariane.{ArianeTile, ArianeTileParams} -import chipyard.{GenericallyAttachableTile, GenericCrossingParams} -import chipyard.clocking.{ClockNodeInjectionUtils } // ----------------------- // Common Config Fragments // ----------------------- @@ -169,22 +167,6 @@ class WithDMIDTM extends Config((site, here, up) => { class WithNoDebug extends Config((site, here, up) => { case DebugModuleKey => None + }) - -// Multiclock sketch -//class WithForcedTileFrequency(fMHz: Double) extends Config((site, here, up) => { -// case TilesLocated(InSubsystem) => -// val genericAttachParams = up(TilesLocated(InSubsystem), site) map { -// case b: BoomTileAttachParams => GenericallyAttachableTile[BoomTile]( -// b.tileParams, GenericCrossingParams(b.crossingParams), b.lookup) -// case r: RocketTileAttachParams => GenericallyAttachableTile[RocketTile]( -// r.tileParams, GenericCrossingParams(r.crossingParams), r.lookup) -// case a: ArianeTileAttachParams => GenericallyAttachableTile[ArianeTile]( -// a.tileParams, GenericCrossingParams(a.crossingParams), a.lookup) -// case g: GenericallyAttachableTile[_] => g -// } -// genericAttachParams.map(p => p.copy(crossingParams = p.crossingParams.copy( -// injectClockNodeFunc = ClockNodeInjectionUtils.forceTakeFrequency(fMHz)))) -//}) - class WithTileFrequency(fMHz: Double) extends ClockNameContainsAssignment("core", fMHz) diff --git a/generators/chipyard/src/main/scala/GenericAttachParams.scala b/generators/chipyard/src/main/scala/GenericAttachParams.scala deleted file mode 100644 index f90598af..00000000 --- a/generators/chipyard/src/main/scala/GenericAttachParams.scala +++ /dev/null @@ -1,38 +0,0 @@ - -package chipyard - -import freechips.rocketchip.diplomacy._ -import freechips.rocketchip.config.{Field, Parameters} -import freechips.rocketchip.subsystem._ -import freechips.rocketchip.tile.{LookupByHartIdImpl, TileParams, InstantiableTileParams, BaseTile} - -import chipyard.clocking.ClockNodeInjectionUtils._ - -case class GenericCrossingParams( - crossingType: ClockCrossingType = SynchronousCrossing(), - master: TilePortParamsLike = TileMasterPortParams(), - slave: TilePortParamsLike = TileSlavePortParams(), - mmioBaseAddressPrefixWhere: TLBusWrapperLocation = CBUS, - injectClockNodeFunc: InjectClockNodeFunc = injectIdentityClockNode, - forceSeparateClockReset: Boolean = false) extends TileCrossingParamsLike { - - def injectClockNode(a: Attachable)(implicit p: Parameters) = injectClockNodeFunc(a, p) -} - -object GenericCrossingParams { - def apply(params: TileCrossingParamsLike): GenericCrossingParams = GenericCrossingParams( - params.crossingType, - params.master, - params.slave, - params.mmioBaseAddressPrefixWhere, - (a: Attachable, p: Parameters) => params.injectClockNode(a)(p), - params.forceSeparateClockReset) -} - -case class GenericallyAttachableTile[TT <: BaseTile]( - tileParams: InstantiableTileParams[TT], - crossingParams: GenericCrossingParams, - lookup: LookupByHartIdImpl) extends CanAttachTile { - type TileType = TT -} - diff --git a/generators/chipyard/src/main/scala/clocking/ClockGroupDealiaser.scala b/generators/chipyard/src/main/scala/clocking/ClockGroupDealiaser.scala deleted file mode 100644 index 54b384e9..00000000 --- a/generators/chipyard/src/main/scala/clocking/ClockGroupDealiaser.scala +++ /dev/null @@ -1,50 +0,0 @@ -package chipyard.clocking - -import chisel3._ - -import freechips.rocketchip.config.{Parameters} -import freechips.rocketchip.diplomacy._ -import freechips.rocketchip.prci._ - -/** - * Somewhat hacky. Since not all clocks in a clock group specify a taken frequency - * current, this LazyModule attempts to dealias them, by finding a specified - * clock with a matching name. - * - * Perhaps another, simpler solution would be to pass a default. - * - */ - -case class ClockGroupDealiaserNode()(implicit valName: ValName) - extends NexusNode(ClockGroupImp)( - dFn = { _ => ClockGroupSourceParameters() }, - uFn = { u => - require(u.size == 1) - val takenClocks = u.head.members.filter(_.take.nonEmpty) - require(takenClocks.nonEmpty, - "At least one sink clock in clock group must specify its take parameter") - u.head.copy(members = takenClocks) - }) - -class ClockGroupDealiaser(name: String)(implicit p: Parameters) extends LazyModule { - val node = ClockGroupDealiaserNode() - - lazy val module = new LazyRawModuleImp(this) { - require(node.out.size == 1 && node.in.size == 1, - "ClockGroupDealiaser requires a single ClockGroup, please use a ClockGroupAggregator") - val (outClocks, e @ ClockGroupEdgeParameters(_, outSinkParams, _, _)) = node.out.head - val (inClocks, ClockGroupEdgeParameters(_, inSinkParams, _, _)) = node.in.head - val inMap = inClocks.member.data.zip(inSinkParams.members).map({ case (b, p) => p.name -> b}).toMap - - for (((outBName, outB), outName) <- outClocks.member.elements.zip(outSinkParams.members.map(_.name))) { - val inClock = inMap.getOrElse(outName, throw new Exception(""" - | No clock in input group with name option matching ${outName}. At least one clock - | with the same must specify a frequency in its take parameter.""".stripMargin)) - outB := inClock - } - } -} - -object ClockGroupDealiaser { - def apply()(implicit p: Parameters, valName: ValName) = LazyModule(new ClockGroupDealiaser(valName.name)).node -} diff --git a/generators/chipyard/src/main/scala/clocking/ClockNodeInjectors.scala b/generators/chipyard/src/main/scala/clocking/ClockNodeInjectors.scala deleted file mode 100644 index 981ed327..00000000 --- a/generators/chipyard/src/main/scala/clocking/ClockNodeInjectors.scala +++ /dev/null @@ -1,29 +0,0 @@ - -package chipyard.clocking - -import freechips.rocketchip.diplomacy._ -import freechips.rocketchip.config.{Parameters} -import freechips.rocketchip.subsystem._ -import freechips.rocketchip.prci.{ClockNode, ClockTempNode, ClockAdapterNode, ClockParameters} -/** - * An adapter node hack c that just throws out the existing sink node - * clock parameters in favor of the provided ones. - */ -class ForceTakeClock(clockParams: Option[ClockParameters])(implicit p: Parameters, v: ValName) extends LazyModule { - val node = ClockAdapterNode(sinkFn = { s => s.copy(take = clockParams) }) - lazy val module = new LazyRawModuleImp(this) { - (node.out zip node.in) map { case ((o, _), (i, _)) => o := i } - } -} - -object ForceTakeClock { - def apply(clockParams: Option[ClockParameters])(implicit p: Parameters, v: ValName): ClockAdapterNode = - LazyModule(new ForceTakeClock(clockParams)).node -} - -object ClockNodeInjectionUtils { - type InjectClockNodeFunc = (Attachable, Parameters) => ClockNode - val injectIdentityClockNode: InjectClockNodeFunc = (a: Attachable, p: Parameters) => ClockTempNode() - def forceTakeFrequency(freqMHz: Double): InjectClockNodeFunc = - (a: Attachable, p: Parameters) => ForceTakeClock(Some(ClockParameters(freqMHz)))(p, ValName("ForcedTakeClock")) -} From f36183d236ea6a7ea48041f5da084665c443d2fd Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Thu, 17 Sep 2020 12:17:02 -0700 Subject: [PATCH 08/19] [clocks] Update AssignerKey name and comment --- generators/chipyard/src/main/scala/Clocks.scala | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/generators/chipyard/src/main/scala/Clocks.scala b/generators/chipyard/src/main/scala/Clocks.scala index 1d3a981e..499d4844 100644 --- a/generators/chipyard/src/main/scala/Clocks.scala +++ b/generators/chipyard/src/main/scala/Clocks.scala @@ -82,20 +82,21 @@ object GenerateReset { case object ClockingSchemeKey extends Field[ChipTop => Unit](ClockingSchemeGenerators.idealizedPLL) /** - * This is a dictionary of clock name to clock frequency in MHz. Names - * correspond to the IO coming off digital top. If the map is undefined for the given name, - * it will return a default value -- DFU. + * 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 -- DFU. */ -case object ClockFrequencyAssignment extends Field[Seq[(String) => Option[Double]]](Seq.empty) +case object ClockFrequencyAssignersKey extends Field[Seq[(String) => Option[Double]]](Seq.empty) case object DefaultClockFrequencyKey extends Field[Double](100.0) class ClockNameMatchesAssignment(name: String, fMHz: Double) extends Config((site, here, up) => { - case ClockFrequencyAssignment => up(ClockFrequencyAssignment, site) ++ + 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 ClockFrequencyAssignment => up(ClockFrequencyAssignment, site) ++ + case ClockFrequencyAssignersKey => up(ClockFrequencyAssignersKey, site) ++ Seq((cName: String) => if (cName.contains(name)) Some(fMHz) else None) }) @@ -115,7 +116,7 @@ object ClockingSchemeGenerators { val referenceClockSource = ClockSourceNode(Seq(ClockSourceParameters())) (aggregator - := ClockGroupFrequencySpecifier(p(ClockFrequencyAssignment), p(DefaultClockFrequencyKey)) + := ClockGroupFrequencySpecifier(p(ClockFrequencyAssignersKey), p(DefaultClockFrequencyKey)) := IdealizedPLL() := referenceClockSource) From 84195d28bb9894a039dda01d506a5a3a952084e1 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Wed, 23 Sep 2020 15:29:52 -0700 Subject: [PATCH 09/19] [clocks] Don't override existing take frequency if present. --- .../src/main/scala/clocking/ClockGroupNamePrefixer.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/generators/chipyard/src/main/scala/clocking/ClockGroupNamePrefixer.scala b/generators/chipyard/src/main/scala/clocking/ClockGroupNamePrefixer.scala index bf756003..a5618c2d 100644 --- a/generators/chipyard/src/main/scala/clocking/ClockGroupNamePrefixer.scala +++ b/generators/chipyard/src/main/scala/clocking/ClockGroupNamePrefixer.scala @@ -57,7 +57,12 @@ object ClockGroupFrequencySpecifier { val clockFreq = assigners.foldLeft(defaultFreq)( (currentFreq, candidateFunc) => candidateFunc(clock.name.get).getOrElse(currentFreq)) - clock.copy(take = clock.take.map(_.copy(freqMHz = clockFreq)).orElse(Some(ClockParameters(clockFreq)))) + 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 From 96bf702c3b12cd1c6ed73090904a3609fe85ee95 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Thu, 24 Sep 2020 23:23:11 -0700 Subject: [PATCH 10/19] [clocks] Factor out the PLL calculations into their own class --- .../src/main/scala/ConfigFragments.scala | 2 +- .../main/scala/clocking/IdealizedPLL.scala | 28 ++++++++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/generators/chipyard/src/main/scala/ConfigFragments.scala b/generators/chipyard/src/main/scala/ConfigFragments.scala index 19fe0630..38dea871 100644 --- a/generators/chipyard/src/main/scala/ConfigFragments.scala +++ b/generators/chipyard/src/main/scala/ConfigFragments.scala @@ -26,7 +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, ClockNameContainsAssignment} +import chipyard._ // Imports for multiclock sketch import boom.common.{BoomTile, BoomTileParams} diff --git a/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala b/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala index 6cf03e27..44e58053 100644 --- a/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala +++ b/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala @@ -7,6 +7,7 @@ import freechips.rocketchip.diplomacy._ import freechips.rocketchip.prci._ import scala.collection.mutable +import scala.collection.immutable.ListMap /** * TODO: figure out how much division is acceptable in our simulators and redefine this. @@ -24,6 +25,23 @@ object FrequencyUtils { } } +class SimplePllConfiguration(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) })):_*) + + def prettyPrint(pllName: String) { + val preamble = s""" + |${pllName} Frequency Summary + | Input Reference Frequency: ${referenceFreqMHz} MHz\n""".stripMargin + 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})" + } + println(preamble + outputSummaries.mkString("\n")) + } +} + case class IdealizedPLLNode(pllName: String)(implicit valName: ValName) extends MixedNexusNode(ClockImp, ClockGroupImp)( dFn = { _ => ClockGroupSourceParameters() }, @@ -53,9 +71,8 @@ class IdealizedPLL(pllName: String)(implicit p: Parameters, valName: ValName) ex val (outClocks, ClockGroupEdgeParameters(_, outSinkParams, _, _)) = node.out.head val referenceFreq = refSinkParam.take.get.freqMHz - println(s"Idealized PLL Frequency Summary") - println(s"-------------------------------") - println(s" Requested Reference Frequency: ${referenceFreq} MHz") + val pllConfig = new SimplePllConfiguration(outSinkParams.members) + pllConfig.prettyPrint(pllName) val dividedClocks = mutable.HashMap[Int, Clock]() def instantiateDivider(div: Int): Clock = { @@ -67,10 +84,7 @@ class IdealizedPLL(pllName: String)(implicit p: Parameters, valName: ValName) ex } for (((sinkBName, sinkB), sinkP) <- outClocks.member.elements.zip(outSinkParams.members)) { - val requested = sinkP.take.get.freqMHz - val div = Math.round(referenceFreq / requested).toInt - val actual = referenceFreq / div.toDouble - println(s" Output Clock ${sinkBName}: Requested: ${requested} MHz, Actual: ${actual} MHz (division of ${div})") + val div = pllConfig.sinkDividerMap(sinkP) sinkB.clock := dividedClocks.getOrElse(div, instantiateDivider(div)) sinkB.reset := refClock.reset } From f6989a1968817daec39fd26f37001b93bbe6f853 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Thu, 24 Sep 2020 23:24:08 -0700 Subject: [PATCH 11/19] [clocks] Use the periphery frequency as the default --- generators/chipyard/src/main/scala/Clocks.scala | 2 +- generators/chipyard/src/main/scala/ConfigFragments.scala | 6 ++++++ .../chipyard/src/main/scala/config/AbstractConfig.scala | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/generators/chipyard/src/main/scala/Clocks.scala b/generators/chipyard/src/main/scala/Clocks.scala index 499d4844..531f6add 100644 --- a/generators/chipyard/src/main/scala/Clocks.scala +++ b/generators/chipyard/src/main/scala/Clocks.scala @@ -88,7 +88,7 @@ case object ClockingSchemeKey extends Field[ChipTop => Unit](ClockingSchemeGener * [[DefaultClockFrequencyKey]] will be used -- DFU. */ case object ClockFrequencyAssignersKey extends Field[Seq[(String) => Option[Double]]](Seq.empty) -case object DefaultClockFrequencyKey extends Field[Double](100.0) +case object DefaultClockFrequencyKey extends Field[Double]() class ClockNameMatchesAssignment(name: String, fMHz: Double) extends Config((site, here, up) => { case ClockFrequencyAssignersKey => up(ClockFrequencyAssignersKey, site) ++ diff --git a/generators/chipyard/src/main/scala/ConfigFragments.scala b/generators/chipyard/src/main/scala/ConfigFragments.scala index 38dea871..df20af68 100644 --- a/generators/chipyard/src/main/scala/ConfigFragments.scala +++ b/generators/chipyard/src/main/scala/ConfigFragments.scala @@ -169,4 +169,10 @@ 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/config/AbstractConfig.scala b/generators/chipyard/src/main/scala/config/AbstractConfig.scala index 950cb4b4..85d3961b 100644 --- a/generators/chipyard/src/main/scala/config/AbstractConfig.scala +++ b/generators/chipyard/src/main/scala/config/AbstractConfig.scala @@ -44,6 +44,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) From cc949aadab96c6bd2ba9281fa8cad93a8b5e5d76 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Thu, 24 Sep 2020 23:28:47 -0700 Subject: [PATCH 12/19] [clocking] Address some of Colin's PR comments --- generators/chipyard/src/main/scala/ConfigFragments.scala | 4 ---- .../chipyard/src/main/scala/clocking/IdealizedPLL.scala | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/generators/chipyard/src/main/scala/ConfigFragments.scala b/generators/chipyard/src/main/scala/ConfigFragments.scala index df20af68..d7becac6 100644 --- a/generators/chipyard/src/main/scala/ConfigFragments.scala +++ b/generators/chipyard/src/main/scala/ConfigFragments.scala @@ -28,9 +28,6 @@ import sifive.blocks.devices.spi._ import chipyard._ -// Imports for multiclock sketch -import boom.common.{BoomTile, BoomTileParams} -import ariane.{ArianeTile, ArianeTileParams} // ----------------------- // Common Config Fragments // ----------------------- @@ -167,7 +164,6 @@ 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) diff --git a/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala b/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala index 44e58053..5b99a17b 100644 --- a/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala +++ b/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala @@ -55,7 +55,7 @@ case class IdealizedPLLNode(pllName: String)(implicit valName: ValName) ) /** - * Generates a digttal-divider-only PLL model that verilator can simulate. + * 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 From 7b8a954d04ce8346b73786f2f892c4995d95462f Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Thu, 24 Sep 2020 23:32:07 -0700 Subject: [PATCH 13/19] [firechip] Rework FireSim clocking to be more similar to default CY targets --- .../src/main/scala/BridgeBinders.scala | 12 +- .../firechip/src/main/scala/FireSim.scala | 185 +++++++++--------- .../src/main/scala/TargetConfigs.scala | 2 +- sims/firesim | 2 +- 4 files changed, 96 insertions(+), 105 deletions(-) diff --git a/generators/firechip/src/main/scala/BridgeBinders.scala b/generators/firechip/src/main/scala/BridgeBinders.scala index 8a4d0a69..9197215c 100644 --- a/generators/firechip/src/main/scala/BridgeBinders.scala +++ b/generators/firechip/src/main/scala/BridgeBinders.scala @@ -68,9 +68,7 @@ class WithFireSimIOCellModels extends Config((site, here, up) => { class WithSerialBridge extends OverrideHarnessBinder({ (system: CanHavePeripherySerial, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[SerialIO]]) => { ports.map { p => - withClockAndReset(p.clock, th.harnessReset) { - SerialBridge(p.clock, p.bits, MainMemoryConsts.globalName)(GetSystemParameters(system)) - } + SerialBridge(p.clock, p.bits, MainMemoryConsts.globalName)(GetSystemParameters(system)) } Nil } @@ -79,7 +77,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 } }) @@ -119,11 +117,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 d2ef4e60..d72ec467 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,114 @@ object NodeIdx { def apply(): Int = idx } + +/** + * 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 + + def getClockRecord: RecordMap[Clock] = _clockRecord.get + + 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 ")) + + 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 + } + + /** + * 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 + } + + val clockBridge = Module(new RationalClockBridge(distinct)) + val cbVecTuples = distinct.zip(clockBridge.io.clocks) + val outputWire = Wire(RecordMap(allClocks.map { c => (c.name, Clock()) }:_*)) + for (parameter <- allClocks) { + val (_, cbClockField) = cbVecTuples.find(_._1.equalFrequency(parameter)).get + outputWire(parameter.name).get := cbClockField + } + _clockRecord = Some(outputWire) + } + getClockRecord + } +} + +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 clock = IO(Input(Clock())).suggestName("clock") + 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 := 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.data.foreach { o => - o.clock := clock - o.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(clockGroupEdge.sink.members) + pllConfig.prettyPrint("FireSim RationalClockBridge") + val rationalClockSpecs = for ((sinkP, division) <- pllConfig.sinkDividerMap) yield { + RationalClock(sinkP.name.get, 1, division) + } chiptop.harnessFunctions += ((th: HasHarnessSignalReferences) => { - 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 ClockingSchemeKey => { chiptop: 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 - } - - 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") - - implicitClockSourceNode.out.unzip._1.map { o => - o.clock := uncore_clock - o.reset := reset - } - - 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 - } - } - }} - - 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 - }) + 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 +163,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 2dede960..fcacde8b 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 c1cd3e5e..4342b333 160000 --- a/sims/firesim +++ b/sims/firesim @@ -1 +1 @@ -Subproject commit c1cd3e5e7013b30f30508c7f47ff13180949eafe +Subproject commit 4342b33301ae9b3f53b98ca3b1d1afff73d64997 From 1b3514f95f4eb58dc13b41093a3948f1ea0a27a0 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Fri, 25 Sep 2020 10:03:46 -0700 Subject: [PATCH 14/19] [clocks] Specify a default frequency for TraceGen --- generators/chipyard/src/main/scala/config/TracegenConfigs.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/generators/chipyard/src/main/scala/config/TracegenConfigs.scala b/generators/chipyard/src/main/scala/config/TracegenConfigs.scala index 78cb6851..f9980bf6 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) From 67145c6ccd9edf7cf42592d6f50f7f115213e205 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Fri, 25 Sep 2020 10:05:28 -0700 Subject: [PATCH 15/19] [clocking] Fix FireSim clock look up --- generators/firechip/src/main/scala/FireSim.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/generators/firechip/src/main/scala/FireSim.scala b/generators/firechip/src/main/scala/FireSim.scala index d72ec467..28daec52 100644 --- a/generators/firechip/src/main/scala/FireSim.scala +++ b/generators/firechip/src/main/scala/FireSim.scala @@ -68,8 +68,8 @@ class ClockBridgeInstantiator { val clockBridge = Module(new RationalClockBridge(distinct)) val cbVecTuples = distinct.zip(clockBridge.io.clocks) - val outputWire = Wire(RecordMap(allClocks.map { c => (c.name, Clock()) }:_*)) - for (parameter <- allClocks) { + 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 } From a6ce85039142c9c1e6c14b4e4cabf10674b579d2 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Tue, 29 Sep 2020 16:06:48 -0700 Subject: [PATCH 16/19] [clocks] ClockDividerN: make first output edge occur on first input edge --- .../chipyard/src/main/resources/vsrc/ClockDividerN.sv | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv b/generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv index 868b0eee..4d940d06 100644 --- a/generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv +++ b/generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv @@ -7,8 +7,10 @@ module ClockDividerN #(parameter DIV)(output logic clk_out = 1'b0, input clk_in); - localparam DIV_COUNTER_WIDTH = $clog2(DIV); + 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 @@ -17,19 +19,19 @@ module ClockDividerN #(parameter DIV)(output logic clk_out = 1'b0, input clk_in) clk_out = clk_in; end end else begin - reg [DIV_COUNTER_WIDTH - 1: 0] count = '0; + 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 == (DIV - 1)) begin + if (count == LOW_TRANSITION[CWIDTH-1:0]) begin clk_out = 1'b0; count <= '0; end else begin - if (count == (LOW_CYCLES - 1)) begin + if (count == HIGH_TRANSITION[CWIDTH-1:0]) begin clk_out = 1'b1; end count <= count + 1'b1; From 5b414f58299b9f600e1220d77e2b0b92a7a47921 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Tue, 29 Sep 2020 16:59:37 -0700 Subject: [PATCH 17/19] [clocks] Emit frequency summary for divider-only PLL model --- .../src/main/scala/clocking/IdealizedPLL.scala | 17 +++++++++-------- .../firechip/src/main/scala/FireSim.scala | 3 +-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala b/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala index 5b99a17b..8dfc7908 100644 --- a/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala +++ b/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala @@ -5,6 +5,7 @@ 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 @@ -25,21 +26,22 @@ object FrequencyUtils { } } -class SimplePllConfiguration(val sinks: Seq[ClockSinkParameters]) { +class SimplePllConfiguration(pllName: 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) })):_*) - def prettyPrint(pllName: String) { - val preamble = s""" + private val preamble = s""" |${pllName} Frequency Summary | Input Reference Frequency: ${referenceFreqMHz} MHz\n""".stripMargin - val outputSummaries = sinkDividerMap.map { case (sink, division) => + 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})" } - println(preamble + outputSummaries.mkString("\n")) - } + + val summaryString = preamble + outputSummaries.mkString("\n") + ElaborationArtefacts.add(s"${pllName}.freq-summary", summaryString) + println(summaryString) } case class IdealizedPLLNode(pllName: String)(implicit valName: ValName) @@ -71,8 +73,7 @@ class IdealizedPLL(pllName: String)(implicit p: Parameters, valName: ValName) ex val (outClocks, ClockGroupEdgeParameters(_, outSinkParams, _, _)) = node.out.head val referenceFreq = refSinkParam.take.get.freqMHz - val pllConfig = new SimplePllConfiguration(outSinkParams.members) - pllConfig.prettyPrint(pllName) + val pllConfig = new SimplePllConfiguration(pllName, outSinkParams.members) val dividedClocks = mutable.HashMap[Int, Clock]() def instantiateDivider(div: Int): Clock = { diff --git a/generators/firechip/src/main/scala/FireSim.scala b/generators/firechip/src/main/scala/FireSim.scala index 28daec52..90fd473a 100644 --- a/generators/firechip/src/main/scala/FireSim.scala +++ b/generators/firechip/src/main/scala/FireSim.scala @@ -124,8 +124,7 @@ class WithFireSimSimpleClocks extends Config((site, here, up) => { } } - val pllConfig = new SimplePllConfiguration(clockGroupEdge.sink.members) - pllConfig.prettyPrint("FireSim RationalClockBridge") + val pllConfig = new SimplePllConfiguration("FireSim RationalClockBridge", clockGroupEdge.sink.members) val rationalClockSpecs = for ((sinkP, division) <- pllConfig.sinkDividerMap) yield { RationalClock(sinkP.name.get, 1, division) } From ebfe3103a45d2b7b4e62609baa083fb4a20647a7 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Tue, 29 Sep 2020 17:33:49 -0700 Subject: [PATCH 18/19] [clocks] IdealizedPll -> DividerOnlyClockGenerator --- generators/chipyard/src/main/scala/Clocks.scala | 12 ++++++------ ...PLL.scala => DividerOnlyClockGenerator.scala} | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) rename generators/chipyard/src/main/scala/clocking/{IdealizedPLL.scala => DividerOnlyClockGenerator.scala} (87%) diff --git a/generators/chipyard/src/main/scala/Clocks.scala b/generators/chipyard/src/main/scala/Clocks.scala index b54d5c42..554e9905 100644 --- a/generators/chipyard/src/main/scala/Clocks.scala +++ b/generators/chipyard/src/main/scala/Clocks.scala @@ -12,7 +12,7 @@ import freechips.rocketchip.util.{ResetCatchAndSync, Pow2ClockDivider} import barstools.iocell.chisel._ -import chipyard.clocking.{IdealizedPLL, ClockGroupNamePrefixer, ClockGroupFrequencySpecifier} +import chipyard.clocking.{DividerOnlyClockGenerator, ClockGroupNamePrefixer, ClockGroupFrequencySpecifier} /** * Chipyard provides three baseline, top-level reset schemes, set using the @@ -79,12 +79,12 @@ object GenerateReset { } -case object ClockingSchemeKey extends Field[ChipTop => Unit](ClockingSchemeGenerators.idealizedPLL) -/** +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 -- DFU. + * [[DefaultClockFrequencyKey]] will be used. */ case object ClockFrequencyAssignersKey extends Field[Seq[(String) => Option[Double]]](Seq.empty) case object DefaultClockFrequencyKey extends Field[Double]() @@ -100,7 +100,7 @@ class ClockNameContainsAssignment(name: String, fMHz: Double) extends Config((si }) object ClockingSchemeGenerators { - val idealizedPLL: ChipTop => Unit = { chiptop => + val dividerOnlyClockGenerator: ChipTop => Unit = { chiptop => implicit val p = chiptop.p // Requires existence of undriven asyncClockGroups in subsystem @@ -116,7 +116,7 @@ object ClockingSchemeGenerators { val referenceClockSource = ClockSourceNode(Seq(ClockSourceParameters())) (aggregator := ClockGroupFrequencySpecifier(p(ClockFrequencyAssignersKey), p(DefaultClockFrequencyKey)) - := IdealizedPLL() + := DividerOnlyClockGenerator() := referenceClockSource) diff --git a/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala b/generators/chipyard/src/main/scala/clocking/DividerOnlyClockGenerator.scala similarity index 87% rename from generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala rename to generators/chipyard/src/main/scala/clocking/DividerOnlyClockGenerator.scala index 8dfc7908..4355fc71 100644 --- a/generators/chipyard/src/main/scala/clocking/IdealizedPLL.scala +++ b/generators/chipyard/src/main/scala/clocking/DividerOnlyClockGenerator.scala @@ -26,12 +26,12 @@ object FrequencyUtils { } } -class SimplePllConfiguration(pllName: String, val sinks: Seq[ClockSinkParameters]) { +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""" - |${pllName} Frequency Summary + |${name} Frequency Summary | Input Reference Frequency: ${referenceFreqMHz} MHz\n""".stripMargin private val outputSummaries = sinkDividerMap.map { case (sink, division) => val requested = sink.take.get.freqMHz @@ -40,11 +40,11 @@ class SimplePllConfiguration(pllName: String, val sinks: Seq[ClockSinkParameters } val summaryString = preamble + outputSummaries.mkString("\n") - ElaborationArtefacts.add(s"${pllName}.freq-summary", summaryString) + ElaborationArtefacts.add(s"${name}.freq-summary", summaryString) println(summaryString) } -case class IdealizedPLLNode(pllName: String)(implicit valName: ValName) +case class DividerOnlyClockGeneratorNode(pllName: String)(implicit valName: ValName) extends MixedNexusNode(ClockImp, ClockGroupImp)( dFn = { _ => ClockGroupSourceParameters() }, uFn = { u => @@ -64,8 +64,8 @@ case class IdealizedPLLNode(pllName: String)(implicit valName: ValName) * frequency. */ -class IdealizedPLL(pllName: String)(implicit p: Parameters, valName: ValName) extends LazyModule { - val node = IdealizedPLLNode(pllName) +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") @@ -92,6 +92,6 @@ class IdealizedPLL(pllName: String)(implicit p: Parameters, valName: ValName) ex } } -object IdealizedPLL { - def apply()(implicit p: Parameters, valName: ValName) = LazyModule(new IdealizedPLL(valName.name)).node +object DividerOnlyClockGenerator { + def apply()(implicit p: Parameters, valName: ValName) = LazyModule(new DividerOnlyClockGenerator(valName.name)).node } From 7d7f7ae4a83f0177a16d8dd0e6aba42d91b3cebd Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Wed, 30 Sep 2020 14:43:29 -0700 Subject: [PATCH 19/19] Bump FireSim --- sims/firesim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sims/firesim b/sims/firesim index 54ffa13d..801baeb9 160000 --- a/sims/firesim +++ b/sims/firesim @@ -1 +1 @@ -Subproject commit 54ffa13d980609549be47222a284521b73e56188 +Subproject commit 801baeb901c207beb0511311e09ae10e0dbb8b5f