[clocking] Improve reference clock selection using a multiple-of-fastest strategy
This commit is contained in:
@@ -14,21 +14,65 @@ import scala.collection.immutable.ListMap
|
|||||||
* TODO: figure out how much division is acceptable in our simulators and redefine this.
|
* TODO: figure out how much division is acceptable in our simulators and redefine this.
|
||||||
*/
|
*/
|
||||||
object FrequencyUtils {
|
object FrequencyUtils {
|
||||||
def computeReferenceFrequencyMHz(
|
/**
|
||||||
|
* Adds up the squared error between the generated clocks (refClock / [integer] divider)
|
||||||
|
* and the requested frequencies.
|
||||||
|
*
|
||||||
|
* @param refMHz The candidate reference clock
|
||||||
|
* @param desiredFreqMHz A list of the requested output frequencies
|
||||||
|
*/
|
||||||
|
def squaredError(refMHz: Double, desiredFreqMHz: List[Double], sum: Double = 0.0): Double = desiredFreqMHz match {
|
||||||
|
case Nil => sum
|
||||||
|
case desired :: xs =>
|
||||||
|
val divider = Math.round(refMHz / desired)
|
||||||
|
val termError = ((refMHz / divider) - desired) / desired
|
||||||
|
squaredError(refMHz, xs, sum + termError * termError)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Picks a candidate reference frequency by doing a brute-force search over
|
||||||
|
* multiples of the fastest requested clock. Choose the smallest multiple that
|
||||||
|
* has an RMS error (across all output frequencies) that is:
|
||||||
|
* 1) zero or failing that,
|
||||||
|
* 2) is within the relativeThreshold of the best or is less than the absoluteThreshold
|
||||||
|
*
|
||||||
|
* @param requestedOutputs The desired output frequencies in MHz
|
||||||
|
* @param maximumAllowableFreqMHz The maximum allowable reference in MHz
|
||||||
|
* @param relativeThreshold See above
|
||||||
|
* @param absoluteThreshold See above
|
||||||
|
*/
|
||||||
|
def computeReferenceAsMultipleOfFastestClock(
|
||||||
requestedOutputs: Seq[ClockParameters],
|
requestedOutputs: Seq[ClockParameters],
|
||||||
maximumAllowableFreqMHz: Double = 8000.0): ClockParameters = {
|
maximumAllowableFreqMHz: Double,
|
||||||
|
relativeThreshold: Double = 1.10,
|
||||||
|
absoluteThreshold: Double = 0.01): ClockParameters = {
|
||||||
|
|
||||||
require(requestedOutputs.nonEmpty)
|
require(requestedOutputs.nonEmpty)
|
||||||
require(!requestedOutputs.contains(0.0))
|
require(!requestedOutputs.contains(0.0))
|
||||||
val freqs = requestedOutputs.map(f => BigInt(Math.round(f.freqMHz * 1000 * 1000)))
|
val requestedFreqs = requestedOutputs.map(_.freqMHz)
|
||||||
val refFreq = freqs.reduce((a, b) => a * b / a.gcd(b)).toDouble / (1000 * 1000)
|
val fastestFreq = requestedFreqs.max
|
||||||
assert(refFreq < maximumAllowableFreqMHz,
|
require(fastestFreq < maximumAllowableFreqMHz)
|
||||||
s"Reference frequency ${refFreq} exceeds maximum allowable value of ${maximumAllowableFreqMHz} MHz")
|
|
||||||
ClockParameters(refFreq)
|
val candidateFreqs =
|
||||||
|
Seq.tabulate(Math.ceil(maximumAllowableFreqMHz / fastestFreq).toInt)(i => (i + 1) * fastestFreq)
|
||||||
|
val errorTuples = candidateFreqs.map { f =>
|
||||||
|
f -> Math.sqrt(squaredError(f, requestedFreqs.toList) / requestedFreqs.size)
|
||||||
|
}
|
||||||
|
val minError = errorTuples.map(_._2).min
|
||||||
|
val viableFreqs = errorTuples.collect {
|
||||||
|
case (f, error) if (error <= minError * relativeThreshold) || (minError > 0 && error < absoluteThreshold) => f
|
||||||
|
}
|
||||||
|
ClockParameters(viableFreqs.min)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimplePllConfiguration(name: String, val sinks: Seq[ClockSinkParameters]) {
|
class SimplePllConfiguration(
|
||||||
val referenceFreqMHz = FrequencyUtils.computeReferenceFrequencyMHz(sinks.flatMap(_.take)).freqMHz
|
name: String,
|
||||||
|
val sinks: Seq[ClockSinkParameters],
|
||||||
|
maximumAllowableFreqMHz: Double = 16000.0 ) {
|
||||||
|
val referenceFreqMHz = FrequencyUtils.computeReferenceAsMultipleOfFastestClock(
|
||||||
|
sinks.flatMap(_.take),
|
||||||
|
maximumAllowableFreqMHz).freqMHz
|
||||||
val sinkDividerMap = ListMap((sinks.map({s => (s, Math.round(referenceFreqMHz / s.take.get.freqMHz).toInt) })):_*)
|
val sinkDividerMap = ListMap((sinks.map({s => (s, Math.round(referenceFreqMHz / s.take.get.freqMHz).toInt) })):_*)
|
||||||
|
|
||||||
private val preamble = s"""
|
private val preamble = s"""
|
||||||
@@ -41,8 +85,10 @@ class SimplePllConfiguration(name: String, val sinks: Seq[ClockSinkParameters])
|
|||||||
}
|
}
|
||||||
|
|
||||||
val summaryString = preamble + outputSummaries.mkString("\n")
|
val summaryString = preamble + outputSummaries.mkString("\n")
|
||||||
ElaborationArtefacts.add(s"${name}.freq-summary", summaryString)
|
def emitSummaries(): Unit = {
|
||||||
println(summaryString)
|
ElaborationArtefacts.add(s"${name}.freq-summary", summaryString)
|
||||||
|
println(summaryString)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class DividerOnlyClockGeneratorNode(pllName: String)(implicit valName: ValName)
|
case class DividerOnlyClockGeneratorNode(pllName: String)(implicit valName: ValName)
|
||||||
@@ -54,7 +100,7 @@ case class DividerOnlyClockGeneratorNode(pllName: String)(implicit valName: ValN
|
|||||||
"All output clocks in group must set their take parameters. Use a ClockGroupDealiaser")
|
"All output clocks in group must set their take parameters. Use a ClockGroupDealiaser")
|
||||||
ClockSinkParameters(
|
ClockSinkParameters(
|
||||||
name = Some(s"${pllName}_reference_input"),
|
name = Some(s"${pllName}_reference_input"),
|
||||||
take = Some(FrequencyUtils.computeReferenceFrequencyMHz(u.head.members.flatMap(_.take)))) }
|
take = Some(ClockParameters(new SimplePllConfiguration(pllName, u.head.members).referenceFreqMHz))) }
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,6 +125,7 @@ class DividerOnlyClockGenerator(pllName: String)(implicit p: Parameters, valName
|
|||||||
|
|
||||||
val referenceFreq = refSinkParam.take.get.freqMHz
|
val referenceFreq = refSinkParam.take.get.freqMHz
|
||||||
val pllConfig = new SimplePllConfiguration(pllName, outSinkParams.members)
|
val pllConfig = new SimplePllConfiguration(pllName, outSinkParams.members)
|
||||||
|
pllConfig.emitSummaries()
|
||||||
|
|
||||||
val dividedClocks = mutable.HashMap[Int, Clock]()
|
val dividedClocks = mutable.HashMap[Int, Clock]()
|
||||||
def instantiateDivider(div: Int): Clock = {
|
def instantiateDivider(div: Int): Clock = {
|
||||||
|
|||||||
@@ -5,17 +5,25 @@ import freechips.rocketchip.prci._
|
|||||||
|
|
||||||
class SimplePllConfigurationSpec extends org.scalatest.FlatSpec {
|
class SimplePllConfigurationSpec extends org.scalatest.FlatSpec {
|
||||||
|
|
||||||
def conf(freqMHz: Iterable[Double]): SimplePllConfiguration = new SimplePllConfiguration("test",
|
def genConf(freqMHz: Iterable[Double]): SimplePllConfiguration = new SimplePllConfiguration(
|
||||||
|
"testPLL",
|
||||||
freqMHz.map({ f => ClockSinkParameters(
|
freqMHz.map({ f => ClockSinkParameters(
|
||||||
name = Some(s"desiredFreq_$f"),
|
name = Some(s"desiredFreq_$f"),
|
||||||
take = Some(ClockParameters(f))) }).toSeq)
|
take = Some(ClockParameters(f))) }).toSeq,
|
||||||
|
maximumAllowableFreqMHz = 16000.0)
|
||||||
|
|
||||||
def tryConf(freqMHz: Double*): Unit = {
|
def trySuccessfulConf(requestedFreqs: Seq[Double], expected: Double): Unit = {
|
||||||
val freqStr = freqMHz.mkString(", ")
|
val freqStr = requestedFreqs.mkString(", ")
|
||||||
it should s"configure for ${freqStr} MHz" in { conf(freqMHz) }
|
it should s"select a reference of ${expected} MHz for ${freqStr} MHz" in {
|
||||||
|
val conf = genConf(requestedFreqs)
|
||||||
|
conf.emitSummaries
|
||||||
|
assert(expected == conf.referenceFreqMHz)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tryConf(3200.0, 1600.0, 1000.0, 100.0)
|
trySuccessfulConf(Seq(3200.0, 1600.0, 1000.0, 100.0), 16000.0)
|
||||||
tryConf(3200.0, 1600.0)
|
trySuccessfulConf(Seq(3200.0, 1600.0), 3200.0)
|
||||||
tryConf(3200.0, 1066.7)
|
trySuccessfulConf(Seq(3200.0, 1066.7), 3200.0)
|
||||||
|
trySuccessfulConf(Seq(100, 50, 6.67), 100)
|
||||||
|
trySuccessfulConf(Seq(1, 2, 3, 5, 7, 11, 13).map(_ * 10.0), 1560.0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ class WithFireSimSimpleClocks extends Config((site, here, up) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val pllConfig = new SimplePllConfiguration("FireSim RationalClockBridge", clockGroupEdge.sink.members)
|
val pllConfig = new SimplePllConfiguration("FireSim RationalClockBridge", clockGroupEdge.sink.members)
|
||||||
|
pllConfig.emitSummaries
|
||||||
val rationalClockSpecs = for ((sinkP, division) <- pllConfig.sinkDividerMap) yield {
|
val rationalClockSpecs = for ((sinkP, division) <- pllConfig.sinkDividerMap) yield {
|
||||||
RationalClock(sinkP.name.get, 1, division)
|
RationalClock(sinkP.name.get, 1, division)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user