Add strict mode

This commit is contained in:
Edward Wang
2017-08-09 04:08:03 -07:00
committed by edwardcwang
parent 11bd81165b
commit 13d8a0f8f5
2 changed files with 64 additions and 26 deletions

View File

@@ -19,15 +19,35 @@ import scala.collection.mutable.{ArrayBuffer, HashMap}
import java.io.{File, FileWriter} import java.io.{File, FileWriter}
import Utils._ import Utils._
case class MacroCompilerException(msg: String) extends Exception(msg)
object MacroCompilerAnnotation { object MacroCompilerAnnotation {
/** Macro compiler mode. */
sealed trait CompilerMode
/** Strict mode - must compile all memories or error out. */
case object Strict extends CompilerMode
/** Synflops mode - compile all memories with synflops (do not map to lib at all). */
case object Synflops extends CompilerMode
/** FallbackSynflops - compile all memories to SRAM when possible and fall back to synflops if a memory fails. **/
case object FallbackSynflops extends CompilerMode
/** Default mode - compile what is possible and do nothing with uncompiled memories. **/
case object Default extends CompilerMode
def stringToCompilerMode(str: String): CompilerMode = (str: @unchecked) match {
case "strict" => Strict
case "synflops" => Synflops
case "fallbacksynflops" => FallbackSynflops
case "default" => Default
case _ => throw new IllegalArgumentException("No such compiler mode " + str)
}
/** /**
* Parameters associated to this MacroCompilerAnnotation. * Parameters associated to this MacroCompilerAnnotation.
* @param mem Path to memory lib * @param mem Path to memory lib
* @param lib Path to library lib or None if no libraries * @param lib Path to library lib or None if no libraries
* @param costMetric Cost metric to use * @param costMetric Cost metric to use
* @param synflops True to syn flops * @param mode Compiler mode (see CompilerMode)
*/ */
case class Params(mem: String, lib: Option[String], costMetric: CostMetric, synflops: Boolean) case class Params(mem: String, lib: Option[String], costMetric: CostMetric, mode: CompilerMode)
/** /**
* Create a MacroCompilerAnnotation. * Create a MacroCompilerAnnotation.
@@ -48,7 +68,8 @@ object MacroCompilerAnnotation {
class MacroCompilerPass(mems: Option[Seq[Macro]], class MacroCompilerPass(mems: Option[Seq[Macro]],
libs: Option[Seq[Macro]], libs: Option[Seq[Macro]],
costMetric: CostMetric = CostMetric.default) extends firrtl.passes.Pass { costMetric: CostMetric = CostMetric.default,
mode: MacroCompilerAnnotation.CompilerMode = MacroCompilerAnnotation.Default) extends firrtl.passes.Pass {
def compile(mem: Macro, lib: Macro): Option[(Module, ExtModule)] = { def compile(mem: Macro, lib: Macro): Option[(Module, ExtModule)] = {
val pairedPorts = mem.sortedPorts zip lib.sortedPorts val pairedPorts = mem.sortedPorts zip lib.sortedPorts
@@ -441,10 +462,11 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
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}")
if (newCost > cost) (best, cost) // Try compiling
else compile(mem, lib) match { compile(mem, lib) match {
case None => (best, cost) // If it was successful and the new cost is lower
case Some(p) => (Some(p), newCost) case Some(p) if (newCost < cost) => (Some(p), newCost)
case _ => (best, cost)
} }
} }
case _ => (best, cost) // Cost function rejected this combination. case _ => (best, cost) // Cost function rejected this combination.
@@ -455,7 +477,12 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
// in the modules list with a compiled version, as well as the extmodule // in the modules list with a compiled version, as well as the extmodule
// stub for the lib. // stub for the lib.
best match { best match {
case None => modules case None => {
if (mode == MacroCompilerAnnotation.Strict)
throw new MacroCompilerException(s"Target memory ${mem.src.name} could not be compiled and strict mode is activated - aborting.")
else
modules
}
case Some((mod, bb)) => case Some((mod, bb)) =>
(modules filterNot (m => m.name == mod.name || m.name == bb.name)) ++ Seq(mod, bb) (modules filterNot (m => m.name == mod.name || m.name == bb.name)) ++ Seq(mod, bb)
} }
@@ -470,7 +497,10 @@ 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, synflops))) => case Seq(MacroCompilerAnnotation(state.circuit.main, MacroCompilerAnnotation.Params(memFile, libFile, costMetric, mode))) =>
if (mode == MacroCompilerAnnotation.FallbackSynflops) {
throw new UnsupportedOperationException("Not implemented yet")
}
// Read, eliminate None, get only SRAM, make firrtl macro // Read, eliminate None, get only SRAM, make firrtl macro
val mems: Option[Seq[Macro]] = mdf.macrolib.Utils.readMDFFromPath(Some(memFile)) match { val mems: Option[Seq[Macro]] = mdf.macrolib.Utils.readMDFFromPath(Some(memFile)) match {
case Some(x:Seq[mdf.macrolib.Macro]) => case Some(x:Seq[mdf.macrolib.Macro]) =>
@@ -483,8 +513,8 @@ class MacroCompilerTransform extends Transform {
case _ => None case _ => None
} }
val transforms = Seq( val transforms = Seq(
new MacroCompilerPass(mems, libs, costMetric), new MacroCompilerPass(mems, libs, costMetric, mode),
new SynFlopsPass(synflops, libs getOrElse mems.get)) new SynFlopsPass(mode == MacroCompilerAnnotation.Synflops, libs getOrElse mems.get))
(transforms foldLeft state)((s, xform) => xform runTransform s).copy(form=outputForm) (transforms foldLeft state)((s, xform) => xform runTransform s).copy(form=outputForm)
case _ => state case _ => state
} }
@@ -518,6 +548,7 @@ object MacroCompiler extends App {
case object Library extends MacroParam case object Library extends MacroParam
case object Verilog extends MacroParam case object Verilog extends MacroParam
case object CostFunc extends MacroParam case object CostFunc extends MacroParam
case object Mode 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(
@@ -527,23 +558,28 @@ object MacroCompiler extends App {
" -v, --verilog: Verilog output", " -v, --verilog: Verilog output",
" -c, --cost-func: Cost function to use. Optional (default: \"default\")", " -c, --cost-func: Cost function to use. Optional (default: \"default\")",
" -cp, --cost-param: Cost function parameter. (Optional depending on the cost function.). e.g. -c ExternalMetric -cp path /path/to/my/cost/script", " -cp, --cost-param: Cost function parameter. (Optional depending on the cost function.). e.g. -c ExternalMetric -cp path /path/to/my/cost/script",
" --syn-flops: Produces synthesizable flop-based memories (for all memories and library memory macros); likely useful for simulation purposes") mkString "\n" """ --mode:
| synflops: Produces synthesizable flop-based memories (for all memories and library memory macros); likely useful for simulation purposes")
| fallbacksynflops: Compile all memories to library when possible and fall back to synthesizable flop-based memories when library synth is not possible
| strict: Compile all memories to library or return an error
| default: Compile all memories to library when possible and do nothing in case of errors.
""".stripMargin) mkString "\n"
def parseArgs(map: MacroParamMap, costMap: CostParamMap, synflops: Boolean, args: List[String]): (MacroParamMap, CostParamMap, Boolean) = def parseArgs(map: MacroParamMap, costMap: CostParamMap, args: List[String]): (MacroParamMap, CostParamMap) =
args match { args match {
case Nil => (map, costMap, synflops) case Nil => (map, costMap)
case ("-m" | "--macro-list") :: value :: tail => case ("-m" | "--macro-list") :: value :: tail =>
parseArgs(map + (Macros -> value), costMap, synflops, tail) parseArgs(map + (Macros -> value), costMap, tail)
case ("-l" | "--library") :: value :: tail => case ("-l" | "--library") :: value :: tail =>
parseArgs(map + (Library -> value), costMap, synflops, tail) parseArgs(map + (Library -> value), costMap, tail)
case ("-v" | "--verilog") :: value :: tail => case ("-v" | "--verilog") :: value :: tail =>
parseArgs(map + (Verilog -> value), costMap, synflops, tail) parseArgs(map + (Verilog -> value), costMap, tail)
case ("-c" | "--cost-func") :: value :: tail => case ("-c" | "--cost-func") :: value :: tail =>
parseArgs(map + (CostFunc -> value), costMap, synflops, tail) parseArgs(map + (CostFunc -> value), costMap, tail)
case ("-cp" | "--cost-param") :: value1 :: value2 :: tail => case ("-cp" | "--cost-param") :: value1 :: value2 :: tail =>
parseArgs(map, costMap + (value1 -> value2), synflops, tail) parseArgs(map, costMap + (value1 -> value2), tail)
case "--syn-flops" :: tail => case "--mode" :: value :: tail =>
parseArgs(map, costMap, true, tail) parseArgs(map + (Mode -> value), costMap, tail)
case arg :: tail => case arg :: tail =>
println(s"Unknown field $arg\n") println(s"Unknown field $arg\n")
println(usage) println(usage)
@@ -551,7 +587,7 @@ object MacroCompiler extends App {
} }
def run(args: List[String]) { def run(args: List[String]) {
val (params, costParams, synflops) = parseArgs(Map[MacroParam, String](), Map[String, String](), false, args) val (params, costParams) = parseArgs(Map[MacroParam, String](), Map[String, String](), args)
try { try {
val macros = Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(params.get(Macros))).get map (x => (new Macro(x)).blackbox) val macros = Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(params.get(Macros))).get map (x => (new Macro(x)).blackbox)
@@ -568,7 +604,7 @@ 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),
synflops MacroCompilerAnnotation.stringToCompilerMode(params.getOrElse(Mode, "default"))
) )
)) ))
) )
@@ -583,11 +619,13 @@ object MacroCompiler extends App {
// Close the writer. // Close the writer.
verilogWriter.close() verilogWriter.close()
} catch { } catch {
case e: java.util.NoSuchElementException => case e: java.util.NoSuchElementException =>
println(usage) println(usage)
sys.exit(1) sys.exit(1)
case e: MacroCompilerException =>
System.err.println(e.getMessage)
sys.exit(1)
case e: Throwable => case e: Throwable =>
throw e throw e
} }

View File

@@ -36,7 +36,7 @@ abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalate
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("--syn-flops") else Nil) (if (synflops) List("--mode", "synflops") 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
@@ -98,7 +98,7 @@ abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalate
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)
val passes = Seq( val passes = Seq(
new MacroCompilerPass(Some(mems), libs, getCostMetric), new MacroCompilerPass(Some(mems), libs, getCostMetric, if (synflops) MacroCompilerAnnotation.Synflops else MacroCompilerAnnotation.Default),
new SynFlopsPass(synflops, libs getOrElse mems), new SynFlopsPass(synflops, libs getOrElse mems),
RemoveEmpty) RemoveEmpty)
val result: Circuit = (passes foldLeft circuit)((c, pass) => pass run c) val result: Circuit = (passes foldLeft circuit)((c, pass) => pass run c)