Use config to manage core registration and custom tests

This commit is contained in:
Zitao Fang
2020-06-13 01:36:14 -07:00
parent 98f6f9292e
commit 119a44b121
6 changed files with 111 additions and 81 deletions

View File

@@ -23,8 +23,7 @@ import sifive.blocks.devices.uart._
import sifive.blocks.devices.spi._
import chipyard.{BuildTop, BuildSystem}
import chipyard.TilesKey
import chipyard.TileSeq._
import chipyard.{GenericTilesKey, GenericTileConfig}
/**
* TODO: Why do we need this?
@@ -61,8 +60,8 @@ class WithSPIFlash(size: BigInt = 0x10000000) extends Config((site, here, up) =>
SPIFlashParams(rAddress = 0x10040000, fAddress = 0x20000000, fSize = size))
})
class WithL2TLBs(entries: Int) extends Config((site, here, up) => {
case TilesKey(tilesKey) => up(tilesKey) tileMap (tile => tile.copy(
class WithL2TLBs(entries: Int) extends GenericTileConfig((site, here, up) => {
case GenericTilesKey(key) => up(GenericTilesKey(key)) map (tile => tile.copy(
core = tile.core.copy(nL2TLBEntries = entries)
))
})
@@ -150,4 +149,4 @@ class WithTraceIO extends Config((site, here, up) => {
case BoomTilesKey => up(BoomTilesKey) map (tile => tile.copy(trace = true))
case ArianeTilesKey => up(ArianeTilesKey) map (tile => tile.copy(trace = true))
case TracePortKey => Some(TracePortParams())
})
})

View File

@@ -15,6 +15,21 @@ import freechips.rocketchip.tile._
import boom.common.{BoomTile, BoomTilesKey, BoomCrossingKey, BoomTileParams}
import ariane.{ArianeTile, ArianeTilesKey, ArianeCrossingKey, ArianeTileParams}
case object CoreEntryKey extends Field[Seq[CoreEntryBase]](Nil)
// If this key is encountered by a GenericTilesKey extractor, throw immediately
// Inside the body of GenericTileConfig, suppressed will be set to true to prevent the extractor from throwing
case class GenericTilesKeyChecker(suppressed: Boolean) extends Field[Int](0)
case class GenericTilesKeyImp(key: Field[Seq[TileParams]]) extends Field[Seq[GenericTileParams]](Nil)
object GenericTilesKey {
def apply(key: Field[Seq[TileParams]]) = GenericTilesKeyImp(key)
def unapply(key: Any): Option[Field[Seq[TileParams]]] = key match {
case GenericTilesKeyChecker(suppressed) if !suppressed => throw new Exception("GenericTilesKey must be in GenericTilesConfig")
case GenericTilesKeyImp(key) => Some(key)
case _ => None
}
}
// Base trait for all third-party core entries
sealed trait CoreEntryBase {
val name: String
@@ -45,6 +60,11 @@ class CoreEntry[TileParamsT <: TileParams with Product: TypeTag, TileT <: BaseTi
// Instantiate a tile and zip it with its parameter info, used by subsystem
def instantiateTile(crossingLookup: (Seq[RocketCrossingParams], Int) => Seq[RocketCrossingParams], logicalTreeNode: LogicalTreeNode)
(implicit p: Parameters, valName: ValName) = {
// Sanity check of GenericTilesKey outside of GenericTileConfig
// People would shoot themselves in the foot easily with this design, so a sanity check is necessary
// Simply trigger the exception by looking up the checker key
p(GenericTilesKeyChecker(false))
val tileParams = p(tilesKey)
val crossings = crossingLookup(p(crossingKey), tileParams.size)
(tileParams zip crossings) map {
@@ -63,12 +83,41 @@ class CoreEntry[TileParamsT <: TileParams with Product: TypeTag, TileT <: BaseTi
}
}
// Config fragment to register a core
class RegisterCore[TileParamsT <: TileParams with Product: TypeTag, TileT <: BaseTile : TypeTag](
name: String,
tilesKey: Field[Seq[TileParamsT]],
crossingKey: Field[Seq[RocketCrossingParams]]
) extends Config((site, here, up) => {
case CoreEntryKey => new CoreEntry[TileParamsT, TileT](name, tilesKey, crossingKey) +: up(CoreEntryKey)
})
// The config used along with GenericTilesKey.
// It change a lookup for registered tile parameter into a lookup with GenericTilesKey in the function body temporarily.
class GenericTileConfig(f: (View, View, View) => PartialFunction[Any, Any]) extends Config(
new Config((site, here, up) => {
case GenericTilesKeyChecker(_) => up(GenericTilesKeyChecker(true))
case key if CoreManager.keyMatch(up, key) => up(GenericTilesKey(key.asInstanceOf[Field[Seq[TileParams]]])) map (t => t.convert)
}) ++
new Config(f) ++
new Config((site, here, up) => {
case GenericTilesKeyChecker(_) => up(GenericTilesKeyChecker(false))
case GenericTilesKey(key) => up(key) map (t => new GenericTileParams(t))
})
)
// A list of all cores.
object CoreManager {
val cores: List[CoreEntryBase] = List(
// TODO ADD YOUR CORE DEFINITION HERE; note that the
// Built-in cores.
val base_cores: List[CoreEntryBase] = List(
new CoreEntry[RocketTileParams, RocketTile]("Rocket", RocketTilesKey, RocketCrossingKey),
new CoreEntry[BoomTileParams, BoomTile]("Boom", BoomTilesKey, BoomCrossingKey),
new CoreEntry[ArianeTileParams, ArianeTile]("Ariane", ArianeTilesKey, ArianeCrossingKey)
)
// Look up all cores that are registered in the current config view.
def cores(view: View): Seq[CoreEntryBase] = view(CoreEntryKey) ++ base_cores
// Check if the key is among the currently registered cores.
def keyMatch(view: View, key: Any) = (cores(view) filter (c => c.keyEqual(key))).size != 0
}

View File

@@ -15,6 +15,36 @@ import freechips.rocketchip.tile._
import boom.common.{BoomTile, BoomTilesKey, BoomCrossingKey, BoomTileParams}
import ariane.{ArianeTile, ArianeTilesKey, ArianeCrossingKey, ArianeTileParams}
// Trait for generic case class of base trait for copying
trait ConcreteBaseTrait[Base] {
this: Product =>
val _origin: Base
// Convert back to core-specific tile
def convert: Base = {
// Reflection Info of this class
val fieldNames = (this.getClass.getDeclaredFields map (f => f.getName)).init
// Reflection of target class
val paramClass = _origin.getClass
val paramNames = (paramClass.getDeclaredFields map (f => f.getName))
val paramCtor = paramClass.getConstructors.head
// Build a list of parameter in the original parameter class
val nameDict = paramNames.zipWithIndex.toMap
val indexList = fieldNames map (n => nameDict.get(n))
val fieldList = this.productIterator.toList map {
case c: ConcreteBaseTrait[_] => c.convert
case v => v
}
val fieldDict = ((indexList zip fieldList) collect { case (Some(i), v) => (i, v) }).toMap
val newValues = _origin.asInstanceOf[Product].productIterator.toList.zipWithIndex map
{ case (v, i) => (if (fieldDict contains i) fieldDict(i) else v).asInstanceOf[AnyRef] }
paramCtor.newInstance(newValues:_*).asInstanceOf[Base]
}
}
// Case class to change common parameters visible in the base traits. Some fields in the base traits may not be configurable as a
// case class constructor parameter for some cores, and those field will be ignored when applied.
case class GenericCoreParams(
@@ -50,7 +80,7 @@ case class GenericCoreParams(
val mtvecWritable: Boolean,
// The original object
val _origin: CoreParams
) extends CoreParams {
) extends CoreParams with ConcreteBaseTrait[CoreParams] {
def this(coreParams: CoreParams) = this(
bootFreqHz = coreParams.bootFreqHz,
useVM = coreParams.useVM,
@@ -86,27 +116,6 @@ case class GenericCoreParams(
_origin = coreParams
)
// Reflection Info of this class
val fieldNames = (this.getClass.getDeclaredFields map (f => f.getName)).init
// Convert back to core-specific tile
def convert: CoreParams = {
// Reflection of target class
val paramClass = _origin.getClass
val paramNames = (paramClass.getDeclaredFields map (f => f.getName))
val paramCtor = paramClass.getConstructors.head
// Build a list of parameter in the original parameter class
val nameDict = paramNames.zipWithIndex.toMap
val indexList = fieldNames map (n => nameDict.get(n))
val fieldList = this.productIterator.toList.init
val fieldDict = ((indexList zip fieldList) collect { case (Some(i), v) => (i, v) }).toMap
val newValues = _origin.asInstanceOf[Product].productIterator.toList.zipWithIndex map
{ case (v, i) => (if (fieldDict contains i) fieldDict(i) else v).asInstanceOf[AnyRef] }
paramCtor.newInstance(newValues:_*).asInstanceOf[CoreParams]
}
// Implement abstract function as placeholder
def lrscCycles: Int = _origin.lrscCycles
}
@@ -121,8 +130,8 @@ case class GenericTileParams(
val blockerCtrlAddr: Option[BigInt],
val name: Option[String],
// The original object
val _origin: TileParams
) extends TileParams {
val _origin: TileParams,
) extends TileParams with ConcreteBaseTrait[TileParams] {
// Copy constructor to build the params
def this(tileParams: TileParams) = this(
core = new GenericCoreParams(tileParams.core),
@@ -136,44 +145,4 @@ case class GenericTileParams(
_origin = tileParams
)
// Reflection Info of this class
val fieldNames = (this.getClass.getDeclaredFields map (f => f.getName)).init
// Convert back to core-specific tile
def convert: TileParams = {
// Reflection of target class
val paramClass = _origin.getClass
val paramNames = (paramClass.getDeclaredFields map (f => f.getName))
val paramCtor = paramClass.getConstructors.head
// Build a list of parameter in the original parameter class
val nameDict = paramNames.zipWithIndex.toMap
val indexList = fieldNames map (n => nameDict.get(n))
val fieldList = this.productIterator.toList.updated(0, core.convert).init
val fieldDict = ((indexList zip fieldList) collect { case (Some(i), v) => (i, v) }).toMap
val newValues = _origin.asInstanceOf[Product].productIterator.toList.zipWithIndex map
{ case (v, i) => (if (fieldDict contains i) fieldDict(i) else v).asInstanceOf[AnyRef] }
paramCtor.newInstance(newValues:_*).asInstanceOf[TileParams]
}
}
// Extractor to capture TilesKey
object TilesKey {
def unapply(key: Any): Option[Field[Seq[_]]] =
if ((CoreManager.cores filter (core => core.keyEqual(key))).size != 0) Some(key.asInstanceOf[Field[Seq[_]]]) else None
}
class TileSeq(list: Seq[Any]) {
def tileMap(f: GenericTileParams => GenericTileParams): Seq[TileParams] = {
// If this is not an unpacked tile key, simply throw a type exception
// Static type checking is not possible when this class is used with TilesKey extractor
val tileList = list.asInstanceOf[Seq[TileParams]]
tileList map (tileParams => f(new GenericTileParams(tileParams)).convert)
}
}
object TileSeq {
implicit def convertSeq(s: Seq[Any]) = new TileSeq(s)
}

View File

@@ -37,7 +37,7 @@ trait HasChipyardTiles extends HasTiles
// Note: the 0-arity function is used to delay the construction of tiles to make sure that they are created
// in order
val allTilesInfo: Seq[(TileParams, RocketCrossingParams, () => BaseTile)] =
(CoreManager.cores flatMap (core => core.instantiateTile(perTileOrGlobalSetting _, logicalTreeNode)))
(CoreManager.cores(p) flatMap (core => core.instantiateTile(perTileOrGlobalSetting _, logicalTreeNode)))
// Make a tile and wire its nodes into the system,
// according to the specified type of clock crossing.

View File

@@ -2,6 +2,7 @@ package chipyard
import scala.collection.mutable.{LinkedHashSet}
import freechips.rocketchip.config.{Parameters, Config, Field, View}
import freechips.rocketchip.subsystem.{RocketTilesKey}
import freechips.rocketchip.tile.{XLen, TileParams}
import freechips.rocketchip.config.{Parameters, Field}
@@ -102,3 +103,17 @@ class TestSuiteHelper
}
}
}
/**
* Config key of custom test suite.
*/
case object TestSuitesKey extends Field[(Seq[TileParams], TestSuiteHelper, Parameters) => Unit]((tiles, helper, p) => helper.addGenericTestSuites(tiles)(p))
/**
* Config fragment to add custom test suite factory function.
*
* @param suiteFactory Test suite factory function. It takes a list of TileParams to be instantiated and the test suite helper.
*/
class WithTestSuite(suiteFactory: (Seq[TileParams], TestSuiteHelper, Parameters) => Unit) extends Config((site, here, up) => {
case TestSuitesKey => suiteFactory
})

