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:
@@ -68,7 +68,7 @@ object MacroCompilerAnnotation {
|
||||
* @param costMetric Cost metric to use
|
||||
* @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.
|
||||
@@ -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.
|
||||
assert (memPort.src.effectiveMaskGran <= memPort.src.width)
|
||||
assert (libPort.src.effectiveMaskGran <= libPort.src.width)
|
||||
assert (memPort.src.effectiveMaskGran <= memPort.src.width.get)
|
||||
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
|
||||
// effectively function as write-enable bits.
|
||||
val memMask = if (memPort.src.effectiveMaskGran == memPort.src.width) None else memPort.src.maskGran
|
||||
val libMask = if (libPort.src.effectiveMaskGran == libPort.src.width) None else libPort.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.get) None else libPort.src.maskGran
|
||||
|
||||
(memMask, libMask) match {
|
||||
// Neither lib nor mem is masked.
|
||||
@@ -163,12 +163,12 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
|
||||
// Only the mem is masked.
|
||||
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.
|
||||
// Just roll over every lib width as usual.
|
||||
// e.g. lib width=4, mem maskGran={4, 8, 12, 16, ...}
|
||||
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.
|
||||
// Consider the case where mem mask = 4 but lib width = 8, unmasked.
|
||||
// We can still compile, but will need to waste the extra bits.
|
||||
@@ -176,13 +176,13 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
} else {
|
||||
// No neat multiples.
|
||||
// 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)
|
||||
// e.g. mem mask = 3, lib width = 8
|
||||
splitMemory(memMask.get)
|
||||
} else {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@@ -378,13 +378,13 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
case Some(PolarizedPort(mem, _)) =>
|
||||
/* Palmer: The bits from the outer memory's write mask that will be
|
||||
* 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)
|
||||
} else {
|
||||
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
|
||||
cat(((0 until libPort.src.width by libPort.src.effectiveMaskGran) map (i => {
|
||||
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.get by libPort.src.effectiveMaskGran) map (i => {
|
||||
if (memPort.src.maskGran.get < libPort.src.effectiveMaskGran && i >= effectiveLibWidth) {
|
||||
// If the memMaskGran is smaller than the lib's gran, then
|
||||
// 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
|
||||
* all bits of the lib mask port. */
|
||||
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
|
||||
UIntLiteral(value, IntWidth(width))
|
||||
} else {
|
||||
@@ -525,7 +525,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
// Run the cost function to evaluate this potential compile.
|
||||
costMetric.cost(mem, lib) match {
|
||||
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
|
||||
compile(mem, lib) match {
|
||||
// If it was successful and the new cost is lower
|
||||
@@ -561,7 +561,7 @@ class MacroCompilerTransform extends Transform {
|
||||
def inputForm = MidForm
|
||||
def outputForm = MidForm
|
||||
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) {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
val transforms = Seq(
|
||||
@@ -614,12 +617,14 @@ object MacroCompiler extends App {
|
||||
case object Firrtl extends MacroParam
|
||||
case object CostFunc extends MacroParam
|
||||
case object Mode extends MacroParam
|
||||
case object UseCompiler extends MacroParam
|
||||
type MacroParamMap = Map[MacroParam, String]
|
||||
type CostParamMap = Map[String, String]
|
||||
val usage = Seq(
|
||||
"Options:",
|
||||
" -m, --macro-list: The set of macros to compile",
|
||||
" -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",
|
||||
" -f, --firrtl: FIRRTL output (optional)",
|
||||
" -c, --cost-func: Cost function to use. Optional (default: \"default\")",
|
||||
@@ -638,6 +643,8 @@ object MacroCompiler extends App {
|
||||
parseArgs(map + (Macros -> value), costMap, tail)
|
||||
case ("-l" | "--library") :: value :: tail =>
|
||||
parseArgs(map + (Library -> value), costMap, tail)
|
||||
case ("-u" | "--use-compiler") :: tail =>
|
||||
parseArgs(map + (UseCompiler -> ""), costMap, tail)
|
||||
case ("-v" | "--verilog") :: value :: tail =>
|
||||
parseArgs(map + (Verilog -> value), costMap, tail)
|
||||
case ("-f" | "--firrtl") :: value :: tail =>
|
||||
@@ -669,7 +676,8 @@ object MacroCompiler extends App {
|
||||
MacroCompilerAnnotation.Params(
|
||||
params.get(Macros).get, params.get(Library),
|
||||
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 {
|
||||
case e: java.util.NoSuchElementException =>
|
||||
e.printStackTrace()
|
||||
println(usage)
|
||||
e.printStackTrace()
|
||||
sys.exit(1)
|
||||
|
||||
@@ -18,9 +18,9 @@ class FirrtlMacroPort(port: MacroPort) {
|
||||
val isWriter = port.input.nonEmpty && port.output.isEmpty
|
||||
val isReadWriter = port.input.nonEmpty && port.output.nonEmpty
|
||||
|
||||
val addrType = UIntType(IntWidth(ceilLog2(port.depth) max 1))
|
||||
val dataType = UIntType(IntWidth(port.width))
|
||||
val maskType = UIntType(IntWidth(port.width / port.effectiveMaskGran))
|
||||
val addrType = UIntType(IntWidth(ceilLog2(port.depth.get) max 1))
|
||||
val dataType = UIntType(IntWidth(port.width.get))
|
||||
val maskType = UIntType(IntWidth(port.width.get / port.effectiveMaskGran))
|
||||
|
||||
// Bundle representing this macro port.
|
||||
val tpe = BundleType(Seq(
|
||||
@@ -72,6 +72,33 @@ object Utils {
|
||||
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) =
|
||||
DoPrim(PrimOps.And, Seq(e1, e2), Nil, e1.tpe)
|
||||
|
||||
@@ -7,6 +7,8 @@ import firrtl.Utils.ceilLog2
|
||||
import java.io.{File, StringWriter}
|
||||
|
||||
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"
|
||||
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) ++
|
||||
(lib match { case None => Nil case Some(l) => List("-l", l.toString) }) ++
|
||||
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.
|
||||
// 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) {
|
||||
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 lib_full = concat(libPrefix, lib)
|
||||
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.
|
||||
@@ -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.
|
||||
def compileExecuteAndTest(mem: String, lib: Option[String], v: String, output: String, synflops: Boolean): Unit = {
|
||||
compile(mem, lib, v, synflops)
|
||||
val result = execute(mem, lib, synflops)
|
||||
def compileExecuteAndTest(mem: String, lib: Option[String], v: String, output: String, synflops: Boolean = false, useCompiler: Boolean = false): Unit = {
|
||||
compile(mem, lib, v, synflops, useCompiler)
|
||||
val result = execute(mem, lib, synflops, useCompiler)
|
||||
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").
|
||||
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
|
||||
// the memory compiler.
|
||||
def execute(memFile: String, libFile: Option[String], synflops: Boolean): Circuit = {
|
||||
execute(Some(memFile), libFile, synflops)
|
||||
}
|
||||
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 = {
|
||||
def execute(memFile: Option[String], libFile: Option[String], synflops: Boolean): Circuit = execute(memFile, libFile, synflops, false)
|
||||
def execute(memFile: Option[String], libFile: Option[String], synflops: Boolean, useCompiler: Boolean): Circuit = {
|
||||
var mem_full = concat(memPrefix, memFile)
|
||||
var lib_full = concat(libPrefix, libFile)
|
||||
|
||||
require(memFile.isDefined)
|
||||
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 {
|
||||
case Some(x) => Some(x map (new Macro(_)))
|
||||
case None => None
|
||||
val libs: Option[Seq[Macro]] = if(useCompiler) {
|
||||
Utils.findSRAMCompiler(mdf.macrolib.Utils.readMDFFromPath(lib_full)).map{x => Utils.buildSRAMMacros(x).map(new Macro(_)) }
|
||||
} 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 circuit = Circuit(NoInfo, macros, macros.last.name)
|
||||
@@ -105,6 +104,7 @@ abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalate
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
// Helper method to deal with String + Option[String]
|
||||
private def concat(a: String, b: String): String = {a + "/" + b}
|
||||
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.
|
||||
trait HasSRAMGenerator {
|
||||
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.
|
||||
// Helper methods for optional width argument
|
||||
def generateTestPort(
|
||||
prefix: String,
|
||||
width: Int,
|
||||
depth: Int,
|
||||
width: Option[Int],
|
||||
depth: Option[Int],
|
||||
maskGran: Option[Int] = None,
|
||||
read: Boolean,
|
||||
readEnable: Boolean = false,
|
||||
@@ -133,55 +137,69 @@ trait HasSRAMGenerator {
|
||||
val realPrefix = if (prefix == "") "" else prefix + "_"
|
||||
|
||||
MacroPort(
|
||||
address=PolarizedPort(name=realPrefix + "addr", polarity=ActiveHigh),
|
||||
clock=PolarizedPort(name=realPrefix + "clk", polarity=PositiveEdge),
|
||||
address = PolarizedPort(name = realPrefix + "addr", polarity = ActiveHigh),
|
||||
clock = PolarizedPort(name = realPrefix + "clk", polarity = PositiveEdge),
|
||||
|
||||
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,
|
||||
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,
|
||||
|
||||
output=if (read) Some(PolarizedPort(name=realPrefix + "dout", polarity=ActiveHigh)) else None,
|
||||
input=if (write) Some(PolarizedPort(name=realPrefix + "din", 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,
|
||||
|
||||
maskPort=maskGran match {
|
||||
case Some(x:Int) => Some(PolarizedPort(name=realPrefix + "mask", polarity=ActiveHigh))
|
||||
maskPort = maskGran match {
|
||||
case Some(x: Int) => Some(PolarizedPort(name = realPrefix + "mask", polarity = ActiveHigh))
|
||||
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.
|
||||
def generateReadPort(prefix: String, width: Int, depth: Int, readEnable: Boolean = false): MacroPort = {
|
||||
generateTestPort(prefix, width, depth, write=false, read=true, readEnable=readEnable)
|
||||
def generateReadPort(prefix: String, width: Option[Int], depth: Option[Int], readEnable: Boolean = false): MacroPort = {
|
||||
generateTestPort(prefix, width, depth, write = false, read = true, readEnable = readEnable)
|
||||
}
|
||||
|
||||
// Generate a write port for testing.
|
||||
def generateWritePort(prefix: String, width: Int, depth: Int, maskGran: Option[Int] = None, writeEnable: Boolean = true): MacroPort = {
|
||||
generateTestPort(prefix, width, depth, maskGran=maskGran, write=true, read=false, writeEnable=writeEnable)
|
||||
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)
|
||||
}
|
||||
|
||||
// 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(
|
||||
prefix, width, depth, maskGran=maskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=false
|
||||
prefix, width, depth, maskGran = maskGran,
|
||||
write = true, writeEnable = true,
|
||||
read = true, readEnable = false
|
||||
)
|
||||
}
|
||||
|
||||
// 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 = {
|
||||
SRAMMacro(
|
||||
name=name,
|
||||
width=width,
|
||||
depth=depth,
|
||||
family="1rw",
|
||||
ports=Seq(generateReadWritePort(prefix, width, depth, maskGran)),
|
||||
extraPorts=extraPorts
|
||||
name = name,
|
||||
width = width,
|
||||
depth = depth,
|
||||
family = "1rw",
|
||||
ports = Seq(generateReadWritePort(prefix, width, depth, maskGran)),
|
||||
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.
|
||||
@@ -192,6 +210,7 @@ trait HasSimpleTestGenerator {
|
||||
// Override these with "override lazy val".
|
||||
// Why lazy? These are used in the constructor here so overriding non-lazily
|
||||
// would be too late.
|
||||
def useCompiler: Boolean = false
|
||||
def memWidth: Int
|
||||
def libWidth: Int
|
||||
def memDepth: Int
|
||||
@@ -224,10 +243,10 @@ trait HasSimpleTestGenerator {
|
||||
val lib = s"lib-${generatorType}${extraTagPrefixed}.json"
|
||||
val v = s"${generatorType}${extraTagPrefixed}.v"
|
||||
|
||||
val mem_name = "target_memory"
|
||||
lazy val mem_name = "target_memory"
|
||||
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)
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
val lastWidthBits = if (memWidth % usableLibWidth == 0) usableLibWidth else (memWidth % usableLibWidth)
|
||||
val selectBits = mem_addr_width - lib_addr_width
|
||||
lazy val lastWidthBits = if (memWidth % usableLibWidth == 0) usableLibWidth else (memWidth % usableLibWidth)
|
||||
lazy val selectBits = mem_addr_width - lib_addr_width
|
||||
|
||||
/**
|
||||
* 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.
|
||||
override def generateBody = ""
|
||||
}
|
||||
|
||||
|
||||
22
macros/src/test/scala/SRAMCompiler.scala
Normal file
22
macros/src/test/scala/SRAMCompiler.scala
Normal 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)
|
||||
}
|
||||
@@ -20,27 +20,27 @@ trait HasSimpleDepthTestGenerator extends HasSimpleTestGenerator {
|
||||
if (selectBits > 0) {
|
||||
output.append (
|
||||
s"""
|
||||
node outer_addr_sel = bits(outer_addr, ${mem_addr_width - 1}, $lib_addr_width)
|
||||
reg outer_addr_sel_reg : UInt<${selectBits}>, outer_clk with :
|
||||
reset => (UInt<1>("h0"), outer_addr_sel_reg)
|
||||
outer_addr_sel_reg <= mux(UInt<1>("h1"), outer_addr_sel, outer_addr_sel_reg)
|
||||
node ${memPortPrefix}_addr_sel = bits(${memPortPrefix}_addr, ${mem_addr_width - 1}, $lib_addr_width)
|
||||
reg ${memPortPrefix}_addr_sel_reg : UInt<${selectBits}>, ${memPortPrefix}_clk with :
|
||||
reset => (UInt<1>("h0"), ${memPortPrefix}_addr_sel_reg)
|
||||
${memPortPrefix}_addr_sel_reg <= mux(UInt<1>("h1"), ${memPortPrefix}_addr_sel, ${memPortPrefix}_addr_sel_reg)
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
for (i <- 0 to depthInstances - 1) {
|
||||
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(
|
||||
s"""
|
||||
inst mem_${i}_0 of awesome_lib_mem
|
||||
mem_${i}_0.lib_clk <= outer_clk
|
||||
mem_${i}_0.lib_addr <= outer_addr
|
||||
node outer_dout_${i}_0 = bits(mem_${i}_0.lib_dout, ${width - 1}, 0)
|
||||
mem_${i}_0.lib_din <= bits(outer_din, ${width - 1}, 0)
|
||||
inst mem_${i}_0 of ${lib_name}
|
||||
mem_${i}_0.${libPortPrefix}_clk <= ${memPortPrefix}_clk
|
||||
mem_${i}_0.${libPortPrefix}_addr <= ${memPortPrefix}_addr
|
||||
node ${memPortPrefix}_dout_${i}_0 = bits(mem_${i}_0.${libPortPrefix}_dout, ${width - 1}, 0)
|
||||
mem_${i}_0.${libPortPrefix}_din <= bits(${memPortPrefix}_din, ${width - 1}, 0)
|
||||
${maskStatement}
|
||||
mem_${i}_0.lib_write_en <= and(and(outer_write_en, UInt<1>("h1")), ${enableIdentifier})
|
||||
node outer_dout_${i} = outer_dout_${i}_0
|
||||
mem_${i}_0.${libPortPrefix}_write_en <= and(and(${memPortPrefix}_write_en, UInt<1>("h1")), ${enableIdentifier})
|
||||
node ${memPortPrefix}_dout_${i} = ${memPortPrefix}_dout_${i}_0
|
||||
"""
|
||||
)
|
||||
}
|
||||
@@ -48,16 +48,16 @@ s"""
|
||||
if (i > depthInstances - 1) {
|
||||
"UInt<1>(\"h0\")"
|
||||
} 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
output append " outer_dout <= "
|
||||
output append s" ${memPortPrefix}_dout <= "
|
||||
if (selectBits > 0) {
|
||||
output append generate_outer_dout_tree(0, depthInstances)
|
||||
} 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
|
||||
|
||||
@@ -40,28 +40,28 @@ trait HasSimpleWidthTestGenerator extends HasSimpleTestGenerator {
|
||||
} else """UInt<1>("h1")"""
|
||||
|
||||
s"""
|
||||
mem_0_${i}.lib_clk <= outer_clk
|
||||
mem_0_${i}.lib_addr <= outer_addr
|
||||
node outer_dout_0_${i} = bits(mem_0_${i}.lib_dout, ${myMemWidth - 1}, 0)
|
||||
mem_0_${i}.lib_din <= bits(outer_din, ${myBaseBit + myMemWidth - 1}, ${myBaseBit})
|
||||
mem_0_${i}.${libPortPrefix}_clk <= ${memPortPrefix}_clk
|
||||
mem_0_${i}.${libPortPrefix}_addr <= ${memPortPrefix}_addr
|
||||
node ${memPortPrefix}_dout_0_${i} = bits(mem_0_${i}.${libPortPrefix}_dout, ${myMemWidth - 1}, 0)
|
||||
mem_0_${i}.${libPortPrefix}_din <= bits(${memPortPrefix}_din, ${myBaseBit + myMemWidth - 1}, ${myBaseBit})
|
||||
${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(_ + _)
|
||||
|
||||
// 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))
|
||||
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)")
|
||||
s"""
|
||||
node outer_dout_0 = ${catStmt}
|
||||
node ${memPortPrefix}_dout_0 = ${catStmt}
|
||||
"""
|
||||
}
|
||||
|
||||
output append
|
||||
"""
|
||||
outer_dout <= mux(UInt<1>("h1"), outer_dout_0, UInt<1>("h0"))
|
||||
s"""
|
||||
${memPortPrefix}_dout <= mux(UInt<1>("h1"), ${memPortPrefix}_dout_0, UInt<1>("h0"))
|
||||
"""
|
||||
output.toString
|
||||
}
|
||||
@@ -398,7 +398,7 @@ class SplitWidth1024x32_readEnable_Lib extends MacroCompilerSpec with HasSRAMGen
|
||||
depth=libDepth,
|
||||
family="1rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"lib", libWidth, libDepth, maskGran=libMaskGran,
|
||||
"lib", Some(libWidth), Some(libDepth), maskGran=libMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
@@ -456,7 +456,7 @@ class SplitWidth1024x32_readEnable_Mem extends MacroCompilerSpec with HasSRAMGen
|
||||
depth=memDepth,
|
||||
family="1rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"outer", memWidth, memDepth, maskGran=memMaskGran,
|
||||
"outer", Some(memWidth), Some(memDepth), maskGran=memMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
@@ -482,7 +482,7 @@ class SplitWidth1024x32_readEnable_LibMem extends MacroCompilerSpec with HasSRAM
|
||||
depth=libDepth,
|
||||
family="1rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"lib", libWidth, libDepth, maskGran=libMaskGran,
|
||||
"lib", Some(libWidth), Some(libDepth), maskGran=libMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
@@ -496,7 +496,7 @@ class SplitWidth1024x32_readEnable_LibMem extends MacroCompilerSpec with HasSRAM
|
||||
depth=memDepth,
|
||||
family="1rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"outer", memWidth, memDepth, maskGran=memMaskGran,
|
||||
"outer", Some(memWidth), Some(memDepth), maskGran=memMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
|
||||
2
mdf
2
mdf
Submodule mdf updated: 2b5f3c16da...2bc5a363e2
Reference in New Issue
Block a user