WIP - Simple divider-only PLL generation flow
This commit is contained in:
40
generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv
Normal file
40
generators/chipyard/src/main/resources/vsrc/ClockDividerN.sv
Normal file
@@ -0,0 +1,40 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
/**
|
||||
* An unsynthesizable divide-by-N clock divider.
|
||||
* Duty cycle is 100 * (ceil(DIV / 2)) / 2.
|
||||
*/
|
||||
|
||||
module ClockDividerN #(parameter DIV)(output logic clk_out = 1'b0, input clk_in);
|
||||
|
||||
localparam DIV_COUNTER_WIDTH = $clog2(DIV);
|
||||
localparam LOW_CYCLES = DIV / 2;
|
||||
|
||||
generate
|
||||
if (DIV == 1) begin
|
||||
// This needs to be procedural because of the assignment on declaration
|
||||
always @(clk_in) begin
|
||||
clk_out = clk_in;
|
||||
end
|
||||
end else begin
|
||||
reg [DIV_COUNTER_WIDTH - 1: 0] count = '0;
|
||||
// The blocking assignment to clock out is used to conform what was done
|
||||
// in RC's clock dividers.
|
||||
// It should have the effect of preventing registers in the divided clock
|
||||
// domain latching register updates launched by the fast clock-domain edge
|
||||
// that occurs at the same simulated time (as the divided clock edge).
|
||||
always @(posedge clk_in) begin
|
||||
if (count == (DIV - 1)) begin
|
||||
clk_out = 1'b0;
|
||||
count <= '0;
|
||||
end
|
||||
else begin
|
||||
if (count == (LOW_CYCLES - 1)) begin
|
||||
clk_out = 1'b1;
|
||||
end
|
||||
count <= count + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
endmodule // ClockDividerN
|
||||
@@ -12,6 +12,8 @@ import freechips.rocketchip.util.{ResetCatchAndSync, Pow2ClockDivider}
|
||||
|
||||
import barstools.iocell.chisel._
|
||||
|
||||
import chipyard.clocking.{IdealizedPLL, ClockGroupDealiaser}
|
||||
|
||||
/**
|
||||
* Chipyard provides three baseline, top-level reset schemes, set using the
|
||||
* [[GlobalResetSchemeKey]] in a Parameters instance. These are:
|
||||
@@ -173,6 +175,40 @@ object ClockingSchemeGenerators {
|
||||
Nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
val idealizedPLL: ChipTop => Unit = { chiptop =>
|
||||
implicit val p = chiptop.p
|
||||
|
||||
// Requires existence of undriven asyncClockGroups in subsystem
|
||||
val systemAsyncClockGroup = chiptop.lSystem match {
|
||||
case l: BaseSubsystem if (p(SubsystemDriveAsyncClockGroupsKey).isEmpty) =>
|
||||
l.asyncClockGroupsNode
|
||||
}
|
||||
|
||||
val aggregator = ClockGroupAggregator()
|
||||
chiptop.implicitClockSinkNode := ClockGroup() := aggregator
|
||||
systemAsyncClockGroup := aggregator
|
||||
|
||||
val referenceClockSource = ClockSourceNode(Seq(ClockSourceParameters()))
|
||||
aggregator := ClockGroupDealiaser() := 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"))
|
||||
chiptop.iocells ++= clockIOCell
|
||||
clock_io.suggestName("clock")
|
||||
|
||||
referenceClockSource.out.unzip._1.map { o =>
|
||||
o.clock := clock_wire
|
||||
o.reset := reset_wire
|
||||
}
|
||||
|
||||
chiptop.harnessFunctions += ((th: HasHarnessUtils) => {
|
||||
clock_io := th.harnessClock
|
||||
Nil })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,11 @@ import sifive.blocks.devices.spi._
|
||||
|
||||
import chipyard.{BuildTop, BuildSystem, ClockingSchemeGenerators, ClockingSchemeKey, TestSuitesKey, TestSuiteHelper}
|
||||
|
||||
|
||||
// Imports for multiclock sketch
|
||||
import boom.common.{BoomTile, BoomTileParams}
|
||||
import ariane.{ArianeTile, ArianeTileParams}
|
||||
import chipyard.{GenericallyAttachableTile, GenericCrossingParams}
|
||||
import chipyard.clocking.{ClockNodeInjectionUtils }
|
||||
// -----------------------
|
||||
// Common Config Fragments
|
||||
// -----------------------
|
||||
@@ -170,3 +174,24 @@ class WithDMIDTM extends Config((site, here, up) => {
|
||||
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 WithIdealizedPLL extends Config((site, here, up) => {
|
||||
case ChipyardClockKey => ClockDrivers.idealizedPLL
|
||||
})
|
||||
|
||||
38
generators/chipyard/src/main/scala/GenericAttachParams.scala
Normal file
38
generators/chipyard/src/main/scala/GenericAttachParams.scala
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
package chipyard
|
||||
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.config.{Field, Parameters}
|
||||
import freechips.rocketchip.subsystem._
|
||||
import freechips.rocketchip.tile.{LookupByHartIdImpl, TileParams, InstantiableTileParams, BaseTile}
|
||||
|
||||
import chipyard.clocking.ClockNodeInjectionUtils._
|
||||
|
||||
case class GenericCrossingParams(
|
||||
crossingType: ClockCrossingType = SynchronousCrossing(),
|
||||
master: TilePortParamsLike = TileMasterPortParams(),
|
||||
slave: TilePortParamsLike = TileSlavePortParams(),
|
||||
mmioBaseAddressPrefixWhere: TLBusWrapperLocation = CBUS,
|
||||
injectClockNodeFunc: InjectClockNodeFunc = injectIdentityClockNode,
|
||||
forceSeparateClockReset: Boolean = false) extends TileCrossingParamsLike {
|
||||
|
||||
def injectClockNode(a: Attachable)(implicit p: Parameters) = injectClockNodeFunc(a, p)
|
||||
}
|
||||
|
||||
object GenericCrossingParams {
|
||||
def apply(params: TileCrossingParamsLike): GenericCrossingParams = GenericCrossingParams(
|
||||
params.crossingType,
|
||||
params.master,
|
||||
params.slave,
|
||||
params.mmioBaseAddressPrefixWhere,
|
||||
(a: Attachable, p: Parameters) => params.injectClockNode(a)(p),
|
||||
params.forceSeparateClockReset)
|
||||
}
|
||||
|
||||
case class GenericallyAttachableTile[TT <: BaseTile](
|
||||
tileParams: InstantiableTileParams[TT],
|
||||
crossingParams: GenericCrossingParams,
|
||||
lookup: LookupByHartIdImpl) extends CanAttachTile {
|
||||
type TileType = TT
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package chipyard.clocking
|
||||
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
|
||||
class ClockDividerN(div: Int) extends BlackBox(Map("DIV" -> div)) with HasBlackBoxResource {
|
||||
require(div > 0);
|
||||
val io = IO(new Bundle {
|
||||
val clk_out = Output(Clock())
|
||||
val clk_in = Input(Clock())
|
||||
})
|
||||
addResource("/vsrc/ClockDividerN.sv")
|
||||
}
|
||||
|
||||
object ClockDivideByN {
|
||||
def apply(clockIn: Clock, div: Int): Clock = {
|
||||
val clockDivider = Module(new ClockDividerN(div))
|
||||
clockDivider.io.clk_in := clockIn
|
||||
clockDivider.io.clk_out
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package chipyard.clocking
|
||||
|
||||
import chisel3._
|
||||
|
||||
import freechips.rocketchip.config.{Parameters}
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.prci._
|
||||
|
||||
/**
|
||||
* Somewhat hacky. Since not all clocks in a clock group specify a taken frequency
|
||||
* current, this LazyModule attempts to dealias them, by finding a specified
|
||||
* clock whose name has the longest matching prefix.
|
||||
*
|
||||
* Perhaps another, simpler solution would be to pass a default.
|
||||
*
|
||||
*/
|
||||
|
||||
case class ClockGroupDealiaserNode()(implicit valName: ValName)
|
||||
extends NexusNode(ClockGroupImp)(
|
||||
dFn = { _ => ClockGroupSourceParameters() },
|
||||
uFn = { u =>
|
||||
require(u.size == 1)
|
||||
val takenClocks = u.head.members.filter(_.take.nonEmpty)
|
||||
require(takenClocks.nonEmpty,
|
||||
"At least one sink clock in clock group must specify its take parameter")
|
||||
u.head.copy(members = takenClocks)
|
||||
})
|
||||
|
||||
class ClockGroupDealiaser(name: String)(implicit p: Parameters) extends LazyModule {
|
||||
val node = ClockGroupDealiaserNode()
|
||||
|
||||
lazy val module = new LazyRawModuleImp(this) {
|
||||
require(node.out.size == 1, "Must use a ClockGroupAggregator")
|
||||
val (outClocks, e @ ClockGroupEdgeParameters(_, outSinkParams, _, _)) = node.out.head
|
||||
val (inClocks, ClockGroupEdgeParameters(_, inSinkParams, _, _)) = node.in.head
|
||||
val inMap = inClocks.member.data.zip(inSinkParams.members).map({ case (b, p) => p.name -> b}).toMap
|
||||
|
||||
for (((outBName, outB), outName) <- outClocks.member.elements.zip(outSinkParams.members.map(_.name))) {
|
||||
val inClock = inMap.getOrElse(outName, throw new Exception("""
|
||||
| No clock in input group with name: Option matching ${outName}. At least one clock
|
||||
| with the same must specify a frequency in its take parameter.""".stripMargin))
|
||||
// This will be removed.
|
||||
dontTouch(outB)
|
||||
outB := inClock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ClockGroupDealiaser {
|
||||
def apply()(implicit p: Parameters, valName: ValName) = LazyModule(new ClockGroupDealiaser(valName.name)).node
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
package chipyard.clocking
|
||||
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.config.{Parameters}
|
||||
import freechips.rocketchip.subsystem._
|
||||
import freechips.rocketchip.prci.{ClockNode, ClockTempNode, ClockAdapterNode, ClockParameters}
|
||||
/**
|
||||
* An adapter node hack c that just throws out the existing sink node
|
||||
* clock parameters in favor of the provided ones.
|
||||
*/
|
||||
class ForceTakeClock(clockParams: Option[ClockParameters])(implicit p: Parameters, v: ValName) extends LazyModule {
|
||||
val node = ClockAdapterNode(sinkFn = { s => s.copy(take = clockParams) })
|
||||
lazy val module = new LazyRawModuleImp(this) {
|
||||
(node.out zip node.in) map { case ((o, _), (i, _)) => o := i }
|
||||
}
|
||||
}
|
||||
|
||||
object ForceTakeClock {
|
||||
def apply(clockParams: Option[ClockParameters])(implicit p: Parameters, v: ValName): ClockAdapterNode =
|
||||
LazyModule(new ForceTakeClock(clockParams)).node
|
||||
}
|
||||
|
||||
object ClockNodeInjectionUtils {
|
||||
type InjectClockNodeFunc = (Attachable, Parameters) => ClockNode
|
||||
val injectIdentityClockNode: InjectClockNodeFunc = (a: Attachable, p: Parameters) => ClockTempNode()
|
||||
def forceTakeFrequency(freqMHz: Double): InjectClockNodeFunc =
|
||||
(a: Attachable, p: Parameters) => ForceTakeClock(Some(ClockParameters(freqMHz)))(p, ValName("ForcedTakeClock"))
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package chipyard.clocking
|
||||
|
||||
import chisel3._
|
||||
|
||||
import freechips.rocketchip.config.{Parameters}
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.prci._
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
object FrequencyUtils {
|
||||
def computeReferenceFrequencyMHz(
|
||||
requestedOutputs: Seq[ClockParameters],
|
||||
maximumAllowableDivisor: Int = 0xFFFF): ClockParameters = {
|
||||
require(requestedOutputs.nonEmpty)
|
||||
require(!requestedOutputs.contains(0.0))
|
||||
val freqs = requestedOutputs.map(f => BigInt(Math.round(f.freqMHz * 1000 * 1000)))
|
||||
val refFreq = freqs.reduce((a, b) => a * b / a.gcd(b)).toDouble / (1000 * 1000)
|
||||
assert((refFreq / freqs.min.toDouble) < maximumAllowableDivisor.toDouble)
|
||||
ClockParameters(refFreq)
|
||||
}
|
||||
}
|
||||
|
||||
case class IdealizedPLLNode(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(FrequencyUtils.computeReferenceFrequencyMHz(u.head.members.flatMap(_.take)))) }
|
||||
)
|
||||
|
||||
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")
|
||||
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)
|
||||
|
||||
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 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}")
|
||||
sinkB.clock := dividedClocks.getOrElse(div, instantiateDivider(div))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object IdealizedPLL {
|
||||
def apply()(implicit p: Parameters, valName: ValName) = LazyModule(new IdealizedPLL(valName.name)).node
|
||||
}
|
||||
@@ -182,4 +182,11 @@ class DividedClockRocketConfig extends Config(
|
||||
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 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