diff --git a/generators/chipyard/src/main/scala/ConfigFragments.scala b/generators/chipyard/src/main/scala/ConfigFragments.scala index da1372db..8336bdc5 100644 --- a/generators/chipyard/src/main/scala/ConfigFragments.scala +++ b/generators/chipyard/src/main/scala/ConfigFragments.scala @@ -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()) -}) \ No newline at end of file +}) diff --git a/generators/chipyard/src/main/scala/CoreManager.scala b/generators/chipyard/src/main/scala/CoreManager.scala index b5129217..d013cc7c 100644 --- a/generators/chipyard/src/main/scala/CoreManager.scala +++ b/generators/chipyard/src/main/scala/CoreManager.scala @@ -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 } diff --git a/generators/chipyard/src/main/scala/GenericCoreConfig.scala b/generators/chipyard/src/main/scala/GenericCoreParams.scala similarity index 69% rename from generators/chipyard/src/main/scala/GenericCoreConfig.scala rename to generators/chipyard/src/main/scala/GenericCoreParams.scala index 159b8b69..28db42b1 100644 --- a/generators/chipyard/src/main/scala/GenericCoreConfig.scala +++ b/generators/chipyard/src/main/scala/GenericCoreParams.scala @@ -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) } diff --git a/generators/chipyard/src/main/scala/Subsystem.scala b/generators/chipyard/src/main/scala/Subsystem.scala index fa5988e2..4ac92c4e 100644 --- a/generators/chipyard/src/main/scala/Subsystem.scala +++ b/generators/chipyard/src/main/scala/Subsystem.scala @@ -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. diff --git a/generators/chipyard/src/main/scala/TestSuites.scala b/generators/chipyard/src/main/scala/TestSuites.scala index 2261000f..5a929010 100644 --- a/generators/chipyard/src/main/scala/TestSuites.scala +++ b/generators/chipyard/src/main/scala/TestSuites.scala @@ -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 +}) diff --git a/generators/chipyard/src/main/scala/stage/phases/AddDefaultTests.scala b/generators/chipyard/src/main/scala/stage/phases/AddDefaultTests.scala index 5855613e..b170706e 100644 --- a/generators/chipyard/src/main/scala/stage/phases/AddDefaultTests.scala +++ b/generators/chipyard/src/main/scala/stage/phases/AddDefaultTests.scala @@ -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