View File

@@ -19,6 +19,7 @@ import freechips.rocketchip.util.HasRocketChipStageUtils
import freechips.rocketchip.tile.XLen
import chipyard.{TestSuiteHelper, CoreManager}
import chipyard.TestSuitesKey
class AddDefaultTests extends Phase with PreservesAll[Phase] with HasRocketChipStageUtils {
// Make sure we run both after RocketChip's version of this phase, and Rocket Chip's annotation emission phase
@@ -33,15 +34,12 @@ class AddDefaultTests extends Phase with PreservesAll[Phase] with HasRocketChipS
val suiteHelper = new TestSuiteHelper
// Use Xlen as a proxy for detecting if we are a processor-like target
// The underlying test suites expect this field to be defined
if (p.lift(XLen).nonEmpty) {
val customizedSuite: Map[String, TestSuiteHelper => Unit] = Map(
// DEFINE CUSTOMIZED TEST HERE, using format ({Core name} -> _.{Test suite builder in TestSuiteHelper})
)
CoreManager.cores map (core => customizedSuite.get(core.name) match {
case Some(builder) => builder(suiteHelper)
case None => suiteHelper.addGenericTestSuites(core.tileParamsLookup)
})
}
if (p.lift(XLen).nonEmpty)
// If a custom test suite is set up, use the custom test suite
if (p.lift(TestSuitesKey).nonEmpty)
CoreManager.cores(p) map (core => p(TestSuitesKey).apply(core.tileParamsLookup, suiteHelper, p))
else
CoreManager.cores(p) map (core => suiteHelper.addGenericTestSuites(core.tileParamsLookup))
// if hwacha parameter exists then generate its tests
// TODO: find a more elegant way to do this. either through