Address review comments
This commit is contained in:
@@ -21,7 +21,7 @@ trait CostMetric extends Serializable {
|
|||||||
def cost(mem: Macro, lib: Macro): Option[BigInt]
|
def cost(mem: Macro, lib: Macro): Option[BigInt]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to return the map of argments (or an empty map if there are none).
|
* Helper function to return the map of arguments (or an empty map if there are none).
|
||||||
*/
|
*/
|
||||||
def commandLineParams(): Map[String, String]
|
def commandLineParams(): Map[String, String]
|
||||||
|
|
||||||
@@ -39,8 +39,10 @@ trait CostMetricCompanion {
|
|||||||
|
|
||||||
// Some default cost functions.
|
// Some default cost functions.
|
||||||
|
|
||||||
/** Palmer's old metric. */
|
/** Palmer's old metric.
|
||||||
object PalmerMetric extends CostMetric with CostMetricCompanion {
|
* TODO: figure out what is the difference between this metric and the current
|
||||||
|
* default metric and either revive or delete this metric. */
|
||||||
|
object OldMetric extends CostMetric with CostMetricCompanion {
|
||||||
override def cost(mem: Macro, lib: Macro): Option[BigInt] = {
|
override def cost(mem: Macro, lib: Macro): Option[BigInt] = {
|
||||||
/* Palmer: A quick cost function (that must be kept in sync with
|
/* Palmer: A quick cost function (that must be kept in sync with
|
||||||
* memory_cost()) that attempts to avoid compiling unncessary
|
* memory_cost()) that attempts to avoid compiling unncessary
|
||||||
@@ -52,15 +54,15 @@ object PalmerMetric extends CostMetric with CostMetricCompanion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def commandLineParams = Map()
|
override def commandLineParams = Map()
|
||||||
override def name = "PalmerMetric"
|
override def name = "OldMetric"
|
||||||
override def construct(m: Map[String, String]) = PalmerMetric
|
override def construct(m: Map[String, String]) = OldMetric
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An external cost function.
|
* An external cost function.
|
||||||
* Calls the specified path with paths to the JSON MDF representation of the mem
|
* Calls the specified path with paths to the JSON MDF representation of the mem
|
||||||
* and lib macros. The external executable should return a BigInt.
|
* and lib macros. The external executable should print a BigInt.
|
||||||
* None will be returned if the external executable does not return a valid
|
* None will be returned if the external executable does not print a valid
|
||||||
* BigInt.
|
* BigInt.
|
||||||
*/
|
*/
|
||||||
class ExternalMetric(path: String) extends CostMetric {
|
class ExternalMetric(path: String) extends CostMetric {
|
||||||
@@ -110,7 +112,7 @@ object ExternalMetric extends CostMetricCompanion {
|
|||||||
|
|
||||||
/** The current default metric in barstools, re-defined by Donggyu. */
|
/** The current default metric in barstools, re-defined by Donggyu. */
|
||||||
// TODO: write tests for this function to make sure it selects the right things
|
// TODO: write tests for this function to make sure it selects the right things
|
||||||
object NewDefaultMetric extends CostMetric with CostMetricCompanion {
|
object DefaultMetric extends CostMetric with CostMetricCompanion {
|
||||||
override def cost(mem: Macro, lib: Macro): Option[BigInt] = {
|
override def cost(mem: Macro, lib: Macro): Option[BigInt] = {
|
||||||
val memMask = mem.src.ports map (_.maskGran) find (_.isDefined) map (_.get)
|
val memMask = mem.src.ports map (_.maskGran) find (_.isDefined) map (_.get)
|
||||||
val libMask = lib.src.ports map (_.maskGran) find (_.isDefined) map (_.get)
|
val libMask = lib.src.ports map (_.maskGran) find (_.isDefined) map (_.get)
|
||||||
@@ -126,8 +128,8 @@ object NewDefaultMetric extends CostMetric with CostMetricCompanion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def commandLineParams = Map()
|
override def commandLineParams = Map()
|
||||||
override def name = "NewDefaultMetric"
|
override def name = "DefaultMetric"
|
||||||
override def construct(m: Map[String, String]) = NewDefaultMetric
|
override def construct(m: Map[String, String]) = DefaultMetric
|
||||||
}
|
}
|
||||||
|
|
||||||
object MacroCompilerUtil {
|
object MacroCompilerUtil {
|
||||||
@@ -158,14 +160,14 @@ object MacroCompilerUtil {
|
|||||||
|
|
||||||
object CostMetric {
|
object CostMetric {
|
||||||
/** Define some default metric. */
|
/** Define some default metric. */
|
||||||
val default: CostMetric = NewDefaultMetric
|
val default: CostMetric = DefaultMetric
|
||||||
|
|
||||||
val costMetricCreators: scala.collection.mutable.Map[String, CostMetricCompanion] = scala.collection.mutable.Map()
|
val costMetricCreators: scala.collection.mutable.Map[String, CostMetricCompanion] = scala.collection.mutable.Map()
|
||||||
|
|
||||||
// Register some default metrics
|
// Register some default metrics
|
||||||
registerCostMetric(PalmerMetric)
|
registerCostMetric(OldMetric)
|
||||||
registerCostMetric(ExternalMetric)
|
registerCostMetric(ExternalMetric)
|
||||||
registerCostMetric(NewDefaultMetric)
|
registerCostMetric(DefaultMetric)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a cost metric.
|
* Register a cost metric.
|
||||||
|
|||||||
@@ -21,6 +21,17 @@ import Utils._
|
|||||||
|
|
||||||
case class MacroCompilerException(msg: String) extends Exception(msg)
|
case class MacroCompilerException(msg: String) extends Exception(msg)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The MacroCompilerAnnotation to trigger the macro compiler.
|
||||||
|
* Note that this annotation does NOT actually target any modules for
|
||||||
|
* compilation. It simply holds all the settings for the memory compiler. The
|
||||||
|
* actual selection of which memories to compile is set in the Params.
|
||||||
|
*
|
||||||
|
* To use, simply annotate the entire circuit itself with this annotation and
|
||||||
|
* include [[MacroCompilerTransform]].
|
||||||
|
*
|
||||||
|
* TODO: make this into a "true" annotation?
|
||||||
|
*/
|
||||||
object MacroCompilerAnnotation {
|
object MacroCompilerAnnotation {
|
||||||
/** Macro compiler mode. */
|
/** Macro compiler mode. */
|
||||||
sealed trait CompilerMode
|
sealed trait CompilerMode
|
||||||
@@ -30,12 +41,22 @@ object MacroCompilerAnnotation {
|
|||||||
case object Synflops extends CompilerMode
|
case object Synflops extends CompilerMode
|
||||||
/** FallbackSynflops - compile all memories to SRAM when possible and fall back to synflops if a memory fails. **/
|
/** FallbackSynflops - compile all memories to SRAM when possible and fall back to synflops if a memory fails. **/
|
||||||
case object FallbackSynflops extends CompilerMode
|
case object FallbackSynflops extends CompilerMode
|
||||||
/** Default mode - compile what is possible and do nothing with uncompiled memories. **/
|
/** CompileAvailable - compile what is possible and do nothing with uncompiled memories. **/
|
||||||
case object Default extends CompilerMode
|
case object CompileAvailable extends CompilerMode
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default mode for the macro compiler.
|
||||||
|
* TODO: Maybe set the default to FallbackSynflops (typical for
|
||||||
|
* vlsi_mem_gen-like scripts) once it's implemented?
|
||||||
|
*/
|
||||||
|
val Default = CompileAvailable
|
||||||
|
|
||||||
|
/** Helper function to select a compiler mode. */
|
||||||
def stringToCompilerMode(str: String): CompilerMode = (str: @unchecked) match {
|
def stringToCompilerMode(str: String): CompilerMode = (str: @unchecked) match {
|
||||||
case "strict" => Strict
|
case "strict" => Strict
|
||||||
case "synflops" => Synflops
|
case "synflops" => Synflops
|
||||||
case "fallbacksynflops" => FallbackSynflops
|
case "fallbacksynflops" => FallbackSynflops
|
||||||
|
case "compileavailable" => CompileAvailable
|
||||||
case "default" => Default
|
case "default" => Default
|
||||||
case _ => throw new IllegalArgumentException("No such compiler mode " + str)
|
case _ => throw new IllegalArgumentException("No such compiler mode " + str)
|
||||||
}
|
}
|
||||||
@@ -51,7 +72,7 @@ object MacroCompilerAnnotation {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a MacroCompilerAnnotation.
|
* Create a MacroCompilerAnnotation.
|
||||||
* @param c Name of the module(?) for this annotation.
|
* @param c Top-level circuit name (see class description)
|
||||||
* @param p Parameters (see above).
|
* @param p Parameters (see above).
|
||||||
*/
|
*/
|
||||||
def apply(c: String, p: Params): Annotation =
|
def apply(c: String, p: Params): Annotation =
|
||||||
@@ -73,7 +94,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
|||||||
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
|
||||||
|
|
||||||
// Parallel mapping
|
// Width mapping
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a list of submemories by width.
|
* This is a list of submemories by width.
|
||||||
@@ -168,6 +189,13 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
|||||||
// In this case we can only compile very wastefully (by treating
|
// In this case we can only compile very wastefully (by treating
|
||||||
// lib as a mem maskGran width memory) :(
|
// lib as a mem maskGran width memory) :(
|
||||||
splitMemory(memMask.get)
|
splitMemory(memMask.get)
|
||||||
|
|
||||||
|
// TODO: there's an optimization that could allow us to pack more
|
||||||
|
// bits in and be more efficient.
|
||||||
|
// e.g. say if mem maskGran = 4, lib maskGran = 8, libWidth = 32
|
||||||
|
// We could use 16 of bit (bits 0-3, 8-11, 16-19, 24-27) instead
|
||||||
|
// of treating it as simply a width 4 (!!!) memory.
|
||||||
|
// This would require a major refactor though.
|
||||||
} else {
|
} else {
|
||||||
System.err.println(s"Lib maskGran ${m} is not a multiple of mem maskGran ${l}: currently not supported")
|
System.err.println(s"Lib maskGran ${m} is not a multiple of mem maskGran ${l}: currently not supported")
|
||||||
return None
|
return None
|
||||||
@@ -180,7 +208,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
|||||||
// Add in the last chunk if there are any leftovers
|
// Add in the last chunk if there are any leftovers
|
||||||
bitPairs += ((currentLSB, mem.src.width.toInt - 1))
|
bitPairs += ((currentLSB, mem.src.width.toInt - 1))
|
||||||
|
|
||||||
// Serial mapping
|
// Depth mapping
|
||||||
val stmts = ArrayBuffer[Statement]()
|
val stmts = ArrayBuffer[Statement]()
|
||||||
val outputs = HashMap[String, ArrayBuffer[(Expression, Expression)]]()
|
val outputs = HashMap[String, ArrayBuffer[(Expression, Expression)]]()
|
||||||
val selects = HashMap[String, Expression]()
|
val selects = HashMap[String, Expression]()
|
||||||
@@ -270,7 +298,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
|||||||
* together a bunch of narrower memories, which can only be
|
* together a bunch of narrower memories, which can only be
|
||||||
* done after generating all the memories. This saves up the
|
* done after generating all the memories. This saves up the
|
||||||
* output statements for later. */
|
* output statements for later. */
|
||||||
val name = s"${mem}_${i}_${j}"
|
val name = s"${mem}_${i}_${j}" // This name is the output from the instance (mem vs ${mem}).
|
||||||
val exp = portToExpression(bits(WSubField(inst, lib), high-low, 0), Some(lib_polarity))
|
val exp = portToExpression(bits(WSubField(inst, lib), high-low, 0), Some(lib_polarity))
|
||||||
stmts += DefNode(NoInfo, name, exp)
|
stmts += DefNode(NoInfo, name, exp)
|
||||||
cats += WRef(name)
|
cats += WRef(name)
|
||||||
@@ -332,15 +360,17 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
|||||||
})).reverse)
|
})).reverse)
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
/* Palmer: If there is no input port on the source memory port
|
/* If there is a lib mask port but no mem mask port, just turn on
|
||||||
* then we don't ever want to turn on this write
|
* all bits of the lib mask port. */
|
||||||
* enable. Otherwise, we just _always_ turn on the
|
if (libPort.src.maskPort.isDefined) {
|
||||||
* write enable port on the inner memory. */
|
|
||||||
if (libPort.src.maskPort.isEmpty) one
|
|
||||||
else {
|
|
||||||
val width = libPort.src.width / libPort.src.effectiveMaskGran
|
val width = libPort.src.width / 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 {
|
||||||
|
// No mask ports on either side.
|
||||||
|
// We treat a "mask" of a single bit to be equivalent to a write
|
||||||
|
// enable (as used below).
|
||||||
|
one
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,25 +420,26 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
|||||||
stmts += connectPorts(memMask, mask, mask_polarity)
|
stmts += connectPorts(memMask, mask, mask_polarity)
|
||||||
stmts += connectPorts(andAddrMatch(and(memWriteEnable, memChipEnable)),
|
stmts += connectPorts(andAddrMatch(and(memWriteEnable, memChipEnable)),
|
||||||
we, mask_polarity)
|
we, mask_polarity)
|
||||||
case (None, Some(PolarizedPort(we, we_polarity)), chipEnable) if bitWidth(memMask.tpe) == 1 =>
|
case (None, Some(PolarizedPort(we, we_polarity)), chipEnable) =>
|
||||||
/* Palmer: If we're expected to provide mask ports without a
|
if (bitWidth(memMask.tpe) == 1) {
|
||||||
* memory that actually has them then we can use the
|
/* Palmer: If we're expected to provide mask ports without a
|
||||||
* write enable port instead of the mask port. */
|
* memory that actually has them then we can use the
|
||||||
stmts += connectPorts(andAddrMatch(and(memWriteEnable, memMask)),
|
* write enable port instead of the mask port. */
|
||||||
we, we_polarity)
|
stmts += connectPorts(andAddrMatch(and(memWriteEnable, memMask)),
|
||||||
chipEnable match {
|
we, we_polarity)
|
||||||
case Some(PolarizedPort(en, en_polarity)) => {
|
chipEnable match {
|
||||||
stmts += connectPorts(andAddrMatch(memChipEnable), en, en_polarity)
|
case Some(PolarizedPort(en, en_polarity)) => {
|
||||||
|
stmts += connectPorts(andAddrMatch(memChipEnable), en, en_polarity)
|
||||||
|
}
|
||||||
|
case _ => // TODO: do we care about the case where mem has chipEnable but lib doesn't?
|
||||||
}
|
}
|
||||||
case _ => // TODO: do we care about the case where mem has chipEnable but lib doesn't?
|
} else {
|
||||||
|
System.err.println("cannot emulate multi-bit mask ports with write enable")
|
||||||
|
return None
|
||||||
}
|
}
|
||||||
case (None, Some(PolarizedPort(we, we_polarity)), Some(PolarizedPort(en, en_polarity))) =>
|
|
||||||
// TODO
|
|
||||||
System.err.println("cannot emulate multi-bit mask ports with write enable")
|
|
||||||
return None
|
|
||||||
case (None, None, None) =>
|
case (None, None, None) =>
|
||||||
/* Palmer: There's nothing to do here since there aren't any
|
// No write ports to match up (this may be a read-only port).
|
||||||
* ports to match up. */
|
// This isn't necessarily an error condition.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cat macro outputs for selection
|
// Cat macro outputs for selection
|
||||||
|
|||||||
@@ -385,5 +385,9 @@ circuit T_2172_ext :
|
|||||||
defname = SRAM2RW64x32
|
defname = SRAM2RW64x32
|
||||||
"""
|
"""
|
||||||
|
|
||||||
compileExecuteAndTest(mem, lib, v, output)
|
// TODO FIXME: Enable this test when firrtl #644 https://github.com/freechipsproject/firrtl/issues/644 is fixed
|
||||||
|
"rocket example" should "work" in {
|
||||||
|
pending
|
||||||
|
}
|
||||||
|
//~ compileExecuteAndTest(mem, lib, v, output)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user