Add memory compiler to macros (#29)

* Add memory compiler to macros

* Removed weird spacing

* Make sramcompiler width/depth range inclusive

* Added sramcompiler test
This commit is contained in:
Adam Izraelevitz
2018-02-16 16:01:10 -08:00
committed by GitHub
parent 8a30579a3e
commit 79c8c283cc
7 changed files with 178 additions and 100 deletions

View File

@@ -68,7 +68,7 @@ object MacroCompilerAnnotation {
* @param costMetric Cost metric to use * @param costMetric Cost metric to use
* @param mode Compiler mode (see CompilerMode) * @param mode Compiler mode (see CompilerMode)
*/ */
case class Params(mem: String, lib: Option[String], costMetric: CostMetric, mode: CompilerMode) case class Params(mem: String, lib: Option[String], costMetric: CostMetric, mode: CompilerMode, useCompiler: Boolean)
/** /**
* Create a MacroCompilerAnnotation. * Create a MacroCompilerAnnotation.
@@ -142,15 +142,15 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
} }
// Make sure we don't have a maskGran larger than the width of the memory. // Make sure we don't have a maskGran larger than the width of the memory.
assert (memPort.src.effectiveMaskGran <= memPort.src.width) assert (memPort.src.effectiveMaskGran <= memPort.src.width.get)
assert (libPort.src.effectiveMaskGran <= libPort.src.width) assert (libPort.src.effectiveMaskGran <= libPort.src.width.get)
val libWidth = libPort.src.width val libWidth = libPort.src.width.get
// Don't consider cases of maskGran == width as "masked" since those masks // Don't consider cases of maskGran == width as "masked" since those masks
// effectively function as write-enable bits. // effectively function as write-enable bits.
val memMask = if (memPort.src.effectiveMaskGran == memPort.src.width) None else memPort.src.maskGran val memMask = if (memPort.src.effectiveMaskGran == memPort.src.width.get) None else memPort.src.maskGran
val libMask = if (libPort.src.effectiveMaskGran == libPort.src.width) None else libPort.src.maskGran val libMask = if (libPort.src.effectiveMaskGran == libPort.src.width.get) None else libPort.src.maskGran
(memMask, libMask) match { (memMask, libMask) match {
// Neither lib nor mem is masked. // Neither lib nor mem is masked.
@@ -163,12 +163,12 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
// Only the mem is masked. // Only the mem is masked.
case (Some(p), None) => { case (Some(p), None) => {
if (p % libPort.src.width == 0) { if (p % libPort.src.width.get == 0) {
// If the mem mask is a multiple of the lib width, then we're good. // If the mem mask is a multiple of the lib width, then we're good.
// Just roll over every lib width as usual. // Just roll over every lib width as usual.
// e.g. lib width=4, mem maskGran={4, 8, 12, 16, ...} // e.g. lib width=4, mem maskGran={4, 8, 12, 16, ...}
splitMemory(libWidth) splitMemory(libWidth)
} else if (libPort.src.width % p == 0) { } else if (libPort.src.width.get % p == 0) {
// Lib width is a multiple of the mem mask. // Lib width is a multiple of the mem mask.
// Consider the case where mem mask = 4 but lib width = 8, unmasked. // Consider the case where mem mask = 4 but lib width = 8, unmasked.
// We can still compile, but will need to waste the extra bits. // We can still compile, but will need to waste the extra bits.
@@ -176,13 +176,13 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
} else { } else {
// No neat multiples. // No neat multiples.
// We might still be able to compile extremely inefficiently. // We might still be able to compile extremely inefficiently.
if (p < libPort.src.width) { if (p < libPort.src.width.get) {
// Compile using mem mask as the effective width. (note that lib is not masked) // Compile using mem mask as the effective width. (note that lib is not masked)
// e.g. mem mask = 3, lib width = 8 // e.g. mem mask = 3, lib width = 8
splitMemory(memMask.get) splitMemory(memMask.get)
} else { } else {
// e.g. mem mask = 13, lib width = 8 // e.g. mem mask = 13, lib width = 8
System.err.println(s"Unmasked target memory: unaligned mem maskGran ${p} with lib (${lib.src.name}) width ${libPort.src.width} not supported") System.err.println(s"Unmasked target memory: unaligned mem maskGran ${p} with lib (${lib.src.name}) width ${libPort.src.width.get} not supported")
return None return None
} }
} }
@@ -378,13 +378,13 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
case Some(PolarizedPort(mem, _)) => case Some(PolarizedPort(mem, _)) =>
/* Palmer: The bits from the outer memory's write mask that will be /* Palmer: The bits from the outer memory's write mask that will be
* used as the write mask for this inner memory. */ * used as the write mask for this inner memory. */
if (libPort.src.effectiveMaskGran == libPort.src.width) { if (libPort.src.effectiveMaskGran == libPort.src.width.get) {
bits(WRef(mem), low / memPort.src.effectiveMaskGran) bits(WRef(mem), low / memPort.src.effectiveMaskGran)
} else { } else {
require(isPowerOfTwo(libPort.src.effectiveMaskGran), "only powers of two masks supported for now") require(isPowerOfTwo(libPort.src.effectiveMaskGran), "only powers of two masks supported for now")
val effectiveLibWidth = if (memPort.src.maskGran.get < libPort.src.effectiveMaskGran) memPort.src.maskGran.get else libPort.src.width val effectiveLibWidth = if (memPort.src.maskGran.get < libPort.src.effectiveMaskGran) memPort.src.maskGran.get else libPort.src.width.get
cat(((0 until libPort.src.width by libPort.src.effectiveMaskGran) map (i => { cat(((0 until libPort.src.width.get by libPort.src.effectiveMaskGran) map (i => {
if (memPort.src.maskGran.get < libPort.src.effectiveMaskGran && i >= effectiveLibWidth) { if (memPort.src.maskGran.get < libPort.src.effectiveMaskGran && i >= effectiveLibWidth) {
// If the memMaskGran is smaller than the lib's gran, then // If the memMaskGran is smaller than the lib's gran, then
// zero out the upper bits. // zero out the upper bits.
@@ -398,7 +398,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
/* If there is a lib mask port but no mem mask port, just turn on /* If there is a lib mask port but no mem mask port, just turn on
* all bits of the lib mask port. */ * all bits of the lib mask port. */
if (libPort.src.maskPort.isDefined) { if (libPort.src.maskPort.isDefined) {
val width = libPort.src.width / libPort.src.effectiveMaskGran val width = libPort.src.width.get / libPort.src.effectiveMaskGran
val value = (BigInt(1) << width.toInt) - 1 val value = (BigInt(1) << width.toInt) - 1
UIntLiteral(value, IntWidth(width)) UIntLiteral(value, IntWidth(width))
} else { } else {
@@ -525,7 +525,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
// Run the cost function to evaluate this potential compile. // Run the cost function to evaluate this potential compile.
costMetric.cost(mem, lib) match { costMetric.cost(mem, lib) match {
case Some(newCost) => { case Some(newCost) => {
System.err.println(s"Cost of ${lib.src.name} for ${mem.src.name}: ${newCost}") //System.err.println(s"Cost of ${lib.src.name} for ${mem.src.name}: ${newCost}")
// Try compiling // Try compiling
compile(mem, lib) match { compile(mem, lib) match {
// If it was successful and the new cost is lower // If it was successful and the new cost is lower
@@ -561,7 +561,7 @@ class MacroCompilerTransform extends Transform {
def inputForm = MidForm def inputForm = MidForm
def outputForm = MidForm def outputForm = MidForm
def execute(state: CircuitState) = getMyAnnotations(state) match { def execute(state: CircuitState) = getMyAnnotations(state) match {
case Seq(MacroCompilerAnnotation(state.circuit.main, MacroCompilerAnnotation.Params(memFile, libFile, costMetric, mode))) => case Seq(MacroCompilerAnnotation(state.circuit.main, MacroCompilerAnnotation.Params(memFile, libFile, costMetric, mode, useCompiler))) =>
if (mode == MacroCompilerAnnotation.FallbackSynflops) { if (mode == MacroCompilerAnnotation.FallbackSynflops) {
throw new UnsupportedOperationException("Not implemented yet") throw new UnsupportedOperationException("Not implemented yet")
} }
@@ -573,7 +573,10 @@ class MacroCompilerTransform extends Transform {
} }
val libs: Option[Seq[Macro]] = mdf.macrolib.Utils.readMDFFromPath(libFile) match { val libs: Option[Seq[Macro]] = mdf.macrolib.Utils.readMDFFromPath(libFile) match {
case Some(x:Seq[mdf.macrolib.Macro]) => case Some(x:Seq[mdf.macrolib.Macro]) =>
Some(Utils.filterForSRAM(Some(x)) getOrElse(List()) map {new Macro(_)}) if(useCompiler){
findSRAMCompiler(Some(x)).map{x => buildSRAMMacros(x).map(new Macro(_)) }
}
else Some(Utils.filterForSRAM(Some(x)) getOrElse(List()) map {new Macro(_)})
case _ => None case _ => None
} }
val transforms = Seq( val transforms = Seq(
@@ -614,12 +617,14 @@ object MacroCompiler extends App {
case object Firrtl extends MacroParam case object Firrtl extends MacroParam
case object CostFunc extends MacroParam case object CostFunc extends MacroParam
case object Mode extends MacroParam case object Mode extends MacroParam
case object UseCompiler extends MacroParam
type MacroParamMap = Map[MacroParam, String] type MacroParamMap = Map[MacroParam, String]
type CostParamMap = Map[String, String] type CostParamMap = Map[String, String]
val usage = Seq( val usage = Seq(
"Options:", "Options:",
" -m, --macro-list: The set of macros to compile", " -m, --macro-list: The set of macros to compile",
" -l, --library: The set of macros that have blackbox instances", " -l, --library: The set of macros that have blackbox instances",
" -u, --use-compiler: Flag, whether to use the memory compiler defined in library",
" -v, --verilog: Verilog output", " -v, --verilog: Verilog output",
" -f, --firrtl: FIRRTL output (optional)", " -f, --firrtl: FIRRTL output (optional)",
" -c, --cost-func: Cost function to use. Optional (default: \"default\")", " -c, --cost-func: Cost function to use. Optional (default: \"default\")",
@@ -638,6 +643,8 @@ object MacroCompiler extends App {
parseArgs(map + (Macros -> value), costMap, tail) parseArgs(map + (Macros -> value), costMap, tail)
case ("-l" | "--library") :: value :: tail => case ("-l" | "--library") :: value :: tail =>
parseArgs(map + (Library -> value), costMap, tail) parseArgs(map + (Library -> value), costMap, tail)
case ("-u" | "--use-compiler") :: tail =>
parseArgs(map + (UseCompiler -> ""), costMap, tail)
case ("-v" | "--verilog") :: value :: tail => case ("-v" | "--verilog") :: value :: tail =>
parseArgs(map + (Verilog -> value), costMap, tail) parseArgs(map + (Verilog -> value), costMap, tail)
case ("-f" | "--firrtl") :: value :: tail => case ("-f" | "--firrtl") :: value :: tail =>
@@ -669,7 +676,8 @@ object MacroCompiler extends App {
MacroCompilerAnnotation.Params( MacroCompilerAnnotation.Params(
params.get(Macros).get, params.get(Library), params.get(Macros).get, params.get(Library),
CostMetric.getCostMetric(params.getOrElse(CostFunc, "default"), costParams), CostMetric.getCostMetric(params.getOrElse(CostFunc, "default"), costParams),
MacroCompilerAnnotation.stringToCompilerMode(params.getOrElse(Mode, "default")) MacroCompilerAnnotation.stringToCompilerMode(params.getOrElse(Mode, "default")),
params.contains(UseCompiler)
) )
)) ))
) )
@@ -705,6 +713,7 @@ object MacroCompiler extends App {
} }
} catch { } catch {
case e: java.util.NoSuchElementException => case e: java.util.NoSuchElementException =>
e.printStackTrace()
println(usage) println(usage)
e.printStackTrace() e.printStackTrace()
sys.exit(1) sys.exit(1)

View File

@@ -18,9 +18,9 @@ class FirrtlMacroPort(port: MacroPort) {
val isWriter = port.input.nonEmpty && port.output.isEmpty val isWriter = port.input.nonEmpty && port.output.isEmpty
val isReadWriter = port.input.nonEmpty && port.output.nonEmpty val isReadWriter = port.input.nonEmpty && port.output.nonEmpty
val addrType = UIntType(IntWidth(ceilLog2(port.depth) max 1)) val addrType = UIntType(IntWidth(ceilLog2(port.depth.get) max 1))
val dataType = UIntType(IntWidth(port.width)) val dataType = UIntType(IntWidth(port.width.get))
val maskType = UIntType(IntWidth(port.width / port.effectiveMaskGran)) val maskType = UIntType(IntWidth(port.width.get / port.effectiveMaskGran))
// Bundle representing this macro port. // Bundle representing this macro port.
val tpe = BundleType(Seq( val tpe = BundleType(Seq(
@@ -72,6 +72,33 @@ object Utils {
case _ => None case _ => None
} }
} }
def findSRAMCompiler(s: Option[Seq[mdf.macrolib.Macro]]): Option[mdf.macrolib.SRAMCompiler] = {
s match {
case Some(l:Seq[mdf.macrolib.Macro]) =>
l collectFirst {
case x: mdf.macrolib.SRAMCompiler => x
}
case _ => None
}
}
def buildSRAMMacros(s: mdf.macrolib.SRAMCompiler): Seq[mdf.macrolib.SRAMMacro] = {
for (g <- s.groups; d <- g.depth; w <- g.width; vt <- g.vt)
yield mdf.macrolib.SRAMMacro(makeName(g, d, w, vt), w, d, g.family, g.ports.map(_.copy(width=Some(w), depth=Some(d))), g.extraPorts)
}
def makeName(g: mdf.macrolib.SRAMGroup, depth: Int, width: Int, vt: String): String = {
g.name.foldLeft(""){ (builder, next) =>
next match {
case "depth"|"DEPTH" => builder + depth
case "width"|"WIDTH" => builder + width
case "vt" => builder + vt.toLowerCase
case "VT" => builder + vt.toUpperCase
case "family" => builder + g.family.toLowerCase
case "FAMILY" => builder + g.family.toUpperCase
case "mux"|"MUX" => builder + g.mux
case other => builder + other
}
}
}
def and(e1: Expression, e2: Expression) = def and(e1: Expression, e2: Expression) =
DoPrim(PrimOps.And, Seq(e1, e2), Nil, e1.tpe) DoPrim(PrimOps.And, Seq(e1, e2), Nil, e1.tpe)

View File

@@ -7,6 +7,8 @@ import firrtl.Utils.ceilLog2
import java.io.{File, StringWriter} import java.io.{File, StringWriter}
abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalatest.Matchers { abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalatest.Matchers {
import scala.language.implicitConversions
implicit def String2SomeString(i: String): Option[String] = Some(i)
val testDir: String = "test_run_dir/macros" val testDir: String = "test_run_dir/macros"
new File(testDir).mkdirs // Make sure the testDir exists new File(testDir).mkdirs // Make sure the testDir exists
@@ -32,11 +34,12 @@ abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalate
} }
} }
private def args(mem: String, lib: Option[String], v: String, synflops: Boolean) = private def args(mem: String, lib: Option[String], v: String, synflops: Boolean, useCompiler: Boolean) =
List("-m", mem.toString, "-v", v) ++ List("-m", mem.toString, "-v", v) ++
(lib match { case None => Nil case Some(l) => List("-l", l.toString) }) ++ (lib match { case None => Nil case Some(l) => List("-l", l.toString) }) ++
costMetricCmdLine ++ costMetricCmdLine ++
(if (synflops) List("--mode", "synflops") else Nil) (if (synflops) List("--mode", "synflops") else Nil) ++
(if (useCompiler) List("--use-compiler") else Nil)
// Run the full compiler as if from the command line interface. // Run the full compiler as if from the command line interface.
// Generates the Verilog; useful in testing since an error will throw an // Generates the Verilog; useful in testing since an error will throw an
@@ -44,12 +47,12 @@ abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalate
def compile(mem: String, lib: String, v: String, synflops: Boolean) { def compile(mem: String, lib: String, v: String, synflops: Boolean) {
compile(mem, Some(lib), v, synflops) compile(mem, Some(lib), v, synflops)
} }
def compile(mem: String, lib: Option[String], v: String, synflops: Boolean) { def compile(mem: String, lib: Option[String], v: String, synflops: Boolean, useCompiler: Boolean = false) {
var mem_full = concat(memPrefix, mem) var mem_full = concat(memPrefix, mem)
var lib_full = concat(libPrefix, lib) var lib_full = concat(libPrefix, lib)
var v_full = concat(vPrefix, v) var v_full = concat(vPrefix, v)
MacroCompiler.run(args(mem_full, lib_full, v_full, synflops)) MacroCompiler.run(args(mem_full, lib_full, v_full, synflops, useCompiler))
} }
// Helper functions to write macro libraries to the given files. // Helper functions to write macro libraries to the given files.
@@ -62,14 +65,11 @@ abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalate
} }
// Convenience function for running both compile, execute, and test at once. // Convenience function for running both compile, execute, and test at once.
def compileExecuteAndTest(mem: String, lib: Option[String], v: String, output: String, synflops: Boolean): Unit = { def compileExecuteAndTest(mem: String, lib: Option[String], v: String, output: String, synflops: Boolean = false, useCompiler: Boolean = false): Unit = {
compile(mem, lib, v, synflops) compile(mem, lib, v, synflops, useCompiler)
val result = execute(mem, lib, synflops) val result = execute(mem, lib, synflops, useCompiler)
test(result, output) test(result, output)
} }
def compileExecuteAndTest(mem: String, lib: String, v: String, output: String, synflops: Boolean = false): Unit = {
compileExecuteAndTest(mem, Some(lib), v, output, synflops)
}
// Compare FIRRTL outputs after reparsing output with ScalaTest ("should be"). // Compare FIRRTL outputs after reparsing output with ScalaTest ("should be").
def test(result: Circuit, output: String): Unit = { def test(result: Circuit, output: String): Unit = {
@@ -79,21 +79,20 @@ abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalate
// Execute the macro compiler and returns a Circuit containing the output of // Execute the macro compiler and returns a Circuit containing the output of
// the memory compiler. // the memory compiler.
def execute(memFile: String, libFile: Option[String], synflops: Boolean): Circuit = { def execute(memFile: Option[String], libFile: Option[String], synflops: Boolean): Circuit = execute(memFile, libFile, synflops, false)
execute(Some(memFile), libFile, synflops) def execute(memFile: Option[String], libFile: Option[String], synflops: Boolean, useCompiler: Boolean): Circuit = {
}
def execute(memFile: String, libFile: String, synflops: Boolean): Circuit = {
execute(Some(memFile), Some(libFile), synflops)
}
def execute(memFile: Option[String], libFile: Option[String], synflops: Boolean): Circuit = {
var mem_full = concat(memPrefix, memFile) var mem_full = concat(memPrefix, memFile)
var lib_full = concat(libPrefix, libFile) var lib_full = concat(libPrefix, libFile)
require(memFile.isDefined) require(memFile.isDefined)
val mems: Seq[Macro] = Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(mem_full)).get map (new Macro(_)) val mems: Seq[Macro] = Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(mem_full)).get map (new Macro(_))
val libs: Option[Seq[Macro]] = Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(lib_full)) match { val libs: Option[Seq[Macro]] = if(useCompiler) {
case Some(x) => Some(x map (new Macro(_))) Utils.findSRAMCompiler(mdf.macrolib.Utils.readMDFFromPath(lib_full)).map{x => Utils.buildSRAMMacros(x).map(new Macro(_)) }
case None => None } else {
Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(lib_full)) match {
case Some(x) => Some(x map (new Macro(_)))
case None => None
}
} }
val macros = mems map (_.blackbox) val macros = mems map (_.blackbox)
val circuit = Circuit(NoInfo, macros, macros.last.name) val circuit = Circuit(NoInfo, macros, macros.last.name)
@@ -105,6 +104,7 @@ abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalate
result result
} }
// Helper method to deal with String + Option[String] // Helper method to deal with String + Option[String]
private def concat(a: String, b: String): String = {a + "/" + b} private def concat(a: String, b: String): String = {a + "/" + b}
private def concat(a: String, b: Option[String]): Option[String] = { private def concat(a: String, b: Option[String]): Option[String] = {
@@ -118,12 +118,16 @@ abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalate
// A collection of standard SRAM generators. // A collection of standard SRAM generators.
trait HasSRAMGenerator { trait HasSRAMGenerator {
import mdf.macrolib._ import mdf.macrolib._
import scala.language.implicitConversions
implicit def Int2SomeInt(i: Int): Option[Int] = Some(i)
// Generate a standard (read/write/combo) port for testing. // Generate a standard (read/write/combo) port for testing.
// Helper methods for optional width argument
def generateTestPort( def generateTestPort(
prefix: String, prefix: String,
width: Int, width: Option[Int],
depth: Int, depth: Option[Int],
maskGran: Option[Int] = None, maskGran: Option[Int] = None,
read: Boolean, read: Boolean,
readEnable: Boolean = false, readEnable: Boolean = false,
@@ -133,55 +137,69 @@ trait HasSRAMGenerator {
val realPrefix = if (prefix == "") "" else prefix + "_" val realPrefix = if (prefix == "") "" else prefix + "_"
MacroPort( MacroPort(
address=PolarizedPort(name=realPrefix + "addr", polarity=ActiveHigh), address = PolarizedPort(name = realPrefix + "addr", polarity = ActiveHigh),
clock=PolarizedPort(name=realPrefix + "clk", polarity=PositiveEdge), clock = PolarizedPort(name = realPrefix + "clk", polarity = PositiveEdge),
readEnable=if (readEnable) Some(PolarizedPort(name=realPrefix + "read_en", polarity=ActiveHigh)) else None, readEnable = if (readEnable) Some(PolarizedPort(name = realPrefix + "read_en", polarity = ActiveHigh)) else None,
writeEnable=if (writeEnable) Some(PolarizedPort(name=realPrefix + "write_en", polarity=ActiveHigh)) else None, writeEnable = if (writeEnable) Some(PolarizedPort(name = realPrefix + "write_en", polarity = ActiveHigh)) else None,
output=if (read) Some(PolarizedPort(name=realPrefix + "dout", polarity=ActiveHigh)) else None, output = if (read) Some(PolarizedPort(name = realPrefix + "dout", polarity = ActiveHigh)) else None,
input=if (write) Some(PolarizedPort(name=realPrefix + "din", polarity=ActiveHigh)) else None, input = if (write) Some(PolarizedPort(name = realPrefix + "din", polarity = ActiveHigh)) else None,
maskPort=maskGran match { maskPort = maskGran match {
case Some(x:Int) => Some(PolarizedPort(name=realPrefix + "mask", polarity=ActiveHigh)) case Some(x: Int) => Some(PolarizedPort(name = realPrefix + "mask", polarity = ActiveHigh))
case _ => None case _ => None
}, },
maskGran=maskGran, maskGran = maskGran,
width=width, depth=depth // These numbers don't matter here. width = width, depth = depth // These numbers don't matter here.
) )
} }
// Generate a read port for testing. // Generate a read port for testing.
def generateReadPort(prefix: String, width: Int, depth: Int, readEnable: Boolean = false): MacroPort = { def generateReadPort(prefix: String, width: Option[Int], depth: Option[Int], readEnable: Boolean = false): MacroPort = {
generateTestPort(prefix, width, depth, write=false, read=true, readEnable=readEnable) generateTestPort(prefix, width, depth, write = false, read = true, readEnable = readEnable)
} }
// Generate a write port for testing. // Generate a write port for testing.
def generateWritePort(prefix: String, width: Int, depth: Int, maskGran: Option[Int] = None, writeEnable: Boolean = true): MacroPort = { def generateWritePort(prefix: String, width: Option[Int], depth: Option[Int], maskGran: Option[Int] = None, writeEnable: Boolean = true): MacroPort = {
generateTestPort(prefix, width, depth, maskGran=maskGran, write=true, read=false, writeEnable=writeEnable) generateTestPort(prefix, width, depth, maskGran = maskGran, write = true, read = false, writeEnable = writeEnable)
} }
// Generate a simple read-write port for testing. // Generate a simple read-write port for testing.
def generateReadWritePort(prefix: String, width: Int, depth: Int, maskGran: Option[Int] = None): MacroPort = { def generateReadWritePort(prefix: String, width: Option[Int], depth: Option[Int], maskGran: Option[Int] = None): MacroPort = {
generateTestPort( generateTestPort(
prefix, width, depth, maskGran=maskGran, prefix, width, depth, maskGran = maskGran,
write=true, writeEnable=true, write = true, writeEnable = true,
read=true, readEnable=false read = true, readEnable = false
) )
} }
// Generate a "simple" SRAM (active high/positive edge, 1 read-write port). // Generate a "simple" SRAM (active high/positive edge, 1 read-write port).
def generateSRAM(name: String, prefix: String, width: Int, depth: Int, maskGran: Option[Int] = None, extraPorts: Seq[MacroExtraPort] = List()): SRAMMacro = { def generateSRAM(name: String, prefix: String, width: Int, depth: Int, maskGran: Option[Int] = None, extraPorts: Seq[MacroExtraPort] = List()): SRAMMacro = {
SRAMMacro( SRAMMacro(
name=name, name = name,
width=width, width = width,
depth=depth, depth = depth,
family="1rw", family = "1rw",
ports=Seq(generateReadWritePort(prefix, width, depth, maskGran)), ports = Seq(generateReadWritePort(prefix, width, depth, maskGran)),
extraPorts=extraPorts extraPorts = extraPorts
) )
} }
// Generate a "simple" SRAM group (active high/positive edge, 1 read-write port).
def generateSimpleSRAMGroup(prefix: String, mux: Int, depth: Range, width: Range, maskGran: Option[Int] = None, extraPorts: Seq[MacroExtraPort] = List()): SRAMGroup = {
SRAMGroup(Seq("mygroup_", "width", "x", "depth", "_", "VT"), "1rw", Seq("svt", "lvt", "ulvt"), mux, depth, width, Seq(generateReadWritePort(prefix, None, None, maskGran)))
}
// 'vt': ('svt','lvt','ulvt'), 'mux': 2, 'depth': range(16,513,8), 'width': range(8,289,2), 'ports': 1
// 'vt': ('svt','lvt','ulvt'), 'mux': 4, 'depth': range(32,1025,16), 'width': range(4,145), 'ports': 1}
def generateSRAMCompiler(name: String, prefix: String): mdf.macrolib.SRAMCompiler = {
SRAMCompiler(name, Seq(
generateSimpleSRAMGroup(prefix, 2, Range(16, 512, 8), Range(8, 288, 2)),
generateSimpleSRAMGroup(prefix, 4, Range(32, 1024, 16), Range(4, 144, 1))
))
}
} }
// Generic "simple" test generator. // Generic "simple" test generator.
@@ -192,6 +210,7 @@ trait HasSimpleTestGenerator {
// Override these with "override lazy val". // Override these with "override lazy val".
// Why lazy? These are used in the constructor here so overriding non-lazily // Why lazy? These are used in the constructor here so overriding non-lazily
// would be too late. // would be too late.
def useCompiler: Boolean = false
def memWidth: Int def memWidth: Int
def libWidth: Int def libWidth: Int
def memDepth: Int def memDepth: Int
@@ -224,10 +243,10 @@ trait HasSimpleTestGenerator {
val lib = s"lib-${generatorType}${extraTagPrefixed}.json" val lib = s"lib-${generatorType}${extraTagPrefixed}.json"
val v = s"${generatorType}${extraTagPrefixed}.v" val v = s"${generatorType}${extraTagPrefixed}.v"
val mem_name = "target_memory" lazy val mem_name = "target_memory"
val mem_addr_width = ceilLog2(memDepth) val mem_addr_width = ceilLog2(memDepth)
val lib_name = "awesome_lib_mem" lazy val lib_name = "awesome_lib_mem"
val lib_addr_width = ceilLog2(libDepth) val lib_addr_width = ceilLog2(libDepth)
// Override these to change the port prefixes if needed. // Override these to change the port prefixes if needed.
@@ -258,8 +277,8 @@ trait HasSimpleTestGenerator {
// Number of width bits in the last width-direction memory. // Number of width bits in the last width-direction memory.
// e.g. if memWidth = 16 and libWidth = 8, this would be 8 since the last memory 0_1 has 8 bits of input width. // e.g. if memWidth = 16 and libWidth = 8, this would be 8 since the last memory 0_1 has 8 bits of input width.
// e.g. if memWidth = 9 and libWidth = 8, this would be 1 since the last memory 0_1 has 1 bit of input width. // e.g. if memWidth = 9 and libWidth = 8, this would be 1 since the last memory 0_1 has 1 bit of input width.
val lastWidthBits = if (memWidth % usableLibWidth == 0) usableLibWidth else (memWidth % usableLibWidth) lazy val lastWidthBits = if (memWidth % usableLibWidth == 0) usableLibWidth else (memWidth % usableLibWidth)
val selectBits = mem_addr_width - lib_addr_width lazy val selectBits = mem_addr_width - lib_addr_width
/** /**
* Convenience function to generate a mask statement. * Convenience function to generate a mask statement.
@@ -410,3 +429,4 @@ trait HasNoLibTestGenerator extends HasSimpleTestGenerator {
// If there is no lib, don't generate a body. // If there is no lib, don't generate a body.
override def generateBody = "" override def generateBody = ""
} }

View File

@@ -0,0 +1,22 @@
package barstools.macros
import mdf.macrolib._
class SRAMCompiler extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
val compiler = generateSRAMCompiler("awesome", "A")
val verilog = s"v-SRAMCompiler.v"
override lazy val depth = 16
override lazy val memWidth = 8
override lazy val libWidth = 8
override lazy val mem_name = "mymem"
override lazy val memPortPrefix = "X"
override lazy val lib_name = "mygroup_8x16_SVT"
override lazy val libPortPrefix = "A"
writeToLib(lib, Seq(compiler))
writeToMem(mem, Seq(generateSRAM("mymem", "X", 8, 16)))
compileExecuteAndTest(mem, Some(lib), verilog, output=output, false, true)
}

View File

@@ -20,27 +20,27 @@ trait HasSimpleDepthTestGenerator extends HasSimpleTestGenerator {
if (selectBits > 0) { if (selectBits > 0) {
output.append ( output.append (
s""" s"""
node outer_addr_sel = bits(outer_addr, ${mem_addr_width - 1}, $lib_addr_width) node ${memPortPrefix}_addr_sel = bits(${memPortPrefix}_addr, ${mem_addr_width - 1}, $lib_addr_width)
reg outer_addr_sel_reg : UInt<${selectBits}>, outer_clk with : reg ${memPortPrefix}_addr_sel_reg : UInt<${selectBits}>, ${memPortPrefix}_clk with :
reset => (UInt<1>("h0"), outer_addr_sel_reg) reset => (UInt<1>("h0"), ${memPortPrefix}_addr_sel_reg)
outer_addr_sel_reg <= mux(UInt<1>("h1"), outer_addr_sel, outer_addr_sel_reg) ${memPortPrefix}_addr_sel_reg <= mux(UInt<1>("h1"), ${memPortPrefix}_addr_sel, ${memPortPrefix}_addr_sel_reg)
""" """
) )
} }
for (i <- 0 to depthInstances - 1) { for (i <- 0 to depthInstances - 1) {
val maskStatement = generateMaskStatement(0, i) val maskStatement = generateMaskStatement(0, i)
val enableIdentifier = if (selectBits > 0) s"""eq(outer_addr_sel, UInt<${selectBits}>("h${i.toHexString}"))""" else "UInt<1>(\"h1\")" val enableIdentifier = if (selectBits > 0) s"""eq(${memPortPrefix}_addr_sel, UInt<${selectBits}>("h${i.toHexString}"))""" else "UInt<1>(\"h1\")"
output.append( output.append(
s""" s"""
inst mem_${i}_0 of awesome_lib_mem inst mem_${i}_0 of ${lib_name}
mem_${i}_0.lib_clk <= outer_clk mem_${i}_0.${libPortPrefix}_clk <= ${memPortPrefix}_clk
mem_${i}_0.lib_addr <= outer_addr mem_${i}_0.${libPortPrefix}_addr <= ${memPortPrefix}_addr
node outer_dout_${i}_0 = bits(mem_${i}_0.lib_dout, ${width - 1}, 0) node ${memPortPrefix}_dout_${i}_0 = bits(mem_${i}_0.${libPortPrefix}_dout, ${width - 1}, 0)
mem_${i}_0.lib_din <= bits(outer_din, ${width - 1}, 0) mem_${i}_0.${libPortPrefix}_din <= bits(${memPortPrefix}_din, ${width - 1}, 0)
${maskStatement} ${maskStatement}
mem_${i}_0.lib_write_en <= and(and(outer_write_en, UInt<1>("h1")), ${enableIdentifier}) mem_${i}_0.${libPortPrefix}_write_en <= and(and(${memPortPrefix}_write_en, UInt<1>("h1")), ${enableIdentifier})
node outer_dout_${i} = outer_dout_${i}_0 node ${memPortPrefix}_dout_${i} = ${memPortPrefix}_dout_${i}_0
""" """
) )
} }
@@ -48,16 +48,16 @@ s"""
if (i > depthInstances - 1) { if (i > depthInstances - 1) {
"UInt<1>(\"h0\")" "UInt<1>(\"h0\")"
} else { } else {
"mux(eq(outer_addr_sel_reg, UInt<%d>(\"h%s\")), outer_dout_%d, %s)".format( s"""mux(eq(${memPortPrefix}_addr_sel_reg, UInt<%d>("h%s")), ${memPortPrefix}_dout_%d, %s)""".format(
selectBits, i.toHexString, i, generate_outer_dout_tree(i + 1, depthInstances) selectBits, i.toHexString, i, generate_outer_dout_tree(i + 1, depthInstances)
) )
} }
} }
output append " outer_dout <= " output append s" ${memPortPrefix}_dout <= "
if (selectBits > 0) { if (selectBits > 0) {
output append generate_outer_dout_tree(0, depthInstances) output append generate_outer_dout_tree(0, depthInstances)
} else { } else {
output append """mux(UInt<1>("h1"), outer_dout_0, UInt<1>("h0"))""" output append s"""mux(UInt<1>("h1"), ${memPortPrefix}_dout_0, UInt<1>("h0"))"""
} }
output.toString output.toString

View File

@@ -40,28 +40,28 @@ trait HasSimpleWidthTestGenerator extends HasSimpleTestGenerator {
} else """UInt<1>("h1")""" } else """UInt<1>("h1")"""
s""" s"""
mem_0_${i}.lib_clk <= outer_clk mem_0_${i}.${libPortPrefix}_clk <= ${memPortPrefix}_clk
mem_0_${i}.lib_addr <= outer_addr mem_0_${i}.${libPortPrefix}_addr <= ${memPortPrefix}_addr
node outer_dout_0_${i} = bits(mem_0_${i}.lib_dout, ${myMemWidth - 1}, 0) node ${memPortPrefix}_dout_0_${i} = bits(mem_0_${i}.${libPortPrefix}_dout, ${myMemWidth - 1}, 0)
mem_0_${i}.lib_din <= bits(outer_din, ${myBaseBit + myMemWidth - 1}, ${myBaseBit}) mem_0_${i}.${libPortPrefix}_din <= bits(${memPortPrefix}_din, ${myBaseBit + myMemWidth - 1}, ${myBaseBit})
${maskStatement} ${maskStatement}
mem_0_${i}.lib_write_en <= and(and(outer_write_en, ${writeEnableBit}), UInt<1>("h1")) mem_0_${i}.${libPortPrefix}_write_en <= and(and(${memPortPrefix}_write_en, ${writeEnableBit}), UInt<1>("h1"))
""" """
}).reduceLeft(_ + _) }).reduceLeft(_ + _)
// Generate final output that concats together the sub-memories. // Generate final output that concats together the sub-memories.
// e.g. cat(outer_dout_0_2, cat(outer_dout_0_1, outer_dout_0_0)) // e.g. cat(outer_dout_0_2, cat(outer_dout_0_1, outer_dout_0_0))
output append { output append {
val doutStatements = ((widthInstances - 1 to 0 by -1) map (i => s"outer_dout_0_${i}")) val doutStatements = ((widthInstances - 1 to 0 by -1) map (i => s"${memPortPrefix}_dout_0_${i}"))
val catStmt = doutStatements.init.foldRight(doutStatements.last)((l: String, r: String) => s"cat($l, $r)") val catStmt = doutStatements.init.foldRight(doutStatements.last)((l: String, r: String) => s"cat($l, $r)")
s""" s"""
node outer_dout_0 = ${catStmt} node ${memPortPrefix}_dout_0 = ${catStmt}
""" """
} }
output append output append
""" s"""
outer_dout <= mux(UInt<1>("h1"), outer_dout_0, UInt<1>("h0")) ${memPortPrefix}_dout <= mux(UInt<1>("h1"), ${memPortPrefix}_dout_0, UInt<1>("h0"))
""" """
output.toString output.toString
} }
@@ -398,7 +398,7 @@ class SplitWidth1024x32_readEnable_Lib extends MacroCompilerSpec with HasSRAMGen
depth=libDepth, depth=libDepth,
family="1rw", family="1rw",
ports=Seq(generateTestPort( ports=Seq(generateTestPort(
"lib", libWidth, libDepth, maskGran=libMaskGran, "lib", Some(libWidth), Some(libDepth), maskGran=libMaskGran,
write=true, writeEnable=true, write=true, writeEnable=true,
read=true, readEnable=true read=true, readEnable=true
)) ))
@@ -456,7 +456,7 @@ class SplitWidth1024x32_readEnable_Mem extends MacroCompilerSpec with HasSRAMGen
depth=memDepth, depth=memDepth,
family="1rw", family="1rw",
ports=Seq(generateTestPort( ports=Seq(generateTestPort(
"outer", memWidth, memDepth, maskGran=memMaskGran, "outer", Some(memWidth), Some(memDepth), maskGran=memMaskGran,
write=true, writeEnable=true, write=true, writeEnable=true,
read=true, readEnable=true read=true, readEnable=true
)) ))
@@ -482,7 +482,7 @@ class SplitWidth1024x32_readEnable_LibMem extends MacroCompilerSpec with HasSRAM
depth=libDepth, depth=libDepth,
family="1rw", family="1rw",
ports=Seq(generateTestPort( ports=Seq(generateTestPort(
"lib", libWidth, libDepth, maskGran=libMaskGran, "lib", Some(libWidth), Some(libDepth), maskGran=libMaskGran,
write=true, writeEnable=true, write=true, writeEnable=true,
read=true, readEnable=true read=true, readEnable=true
)) ))
@@ -496,7 +496,7 @@ class SplitWidth1024x32_readEnable_LibMem extends MacroCompilerSpec with HasSRAM
depth=memDepth, depth=memDepth,
family="1rw", family="1rw",
ports=Seq(generateTestPort( ports=Seq(generateTestPort(
"outer", memWidth, memDepth, maskGran=memMaskGran, "outer", Some(memWidth), Some(memDepth), maskGran=memMaskGran,
write=true, writeEnable=true, write=true, writeEnable=true,
read=true, readEnable=true read=true, readEnable=true
)) ))

2
mdf

Submodule mdf updated: 2b5f3c16da...2bc5a363e2