From 0f33ea3999f0c7cf5610e4099178c683ada680f8 Mon Sep 17 00:00:00 2001 From: David Biancolin Date: Thu, 17 Sep 2020 11:37:56 -0700 Subject: [PATCH] [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)