diff options
| author | Angie | 2016-08-23 17:32:15 -0700 |
|---|---|---|
| committer | jackkoenig | 2016-09-06 00:17:18 -0700 |
| commit | b0252c1f56b92d4bde83cf73059a1c2566a8ba55 (patch) | |
| tree | 5794f87574b4d06ad95e741b90fee86ef8e2eed3 /src | |
| parent | 6bf15386079d862d042968f5d2ac30c9d092134c (diff) | |
Edited conf generation to handle mem namespace collision
* Also started separate pass for annotating valid memory
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/scala/firrtl/Driver.scala | 2 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/AnnotateMemMacros.scala | 4 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/AnnotateValidMemConfigs.scala | 200 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/ReplSeqMem.scala | 23 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/ReplaceMemMacros.scala | 20 |
5 files changed, 231 insertions, 18 deletions
diff --git a/src/main/scala/firrtl/Driver.scala b/src/main/scala/firrtl/Driver.scala index 79f2fdaf..4022a813 100644 --- a/src/main/scala/firrtl/Driver.scala +++ b/src/main/scala/firrtl/Driver.scala @@ -58,7 +58,7 @@ Optional Arguments: --inferRW <circuit> Enable readwrite port inference for the target circuit --inline <module>|<instance> Inline a module (e.g. "MyModule") or instance (e.g. "MyModule.myinstance") - --replSeqMem -c:<circuit>:-i<filename>:-o<filename> + --replSeqMem -c:<circuit>:-i:<filename>:-o:<filename> *** Replace sequential memories with blackboxes + configuration file *** Input configuration file optional *** Note: sub-arguments to --replSeqMem should be delimited by : and not white space! diff --git a/src/main/scala/firrtl/passes/AnnotateMemMacros.scala b/src/main/scala/firrtl/passes/AnnotateMemMacros.scala index 8d7c3f65..a59ca4af 100644 --- a/src/main/scala/firrtl/passes/AnnotateMemMacros.scala +++ b/src/main/scala/firrtl/passes/AnnotateMemMacros.scala @@ -9,7 +9,7 @@ import firrtl.Utils._ case class AppendableInfo(fields: Map[String,Any]) extends Info { def append(a: Map[String,Any]) = this.copy(fields = fields ++ a) - def append(a: Tuple2[String,Any]): AppendableInfo = append(Map(a)) + def append(a: (String,Any)): AppendableInfo = append(Map(a)) def get(f: String) = fields.get(f) override def equals(b: Any) = b match { case i: AppendableInfo => fields - "info" == i.fields - "info" @@ -89,7 +89,7 @@ object AnalysisUtils { case i: AppendableInfo => i.append(add) case _ => AppendableInfo(fields = add + ("info" -> info)) } - def appendInfo[T <: Info](info: T, add: Tuple2[String,Any]): AppendableInfo = appendInfo(info,Map(add)) + def appendInfo[T <: Info](info: T, add: (String,Any)): AppendableInfo = appendInfo(info,Map(add)) def getInfo[T <: Info](info: T, k: String) = info match{ case i: AppendableInfo => i.get(k) case _ => None diff --git a/src/main/scala/firrtl/passes/AnnotateValidMemConfigs.scala b/src/main/scala/firrtl/passes/AnnotateValidMemConfigs.scala new file mode 100644 index 00000000..1a03840a --- /dev/null +++ b/src/main/scala/firrtl/passes/AnnotateValidMemConfigs.scala @@ -0,0 +1,200 @@ +package firrtl.passes + +import firrtl.ir._ +import firrtl._ +import net.jcazevedo.moultingyaml._ +import net.jcazevedo.moultingyaml.DefaultYamlProtocol._ +import AnalysisUtils._ +import scala.collection.mutable.ArrayBuffer + +object CustomYAMLProtocol extends DefaultYamlProtocol { + // bottom depends on top + implicit val dr = yamlFormat4(DimensionRules) + implicit val md = yamlFormat2(MemDimension) + implicit val sr = yamlFormat4(SRAMRules) + implicit val sc = yamlFormat11(SRAMCompiler) +} + +case class DimensionRules( + min: Int, + // step size + inc: Int, + max: Int, + // these values should not be used, regardless of min,inc,max + illegal: Option[List[Int]] +){ + def getValid = { + val range = (min to max by inc).toList + range.filterNot(illegal.getOrElse(List[Int]()).toSet) + } +} + +case class MemDimension( + rules: Option[DimensionRules], + set: Option[List[Int]] +){ + require ( + if(rules == None) set != None else set == None, + "Should specify either rules or a list of valid options, but not both" + ) + def getValid = set.getOrElse(rules.get.getValid) +} + +case class SRAMConfig( + ymux: String, + ybank: String, + width: String, + depth: String +){ + def serialize(pattern: String): String = { + val fieldMap = getClass.getDeclaredFields.map{f => + f.setAccessible(true) + f.getName -> f.get(this) + }.toMap + + val fieldDelimiter = """\[.*?\]""".r + val configOptions = fieldDelimiter.findAllIn(pattern).toList + + configOptions.foldLeft(pattern)((b,a) => { + // Expects the contents of [] are valid configuration fields (otherwise key match error) + val fieldVal = { + try fieldMap(a.substring(1,a.length-1)) + catch { case e: Exception => Error("**SRAM config field incorrect**") } + } + b.replace(a,fieldVal.toString) + }) + } +} + +// Ex: https://www.ece.cmu.edu/~ece548/hw/hw5/meml80.pdf +case class SRAMRules( + // column mux parameter (for adjusting aspect ratio) + ymux: (Int,String), + // vertical segmentation (banking -- tradeoff performance / area) + ybank: (Int,String), + width: MemDimension, + depth: MemDimension +){ + def getValidWidths = width.getValid + def getValidDepths = depth.getValid + def getValidConfig(m: DefMemory): Option[SRAMConfig] = { + val width = bitWidth(m.dataType) + val depth = m.depth + if (getValidWidths.contains(width) && getValidDepths.contains(depth)) + Some(SRAMConfig(ymux = ymux._2, ybank = ybank._2, width = width.toString, depth = depth.toString)) + else + None + } + +} + +// vendor-specific compilers +case class SRAMCompiler( + vendor: String, + node: String, + // i.e. RF, SRAM, etc. + memType: String, + portType: String, + useWmask: Boolean, + // area of individual bitcells (um2) to determine periphery overhead + bitCellArea: Option[Double], + // rules for valid SRAM flavors + rules: Seq[SRAMRules], + // path to executable + path: Option[String], + // (output) config file path + configFile: Option[String], + // config pattern + configPattern: Option[String], + // read documentation for details + defaultArgs: Option[String] +){ + require(portType == "RW" || portType == "R,W", "Memory must be single port RW or dual port R,W") + require( + (configFile != None && configPattern != None) || configFile == None, + "Config pattern must be provided with config file" + ) + def ymuxVals = rules.map(_.ymux._1).sortWith(_ < _) + def ybankVals = rules.map(_.ybank._1).sortWith(_ > _) + // optimize search for better FoM (area,power,clk); ymux has more effect + def defaultSearchOrdering = for (x <- ymuxVals; y <- ybankVals) yield { + rules.find(r => r.ymux._1 == x && r.ybank._1 == y).get + } + + private val configOutputBuffer = new java.io.CharArrayWriter + + def append(m: DefMemory) : Option[SRAMConfig] = { + val validCombos = ArrayBuffer[SRAMConfig]() + defaultSearchOrdering foreach { r => + val config = r.getValidConfig(m) + if (config != None) validCombos += config.get + } + // non empty if successfully found compiler option that supports depth/width + if (validCombos.nonEmpty){ + if (configPattern != None) + configOutputBuffer.append(validCombos.head.serialize(configPattern.get)) + Some(validCombos.head) + } + else None + } + + // # of mems with given width, depth to make up the memory you want + private case class MemInfo(num: Int, width: Int, depth: Int) + + // split memory until width, depth achievable via given memory compiler + private def getInRange(m: MemInfo): Seq[MemInfo] = { + val validXRange = ArrayBuffer[SRAMRules]() + val validYRange = ArrayBuffer[SRAMRules]() + defaultSearchOrdering foreach { r => + if (m.width < r.getValidWidths.max) validXRange += r + if (m.depth < r.getValidDepths.max) validYRange += r + } + if (validXRange.isEmpty && validYRange.isEmpty) + getInRange(MemInfo(4*m.num,m.width/2,m.depth/2)) + else if (validXRange.isEmpty && validYRange.nonEmpty) + getInRange(MemInfo(2*m.num,m.width/2,m.depth)) + else if (validXRange.nonEmpty && validYRange.isEmpty) + getInRange(MemInfo(2*m.num,m.width,m.depth/2)) + else if (validXRange.union(validYRange).nonEmpty) + Seq(m) + else + getInRange(MemInfo(2*m.num,m.width,m.depth/2)) ++ getInRange(MemInfo(2*m.num,m.width/2,m.depth)) + } + +} + +class YamlFileReader(file: String){ + import CustomYAMLProtocol._ + def parse: Seq[YamlValue] = { + val yamlString = scala.io.Source.fromFile(file).getLines.mkString("\n") + yamlString.parseYamls + } +} + +class AnnotateValidMemConfigs(reader: Option[YamlFileReader]) extends Pass { + + def name = "Annotate memories with valid split depths, widths, #\'s" + + def run(c: Circuit) = { + + def annotateModMems(m: Module) = { + + def updateStmts(s: Statement): Statement = s match { + + case m: DefMemory if containsInfo(m.info,"useMacro") => m + case b: Block => Block(b.stmts map updateStmts) + case s => s + + } + m.copy(body=updateStmts(m.body)) + } + + val updatedMods = c.modules map { + case m: Module => annotateModMems(m) + case m: ExtModule => m + } + c.copy(modules = updatedMods) + + } + +}
\ No newline at end of file diff --git a/src/main/scala/firrtl/passes/ReplSeqMem.scala b/src/main/scala/firrtl/passes/ReplSeqMem.scala index 72b69f3b..29274e54 100644 --- a/src/main/scala/firrtl/passes/ReplSeqMem.scala +++ b/src/main/scala/firrtl/passes/ReplSeqMem.scala @@ -12,6 +12,10 @@ case object InputConfigFileName extends PassOption case object OutputConfigFileName extends PassOption case object PassCircuitName extends PassOption +object Error { + def apply[T <: Any](msg: String) = throw new Exception(msg) +} + object PassConfigUtil { def getPassOptions(t: String, usage: String = "") = { @@ -31,7 +35,7 @@ object PassConfigUtil { case "-c" :: value :: tail => nextPassOption(map + (PassCircuitName -> value), tail) case option :: tail => - throw new Exception("Unknown option " + option + usage) + Error("Unknown option " + option + usage) } } nextPassOption(Map[PassOption, String](), passArgList) @@ -50,7 +54,7 @@ class ConfWriter(filename: String) { val ports = (writers ++ readers ++ readwriters).mkString(",") val maskGranConf = if (maskGran == None) "" else s"mask_gran ${maskGran.get}" val width = bitWidth(m.dataType) - val conf = s"name ${m.name}_ext depth ${m.depth} width ${width} ports ${ports} ${maskGranConf} \n" + val conf = s"name ${m.name} depth ${m.depth} width ${width} ports ${ports} ${maskGranConf} \n" outputBuffer.append(conf) } def serialize = { @@ -68,7 +72,7 @@ case class ReplSeqMemAnnotation(t: String, tID: TransID) Pass to replace sequential memories with blackboxes + configuration file Usage: - --replSeqMem -c:<circuit>:-i<filename>:-o<filename> + --replSeqMem -c:<circuit>:-i:<filename>:-o:<filename> *** Note: sub-arguments to --replSeqMem should be delimited by : and not white space! Required Arguments: @@ -82,11 +86,11 @@ Optional Arguments: val passOptions = PassConfigUtil.getPassOptions(t,usage) val outputConfig = passOptions.getOrElse( OutputConfigFileName, - throw new Exception("No output config file provided for ReplSeqMem!" + usage) + Error("No output config file provided for ReplSeqMem!" + usage) ) val passCircuit = passOptions.getOrElse( PassCircuitName, - throw new Exception("No circuit name specified for ReplSeqMem!" + usage) + Error("No circuit name specified for ReplSeqMem!" + usage) ) val target = CircuitName(passCircuit) def duplicate(n: Named) = this.copy(t=t.replace("-c:"+passCircuit,"-c:"+n.name)) @@ -98,6 +102,14 @@ class ReplSeqMem(transID: TransID) extends Transform with LazyLogging { map get transID match { case Some(p) => p get CircuitName(circuit.main) match { case Some(ReplSeqMemAnnotation(t, _)) => { + + val inputFileName = PassConfigUtil.getPassOptions(t).getOrElse(InputConfigFileName,"") + val inConfigFile = { + if (inputFileName.isEmpty) None + else if (new java.io.File(inputFileName).exists) Some(new YamlFileReader(inputFileName)) + else Error("Input configuration file does not exist!") + } + val outConfigFile = new ConfWriter(PassConfigUtil.getPassOptions(t).get(OutputConfigFileName).get) TransformResult( ( @@ -105,6 +117,7 @@ class ReplSeqMem(transID: TransID) extends Transform with LazyLogging { Legalize, AnnotateMemMacros, UpdateDuplicateMemMacros, + new AnnotateValidMemConfigs(inConfigFile), new ReplaceMemMacros(outConfigFile), RemoveEmpty, CheckInitialization, diff --git a/src/main/scala/firrtl/passes/ReplaceMemMacros.scala b/src/main/scala/firrtl/passes/ReplaceMemMacros.scala index 3c3f8e93..8144477c 100644 --- a/src/main/scala/firrtl/passes/ReplaceMemMacros.scala +++ b/src/main/scala/firrtl/passes/ReplaceMemMacros.scala @@ -33,11 +33,12 @@ class ReplaceMemMacros(writer: ConfWriter) extends Pass { // prototype mem if (ref == None) { - val newName = moduleNamespace.newName(m.name) - val newMem = m.copy(name = newName) - memMods ++= createMemModule(newMem) + val newWrapperName = moduleNamespace.newName(m.name) + val newMemBBName = moduleNamespace.newName(m.name + "_ext") + val newMem = m.copy(name = newMemBBName) + memMods ++= createMemModule(newMem,newWrapperName) uniqueMems += newMem - WDefInstance(info, m.name, newMem.name, UnknownType) + WDefInstance(info, m.name, newWrapperName, UnknownType) } else { val r = ref.get match {case s: String => s} @@ -63,17 +64,16 @@ class ReplaceMemMacros(writer: ConfWriter) extends Pass { } // from Albert - def createMemModule(m: DefMemory): Seq[DefModule] = { + def createMemModule(m: DefMemory, wrapperName: String): Seq[DefModule] = { assert(m.dataType != UnknownType) - val bbName = m.name + "_ext" val stmts = ArrayBuffer[Statement]() val wrapperioPorts = MemPortUtils.memToBundle(m).fields.map(f => Port(NoInfo, f.name, Input, f.tpe)) val bbProto = m.copy(dataType = flattenType(m.dataType)) //val bbioPorts = MemPortUtils.memToBundle(bbProto).fields.map(f => Port(NoInfo, f.name, Input, f.tpe)) val bbioPorts = MemPortUtils.memToFlattenBundle(m).fields.map(f => Port(NoInfo, f.name, Input, f.tpe)) - stmts += WDefInstance(NoInfo,bbName,bbName,UnknownType) - val bbRef = createRef(bbName) + stmts += WDefInstance(NoInfo,m.name,m.name,UnknownType) + val bbRef = createRef(m.name) stmts ++= (m.readers zip bbProto.readers).map{ case (x,y) => adaptReader(createRef(x),m,createSubField(bbRef,y),bbProto) }.flatten @@ -83,8 +83,8 @@ class ReplaceMemMacros(writer: ConfWriter) extends Pass { stmts ++= (m.readwriters zip bbProto.readwriters).map{ case (x,y) => adaptReadWriter(createRef(x),m,createSubField(bbRef,y),bbProto) }.flatten - val wrapper = Module(NoInfo,m.name,wrapperioPorts,Block(stmts)) - val bb = ExtModule(NoInfo,bbName,bbioPorts) + val wrapper = Module(NoInfo,wrapperName,wrapperioPorts,Block(stmts)) + val bb = ExtModule(NoInfo,m.name,bbioPorts) // TODO: Annotate? -- use actual annotation map // add to conf file |
