aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAngie2016-08-24 19:12:38 -0700
committerjackkoenig2016-09-06 00:17:18 -0700
commit355cf277f6f7999b27611ada052fcf3005b015d9 (patch)
treef3e4d3c7219680d8b79687b98fae7d06c6b6f50e /src
parentb0252c1f56b92d4bde83cf73059a1c2566a8ba55 (diff)
Expanded annotations for valid memory sizes
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/firrtl/passes/AnnotateValidMemConfigs.scala177
-rw-r--r--src/main/scala/firrtl/passes/ReplSeqMem.scala8
-rw-r--r--src/test/scala/firrtlTests/ReplSeqMemTests.scala2
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 :