[clocks] Stringly specified clock frequencies; DRY out schemes
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user