[clocks] Stringly specified clock frequencies; DRY out schemes

This commit is contained in:
David Biancolin
2020-09-17 11:37:56 -07:00
parent 6a26a350ee
commit 0f33ea3999
6 changed files with 125 additions and 136 deletions

View File

@@ -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)

View File

@@ -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"))

View File

@@ -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)

View File

@@ -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
}
}

View File

@@ -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
}
}
}

View File

@@ -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)