diff --git a/generators/chipyard/src/main/scala/HarnessBinders.scala b/generators/chipyard/src/main/scala/HarnessBinders.scala index 51d39ce4..c39242f9 100644 --- a/generators/chipyard/src/main/scala/HarnessBinders.scala +++ b/generators/chipyard/src/main/scala/HarnessBinders.scala @@ -154,7 +154,7 @@ class WithSimAXIMemOverSerialTL extends OverrideHarnessBinder({ ports.map({ port => // DOC include start: HarnessClockInstantiatorEx withClockAndReset(th.buildtopClock, th.buildtopReset) { - val memOverSerialTLClockBundle = p(HarnessClockInstantiatorKey).requestClockBundle("mem_over_serial_tl_clock", memFreq) + val memOverSerialTLClockBundle = th.harnessClockInstantiator.requestClockBundle("mem_over_serial_tl_clock", memFreq) val serial_bits = SerialAdapter.asyncQueue(port, th.buildtopClock, th.buildtopReset) val harnessMultiClockAXIRAM = SerialAdapter.connectHarnessMultiClockAXIRAM( system.serdesser.get, diff --git a/generators/chipyard/src/main/scala/HarnessClocks.scala b/generators/chipyard/src/main/scala/HarnessClocks.scala new file mode 100644 index 00000000..f973c292 --- /dev/null +++ b/generators/chipyard/src/main/scala/HarnessClocks.scala @@ -0,0 +1,85 @@ +package chipyard + +import chisel3._ + +import scala.collection.mutable.{ArrayBuffer, LinkedHashMap} +import freechips.rocketchip.diplomacy.{LazyModule} +import freechips.rocketchip.config.{Field, Parameters, Config} +import freechips.rocketchip.util.{ResetCatchAndSync} +import freechips.rocketchip.prci._ + +import chipyard.harness.{ApplyHarnessBinders, HarnessBinders} +import chipyard.iobinders.HasIOBinders +import chipyard.clocking.{SimplePllConfiguration, ClockDividerN} +import chipyard.HarnessClockInstantiatorKey + +trait HarnessClockInstantiator { + val _clockMap: LinkedHashMap[String, (Double, ClockBundle)] = LinkedHashMap.empty + + // request a clock bundle at a particular frequency + def requestClockBundle(name: String, freqRequested: Double): ClockBundle = { + val clockBundle = Wire(new ClockBundle(ClockBundleParameters())) + _clockMap(name) = (freqRequested, clockBundle) + clockBundle + } + + def instantiateHarnessClocks(refClock: ClockBundle): Unit +} + +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 + } + } +} + +class AbsoluteFreqHarnessClockInstantiator extends HarnessClockInstantiator { + 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 + + // connect wires to clock source + for (sinkParams <- sinks) { + val source = Module(new ClockSourceAtFreq(sinkParams.take.get.freqMHz)) + source.io.power := true.B + source.io.gate := false.B + + _clockMap(sinkParams.name.get)._2.clock := source.io.clk + _clockMap(sinkParams.name.get)._2.reset := refClock.reset + } + } +} + +class WithAbsoluteFreqHarnessClockInstantiator extends Config((site, here, up) => { + case HarnessClockInstantiatorKey => () => new AbsoluteFreqHarnessClockInstantiator +}) diff --git a/generators/chipyard/src/main/scala/TestHarness.scala b/generators/chipyard/src/main/scala/TestHarness.scala index 23bda680..a8b18d4f 100644 --- a/generators/chipyard/src/main/scala/TestHarness.scala +++ b/generators/chipyard/src/main/scala/TestHarness.scala @@ -18,9 +18,11 @@ import chipyard.clocking.{SimplePllConfiguration, ClockDividerN} 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) trait HasHarnessSignalReferences { implicit val p: Parameters + val harnessClockInstantiator = p(HarnessClockInstantiatorKey)() // clock/reset of the chiptop reference clock (can be different than the implicit harness clock/reset) var refClockFreq: Double = p(DefaultClockFrequencyKey) def setRefClockFreq(freqMHz: Double) = { refClockFreq = freqMHz } @@ -30,53 +32,6 @@ trait HasHarnessSignalReferences { def success: Bool } -class HarnessClockInstantiator { - private val _clockMap: LinkedHashMap[String, (Double, ClockBundle)] = LinkedHashMap.empty - - // request a clock bundle at a particular frequency - def requestClockBundle(name: String, freqRequested: Double): ClockBundle = { - val clockBundle = Wire(new ClockBundle(ClockBundleParameters())) - _clockMap(name) = (freqRequested, clockBundle) - clockBundle - } - - // connect all clock wires specified to a divider only PLL - def instantiateHarnessDividerPLL(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 - } - } -} - -case object HarnessClockInstantiatorKey extends Field[HarnessClockInstantiator](new HarnessClockInstantiator) - class TestHarness(implicit val p: Parameters) extends Module with HasHarnessSignalReferences { val io = IO(new Bundle { val success = Output(Bool()) @@ -96,7 +51,7 @@ class TestHarness(implicit val p: Parameters) extends Module with HasHarnessSign ApplyHarnessBinders(this, d.lazySystem, d.portMap) } - val refClkBundle = p(HarnessClockInstantiatorKey).requestClockBundle("buildtop_reference_clock", getRefClockFreq * (1000 * 1000)) + val refClkBundle = harnessClockInstantiator.requestClockBundle("buildtop_reference_clock", getRefClockFreq * (1000 * 1000)) buildtopClock := refClkBundle.clock buildtopReset := WireInit(refClkBundle.reset) @@ -104,5 +59,5 @@ class TestHarness(implicit val p: Parameters) extends Module with HasHarnessSign val implicitHarnessClockBundle = Wire(new ClockBundle(ClockBundleParameters())) implicitHarnessClockBundle.clock := clock implicitHarnessClockBundle.reset := reset - p(HarnessClockInstantiatorKey).instantiateHarnessDividerPLL(implicitHarnessClockBundle) + harnessClockInstantiator.instantiateHarnessClocks(implicitHarnessClockBundle) } diff --git a/generators/chipyard/src/main/scala/clocking/ClockBinders.scala b/generators/chipyard/src/main/scala/clocking/ClockBinders.scala index 5aa7cbc8..01eb261c 100644 --- a/generators/chipyard/src/main/scala/clocking/ClockBinders.scala +++ b/generators/chipyard/src/main/scala/clocking/ClockBinders.scala @@ -6,6 +6,7 @@ import chipyard.iobinders.{OverrideLazyIOBinder, GetSystemParameters, IOCellKey} import freechips.rocketchip.prci._ import freechips.rocketchip.diplomacy._ import freechips.rocketchip.subsystem._ +import freechips.rocketchip.tilelink._ import barstools.iocell.chisel._ class ClockWithFreq(val freqMHz: Double) extends Bundle { @@ -50,3 +51,63 @@ class WithDividerOnlyClockGenerator extends OverrideLazyIOBinder({ } } }) + +// Note: This will not simulate properly with verilator or firesim +class WithPLLSelectorDividerClockGenerator 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 + }} + } + val tlbus = system.asInstanceOf[BaseSubsystem].locateTLBusWrapper(system.prciParams.slaveWhere) + val baseAddress = system.prciParams.baseAddress + val clockDivider = system.prci_ctrl_domain { LazyModule(new TLClockDivider (baseAddress + 0x20000, tlbus.beatBytes)) } + val clockSelector = system.prci_ctrl_domain { LazyModule(new TLClockSelector(baseAddress + 0x30000, tlbus.beatBytes)) } + val pllCtrl = system.prci_ctrl_domain { LazyModule(new FakePLLCtrl (baseAddress + 0x40000, tlbus.beatBytes)) } + + tlbus.toVariableWidthSlave(Some("clock-div-ctrl")) { clockDivider.tlNode := TLBuffer() } + tlbus.toVariableWidthSlave(Some("clock-sel-ctrl")) { clockSelector.tlNode := TLBuffer() } + tlbus.toVariableWidthSlave(Some("pll-ctrl")) { pllCtrl.tlNode := TLBuffer() } + + system.allClockGroupsNode := clockDivider.clockNode := clockSelector.clockNode + + // Connect all other requested clocks + val slowClockSource = ClockSourceNode(Seq(ClockSourceParameters())) + val pllClockSource = ClockSourceNode(Seq(ClockSourceParameters())) + + // The order of the connections to clockSelector.clockNode configures what + clockSelector.clockNode := slowClockSource + clockSelector.clockNode := pllClockSource + + val pllCtrlSink = BundleBridgeSink[FakePLLCtrlBundle]() + pllCtrlSink := pllCtrl.ctrlNode + + InModuleBody { + val clock_wire = Wire(Input(new ClockWithFreq(80))) + 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)) + + slowClockSource.out.unzip._1.map { o => + o.clock := clock_wire.clock + o.reset := reset_wire + } + + // For a real chip you should replace this ClockSourceAtFreqFromPlusArg + // with a blackbox of whatever PLL is being integrated + val fakeClockSource = Module(new ClockSourceAtFreqFromPlusArg("pll_freq_mhz")) + fakeClockSource.io.power := pllCtrlSink.in(0)._1.power + fakeClockSource.io.gate := pllCtrlSink.in(0)._1.gate + + (Seq(clock_io, reset_io), clockIOCell ++ resetIOCell) + } + } +}) diff --git a/generators/chipyard/src/main/scala/clocking/FakePLL.scala b/generators/chipyard/src/main/scala/clocking/FakePLL.scala new file mode 100644 index 00000000..a903ced6 --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/FakePLL.scala @@ -0,0 +1,36 @@ +package chipyard.clocking + +import chisel3._ +import chisel3.util._ +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.devices.tilelink._ +import freechips.rocketchip.regmapper._ +import freechips.rocketchip.util._ + +class FakePLLCtrlBundle extends Bundle { + val gate = Bool() + val power = Bool() +} + +class FakePLLCtrl(address: BigInt, beatBytes: Int)(implicit p: Parameters) extends LazyModule +{ + val device = new SimpleDevice(s"pll", Nil) + val tlNode = TLRegisterNode(Seq(AddressSet(address, 4096-1)), device, "reg/control", beatBytes=beatBytes) + val ctrlNode = BundleBridgeSource(() => Output(new FakePLLCtrlBundle)) + lazy val module = new LazyModuleImp(this) { + // This PLL only has 2 address, the gate and power + // Both should be set to turn on the PLL + // TODO: Should these be reset by the top level reset pin? + val gate_reg = Module(new AsyncResetRegVec(w=1, init=0)) + val power_reg = Module(new AsyncResetRegVec(w=1, init=0)) + + ctrlNode.out(0)._1.gate := gate_reg.io.q + ctrlNode.out(0)._1.power := power_reg.io.q + tlNode.regmap( + 0 -> Seq(RegField.rwReg(1, gate_reg.io)), + 4 -> Seq(RegField.rwReg(1, power_reg.io)) + ) + } +} diff --git a/generators/chipyard/src/main/scala/clocking/TLClockDivider.scala b/generators/chipyard/src/main/scala/clocking/TLClockDivider.scala new file mode 100644 index 00000000..b9d62344 --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/TLClockDivider.scala @@ -0,0 +1,52 @@ +package chipyard.clocking + +import chisel3._ + +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.devices.tilelink._ +import freechips.rocketchip.regmapper._ +import freechips.rocketchip.util._ +import freechips.rocketchip.prci._ +import freechips.rocketchip.util.ElaborationArtefacts + +import testchipip._ + +class TLClockDivider(address: BigInt, beatBytes: Int, divBits: Int = 8)(implicit p: Parameters) extends LazyModule { + val device = new SimpleDevice(s"clk-div-ctrl", Nil) + val clockNode = ClockGroupIdentityNode() + val tlNode = TLRegisterNode(Seq(AddressSet(address, 4096-1)), device, "reg/control", beatBytes=beatBytes) + + lazy val module = new LazyModuleImp(this) { + require (clockNode.out.size == 1) + val sources = clockNode.in.head._1.member.data.toSeq + val sinks = clockNode.out.head._1.member.elements.toSeq + require (sources.size == sinks.size) + val nSinks = sinks.size + + val regs = (0 until nSinks) .map { i => + val sinkName = sinks(i)._1 + val asyncReset = sources(i).reset + val reg = withReset (asyncReset) { + Module(new AsyncResetRegVec(w=divBits, init=0)) + } + println(s"${(address+i*4).toString(16)}: Clock domain $sinkName divider") + sinks(i)._2.clock := withClockAndReset(sources(i).clock, asyncReset) { + val divider = Module(new testchipip.ClockDivideOrPass(divBits, depth = 3, genClockGate = p(ClockGateImpl))) + divider.io.divisor := reg.io.q + divider.io.resetAsync := ResetStretcher(sources(i).clock, asyncReset, 20).asAsyncReset + divider.io.clockOut + } + + // Note this is not synchronized to the output clock, which takes time to appear + // so this is still asyncreset + sinks(i)._2.reset := ResetStretcher(sources(i).clock, asyncReset, 40).asAsyncReset + reg + } + + tlNode.regmap((0 until nSinks).map { i => + i * 4 -> Seq(RegField.rwReg(divBits, regs(i).io)) + }: _*) + } +} diff --git a/generators/chipyard/src/main/scala/clocking/TLClockSelector.scala b/generators/chipyard/src/main/scala/clocking/TLClockSelector.scala new file mode 100644 index 00000000..22110e62 --- /dev/null +++ b/generators/chipyard/src/main/scala/clocking/TLClockSelector.scala @@ -0,0 +1,70 @@ +package chipyard.clocking + +import chisel3._ +import chisel3.util._ +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.devices.tilelink._ +import freechips.rocketchip.regmapper._ +import freechips.rocketchip.util._ +import freechips.rocketchip.prci._ +import freechips.rocketchip.util.ElaborationArtefacts + +import testchipip._ + +object ResetStretcher { + def apply(clock: Clock, reset: Reset, cycles: Int): Reset = { + withClockAndReset(clock, reset) { + val n = log2Ceil(cycles) + val count = Module(new AsyncResetRegVec(w=n, init=0)) + val resetout = Module(new AsyncResetRegVec(w=1, init=1)) + count.io.en := resetout.io.q + count.io.d := count.io.q + 1.U + resetout.io.en := resetout.io.q + resetout.io.d := count.io.q < (cycles-1).U + + resetout.io.q.asBool + } + } +} + + +case class ClockSelNode()(implicit valName: ValName) + extends MixedNexusNode(ClockImp, ClockGroupImp)( + dFn = { d => ClockGroupSourceParameters() }, + uFn = { u => ClockSinkParameters() } +) + +class TLClockSelector(address: BigInt, beatBytes: Int)(implicit p: Parameters) extends LazyModule { + val device = new SimpleDevice("clk-sel-ctrl", Nil) + val tlNode = TLRegisterNode(Seq(AddressSet(address, 4096-1)), device, "reg/control", beatBytes=beatBytes) + + val clockNode = ClockSelNode() + + lazy val module = new LazyModuleImp(this) { + val asyncReset = clockNode.in.map(_._1).map(_.reset).toSeq(0) + val clocks = clockNode.in.map(_._1).map(_.clock) + val (outClocks, _) = clockNode.out.head + val (sinkNames, sinks) = outClocks.member.elements.toSeq.unzip + + val regs = (0 until sinks.size).map { i => + val sinkName = sinkNames(i) + val sel = Wire(UInt(log2Ceil(clocks.size).W)) + val reg = withReset(asyncReset) { Module(new AsyncResetRegVec(w=log2Ceil(clocks.size), init=0)) } + sel := reg.io.q + println(s"${(address+i*4).toString(16)}: Clock domain $sinkName clock mux") + + val mux = testchipip.ClockMutexMux(clocks).suggestName(s"${sinkName}_clkmux") + mux.io.sel := sel + mux.io.resetAsync := asyncReset.asAsyncReset + sinks(i).clock := mux.io.clockOut + sinks(i).reset := ResetStretcher(clocks(0), asyncReset, 20).asAsyncReset + + reg + } + tlNode.regmap((0 until sinks.size).map { i => + i * 4 -> Seq(RegField.rwReg(log2Ceil(clocks.size), regs(i).io)) + }: _*) + } +} diff --git a/generators/chipyard/src/main/scala/clocking/TileClockGater.scala b/generators/chipyard/src/main/scala/clocking/TileClockGater.scala index 9b7696fb..a77b02d5 100644 --- a/generators/chipyard/src/main/scala/clocking/TileClockGater.scala +++ b/generators/chipyard/src/main/scala/clocking/TileClockGater.scala @@ -32,7 +32,7 @@ class TileClockGater(address: BigInt, beatBytes: Int, enable: Boolean)(implicit val sinkName = sinks(i)._1 val reg = withReset(sources(i).reset) { Module(new AsyncResetRegVec(w=1, init=1)) } if (sinkName.contains("tile") && enable) { - println(s"ClockGate for ${sinkName} regmapped at ${(address+i*4).toString(16)}") + println(s"${(address+i*4).toString(16)}: Tile $sinkName clock gate") sinks(i)._2.clock := ClockGate(sources(i).clock, reg.io.q.asBool) sinks(i)._2.reset := sources(i).reset } else { diff --git a/generators/chipyard/src/main/scala/clocking/TileResetSetter.scala b/generators/chipyard/src/main/scala/clocking/TileResetSetter.scala index d8243e37..9ea4bfd5 100644 --- a/generators/chipyard/src/main/scala/clocking/TileResetSetter.scala +++ b/generators/chipyard/src/main/scala/clocking/TileResetSetter.scala @@ -39,16 +39,16 @@ class TileResetSetter(address: BigInt, beatBytes: Int, tileNames: Seq[String], i }): _*) val tileMap = tileNames.zipWithIndex.map({ case (n, i) => - n -> (tile_async_resets(i), r_tile_resets(i).io.q) + n -> (tile_async_resets(i), r_tile_resets(i).io.q, address + i * 4) }) (clockNode.out zip clockNode.in).map { case ((o, _), (i, _)) => (o.member.elements zip i.member.elements).foreach { case ((name, oD), (_, iD)) => oD.clock := iD.clock oD.reset := iD.reset - for ((n, (rIn, rOut)) <- tileMap) { + for ((n, (rIn, rOut, addr)) <- tileMap) { if (name.contains(n)) { - println(name, n) + println(s"${addr.toString(16)}: Tile $name reset control") // Async because the reset coming out of the AsyncResetRegVec is // clocked to the bus this is attached to, not the clock in this // clock bundle. We expect a ClockGroupResetSynchronizer downstream