Emit hammer IR from MacroCompiler (#50)

This commit is contained in:
Colin Schmidt
2019-03-25 22:52:39 -07:00
committed by edwardcwang
parent fdad525007
commit affd033f0a
4 changed files with 44 additions and 12 deletions

View File

@@ -18,6 +18,7 @@ import firrtl.CompilerUtils.getLoweringTransforms
import mdf.macrolib.{PolarizedPort, PortPolarity, SRAMMacro, SRAMGroup, SRAMCompiler} import mdf.macrolib.{PolarizedPort, PortPolarity, SRAMMacro, SRAMGroup, SRAMCompiler}
import scala.collection.mutable.{ArrayBuffer, HashMap} import scala.collection.mutable.{ArrayBuffer, HashMap}
import java.io.{File, FileWriter} import java.io.{File, FileWriter}
import scala.io.{Source}
import Utils._ import Utils._
case class MacroCompilerException(msg: String) extends Exception(msg) case class MacroCompilerException(msg: String) extends Exception(msg)
@@ -76,12 +77,14 @@ object MacroCompilerAnnotation {
* @param mem Path to memory lib * @param mem Path to memory lib
* @param memFormat Type of memory lib (Some("conf"), Some("mdf"), or None (defaults to mdf)) * @param memFormat Type of memory lib (Some("conf"), Some("mdf"), or None (defaults to mdf))
* @param lib Path to library lib or None if no libraries * @param lib Path to library lib or None if no libraries
* @param hammerIR Path to HammerIR output or None (not generated in this case)
* @param costMetric Cost metric to use * @param costMetric Cost metric to use
* @param mode Compiler mode (see CompilerMode) * @param mode Compiler mode (see CompilerMode)
* @param forceCompile Set of memories to force compiling to lib regardless of the mode * @param forceCompile Set of memories to force compiling to lib regardless of the mode
* @param forceSynflops Set of memories to force compiling as flops regardless of the mode * @param forceSynflops Set of memories to force compiling as flops regardless of the mode
*/ */
case class Params(mem: String, memFormat: Option[String], lib: Option[String], costMetric: CostMetric, mode: CompilerMode, useCompiler: Boolean, case class Params(mem: String, memFormat: Option[String], lib: Option[String], hammerIR: Option[String],
costMetric: CostMetric, mode: CompilerMode, useCompiler: Boolean,
forceCompile: Set[String], forceSynflops: Set[String]) forceCompile: Set[String], forceSynflops: Set[String])
/** /**
@@ -104,6 +107,7 @@ object MacroCompilerAnnotation {
class MacroCompilerPass(mems: Option[Seq[Macro]], class MacroCompilerPass(mems: Option[Seq[Macro]],
libs: Option[Seq[Macro]], libs: Option[Seq[Macro]],
compilers: Option[SRAMCompiler], compilers: Option[SRAMCompiler],
hammerIR: Option[String],
costMetric: CostMetric = CostMetric.default, costMetric: CostMetric = CostMetric.default,
mode: MacroCompilerAnnotation.CompilerMode = MacroCompilerAnnotation.Default) extends firrtl.passes.Pass { mode: MacroCompilerAnnotation.CompilerMode = MacroCompilerAnnotation.Default) extends firrtl.passes.Pass {
// Helper function to check the legality of bitPairs. // Helper function to check the legality of bitPairs.
@@ -262,7 +266,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
bitPairs.toSeq bitPairs.toSeq
} }
def compile(mem: Macro, lib: Macro): Option[(Module, ExtModule)] = { def compile(mem: Macro, lib: Macro): Option[(Module, Macro)] = {
assert(mem.sortedPorts.lengthCompare(lib.sortedPorts.length) == 0, assert(mem.sortedPorts.lengthCompare(lib.sortedPorts.length) == 0,
"mem and lib should have an equal number of ports") "mem and lib should have an equal number of ports")
val pairedPorts = mem.sortedPorts zip lib.sortedPorts val pairedPorts = mem.sortedPorts zip lib.sortedPorts
@@ -555,10 +559,11 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
} }
} }
Some((mem.module(Block(stmts.toSeq)), lib.blackbox)) Some((mem.module(Block(stmts.toSeq)), lib))
} }
def run(c: Circuit): Circuit = { def run(c: Circuit): Circuit = {
var firstLib = true
val modules = (mems, libs) match { val modules = (mems, libs) match {
case (Some(mems), Some(libs)) => case (Some(mems), Some(libs)) =>
// Try to compile each of the memories in mems. // Try to compile each of the memories in mems.
@@ -590,7 +595,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
// Try to compile mem against each lib in libs, keeping track of the // Try to compile mem against each lib in libs, keeping track of the
// best compiled version, external lib used, and cost. // best compiled version, external lib used, and cost.
val (best, cost) = (fullLibs foldLeft (None: Option[(Module, ExtModule)], Double.MaxValue)){ val (best, cost) = (fullLibs foldLeft (None: Option[(Module, Macro)], Double.MaxValue)){
case ((best, cost), lib) if mem.src.ports.size != lib.src.ports.size => case ((best, cost), lib) if mem.src.ports.size != lib.src.ports.size =>
/* Palmer: FIXME: This just assumes the Chisel and vendor ports are in the same /* Palmer: FIXME: This just assumes the Chisel and vendor ports are in the same
* order, but I'm starting with what actually gets generated. */ * order, but I'm starting with what actually gets generated. */
@@ -623,7 +628,18 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
modules modules
} }
case Some((mod, bb)) => case Some((mod, bb)) =>
(modules filterNot (m => m.name == mod.name || m.name == bb.name)) ++ Seq(mod, bb) hammerIR match {
case Some(f) => {
val hammerIRWriter = new FileWriter(new File(f), !firstLib)
if(firstLib) hammerIRWriter.write("[\n")
hammerIRWriter.write(bb.src.toJSON().toString())
hammerIRWriter.write("\n,\n")
hammerIRWriter.close()
firstLib = false
}
case None =>
}
(modules filterNot (m => m.name == mod.name || m.name == bb.blackbox.name)) ++ Seq(mod, bb.blackbox)
} }
} }
case _ => c.modules case _ => c.modules
@@ -638,7 +654,7 @@ class MacroCompilerTransform extends Transform {
def execute(state: CircuitState) = getMyAnnotations(state) match { def execute(state: CircuitState) = getMyAnnotations(state) match {
case Seq(MacroCompilerAnnotation(state.circuit.main, case Seq(MacroCompilerAnnotation(state.circuit.main,
MacroCompilerAnnotation.Params(memFile, memFileFormat, libFile, costMetric, mode, useCompiler, forceCompile, forceSynflops))) => MacroCompilerAnnotation.Params(memFile, memFileFormat, libFile, hammerIR, costMetric, mode, useCompiler, forceCompile, forceSynflops))) =>
if (mode == MacroCompilerAnnotation.FallbackSynflops) { if (mode == MacroCompilerAnnotation.FallbackSynflops) {
throw new UnsupportedOperationException("Not implemented yet") throw new UnsupportedOperationException("Not implemented yet")
} }
@@ -686,7 +702,7 @@ class MacroCompilerTransform extends Transform {
}.getOrElse(Seq.empty) }.getOrElse(Seq.empty)
val transforms = Seq( val transforms = Seq(
new MacroCompilerPass(memCompile, libs, compilers, costMetric, mode), new MacroCompilerPass(memCompile, libs, compilers, hammerIR, costMetric, mode),
new SynFlopsPass(true, memSynflops ++ (if (mode == MacroCompilerAnnotation.CompileAndSynflops) { new SynFlopsPass(true, memSynflops ++ (if (mode == MacroCompilerAnnotation.CompileAndSynflops) {
libs.get libs.get
} else { } else {
@@ -729,6 +745,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 Firrtl extends MacroParam case object Firrtl extends MacroParam
case object HammerIR 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 case object UseCompiler extends MacroParam
@@ -746,6 +763,7 @@ object MacroCompiler extends App {
" -u, --use-compiler: Flag, whether to use the memory compiler defined in library", " -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)",
" -hir, --hammer-ir: Hammer-IR output currently only needed for IP compilers",
" -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",
" --force-compile [mem]: Force the given memory to be compiled to target libs regardless of the mode", " --force-compile [mem]: Force the given memory to be compiled to target libs regardless of the mode",
@@ -769,6 +787,8 @@ object MacroCompiler extends App {
parseArgs(map + (Verilog -> value), costMap, forcedMemories, tail) parseArgs(map + (Verilog -> value), costMap, forcedMemories, tail)
case ("-f" | "--firrtl") :: value :: tail => case ("-f" | "--firrtl") :: value :: tail =>
parseArgs(map + (Firrtl -> value), costMap, forcedMemories, tail) parseArgs(map + (Firrtl -> value), costMap, forcedMemories, tail)
case ("-hir" | "--hammer-ir") :: value :: tail =>
parseArgs(map + (HammerIR -> value), costMap, forcedMemories, tail)
case ("-c" | "--cost-func") :: value :: tail => case ("-c" | "--cost-func") :: value :: tail =>
parseArgs(map + (CostFunc -> value), costMap, forcedMemories, tail) parseArgs(map + (CostFunc -> value), costMap, forcedMemories, tail)
case ("-cp" | "--cost-param") :: value1 :: value2 :: tail => case ("-cp" | "--cost-param") :: value1 :: value2 :: tail =>
@@ -802,6 +822,7 @@ object MacroCompiler extends App {
circuit.main, circuit.main,
MacroCompilerAnnotation.Params( MacroCompilerAnnotation.Params(
params.get(Macros).get, params.get(MacrosFormat), params.get(Library), params.get(Macros).get, params.get(MacrosFormat), params.get(Library),
params.get(HammerIR),
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), params.contains(UseCompiler),
@@ -839,6 +860,17 @@ object MacroCompiler extends App {
} }
case None => case None =>
} }
params.get(HammerIR) match {
case Some(hammerIRFile: String) => {
val lines = Source.fromFile(hammerIRFile).getLines().toList
val hammerIRWriter = new FileWriter(new File(hammerIRFile))
// JSON means we need to destroy the last comma :(
lines.dropRight(1).foreach(l => hammerIRWriter.write(l + "\n"))
hammerIRWriter.write("]\n")
hammerIRWriter.close()
}
case None =>
}
} else { } else {
// Warn user // Warn user
System.err println "WARNING: Empty *.mems.conf file. No memories generated." System.err println "WARNING: Empty *.mems.conf file. No memories generated."

View File

@@ -78,7 +78,7 @@ object Utils {
} }
def readConfFromString(str: String): Seq[mdf.macrolib.Macro] = { def readConfFromString(str: String): Seq[mdf.macrolib.Macro] = {
MemConf.fromString(str).map { m:MemConf => MemConf.fromString(str).map { m:MemConf =>
SRAMMacro(m.name, m.width, m.depth, Utils.portSpecToFamily(m.ports), Utils.portSpecToMacroPort(m.width, m.depth, m.maskGranularity, m.ports), Seq.empty[MacroExtraPort]) SRAMMacro(m.name, m.width, m.depth, Utils.portSpecToFamily(m.ports), Utils.portSpecToMacroPort(m.width, m.depth, m.maskGranularity, m.ports))
} }
} }
def portSpecToFamily(ports: Seq[MemPort]): String = { def portSpecToFamily(ports: Seq[MemPort]): String = {
@@ -167,10 +167,10 @@ object Utils {
} }
def buildSRAMMacros(s: mdf.macrolib.SRAMCompiler): Seq[mdf.macrolib.SRAMMacro] = { def buildSRAMMacros(s: mdf.macrolib.SRAMCompiler): Seq[mdf.macrolib.SRAMMacro] = {
for (g <- s.groups; d <- g.depth; w <- g.width; vt <- g.vt) 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) yield mdf.macrolib.SRAMMacro(makeName(g, d, w, vt), w, d, g.family, g.ports.map(_.copy(width=Some(w), depth=Some(d))), vt, g.mux, g.extraPorts)
} }
def buildSRAMMacro(g: mdf.macrolib.SRAMGroup, d: Int, w: Int, vt: String): mdf.macrolib.SRAMMacro = { def buildSRAMMacro(g: mdf.macrolib.SRAMGroup, d: Int, w: Int, vt: String): mdf.macrolib.SRAMMacro = {
return mdf.macrolib.SRAMMacro(makeName(g, d, w, vt), w, d, g.family, g.ports.map(_.copy(width=Some(w), depth=Some(d))), g.extraPorts) return mdf.macrolib.SRAMMacro(makeName(g, d, w, vt), w, d, g.family, g.ports.map(_.copy(width=Some(w), depth=Some(d))), vt, g.mux, g.extraPorts)
} }
def makeName(g: mdf.macrolib.SRAMGroup, depth: Int, width: Int, vt: String): String = { def makeName(g: mdf.macrolib.SRAMGroup, depth: Int, width: Int, vt: String): String = {
g.name.foldLeft(""){ (builder, next) => g.name.foldLeft(""){ (builder, next) =>

View File

@@ -99,7 +99,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, None, getCostMetric, if (synflops) MacroCompilerAnnotation.Synflops else MacroCompilerAnnotation.Default), new MacroCompilerPass(Some(mems), libs, None, None, 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)

2
mdf

Submodule mdf updated: e9befe89eb...94839b30ba