aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAngie2016-08-23 17:32:15 -0700
committerjackkoenig2016-09-06 00:17:18 -0700
commitb0252c1f56b92d4bde83cf73059a1c2566a8ba55 (patch)
tree5794f87574b4d06ad95e741b90fee86ef8e2eed3 /src
parent6bf15386079d862d042968f5d2ac30c9d092134c (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.scala2
-rw-r--r--src/main/scala/firrtl/passes/AnnotateMemMacros.scala4
-rw-r--r--src/main/scala/firrtl/passes/AnnotateValidMemConfigs.scala200
-rw-r--r--src/main/scala/firrtl/passes/ReplSeqMem.scala23
-rw-r--r--src/main/scala/firrtl/passes/ReplaceMemMacros.scala20
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