Switch RTL sims to absolute clock-generators
This commit is contained in:
@@ -25,7 +25,6 @@ import barstools.iocell.chisel._
|
||||
import testchipip._
|
||||
import icenet.{CanHavePeripheryIceNIC, SimNetwork, NicLoopback, NICKey, NICIOvonly}
|
||||
import chipyard.{CanHaveMasterTLMemPort}
|
||||
import chipyard.clocking.{HasChipyardPRCI, DividerOnlyClockGenerator}
|
||||
|
||||
import scala.reflect.{ClassTag}
|
||||
|
||||
|
||||
@@ -13,53 +13,11 @@ class ClockWithFreq(val freqMHz: Double) extends Bundle {
|
||||
val clock = Clock()
|
||||
}
|
||||
|
||||
// This uses synthesizable clock divisors to approximate frequency rations
|
||||
// between the requested clocks. This is currently the defualt clock generator "model",
|
||||
// as it can be used in VCS/Xcelium/Verilator/FireSim
|
||||
class WithDividerOnlyClockGenerator extends OverrideLazyIOBinder({
|
||||
(system: HasChipyardPRCI) => {
|
||||
// Connect the implicit clock
|
||||
implicit val p = GetSystemParameters(system)
|
||||
val implicitClockSinkNode = ClockSinkNode(Seq(ClockSinkParameters(name = Some("implicit_clock"))))
|
||||
system.connectImplicitClockSinkNode(implicitClockSinkNode)
|
||||
InModuleBody {
|
||||
val implicit_clock = implicitClockSinkNode.in.head._1.clock
|
||||
val implicit_reset = implicitClockSinkNode.in.head._1.reset
|
||||
system.asInstanceOf[BaseSubsystem].module match { case l: LazyModuleImp => {
|
||||
l.clock := implicit_clock
|
||||
l.reset := implicit_reset
|
||||
}}
|
||||
}
|
||||
|
||||
// Connect all other requested clocks
|
||||
val referenceClockSource = ClockSourceNode(Seq(ClockSourceParameters()))
|
||||
val dividerOnlyClockGen = LazyModule(new DividerOnlyClockGenerator("buildTopClockGenerator"))
|
||||
|
||||
(system.allClockGroupsNode
|
||||
:= dividerOnlyClockGen.node
|
||||
:= referenceClockSource)
|
||||
|
||||
InModuleBody {
|
||||
val clock_wire = Wire(Input(new ClockWithFreq(dividerOnlyClockGen.module.referenceFreq)))
|
||||
val reset_wire = Wire(Input(AsyncReset()))
|
||||
val (clock_io, clockIOCell) = IOCell.generateIOFromSignal(clock_wire, "clock", p(IOCellKey))
|
||||
val (reset_io, resetIOCell) = IOCell.generateIOFromSignal(reset_wire, "reset", p(IOCellKey))
|
||||
|
||||
referenceClockSource.out.unzip._1.map { o =>
|
||||
o.clock := clock_wire.clock
|
||||
o.reset := reset_wire
|
||||
}
|
||||
|
||||
(Seq(clock_io, reset_io), clockIOCell ++ resetIOCell)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// This uses the FakePLL, which uses a ClockAtFreq Verilog blackbox to generate
|
||||
// the requested clocks. This also adds TileLink ClockDivider and ClockSelector
|
||||
// blocks, which allow memory-mapped control of clock division, and clock muxing
|
||||
// between the FakePLL and the slow off-chip clock
|
||||
// Note: This will not simulate properly with verilator or firesim
|
||||
// Note: This will not simulate properly with firesim
|
||||
class WithPLLSelectorDividerClockGenerator extends OverrideLazyIOBinder({
|
||||
(system: HasChipyardPRCI) => {
|
||||
// Connect the implicit clock
|
||||
@@ -100,7 +58,7 @@ class WithPLLSelectorDividerClockGenerator extends OverrideLazyIOBinder({
|
||||
pllCtrlSink := pllCtrl.ctrlNode
|
||||
|
||||
InModuleBody {
|
||||
val clock_wire = Wire(Input(new ClockWithFreq(80)))
|
||||
val clock_wire = Wire(Input(new ClockWithFreq(100)))
|
||||
val reset_wire = Wire(Input(AsyncReset()))
|
||||
val (clock_io, clockIOCell) = IOCell.generateIOFromSignal(clock_wire, "clock", p(IOCellKey))
|
||||
val (reset_io, resetIOCell) = IOCell.generateIOFromSignal(reset_wire, "reset", p(IOCellKey))
|
||||
@@ -125,3 +83,41 @@ class WithPLLSelectorDividerClockGenerator extends OverrideLazyIOBinder({
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// This passes all clocks through to the TestHarness
|
||||
class WithPassthroughClockGenerator extends OverrideLazyIOBinder({
|
||||
(system: HasChipyardPRCI) => {
|
||||
// Connect the implicit clock
|
||||
implicit val p = GetSystemParameters(system)
|
||||
val implicitClockSinkNode = ClockSinkNode(Seq(ClockSinkParameters(name = Some("implicit_clock"))))
|
||||
system.connectImplicitClockSinkNode(implicitClockSinkNode)
|
||||
InModuleBody {
|
||||
val implicit_clock = implicitClockSinkNode.in.head._1.clock
|
||||
val implicit_reset = implicitClockSinkNode.in.head._1.reset
|
||||
system.asInstanceOf[BaseSubsystem].module match { case l: LazyModuleImp => {
|
||||
l.clock := implicit_clock
|
||||
l.reset := implicit_reset
|
||||
}}
|
||||
}
|
||||
|
||||
// This aggregate node should do nothing
|
||||
val clockGroupAggNode = ClockGroupAggregateNode("fake")
|
||||
val clockGroupsSourceNode = ClockGroupSourceNode(Seq(ClockGroupSourceParameters()))
|
||||
system.allClockGroupsNode :*= clockGroupAggNode := clockGroupsSourceNode
|
||||
|
||||
InModuleBody {
|
||||
val reset_io = IO(Input(AsyncReset()))
|
||||
val clock_ios = clockGroupAggNode.out.map { case (bundle, edge) =>
|
||||
val freqs = edge.sink.members.map(_.take.map(_.freqMHz)).flatten
|
||||
require(freqs.distinct.size == 1)
|
||||
val clock_io = IO(Input(new ClockWithFreq(freqs.head))).suggestName(s"clock_${edge.sink.name}")
|
||||
bundle.member.data.foreach { b =>
|
||||
b.clock := clock_io.clock
|
||||
b.reset := reset_io
|
||||
}
|
||||
clock_io
|
||||
}
|
||||
((clock_ios :+ reset_io), Nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -92,57 +92,3 @@ class SimplePllConfiguration(
|
||||
def referenceSinkParams(): ClockSinkParameters = sinkDividerMap.find(_._2 == 1).get._1
|
||||
}
|
||||
|
||||
case class DividerOnlyClockGeneratorNode(pllName: String)(implicit valName: ValName)
|
||||
extends MixedNexusNode(ClockImp, ClockGroupImp)(
|
||||
dFn = { _ => ClockGroupSourceParameters() },
|
||||
uFn = { u =>
|
||||
require(u.size == 1)
|
||||
require(!u.head.members.contains(None),
|
||||
"All output clocks in group must set their take parameters. Use a ClockGroupDealiaser")
|
||||
ClockSinkParameters(
|
||||
name = Some(s"${pllName}_reference_input"),
|
||||
take = Some(ClockParameters(new SimplePllConfiguration(pllName, u.head.members).referenceFreqMHz))) }
|
||||
)
|
||||
|
||||
/**
|
||||
* Generates a digital-divider-only PLL model that verilator can simulate.
|
||||
* Inspects all take-specified frequencies in the output ClockGroup, calculates a
|
||||
* fast reference clock (roughly LCM(requested frequencies)) which is passed up the
|
||||
* diplomatic graph, and then generates dividers for each unique requested
|
||||
* frequency.
|
||||
*
|
||||
* Output resets are not synchronized to generated clocks and should be
|
||||
* synchronized by the user in a manner they see fit.
|
||||
*
|
||||
*/
|
||||
|
||||
class DividerOnlyClockGenerator(pllName: String)(implicit p: Parameters, valName: ValName) extends LazyModule {
|
||||
val node = DividerOnlyClockGeneratorNode(pllName)
|
||||
|
||||
lazy val module = new Impl
|
||||
class Impl extends LazyRawModuleImp(this) {
|
||||
require(node.out.size == 1, "Idealized PLL expects to generate a single output clock group. Use a ClockGroupAggregator")
|
||||
val (refClock, ClockEdgeParameters(_, refSinkParam, _, _)) = node.in.head
|
||||
val (outClocks, ClockGroupEdgeParameters(_, outSinkParams, _, _)) = node.out.head
|
||||
|
||||
val referenceFreq = refSinkParam.take.get.freqMHz
|
||||
val pllConfig = new SimplePllConfiguration(pllName, outSinkParams.members)
|
||||
pllConfig.emitSummaries()
|
||||
|
||||
val dividedClocks = mutable.HashMap[Int, Clock]()
|
||||
def instantiateDivider(div: Int): Clock = {
|
||||
val divider = Module(new ClockDividerN(div))
|
||||
divider.suggestName(s"ClockDivideBy${div}")
|
||||
divider.io.clk_in := refClock.clock
|
||||
dividedClocks(div) = divider.io.clk_out
|
||||
divider.io.clk_out
|
||||
}
|
||||
|
||||
for (((sinkBName, sinkB), sinkP) <- outClocks.member.elements.zip(outSinkParams.members)) {
|
||||
val div = pllConfig.sinkDividerMap(sinkP)
|
||||
sinkB.clock := dividedClocks.getOrElse(div, instantiateDivider(div))
|
||||
// Reset handling and synchronization is expected to be handled by a downstream node
|
||||
sinkB.reset := refClock.reset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ class AbstractConfig extends Config(
|
||||
new chipyard.harness.WithTieOffL2FBusAXI ++ // tie-off external AXI4 master, if present
|
||||
new chipyard.harness.WithCustomBootPinPlusArg ++
|
||||
new chipyard.harness.WithClockAndResetFromHarness ++
|
||||
new chipyard.harness.WithAbsoluteFreqHarnessClockInstantiator ++
|
||||
|
||||
// The IOBinders instantiate ChipTop IOs to match desired digital IOs
|
||||
// IOCells are generated for "Chip-like" IOs, while simulation-only IOs are directly punched through
|
||||
@@ -41,10 +42,8 @@ class AbstractConfig extends Config(
|
||||
new chipyard.iobinders.WithExtInterruptIOCells ++
|
||||
new chipyard.iobinders.WithCustomBootPin ++
|
||||
|
||||
// Default behavior is to use a divider-only clock-generator
|
||||
// This works in VCS, Verilator, and FireSim/
|
||||
// This should get replaced with a PLL-like config instead
|
||||
new chipyard.clocking.WithDividerOnlyClockGenerator ++
|
||||
// By default, punch out IOs to the Harness
|
||||
new chipyard.clocking.WithPassthroughClockGenerator ++
|
||||
|
||||
new testchipip.WithCustomBootPin ++ // add a custom-boot-pin to support pin-driven boot address
|
||||
new testchipip.WithBootAddrReg ++ // add a boot-addr-reg for configurable boot address
|
||||
|
||||
@@ -4,12 +4,13 @@ import org.chipsalliance.cde.config.{Config}
|
||||
import freechips.rocketchip.rocket.{DCacheParams}
|
||||
|
||||
class AbstractTraceGenConfig extends Config(
|
||||
new chipyard.harness.WithAbsoluteFreqHarnessClockInstantiator ++
|
||||
new chipyard.harness.WithBlackBoxSimMem ++
|
||||
new chipyard.harness.WithTraceGenSuccess ++
|
||||
new chipyard.harness.WithClockAndResetFromHarness ++
|
||||
new chipyard.iobinders.WithAXI4MemPunchthrough ++
|
||||
new chipyard.iobinders.WithTraceGenSuccessPunchthrough ++
|
||||
new chipyard.clocking.WithDividerOnlyClockGenerator ++
|
||||
new chipyard.clocking.WithPassthroughClockGenerator ++
|
||||
new chipyard.config.WithTracegenSystem ++
|
||||
new chipyard.config.WithNoSubsystemDrivenClocks ++
|
||||
new chipyard.config.WithPeripheryBusFrequencyAsDefault ++
|
||||
|
||||
@@ -36,44 +36,6 @@ trait HarnessClockInstantiator {
|
||||
def instantiateHarnessClocks(refClock: ClockBundle): Unit
|
||||
}
|
||||
|
||||
// The DividerOnlyHarnessClockInstantiator uses synthesizable clock divisors
|
||||
// to approximate frequency ratios between the requested clocks
|
||||
class DividerOnlyHarnessClockInstantiator extends HarnessClockInstantiator {
|
||||
// connect all clock wires specified to a divider only PLL
|
||||
def instantiateHarnessClocks(refClock: ClockBundle): Unit = {
|
||||
val sinks = _clockMap.map({ case (name, (freq, bundle)) =>
|
||||
ClockSinkParameters(take=Some(ClockParameters(freqMHz=freq / (1000 * 1000))), name=Some(name))
|
||||
}).toSeq
|
||||
|
||||
val pllConfig = new SimplePllConfiguration("harnessDividerOnlyClockGenerator", sinks)
|
||||
pllConfig.emitSummaries()
|
||||
|
||||
val dividedClocks = LinkedHashMap[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
|
||||
}
|
||||
|
||||
// connect wires to clock source
|
||||
for (sinkParams <- sinks) {
|
||||
// bypass the reference freq. (don't create a divider + reset sync)
|
||||
val (divClock, divReset) = if (sinkParams.take.get.freqMHz != pllConfig.referenceFreqMHz) {
|
||||
val div = pllConfig.sinkDividerMap(sinkParams)
|
||||
val divClock = dividedClocks.getOrElse(div, instantiateDivider(div))
|
||||
(divClock, ResetCatchAndSync(divClock, refClock.reset.asBool))
|
||||
} else {
|
||||
(refClock.clock, refClock.reset)
|
||||
}
|
||||
|
||||
_clockMap(sinkParams.name.get)._2.clock := divClock
|
||||
_clockMap(sinkParams.name.get)._2.reset := divReset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The AbsoluteFreqHarnessClockInstantiator uses a Verilog blackbox to
|
||||
// provide the precise requested frequency.
|
||||
// This ClockInstantiator cannot be synthesized, run in Verilator, or run in FireSim
|
||||
|
||||
@@ -19,7 +19,7 @@ import chipyard.{ChipTop}
|
||||
|
||||
case object BuildTop extends Field[Parameters => LazyModule]((p: Parameters) => new ChipTop()(p))
|
||||
case object DefaultClockFrequencyKey extends Field[Double](100.0) // MHz
|
||||
case object HarnessClockInstantiatorKey extends Field[() => HarnessClockInstantiator](() => new DividerOnlyHarnessClockInstantiator)
|
||||
case object HarnessClockInstantiatorKey extends Field[() => HarnessClockInstantiator]()
|
||||
|
||||
trait HasHarnessSignalReferences {
|
||||
implicit val p: Parameters
|
||||
|
||||
Reference in New Issue
Block a user