Update FireSim to support harness clocks | Small config renaming

This commit is contained in:
Abraham Gonzalez
2021-03-08 22:03:07 +00:00
parent 562d8e5116
commit 6ab8f8f8fc
6 changed files with 149 additions and 48 deletions

View File

@@ -2,6 +2,8 @@
package firesim.firesim
import scala.collection.mutable.{HashMap}
import chisel3._
import chisel3.experimental.{IO}
@@ -38,44 +40,104 @@ object NodeIdx {
/**
* Under FireSim's current multiclock implementation there can be only a
* single clock bridge. This requires, therefore, that it be instantiated in
* the harness and reused across all supernode instances. This class attempts to
* the harness and reused across all supernode instances. This class attempts to
* memoize its instantiation such that it can be referenced from within a ClockScheme function.
*/
class ClockBridgeInstantiator {
private var _clockRecord: Option[RecordMap[Clock]] = None
private var _harnessClockMap: HashMap[String, (Double, Clock)] = HashMap.empty
def getClockRecord: RecordMap[Clock] = _clockRecord.get
// Assumes that the supernode implementation results in duplicated clocks
// (i.e. only 1 set of clocks is generated for all BuildTop designs)
private var _ratClockMap: HashMap[String, (RationalClock, Clock)] = HashMap.empty
private var _ratRefName: Option[String] = None
def getClockRecordOrInstantiate(allClocks: Seq[RationalClock], baseClockName: String): RecordMap[Clock] = {
if (_clockRecord.isEmpty) {
require(allClocks.exists(_.name == baseClockName),
s"Provided base-clock name, ${baseClockName}, does not match a defined clock. Available clocks:\n " +
allClocks.map(_.name).mkString("\n "))
/**
* Request a clock at a particular frequency
*
* @param name An identifier for the associated clock domain
*
* @param freqRequested Freq. for the domain in Hz
*/
def getClock(name: String, freqRequested: Double): Clock = {
val clkWire = Wire(new Clock)
_harnessClockMap(name) = (freqRequested, clkWire)
clkWire
}
val baseClock = allClocks.find(_.name == baseClockName).get
val simplified = allClocks.map { c =>
c.copy(multiplier = c.multiplier * baseClock.divisor, divisor = c.divisor * baseClock.multiplier)
.simplify
}
/**
* Get a RecordMap of clocks for a set of input RationalClocks
*
* @param allClocks Seq. of RationalClocks that want a clock
*
* @param baseClockName Name of domain that the allClocks is rational to
*/
def getClockRecordMap(allClocks: Seq[RationalClock], baseClockName: String): RecordMap[Clock] = {
val ratClockRecordMapWire = Wire(RecordMap(allClocks.map { c => (c.name, Clock()) }:_*))
/**
* Removes clocks that have the same frequency before instantiating the
* clock bridge to avoid unnecessary BUFGCE use.
*/
val distinct = simplified.foldLeft(Seq(RationalClock(baseClockName, 1, 1))) { case (list, candidate) =>
if (list.exists { clock => clock.equalFrequency(candidate) }) list else list :+ candidate
}
val clockBridge = Module(new RationalClockBridge(distinct))
val cbVecTuples = distinct.zip(clockBridge.io.clocks)
val outputWire = Wire(RecordMap(simplified.map { c => (c.name, Clock()) }:_*))
for (parameter <- simplified) {
val (_, cbClockField) = cbVecTuples.find(_._1.equalFrequency(parameter)).get
outputWire(parameter.name).get := cbClockField
}
_clockRecord = Some(outputWire)
_ratRefName = Some(baseClockName)
for (clock <- allClocks) {
val clkWire = Wire(new Clock)
_ratClockMap(clock.name) = (clock, clkWire)
ratClockRecordMapWire(clock.name).get := clkWire
}
ratClockRecordMapWire
}
/**
* Connect all clocks requested to ClockBridge
*/
def instantiateFireSimDividerPLL: Unit = {
// Simplify the RationalClocks ratio's
val refRatClock = _ratClockMap.find(_._1 == _ratRefName.get).get._2._1
val simpleRatClocks = _ratClockMap.map { t =>
val ratClock = t._2._1
ratClock.copy(
multiplier = ratClock.multiplier * refRatClock.divisor,
divisor = ratClock.divisor * refRatClock.multiplier).simplify
}
// Determine all the clock dividers (harness + rational clocks)
// Note: Requires that the BuildTop reference frequency is requested with proper freq.
val refRatClockFreq = _harnessClockMap.find(_._1 == _ratRefName.get).get._2._1
val refRatSinkParams = ClockSinkParameters(take=Some(ClockParameters(freqMHz=refRatClockFreq / (1000 * 1000))),name=Some(_ratRefName.get))
val harSinkParams = _harnessClockMap.map { case (name, (freq, bundle)) =>
ClockSinkParameters(take=Some(ClockParameters(freqMHz=freq / (1000 * 1000))),name=Some(name))
}.toSeq
val allSinkParams = harSinkParams :+ refRatSinkParams
// Use PLL config to determine overall div's
val pllConfig = new SimplePllConfiguration("firesimOverallClockBridge", allSinkParams)
pllConfig.emitSummaries
// Adjust all BuildTop RationalClocks with the div determined by the PLL
val refRatDiv = pllConfig.sinkDividerMap(refRatSinkParams)
val adjRefRatClocks = simpleRatClocks.map { clock =>
clock.copy(divisor = clock.divisor * refRatDiv).simplify
}
// Convert harness clocks to RationalClocks
val harRatClocks = harSinkParams.map { case ClockSinkParameters(_, _, _, _, clkParamsOpt, nameOpt) =>
RationalClock(nameOpt.get, 1, pllConfig.referenceFreqMHz.toInt / clkParamsOpt.get.freqMHz.toInt)
}
val allAdjRatClks = adjRefRatClocks ++ harRatClocks
// Removes clocks that have the same frequency before instantiating the
// clock bridge to avoid unnecessary BUFGCE use.
val allDistinctRatClocks = allAdjRatClks.foldLeft(Seq(RationalClock(pllConfig.referenceSinkParams.name.get, 1, 1))) {
case (list, candidate) => if (list.exists { clock => clock.equalFrequency(candidate) }) list else list :+ candidate
}
val clockBridge = Module(new RationalClockBridge(allDistinctRatClocks))
val cbVecTuples = allDistinctRatClocks.zip(clockBridge.io.clocks)
// Connect all clocks (harness + BuildTop clocks)
for (clock <- allAdjRatClks) {
val (_, cbClockField) = cbVecTuples.find(_._1.equalFrequency(clock)).get
_ratClockMap.get(clock.name).map { case (_, clk) => clk := cbClockField }
_harnessClockMap.get(clock.name).map { case (_, clk) => clk := cbClockField }
}
getClockRecord
}
}
@@ -117,16 +179,19 @@ class WithFireSimSimpleClocks extends Config((site, here, up) => {
clockBundle.reset := reset
}
val pllConfig = new SimplePllConfiguration("FireSim RationalClockBridge", clockGroupEdge.sink.members)
val pllConfig = new SimplePllConfiguration("firesimBuildTopClockGenerator", clockGroupEdge.sink.members)
pllConfig.emitSummaries
val rationalClockSpecs = for ((sinkP, division) <- pllConfig.sinkDividerMap) yield {
RationalClock(sinkP.name.get, 1, division)
}
// Set the reference frequency used
chiptop.refClockFreqMHz = Some(pllConfig.referenceFreqMHz)
chiptop.harnessFunctions += ((th: HasHarnessSignalReferences) => {
reset := th.harnessReset
input_clocks := p(ClockBridgeInstantiatorKey)
.getClockRecordOrInstantiate(rationalClockSpecs.toSeq, p(FireSimBaseClockNameKey))
.getClockRecordMap(rationalClockSpecs.toSeq, p(FireSimBaseClockNameKey))
Nil })
}
}
@@ -140,6 +205,8 @@ class FireSim(implicit val p: Parameters) extends RawModule with HasHarnessSigna
def dutReset = { require(false, "dutReset should not be used in Firesim"); false.B }
def success = { require(false, "success should not be used in Firesim"); false.B }
var btFreqMHz: Option[Double] = None
// Instantiate multiple instances of the DUT to implement supernode
for (i <- 0 until p(NumNodes)) {
// It's not a RC bump without some hacks...
@@ -150,7 +217,15 @@ class FireSim(implicit val p: Parameters) extends RawModule with HasHarnessSigna
val lazyModule = LazyModule(p(BuildTop)(p.alterPartial({
case AsyncClockGroupsKey => p(AsyncClockGroupsKey).copy
})))
val module = Module(lazyModule.module)
withClockAndReset(harnessClock, harnessReset) {
val module = Module(lazyModule.module)
}
btFreqMHz = Some(lazyModule match {
case d: HasReferenceClockFreq => d.refClockFreqMHz.getOrElse(p(DefaultClockFrequencyKey))
case _ => p(DefaultClockFrequencyKey)
})
lazyModule match { case d: HasTestHarnessFunctions =>
require(d.harnessFunctions.size == 1, "There should only be 1 harness function to connect clock+reset")
d.harnessFunctions.foreach(_(this))
@@ -160,5 +235,8 @@ class FireSim(implicit val p: Parameters) extends RawModule with HasHarnessSigna
}
NodeIdx.increment()
}
harnessClock := p(ClockBridgeInstantiatorKey).getClockRecord("implicit_clock").get
harnessClock := p(ClockBridgeInstantiatorKey).getClock(p(FireSimBaseClockNameKey), btFreqMHz.get * (1000 * 1000))
p(ClockBridgeInstantiatorKey).instantiateFireSimDividerPLL
}