diff --git a/generators/chipyard/src/main/scala/ConfigFragments.scala b/generators/chipyard/src/main/scala/ConfigFragments.scala index cad47f72..4d0e0725 100644 --- a/generators/chipyard/src/main/scala/ConfigFragments.scala +++ b/generators/chipyard/src/main/scala/ConfigFragments.scala @@ -59,14 +59,7 @@ 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 RocketTilesKey => up(RocketTilesKey) map (tile => tile.copy( - core = tile.core.copy(nL2TLBEntries = entries) - )) - case BoomTilesKey => up(BoomTilesKey) map (tile => tile.copy( - core = tile.core.copy(nL2TLBEntries = entries) - )) -}) +class WithL2TLBs(entries: Int) extends GenericCoreConfig(Map("core" -> Map("nL2TLBEntries" -> entries))) class WithTracegenSystem extends Config((site, here, up) => { case BuildSystem => (p: Parameters) => LazyModule(new tracegen.TraceGenSystem()(p)) @@ -148,7 +141,7 @@ class WithControlCore extends Config((site, here, up) => { }) class WithTraceIO extends GenericCoreConfig( - properties = Map("trace" -> true), + newValues = Map("trace" -> true), specialCase = (site, here, up) => { case TracePortKey => Some(TracePortParams()) } diff --git a/generators/chipyard/src/main/scala/CoreManager.scala b/generators/chipyard/src/main/scala/CoreManager.scala index 2756a42b..57b63743 100644 --- a/generators/chipyard/src/main/scala/CoreManager.scala +++ b/generators/chipyard/src/main/scala/CoreManager.scala @@ -14,10 +14,10 @@ import freechips.rocketchip.tile._ import boom.common.{BoomTile, BoomTilesKey, BoomCrossingKey, BoomTileParams} import ariane.{ArianeTile, ArianeTilesKey, ArianeCrossingKey, ArianeTileParams} -import chipsalliance.rocketchip.config.Parameters // Base trait for all third-party core entries sealed trait CoreEntryBase { + val name: String def tileParamsLookup(implicit p: Parameters): Seq[TileParams] def updateWithFilter(view: View, p: Any => Boolean): PartialFunction[Any, Map[String, Any] => Any] def instantiateTile(crossingLookup: (Seq[RocketCrossingParams], Int) => Seq[RocketCrossingParams], logicalTreeNode: LogicalTreeNode) @@ -26,36 +26,22 @@ sealed trait CoreEntryBase { // Implementation of third-party core entries class CoreEntry[TileParamsT <: TileParams with Product: TypeTag, TileT <: BaseTile : TypeTag]( + val name: String, tilesKey: Field[Seq[TileParamsT]], crossingKey: Field[Seq[RocketCrossingParams]] ) extends CoreEntryBase { - // Use reflection to get the parameter's constructor - private val mirror = runtimeMirror(getClass.getClassLoader) - private val paramClass = mirror.runtimeClass(typeOf[TileParamsT].typeSymbol.asClass) - private val paramNames = (paramClass.getDeclaredFields map (f => f.getName)).zipWithIndex.toMap - private val paramCtor = paramClass.getConstructors.head - // Use reflection to get the tile's constructor + private val mirror = runtimeMirror(getClass.getClassLoader) private val tileClass = mirror.runtimeClass(typeOf[TileT].typeSymbol.asClass) private val tileCtor = tileClass.getConstructors.filter(ctor => ctor.getParameterTypes()(4) == classOf[Parameters]).head - // Version of case class' copy() using reflection, where fields to be updated are passed by a map - def copyTileParam(tileParam: TileParamsT, properties: Map[String, Any]) = { - val values = tileParam.productIterator.toList - //val filteredProperties = properties filter { case (key, value) => paramNames contains key } - val indexedProperties = /*filteredProperties*/ properties map { case (key, value) => (paramNames(key), value) } - val newValues = (0 until values.size) map - (i => (if (indexedProperties contains i) indexedProperties(i) else values(i)).asInstanceOf[AnyRef]) - paramCtor.newInstance(newValues:_*) - } - // Tile parameter lookup using correct type def tileParamsLookup(implicit p: Parameters) = p(tilesKey) // If this core meet the requirement given by p, update parameter fields in the map def updateWithFilter(view: View, p: Any => Boolean): PartialFunction[Any, Map[String, Any] => Any] = { - case key if (key == tilesKey && p(tilesKey)) => properties => view(tilesKey) map - (tile => copyTileParam(tile, properties)) + case key if (key == tilesKey && p(tilesKey)) => newValues => view(tilesKey) map + (tile => CopyParam(tile, newValues)) } // Instantiate a tile and zip it with its parameter info, used by subsystem @@ -73,27 +59,12 @@ class CoreEntry[TileParamsT <: TileParams with Product: TypeTag, TileT <: BaseTi } } -// Generic Core Config - change properties in the given map -class GenericCoreConfig( - // Parameter properties to be changed and their new values. Any field not in a core's parameters will be ignored. - properties: Map[String, Any], - // Function for filtering the list of TilesKey. - filterFunc: Any => Boolean = (_ => true), - // Handling special cases where partial function input is not a TilesKey. - specialCase: (View, View, View) => PartialFunction[Any, Any] = ((_, _, _) => Map.empty) -) extends Config((site, here, up) => - scala.Function.unlift((key: Any) => { - val tiles = CoreManager.cores flatMap (core => core.updateWithFilter(up, filterFunc).lift(key)) - if (tiles.size == 0) None else Some(tiles map (tile => tile(properties))) - }).orElse(specialCase(site, here, up)) -) - // A list of all cores. object CoreManager { val cores: List[CoreEntryBase] = List( - // TODO ADD YOUR CORE DEFINITION HERE - new CoreEntry[RocketTileParams, RocketTile](RocketTilesKey, RocketCrossingKey), - new CoreEntry[BoomTileParams, BoomTile](BoomTilesKey, BoomCrossingKey), - new CoreEntry[ArianeTileParams, ArianeTile](ArianeTilesKey, ArianeCrossingKey) + // TODO ADD YOUR CORE DEFINITION HERE; note that the + new CoreEntry[RocketTileParams, RocketTile]("Rocket", RocketTilesKey, RocketCrossingKey), + new CoreEntry[BoomTileParams, BoomTile]("Boom", BoomTilesKey, BoomCrossingKey), + new CoreEntry[ArianeTileParams, ArianeTile]("Ariane", ArianeTilesKey, ArianeCrossingKey) ) } diff --git a/generators/chipyard/src/main/scala/GenericCoreConfig.scala b/generators/chipyard/src/main/scala/GenericCoreConfig.scala new file mode 100644 index 00000000..ac099742 --- /dev/null +++ b/generators/chipyard/src/main/scala/GenericCoreConfig.scala @@ -0,0 +1,133 @@ +package chipyard + +import scala.reflect.ClassTag +import scala.reflect.runtime.universe._ + +import chisel3._ + +import freechips.rocketchip.config.{Parameters, Config, Field, View} +import freechips.rocketchip.subsystem.{SystemBusKey, RocketTilesKey, RocketCrossingParams, RocketCrossingKey} +import freechips.rocketchip.diplomacy.{LazyModule, ClockCrossingType, ValName} +import freechips.rocketchip.diplomaticobjectmodel.logicaltree.LogicalTreeNode +import freechips.rocketchip.rocket._ +import freechips.rocketchip.tile._ + +import boom.common.{BoomTile, BoomTilesKey, BoomCrossingKey, BoomTileParams} +import ariane.{ArianeTile, ArianeTilesKey, ArianeCrossingKey, ArianeTileParams} + +// Extractor object accompanied class +// This is used to check the convertibility for those wrapped in Option, since Option's type is erased at runtime. +trait SubParameterBase { + def toProduct: Product + def cast(p: Any): Any +} +final class SubParameter[T <: Product](param: T) extends SubParameterBase { + def toProduct: Product = param + def cast(p: Any) = p.asInstanceOf[T] +} + +// Extractor object that help identify the parameter case classes. +// Add your customized nested parameter classes (or their commom base classes) here. +object CustomizedSubParameter { + def unapply(param: Product): Option[Product] = param match { + // ADD YOUR NESTED PARAMETER CLASS HERE, in the format shown below in SubParameter + case _ => None + } +} + +// Standard nested +object SubParameter { + def unapply(param: Product): Option[SubParameterBase] = param match { + case p: TileParams => Some(new SubParameter(p)) + case p: CoreParams => Some(new SubParameter(p)) + case p: ICacheParams => Some(new SubParameter(p)) + case p: DCacheParams => Some(new SubParameter(p)) + case p: MulDivParams => Some(new SubParameter(p)) + case p: FPUParams => Some(new SubParameter(p)) + case p: BTBParams => Some(new SubParameter(p)) + case p: BHTParams => Some(new SubParameter(p)) + case CustomizedSubParameter(p) => Some(new SubParameter(p)) + case _ => None + } +} + +// Dynamic update helper for Parameter class. +class CopyParam(paramExtracted: SubParameterBase) { + // Constructor for corresponding TileParams + private val param: Product = paramExtracted.toProduct + private val paramClass = param.getClass + private val paramNames = (paramClass.getDeclaredFields map (f => f.getName)) + private val paramCtor = paramClass.getConstructors.head + + // Function to build value entry + private def buildEntry(value: Any): Any = value match { + case Some(v) => Some(buildEntry(v)) + case SubParameter(p) => new CopyParam(p) + case v => v + } + + // Value of the case class + private val entries = param.productIterator.toList map (v => buildEntry(v)) + + // Update one value entry + private def updateEntry(entry: Any, newValue: Any): Any = entry match { + case Some(e) => newValue match { + case Some(v) => Some(updateEntry(e, v)) + case None => None + } + case e: CopyParam => newValue match { + case newValues: Map[String, Any] => e.update(newValues) + case v => paramExtracted.cast(v) + } + // Use cast() to check the type of the new value. Here I assume that all entries in the parameters class are simple values + // (like Int, BigInt and String), which are all final. This may breaks if a polymorphic type is added (unless it's a case + // class and registered above). + case e => e.getClass.cast(newValue) + } + + // Update the entire parameter object. + def update(newValues: Map[String, Any]): Any = { + val filteredValues = newValues.filter({ case (key, value) => paramNames contains key }) + val newValueList = entries.zipWithIndex map { + case (value, i) if newValues contains paramNames(i) => updateEntry(value, filteredValues(paramNames(i))).asInstanceOf[AnyRef] + case (value, i) => (value match { + case Some(v) => v match { + case copyParam: CopyParam => Some(copyParam.param) + case _ => Some(v) + } + case copyParam: CopyParam => copyParam.param + case _ => value + }).asInstanceOf[AnyRef] + } + paramCtor.newInstance(newValueList:_*) + } + + // For debug purpose - print what's in the object + override def toString(): String = paramClass.getSimpleName + "(" + entries.toString + ")" +} + +object CopyParam { + def apply(param: Product, newValues: Map[String, Any]): Any = param match { + case SubParameter(p) => new CopyParam(p).update(newValues) + case _ => throw new Exception("param is not a known Parameter type: add your custom parameter class to GenericCoreConfig.scala to fix it") + } +} + +// Change parameters for all registered cores in CoreManager. +class GenericCoreConfig ( + // Key-value pairs to be updated (keys are the name of fields). Any field not in a core's parameters will be ignored. + // If a field is a case class containing parameters (or an Option of that), you can use another Map containing the key-value pairs to + // update that case class. Using a new case class instance as the value is also acceptable. + // If a field is an Option, you should wrap your new values with Some() or set it to None. This also applies when a new case + // class instance is used for an Option field. + newValues: Map[String, Any], + // Function for filtering the list of TilesKey. + filterFunc: Any => Boolean = (_ => true), + // Handling special cases where partial function input is not a TilesKey. + specialCase: (View, View, View) => PartialFunction[Any, Any] = ((_, _, _) => Map.empty) +) extends Config((site, here, up) => + scala.Function.unlift((key: Any) => { + val tiles = CoreManager.cores flatMap (core => core.updateWithFilter(up, filterFunc).lift(key)) + if (tiles.size == 0) None else Some(tiles(0)(newValues)) + }).orElse(specialCase(site, here, up)) +) \ No newline at end of file diff --git a/generators/chipyard/src/main/scala/stage/phases/AddDefaultTests.scala b/generators/chipyard/src/main/scala/stage/phases/AddDefaultTests.scala index a36131b1..5855613e 100644 --- a/generators/chipyard/src/main/scala/stage/phases/AddDefaultTests.scala +++ b/generators/chipyard/src/main/scala/stage/phases/AddDefaultTests.scala @@ -33,8 +33,15 @@ 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) - CoreManager.cores map (core => suiteHelper.addGenericTestSuites(core.tileParamsLookup)) + 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 hwacha parameter exists then generate its tests // TODO: find a more elegant way to do this. either through