diff options
| author | Angie | 2016-08-24 19:12:38 -0700 |
|---|---|---|
| committer | jackkoenig | 2016-09-06 00:17:18 -0700 |
| commit | 355cf277f6f7999b27611ada052fcf3005b015d9 (patch) | |
| tree | f3e4d3c7219680d8b79687b98fae7d06c6b6f50e /src | |
| parent | b0252c1f56b92d4bde83cf73059a1c2566a8ba55 (diff) | |
Expanded annotations for valid memory sizes
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/scala/firrtl/passes/AnnotateValidMemConfigs.scala | 177 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/ReplSeqMem.scala | 8 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/ReplSeqMemTests.scala | 2 |
3 files changed, 141 insertions, 46 deletions
diff --git a/src/main/scala/firrtl/passes/AnnotateValidMemConfigs.scala b/src/main/scala/firrtl/passes/AnnotateValidMemConfigs.scala index 1a03840a..a84eb7e1 100644 --- a/src/main/scala/firrtl/passes/AnnotateValidMemConfigs.scala +++ b/src/main/scala/firrtl/passes/AnnotateValidMemConfigs.scala @@ -12,7 +12,8 @@ object CustomYAMLProtocol extends DefaultYamlProtocol { implicit val dr = yamlFormat4(DimensionRules) implicit val md = yamlFormat2(MemDimension) implicit val sr = yamlFormat4(SRAMRules) - implicit val sc = yamlFormat11(SRAMCompiler) + implicit val wm = yamlFormat2(WMaskArg) + implicit val sc = yamlFormat10(SRAMCompiler) } case class DimensionRules( @@ -37,15 +38,19 @@ case class MemDimension( 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) + def getValid = set.getOrElse(rules.get.getValid).sorted } case class SRAMConfig( - ymux: String, - ybank: String, - width: String, - depth: String + ymux: String = "", + ybank: String = "", + width: Int, + depth: Int, + xsplit: Int = 1, + ysplit: Int = 1 ){ + // how many duplicate copies of this SRAM are needed + def num = xsplit * ysplit def serialize(pattern: String): String = { val fieldMap = getClass.getDeclaredFields.map{f => f.setAccessible(true) @@ -77,17 +82,20 @@ case class SRAMRules( ){ def getValidWidths = width.getValid def getValidDepths = depth.getValid - def getValidConfig(m: DefMemory): Option[SRAMConfig] = { - val width = bitWidth(m.dataType) - val depth = m.depth + def getValidConfig(width: Int, depth: Int): Option[SRAMConfig] = { if (getValidWidths.contains(width) && getValidDepths.contains(depth)) - Some(SRAMConfig(ymux = ymux._2, ybank = ybank._2, width = width.toString, depth = depth.toString)) + Some(SRAMConfig(ymux = ymux._2, ybank = ybank._2, width = width, depth = depth)) else None } - + def getValidConfig(m: DefMemory): Option[SRAMConfig] = getValidConfig(bitWidth(m.dataType).intValue,m.depth) } +case class WMaskArg( + t: String, + f: String +) + // vendor-specific compilers case class SRAMCompiler( vendor: String, @@ -95,9 +103,7 @@ case class SRAMCompiler( // i.e. RF, SRAM, etc. memType: String, portType: String, - useWmask: Boolean, - // area of individual bitcells (um2) to determine periphery overhead - bitCellArea: Option[Double], + wMaskArg: Option[WMaskArg], // rules for valid SRAM flavors rules: Seq[SRAMRules], // path to executable @@ -111,90 +117,173 @@ case class SRAMCompiler( ){ require(portType == "RW" || portType == "R,W", "Memory must be single port RW or dual port R,W") require( - (configFile != None && configPattern != None) || configFile == None, + (configFile != None && configPattern != None && wMaskArg != 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(_ > _) + // TODO: verify this default ordering works out // 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 + private val maskConfigOutputBuffer = new java.io.CharArrayWriter + private val noMaskConfigOutputBuffer = new java.io.CharArrayWriter - def append(m: DefMemory) : Option[SRAMConfig] = { + def append(m: DefMemory) : DefMemory = { 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) + // TODO: don't just take first option + val usedConfig = { + if (validCombos.nonEmpty) validCombos.head + else getBestAlternative(m) + } + if (configPattern != None) { + val newConfig = usedConfig.serialize(configPattern.get) + "\n" + val currentBuff = { + if (containsInfo(m.info,"maskGran")) maskConfigOutputBuffer + else noMaskConfigOutputBuffer + } + if (!currentBuff.toString.contains(newConfig)) + currentBuff.append(newConfig) } - else None + m.copy(info = appendInfo(m.info,"sramConfig" -> usedConfig)) } - // # of mems with given width, depth to make up the memory you want - private case class MemInfo(num: Int, width: Int, depth: Int) - + // TODO: Should you really be splitting in 2 if, say, depth is 1 more than allowed? should be thresholded and + // handled w/ a separate set of registers ? // split memory until width, depth achievable via given memory compiler - private def getInRange(m: MemInfo): Seq[MemInfo] = { + private def getInRange(m: SRAMConfig): Seq[SRAMConfig] = { 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 (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)) + getInRange(SRAMConfig(xsplit = 2*m.xsplit, ysplit = 2*m.ysplit, width = m.width/2,depth = m.depth/2)) else if (validXRange.isEmpty && validYRange.nonEmpty) - getInRange(MemInfo(2*m.num,m.width/2,m.depth)) + getInRange(SRAMConfig(xsplit = 2*m.xsplit, ysplit = m.ysplit, width = m.width/2,depth = m.depth)) else if (validXRange.nonEmpty && validYRange.isEmpty) - getInRange(MemInfo(2*m.num,m.width,m.depth/2)) - else if (validXRange.union(validYRange).nonEmpty) + getInRange(SRAMConfig(xsplit = m.xsplit, ysplit = 2*m.ysplit, width = m.width,depth = m.depth/2)) + else if (validXRange.intersect(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)) + getInRange(SRAMConfig(xsplit = m.xsplit, ysplit = 2*m.ysplit, width = m.width,depth = m.depth/2)) ++ + getInRange(SRAMConfig(xsplit = 2*m.xsplit, ysplit = m.ysplit, width = m.width/2,depth = m.depth)) + } + + private def getBestAlternative(m: DefMemory): SRAMConfig = { + val validConfigs = getInRange(SRAMConfig(width = bitWidth(m.dataType).intValue, depth = m.depth)) + val minNum = validConfigs.map(x => x.num).min + val validMinConfigs = validConfigs.filter(_.num == minNum) + val validMinConfigsSquareness = validMinConfigs.map(x => math.abs(x.width.toDouble/x.depth - 1) -> x).toMap + val squarestAspectRatio = validMinConfigsSquareness.map(x => x._1).min + val validConfig = validMinConfigsSquareness(squarestAspectRatio) + val validRules = ArrayBuffer[SRAMRules]() + defaultSearchOrdering foreach { r => + if (validConfig.width <= r.getValidWidths.max && validConfig.depth <= r.getValidDepths.max) validRules += r + } + // TODO: don't just take first option + val bestRule = validRules.head + val memWidth = bestRule.getValidWidths.find(validConfig.width <= _).get + val memDepth = bestRule.getValidDepths.find(validConfig.depth <= _).get + bestRule.getValidConfig(width = memWidth, depth = memDepth).get.copy(xsplit = validConfig.xsplit, ysplit = validConfig.ysplit) + } + + def serialize() = { + // TODO } } +// TODO: assumption that you would stick to just SRAMs or just RFs in a design -- is that true? +// Or is this where module-level transforms (rather than circuit-level) make sense? class YamlFileReader(file: String){ import CustomYAMLProtocol._ - def parse: Seq[YamlValue] = { - val yamlString = scala.io.Source.fromFile(file).getLines.mkString("\n") - yamlString.parseYamls + def parse[A](implicit reader: YamlReader[A]) : Seq[A] = { + if (new java.io.File(file).exists) { + val yamlString = scala.io.Source.fromFile(file).getLines.mkString("\n") + val optionOut = yamlString.parseYamls.map(x => + try Some(reader.read(x)) + catch {case e: Exception => None} + ) + optionOut.filter(_ != None).map(_.get) + } + else Error("Yaml file doesn't exist!") + } +} + +class YamlFileWriter(file: String) { + import CustomYAMLProtocol._ + val outputBuffer = new java.io.CharArrayWriter + val separator = "--- \n" + def append(in: YamlValue) = { + outputBuffer.append(separator + in.prettyPrint) + } + def serialize = { + val outputFile = new java.io.PrintWriter(file) + outputFile.write(outputBuffer.toString) + outputFile.close() } } class AnnotateValidMemConfigs(reader: Option[YamlFileReader]) extends Pass { + import CustomYAMLProtocol._ + def name = "Annotate memories with valid split depths, widths, #\'s" - def run(c: Circuit) = { + // TODO: Consider splitting InferRW to analysis + actual optimization pass, in case sp doesn't exist + // TODO: Don't get first available? + case class SRAMCompilerSet( + sp: Option[SRAMCompiler] = None, + dp: Option[SRAMCompiler] = None + ){ + def serialize() = { + if (sp != None) sp.get.serialize() + if (dp != None) dp.get.serialize() + } + } + val sramCompilers = { + if (reader == None) None + else { + val compilers = reader.get.parse[SRAMCompiler] + val sp = compilers.find(_.portType == "RW") + val dp = compilers.find(_.portType == "R,W") + Some(SRAMCompilerSet(sp = sp, dp = dp)) + } + } + 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 m: DefMemory if containsInfo(m.info,"useMacro") => { + if (sramCompilers == None) m + else { + if (m.readwriters.length == 1) + if (sramCompilers.get.sp == None) Error("Design needs RW port memory compiler!") + else sramCompilers.get.sp.get.append(m) + else + if (sramCompilers.get.dp == None) Error("Design needs R,W port memory compiler!") + else sramCompilers.get.dp.get.append(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 29274e54..a1b83efc 100644 --- a/src/main/scala/firrtl/passes/ReplSeqMem.scala +++ b/src/main/scala/firrtl/passes/ReplSeqMem.scala @@ -13,7 +13,13 @@ case object OutputConfigFileName extends PassOption case object PassCircuitName extends PassOption object Error { - def apply[T <: Any](msg: String) = throw new Exception(msg) + def apply(msg: String) = throw new Exception(msg) +} +object Warn { + def apply[T <: Any](msg: String, r: T) = { + println(Console.RED + msg + Console.RESET) + r + } } object PassConfigUtil { diff --git a/src/test/scala/firrtlTests/ReplSeqMemTests.scala b/src/test/scala/firrtlTests/ReplSeqMemTests.scala index 3f911fea..57a274c2 100644 --- a/src/test/scala/firrtlTests/ReplSeqMemTests.scala +++ b/src/test/scala/firrtlTests/ReplSeqMemTests.scala @@ -17,7 +17,7 @@ class ReplSeqMemSpec extends SimpleTransformSpec { new EmitFirrtl(writer) ) - "ReplSeqMem" should "generated blackbox wrappers (no wmask, r, w ports)" in { + "ReplSeqMem" should "generate blackbox wrappers (no wmask, r, w ports)" in { val input = """ circuit sram6t : module sram6t : |
