Merge pull request #97 from ucb-bar/fix-deprecations-1
Fix deprecations 1
This commit is contained in:
10
build.sbt
10
build.sbt
@@ -7,15 +7,14 @@ val defaultVersions = Map(
|
||||
|
||||
lazy val commonSettings = Seq(
|
||||
organization := "edu.berkeley.cs",
|
||||
version := "0.1-SNAPSHOT",
|
||||
version := "0.4-SNAPSHOT",
|
||||
scalaVersion := "2.12.10",
|
||||
scalacOptions := Seq("-deprecation", "-feature", "-language:reflectiveCalls", "-Xsource:2.11"),
|
||||
libraryDependencies ++= Seq("chisel3","chisel-iotesters").map {
|
||||
dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep))
|
||||
},
|
||||
libraryDependencies in Test ++= Seq(
|
||||
"org.scalatest" %% "scalatest" % "2.2.5" % "test",
|
||||
"org.scalacheck" %% "scalacheck" % "1.12.4" % "test"
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scalatest" %% "scalatest" % "3.2.2" % "test",
|
||||
),
|
||||
resolvers ++= Seq(
|
||||
Resolver.sonatypeRepo("snapshots"),
|
||||
@@ -31,9 +30,6 @@ lazy val macros = (project in file("macros"))
|
||||
.dependsOn(mdf)
|
||||
.settings(commonSettings)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"edu.berkeley.cs" %% "firrtl-interpreter" % "1.4.+" % Test
|
||||
),
|
||||
mainClass := Some("barstools.macros.MacroCompiler")
|
||||
)
|
||||
.enablePlugins(sbtassembly.AssemblyPlugin)
|
||||
|
||||
@@ -6,8 +6,10 @@ import chisel3._
|
||||
import chisel3.util.{HasBlackBoxResource}
|
||||
import chisel3.experimental.{Analog, IntParam}
|
||||
|
||||
class AnalogConst(value: Int, width: Int = 1) extends BlackBox(Map("CONST" -> IntParam(value), "WIDTH" -> IntParam(width))) with HasBlackBoxResource{
|
||||
val io = IO(new Bundle {val io = Analog(width.W) } )
|
||||
class AnalogConst(value: Int, width: Int = 1)
|
||||
extends BlackBox(Map("CONST" -> IntParam(value), "WIDTH" -> IntParam(width)))
|
||||
with HasBlackBoxResource {
|
||||
val io = IO(new Bundle { val io = Analog(width.W) })
|
||||
addResource("/barstools/iocell/vsrc/Analog.v")
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ package barstools.iocell.chisel
|
||||
|
||||
import chisel3._
|
||||
import chisel3.util.{Cat, HasBlackBoxResource}
|
||||
import chisel3.experimental.{Analog, DataMirror, IO, BaseModule}
|
||||
import chisel3.experimental.{Analog, BaseModule, DataMirror, IO}
|
||||
|
||||
// The following four IO cell bundle types are bare-minimum functional connections
|
||||
// for modeling 4 different IO cell scenarios. The intention is that the user
|
||||
@@ -13,24 +13,22 @@ import chisel3.experimental.{Analog, DataMirror, IO, BaseModule}
|
||||
// (https://github.com/sifive/sifive-blocks/blob/master/src/main/scala/devices/pinctrl/PinCtrl.scala),
|
||||
// but we want to avoid a dependency on an external libraries.
|
||||
|
||||
/**
|
||||
* The base IO bundle for an analog signal (typically something with no digital buffers inside)
|
||||
* pad: off-chip (external) connection
|
||||
* core: internal connection
|
||||
*/
|
||||
/** The base IO bundle for an analog signal (typically something with no digital buffers inside)
|
||||
* pad: off-chip (external) connection
|
||||
* core: internal connection
|
||||
*/
|
||||
class AnalogIOCellBundle extends Bundle {
|
||||
val pad = Analog(1.W) // Pad/bump signal (off-chip)
|
||||
val core = Analog(1.W) // core signal (on-chip)
|
||||
val pad = Analog(1.W) // Pad/bump signal (off-chip)
|
||||
val core = Analog(1.W) // core signal (on-chip)
|
||||
}
|
||||
|
||||
/**
|
||||
* The base IO bundle for a signal with runtime-controllable direction
|
||||
* pad: off-chip (external) connection
|
||||
* i: input to chip logic (output from IO cell)
|
||||
* ie: enable signal for i
|
||||
* o: output from chip logic (input to IO cell)
|
||||
* oe: enable signal for o
|
||||
*/
|
||||
/** The base IO bundle for a signal with runtime-controllable direction
|
||||
* pad: off-chip (external) connection
|
||||
* i: input to chip logic (output from IO cell)
|
||||
* ie: enable signal for i
|
||||
* o: output from chip logic (input to IO cell)
|
||||
* oe: enable signal for o
|
||||
*/
|
||||
class DigitalGPIOCellBundle extends Bundle {
|
||||
val pad = Analog(1.W)
|
||||
val i = Output(Bool())
|
||||
@@ -39,24 +37,22 @@ class DigitalGPIOCellBundle extends Bundle {
|
||||
val oe = Input(Bool())
|
||||
}
|
||||
|
||||
/**
|
||||
* The base IO bundle for a digital output signal
|
||||
* pad: off-chip (external) connection
|
||||
* o: output from chip logic (input to IO cell)
|
||||
* oe: enable signal for o
|
||||
*/
|
||||
/** The base IO bundle for a digital output signal
|
||||
* pad: off-chip (external) connection
|
||||
* o: output from chip logic (input to IO cell)
|
||||
* oe: enable signal for o
|
||||
*/
|
||||
class DigitalOutIOCellBundle extends Bundle {
|
||||
val pad = Output(Bool())
|
||||
val o = Input(Bool())
|
||||
val oe = Input(Bool())
|
||||
}
|
||||
|
||||
/**
|
||||
* The base IO bundle for a digital input signal
|
||||
* pad: off-chip (external) connection
|
||||
* i: input to chip logic (output from IO cell)
|
||||
* ie: enable signal for i
|
||||
*/
|
||||
/** The base IO bundle for a digital input signal
|
||||
* pad: off-chip (external) connection
|
||||
* i: input to chip logic (output from IO cell)
|
||||
* ie: enable signal for i
|
||||
*/
|
||||
class DigitalInIOCellBundle extends Bundle {
|
||||
val pad = Input(Bool())
|
||||
val i = Output(Bool())
|
||||
@@ -102,11 +98,10 @@ class GenericDigitalOutIOCell extends GenericIOCell with DigitalOutIOCell {
|
||||
val io = IO(new DigitalOutIOCellBundle)
|
||||
}
|
||||
|
||||
|
||||
trait IOCellTypeParams {
|
||||
def analog(): AnalogIOCell
|
||||
def gpio(): DigitalGPIOCell
|
||||
def input(): DigitalInIOCell
|
||||
def gpio(): DigitalGPIOCell
|
||||
def input(): DigitalInIOCell
|
||||
def output(): DigitalOutIOCell
|
||||
}
|
||||
|
||||
@@ -118,47 +113,49 @@ case class GenericIOCellParams() extends IOCellTypeParams {
|
||||
}
|
||||
|
||||
object IOCell {
|
||||
/**
|
||||
* From within a RawModule or MultiIOModule context, generate new module IOs from a given
|
||||
* signal and return the new IO and a Seq containing all generated IO cells.
|
||||
* @param coreSignal The signal onto which to add IO cells
|
||||
* @param name An optional name or name prefix to use for naming IO cells
|
||||
* @param abstractResetAsAsync When set, will coerce abstract resets to
|
||||
* AsyncReset, and otherwise to Bool (sync reset)
|
||||
* @return A tuple of (the generated IO data node, a Seq of all generated IO cell instances)
|
||||
*/
|
||||
def generateIOFromSignal[T <: Data](coreSignal: T, name: String,
|
||||
typeParams: IOCellTypeParams = GenericIOCellParams(),
|
||||
abstractResetAsAsync: Boolean = false): (T, Seq[IOCell]) =
|
||||
{
|
||||
|
||||
/** From within a RawModule or MultiIOModule context, generate new module IOs from a given
|
||||
* signal and return the new IO and a Seq containing all generated IO cells.
|
||||
* @param coreSignal The signal onto which to add IO cells
|
||||
* @param name An optional name or name prefix to use for naming IO cells
|
||||
* @param abstractResetAsAsync When set, will coerce abstract resets to
|
||||
* AsyncReset, and otherwise to Bool (sync reset)
|
||||
* @return A tuple of (the generated IO data node, a Seq of all generated IO cell instances)
|
||||
*/
|
||||
def generateIOFromSignal[T <: Data](
|
||||
coreSignal: T,
|
||||
name: String,
|
||||
typeParams: IOCellTypeParams = GenericIOCellParams(),
|
||||
abstractResetAsAsync: Boolean = false
|
||||
): (T, Seq[IOCell]) = {
|
||||
val padSignal = IO(DataMirror.internal.chiselTypeClone[T](coreSignal)).suggestName(name)
|
||||
val resetFn = if (abstractResetAsAsync) toAsyncReset else toSyncReset
|
||||
val iocells = IOCell.generateFromSignal(coreSignal, padSignal, Some(s"iocell_$name"), typeParams, resetFn)
|
||||
(padSignal, iocells)
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect two identical signals together by adding IO cells between them and return a Seq
|
||||
* containing all generated IO cells.
|
||||
* @param coreSignal The core-side (internal) signal onto which to connect/add IO cells
|
||||
* @param padSignal The pad-side (external) signal onto which to connect IO cells
|
||||
* @param name An optional name or name prefix to use for naming IO cells
|
||||
* @return A Seq of all generated IO cell instances
|
||||
*/
|
||||
val toSyncReset: (Reset) => Bool = _.toBool
|
||||
/** Connect two identical signals together by adding IO cells between them and return a Seq
|
||||
* containing all generated IO cells.
|
||||
* @param coreSignal The core-side (internal) signal onto which to connect/add IO cells
|
||||
* @param padSignal The pad-side (external) signal onto which to connect IO cells
|
||||
* @param name An optional name or name prefix to use for naming IO cells
|
||||
* @return A Seq of all generated IO cell instances
|
||||
*/
|
||||
val toSyncReset: (Reset) => Bool = _.toBool
|
||||
val toAsyncReset: (Reset) => AsyncReset = _.asAsyncReset
|
||||
def generateFromSignal[T <: Data, R <: Reset](
|
||||
coreSignal: T,
|
||||
padSignal: T,
|
||||
name: Option[String] = None,
|
||||
typeParams: IOCellTypeParams = GenericIOCellParams(),
|
||||
concretizeResetFn : (Reset) => R = toSyncReset): Seq[IOCell] =
|
||||
{
|
||||
coreSignal: T,
|
||||
padSignal: T,
|
||||
name: Option[String] = None,
|
||||
typeParams: IOCellTypeParams = GenericIOCellParams(),
|
||||
concretizeResetFn: (Reset) => R = toSyncReset
|
||||
): Seq[IOCell] = {
|
||||
def genCell[T <: Data](
|
||||
castToBool: (T) => Bool,
|
||||
castFromBool: (Bool) => T)(
|
||||
coreSignal: T,
|
||||
padSignal: T): Seq[IOCell] = {
|
||||
castToBool: (T) => Bool,
|
||||
castFromBool: (Bool) => T
|
||||
)(coreSignal: T,
|
||||
padSignal: T
|
||||
): Seq[IOCell] = {
|
||||
DataMirror.directionOf(coreSignal) match {
|
||||
case ActualDirection.Input => {
|
||||
val iocell = typeParams.input()
|
||||
@@ -188,7 +185,10 @@ object IOCell {
|
||||
if (coreSignal.getWidth == 0) {
|
||||
Seq()
|
||||
} else {
|
||||
require(coreSignal.getWidth == 1, "Analogs wider than 1 bit are not supported because we can't bit-select Analogs (https://github.com/freechipsproject/chisel3/issues/536)")
|
||||
require(
|
||||
coreSignal.getWidth == 1,
|
||||
"Analogs wider than 1 bit are not supported because we can't bit-select Analogs (https://github.com/freechipsproject/chisel3/issues/536)"
|
||||
)
|
||||
val iocell = typeParams.analog()
|
||||
name.foreach(n => iocell.suggestName(n))
|
||||
iocell.io.core <> coreSignal
|
||||
@@ -204,7 +204,7 @@ object IOCell {
|
||||
// This dummy assignment will prevent invalid firrtl from being emitted
|
||||
DataMirror.directionOf(coreSignal) match {
|
||||
case ActualDirection.Input => coreSignal := 0.U
|
||||
case _ => {}
|
||||
case _ => {}
|
||||
}
|
||||
Seq()
|
||||
} else {
|
||||
@@ -1,141 +0,0 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package barstools.macros
|
||||
|
||||
import firrtl._
|
||||
import firrtl.ir._
|
||||
import firrtl.Utils._
|
||||
import firrtl.passes.MemPortUtils.{memPortField, memType}
|
||||
import Utils._
|
||||
|
||||
class SynFlopsPass(synflops: Boolean, libs: Seq[Macro]) extends firrtl.passes.Pass {
|
||||
val extraMods = scala.collection.mutable.ArrayBuffer.empty[Module]
|
||||
lazy val libMods = (libs map { lib => lib.src.name -> {
|
||||
val (dataType, dataWidth) = (lib.src.ports foldLeft (None: Option[BigInt]))((res, port) =>
|
||||
(res, port.maskPort) match {
|
||||
case (_, None) =>
|
||||
res
|
||||
case (None, Some(_)) =>
|
||||
Some(port.effectiveMaskGran)
|
||||
case (Some(x), Some(_)) =>
|
||||
assert(x == port.effectiveMaskGran)
|
||||
res
|
||||
}
|
||||
) match {
|
||||
case None => (UIntType(IntWidth(lib.src.width)), lib.src.width)
|
||||
case Some(gran) => (UIntType(IntWidth(gran)), gran.intValue)
|
||||
}
|
||||
|
||||
val maxDepth = min(lib.src.depth, 1<<26)
|
||||
val numMems = lib.src.depth / maxDepth
|
||||
|
||||
// Change macro to be mapped onto to look like the below mem
|
||||
// by changing its depth, and width
|
||||
val lib_macro = new Macro(lib.src.copy(name="split_"+lib.src.name,
|
||||
depth = maxDepth, width = dataWidth, ports = lib.src.ports.map(p =>
|
||||
p.copy(width = p.width.map(_ => dataWidth), depth = p.depth.map(_ => maxDepth),
|
||||
maskGran = p.maskGran.map(_ => dataWidth)))))
|
||||
val mod_macro = (new MacroCompilerPass(None,None,None,None)).compile(lib, lib_macro)
|
||||
val (real_mod, real_macro) = mod_macro.get
|
||||
|
||||
val mem = DefMemory(
|
||||
NoInfo,
|
||||
"ram",
|
||||
dataType,
|
||||
maxDepth,
|
||||
1, // writeLatency
|
||||
1, // readLatency. This is possible because of VerilogMemDelays
|
||||
real_macro.readers.indices map (i => s"R_$i"),
|
||||
real_macro.writers.indices map (i => s"W_$i"),
|
||||
real_macro.readwriters.indices map (i => s"RW_$i")
|
||||
)
|
||||
|
||||
val readConnects = real_macro.readers.zipWithIndex flatMap { case (r, i) =>
|
||||
val clock = portToExpression(r.src.clock.get)
|
||||
val address = portToExpression(r.src.address)
|
||||
val enable = (r.src chipEnable, r.src readEnable) match {
|
||||
case (Some(en_port), Some(re_port)) =>
|
||||
and(portToExpression(en_port),
|
||||
portToExpression(re_port))
|
||||
case (Some(en_port), None) => portToExpression(en_port)
|
||||
case (None, Some(re_port)) => portToExpression(re_port)
|
||||
case (None, None) => one
|
||||
}
|
||||
val data = memPortField(mem, s"R_$i", "data")
|
||||
val read = data
|
||||
Seq(
|
||||
Connect(NoInfo, memPortField(mem, s"R_$i", "clk"), clock),
|
||||
Connect(NoInfo, memPortField(mem, s"R_$i", "addr"), address),
|
||||
Connect(NoInfo, memPortField(mem, s"R_$i", "en"), enable),
|
||||
Connect(NoInfo, WRef(r.src.output.get.name), read)
|
||||
)
|
||||
}
|
||||
|
||||
val writeConnects = real_macro.writers.zipWithIndex flatMap { case (w, i) =>
|
||||
val clock = portToExpression(w.src.clock.get)
|
||||
val address = portToExpression(w.src.address)
|
||||
val enable = (w.src.chipEnable, w.src.writeEnable) match {
|
||||
case (Some(en), Some(we)) =>
|
||||
and(portToExpression(en),
|
||||
portToExpression(we))
|
||||
case (Some(en), None) => portToExpression(en)
|
||||
case (None, Some(we)) => portToExpression(we)
|
||||
case (None, None) => zero // is it possible?
|
||||
}
|
||||
val mask = w.src.maskPort match {
|
||||
case Some(m) => portToExpression(m)
|
||||
case None => one
|
||||
}
|
||||
val data = memPortField(mem, s"W_$i", "data")
|
||||
val write = portToExpression(w.src.input.get)
|
||||
Seq(
|
||||
Connect(NoInfo, memPortField(mem, s"W_$i", "clk"), clock),
|
||||
Connect(NoInfo, memPortField(mem, s"W_$i", "addr"), address),
|
||||
Connect(NoInfo, memPortField(mem, s"W_$i", "en"), enable),
|
||||
Connect(NoInfo, memPortField(mem, s"W_$i", "mask"), mask),
|
||||
Connect(NoInfo, data, write)
|
||||
)
|
||||
}
|
||||
|
||||
val readwriteConnects = real_macro.readwriters.zipWithIndex flatMap { case (rw, i) =>
|
||||
val clock = portToExpression(rw.src.clock.get)
|
||||
val address = portToExpression(rw.src.address)
|
||||
val wmode = rw.src.writeEnable match {
|
||||
case Some(we) => portToExpression(we)
|
||||
case None => zero // is it possible?
|
||||
}
|
||||
val wmask = rw.src.maskPort match {
|
||||
case Some(wm) => portToExpression(wm)
|
||||
case None => one
|
||||
}
|
||||
val enable = (rw.src.chipEnable, rw.src.readEnable) match {
|
||||
case (Some(en), Some(re)) =>
|
||||
and(portToExpression(en), or(portToExpression(re), wmode))
|
||||
case (Some(en), None) => portToExpression(en)
|
||||
case (None, Some(re)) => or(portToExpression(re), wmode)
|
||||
case (None, None) => one
|
||||
}
|
||||
val wdata = memPortField(mem, s"RW_$i", "wdata")
|
||||
val rdata = memPortField(mem, s"RW_$i", "rdata")
|
||||
val write = portToExpression(rw.src.input.get)
|
||||
val read = rdata
|
||||
Seq(
|
||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "clk"), clock),
|
||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "addr"), address),
|
||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "en"), enable),
|
||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "wmode"), wmode),
|
||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "wmask"), wmask),
|
||||
Connect(NoInfo, WRef(rw.src.output.get.name), read),
|
||||
Connect(NoInfo, wdata, write)
|
||||
)
|
||||
}
|
||||
|
||||
extraMods.append(real_macro.module(Block(mem +: (readConnects ++ writeConnects ++ readwriteConnects))))
|
||||
real_mod
|
||||
}}).toMap
|
||||
|
||||
def run(c: Circuit): Circuit = {
|
||||
if (!synflops) c
|
||||
else c.copy(modules = (c.modules map (m => libMods.getOrElse(m.name, m))) ++ extraMods)
|
||||
}
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package barstools.macros
|
||||
|
||||
import firrtl._
|
||||
import firrtl.ir._
|
||||
import firrtl.PrimOps
|
||||
import firrtl.passes.memlib.{MemConf, MemPort, ReadPort, WritePort, ReadWritePort, MaskedWritePort, MaskedReadWritePort}
|
||||
import firrtl.Utils.BoolType
|
||||
import mdf.macrolib.{Constant, MacroPort, SRAMMacro}
|
||||
import mdf.macrolib.{PolarizedPort, PortPolarity, ActiveLow, ActiveHigh, NegativeEdge, PositiveEdge, MacroExtraPort}
|
||||
import java.io.File
|
||||
import scala.language.implicitConversions
|
||||
|
||||
object MacroCompilerMath {
|
||||
def ceilLog2(x: BigInt): Int = (x-1).bitLength
|
||||
}
|
||||
|
||||
class FirrtlMacroPort(port: MacroPort) {
|
||||
val src = port
|
||||
|
||||
val isReader = port.output.nonEmpty && port.input.isEmpty
|
||||
val isWriter = port.input.nonEmpty && port.output.isEmpty
|
||||
val isReadWriter = port.input.nonEmpty && port.output.nonEmpty
|
||||
|
||||
val addrType = UIntType(IntWidth(MacroCompilerMath.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(
|
||||
Field(port.address.name, Flip, addrType)) ++
|
||||
(port.clock map (p => Field(p.name, Flip, ClockType))) ++
|
||||
(port.input map (p => Field(p.name, Flip, dataType))) ++
|
||||
(port.output map (p => Field(p.name, Default, dataType))) ++
|
||||
(port.chipEnable map (p => Field(p.name, Flip, BoolType))) ++
|
||||
(port.readEnable map (p => Field(p.name, Flip, BoolType))) ++
|
||||
(port.writeEnable map (p => Field(p.name, Flip, BoolType))) ++
|
||||
(port.maskPort map (p => Field(p.name, Flip, maskType)))
|
||||
)
|
||||
val ports = tpe.fields map (f => Port(
|
||||
NoInfo, f.name, f.flip match { case Default => Output case Flip => Input }, f.tpe))
|
||||
}
|
||||
|
||||
// Reads an SRAMMacro and generates firrtl blackboxes.
|
||||
class Macro(srcMacro: SRAMMacro) {
|
||||
val src = srcMacro
|
||||
|
||||
val firrtlPorts = srcMacro.ports map { new FirrtlMacroPort(_) }
|
||||
|
||||
val writers = firrtlPorts filter (p => p.isWriter)
|
||||
val readers = firrtlPorts filter (p => p.isReader)
|
||||
val readwriters = firrtlPorts filter (p => p.isReadWriter)
|
||||
|
||||
val sortedPorts = writers ++ readers ++ readwriters
|
||||
val extraPorts = srcMacro.extraPorts map { p =>
|
||||
assert(p.portType == Constant) // TODO: release it?
|
||||
val name = p.name
|
||||
val width = BigInt(p.width.toLong)
|
||||
val value = BigInt(p.value.toLong)
|
||||
(name -> UIntLiteral(value, IntWidth(width)))
|
||||
}
|
||||
|
||||
// Bundle representing this memory blackbox
|
||||
val tpe = BundleType(firrtlPorts flatMap (_.tpe.fields))
|
||||
|
||||
private val modPorts = (firrtlPorts flatMap (_.ports)) ++
|
||||
(extraPorts map { case (name, value) => Port(NoInfo, name, Input, value.tpe) })
|
||||
val blackbox = ExtModule(NoInfo, srcMacro.name, modPorts, srcMacro.name, Nil)
|
||||
def module(body: Statement) = Module(NoInfo, srcMacro.name, modPorts, body)
|
||||
}
|
||||
|
||||
object Utils {
|
||||
def filterForSRAM(s: Option[Seq[mdf.macrolib.Macro]]): Option[Seq[mdf.macrolib.SRAMMacro]] = {
|
||||
s match {
|
||||
case Some(l:Seq[mdf.macrolib.Macro]) => Some(l filter { _.isInstanceOf[mdf.macrolib.SRAMMacro] } map { m => m.asInstanceOf[mdf.macrolib.SRAMMacro] })
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
// This utility reads a conf in and returns MDF like mdf.macrolib.Utils.readMDFFromPath
|
||||
def readConfFromPath(path: Option[String]): Option[Seq[mdf.macrolib.Macro]] = {
|
||||
path.map((p) => Utils.readConfFromString(scala.io.Source.fromFile(p).mkString))
|
||||
}
|
||||
def readConfFromString(str: String): Seq[mdf.macrolib.Macro] = {
|
||||
MemConf.fromString(str).map { m:MemConf =>
|
||||
val ports = m.ports.map { case (port, num) => Seq.fill(num)(port) } reduce (_ ++ _)
|
||||
SRAMMacro(m.name, m.width, m.depth, Utils.portSpecToFamily(ports), Utils.portSpecToMacroPort(m.width, m.depth, m.maskGranularity, ports))
|
||||
}
|
||||
}
|
||||
def portSpecToFamily(ports: Seq[MemPort]): String = {
|
||||
val numR = ports.count(_ match { case ReadPort => true; case _ => false})
|
||||
val numW = ports.count(_ match { case WritePort|MaskedWritePort => true; case _ => false})
|
||||
val numRW = ports.count(_ match { case ReadWritePort|MaskedReadWritePort => true; case _ => false})
|
||||
val numRStr = if(numR > 0) s"${numR}r" else ""
|
||||
val numWStr = if(numW > 0) s"${numW}w" else ""
|
||||
val numRWStr = if(numRW > 0) s"${numRW}rw" else ""
|
||||
return numRStr + numWStr + numRWStr
|
||||
}
|
||||
// This translates between two represenations of ports
|
||||
def portSpecToMacroPort(width: Int, depth: BigInt, maskGran: Option[Int], ports: Seq[MemPort]): Seq[MacroPort] = {
|
||||
var numR = 0
|
||||
var numW = 0
|
||||
var numRW = 0
|
||||
ports.map { _ match {
|
||||
case ReadPort => {
|
||||
val portName = s"R${numR}"
|
||||
numR += 1
|
||||
MacroPort(
|
||||
width=Some(width), depth=Some(depth),
|
||||
address=PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||
clock=Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||
readEnable=Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||
output=Some(PolarizedPort(s"${portName}_data", ActiveHigh))
|
||||
) }
|
||||
case WritePort => {
|
||||
val portName = s"W${numW}"
|
||||
numW += 1
|
||||
MacroPort(
|
||||
width=Some(width), depth=Some(depth),
|
||||
address=PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||
clock=Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||
writeEnable=Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||
input=Some(PolarizedPort(s"${portName}_data", ActiveHigh))
|
||||
) }
|
||||
case MaskedWritePort => {
|
||||
val portName = s"W${numW}"
|
||||
numW += 1
|
||||
MacroPort(
|
||||
width=Some(width), depth=Some(depth),
|
||||
address=PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||
clock=Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||
writeEnable=Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||
maskPort=Some(PolarizedPort(s"${portName}_mask", ActiveHigh)),
|
||||
maskGran=maskGran,
|
||||
input=Some(PolarizedPort(s"${portName}_data", ActiveHigh))
|
||||
) }
|
||||
case ReadWritePort => {
|
||||
val portName = s"RW${numRW}"
|
||||
numRW += 1
|
||||
MacroPort(
|
||||
width=Some(width), depth=Some(depth),
|
||||
address=PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||
clock=Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||
chipEnable=Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||
writeEnable=Some(PolarizedPort(s"${portName}_wmode", ActiveHigh)),
|
||||
input=Some(PolarizedPort(s"${portName}_wdata", ActiveHigh)),
|
||||
output=Some(PolarizedPort(s"${portName}_rdata", ActiveHigh))
|
||||
) }
|
||||
case MaskedReadWritePort => {
|
||||
val portName = s"RW${numRW}"
|
||||
numRW += 1
|
||||
MacroPort(
|
||||
width=Some(width), depth=Some(depth),
|
||||
address=PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||
clock=Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||
chipEnable=Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||
writeEnable=Some(PolarizedPort(s"${portName}_wmode", ActiveHigh)),
|
||||
maskPort=Some(PolarizedPort(s"${portName}_wmask", ActiveHigh)),
|
||||
maskGran=maskGran,
|
||||
input=Some(PolarizedPort(s"${portName}_wdata", ActiveHigh)),
|
||||
output=Some(PolarizedPort(s"${portName}_rdata", ActiveHigh))
|
||||
) }
|
||||
}}
|
||||
}
|
||||
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))), vt, g.mux, g.extraPorts)
|
||||
}
|
||||
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))), vt, g.mux, 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)
|
||||
def or(e1: Expression, e2: Expression) =
|
||||
DoPrim(PrimOps.Or, Seq(e1, e2), Nil, e1.tpe)
|
||||
def bits(e: Expression, high: BigInt, low: BigInt): Expression =
|
||||
DoPrim(PrimOps.Bits, Seq(e), Seq(high, low), UIntType(IntWidth(high-low+1)))
|
||||
def bits(e: Expression, idx: BigInt): Expression = bits(e, idx, idx)
|
||||
def cat(es: Seq[Expression]): Expression =
|
||||
if (es.size == 1) es.head
|
||||
else DoPrim(PrimOps.Cat, Seq(es.head, cat(es.tail)), Nil, UnknownType)
|
||||
def not(e: Expression) =
|
||||
DoPrim(PrimOps.Not, Seq(e), Nil, e.tpe)
|
||||
|
||||
// Convert a port to a FIRRTL expression, handling polarity along the way.
|
||||
def portToExpression(pp: PolarizedPort): Expression =
|
||||
portToExpression(WRef(pp.name), Some(pp.polarity))
|
||||
|
||||
def portToExpression(exp: Expression, polarity: Option[PortPolarity]): Expression =
|
||||
polarity match {
|
||||
case Some(ActiveLow) | Some(NegativeEdge) => not(exp)
|
||||
case _ => exp
|
||||
}
|
||||
|
||||
// Check if a number is a power of two
|
||||
def isPowerOfTwo(x: Int): Boolean = (x & (x - 1)) == 0
|
||||
}
|
||||
@@ -2,27 +2,25 @@
|
||||
|
||||
package barstools.macros
|
||||
|
||||
/**
|
||||
* Trait which can calculate the cost of compiling a memory against a certain
|
||||
* library memory macro using a cost function.
|
||||
*/
|
||||
/** Trait which can calculate the cost of compiling a memory against a certain
|
||||
* library memory macro using a cost function.
|
||||
*/
|
||||
// TODO: eventually explore compiling a single target memory using multiple
|
||||
// different kinds of target memory.
|
||||
trait CostMetric extends Serializable {
|
||||
/**
|
||||
* Cost function that returns the cost of compiling a memory using a certain
|
||||
* macro.
|
||||
*
|
||||
* @param mem Memory macro to compile (target memory)
|
||||
* @param lib Library memory macro to use (library memory)
|
||||
* @return The cost of this compile, defined by this cost metric, or None if
|
||||
* it cannot be compiled.
|
||||
*/
|
||||
|
||||
/** Cost function that returns the cost of compiling a memory using a certain
|
||||
* macro.
|
||||
*
|
||||
* @param mem Memory macro to compile (target memory)
|
||||
* @param lib Library memory macro to use (library memory)
|
||||
* @return The cost of this compile, defined by this cost metric, or None if
|
||||
* it cannot be compiled.
|
||||
*/
|
||||
def cost(mem: Macro, lib: Macro): Option[Double]
|
||||
|
||||
/**
|
||||
* Helper function to return the map of arguments (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]
|
||||
|
||||
// We also want this to show up for the class itself.
|
||||
@@ -40,8 +38,9 @@ trait CostMetricCompanion {
|
||||
// Some default cost functions.
|
||||
|
||||
/** Palmer's old metric.
|
||||
* TODO: figure out what is the difference between this metric and the current
|
||||
* default metric and either revive or delete this metric. */
|
||||
* 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[Double] = {
|
||||
/* Palmer: A quick cost function (that must be kept in sync with
|
||||
@@ -58,17 +57,17 @@ object OldMetric extends CostMetric with CostMetricCompanion {
|
||||
override def construct(m: Map[String, String]) = OldMetric
|
||||
}
|
||||
|
||||
/**
|
||||
* An external cost function.
|
||||
* Calls the specified path with paths to the JSON MDF representation of the mem
|
||||
* and lib macros. The external executable should print a Double.
|
||||
* None will be returned if the external executable does not print a valid
|
||||
* Double.
|
||||
*/
|
||||
/** An external cost function.
|
||||
* Calls the specified path with paths to the JSON MDF representation of the mem
|
||||
* and lib macros. The external executable should print a Double.
|
||||
* None will be returned if the external executable does not print a valid
|
||||
* Double.
|
||||
*/
|
||||
class ExternalMetric(path: String) extends CostMetric {
|
||||
import mdf.macrolib.Utils.writeMacroToPath
|
||||
|
||||
import java.io._
|
||||
import scala.language.postfixOps // for !! postfix op
|
||||
import scala.language.postfixOps
|
||||
import sys.process._
|
||||
|
||||
override def cost(mem: Macro, lib: Macro): Option[Double] = {
|
||||
@@ -104,7 +103,7 @@ object ExternalMetric extends CostMetricCompanion {
|
||||
override def construct(m: Map[String, String]) = {
|
||||
val pathOption = m.get("path")
|
||||
pathOption match {
|
||||
case Some(path:String) => new ExternalMetric(path)
|
||||
case Some(path: String) => new ExternalMetric(path)
|
||||
case _ => throw new IllegalArgumentException("ExternalMetric missing option 'path'")
|
||||
}
|
||||
}
|
||||
@@ -114,14 +113,17 @@ object ExternalMetric extends CostMetricCompanion {
|
||||
// TODO: write tests for this function to make sure it selects the right things
|
||||
object DefaultMetric extends CostMetric with CostMetricCompanion {
|
||||
override def cost(mem: Macro, lib: Macro): Option[Double] = {
|
||||
val memMask = mem.src.ports map (_.maskGran) find (_.isDefined) map (_.get)
|
||||
val libMask = lib.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 memWidth = (memMask, libMask) match {
|
||||
case (None, _) => mem.src.width
|
||||
case (Some(p), None) => (mem.src.width/p)*math.ceil(p.toDouble/lib.src.width)*lib.src.width //We map the mask to distinct memories
|
||||
case (Some(p), None) =>
|
||||
(mem.src.width / p) * math.ceil(
|
||||
p.toDouble / lib.src.width
|
||||
) * lib.src.width //We map the mask to distinct memories
|
||||
case (Some(p), Some(m)) => {
|
||||
if(m <= p) (mem.src.width/p)*math.ceil(p.toDouble/m)*m //Using multiple m's to create a p (integeraly)
|
||||
else (mem.src.width/p)*m //Waste the extra maskbits
|
||||
if (m <= p) (mem.src.width / p) * math.ceil(p.toDouble / m) * m //Using multiple m's to create a p (integeraly)
|
||||
else (mem.src.width / p) * m //Waste the extra maskbits
|
||||
}
|
||||
}
|
||||
val depthCost = math.ceil(mem.src.depth.toDouble / lib.src.depth.toDouble)
|
||||
@@ -129,10 +131,10 @@ object DefaultMetric extends CostMetric with CostMetricCompanion {
|
||||
val bitsCost = (lib.src.depth * lib.src.width).toDouble
|
||||
// Fraction of wasted bits plus const per mem
|
||||
val requestedBits = (mem.src.depth * mem.src.width).toDouble
|
||||
val bitsWasted = depthCost*widthCost*bitsCost - requestedBits
|
||||
val bitsWasted = depthCost * widthCost * bitsCost - requestedBits
|
||||
val wastedConst = 0.05 // 0 means waste as few bits with no regard for instance count
|
||||
val costPerInst = wastedConst*depthCost*widthCost
|
||||
Some(1.0*bitsWasted/requestedBits+costPerInst)
|
||||
val costPerInst = wastedConst * depthCost * widthCost
|
||||
Some(1.0 * bitsWasted / requestedBits + costPerInst)
|
||||
}
|
||||
|
||||
override def commandLineParams = Map()
|
||||
@@ -147,10 +149,11 @@ object MacroCompilerUtil {
|
||||
// Adapted from https://stackoverflow.com/a/134918
|
||||
|
||||
/** Serialize an arbitrary object to String.
|
||||
* Used to pass structured values through as an annotation. */
|
||||
* Used to pass structured values through as an annotation.
|
||||
*/
|
||||
def objToString(o: Serializable): String = {
|
||||
val baos: ByteArrayOutputStream = new ByteArrayOutputStream
|
||||
val oos: ObjectOutputStream = new ObjectOutputStream(baos)
|
||||
val oos: ObjectOutputStream = new ObjectOutputStream(baos)
|
||||
oos.writeObject(o)
|
||||
oos.close()
|
||||
return Base64.getEncoder.encodeToString(baos.toByteArray)
|
||||
@@ -167,6 +170,7 @@ object MacroCompilerUtil {
|
||||
}
|
||||
|
||||
object CostMetric {
|
||||
|
||||
/** Define some default metric. */
|
||||
val default: CostMetric = DefaultMetric
|
||||
|
||||
@@ -177,13 +181,12 @@ object CostMetric {
|
||||
registerCostMetric(ExternalMetric)
|
||||
registerCostMetric(DefaultMetric)
|
||||
|
||||
/**
|
||||
* Register a cost metric.
|
||||
* @param createFuncHelper Companion object to fetch the name and construct
|
||||
* the metric.
|
||||
*/
|
||||
/** Register a cost metric.
|
||||
* @param createFuncHelper Companion object to fetch the name and construct
|
||||
* the metric.
|
||||
*/
|
||||
def registerCostMetric(createFuncHelper: CostMetricCompanion): Unit = {
|
||||
costMetricCreators.update(createFuncHelper.name, createFuncHelper)
|
||||
costMetricCreators.update(createFuncHelper.name(), createFuncHelper)
|
||||
}
|
||||
|
||||
/** Select a cost metric from string. */
|
||||
@@ -193,7 +196,7 @@ object CostMetric {
|
||||
} else if (!costMetricCreators.contains(m)) {
|
||||
throw new IllegalArgumentException("Invalid cost metric " + m)
|
||||
} else {
|
||||
costMetricCreators.get(m).get.construct(params)
|
||||
costMetricCreators(m).construct(params)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,24 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
/**
|
||||
* Terminology note:
|
||||
* mem - target memory to compile, in design (e.g. Mem() in rocket)
|
||||
* lib - technology SRAM(s) to use to compile mem
|
||||
*/
|
||||
/** Terminology note:
|
||||
* mem - target memory to compile, in design (e.g. Mem() in rocket)
|
||||
* lib - technology SRAM(s) to use to compile mem
|
||||
*/
|
||||
|
||||
package barstools.macros
|
||||
|
||||
import firrtl._
|
||||
import firrtl.ir._
|
||||
import firrtl.PrimOps
|
||||
import barstools.macros.Utils._
|
||||
import firrtl.Utils._
|
||||
import firrtl.annotations._
|
||||
import firrtl.transforms.{NoDCEAnnotation}
|
||||
import firrtl.CompilerUtils.getLoweringTransforms
|
||||
import mdf.macrolib.{PolarizedPort, PortPolarity, SRAMMacro, SRAMGroup, SRAMCompiler}
|
||||
import scala.collection.mutable.{ArrayBuffer, HashMap}
|
||||
import firrtl.ir._
|
||||
import firrtl.stage.{FirrtlSourceAnnotation, FirrtlStage, Forms, OutputFileAnnotation, RunFirrtlTransformAnnotation}
|
||||
import firrtl.transforms.NoDCEAnnotation
|
||||
import firrtl.{PrimOps, _}
|
||||
import mdf.macrolib._
|
||||
|
||||
import java.io.{File, FileWriter}
|
||||
import scala.io.{Source}
|
||||
import Utils._
|
||||
import scala.collection.mutable.{ArrayBuffer, HashMap}
|
||||
import scala.io.Source
|
||||
|
||||
case class MacroCompilerException(msg: String) extends Exception(msg)
|
||||
|
||||
@@ -30,56 +29,75 @@ case class MacroCompilerAnnotation(content: String) extends NoTargetAnnotation {
|
||||
def params: Params = MacroCompilerUtil.objFromString(content).asInstanceOf[Params]
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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]].
|
||||
*
|
||||
*/
|
||||
/** 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]].
|
||||
*/
|
||||
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
|
||||
|
||||
/** CompileAndSynflops mode - compile all memories and create mock versions of the target libs with synflops. */
|
||||
case object CompileAndSynflops 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
|
||||
/** CompileAvailable - compile what is possible and do nothing with uncompiled memories. **/
|
||||
|
||||
/** CompileAvailable - compile what is possible and do nothing with uncompiled memories. * */
|
||||
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?
|
||||
*/
|
||||
/** 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
|
||||
|
||||
// Options as list of (CompilerMode, command-line name, description)
|
||||
val options: Seq[(CompilerMode, String, String)] = Seq(
|
||||
(Default, "default", "Select the default option from below."),
|
||||
(Strict, "strict", "Compile all memories to library or return an error."),
|
||||
(Synflops, "synflops", "Produces synthesizable flop-based memories for all memories (do not map to lib at all); likely useful for simulation purposes."),
|
||||
(CompileAndSynflops, "compileandsynflops", "Compile all memories and create mock versions of the target libs with synflops; likely also useful for simulation purposes."),
|
||||
(FallbackSynflops, "fallbacksynflops", "Compile all memories to library when possible and fall back to synthesizable flop-based memories when library synth is not possible."),
|
||||
(CompileAvailable, "compileavailable", "Compile all memories to library when possible and do nothing in case of errors. (default)")
|
||||
(
|
||||
Synflops,
|
||||
"synflops",
|
||||
"Produces synthesizable flop-based memories for all memories (do not map to lib at all); likely useful for simulation purposes."
|
||||
),
|
||||
(
|
||||
CompileAndSynflops,
|
||||
"compileandsynflops",
|
||||
"Compile all memories and create mock versions of the target libs with synflops; likely also useful for simulation purposes."
|
||||
),
|
||||
(
|
||||
FallbackSynflops,
|
||||
"fallbacksynflops",
|
||||
"Compile all memories to library when possible and fall back to synthesizable flop-based memories when library synth is not possible."
|
||||
),
|
||||
(
|
||||
CompileAvailable,
|
||||
"compileavailable",
|
||||
"Compile all memories to library when possible and do nothing in case of errors. (default)"
|
||||
)
|
||||
)
|
||||
|
||||
/** Helper function to select a compiler mode. */
|
||||
def stringToCompilerMode(str: String): CompilerMode = options.collectFirst { case (mode, cmd, _) if cmd == str => mode } match {
|
||||
def stringToCompilerMode(str: String): CompilerMode = options.collectFirst {
|
||||
case (mode, cmd, _) if cmd == str => mode
|
||||
} match {
|
||||
case Some(x) => x
|
||||
case None => throw new IllegalArgumentException("No such compiler mode " + str)
|
||||
case None => 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 memFormat Type of memory lib (Some("conf"), Some("mdf"), or None (defaults to mdf))
|
||||
@@ -90,26 +108,34 @@ object MacroCompilerAnnotation {
|
||||
* @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
|
||||
*/
|
||||
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])
|
||||
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])
|
||||
|
||||
/**
|
||||
* Create a MacroCompilerAnnotation.
|
||||
* @param c Top-level circuit name (see class description)
|
||||
* @param p Parameters (see above).
|
||||
*/
|
||||
/** Create a MacroCompilerAnnotation.
|
||||
* @param c Top-level circuit name (see class description)
|
||||
* @param p Parameters (see above).
|
||||
*/
|
||||
def apply(c: String, p: Params): MacroCompilerAnnotation =
|
||||
MacroCompilerAnnotation(MacroCompilerUtil.objToString(p))
|
||||
|
||||
}
|
||||
|
||||
class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
libs: Option[Seq[Macro]],
|
||||
compilers: Option[SRAMCompiler],
|
||||
hammerIR: Option[String],
|
||||
costMetric: CostMetric = CostMetric.default,
|
||||
mode: MacroCompilerAnnotation.CompilerMode = MacroCompilerAnnotation.Default) extends firrtl.passes.Pass {
|
||||
class MacroCompilerPass(
|
||||
mems: Option[Seq[Macro]],
|
||||
libs: Option[Seq[Macro]],
|
||||
compilers: Option[SRAMCompiler],
|
||||
hammerIR: Option[String],
|
||||
costMetric: CostMetric = CostMetric.default,
|
||||
mode: MacroCompilerAnnotation.CompilerMode = MacroCompilerAnnotation.Default)
|
||||
extends firrtl.passes.Pass {
|
||||
// Helper function to check the legality of bitPairs.
|
||||
// e.g. ((0,21), (22,43)) is legal
|
||||
// ((0,21), (22,21)) is illegal and will throw an assert
|
||||
@@ -121,8 +147,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate bit pairs.
|
||||
/** Calculate bit pairs.
|
||||
* This is a list of submemories by width.
|
||||
* The tuples are (lsb, msb) inclusive.
|
||||
* Example: (0, 7) and (8, 15) might be a split for a width=16 memory into two width=8 target memories.
|
||||
@@ -133,7 +158,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
* @return Bit pairs or empty list if there was an error.
|
||||
*/
|
||||
private def calculateBitPairs(mem: Macro, lib: Macro): Seq[(BigInt, BigInt)] = {
|
||||
val pairedPorts = mem.sortedPorts zip lib.sortedPorts
|
||||
val pairedPorts = mem.sortedPorts.zip(lib.sortedPorts)
|
||||
|
||||
val bitPairs = ArrayBuffer[(BigInt, BigInt)]()
|
||||
var currentLSB: BigInt = 0
|
||||
@@ -204,7 +229,9 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
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.get} 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 Seq()
|
||||
}
|
||||
}
|
||||
@@ -267,9 +294,11 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
}
|
||||
|
||||
def compile(mem: Macro, lib: Macro): Option[(Module, Macro)] = {
|
||||
assert(mem.sortedPorts.lengthCompare(lib.sortedPorts.length) == 0,
|
||||
"mem and lib should have an equal number of ports")
|
||||
val pairedPorts = mem.sortedPorts zip lib.sortedPorts
|
||||
assert(
|
||||
mem.sortedPorts.lengthCompare(lib.sortedPorts.length) == 0,
|
||||
"mem and lib should have an equal number of ports"
|
||||
)
|
||||
val pairedPorts = mem.sortedPorts.zip(lib.sortedPorts)
|
||||
|
||||
// Width mapping. See calculateBitPairs.
|
||||
val bitPairs: Seq[(BigInt, BigInt)] = calculateBitPairs(mem, lib)
|
||||
@@ -288,14 +317,14 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
/* Palmer: If we've got a parallel memory then we've got to take the
|
||||
* address bits into account. */
|
||||
if (mem.src.depth > lib.src.depth) {
|
||||
mem.src.ports foreach { port =>
|
||||
mem.src.ports.foreach { port =>
|
||||
val high = MacroCompilerMath.ceilLog2(mem.src.depth)
|
||||
val low = MacroCompilerMath.ceilLog2(lib.src.depth)
|
||||
val ref = WRef(port.address.name)
|
||||
val nodeName = s"${ref.name}_sel"
|
||||
val tpe = UIntType(IntWidth(high-low))
|
||||
val tpe = UIntType(IntWidth(high - low))
|
||||
selects(ref.name) = WRef(nodeName, tpe)
|
||||
stmts += DefNode(NoInfo, nodeName, bits(ref, high-1, low))
|
||||
stmts += DefNode(NoInfo, nodeName, bits(ref, high - 1, low))
|
||||
// Donggyu: output selection should be piped
|
||||
if (port.output.isDefined) {
|
||||
val regName = s"${ref.name}_sel_reg"
|
||||
@@ -304,7 +333,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
and(WRef(ce.name, BoolType), WRef(re.name, BoolType))
|
||||
case (Some(ce), None) => WRef(ce.name, BoolType)
|
||||
case (None, Some(re)) => WRef(re.name, BoolType)
|
||||
case (None, None) => one
|
||||
case (None, None) => one
|
||||
}
|
||||
selectRegs(ref.name) = WRef(regName, tpe)
|
||||
stmts += DefRegister(NoInfo, regName, tpe, WRef(port.clock.get.name), zero, WRef(regName))
|
||||
@@ -318,18 +347,18 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
// Create the instance.
|
||||
stmts += WDefInstance(NoInfo, name, lib.src.name, lib.tpe)
|
||||
// Connect extra ports of the lib.
|
||||
stmts ++= lib.extraPorts map { case (portName, portValue) =>
|
||||
stmts ++= lib.extraPorts.map { case (portName, portValue) =>
|
||||
Connect(NoInfo, WSubField(WRef(name), portName), portValue)
|
||||
}
|
||||
}
|
||||
for ((memPort, libPort) <- pairedPorts) {
|
||||
val addrMatch = selects get memPort.src.address.name match {
|
||||
val addrMatch = selects.get(memPort.src.address.name) match {
|
||||
case None => one
|
||||
case Some(addr) =>
|
||||
val index = UIntLiteral(i, IntWidth(bitWidth(addr.tpe)))
|
||||
DoPrim(PrimOps.Eq, Seq(addr, index), Nil, index.tpe)
|
||||
}
|
||||
val addrMatchReg = selectRegs get memPort.src.address.name match {
|
||||
val addrMatchReg = selectRegs.get(memPort.src.address.name) match {
|
||||
case None => one
|
||||
case Some(reg) =>
|
||||
val index = UIntLiteral(i, IntWidth(bitWidth(reg.tpe)))
|
||||
@@ -342,29 +371,22 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
for (((low, high), j) <- bitPairs.zipWithIndex) {
|
||||
val inst = WRef(s"mem_${i}_${j}", lib.tpe)
|
||||
|
||||
def connectPorts2(mem: Expression,
|
||||
lib: String,
|
||||
polarity: Option[PortPolarity]): Statement =
|
||||
def connectPorts2(mem: Expression, lib: String, polarity: Option[PortPolarity]): Statement =
|
||||
Connect(NoInfo, WSubField(inst, lib), portToExpression(mem, polarity))
|
||||
def connectPorts(mem: Expression,
|
||||
lib: String,
|
||||
polarity: PortPolarity): Statement =
|
||||
def connectPorts(mem: Expression, lib: String, polarity: PortPolarity): Statement =
|
||||
connectPorts2(mem, lib, Some(polarity))
|
||||
|
||||
// Clock port mapping
|
||||
/* Palmer: FIXME: I don't handle memories with read/write clocks yet. */
|
||||
/* Colin not all libPorts have clocks but all memPorts do*/
|
||||
libPort.src.clock.foreach { cPort =>
|
||||
stmts += connectPorts(WRef(memPort.src.clock.get.name),
|
||||
cPort.name,
|
||||
cPort.polarity) }
|
||||
stmts += connectPorts(WRef(memPort.src.clock.get.name), cPort.name, cPort.polarity)
|
||||
}
|
||||
|
||||
// Adress port mapping
|
||||
/* Palmer: The address port to a memory is just the low-order bits of
|
||||
* the top address. */
|
||||
stmts += connectPorts(WRef(memPort.src.address.name),
|
||||
libPort.src.address.name,
|
||||
libPort.src.address.polarity)
|
||||
stmts += connectPorts(WRef(memPort.src.address.name), libPort.src.address.name, libPort.src.address.polarity)
|
||||
|
||||
// Output port mapping
|
||||
(memPort.src.output, libPort.src.output) match {
|
||||
@@ -374,20 +396,20 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
* done after generating all the memories. This saves up the
|
||||
* output statements for later. */
|
||||
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)
|
||||
cats += WRef(name)
|
||||
case (None, Some(lib)) =>
|
||||
/* Palmer: If the inner memory has an output port but the outer
|
||||
* one doesn't then it's safe to just leave the outer
|
||||
* port floating. */
|
||||
/* Palmer: If the inner memory has an output port but the outer
|
||||
* one doesn't then it's safe to just leave the outer
|
||||
* port floating. */
|
||||
case (None, None) =>
|
||||
/* Palmer: If there's no output ports at all (ie, read-only
|
||||
* port on the memory) then just don't worry about it,
|
||||
* there's nothing to do. */
|
||||
/* Palmer: If there's no output ports at all (ie, read-only
|
||||
* port on the memory) then just don't worry about it,
|
||||
* there's nothing to do. */
|
||||
case (Some(PolarizedPort(mem, _)), None) =>
|
||||
System.err println "WARNING: Unable to match output ports on memory"
|
||||
System.err println s" outer output port: ${mem}"
|
||||
System.err.println("WARNING: Unable to match output ports on memory")
|
||||
System.err.println(s" outer output port: ${mem}")
|
||||
return None
|
||||
}
|
||||
|
||||
@@ -397,7 +419,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
/* Palmer: The input port to a memory just needs to happen in parallel,
|
||||
* this does a part select to narrow the memory down. */
|
||||
stmts += connectPorts(bits(WRef(mem), high, low), lib, lib_polarity)
|
||||
case (None, Some(lib)) =>
|
||||
case (None, Some(lib)) =>
|
||||
/* Palmer: If the inner memory has an input port but the other
|
||||
* one doesn't then it's safe to just leave the inner
|
||||
* port floating. This should be handled by the
|
||||
@@ -406,12 +428,12 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
//Firrtl cares about dangling inputs now tie it off
|
||||
stmts += IsInvalid(NoInfo, WSubField(inst, lib.name))
|
||||
case (None, None) =>
|
||||
/* Palmer: If there's no input ports at all (ie, read-only
|
||||
* port on the memory) then just don't worry about it,
|
||||
* there's nothing to do. */
|
||||
/* Palmer: If there's no input ports at all (ie, read-only
|
||||
* port on the memory) then just don't worry about it,
|
||||
* there's nothing to do. */
|
||||
case (Some(PolarizedPort(mem, _)), None) =>
|
||||
System.err println "WARNING: Unable to match input ports on memory"
|
||||
System.err println s" outer input port: ${mem}"
|
||||
System.err.println("WARNING: Unable to match input ports on memory")
|
||||
System.err.println(s" outer input port: ${mem}")
|
||||
return None
|
||||
}
|
||||
|
||||
@@ -430,26 +452,33 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
// Example: if we have a lib whose maskGran is 8 but our mem's maskGran is 4.
|
||||
// The other case is if we're using a larger lib than mem.
|
||||
val usingLessThanLibMaskGran = (memPort.src.maskGran.get < libPort.src.effectiveMaskGran)
|
||||
val effectiveLibWidth = if (usingLessThanLibMaskGran)
|
||||
memPort.src.maskGran.get
|
||||
else
|
||||
libPort.src.width.get
|
||||
val effectiveLibWidth =
|
||||
if (usingLessThanLibMaskGran)
|
||||
memPort.src.maskGran.get
|
||||
else
|
||||
libPort.src.width.get
|
||||
|
||||
cat(((0 until libPort.src.width.get by libPort.src.effectiveMaskGran) map (i => {
|
||||
if (usingLessThanLibMaskGran && i >= effectiveLibWidth) {
|
||||
// If the memMaskGran is smaller than the lib's gran, then
|
||||
// zero out the upper bits.
|
||||
zero
|
||||
} else {
|
||||
if ((low + i) >= memPort.src.width.get) {
|
||||
// If our bit is larger than the whole width of the mem, just zero out the upper bits.
|
||||
zero
|
||||
} else {
|
||||
// Pick the appropriate bit from the mem mask.
|
||||
bits(WRef(mem), (low + i) / memPort.src.effectiveMaskGran)
|
||||
}
|
||||
}
|
||||
})).reverse)
|
||||
cat(
|
||||
(
|
||||
(0 until libPort.src.width.get by libPort.src.effectiveMaskGran)
|
||||
.map(i => {
|
||||
if (usingLessThanLibMaskGran && i >= effectiveLibWidth) {
|
||||
// If the memMaskGran is smaller than the lib's gran, then
|
||||
// zero out the upper bits.
|
||||
zero
|
||||
} else {
|
||||
if ((low + i) >= memPort.src.width.get) {
|
||||
// If our bit is larger than the whole width of the mem, just zero out the upper bits.
|
||||
zero
|
||||
} else {
|
||||
// Pick the appropriate bit from the mem mask.
|
||||
bits(WRef(mem), (low + i) / memPort.src.effectiveMaskGran)
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
.reverse
|
||||
)
|
||||
}
|
||||
case None =>
|
||||
/* If there is a lib mask port but no mem mask port, just turn on
|
||||
@@ -483,7 +512,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
// Chip enable port mapping
|
||||
val memChipEnable = memPort.src.chipEnable match {
|
||||
case Some(PolarizedPort(mem, _)) => WRef(mem)
|
||||
case None => one
|
||||
case None => one
|
||||
}
|
||||
|
||||
// Read enable port mapping
|
||||
@@ -502,7 +531,11 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
* implement the outer memory's collection of ports using what
|
||||
* the inner memory has availiable. */
|
||||
((libPort.src.maskPort, libPort.src.writeEnable, libPort.src.chipEnable): @unchecked) match {
|
||||
case (Some(PolarizedPort(mask, mask_polarity)), Some(PolarizedPort(we, we_polarity)), Some(PolarizedPort(en, en_polarity))) =>
|
||||
case (
|
||||
Some(PolarizedPort(mask, mask_polarity)),
|
||||
Some(PolarizedPort(we, we_polarity)),
|
||||
Some(PolarizedPort(en, en_polarity))
|
||||
) =>
|
||||
/* Palmer: This is the simple option: every port exists. */
|
||||
stmts += connectPorts(memMask, mask, mask_polarity)
|
||||
stmts += connectPorts(andAddrMatch(memWriteEnable), we, we_polarity)
|
||||
@@ -510,8 +543,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
case (Some(PolarizedPort(mask, mask_polarity)), Some(PolarizedPort(we, we_polarity)), None) =>
|
||||
/* Palmer: If we don't have a chip enable but do have mask ports. */
|
||||
stmts += connectPorts(memMask, mask, mask_polarity)
|
||||
stmts += connectPorts(andAddrMatch(and(memWriteEnable, memChipEnable)),
|
||||
we, we_polarity)
|
||||
stmts += connectPorts(andAddrMatch(and(memWriteEnable, memChipEnable)), we, we_polarity)
|
||||
case (None, Some(PolarizedPort(we, we_polarity)), chipEnable) =>
|
||||
if (bitWidth(memMask.tpe) == 1) {
|
||||
/* Palmer: If we're expected to provide mask ports without a
|
||||
@@ -519,13 +551,15 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
* write enable port instead of the mask port. */
|
||||
chipEnable match {
|
||||
case Some(PolarizedPort(en, en_polarity)) => {
|
||||
stmts += connectPorts(andAddrMatch(and(memWriteEnable, memMask)),
|
||||
we, we_polarity)
|
||||
stmts += connectPorts(andAddrMatch(and(memWriteEnable, memMask)), we, we_polarity)
|
||||
stmts += connectPorts(andAddrMatch(memChipEnable), en, en_polarity)
|
||||
}
|
||||
case _ => {
|
||||
stmts += connectPorts(andAddrMatch(and(and(memWriteEnable, memChipEnable), memMask)),
|
||||
we, we_polarity)
|
||||
stmts += connectPorts(
|
||||
andAddrMatch(and(and(memWriteEnable, memChipEnable), memMask)),
|
||||
we,
|
||||
we_polarity
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -533,8 +567,8 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
return None
|
||||
}
|
||||
case (None, None, None) =>
|
||||
// No write ports to match up (this may be a read-only port).
|
||||
// This isn't necessarily an error condition.
|
||||
// No write ports to match up (this may be a read-only port).
|
||||
// This isn't necessarily an error condition.
|
||||
}
|
||||
}
|
||||
// Cat macro outputs for selection
|
||||
@@ -542,7 +576,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
case Some(PolarizedPort(mem, _)) if cats.nonEmpty =>
|
||||
val name = s"${mem}_${i}"
|
||||
stmts += DefNode(NoInfo, name, cat(cats.toSeq.reverse))
|
||||
(outputs getOrElseUpdate (mem, ArrayBuffer[(Expression, Expression)]())) +=
|
||||
(outputs.getOrElseUpdate(mem, ArrayBuffer[(Expression, Expression)]())) +=
|
||||
(addrMatchReg -> WRef(name))
|
||||
case _ =>
|
||||
}
|
||||
@@ -550,15 +584,17 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
}
|
||||
// Connect mem outputs
|
||||
val zeroOutputValue: Expression = UIntLiteral(0, IntWidth(mem.src.width))
|
||||
mem.src.ports foreach { port =>
|
||||
mem.src.ports.foreach { port =>
|
||||
port.output match {
|
||||
case Some(PolarizedPort(mem, _)) => outputs get mem match {
|
||||
case Some(select) =>
|
||||
val output = (select foldRight (zeroOutputValue)) {
|
||||
case ((cond, tval), fval) => Mux(cond, tval, fval, fval.tpe) }
|
||||
stmts += Connect(NoInfo, WRef(mem), output)
|
||||
case None =>
|
||||
}
|
||||
case Some(PolarizedPort(mem, _)) =>
|
||||
outputs.get(mem) match {
|
||||
case Some(select) =>
|
||||
val output = (select.foldRight(zeroOutputValue)) { case ((cond, tval), fval) =>
|
||||
Mux(cond, tval, fval, fval.tpe)
|
||||
}
|
||||
stmts += Connect(NoInfo, WRef(mem), output)
|
||||
case None =>
|
||||
}
|
||||
case None =>
|
||||
}
|
||||
}
|
||||
@@ -573,123 +609,138 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
|
||||
// Try to compile each of the memories in mems.
|
||||
// The 'state' is c.modules, which is a list of all the firrtl modules
|
||||
// in the 'circuit'.
|
||||
(mems foldLeft c.modules){ (modules, mem) =>
|
||||
|
||||
val sram = mem.src
|
||||
def groupMatchesMask(group: SRAMGroup, mem:SRAMMacro): Boolean = {
|
||||
val memMask = mem.ports map (_.maskGran) find (_.isDefined) map (_.get)
|
||||
val libMask = group.ports map (_.maskGran) find (_.isDefined) map (_.get)
|
||||
(memMask, libMask) match {
|
||||
case (None, _) => true
|
||||
case (Some(_), None) => false
|
||||
case (Some(m), Some(l)) => l <= m //Ignore memories that don't have nice mask
|
||||
(mems.foldLeft(c.modules)) { (modules, mem) =>
|
||||
val sram = mem.src
|
||||
def groupMatchesMask(group: SRAMGroup, mem: SRAMMacro): Boolean = {
|
||||
val memMask = mem.ports.map(_.maskGran).find(_.isDefined).map(_.get)
|
||||
val libMask = group.ports.map(_.maskGran).find(_.isDefined).map(_.get)
|
||||
(memMask, libMask) match {
|
||||
case (None, _) => true
|
||||
case (Some(_), None) => false
|
||||
case (Some(m), Some(l)) => l <= m //Ignore memories that don't have nice mask
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add compiler memories that might map well to libs
|
||||
val compLibs = compilers match {
|
||||
case Some(SRAMCompiler(_, groups)) => {
|
||||
groups.filter(g => g.family == sram.family && groupMatchesMask(g, sram)).map( g => {
|
||||
for(w <- g.width; d <- g.depth if((sram.width % w == 0) && (sram.depth % d == 0)))
|
||||
yield Seq(new Macro(buildSRAMMacro(g, d, w, g.vt.head)))
|
||||
} )
|
||||
// Add compiler memories that might map well to libs
|
||||
val compLibs = compilers match {
|
||||
case Some(SRAMCompiler(_, groups)) => {
|
||||
groups
|
||||
.filter(g => g.family == sram.family && groupMatchesMask(g, sram))
|
||||
.map(g => {
|
||||
for {
|
||||
w <- g.width
|
||||
d <- g.depth if ((sram.width % w == 0) && (sram.depth % d == 0))
|
||||
} yield Seq(new Macro(buildSRAMMacro(g, d, w, g.vt.head)))
|
||||
})
|
||||
}
|
||||
case None => Seq()
|
||||
}
|
||||
case None => Seq()
|
||||
}
|
||||
val fullLibs = libs ++ compLibs.flatten.flatten
|
||||
val fullLibs = libs ++ compLibs.flatten.flatten
|
||||
|
||||
// Try to compile mem against each lib in libs, keeping track of the
|
||||
// best compiled version, external lib used, and cost.
|
||||
val (best, cost) = (fullLibs foldLeft (None: Option[(Module, Macro)], Double.MaxValue)){
|
||||
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
|
||||
* order, but I'm starting with what actually gets generated. */
|
||||
System.err println s"INFO: unable to compile ${mem.src.name} using ${lib.src.name} port count must match"
|
||||
(best, cost)
|
||||
case ((best, cost), lib) =>
|
||||
// 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}")
|
||||
// Try compiling
|
||||
compile(mem, lib) match {
|
||||
// If it was successful and the new cost is lower
|
||||
case Some(p) if (newCost < cost) => (Some(p), newCost)
|
||||
case _ => (best, cost)
|
||||
// Try to compile mem against each lib in libs, keeping track of the
|
||||
// best compiled version, external lib used, and cost.
|
||||
val (best, cost) = (fullLibs.foldLeft(None: Option[(Module, Macro)], Double.MaxValue)) {
|
||||
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
|
||||
* order, but I'm starting with what actually gets generated. */
|
||||
System.err.println(s"INFO: unable to compile ${mem.src.name} using ${lib.src.name} port count must match")
|
||||
(best, cost)
|
||||
case ((best, cost), lib) =>
|
||||
// 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}")
|
||||
// Try compiling
|
||||
compile(mem, lib) match {
|
||||
// If it was successful and the new cost is lower
|
||||
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.
|
||||
}
|
||||
}
|
||||
|
||||
// If we were able to compile anything, then replace the original module
|
||||
// in the modules list with a compiled version, as well as the extmodule
|
||||
// stub for the lib.
|
||||
best match {
|
||||
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)) =>
|
||||
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 =>
|
||||
|
||||
// If we were able to compile anything, then replace the original module
|
||||
// in the modules list with a compiled version, as well as the extmodule
|
||||
// stub for the lib.
|
||||
best match {
|
||||
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
|
||||
}
|
||||
(modules filterNot (m => m.name == mod.name || m.name == bb.blackbox.name)) ++ Seq(mod, bb.blackbox)
|
||||
case Some((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
|
||||
}
|
||||
c.copy(modules = modules)
|
||||
}
|
||||
}
|
||||
|
||||
class MacroCompilerTransform extends Transform {
|
||||
def inputForm = MidForm
|
||||
def outputForm = MidForm
|
||||
class MacroCompilerTransform extends Transform with DependencyAPIMigration {
|
||||
override def prerequisites = Forms.LowForm
|
||||
override def optionalPrerequisites = Forms.LowFormOptimized
|
||||
override def optionalPrerequisiteOf = Forms.LowEmitters
|
||||
override def invalidates(a: Transform) = false
|
||||
|
||||
def execute(state: CircuitState) = state.annotations.collect { case a: MacroCompilerAnnotation => a } match {
|
||||
case Seq(anno: MacroCompilerAnnotation) =>
|
||||
val MacroCompilerAnnotation.Params(memFile, memFileFormat, libFile, hammerIR, costMetric, mode, useCompiler, forceCompile, forceSynflops) = anno.params
|
||||
val MacroCompilerAnnotation.Params(
|
||||
memFile,
|
||||
memFileFormat,
|
||||
libFile,
|
||||
hammerIR,
|
||||
costMetric,
|
||||
mode,
|
||||
useCompiler,
|
||||
forceCompile,
|
||||
forceSynflops
|
||||
) = anno.params
|
||||
if (mode == MacroCompilerAnnotation.FallbackSynflops) {
|
||||
throw new UnsupportedOperationException("Not implemented yet")
|
||||
}
|
||||
|
||||
// Check that we don't have any modules both forced to compile and synflops.
|
||||
assert((forceCompile intersect forceSynflops).isEmpty, "Cannot have modules both forced to compile and synflops")
|
||||
assert((forceCompile.intersect(forceSynflops)).isEmpty, "Cannot have modules both forced to compile and synflops")
|
||||
|
||||
// Read, eliminate None, get only SRAM, make firrtl macro
|
||||
val mems: Option[Seq[Macro]] = (memFileFormat match {
|
||||
case Some("conf") => Utils.readConfFromPath(Some(memFile))
|
||||
case _ => mdf.macrolib.Utils.readMDFFromPath(Some(memFile))
|
||||
case _ => mdf.macrolib.Utils.readMDFFromPath(Some(memFile))
|
||||
}) match {
|
||||
case Some(x:Seq[mdf.macrolib.Macro]) =>
|
||||
Some(Utils.filterForSRAM(Some(x)) getOrElse(List()) map {new Macro(_)})
|
||||
case Some(x: Seq[mdf.macrolib.Macro]) =>
|
||||
Some(Utils.filterForSRAM(Some(x)).getOrElse(List()).map { new Macro(_) })
|
||||
case _ => None
|
||||
}
|
||||
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(_)})
|
||||
case Some(x: Seq[mdf.macrolib.Macro]) =>
|
||||
Some(Utils.filterForSRAM(Some(x)).getOrElse(List()).map { new Macro(_) })
|
||||
case _ => None
|
||||
}
|
||||
val compilers: Option[mdf.macrolib.SRAMCompiler] = mdf.macrolib.Utils.readMDFFromPath(libFile) match {
|
||||
case Some(x:Seq[mdf.macrolib.Macro]) =>
|
||||
if(useCompiler){
|
||||
case Some(x: Seq[mdf.macrolib.Macro]) =>
|
||||
if (useCompiler) {
|
||||
findSRAMCompiler(Some(x))
|
||||
}
|
||||
else None
|
||||
} else None
|
||||
case _ => None
|
||||
}
|
||||
|
||||
|
||||
// Helper function to turn a set of mem names into a Seq[Macro].
|
||||
def setToSeqMacro(names: Set[String]): Seq[Macro] = {
|
||||
names.toSeq.map(memName => mems.get.collectFirst { case m if m.src.name == memName => m }.get)
|
||||
@@ -707,21 +758,25 @@ class MacroCompilerTransform extends Transform {
|
||||
|
||||
val transforms = Seq(
|
||||
new MacroCompilerPass(memCompile, libs, compilers, hammerIR, costMetric, mode),
|
||||
new SynFlopsPass(true, memSynflops ++ (if (mode == MacroCompilerAnnotation.CompileAndSynflops) {
|
||||
libs.get
|
||||
} else {
|
||||
Seq.empty
|
||||
})))
|
||||
(transforms foldLeft state) ((s, xform) => xform runTransform s).copy(form = outputForm)
|
||||
new SynFlopsPass(
|
||||
true,
|
||||
memSynflops ++ (if (mode == MacroCompilerAnnotation.CompileAndSynflops) {
|
||||
libs.get
|
||||
} else {
|
||||
Seq.empty
|
||||
})
|
||||
)
|
||||
)
|
||||
(transforms.foldLeft(state))((s, xform) => xform.runTransform(s))
|
||||
case _ => state
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Use firrtl.LowerFirrtlOptimizations
|
||||
class MacroCompilerOptimizations extends SeqTransform {
|
||||
def inputForm: CircuitForm = LowForm
|
||||
|
||||
def outputForm: CircuitForm = LowForm
|
||||
class MacroCompilerOptimizations extends SeqTransform with DependencyAPIMigration {
|
||||
override def prerequisites = Forms.LowForm
|
||||
override def optionalPrerequisites = Forms.LowFormOptimized
|
||||
override def optionalPrerequisiteOf = Forms.LowEmitters
|
||||
override def invalidates(a: Transform) = false
|
||||
|
||||
def transforms: Seq[Transform] = Seq(
|
||||
passes.RemoveValidIf,
|
||||
@@ -730,16 +785,8 @@ class MacroCompilerOptimizations extends SeqTransform {
|
||||
new firrtl.transforms.ConstantPropagation,
|
||||
passes.Legalize,
|
||||
passes.SplitExpressions,
|
||||
passes.CommonSubexpressionElimination)
|
||||
}
|
||||
|
||||
class MacroCompiler extends Compiler {
|
||||
def emitter: Emitter = new VerilogEmitter
|
||||
|
||||
def transforms: Seq[Transform] =
|
||||
Seq(new MacroCompilerTransform) ++
|
||||
getLoweringTransforms(firrtl.ChirrtlForm, firrtl.LowForm) ++
|
||||
Seq(new MacroCompilerOptimizations)
|
||||
passes.CommonSubexpressionElimination
|
||||
)
|
||||
}
|
||||
|
||||
object MacroCompiler extends App {
|
||||
@@ -757,8 +804,9 @@ object MacroCompiler extends App {
|
||||
type MacroParamMap = Map[MacroParam, String]
|
||||
type CostParamMap = Map[String, String]
|
||||
type ForcedMemories = (Set[String], Set[String])
|
||||
val modeOptions: Seq[String] = MacroCompilerAnnotation.options
|
||||
.map { case (_, cmd, description) => s" $cmd: $description" }
|
||||
val modeOptions: Seq[String] = MacroCompilerAnnotation.options.map { case (_, cmd, description) =>
|
||||
s" $cmd: $description"
|
||||
}
|
||||
val usage: String = (Seq(
|
||||
"Options:",
|
||||
" -n, --macro-conf: The set of macros to compile in firrtl-generated conf format (exclusive with -m)",
|
||||
@@ -773,16 +821,20 @@ object MacroCompiler extends App {
|
||||
" --force-compile [mem]: Force the given memory to be compiled to target libs regardless of the mode",
|
||||
" --force-synflops [mem]: Force the given memory to be compiled via synflops regardless of the mode",
|
||||
" --mode:"
|
||||
) ++ modeOptions) mkString "\n"
|
||||
) ++ modeOptions).mkString("\n")
|
||||
|
||||
def parseArgs(map: MacroParamMap, costMap: CostParamMap, forcedMemories: ForcedMemories,
|
||||
args: List[String]): (MacroParamMap, CostParamMap, ForcedMemories) =
|
||||
def parseArgs(
|
||||
map: MacroParamMap,
|
||||
costMap: CostParamMap,
|
||||
forcedMemories: ForcedMemories,
|
||||
args: List[String]
|
||||
): (MacroParamMap, CostParamMap, ForcedMemories) =
|
||||
args match {
|
||||
case Nil => (map, costMap, forcedMemories)
|
||||
case ("-n" | "--macro-conf") :: value :: tail =>
|
||||
parseArgs(map + (Macros -> value) + (MacrosFormat -> "conf"), costMap, forcedMemories, tail)
|
||||
parseArgs(map + (Macros -> value) + (MacrosFormat -> "conf"), costMap, forcedMemories, tail)
|
||||
case ("-m" | "--macro-mdf") :: value :: tail =>
|
||||
parseArgs(map + (Macros -> value) + (MacrosFormat -> "mdf"), costMap, forcedMemories, tail)
|
||||
parseArgs(map + (Macros -> value) + (MacrosFormat -> "mdf"), costMap, forcedMemories, tail)
|
||||
case ("-l" | "--library") :: value :: tail =>
|
||||
parseArgs(map + (Library -> value), costMap, forcedMemories, tail)
|
||||
case ("-u" | "--use-compiler") :: tail =>
|
||||
@@ -810,11 +862,17 @@ object MacroCompiler extends App {
|
||||
}
|
||||
|
||||
def run(args: List[String]) {
|
||||
val (params, costParams, forcedMemories) = parseArgs(Map[MacroParam, String](), Map[String, String](), (Set.empty, Set.empty), args)
|
||||
val (params, costParams, forcedMemories) =
|
||||
parseArgs(Map[MacroParam, String](), Map[String, String](), (Set.empty, Set.empty), args)
|
||||
try {
|
||||
val macros = params.get(MacrosFormat) match {
|
||||
case Some("conf") => Utils.filterForSRAM(Utils.readConfFromPath(params.get(Macros))).get map (x => (new Macro(x)).blackbox)
|
||||
case _ => Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(params.get(Macros))).get map (x => (new Macro(x)).blackbox)
|
||||
case Some("conf") =>
|
||||
Utils.filterForSRAM(Utils.readConfFromPath(params.get(Macros))).get.map(x => (new Macro(x)).blackbox)
|
||||
case _ =>
|
||||
Utils
|
||||
.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(params.get(Macros)))
|
||||
.get
|
||||
.map(x => (new Macro(x)).blackbox)
|
||||
}
|
||||
|
||||
if (macros.nonEmpty) {
|
||||
@@ -822,35 +880,39 @@ object MacroCompiler extends App {
|
||||
// determined as the firrtl "top-level module".
|
||||
val circuit = Circuit(NoInfo, macros, macros.last.name)
|
||||
val annotations = AnnotationSeq(
|
||||
Seq(MacroCompilerAnnotation(
|
||||
circuit.main,
|
||||
MacroCompilerAnnotation.Params(
|
||||
params.get(Macros).get, params.get(MacrosFormat), params.get(Library),
|
||||
params.get(HammerIR),
|
||||
CostMetric.getCostMetric(params.getOrElse(CostFunc, "default"), costParams),
|
||||
MacroCompilerAnnotation.stringToCompilerMode(params.getOrElse(Mode, "default")),
|
||||
params.contains(UseCompiler),
|
||||
forceCompile = forcedMemories._1, forceSynflops = forcedMemories._2
|
||||
Seq(
|
||||
MacroCompilerAnnotation(
|
||||
circuit.main,
|
||||
MacroCompilerAnnotation.Params(
|
||||
params(Macros),
|
||||
params.get(MacrosFormat),
|
||||
params.get(Library),
|
||||
params.get(HammerIR),
|
||||
CostMetric.getCostMetric(params.getOrElse(CostFunc, "default"), costParams),
|
||||
MacroCompilerAnnotation.stringToCompilerMode(params.getOrElse(Mode, "default")),
|
||||
params.contains(UseCompiler),
|
||||
forceCompile = forcedMemories._1,
|
||||
forceSynflops = forcedMemories._2
|
||||
)
|
||||
)
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
// The actual MacroCompilerTransform basically just generates an input circuit
|
||||
val macroCompilerInput = CircuitState(circuit, MidForm, annotations)
|
||||
val macroCompilerInput = CircuitState(circuit, annotations)
|
||||
val macroCompiled = (new MacroCompilerTransform).execute(macroCompilerInput)
|
||||
|
||||
|
||||
// Since the MacroCompiler defines its own CLI, reconcile this with FIRRTL options
|
||||
val firOptions = new ExecutionOptionsManager("macrocompiler") with HasFirrtlOptions {
|
||||
firrtlOptions = FirrtlExecutionOptions(
|
||||
outputFileNameOverride = params.get(Verilog).getOrElse(""),
|
||||
noDCE = true,
|
||||
firrtlSource = Some(macroCompiled.circuit.serialize)
|
||||
)
|
||||
}
|
||||
|
||||
// Run FIRRTL compiler
|
||||
Driver.execute(firOptions)
|
||||
(new FirrtlStage).execute(
|
||||
Array.empty,
|
||||
Seq(
|
||||
OutputFileAnnotation(params.getOrElse(Verilog, "")),
|
||||
RunFirrtlTransformAnnotation(new VerilogEmitter),
|
||||
EmitCircuitAnnotation(classOf[VerilogEmitter]),
|
||||
NoDCEAnnotation,
|
||||
FirrtlSourceAnnotation(macroCompiled.circuit.serialize)
|
||||
)
|
||||
)
|
||||
|
||||
params.get(HammerIR) match {
|
||||
case Some(hammerIRFile: String) => {
|
||||
@@ -865,7 +927,7 @@ object MacroCompiler extends App {
|
||||
}
|
||||
} else {
|
||||
// Warn user
|
||||
System.err println "WARNING: Empty *.mems.conf file. No memories generated."
|
||||
System.err.println("WARNING: Empty *.mems.conf file. No memories generated.")
|
||||
|
||||
// Emit empty verilog file if no macros found
|
||||
params.get(Verilog) match {
|
||||
@@ -879,8 +941,11 @@ object MacroCompiler extends App {
|
||||
}
|
||||
} catch {
|
||||
case e: java.util.NoSuchElementException =>
|
||||
e.printStackTrace()
|
||||
println(usage)
|
||||
if (args.isEmpty) {
|
||||
println("Command line arguments must be specified")
|
||||
} else {
|
||||
e.printStackTrace()
|
||||
}
|
||||
e.printStackTrace()
|
||||
sys.exit(1)
|
||||
case e: MacroCompilerException =>
|
||||
151
macros/src/main/scala/barstools/macros/SynFlops.scala
Normal file
151
macros/src/main/scala/barstools/macros/SynFlops.scala
Normal file
@@ -0,0 +1,151 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package barstools.macros
|
||||
|
||||
import barstools.macros.Utils._
|
||||
import firrtl.Utils._
|
||||
import firrtl._
|
||||
import firrtl.ir._
|
||||
import firrtl.passes.MemPortUtils.memPortField
|
||||
|
||||
class SynFlopsPass(synflops: Boolean, libs: Seq[Macro]) extends firrtl.passes.Pass {
|
||||
val extraMods = scala.collection.mutable.ArrayBuffer.empty[Module]
|
||||
lazy val libMods = (libs.map { lib =>
|
||||
lib.src.name -> {
|
||||
val (dataType, dataWidth) = (lib.src.ports.foldLeft(None: Option[BigInt]))((res, port) =>
|
||||
(res, port.maskPort) match {
|
||||
case (_, None) =>
|
||||
res
|
||||
case (None, Some(_)) =>
|
||||
Some(port.effectiveMaskGran)
|
||||
case (Some(x), Some(_)) =>
|
||||
assert(x == port.effectiveMaskGran)
|
||||
res
|
||||
}
|
||||
) match {
|
||||
case None => (UIntType(IntWidth(lib.src.width)), lib.src.width)
|
||||
case Some(gran) => (UIntType(IntWidth(gran)), gran.intValue)
|
||||
}
|
||||
|
||||
val maxDepth = min(lib.src.depth, 1 << 26)
|
||||
val numMems = lib.src.depth / maxDepth
|
||||
|
||||
// Change macro to be mapped onto to look like the below mem
|
||||
// by changing its depth, and width
|
||||
val lib_macro = new Macro(
|
||||
lib.src.copy(
|
||||
name = "split_" + lib.src.name,
|
||||
depth = maxDepth,
|
||||
width = dataWidth,
|
||||
ports = lib.src.ports.map(p =>
|
||||
p.copy(
|
||||
width = p.width.map(_ => dataWidth),
|
||||
depth = p.depth.map(_ => maxDepth),
|
||||
maskGran = p.maskGran.map(_ => dataWidth)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
val mod_macro = (new MacroCompilerPass(None, None, None, None)).compile(lib, lib_macro)
|
||||
val (real_mod, real_macro) = mod_macro.get
|
||||
|
||||
val mem = DefMemory(
|
||||
NoInfo,
|
||||
"ram",
|
||||
dataType,
|
||||
maxDepth,
|
||||
1, // writeLatency
|
||||
1, // readLatency. This is possible because of VerilogMemDelays
|
||||
real_macro.readers.indices.map(i => s"R_$i"),
|
||||
real_macro.writers.indices.map(i => s"W_$i"),
|
||||
real_macro.readwriters.indices.map(i => s"RW_$i")
|
||||
)
|
||||
|
||||
val readConnects = real_macro.readers.zipWithIndex.flatMap { case (r, i) =>
|
||||
val clock = portToExpression(r.src.clock.get)
|
||||
val address = portToExpression(r.src.address)
|
||||
val enable = (r.src chipEnable, r.src readEnable) match {
|
||||
case (Some(en_port), Some(re_port)) =>
|
||||
and(portToExpression(en_port), portToExpression(re_port))
|
||||
case (Some(en_port), None) => portToExpression(en_port)
|
||||
case (None, Some(re_port)) => portToExpression(re_port)
|
||||
case (None, None) => one
|
||||
}
|
||||
val data = memPortField(mem, s"R_$i", "data")
|
||||
val read = data
|
||||
Seq(
|
||||
Connect(NoInfo, memPortField(mem, s"R_$i", "clk"), clock),
|
||||
Connect(NoInfo, memPortField(mem, s"R_$i", "addr"), address),
|
||||
Connect(NoInfo, memPortField(mem, s"R_$i", "en"), enable),
|
||||
Connect(NoInfo, WRef(r.src.output.get.name), read)
|
||||
)
|
||||
}
|
||||
|
||||
val writeConnects = real_macro.writers.zipWithIndex.flatMap { case (w, i) =>
|
||||
val clock = portToExpression(w.src.clock.get)
|
||||
val address = portToExpression(w.src.address)
|
||||
val enable = (w.src.chipEnable, w.src.writeEnable) match {
|
||||
case (Some(en), Some(we)) =>
|
||||
and(portToExpression(en), portToExpression(we))
|
||||
case (Some(en), None) => portToExpression(en)
|
||||
case (None, Some(we)) => portToExpression(we)
|
||||
case (None, None) => zero // is it possible?
|
||||
}
|
||||
val mask = w.src.maskPort match {
|
||||
case Some(m) => portToExpression(m)
|
||||
case None => one
|
||||
}
|
||||
val data = memPortField(mem, s"W_$i", "data")
|
||||
val write = portToExpression(w.src.input.get)
|
||||
Seq(
|
||||
Connect(NoInfo, memPortField(mem, s"W_$i", "clk"), clock),
|
||||
Connect(NoInfo, memPortField(mem, s"W_$i", "addr"), address),
|
||||
Connect(NoInfo, memPortField(mem, s"W_$i", "en"), enable),
|
||||
Connect(NoInfo, memPortField(mem, s"W_$i", "mask"), mask),
|
||||
Connect(NoInfo, data, write)
|
||||
)
|
||||
}
|
||||
|
||||
val readwriteConnects = real_macro.readwriters.zipWithIndex.flatMap { case (rw, i) =>
|
||||
val clock = portToExpression(rw.src.clock.get)
|
||||
val address = portToExpression(rw.src.address)
|
||||
val wmode = rw.src.writeEnable match {
|
||||
case Some(we) => portToExpression(we)
|
||||
case None => zero // is it possible?
|
||||
}
|
||||
val wmask = rw.src.maskPort match {
|
||||
case Some(wm) => portToExpression(wm)
|
||||
case None => one
|
||||
}
|
||||
val enable = (rw.src.chipEnable, rw.src.readEnable) match {
|
||||
case (Some(en), Some(re)) =>
|
||||
and(portToExpression(en), or(portToExpression(re), wmode))
|
||||
case (Some(en), None) => portToExpression(en)
|
||||
case (None, Some(re)) => or(portToExpression(re), wmode)
|
||||
case (None, None) => one
|
||||
}
|
||||
val wdata = memPortField(mem, s"RW_$i", "wdata")
|
||||
val rdata = memPortField(mem, s"RW_$i", "rdata")
|
||||
val write = portToExpression(rw.src.input.get)
|
||||
val read = rdata
|
||||
Seq(
|
||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "clk"), clock),
|
||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "addr"), address),
|
||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "en"), enable),
|
||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "wmode"), wmode),
|
||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "wmask"), wmask),
|
||||
Connect(NoInfo, WRef(rw.src.output.get.name), read),
|
||||
Connect(NoInfo, wdata, write)
|
||||
)
|
||||
}
|
||||
|
||||
extraMods.append(real_macro.module(Block(mem +: (readConnects ++ writeConnects ++ readwriteConnects))))
|
||||
real_mod
|
||||
}
|
||||
}).toMap
|
||||
|
||||
def run(c: Circuit): Circuit = {
|
||||
if (!synflops) c
|
||||
else c.copy(modules = (c.modules.map(m => libMods.getOrElse(m.name, m))) ++ extraMods)
|
||||
}
|
||||
}
|
||||
269
macros/src/main/scala/barstools/macros/Utils.scala
Normal file
269
macros/src/main/scala/barstools/macros/Utils.scala
Normal file
@@ -0,0 +1,269 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package barstools.macros
|
||||
|
||||
import firrtl.Utils.BoolType
|
||||
import firrtl.ir._
|
||||
import firrtl.passes.memlib._
|
||||
import firrtl.{PrimOps, _}
|
||||
import mdf.macrolib.{Input => _, Output => _, _}
|
||||
|
||||
import scala.language.implicitConversions
|
||||
|
||||
object MacroCompilerMath {
|
||||
def ceilLog2(x: BigInt): Int = (x - 1).bitLength
|
||||
}
|
||||
|
||||
class FirrtlMacroPort(port: MacroPort) {
|
||||
val src = port
|
||||
|
||||
val isReader = port.output.nonEmpty && port.input.isEmpty
|
||||
val isWriter = port.input.nonEmpty && port.output.isEmpty
|
||||
val isReadWriter = port.input.nonEmpty && port.output.nonEmpty
|
||||
|
||||
val addrType = UIntType(IntWidth(MacroCompilerMath.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(Field(port.address.name, Flip, addrType)) ++
|
||||
(port.clock.map(p => Field(p.name, Flip, ClockType))) ++
|
||||
(port.input.map(p => Field(p.name, Flip, dataType))) ++
|
||||
(port.output.map(p => Field(p.name, Default, dataType))) ++
|
||||
(port.chipEnable.map(p => Field(p.name, Flip, BoolType))) ++
|
||||
(port.readEnable.map(p => Field(p.name, Flip, BoolType))) ++
|
||||
(port.writeEnable.map(p => Field(p.name, Flip, BoolType))) ++
|
||||
(port.maskPort.map(p => Field(p.name, Flip, maskType)))
|
||||
)
|
||||
val ports = tpe.fields.map(f =>
|
||||
Port(
|
||||
NoInfo,
|
||||
f.name,
|
||||
f.flip match {
|
||||
case Default => Output
|
||||
case Flip => Input
|
||||
},
|
||||
f.tpe
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Reads an SRAMMacro and generates firrtl blackboxes.
|
||||
class Macro(srcMacro: SRAMMacro) {
|
||||
val src = srcMacro
|
||||
|
||||
val firrtlPorts = srcMacro.ports.map { new FirrtlMacroPort(_) }
|
||||
|
||||
val writers = firrtlPorts.filter(p => p.isWriter)
|
||||
val readers = firrtlPorts.filter(p => p.isReader)
|
||||
val readwriters = firrtlPorts.filter(p => p.isReadWriter)
|
||||
|
||||
val sortedPorts = writers ++ readers ++ readwriters
|
||||
val extraPorts = srcMacro.extraPorts.map { p =>
|
||||
assert(p.portType == Constant) // TODO: release it?
|
||||
val name = p.name
|
||||
val width = BigInt(p.width.toLong)
|
||||
val value = BigInt(p.value.toLong)
|
||||
(name -> UIntLiteral(value, IntWidth(width)))
|
||||
}
|
||||
|
||||
// Bundle representing this memory blackbox
|
||||
val tpe = BundleType(firrtlPorts.flatMap(_.tpe.fields))
|
||||
|
||||
private val modPorts = (firrtlPorts.flatMap(_.ports)) ++
|
||||
(extraPorts.map { case (name, value) => Port(NoInfo, name, Input, value.tpe) })
|
||||
val blackbox = ExtModule(NoInfo, srcMacro.name, modPorts, srcMacro.name, Nil)
|
||||
def module(body: Statement) = Module(NoInfo, srcMacro.name, modPorts, body)
|
||||
}
|
||||
|
||||
object Utils {
|
||||
def filterForSRAM(s: Option[Seq[mdf.macrolib.Macro]]): Option[Seq[mdf.macrolib.SRAMMacro]] = {
|
||||
s match {
|
||||
case Some(l: Seq[mdf.macrolib.Macro]) =>
|
||||
Some(l.filter { _.isInstanceOf[mdf.macrolib.SRAMMacro] }.map { m => m.asInstanceOf[mdf.macrolib.SRAMMacro] })
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
// This utility reads a conf in and returns MDF like mdf.macrolib.Utils.readMDFFromPath
|
||||
def readConfFromPath(path: Option[String]): Option[Seq[mdf.macrolib.Macro]] = {
|
||||
path.map((p) => Utils.readConfFromString(scala.io.Source.fromFile(p).mkString))
|
||||
}
|
||||
def readConfFromString(str: String): Seq[mdf.macrolib.Macro] = {
|
||||
MemConf.fromString(str).map { m: MemConf =>
|
||||
val ports = m.ports.map { case (port, num) => Seq.fill(num)(port) }.reduce(_ ++ _)
|
||||
SRAMMacro(
|
||||
m.name,
|
||||
m.width,
|
||||
m.depth,
|
||||
Utils.portSpecToFamily(ports),
|
||||
Utils.portSpecToMacroPort(m.width, m.depth, m.maskGranularity, ports)
|
||||
)
|
||||
}
|
||||
}
|
||||
def portSpecToFamily(ports: Seq[MemPort]): String = {
|
||||
val numR = ports.count(_ match { case ReadPort => true; case _ => false })
|
||||
val numW = ports.count(_ match { case WritePort | MaskedWritePort => true; case _ => false })
|
||||
val numRW = ports.count(_ match { case ReadWritePort | MaskedReadWritePort => true; case _ => false })
|
||||
val numRStr = if (numR > 0) s"${numR}r" else ""
|
||||
val numWStr = if (numW > 0) s"${numW}w" else ""
|
||||
val numRWStr = if (numRW > 0) s"${numRW}rw" else ""
|
||||
return numRStr + numWStr + numRWStr
|
||||
}
|
||||
// This translates between two represenations of ports
|
||||
def portSpecToMacroPort(width: Int, depth: BigInt, maskGran: Option[Int], ports: Seq[MemPort]): Seq[MacroPort] = {
|
||||
var numR = 0
|
||||
var numW = 0
|
||||
var numRW = 0
|
||||
ports.map {
|
||||
_ match {
|
||||
case ReadPort => {
|
||||
val portName = s"R${numR}"
|
||||
numR += 1
|
||||
MacroPort(
|
||||
width = Some(width),
|
||||
depth = Some(depth),
|
||||
address = PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||
clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||
readEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||
output = Some(PolarizedPort(s"${portName}_data", ActiveHigh))
|
||||
)
|
||||
}
|
||||
case WritePort => {
|
||||
val portName = s"W${numW}"
|
||||
numW += 1
|
||||
MacroPort(
|
||||
width = Some(width),
|
||||
depth = Some(depth),
|
||||
address = PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||
clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||
writeEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||
input = Some(PolarizedPort(s"${portName}_data", ActiveHigh))
|
||||
)
|
||||
}
|
||||
case MaskedWritePort => {
|
||||
val portName = s"W${numW}"
|
||||
numW += 1
|
||||
MacroPort(
|
||||
width = Some(width),
|
||||
depth = Some(depth),
|
||||
address = PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||
clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||
writeEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||
maskPort = Some(PolarizedPort(s"${portName}_mask", ActiveHigh)),
|
||||
maskGran = maskGran,
|
||||
input = Some(PolarizedPort(s"${portName}_data", ActiveHigh))
|
||||
)
|
||||
}
|
||||
case ReadWritePort => {
|
||||
val portName = s"RW${numRW}"
|
||||
numRW += 1
|
||||
MacroPort(
|
||||
width = Some(width),
|
||||
depth = Some(depth),
|
||||
address = PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||
clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||
chipEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||
writeEnable = Some(PolarizedPort(s"${portName}_wmode", ActiveHigh)),
|
||||
input = Some(PolarizedPort(s"${portName}_wdata", ActiveHigh)),
|
||||
output = Some(PolarizedPort(s"${portName}_rdata", ActiveHigh))
|
||||
)
|
||||
}
|
||||
case MaskedReadWritePort => {
|
||||
val portName = s"RW${numRW}"
|
||||
numRW += 1
|
||||
MacroPort(
|
||||
width = Some(width),
|
||||
depth = Some(depth),
|
||||
address = PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||
clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||
chipEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||
writeEnable = Some(PolarizedPort(s"${portName}_wmode", ActiveHigh)),
|
||||
maskPort = Some(PolarizedPort(s"${portName}_wmask", ActiveHigh)),
|
||||
maskGran = maskGran,
|
||||
input = Some(PolarizedPort(s"${portName}_wdata", ActiveHigh)),
|
||||
output = Some(PolarizedPort(s"${portName}_rdata", ActiveHigh))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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))),
|
||||
vt,
|
||||
g.mux,
|
||||
g.extraPorts
|
||||
)
|
||||
}
|
||||
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))),
|
||||
vt,
|
||||
g.mux,
|
||||
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)
|
||||
def or(e1: Expression, e2: Expression) =
|
||||
DoPrim(PrimOps.Or, Seq(e1, e2), Nil, e1.tpe)
|
||||
def bits(e: Expression, high: BigInt, low: BigInt): Expression =
|
||||
DoPrim(PrimOps.Bits, Seq(e), Seq(high, low), UIntType(IntWidth(high - low + 1)))
|
||||
def bits(e: Expression, idx: BigInt): Expression = bits(e, idx, idx)
|
||||
def cat(es: Seq[Expression]): Expression =
|
||||
if (es.size == 1) es.head
|
||||
else DoPrim(PrimOps.Cat, Seq(es.head, cat(es.tail)), Nil, UnknownType)
|
||||
def not(e: Expression) =
|
||||
DoPrim(PrimOps.Not, Seq(e), Nil, e.tpe)
|
||||
|
||||
// Convert a port to a FIRRTL expression, handling polarity along the way.
|
||||
def portToExpression(pp: PolarizedPort): Expression =
|
||||
portToExpression(WRef(pp.name), Some(pp.polarity))
|
||||
|
||||
def portToExpression(exp: Expression, polarity: Option[PortPolarity]): Expression =
|
||||
polarity match {
|
||||
case Some(ActiveLow) | Some(NegativeEdge) => not(exp)
|
||||
case _ => exp
|
||||
}
|
||||
|
||||
// Check if a number is a power of two
|
||||
def isPowerOfTwo(x: Int): Boolean = (x & (x - 1)) == 0
|
||||
}
|
||||
@@ -1,27 +1,29 @@
|
||||
[
|
||||
{
|
||||
"type" : "sram",
|
||||
"name" : "fake_mem",
|
||||
"width" : 64,
|
||||
"depth" : "512",
|
||||
"mux" : 4,
|
||||
"family" : "1rw",
|
||||
"ports" : [ {
|
||||
"address port name" : "addr",
|
||||
"address port polarity" : "active high",
|
||||
"clock port name" : "clk",
|
||||
"clock port polarity" : "positive edge",
|
||||
"write enable port name" : "wen",
|
||||
"write enable port polarity" : "active high",
|
||||
"read enable port name" : "ren",
|
||||
"read enable port polarity" : "active high",
|
||||
"output port name" : "dataout",
|
||||
"output port polarity" : "active high",
|
||||
"input port name" : "datain",
|
||||
"input port polarity" : "active high",
|
||||
"mask port name" : "mport",
|
||||
"mask port polarity" : "active low",
|
||||
"mask granularity" : 1
|
||||
} ]
|
||||
"type": "sram",
|
||||
"name": "fake_mem",
|
||||
"width": 64,
|
||||
"depth": "512",
|
||||
"mux": 4,
|
||||
"family": "1rw",
|
||||
"ports": [
|
||||
{
|
||||
"address port name": "addr",
|
||||
"address port polarity": "active high",
|
||||
"clock port name": "clk",
|
||||
"clock port polarity": "positive edge",
|
||||
"write enable port name": "wen",
|
||||
"write enable port polarity": "active high",
|
||||
"read enable port name": "ren",
|
||||
"read enable port polarity": "active high",
|
||||
"output port name": "dataout",
|
||||
"output port polarity": "active high",
|
||||
"input port name": "datain",
|
||||
"input port polarity": "active high",
|
||||
"mask port name": "mport",
|
||||
"mask port polarity": "active low",
|
||||
"mask granularity": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
[
|
||||
{
|
||||
"type" : "sram",
|
||||
"name" : "fake_mem",
|
||||
"width" : 64,
|
||||
"depth" : "4096",
|
||||
"mux" : 4,
|
||||
"family" : "1rw",
|
||||
"ports" : [ {
|
||||
"address port name" : "addr",
|
||||
"address port polarity" : "active high",
|
||||
"clock port name" : "clk",
|
||||
"clock port polarity" : "positive edge",
|
||||
"write enable port name" : "wen",
|
||||
"write enable port polarity" : "active high",
|
||||
"read enable port name" : "ren",
|
||||
"read enable port polarity" : "active high",
|
||||
"output port name" : "dataout",
|
||||
"output port polarity" : "active high",
|
||||
"input port name" : "datain",
|
||||
"input port polarity" : "active high"
|
||||
} ]
|
||||
"type": "sram",
|
||||
"name": "fake_mem",
|
||||
"width": 64,
|
||||
"depth": "4096",
|
||||
"mux": 4,
|
||||
"family": "1rw",
|
||||
"ports": [
|
||||
{
|
||||
"address port name": "addr",
|
||||
"address port polarity": "active high",
|
||||
"clock port name": "clk",
|
||||
"clock port polarity": "positive edge",
|
||||
"write enable port name": "wen",
|
||||
"write enable port polarity": "active high",
|
||||
"read enable port name": "ren",
|
||||
"read enable port polarity": "active high",
|
||||
"output port name": "dataout",
|
||||
"output port polarity": "active high",
|
||||
"input port name": "datain",
|
||||
"input port polarity": "active high"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,453 +0,0 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package barstools.macros
|
||||
|
||||
import firrtl.ir.{Circuit, NoInfo}
|
||||
import firrtl.passes.RemoveEmpty
|
||||
import firrtl.Parser.parse
|
||||
|
||||
import java.io.{File, StringWriter}
|
||||
|
||||
import mdf.macrolib.SRAMMacro
|
||||
|
||||
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
|
||||
|
||||
// Override these to change the prefixing of macroDir and testDir
|
||||
val memPrefix: String = testDir
|
||||
val libPrefix: String = testDir
|
||||
val vPrefix: String = testDir
|
||||
|
||||
// Override this to use a different cost metric.
|
||||
// If this is None, the compile() call will not have any -c/-cp arguments, and
|
||||
// execute() will use CostMetric.default.
|
||||
val costMetric: Option[CostMetric] = None
|
||||
private def getCostMetric: CostMetric = costMetric.getOrElse(CostMetric.default)
|
||||
|
||||
private def costMetricCmdLine = {
|
||||
costMetric match {
|
||||
case None => Nil
|
||||
case Some(m) => {
|
||||
val name = m.name
|
||||
val params = m.commandLineParams
|
||||
List("-c", name) ++ params.flatMap{ case (key, value) => List("-cp", key, value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 (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
|
||||
// exception.
|
||||
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, 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, useCompiler))
|
||||
}
|
||||
|
||||
// Helper functions to write macro libraries to the given files.
|
||||
def writeToLib(lib: String, libs: Seq[mdf.macrolib.Macro]) = {
|
||||
mdf.macrolib.Utils.writeMDFToPath(Some(concat(libPrefix, lib)), libs)
|
||||
}
|
||||
|
||||
def writeToMem(mem: String, mems: Seq[mdf.macrolib.Macro]) = {
|
||||
mdf.macrolib.Utils.writeMDFToPath(Some(concat(memPrefix, mem)), mems)
|
||||
}
|
||||
|
||||
// Convenience function for running both compile, execute, and test at once.
|
||||
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)
|
||||
}
|
||||
|
||||
// Compare FIRRTL outputs after reparsing output with ScalaTest ("should be").
|
||||
def test(result: Circuit, output: String): Unit = {
|
||||
val gold = RemoveEmpty run parse(output)
|
||||
(result.serialize) should be (gold.serialize)
|
||||
}
|
||||
|
||||
// Execute the macro compiler and returns a Circuit containing the output of
|
||||
// the memory compiler.
|
||||
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]] = 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)
|
||||
val passes = Seq(
|
||||
new MacroCompilerPass(Some(mems), libs, None, None, getCostMetric, if (synflops) MacroCompilerAnnotation.Synflops else MacroCompilerAnnotation.Default),
|
||||
new SynFlopsPass(synflops, libs getOrElse mems),
|
||||
RemoveEmpty)
|
||||
val result: Circuit = (passes foldLeft circuit)((c, pass) => pass run c)
|
||||
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] = {
|
||||
b match {
|
||||
case Some(b2:String) => Some(a + "/" + b2)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A collection of standard SRAM generators.
|
||||
trait HasSRAMGenerator {
|
||||
import mdf.macrolib._
|
||||
import scala.language.implicitConversions
|
||||
implicit def Int2SomeInt(i: Int): Option[Int] = Some(i)
|
||||
implicit def BigInt2SomeBigInt(i: BigInt): Option[BigInt] = Some(i)
|
||||
|
||||
|
||||
// Generate a standard (read/write/combo) port for testing.
|
||||
// Helper methods for optional width argument
|
||||
def generateTestPort(
|
||||
prefix: String,
|
||||
width: Option[Int],
|
||||
depth: Option[BigInt],
|
||||
maskGran: Option[Int] = None,
|
||||
read: Boolean,
|
||||
readEnable: Boolean = false,
|
||||
write: Boolean,
|
||||
writeEnable: Boolean = false
|
||||
): MacroPort = {
|
||||
val realPrefix = if (prefix == "") "" else prefix + "_"
|
||||
|
||||
MacroPort(
|
||||
address = PolarizedPort(name = realPrefix + "addr", polarity = ActiveHigh),
|
||||
clock = Some(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,
|
||||
|
||||
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))
|
||||
case _ => None
|
||||
},
|
||||
maskGran = maskGran,
|
||||
|
||||
width = width, depth = depth // These numbers don't matter here.
|
||||
)
|
||||
}
|
||||
|
||||
// Generate a read port for testing.
|
||||
def generateReadPort(prefix: String, width: Option[Int], depth: Option[BigInt], 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: Option[Int], depth: Option[BigInt], 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: Option[Int], depth: Option[BigInt], maskGran: Option[Int] = None): MacroPort = {
|
||||
generateTestPort(
|
||||
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: BigInt, 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
|
||||
)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Set up scaffolding for generating memories, files, etc.
|
||||
// Override this generator to specify the expected FIRRTL output.
|
||||
trait HasSimpleTestGenerator {
|
||||
this: MacroCompilerSpec with HasSRAMGenerator =>
|
||||
// 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: BigInt
|
||||
def libDepth: BigInt
|
||||
def memMaskGran: Option[Int] = None
|
||||
def libMaskGran: Option[Int] = None
|
||||
def extraPorts: Seq[mdf.macrolib.MacroExtraPort] = List()
|
||||
def extraTag: String = ""
|
||||
|
||||
// "Effective" libMaskGran by considering write_enable.
|
||||
val effectiveLibMaskGran = libMaskGran.getOrElse(libWidth)
|
||||
|
||||
// Override this in the sub-generator if you need a more specific name.
|
||||
// Defaults to using reflection to pull the name of the test using this
|
||||
// generator.
|
||||
def generatorType: String = this.getClass.getSimpleName
|
||||
|
||||
//require (memDepth >= libDepth)
|
||||
|
||||
// Convenience variables to check if a mask exists.
|
||||
val memHasMask = memMaskGran != None
|
||||
val libHasMask = libMaskGran != None
|
||||
// We need to figure out how many mask bits there are in the mem.
|
||||
val memMaskBits = if (memHasMask) memWidth / memMaskGran.get else 0
|
||||
val libMaskBits = if (libHasMask) libWidth / libMaskGran.get else 0
|
||||
|
||||
val extraTagPrefixed = if (extraTag == "") "" else ("-" + extraTag)
|
||||
|
||||
val mem = s"mem-${generatorType}${extraTagPrefixed}.json"
|
||||
val lib = s"lib-${generatorType}${extraTagPrefixed}.json"
|
||||
val v = s"${generatorType}${extraTagPrefixed}.v"
|
||||
|
||||
lazy val mem_name = "target_memory"
|
||||
val mem_addr_width = MacroCompilerMath.ceilLog2(memDepth)
|
||||
|
||||
lazy val lib_name = "awesome_lib_mem"
|
||||
val lib_addr_width = MacroCompilerMath.ceilLog2(libDepth)
|
||||
|
||||
// Override these to change the port prefixes if needed.
|
||||
def libPortPrefix: String = "lib"
|
||||
def memPortPrefix: String = "outer"
|
||||
|
||||
// These generate "simple" SRAMs (1 masked read-write port) by default,
|
||||
// but can be overridden if need be.
|
||||
def generateLibSRAM() = generateSRAM(lib_name, libPortPrefix, libWidth, libDepth, libMaskGran, extraPorts)
|
||||
def generateMemSRAM() = generateSRAM(mem_name, memPortPrefix, memWidth, memDepth, memMaskGran)
|
||||
|
||||
def libSRAM = generateLibSRAM
|
||||
def memSRAM = generateMemSRAM
|
||||
|
||||
def libSRAMs: Seq[SRAMMacro] = Seq(libSRAM)
|
||||
def memSRAMs: Seq[SRAMMacro] = Seq(memSRAM)
|
||||
|
||||
writeToLib(lib, libSRAMs)
|
||||
writeToMem(mem, memSRAMs)
|
||||
|
||||
// For masks, width it's a bit tricky since we have to consider cases like
|
||||
// memMaskGran = 4 and libMaskGran = 8.
|
||||
// Consider the actually usable libWidth in cases like the above.
|
||||
val usableLibWidth = if (memMaskGran.getOrElse(Int.MaxValue) < effectiveLibMaskGran) memMaskGran.get else libWidth
|
||||
|
||||
// Number of lib instances needed to hold the mem, in both directions.
|
||||
// Round up (e.g. 1.5 instances = effectively 2 instances)
|
||||
val depthInstances = math.ceil(memDepth.toFloat / libDepth.toFloat).toInt
|
||||
val widthInstances = math.ceil(memWidth.toFloat / usableLibWidth).toInt
|
||||
|
||||
// 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.
|
||||
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.
|
||||
* @param widthInst Width instance (mem_0_x)
|
||||
* @param depthInst Depth instance (mem_x_0)
|
||||
*/
|
||||
def generateMaskStatement(widthInst: Int, depthInst: Int): String = {
|
||||
// Width of this submemory.
|
||||
val myMemWidth = if (widthInst == widthInstances - 1) lastWidthBits else usableLibWidth
|
||||
// Base bit of this submemory.
|
||||
// e.g. if libWidth is 8 and this is submemory 2 (0-indexed), then this
|
||||
// would be 16.
|
||||
val myBaseBit = usableLibWidth*widthInst
|
||||
|
||||
if (libMaskGran.isDefined) {
|
||||
if (memMaskGran.isEmpty) {
|
||||
// If there is no memory mask, we should just turn all the lib mask
|
||||
// bits high.
|
||||
s"""mem_${depthInst}_${widthInst}.lib_mask <= UInt<${libMaskBits}>("h${((1 << libMaskBits) - 1).toHexString}")"""
|
||||
} else {
|
||||
// Calculate which bit of outer_mask contains the given bit.
|
||||
// e.g. if memMaskGran = 2, libMaskGran = 1 and libWidth = 4, then
|
||||
// calculateMaskBit({0, 1}) = 0 and calculateMaskBit({1, 2}) = 1
|
||||
def calculateMaskBit(bit:Int): Int = bit / memMaskGran.getOrElse(memWidth)
|
||||
|
||||
val bitsArr = ((libMaskBits - 1 to 0 by -1) map (x => {
|
||||
if (x*libMaskGran.get > myMemWidth) {
|
||||
// If we have extra mask bits leftover after the effective width,
|
||||
// disable those bits.
|
||||
"""UInt<1>("h0")"""
|
||||
} else {
|
||||
val outerMaskBit = calculateMaskBit(x*libMaskGran.get + myBaseBit)
|
||||
s"bits(outer_mask, ${outerMaskBit}, ${outerMaskBit})"
|
||||
}
|
||||
}))
|
||||
val maskVal = bitsArr.reduceRight((bit, rest) => s"cat($bit, $rest)")
|
||||
s"mem_${depthInst}_${widthInst}.lib_mask <= ${maskVal}"
|
||||
}
|
||||
} else ""
|
||||
}
|
||||
|
||||
/** Helper function to generate a port.
|
||||
*
|
||||
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
||||
* @param addrWidth Address port width
|
||||
* @param width data width
|
||||
* @param write Has a write port?
|
||||
* @param writeEnable Has a write enable port?
|
||||
* @param read Has a read port?
|
||||
* @param readEnable Has a read enable port?
|
||||
* @param mask Mask granularity (# bits) of the port or None.
|
||||
* @param extraPorts Extra ports (name, # bits)
|
||||
*/
|
||||
def generatePort(prefix: String, addrWidth: Int, width: Int, write: Boolean, writeEnable: Boolean, read: Boolean, readEnable: Boolean, mask: Option[Int], extraPorts: Seq[(String, Int)] = Seq()): String = {
|
||||
val realPrefix = if (prefix == "") "" else prefix + "_"
|
||||
|
||||
val readStr = if (read) s"output ${realPrefix}dout : UInt<$width>" else ""
|
||||
val writeStr = if (write) s"input ${realPrefix}din : UInt<$width>" else ""
|
||||
val readEnableStr = if (readEnable) s"input ${realPrefix}read_en : UInt<1>" else ""
|
||||
val writeEnableStr = if (writeEnable) s"input ${realPrefix}write_en : UInt<1>" else ""
|
||||
val maskStr = mask match {
|
||||
case Some(maskBits: Int) => s"input ${realPrefix}mask : UInt<$maskBits>"
|
||||
case _ => ""
|
||||
}
|
||||
val extraPortsStr = extraPorts.map { case (name, bits) => s" input $name : UInt<$bits>" }.mkString("\n")
|
||||
s"""
|
||||
input ${realPrefix}addr : UInt<$addrWidth>
|
||||
input ${realPrefix}clk : Clock
|
||||
$writeStr
|
||||
$readStr
|
||||
$readEnableStr
|
||||
$writeEnableStr
|
||||
$maskStr
|
||||
$extraPortsStr
|
||||
"""
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to generate a RW footer port.
|
||||
*
|
||||
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
||||
* @param readEnable Has a read enable port?
|
||||
* @param mask Mask granularity (# bits) of the port or None.
|
||||
* @param extraPorts Extra ports (name, # bits)
|
||||
*/
|
||||
def generateReadWriteFooterPort(prefix: String, readEnable: Boolean, mask: Option[Int], extraPorts: Seq[(String, Int)] = Seq()): String = {
|
||||
generatePort(prefix, lib_addr_width, libWidth,
|
||||
write = true, writeEnable = true, read = true, readEnable = readEnable, mask = mask, extraPorts = extraPorts)
|
||||
}
|
||||
|
||||
/** Helper function to generate a RW header port.
|
||||
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
||||
* @param readEnable Has a read enable port?
|
||||
* @param mask Mask granularity (# bits) of the port or None. */
|
||||
def generateReadWriteHeaderPort(prefix: String, readEnable: Boolean, mask: Option[Int]): String = {
|
||||
generatePort(prefix, mem_addr_width, memWidth,
|
||||
write=true, writeEnable=true, read=true, readEnable=readEnable, mask)
|
||||
}
|
||||
|
||||
// Generate the header memory ports.
|
||||
def generateHeaderPorts(): String = {
|
||||
require (memSRAM.ports.size == 1, "Header generator only supports single RW port mem")
|
||||
generateReadWriteHeaderPort(memPortPrefix, memSRAM.ports(0).readEnable.isDefined, if (memHasMask) Some(memMaskBits) else None)
|
||||
}
|
||||
|
||||
// Generate the header (contains the circuit statement and the target memory
|
||||
// module.
|
||||
def generateHeader(): String = {
|
||||
s"""
|
||||
circuit $mem_name :
|
||||
module $mem_name :
|
||||
${generateHeaderPorts}
|
||||
"""
|
||||
}
|
||||
|
||||
// Generate the target memory ports.
|
||||
def generateFooterPorts(): String = {
|
||||
require(libSRAM.ports.size == 1, "Footer generator only supports single RW port mem")
|
||||
generateReadWriteFooterPort(libPortPrefix, libSRAM.ports(0).readEnable.isDefined,
|
||||
if (libHasMask) Some(libMaskBits) else None, extraPorts.map(p => (p.name, p.width)))
|
||||
}
|
||||
|
||||
// Generate the footer (contains the target memory extmodule declaration by default).
|
||||
def generateFooter(): String = {
|
||||
s"""
|
||||
extmodule $lib_name :
|
||||
${generateFooterPorts}
|
||||
|
||||
defname = $lib_name
|
||||
"""
|
||||
}
|
||||
|
||||
// Abstract method to generate body; to be overridden by specific generator type.
|
||||
def generateBody(): String
|
||||
|
||||
// Generate the entire output from header, body, and footer.
|
||||
def generateOutput(): String = {
|
||||
s"""
|
||||
${generateHeader}
|
||||
${generateBody}
|
||||
${generateFooter}
|
||||
"""
|
||||
}
|
||||
|
||||
val output = generateOutput()
|
||||
}
|
||||
|
||||
// Use this trait for tests that invoke the memory compiler without lib.
|
||||
trait HasNoLibTestGenerator extends HasSimpleTestGenerator {
|
||||
this: MacroCompilerSpec with HasSRAMGenerator =>
|
||||
|
||||
// If there isn't a lib, then the "lib" will become a FIRRTL "mem", which
|
||||
// in turn becomes synthesized flops.
|
||||
// Therefore, make "lib" width/depth equal to the mem.
|
||||
override lazy val libDepth = memDepth
|
||||
override lazy val libWidth = memWidth
|
||||
override lazy val lib_name = mem_name
|
||||
// Do the same for port names.
|
||||
override lazy val libPortPrefix = memPortPrefix
|
||||
|
||||
// If there is no lib, don't generate a body.
|
||||
override def generateBody = ""
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@ import mdf.macrolib._
|
||||
|
||||
/** Tests to check that the cost function mechanism is working properly. */
|
||||
|
||||
/**
|
||||
* A test metric that simply favours memories with smaller widths, to test that
|
||||
* the metric is chosen properly.
|
||||
*/
|
||||
/** A test metric that simply favours memories with smaller widths, to test that
|
||||
* the metric is chosen properly.
|
||||
*/
|
||||
object TestMinWidthMetric extends CostMetric with CostMetricCompanion {
|
||||
// Smaller width = lower cost = favoured
|
||||
override def cost(mem: Macro, lib: Macro): Option[Double] = Some(lib.src.width)
|
||||
@@ -30,29 +29,29 @@ class SelectCostMetric extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
|
||||
val libSRAMs = Seq(
|
||||
SRAMMacro(
|
||||
name="SRAM_WIDTH_128",
|
||||
depth=BigInt(1024),
|
||||
width=128,
|
||||
family="1rw",
|
||||
ports=Seq(
|
||||
name = "SRAM_WIDTH_128",
|
||||
depth = BigInt(1024),
|
||||
width = 128,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateReadWritePort("", 128, BigInt(1024))
|
||||
)
|
||||
),
|
||||
SRAMMacro(
|
||||
name="SRAM_WIDTH_64",
|
||||
depth=BigInt(1024),
|
||||
width=64,
|
||||
family="1rw",
|
||||
ports=Seq(
|
||||
name = "SRAM_WIDTH_64",
|
||||
depth = BigInt(1024),
|
||||
width = 64,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateReadWritePort("", 64, BigInt(1024))
|
||||
)
|
||||
),
|
||||
SRAMMacro(
|
||||
name="SRAM_WIDTH_32",
|
||||
depth=BigInt(1024),
|
||||
width=32,
|
||||
family="1rw",
|
||||
ports=Seq(
|
||||
name = "SRAM_WIDTH_32",
|
||||
depth = BigInt(1024),
|
||||
width = 32,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateReadWritePort("", 32, BigInt(1024))
|
||||
)
|
||||
)
|
||||
@@ -65,7 +64,7 @@ class SelectCostMetric extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
|
||||
// Check that the min width SRAM was chosen, even though it is less efficient.
|
||||
val output =
|
||||
"""
|
||||
"""
|
||||
circuit target_memory :
|
||||
module target_memory :
|
||||
input addr : UInt<10>
|
||||
556
macros/src/test/scala/barstools/macros/MacroCompilerSpec.scala
Normal file
556
macros/src/test/scala/barstools/macros/MacroCompilerSpec.scala
Normal file
@@ -0,0 +1,556 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package barstools.macros
|
||||
|
||||
import firrtl.Parser.parse
|
||||
import firrtl.ir.{Circuit, NoInfo}
|
||||
import firrtl.passes.RemoveEmpty
|
||||
import mdf.macrolib.SRAMMacro
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
import java.io.File
|
||||
|
||||
abstract class MacroCompilerSpec extends AnyFlatSpec with 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
|
||||
|
||||
// Override these to change the prefixing of macroDir and testDir
|
||||
val memPrefix: String = testDir
|
||||
val libPrefix: String = testDir
|
||||
val vPrefix: String = testDir
|
||||
|
||||
// Override this to use a different cost metric.
|
||||
// If this is None, the compile() call will not have any -c/-cp arguments, and
|
||||
// execute() will use CostMetric.default.
|
||||
val costMetric: Option[CostMetric] = None
|
||||
private def getCostMetric: CostMetric = costMetric.getOrElse(CostMetric.default)
|
||||
|
||||
private def costMetricCmdLine = {
|
||||
costMetric match {
|
||||
case None => Nil
|
||||
case Some(m) => {
|
||||
val name = m.name
|
||||
val params = m.commandLineParams
|
||||
List("-c", name) ++ params.flatMap { case (key, value) => List("-cp", key, value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 (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
|
||||
// exception.
|
||||
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, 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, useCompiler))
|
||||
}
|
||||
|
||||
// Helper functions to write macro libraries to the given files.
|
||||
def writeToLib(lib: String, libs: Seq[mdf.macrolib.Macro]) = {
|
||||
mdf.macrolib.Utils.writeMDFToPath(Some(concat(libPrefix, lib)), libs)
|
||||
}
|
||||
|
||||
def writeToMem(mem: String, mems: Seq[mdf.macrolib.Macro]) = {
|
||||
mdf.macrolib.Utils.writeMDFToPath(Some(concat(memPrefix, mem)), mems)
|
||||
}
|
||||
|
||||
// Convenience function for running both compile, execute, and test at once.
|
||||
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)
|
||||
}
|
||||
|
||||
// Compare FIRRTL outputs after reparsing output with ScalaTest ("should be").
|
||||
def test(result: Circuit, output: String): Unit = {
|
||||
val gold = RemoveEmpty.run(parse(output))
|
||||
(result.serialize) should be(gold.serialize)
|
||||
}
|
||||
|
||||
// Execute the macro compiler and returns a Circuit containing the output of
|
||||
// the memory compiler.
|
||||
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]] = 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)
|
||||
val passes = Seq(
|
||||
new MacroCompilerPass(
|
||||
Some(mems),
|
||||
libs,
|
||||
None,
|
||||
None,
|
||||
getCostMetric,
|
||||
if (synflops) MacroCompilerAnnotation.Synflops else MacroCompilerAnnotation.Default
|
||||
),
|
||||
new SynFlopsPass(synflops, libs.getOrElse(mems)),
|
||||
RemoveEmpty
|
||||
)
|
||||
val result: Circuit = (passes.foldLeft(circuit))((c, pass) => pass.run(c))
|
||||
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] = {
|
||||
b match {
|
||||
case Some(b2: String) => Some(a + "/" + b2)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A collection of standard SRAM generators.
|
||||
trait HasSRAMGenerator {
|
||||
import mdf.macrolib._
|
||||
|
||||
import scala.language.implicitConversions
|
||||
implicit def Int2SomeInt(i: Int): Option[Int] = Some(i)
|
||||
implicit def BigInt2SomeBigInt(i: BigInt): Option[BigInt] = Some(i)
|
||||
|
||||
// Generate a standard (read/write/combo) port for testing.
|
||||
// Helper methods for optional width argument
|
||||
def generateTestPort(
|
||||
prefix: String,
|
||||
width: Option[Int],
|
||||
depth: Option[BigInt],
|
||||
maskGran: Option[Int] = None,
|
||||
read: Boolean,
|
||||
readEnable: Boolean = false,
|
||||
write: Boolean,
|
||||
writeEnable: Boolean = false
|
||||
): MacroPort = {
|
||||
val realPrefix = if (prefix == "") "" else prefix + "_"
|
||||
|
||||
MacroPort(
|
||||
address = PolarizedPort(name = realPrefix + "addr", polarity = ActiveHigh),
|
||||
clock = Some(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,
|
||||
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))
|
||||
case _ => None
|
||||
},
|
||||
maskGran = maskGran,
|
||||
width = width,
|
||||
depth = depth // These numbers don't matter here.
|
||||
)
|
||||
}
|
||||
|
||||
// Generate a read port for testing.
|
||||
def generateReadPort(
|
||||
prefix: String,
|
||||
width: Option[Int],
|
||||
depth: Option[BigInt],
|
||||
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: Option[Int],
|
||||
depth: Option[BigInt],
|
||||
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: Option[Int],
|
||||
depth: Option[BigInt],
|
||||
maskGran: Option[Int] = None
|
||||
): MacroPort = {
|
||||
generateTestPort(
|
||||
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: BigInt,
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Set up scaffolding for generating memories, files, etc.
|
||||
// Override this generator to specify the expected FIRRTL output.
|
||||
trait HasSimpleTestGenerator {
|
||||
this: MacroCompilerSpec with HasSRAMGenerator =>
|
||||
// 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: BigInt
|
||||
def libDepth: BigInt
|
||||
def memMaskGran: Option[Int] = None
|
||||
def libMaskGran: Option[Int] = None
|
||||
def extraPorts: Seq[mdf.macrolib.MacroExtraPort] = List()
|
||||
def extraTag: String = ""
|
||||
|
||||
// "Effective" libMaskGran by considering write_enable.
|
||||
val effectiveLibMaskGran = libMaskGran.getOrElse(libWidth)
|
||||
|
||||
// Override this in the sub-generator if you need a more specific name.
|
||||
// Defaults to using reflection to pull the name of the test using this
|
||||
// generator.
|
||||
def generatorType: String = this.getClass.getSimpleName
|
||||
|
||||
//require (memDepth >= libDepth)
|
||||
|
||||
// Convenience variables to check if a mask exists.
|
||||
val memHasMask = memMaskGran != None
|
||||
val libHasMask = libMaskGran != None
|
||||
// We need to figure out how many mask bits there are in the mem.
|
||||
val memMaskBits = if (memHasMask) memWidth / memMaskGran.get else 0
|
||||
val libMaskBits = if (libHasMask) libWidth / libMaskGran.get else 0
|
||||
|
||||
val extraTagPrefixed = if (extraTag == "") "" else ("-" + extraTag)
|
||||
|
||||
val mem = s"mem-${generatorType}${extraTagPrefixed}.json"
|
||||
val lib = s"lib-${generatorType}${extraTagPrefixed}.json"
|
||||
val v = s"${generatorType}${extraTagPrefixed}.v"
|
||||
|
||||
lazy val mem_name = "target_memory"
|
||||
val mem_addr_width = MacroCompilerMath.ceilLog2(memDepth)
|
||||
|
||||
lazy val lib_name = "awesome_lib_mem"
|
||||
val lib_addr_width = MacroCompilerMath.ceilLog2(libDepth)
|
||||
|
||||
// Override these to change the port prefixes if needed.
|
||||
def libPortPrefix: String = "lib"
|
||||
def memPortPrefix: String = "outer"
|
||||
|
||||
// These generate "simple" SRAMs (1 masked read-write port) by default,
|
||||
// but can be overridden if need be.
|
||||
def generateLibSRAM() = generateSRAM(lib_name, libPortPrefix, libWidth, libDepth, libMaskGran, extraPorts)
|
||||
def generateMemSRAM() = generateSRAM(mem_name, memPortPrefix, memWidth, memDepth, memMaskGran)
|
||||
|
||||
def libSRAM = generateLibSRAM
|
||||
def memSRAM = generateMemSRAM
|
||||
|
||||
def libSRAMs: Seq[SRAMMacro] = Seq(libSRAM)
|
||||
def memSRAMs: Seq[SRAMMacro] = Seq(memSRAM)
|
||||
|
||||
writeToLib(lib, libSRAMs)
|
||||
writeToMem(mem, memSRAMs)
|
||||
|
||||
// For masks, width it's a bit tricky since we have to consider cases like
|
||||
// memMaskGran = 4 and libMaskGran = 8.
|
||||
// Consider the actually usable libWidth in cases like the above.
|
||||
val usableLibWidth = if (memMaskGran.getOrElse(Int.MaxValue) < effectiveLibMaskGran) memMaskGran.get else libWidth
|
||||
|
||||
// Number of lib instances needed to hold the mem, in both directions.
|
||||
// Round up (e.g. 1.5 instances = effectively 2 instances)
|
||||
val depthInstances = math.ceil(memDepth.toFloat / libDepth.toFloat).toInt
|
||||
val widthInstances = math.ceil(memWidth.toFloat / usableLibWidth).toInt
|
||||
|
||||
// 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.
|
||||
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.
|
||||
* @param widthInst Width instance (mem_0_x)
|
||||
* @param depthInst Depth instance (mem_x_0)
|
||||
*/
|
||||
def generateMaskStatement(widthInst: Int, depthInst: Int): String = {
|
||||
// Width of this submemory.
|
||||
val myMemWidth = if (widthInst == widthInstances - 1) lastWidthBits else usableLibWidth
|
||||
// Base bit of this submemory.
|
||||
// e.g. if libWidth is 8 and this is submemory 2 (0-indexed), then this
|
||||
// would be 16.
|
||||
val myBaseBit = usableLibWidth * widthInst
|
||||
|
||||
if (libMaskGran.isDefined) {
|
||||
if (memMaskGran.isEmpty) {
|
||||
// If there is no memory mask, we should just turn all the lib mask
|
||||
// bits high.
|
||||
s"""mem_${depthInst}_${widthInst}.lib_mask <= UInt<${libMaskBits}>("h${((1 << libMaskBits) - 1).toHexString}")"""
|
||||
} else {
|
||||
// Calculate which bit of outer_mask contains the given bit.
|
||||
// e.g. if memMaskGran = 2, libMaskGran = 1 and libWidth = 4, then
|
||||
// calculateMaskBit({0, 1}) = 0 and calculateMaskBit({1, 2}) = 1
|
||||
def calculateMaskBit(bit: Int): Int = bit / memMaskGran.getOrElse(memWidth)
|
||||
|
||||
val bitsArr = ((libMaskBits - 1 to 0 by -1).map(x => {
|
||||
if (x * libMaskGran.get > myMemWidth) {
|
||||
// If we have extra mask bits leftover after the effective width,
|
||||
// disable those bits.
|
||||
"""UInt<1>("h0")"""
|
||||
} else {
|
||||
val outerMaskBit = calculateMaskBit(x * libMaskGran.get + myBaseBit)
|
||||
s"bits(outer_mask, ${outerMaskBit}, ${outerMaskBit})"
|
||||
}
|
||||
}))
|
||||
val maskVal = bitsArr.reduceRight((bit, rest) => s"cat($bit, $rest)")
|
||||
s"mem_${depthInst}_${widthInst}.lib_mask <= ${maskVal}"
|
||||
}
|
||||
} else ""
|
||||
}
|
||||
|
||||
/** Helper function to generate a port.
|
||||
*
|
||||
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
||||
* @param addrWidth Address port width
|
||||
* @param width data width
|
||||
* @param write Has a write port?
|
||||
* @param writeEnable Has a write enable port?
|
||||
* @param read Has a read port?
|
||||
* @param readEnable Has a read enable port?
|
||||
* @param mask Mask granularity (# bits) of the port or None.
|
||||
* @param extraPorts Extra ports (name, # bits)
|
||||
*/
|
||||
def generatePort(
|
||||
prefix: String,
|
||||
addrWidth: Int,
|
||||
width: Int,
|
||||
write: Boolean,
|
||||
writeEnable: Boolean,
|
||||
read: Boolean,
|
||||
readEnable: Boolean,
|
||||
mask: Option[Int],
|
||||
extraPorts: Seq[(String, Int)] = Seq()
|
||||
): String = {
|
||||
val realPrefix = if (prefix == "") "" else prefix + "_"
|
||||
|
||||
val readStr = if (read) s"output ${realPrefix}dout : UInt<$width>" else ""
|
||||
val writeStr = if (write) s"input ${realPrefix}din : UInt<$width>" else ""
|
||||
val readEnableStr = if (readEnable) s"input ${realPrefix}read_en : UInt<1>" else ""
|
||||
val writeEnableStr = if (writeEnable) s"input ${realPrefix}write_en : UInt<1>" else ""
|
||||
val maskStr = mask match {
|
||||
case Some(maskBits: Int) => s"input ${realPrefix}mask : UInt<$maskBits>"
|
||||
case _ => ""
|
||||
}
|
||||
val extraPortsStr = extraPorts.map { case (name, bits) => s" input $name : UInt<$bits>" }.mkString("\n")
|
||||
s"""
|
||||
input ${realPrefix}addr : UInt<$addrWidth>
|
||||
input ${realPrefix}clk : Clock
|
||||
$writeStr
|
||||
$readStr
|
||||
$readEnableStr
|
||||
$writeEnableStr
|
||||
$maskStr
|
||||
$extraPortsStr
|
||||
"""
|
||||
}
|
||||
|
||||
/** Helper function to generate a RW footer port.
|
||||
*
|
||||
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
||||
* @param readEnable Has a read enable port?
|
||||
* @param mask Mask granularity (# bits) of the port or None.
|
||||
* @param extraPorts Extra ports (name, # bits)
|
||||
*/
|
||||
def generateReadWriteFooterPort(
|
||||
prefix: String,
|
||||
readEnable: Boolean,
|
||||
mask: Option[Int],
|
||||
extraPorts: Seq[(String, Int)] = Seq()
|
||||
): String = {
|
||||
generatePort(
|
||||
prefix,
|
||||
lib_addr_width,
|
||||
libWidth,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = readEnable,
|
||||
mask = mask,
|
||||
extraPorts = extraPorts
|
||||
)
|
||||
}
|
||||
|
||||
/** Helper function to generate a RW header port.
|
||||
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
||||
* @param readEnable Has a read enable port?
|
||||
* @param mask Mask granularity (# bits) of the port or None.
|
||||
*/
|
||||
def generateReadWriteHeaderPort(prefix: String, readEnable: Boolean, mask: Option[Int]): String = {
|
||||
generatePort(
|
||||
prefix,
|
||||
mem_addr_width,
|
||||
memWidth,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = readEnable,
|
||||
mask
|
||||
)
|
||||
}
|
||||
|
||||
// Generate the header memory ports.
|
||||
def generateHeaderPorts(): String = {
|
||||
require(memSRAM.ports.size == 1, "Header generator only supports single RW port mem")
|
||||
generateReadWriteHeaderPort(
|
||||
memPortPrefix,
|
||||
memSRAM.ports(0).readEnable.isDefined,
|
||||
if (memHasMask) Some(memMaskBits) else None
|
||||
)
|
||||
}
|
||||
|
||||
// Generate the header (contains the circuit statement and the target memory
|
||||
// module.
|
||||
def generateHeader(): String = {
|
||||
s"""
|
||||
circuit $mem_name :
|
||||
module $mem_name :
|
||||
${generateHeaderPorts}
|
||||
"""
|
||||
}
|
||||
|
||||
// Generate the target memory ports.
|
||||
def generateFooterPorts(): String = {
|
||||
require(libSRAM.ports.size == 1, "Footer generator only supports single RW port mem")
|
||||
generateReadWriteFooterPort(
|
||||
libPortPrefix,
|
||||
libSRAM.ports(0).readEnable.isDefined,
|
||||
if (libHasMask) Some(libMaskBits) else None,
|
||||
extraPorts.map(p => (p.name, p.width))
|
||||
)
|
||||
}
|
||||
|
||||
// Generate the footer (contains the target memory extmodule declaration by default).
|
||||
def generateFooter(): String = {
|
||||
s"""
|
||||
extmodule $lib_name :
|
||||
${generateFooterPorts}
|
||||
|
||||
defname = $lib_name
|
||||
"""
|
||||
}
|
||||
|
||||
// Abstract method to generate body; to be overridden by specific generator type.
|
||||
def generateBody(): String
|
||||
|
||||
// Generate the entire output from header, body, and footer.
|
||||
def generateOutput(): String = {
|
||||
s"""
|
||||
${generateHeader}
|
||||
${generateBody}
|
||||
${generateFooter}
|
||||
"""
|
||||
}
|
||||
|
||||
val output = generateOutput()
|
||||
}
|
||||
|
||||
// Use this trait for tests that invoke the memory compiler without lib.
|
||||
trait HasNoLibTestGenerator extends HasSimpleTestGenerator {
|
||||
this: MacroCompilerSpec with HasSRAMGenerator =>
|
||||
|
||||
// If there isn't a lib, then the "lib" will become a FIRRTL "mem", which
|
||||
// in turn becomes synthesized flops.
|
||||
// Therefore, make "lib" width/depth equal to the mem.
|
||||
override lazy val libDepth = memDepth
|
||||
override lazy val libWidth = memWidth
|
||||
override lazy val lib_name = mem_name
|
||||
// Do the same for port names.
|
||||
override lazy val libPortPrefix = memPortPrefix
|
||||
|
||||
// If there is no lib, don't generate a body.
|
||||
override def generateBody = ""
|
||||
}
|
||||
@@ -1,37 +1,39 @@
|
||||
package barstools.macros
|
||||
|
||||
import mdf.macrolib._
|
||||
|
||||
// Test the ability of the compiler to deal with various mask combinations.
|
||||
|
||||
trait MasksTestSettings {
|
||||
this: MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator =>
|
||||
override lazy val memDepth = BigInt(2048)
|
||||
override lazy val libDepth = BigInt(1024)
|
||||
override lazy val memDepth = BigInt(2048)
|
||||
override lazy val libDepth = BigInt(1024)
|
||||
}
|
||||
|
||||
// Try all four different kinds of mask config:
|
||||
/**
|
||||
*
|
||||
* Non-masked mem Masked mem
|
||||
* ---------------------------------
|
||||
* Non-masked lib | | |
|
||||
* ---------------------------------
|
||||
* Masked lib | | |
|
||||
* ---------------------------------
|
||||
*/
|
||||
/** Non-masked mem Masked mem
|
||||
* ---------------------------------
|
||||
* Non-masked lib | | |
|
||||
* ---------------------------------
|
||||
* Masked lib | | |
|
||||
* ---------------------------------
|
||||
*/
|
||||
|
||||
class Masks_FourTypes_NonMaskedMem_NonMaskedLib extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class Masks_FourTypes_NonMaskedMem_NonMaskedLib
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 32
|
||||
override lazy val memMaskGran = None
|
||||
override lazy val libWidth = 8
|
||||
override lazy val libWidth = 8
|
||||
override lazy val libMaskGran = None
|
||||
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_FourTypes_NonMaskedMem_MaskedLib extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class Masks_FourTypes_NonMaskedMem_MaskedLib
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 32
|
||||
override lazy val memMaskGran = None
|
||||
@@ -41,7 +43,10 @@ class Masks_FourTypes_NonMaskedMem_MaskedLib extends MacroCompilerSpec with HasS
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_FourTypes_MaskedMem_NonMaskedLib extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class Masks_FourTypes_MaskedMem_NonMaskedLib
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 32
|
||||
override lazy val memMaskGran = Some(8)
|
||||
@@ -51,7 +56,10 @@ class Masks_FourTypes_MaskedMem_NonMaskedLib extends MacroCompilerSpec with HasS
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_FourTypes_MaskedMem_NonMaskedLib_SmallerMaskGran extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class Masks_FourTypes_MaskedMem_NonMaskedLib_SmallerMaskGran
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 32
|
||||
override lazy val memMaskGran = Some(4)
|
||||
@@ -61,7 +69,10 @@ class Masks_FourTypes_MaskedMem_NonMaskedLib_SmallerMaskGran extends MacroCompil
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_FourTypes_MaskedMem_MaskedLib extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class Masks_FourTypes_MaskedMem_MaskedLib
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 32
|
||||
override lazy val memMaskGran = Some(8)
|
||||
@@ -71,7 +82,10 @@ class Masks_FourTypes_MaskedMem_MaskedLib extends MacroCompilerSpec with HasSRAM
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_FourTypes_MaskedMem_MaskedLib_SameMaskGran extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class Masks_FourTypes_MaskedMem_MaskedLib_SameMaskGran
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 32
|
||||
override lazy val memMaskGran = Some(8)
|
||||
@@ -81,7 +95,10 @@ class Masks_FourTypes_MaskedMem_MaskedLib_SameMaskGran extends MacroCompilerSpec
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_FourTypes_MaskedMem_MaskedLib_SmallerMaskGran extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class Masks_FourTypes_MaskedMem_MaskedLib_SmallerMaskGran
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 64
|
||||
override lazy val memMaskGran = Some(4)
|
||||
@@ -105,7 +122,11 @@ class Masks_BitMaskedMem_NonMaskedLib extends MacroCompilerSpec with HasSRAMGene
|
||||
|
||||
// FPGA-style byte-masked memories.
|
||||
|
||||
class Masks_FPGAStyle_32_8 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_FPGAStyle_32_8
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 32
|
||||
override lazy val memMaskGran = Some(32)
|
||||
override lazy val libMaskGran = Some(8)
|
||||
@@ -115,7 +136,11 @@ class Masks_FPGAStyle_32_8 extends MacroCompilerSpec with HasSRAMGenerator with
|
||||
|
||||
// Simple powers of two with bit-masked lib.
|
||||
|
||||
class Masks_PowersOfTwo_8_1 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_PowersOfTwo_8_1
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 64
|
||||
override lazy val memMaskGran = Some(8)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
@@ -123,7 +148,11 @@ class Masks_PowersOfTwo_8_1 extends MacroCompilerSpec with HasSRAMGenerator with
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_PowersOfTwo_16_1 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_PowersOfTwo_16_1
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 64
|
||||
override lazy val memMaskGran = Some(16)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
@@ -131,7 +160,11 @@ class Masks_PowersOfTwo_16_1 extends MacroCompilerSpec with HasSRAMGenerator wit
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_PowersOfTwo_32_1 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_PowersOfTwo_32_1
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 64
|
||||
override lazy val memMaskGran = Some(32)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
@@ -139,7 +172,11 @@ class Masks_PowersOfTwo_32_1 extends MacroCompilerSpec with HasSRAMGenerator wit
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_PowersOfTwo_64_1 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_PowersOfTwo_64_1
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 64
|
||||
override lazy val memMaskGran = Some(64)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
@@ -149,7 +186,11 @@ class Masks_PowersOfTwo_64_1 extends MacroCompilerSpec with HasSRAMGenerator wit
|
||||
|
||||
// Simple powers of two with non bit-masked lib.
|
||||
|
||||
class Masks_PowersOfTwo_32_4 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_PowersOfTwo_32_4
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 128
|
||||
override lazy val memMaskGran = Some(32)
|
||||
override lazy val libMaskGran = Some(4)
|
||||
@@ -157,7 +198,11 @@ class Masks_PowersOfTwo_32_4 extends MacroCompilerSpec with HasSRAMGenerator wit
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_PowersOfTwo_32_8 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_PowersOfTwo_32_8
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 128
|
||||
override lazy val memMaskGran = Some(32)
|
||||
override lazy val libMaskGran = Some(8)
|
||||
@@ -165,7 +210,11 @@ class Masks_PowersOfTwo_32_8 extends MacroCompilerSpec with HasSRAMGenerator wit
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_PowersOfTwo_8_8 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_PowersOfTwo_8_8
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 128
|
||||
override lazy val memMaskGran = Some(8)
|
||||
override lazy val libMaskGran = Some(8)
|
||||
@@ -175,7 +224,11 @@ class Masks_PowersOfTwo_8_8 extends MacroCompilerSpec with HasSRAMGenerator with
|
||||
|
||||
// Width as a multiple of the mask, bit-masked lib
|
||||
|
||||
class Masks_IntegerMaskMultiple_20_10 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_IntegerMaskMultiple_20_10
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 20
|
||||
override lazy val memMaskGran = Some(10)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
@@ -183,16 +236,24 @@ class Masks_IntegerMaskMultiple_20_10 extends MacroCompilerSpec with HasSRAMGene
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_IntegerMaskMultiple_21_7 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_IntegerMaskMultiple_21_7
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 21
|
||||
override lazy val memMaskGran = Some(21)
|
||||
override lazy val libMaskGran = Some(7)
|
||||
|
||||
it should "be enabled when non-power of two masks are supported" is (pending)
|
||||
(it should "be enabled when non-power of two masks are supported").is(pending)
|
||||
//~ compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_IntegerMaskMultiple_21_21 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_IntegerMaskMultiple_21_21
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 21
|
||||
override lazy val memMaskGran = Some(21)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
@@ -200,7 +261,11 @@ class Masks_IntegerMaskMultiple_21_21 extends MacroCompilerSpec with HasSRAMGene
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_IntegerMaskMultiple_84_21 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_IntegerMaskMultiple_84_21
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 84
|
||||
override lazy val memMaskGran = Some(21)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
@@ -208,7 +273,11 @@ class Masks_IntegerMaskMultiple_84_21 extends MacroCompilerSpec with HasSRAMGene
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_IntegerMaskMultiple_92_23 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_IntegerMaskMultiple_92_23
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 92
|
||||
override lazy val memMaskGran = Some(23)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
@@ -216,7 +285,11 @@ class Masks_IntegerMaskMultiple_92_23 extends MacroCompilerSpec with HasSRAMGene
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_IntegerMaskMultiple_117_13 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_IntegerMaskMultiple_117_13
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 117
|
||||
override lazy val memMaskGran = Some(13)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
@@ -224,7 +297,11 @@ class Masks_IntegerMaskMultiple_117_13 extends MacroCompilerSpec with HasSRAMGen
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_IntegerMaskMultiple_160_20 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_IntegerMaskMultiple_160_20
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 160
|
||||
override lazy val memMaskGran = Some(20)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
@@ -232,7 +309,11 @@ class Masks_IntegerMaskMultiple_160_20 extends MacroCompilerSpec with HasSRAMGen
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class Masks_IntegerMaskMultiple_184_23 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_IntegerMaskMultiple_184_23
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 184
|
||||
override lazy val memMaskGran = Some(23)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
@@ -242,11 +323,15 @@ class Masks_IntegerMaskMultiple_184_23 extends MacroCompilerSpec with HasSRAMGen
|
||||
|
||||
// Width as an non-integer multiple of the mask, bit-masked lib
|
||||
|
||||
class Masks_NonIntegerMaskMultiple_32_3 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with MasksTestSettings {
|
||||
class Masks_NonIntegerMaskMultiple_32_3
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with MasksTestSettings {
|
||||
override lazy val width = 32
|
||||
override lazy val memMaskGran = Some(3)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
it should "be enabled when non-power of two masks are supported" is (pending)
|
||||
(it should "be enabled when non-power of two masks are supported").is(pending)
|
||||
//~ compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
@@ -13,42 +13,70 @@ class SplitWidth_2rw extends MacroCompilerSpec with HasSRAMGenerator with HasSim
|
||||
|
||||
override def generateMemSRAM() = {
|
||||
SRAMMacro(
|
||||
name=mem_name,
|
||||
width=memWidth,
|
||||
depth=memDepth,
|
||||
family="2rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"portA", memWidth, Some(memDepth), maskGran=memMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
), generateTestPort(
|
||||
"portB", memWidth, Some(memDepth), maskGran=memMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
name = mem_name,
|
||||
width = memWidth,
|
||||
depth = memDepth,
|
||||
family = "2rw",
|
||||
ports = Seq(
|
||||
generateTestPort(
|
||||
"portA",
|
||||
memWidth,
|
||||
Some(memDepth),
|
||||
maskGran = memMaskGran,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = true
|
||||
),
|
||||
generateTestPort(
|
||||
"portB",
|
||||
memWidth,
|
||||
Some(memDepth),
|
||||
maskGran = memMaskGran,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = true
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def generateLibSRAM() = {
|
||||
SRAMMacro(
|
||||
name=lib_name,
|
||||
width=libWidth,
|
||||
depth=libDepth,
|
||||
family="2rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"portA", libWidth, libDepth,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
), generateTestPort(
|
||||
"portB", libWidth, libDepth,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
name = lib_name,
|
||||
width = libWidth,
|
||||
depth = libDepth,
|
||||
family = "2rw",
|
||||
ports = Seq(
|
||||
generateTestPort(
|
||||
"portA",
|
||||
libWidth,
|
||||
libDepth,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = true
|
||||
),
|
||||
generateTestPort(
|
||||
"portB",
|
||||
libWidth,
|
||||
libDepth,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = true
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def generateHeaderPorts() = {
|
||||
generateReadWriteHeaderPort("portA", true, Some(memMaskBits)) + "\n" + generateReadWriteHeaderPort("portB", true, Some(memMaskBits))
|
||||
generateReadWriteHeaderPort("portA", true, Some(memMaskBits)) + "\n" + generateReadWriteHeaderPort(
|
||||
"portB",
|
||||
true,
|
||||
Some(memMaskBits)
|
||||
)
|
||||
}
|
||||
|
||||
override def generateFooterPorts() = {
|
||||
@@ -56,7 +84,7 @@ class SplitWidth_2rw extends MacroCompilerSpec with HasSRAMGenerator with HasSim
|
||||
}
|
||||
|
||||
override def generateBody() =
|
||||
"""
|
||||
"""
|
||||
inst mem_0_0 of awesome_lib_mem
|
||||
inst mem_0_1 of awesome_lib_mem
|
||||
inst mem_0_2 of awesome_lib_mem
|
||||
@@ -128,56 +156,112 @@ class SplitWidth_1r_1w extends MacroCompilerSpec with HasSRAMGenerator with HasS
|
||||
|
||||
override def generateMemSRAM() = {
|
||||
SRAMMacro(
|
||||
name=mem_name,
|
||||
width=memWidth,
|
||||
depth=memDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(generateTestPort(
|
||||
"portA", memWidth, Some(memDepth), maskGran=memMaskGran,
|
||||
write=false, writeEnable=false,
|
||||
read=true, readEnable=true
|
||||
), generateTestPort(
|
||||
"portB", memWidth, Some(memDepth), maskGran=memMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=false, readEnable=false
|
||||
))
|
||||
name = mem_name,
|
||||
width = memWidth,
|
||||
depth = memDepth,
|
||||
family = "1r1w",
|
||||
ports = Seq(
|
||||
generateTestPort(
|
||||
"portA",
|
||||
memWidth,
|
||||
Some(memDepth),
|
||||
maskGran = memMaskGran,
|
||||
write = false,
|
||||
writeEnable = false,
|
||||
read = true,
|
||||
readEnable = true
|
||||
),
|
||||
generateTestPort(
|
||||
"portB",
|
||||
memWidth,
|
||||
Some(memDepth),
|
||||
maskGran = memMaskGran,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = false,
|
||||
readEnable = false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def generateLibSRAM() = {
|
||||
SRAMMacro(
|
||||
name=lib_name,
|
||||
width=libWidth,
|
||||
depth=libDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(generateTestPort(
|
||||
"portA", libWidth, libDepth,
|
||||
write=false, writeEnable=false,
|
||||
read=true, readEnable=true
|
||||
), generateTestPort(
|
||||
"portB", libWidth, libDepth,
|
||||
write=true, writeEnable=true,
|
||||
read=false, readEnable=false
|
||||
))
|
||||
name = lib_name,
|
||||
width = libWidth,
|
||||
depth = libDepth,
|
||||
family = "1r1w",
|
||||
ports = Seq(
|
||||
generateTestPort(
|
||||
"portA",
|
||||
libWidth,
|
||||
libDepth,
|
||||
write = false,
|
||||
writeEnable = false,
|
||||
read = true,
|
||||
readEnable = true
|
||||
),
|
||||
generateTestPort(
|
||||
"portB",
|
||||
libWidth,
|
||||
libDepth,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = false,
|
||||
readEnable = false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def generateHeaderPorts() = {
|
||||
generatePort("portA", mem_addr_width, memWidth,
|
||||
write=false, writeEnable=false, read=true, readEnable=true, Some(memMaskBits)) + "\n" +
|
||||
generatePort("portB", mem_addr_width, memWidth,
|
||||
write=true, writeEnable=true, read=false, readEnable=false, Some(memMaskBits))
|
||||
generatePort(
|
||||
"portA",
|
||||
mem_addr_width,
|
||||
memWidth,
|
||||
write = false,
|
||||
writeEnable = false,
|
||||
read = true,
|
||||
readEnable = true,
|
||||
Some(memMaskBits)
|
||||
) + "\n" +
|
||||
generatePort(
|
||||
"portB",
|
||||
mem_addr_width,
|
||||
memWidth,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = false,
|
||||
readEnable = false,
|
||||
Some(memMaskBits)
|
||||
)
|
||||
}
|
||||
|
||||
override def generateFooterPorts() = {
|
||||
generatePort("portA", lib_addr_width, libWidth,
|
||||
write=false, writeEnable=false, read=true, readEnable=true, None) + "\n" +
|
||||
generatePort("portB", lib_addr_width, libWidth,
|
||||
write=true, writeEnable=true, read=false, readEnable=false, None)
|
||||
generatePort(
|
||||
"portA",
|
||||
lib_addr_width,
|
||||
libWidth,
|
||||
write = false,
|
||||
writeEnable = false,
|
||||
read = true,
|
||||
readEnable = true,
|
||||
None
|
||||
) + "\n" +
|
||||
generatePort(
|
||||
"portB",
|
||||
lib_addr_width,
|
||||
libWidth,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = false,
|
||||
readEnable = false,
|
||||
None
|
||||
)
|
||||
}
|
||||
|
||||
override def generateBody() =
|
||||
"""
|
||||
"""
|
||||
inst mem_0_0 of awesome_lib_mem
|
||||
inst mem_0_1 of awesome_lib_mem
|
||||
inst mem_0_2 of awesome_lib_mem
|
||||
@@ -234,42 +318,70 @@ class SplitWidth_2rw_differentMasks extends MacroCompilerSpec with HasSRAMGenera
|
||||
override def generateMemSRAM() = {
|
||||
println(memMaskGranB)
|
||||
SRAMMacro(
|
||||
name=mem_name,
|
||||
width=memWidth,
|
||||
depth=memDepth,
|
||||
family="2rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"portA", memWidth, Some(memDepth), maskGran=memMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
), generateTestPort(
|
||||
"portB", memWidth, Some(memDepth), maskGran=Some(memMaskGranB),
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
name = mem_name,
|
||||
width = memWidth,
|
||||
depth = memDepth,
|
||||
family = "2rw",
|
||||
ports = Seq(
|
||||
generateTestPort(
|
||||
"portA",
|
||||
memWidth,
|
||||
Some(memDepth),
|
||||
maskGran = memMaskGran,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = true
|
||||
),
|
||||
generateTestPort(
|
||||
"portB",
|
||||
memWidth,
|
||||
Some(memDepth),
|
||||
maskGran = Some(memMaskGranB),
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = true
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def generateLibSRAM() = {
|
||||
SRAMMacro(
|
||||
name=lib_name,
|
||||
width=libWidth,
|
||||
depth=libDepth,
|
||||
family="2rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"portA", libWidth, libDepth,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
), generateTestPort(
|
||||
"portB", libWidth, libDepth,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
name = lib_name,
|
||||
width = libWidth,
|
||||
depth = libDepth,
|
||||
family = "2rw",
|
||||
ports = Seq(
|
||||
generateTestPort(
|
||||
"portA",
|
||||
libWidth,
|
||||
libDepth,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = true
|
||||
),
|
||||
generateTestPort(
|
||||
"portB",
|
||||
libWidth,
|
||||
libDepth,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = true
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def generateHeaderPorts() = {
|
||||
generateReadWriteHeaderPort("portA", true, Some(memMaskBits)) + "\n" + generateReadWriteHeaderPort("portB", true, Some(memWidth / memMaskGranB))
|
||||
generateReadWriteHeaderPort("portA", true, Some(memMaskBits)) + "\n" + generateReadWriteHeaderPort(
|
||||
"portB",
|
||||
true,
|
||||
Some(memWidth / memMaskGranB)
|
||||
)
|
||||
}
|
||||
|
||||
override def generateFooterPorts() = {
|
||||
@@ -277,7 +389,7 @@ class SplitWidth_2rw_differentMasks extends MacroCompilerSpec with HasSRAMGenera
|
||||
}
|
||||
|
||||
override def generateBody() =
|
||||
"""
|
||||
"""
|
||||
inst mem_0_0 of awesome_lib_mem
|
||||
inst mem_0_1 of awesome_lib_mem
|
||||
inst mem_0_2 of awesome_lib_mem
|
||||
@@ -1,7 +1,5 @@
|
||||
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"
|
||||
@@ -15,8 +13,7 @@ class SRAMCompiler extends MacroCompilerSpec with HasSRAMGenerator with HasSimpl
|
||||
|
||||
writeToLib(lib, Seq(compiler))
|
||||
|
||||
|
||||
writeToMem(mem, Seq(generateSRAM("mymem", "X", 8, 16)))
|
||||
|
||||
compileExecuteAndTest(mem, Some(lib), verilog, output=output, false, true)
|
||||
compileExecuteAndTest(mem, Some(lib), verilog, output = output, false, true)
|
||||
}
|
||||
@@ -1,40 +1,41 @@
|
||||
package barstools.macros
|
||||
|
||||
import mdf.macrolib._
|
||||
|
||||
// Test the depth splitting aspect of the memory compiler.
|
||||
// This file is for simple tests: one read-write port, powers of two sizes, etc.
|
||||
// For example, implementing a 4096x32 memory using four 1024x32 memories.
|
||||
|
||||
trait HasSimpleDepthTestGenerator extends HasSimpleTestGenerator {
|
||||
this: MacroCompilerSpec with HasSRAMGenerator =>
|
||||
def width: Int
|
||||
def width: Int
|
||||
|
||||
override lazy val memWidth = width
|
||||
override lazy val libWidth = width
|
||||
override lazy val memWidth = width
|
||||
override lazy val libWidth = width
|
||||
|
||||
// Generate a depth-splitting body.
|
||||
override def generateBody(): String = {
|
||||
val output = new StringBuilder
|
||||
// Generate a depth-splitting body.
|
||||
override def generateBody(): String = {
|
||||
val output = new StringBuilder
|
||||
|
||||
if (selectBits > 0) {
|
||||
output.append (
|
||||
s"""
|
||||
if (selectBits > 0) {
|
||||
output.append(
|
||||
s"""
|
||||
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(${memPortPrefix}_addr_sel, UInt<${selectBits}>("h${i.toHexString}"))""" else "UInt<1>(\"h1\")"
|
||||
val chipEnable = s"""UInt<1>("h1")"""
|
||||
val writeEnable = if (memMaskGran.isEmpty) s"and(${memPortPrefix}_write_en, ${chipEnable})" else s"${memPortPrefix}_write_en"
|
||||
output.append(
|
||||
s"""
|
||||
for (i <- 0 to depthInstances - 1) {
|
||||
val maskStatement = generateMaskStatement(0, i)
|
||||
val enableIdentifier =
|
||||
if (selectBits > 0) s"""eq(${memPortPrefix}_addr_sel, UInt<${selectBits}>("h${i.toHexString}"))"""
|
||||
else "UInt<1>(\"h1\")"
|
||||
val chipEnable = s"""UInt<1>("h1")"""
|
||||
val writeEnable =
|
||||
if (memMaskGran.isEmpty) s"and(${memPortPrefix}_write_en, ${chipEnable})" else s"${memPortPrefix}_write_en"
|
||||
output.append(
|
||||
s"""
|
||||
inst mem_${i}_0 of ${lib_name}
|
||||
mem_${i}_0.${libPortPrefix}_clk <= ${memPortPrefix}_clk
|
||||
mem_${i}_0.${libPortPrefix}_addr <= ${memPortPrefix}_addr
|
||||
@@ -44,26 +45,29 @@ s"""
|
||||
mem_${i}_0.${libPortPrefix}_write_en <= and(and(${writeEnable}, UInt<1>("h1")), ${enableIdentifier})
|
||||
node ${memPortPrefix}_dout_${i} = ${memPortPrefix}_dout_${i}_0
|
||||
"""
|
||||
)
|
||||
}
|
||||
def generate_outer_dout_tree(i: Int, depthInstances: Int): String = {
|
||||
if (i > depthInstances - 1) {
|
||||
s"""UInt<${libWidth}>("h0")"""
|
||||
} else {
|
||||
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)
|
||||
)
|
||||
}
|
||||
def generate_outer_dout_tree(i:Int, depthInstances: Int): String = {
|
||||
if (i > depthInstances - 1) {
|
||||
s"""UInt<${libWidth}>("h0")"""
|
||||
} else {
|
||||
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 s" ${memPortPrefix}_dout <= "
|
||||
if (selectBits > 0) {
|
||||
output append generate_outer_dout_tree(0, depthInstances)
|
||||
} else {
|
||||
output append s"""mux(UInt<1>("h1"), ${memPortPrefix}_dout_0, UInt<${libWidth}>("h0"))"""
|
||||
}
|
||||
|
||||
output.toString
|
||||
}
|
||||
output.append(s" ${memPortPrefix}_dout <= ")
|
||||
if (selectBits > 0) {
|
||||
output.append(generate_outer_dout_tree(0, depthInstances))
|
||||
} else {
|
||||
output.append(s"""mux(UInt<1>("h1"), ${memPortPrefix}_dout_0, UInt<${libWidth}>("h0"))""")
|
||||
}
|
||||
|
||||
output.toString
|
||||
}
|
||||
}
|
||||
|
||||
// Try different widths
|
||||
@@ -156,7 +160,10 @@ class SplitDepth2048x8_mrw_lib8 extends MacroCompilerSpec with HasSRAMGenerator
|
||||
}
|
||||
|
||||
// Non-bit level mask
|
||||
class SplitDepth2048x64_mrw_mem32_lib8 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator {
|
||||
class SplitDepth2048x64_mrw_mem32_lib8
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator {
|
||||
override lazy val width = 64
|
||||
override lazy val memDepth = BigInt(2048)
|
||||
override lazy val libDepth = BigInt(1024)
|
||||
@@ -167,7 +174,10 @@ class SplitDepth2048x64_mrw_mem32_lib8 extends MacroCompilerSpec with HasSRAMGen
|
||||
}
|
||||
|
||||
// Bit level mask
|
||||
class SplitDepth2048x32_mrw_mem16_lib1 extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator {
|
||||
class SplitDepth2048x32_mrw_mem16_lib1
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator {
|
||||
override lazy val width = 32
|
||||
override lazy val memDepth = BigInt(2048)
|
||||
override lazy val libDepth = BigInt(1024)
|
||||
@@ -215,7 +225,7 @@ class SplitDepth2048x32_mrw_mem3_lib1 extends MacroCompilerSpec with HasSRAMGene
|
||||
override lazy val memMaskGran = Some(3)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
it should "be enabled when non-power of two masks are supported" is (pending)
|
||||
(it should "be enabled when non-power of two masks are supported").is(pending)
|
||||
//compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
@@ -226,7 +236,7 @@ class SplitDepth2048x32_mrw_mem7_lib1 extends MacroCompilerSpec with HasSRAMGene
|
||||
override lazy val memMaskGran = Some(7)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
it should "be enabled when non-power of two masks are supported" is (pending)
|
||||
(it should "be enabled when non-power of two masks are supported").is(pending)
|
||||
//compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
@@ -237,7 +247,7 @@ class SplitDepth2048x32_mrw_mem9_lib1 extends MacroCompilerSpec with HasSRAMGene
|
||||
override lazy val memMaskGran = Some(9)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
it should "be enabled when non-power of two masks are supported" is (pending)
|
||||
(it should "be enabled when non-power of two masks are supported").is(pending)
|
||||
//compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
@@ -249,12 +259,12 @@ class SplitDepth2048x8_extraPort extends MacroCompilerSpec with HasSRAMGenerator
|
||||
override lazy val memDepth = BigInt(2048)
|
||||
override lazy val libDepth = BigInt(1024)
|
||||
override lazy val extraPorts = List(
|
||||
MacroExtraPort(name="extra_port", width=8, portType=Constant, value=0xff)
|
||||
MacroExtraPort(name = "extra_port", width = 8, portType = Constant, value = 0xff)
|
||||
)
|
||||
override lazy val extraTag = "extraPort"
|
||||
|
||||
override def generateOutput(): String =
|
||||
"""
|
||||
"""
|
||||
circuit target_memory :
|
||||
module target_memory :
|
||||
input outer_addr : UInt<11>
|
||||
@@ -319,22 +329,22 @@ class SplitDepth_SplitPortsNonMasked extends MacroCompilerSpec with HasSRAMGener
|
||||
val v = "split_depth-r-w-split-lib-split-mem.v"
|
||||
|
||||
val libMacro = SRAMMacro(
|
||||
name="awesome_lib_mem",
|
||||
width=width,
|
||||
depth=libDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(
|
||||
name = "awesome_lib_mem",
|
||||
width = width,
|
||||
depth = libDepth,
|
||||
family = "1r1w",
|
||||
ports = Seq(
|
||||
generateReadPort("innerA", width, libDepth),
|
||||
generateWritePort("innerB", width, libDepth)
|
||||
)
|
||||
)
|
||||
|
||||
val memMacro = SRAMMacro(
|
||||
name="target_memory",
|
||||
width=width,
|
||||
depth=memDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(
|
||||
name = "target_memory",
|
||||
width = width,
|
||||
depth = memDepth,
|
||||
family = "1r1w",
|
||||
ports = Seq(
|
||||
generateReadPort("outerB", width, memDepth),
|
||||
generateWritePort("outerA", width, memDepth)
|
||||
)
|
||||
@@ -344,7 +354,7 @@ class SplitDepth_SplitPortsNonMasked extends MacroCompilerSpec with HasSRAMGener
|
||||
writeToLib(lib, Seq(libMacro))
|
||||
|
||||
val output =
|
||||
"""
|
||||
"""
|
||||
circuit target_memory :
|
||||
module target_memory :
|
||||
input outerB_addr : UInt<11>
|
||||
@@ -406,11 +416,11 @@ circuit target_memory :
|
||||
val v = "split_depth-r-w-regular-lib-split-mem.v"
|
||||
|
||||
val memMacro = SRAMMacro(
|
||||
name="target_memory",
|
||||
width=width,
|
||||
depth=memDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(
|
||||
name = "target_memory",
|
||||
width = width,
|
||||
depth = memDepth,
|
||||
family = "1r1w",
|
||||
ports = Seq(
|
||||
generateReadPort("outerB", width, memDepth),
|
||||
generateWritePort("outerA", width, memDepth)
|
||||
)
|
||||
@@ -420,7 +430,7 @@ circuit target_memory :
|
||||
writeToLib(lib, Seq(generateSRAM("awesome_lib_mem", "lib", width, libDepth)))
|
||||
|
||||
val output =
|
||||
"""
|
||||
"""
|
||||
TODO
|
||||
"""
|
||||
|
||||
@@ -439,11 +449,11 @@ TODO
|
||||
val v = "split_depth-r-w-split-lib-regular-mem.v"
|
||||
|
||||
val libMacro = SRAMMacro(
|
||||
name="awesome_lib_mem",
|
||||
width=width,
|
||||
depth=libDepth,
|
||||
family="1rw",
|
||||
ports=Seq(
|
||||
name = "awesome_lib_mem",
|
||||
width = width,
|
||||
depth = libDepth,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateReadPort("innerA", width, libDepth),
|
||||
generateWritePort("innerB", width, libDepth)
|
||||
)
|
||||
@@ -453,7 +463,7 @@ TODO
|
||||
writeToLib(lib, Seq(libMacro))
|
||||
|
||||
val output =
|
||||
"""
|
||||
"""
|
||||
TODO
|
||||
"""
|
||||
|
||||
@@ -480,22 +490,22 @@ class SplitDepth_SplitPortsMasked extends MacroCompilerSpec with HasSRAMGenerato
|
||||
val v = "split_depth-r-mw-split-lib-split-mem.v"
|
||||
|
||||
val libMacro = SRAMMacro(
|
||||
name="awesome_lib_mem",
|
||||
width=width,
|
||||
depth=libDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(
|
||||
name = "awesome_lib_mem",
|
||||
width = width,
|
||||
depth = libDepth,
|
||||
family = "1r1w",
|
||||
ports = Seq(
|
||||
generateReadPort("innerA", width, libDepth),
|
||||
generateWritePort("innerB", width, libDepth, libMaskGran)
|
||||
)
|
||||
)
|
||||
|
||||
val memMacro = SRAMMacro(
|
||||
name="target_memory",
|
||||
width=width,
|
||||
depth=memDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(
|
||||
name = "target_memory",
|
||||
width = width,
|
||||
depth = memDepth,
|
||||
family = "1r1w",
|
||||
ports = Seq(
|
||||
generateReadPort("outerB", width, memDepth),
|
||||
generateWritePort("outerA", width, memDepth, memMaskGran)
|
||||
)
|
||||
@@ -505,7 +515,7 @@ class SplitDepth_SplitPortsMasked extends MacroCompilerSpec with HasSRAMGenerato
|
||||
writeToLib(lib, Seq(libMacro))
|
||||
|
||||
val output =
|
||||
"""
|
||||
"""
|
||||
circuit target_memory :
|
||||
module target_memory :
|
||||
input outerB_addr : UInt<11>
|
||||
@@ -571,11 +581,11 @@ circuit target_memory :
|
||||
val v = "split_depth-r-mw-regular-lib-split-mem.v"
|
||||
|
||||
val memMacro = SRAMMacro(
|
||||
name="target_memory",
|
||||
width=width,
|
||||
depth=memDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(
|
||||
name = "target_memory",
|
||||
width = width,
|
||||
depth = memDepth,
|
||||
family = "1r1w",
|
||||
ports = Seq(
|
||||
generateReadPort("outerB", width, memDepth),
|
||||
generateWritePort("outerA", width, memDepth, memMaskGran)
|
||||
)
|
||||
@@ -585,7 +595,7 @@ circuit target_memory :
|
||||
writeToLib(lib, Seq(generateSRAM("awesome_lib_mem", "lib", width, libDepth, libMaskGran)))
|
||||
|
||||
val output =
|
||||
"""
|
||||
"""
|
||||
TODO
|
||||
"""
|
||||
|
||||
@@ -604,11 +614,11 @@ TODO
|
||||
val v = "split_depth-r-mw-split-lib-regular-mem.v"
|
||||
|
||||
val libMacro = SRAMMacro(
|
||||
name="awesome_lib_mem",
|
||||
width=width,
|
||||
depth=libDepth,
|
||||
family="1rw",
|
||||
ports=Seq(
|
||||
name = "awesome_lib_mem",
|
||||
width = width,
|
||||
depth = libDepth,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateReadPort("innerA", width, libDepth),
|
||||
generateWritePort("innerB", width, libDepth, libMaskGran)
|
||||
)
|
||||
@@ -618,7 +628,7 @@ TODO
|
||||
writeToLib(lib, Seq(libMacro))
|
||||
|
||||
val output =
|
||||
"""
|
||||
"""
|
||||
TODO
|
||||
"""
|
||||
|
||||
@@ -5,43 +5,45 @@ package barstools.macros
|
||||
|
||||
trait HasSimpleWidthTestGenerator extends HasSimpleTestGenerator {
|
||||
this: MacroCompilerSpec with HasSRAMGenerator =>
|
||||
def depth: BigInt
|
||||
def depth: BigInt
|
||||
|
||||
override lazy val memDepth = depth
|
||||
override lazy val libDepth = depth
|
||||
override lazy val memDepth = depth
|
||||
override lazy val libDepth = depth
|
||||
|
||||
override def generateBody(): String = {
|
||||
val output = new StringBuilder
|
||||
override def generateBody(): String = {
|
||||
val output = new StringBuilder
|
||||
|
||||
// Generate mem_0_<i> lines for number of width instances.
|
||||
output.append(
|
||||
((0 to widthInstances - 1) map {i:Int => s"""
|
||||
// Generate mem_0_<i> lines for number of width instances.
|
||||
output.append(
|
||||
((0 to widthInstances - 1).map { i: Int =>
|
||||
s"""
|
||||
inst mem_0_${i} of ${lib_name}
|
||||
"""
|
||||
}).reduceLeft(_ + _)
|
||||
)
|
||||
}).reduceLeft(_ + _)
|
||||
)
|
||||
|
||||
// Generate submemory connection blocks.
|
||||
output append (for (i <- 0 to widthInstances - 1) yield {
|
||||
// Width of this submemory.
|
||||
val myMemWidth = if (i == widthInstances - 1) lastWidthBits else usableLibWidth
|
||||
// Base bit of this submemory.
|
||||
// e.g. if libWidth is 8 and this is submemory 2 (0-indexed), then this
|
||||
// would be 16.
|
||||
val myBaseBit = usableLibWidth*i
|
||||
// Generate submemory connection blocks.
|
||||
output.append((for (i <- 0 to widthInstances - 1) yield {
|
||||
// Width of this submemory.
|
||||
val myMemWidth = if (i == widthInstances - 1) lastWidthBits else usableLibWidth
|
||||
// Base bit of this submemory.
|
||||
// e.g. if libWidth is 8 and this is submemory 2 (0-indexed), then this
|
||||
// would be 16.
|
||||
val myBaseBit = usableLibWidth * i
|
||||
|
||||
val maskStatement = generateMaskStatement(i, 0)
|
||||
val maskStatement = generateMaskStatement(i, 0)
|
||||
|
||||
// We need to use writeEnable as a crude "mask" if mem has a mask but
|
||||
// lib does not.
|
||||
val writeEnableBit = if (libMaskGran.isEmpty && memMaskGran.isDefined) {
|
||||
val outerMaskBit = myBaseBit / memMaskGran.get
|
||||
s"bits(outer_mask, ${outerMaskBit}, ${outerMaskBit})"
|
||||
} else """UInt<1>("h1")"""
|
||||
val chipEnable = s"""UInt<1>("h1")"""
|
||||
val writeEnableExpr = if (libMaskGran.isEmpty) s"and(${memPortPrefix}_write_en, ${chipEnable})" else s"${memPortPrefix}_write_en"
|
||||
// We need to use writeEnable as a crude "mask" if mem has a mask but
|
||||
// lib does not.
|
||||
val writeEnableBit = if (libMaskGran.isEmpty && memMaskGran.isDefined) {
|
||||
val outerMaskBit = myBaseBit / memMaskGran.get
|
||||
s"bits(outer_mask, ${outerMaskBit}, ${outerMaskBit})"
|
||||
} else """UInt<1>("h1")"""
|
||||
val chipEnable = s"""UInt<1>("h1")"""
|
||||
val writeEnableExpr =
|
||||
if (libMaskGran.isEmpty) s"and(${memPortPrefix}_write_en, ${chipEnable})" else s"${memPortPrefix}_write_en"
|
||||
|
||||
s"""
|
||||
s"""
|
||||
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)
|
||||
@@ -49,24 +51,23 @@ s"""
|
||||
${maskStatement}
|
||||
mem_0_${i}.${libPortPrefix}_write_en <= and(and(${writeEnableExpr}, ${writeEnableBit}), UInt<1>("h1"))
|
||||
"""
|
||||
}).reduceLeft(_ + _)
|
||||
}).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"${memPortPrefix}_dout_0_${i}"))
|
||||
val catStmt = doutStatements.init.foldRight(doutStatements.last)((l: String, r: String) => s"cat($l, $r)")
|
||||
s"""
|
||||
// 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"${memPortPrefix}_dout_0_${i}"))
|
||||
val catStmt = doutStatements.init.foldRight(doutStatements.last)((l: String, r: String) => s"cat($l, $r)")
|
||||
s"""
|
||||
node ${memPortPrefix}_dout_0 = ${catStmt}
|
||||
"""
|
||||
}
|
||||
|
||||
output append
|
||||
s"""
|
||||
${memPortPrefix}_dout <= mux(UInt<1>("h1"), ${memPortPrefix}_dout_0, UInt<${memWidth}>("h0"))
|
||||
"""
|
||||
output.toString
|
||||
}
|
||||
|
||||
output.append(s"""
|
||||
${memPortPrefix}_dout <= mux(UInt<1>("h1"), ${memPortPrefix}_dout_0, UInt<${memWidth}>("h0"))
|
||||
""")
|
||||
output.toString
|
||||
}
|
||||
}
|
||||
|
||||
// Try different widths against a base memory width of 8.
|
||||
@@ -268,7 +269,10 @@ class SplitWidth1024x16_mem11_rw extends MacroCompilerSpec with HasSRAMGenerator
|
||||
|
||||
// Masked RAM
|
||||
|
||||
class SplitWidth1024x8_memGran_8_libGran_1_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class SplitWidth1024x8_memGran_8_libGran_1_rw
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 8
|
||||
override lazy val libWidth = 8
|
||||
@@ -278,7 +282,10 @@ class SplitWidth1024x8_memGran_8_libGran_1_rw extends MacroCompilerSpec with Has
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class SplitWidth1024x16_memGran_8_libGran_1_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class SplitWidth1024x16_memGran_8_libGran_1_rw
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 16
|
||||
override lazy val libWidth = 8
|
||||
@@ -288,7 +295,10 @@ class SplitWidth1024x16_memGran_8_libGran_1_rw extends MacroCompilerSpec with Ha
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class SplitWidth1024x16_memGran_8_libGran_8_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class SplitWidth1024x16_memGran_8_libGran_8_rw
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 16
|
||||
override lazy val libWidth = 8
|
||||
@@ -298,7 +308,10 @@ class SplitWidth1024x16_memGran_8_libGran_8_rw extends MacroCompilerSpec with Ha
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class SplitWidth1024x128_memGran_8_libGran_1_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class SplitWidth1024x128_memGran_8_libGran_1_rw
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 128
|
||||
override lazy val libWidth = 32
|
||||
@@ -308,7 +321,10 @@ class SplitWidth1024x128_memGran_8_libGran_1_rw extends MacroCompilerSpec with H
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class SplitWidth1024x16_memGran_4_libGran_1_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class SplitWidth1024x16_memGran_4_libGran_1_rw
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 16
|
||||
override lazy val libWidth = 8
|
||||
@@ -318,7 +334,10 @@ class SplitWidth1024x16_memGran_4_libGran_1_rw extends MacroCompilerSpec with Ha
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class SplitWidth1024x16_memGran_2_libGran_1_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class SplitWidth1024x16_memGran_2_libGran_1_rw
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 16
|
||||
override lazy val libWidth = 8
|
||||
@@ -328,7 +347,10 @@ class SplitWidth1024x16_memGran_2_libGran_1_rw extends MacroCompilerSpec with Ha
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class SplitWidth1024x16_memGran_16_libGran_1_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class SplitWidth1024x16_memGran_16_libGran_1_rw
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 16
|
||||
override lazy val libWidth = 8
|
||||
@@ -360,7 +382,10 @@ class SplitWidth1024x16_libGran_1_rw extends MacroCompilerSpec with HasSRAMGener
|
||||
|
||||
// Non-memMask and non-1 libMask
|
||||
|
||||
class SplitWidth1024x16_memGran_8_libGran_2_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class SplitWidth1024x16_memGran_8_libGran_2_rw
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 16
|
||||
override lazy val libWidth = 8
|
||||
@@ -372,21 +397,27 @@ class SplitWidth1024x16_memGran_8_libGran_2_rw extends MacroCompilerSpec with Ha
|
||||
|
||||
// Non-power of two memGran
|
||||
|
||||
class SplitWidth1024x16_memGran_9_libGran_1_rw extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class SplitWidth1024x16_memGran_9_libGran_1_rw
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
override lazy val depth = BigInt(1024)
|
||||
override lazy val memWidth = 16
|
||||
override lazy val libWidth = 8
|
||||
override lazy val memMaskGran = Some(9)
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
it should "be enabled when non-power of two masks are supported" is (pending)
|
||||
(it should "be enabled when non-power of two masks are supported").is(pending)
|
||||
//~ compile(mem, lib, v, false)
|
||||
//~ execute(mem, lib, false, output)
|
||||
}
|
||||
|
||||
// Read enable
|
||||
|
||||
class SplitWidth1024x32_readEnable_Lib extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class SplitWidth1024x32_readEnable_Lib
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
import mdf.macrolib._
|
||||
|
||||
override lazy val depth = BigInt(1024)
|
||||
@@ -395,20 +426,27 @@ class SplitWidth1024x32_readEnable_Lib extends MacroCompilerSpec with HasSRAMGen
|
||||
|
||||
override def generateLibSRAM() = {
|
||||
SRAMMacro(
|
||||
name=lib_name,
|
||||
width=libWidth,
|
||||
depth=libDepth,
|
||||
family="1rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"lib", Some(libWidth), Some(libDepth), maskGran=libMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
name = lib_name,
|
||||
width = libWidth,
|
||||
depth = libDepth,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateTestPort(
|
||||
"lib",
|
||||
Some(libWidth),
|
||||
Some(libDepth),
|
||||
maskGran = libMaskGran,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = true
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def generateBody() =
|
||||
"""
|
||||
"""
|
||||
inst mem_0_0 of awesome_lib_mem
|
||||
inst mem_0_1 of awesome_lib_mem
|
||||
inst mem_0_2 of awesome_lib_mem
|
||||
@@ -444,7 +482,10 @@ class SplitWidth1024x32_readEnable_Lib extends MacroCompilerSpec with HasSRAMGen
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class SplitWidth1024x32_readEnable_Mem extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class SplitWidth1024x32_readEnable_Mem
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
import mdf.macrolib._
|
||||
|
||||
override lazy val depth = BigInt(1024)
|
||||
@@ -453,15 +494,22 @@ class SplitWidth1024x32_readEnable_Mem extends MacroCompilerSpec with HasSRAMGen
|
||||
|
||||
override def generateMemSRAM() = {
|
||||
SRAMMacro(
|
||||
name=mem_name,
|
||||
width=memWidth,
|
||||
depth=memDepth,
|
||||
family="1rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"outer", Some(memWidth), Some(memDepth), maskGran=memMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
name = mem_name,
|
||||
width = memWidth,
|
||||
depth = memDepth,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateTestPort(
|
||||
"outer",
|
||||
Some(memWidth),
|
||||
Some(memDepth),
|
||||
maskGran = memMaskGran,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = true
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -470,7 +518,10 @@ class SplitWidth1024x32_readEnable_Mem extends MacroCompilerSpec with HasSRAMGen
|
||||
compileExecuteAndTest(mem, lib, v, output)
|
||||
}
|
||||
|
||||
class SplitWidth1024x32_readEnable_LibMem extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
|
||||
class SplitWidth1024x32_readEnable_LibMem
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator {
|
||||
import mdf.macrolib._
|
||||
|
||||
override lazy val depth = BigInt(1024)
|
||||
@@ -479,34 +530,48 @@ class SplitWidth1024x32_readEnable_LibMem extends MacroCompilerSpec with HasSRAM
|
||||
|
||||
override def generateLibSRAM() = {
|
||||
SRAMMacro(
|
||||
name=lib_name,
|
||||
width=libWidth,
|
||||
depth=libDepth,
|
||||
family="1rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"lib", Some(libWidth), Some(libDepth), maskGran=libMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
name = lib_name,
|
||||
width = libWidth,
|
||||
depth = libDepth,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateTestPort(
|
||||
"lib",
|
||||
Some(libWidth),
|
||||
Some(libDepth),
|
||||
maskGran = libMaskGran,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = true
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def generateMemSRAM() = {
|
||||
SRAMMacro(
|
||||
name=mem_name,
|
||||
width=memWidth,
|
||||
depth=memDepth,
|
||||
family="1rw",
|
||||
ports=Seq(generateTestPort(
|
||||
"outer", Some(memWidth), Some(memDepth), maskGran=memMaskGran,
|
||||
write=true, writeEnable=true,
|
||||
read=true, readEnable=true
|
||||
))
|
||||
name = mem_name,
|
||||
width = memWidth,
|
||||
depth = memDepth,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateTestPort(
|
||||
"outer",
|
||||
Some(memWidth),
|
||||
Some(memDepth),
|
||||
maskGran = memMaskGran,
|
||||
write = true,
|
||||
writeEnable = true,
|
||||
read = true,
|
||||
readEnable = true
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override def generateBody() =
|
||||
"""
|
||||
"""
|
||||
inst mem_0_0 of awesome_lib_mem
|
||||
inst mem_0_1 of awesome_lib_mem
|
||||
inst mem_0_2 of awesome_lib_mem
|
||||
@@ -29,8 +29,8 @@ class WriteEnableTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
|
||||
override val libPrefix = "macros/src/test/resources"
|
||||
|
||||
val memSRAMs = mdf.macrolib.Utils.readMDFFromString(
|
||||
"""
|
||||
val memSRAMs = mdf.macrolib.Utils
|
||||
.readMDFFromString("""
|
||||
[ {
|
||||
"type" : "sram",
|
||||
"name" : "cc_banks_0_ext",
|
||||
@@ -58,7 +58,7 @@ class WriteEnableTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
writeToMem(mem, memSRAMs)
|
||||
|
||||
val output =
|
||||
"""
|
||||
"""
|
||||
circuit cc_banks_0_ext :
|
||||
module cc_banks_0_ext :
|
||||
input RW0_addr : UInt<12>
|
||||
@@ -99,8 +99,8 @@ class MaskPortTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
|
||||
override val libPrefix = "macros/src/test/resources"
|
||||
|
||||
val memSRAMs = mdf.macrolib.Utils.readMDFFromString(
|
||||
"""
|
||||
val memSRAMs = mdf.macrolib.Utils
|
||||
.readMDFFromString("""
|
||||
[ {
|
||||
"type" : "sram",
|
||||
"name" : "cc_dir_ext",
|
||||
@@ -131,7 +131,7 @@ class MaskPortTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
writeToMem(mem, memSRAMs)
|
||||
|
||||
val output =
|
||||
"""
|
||||
"""
|
||||
circuit cc_dir_ext :
|
||||
module cc_dir_ext :
|
||||
input RW0_addr : UInt<9>
|
||||
@@ -183,8 +183,8 @@ class BOOMTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
|
||||
override val libPrefix = "macros/src/test/resources"
|
||||
|
||||
val memSRAMs = mdf.macrolib.Utils.readMDFFromString(
|
||||
"""
|
||||
val memSRAMs = mdf.macrolib.Utils
|
||||
.readMDFFromString("""
|
||||
[ {
|
||||
"type" : "sram",
|
||||
"name" : "_T_182_ext",
|
||||
@@ -354,7 +354,7 @@ class BOOMTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
writeToMem(mem, memSRAMs)
|
||||
|
||||
val output = // TODO: check correctness...
|
||||
"""
|
||||
"""
|
||||
circuit smem_0_ext :
|
||||
module _T_182_ext :
|
||||
input R0_addr : UInt<6>
|
||||
@@ -1350,14 +1350,14 @@ circuit smem_0_ext :
|
||||
|
||||
class SmallTagArrayTest extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleTestGenerator {
|
||||
// Test that mapping a smaller memory using a larger lib can still work.
|
||||
override def memWidth: Int = 26
|
||||
override def memDepth: BigInt = BigInt(2)
|
||||
override def memMaskGran: Option[Int] = Some(26)
|
||||
override def memWidth: Int = 26
|
||||
override def memDepth: BigInt = BigInt(2)
|
||||
override def memMaskGran: Option[Int] = Some(26)
|
||||
override def memPortPrefix: String = ""
|
||||
|
||||
override def libWidth: Int = 32
|
||||
override def libDepth: BigInt = BigInt(64)
|
||||
override def libMaskGran: Option[Int] = Some(1)
|
||||
override def libWidth: Int = 32
|
||||
override def libDepth: BigInt = BigInt(64)
|
||||
override def libMaskGran: Option[Int] = Some(1)
|
||||
override def libPortPrefix: String = ""
|
||||
|
||||
override def extraPorts: Seq[MacroExtraPort] = Seq(
|
||||
@@ -1388,73 +1388,73 @@ class RocketChipTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
|
||||
val libSRAMs = Seq(
|
||||
SRAMMacro(
|
||||
name="SRAM1RW1024x8",
|
||||
depth=1024,
|
||||
width=8,
|
||||
family="1rw",
|
||||
ports=Seq(
|
||||
name = "SRAM1RW1024x8",
|
||||
depth = 1024,
|
||||
width = 8,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateReadWritePort("", 8, BigInt(1024))
|
||||
)
|
||||
),
|
||||
SRAMMacro(
|
||||
name="SRAM1RW512x32",
|
||||
depth=512,
|
||||
width=32,
|
||||
family="1rw",
|
||||
ports=Seq(
|
||||
name = "SRAM1RW512x32",
|
||||
depth = 512,
|
||||
width = 32,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateReadWritePort("", 32, BigInt(512))
|
||||
)
|
||||
),
|
||||
SRAMMacro(
|
||||
name="SRAM1RW64x128",
|
||||
depth=64,
|
||||
width=128,
|
||||
family="1rw",
|
||||
ports=Seq(
|
||||
name = "SRAM1RW64x128",
|
||||
depth = 64,
|
||||
width = 128,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateReadWritePort("", 128, BigInt(64))
|
||||
)
|
||||
),
|
||||
SRAMMacro(
|
||||
name="SRAM1RW64x32",
|
||||
depth=64,
|
||||
width=32,
|
||||
family="1rw",
|
||||
ports=Seq(
|
||||
name = "SRAM1RW64x32",
|
||||
depth = 64,
|
||||
width = 32,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateReadWritePort("", 32, BigInt(64))
|
||||
)
|
||||
),
|
||||
SRAMMacro(
|
||||
name="SRAM1RW64x8",
|
||||
depth=64,
|
||||
width=8,
|
||||
family="1rw",
|
||||
ports=Seq(
|
||||
name = "SRAM1RW64x8",
|
||||
depth = 64,
|
||||
width = 8,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateReadWritePort("", 8, BigInt(64))
|
||||
)
|
||||
),
|
||||
SRAMMacro(
|
||||
name="SRAM1RW512x8",
|
||||
depth=512,
|
||||
width=8,
|
||||
family="1rw",
|
||||
ports=Seq(
|
||||
name = "SRAM1RW512x8",
|
||||
depth = 512,
|
||||
width = 8,
|
||||
family = "1rw",
|
||||
ports = Seq(
|
||||
generateReadWritePort("", 8, BigInt(512))
|
||||
)
|
||||
),
|
||||
SRAMMacro(
|
||||
name="SRAM2RW64x32",
|
||||
depth=64,
|
||||
width=32,
|
||||
family="1r1w",
|
||||
ports=Seq(
|
||||
name = "SRAM2RW64x32",
|
||||
depth = 64,
|
||||
width = 32,
|
||||
family = "1r1w",
|
||||
ports = Seq(
|
||||
generateReadPort("portA", 32, BigInt(64)),
|
||||
generateWritePort("portB", 32, BigInt(64))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val memSRAMs = mdf.macrolib.Utils.readMDFFromString(
|
||||
"""
|
||||
val memSRAMs = mdf.macrolib.Utils
|
||||
.readMDFFromString("""
|
||||
[
|
||||
{
|
||||
"type": "sram",
|
||||
@@ -1537,7 +1537,7 @@ class RocketChipTest extends MacroCompilerSpec with HasSRAMGenerator {
|
||||
writeToMem(mem, memSRAMs)
|
||||
|
||||
val output = // TODO: check correctness...
|
||||
"""
|
||||
"""
|
||||
circuit T_2172_ext :
|
||||
module tag_array_ext :
|
||||
input RW0_addr : UInt<6>
|
||||
@@ -4,13 +4,13 @@ package barstools.macros
|
||||
|
||||
trait HasSynFlopsTestGenerator extends HasSimpleTestGenerator {
|
||||
this: MacroCompilerSpec with HasSRAMGenerator =>
|
||||
def generateFlops: String = {
|
||||
s"""
|
||||
def generateFlops: String = {
|
||||
s"""
|
||||
inst mem_0_0 of split_${lib_name}
|
||||
mem_0_0.${libPortPrefix}_clk <= ${libPortPrefix}_clk
|
||||
mem_0_0.${libPortPrefix}_addr <= ${libPortPrefix}_addr
|
||||
node ${libPortPrefix}_dout_0_0 = bits(mem_0_0.${libPortPrefix}_dout, ${libWidth-1}, 0)
|
||||
mem_0_0.${libPortPrefix}_din <= bits(${libPortPrefix}_din, ${libWidth-1}, 0)
|
||||
node ${libPortPrefix}_dout_0_0 = bits(mem_0_0.${libPortPrefix}_dout, ${libWidth - 1}, 0)
|
||||
mem_0_0.${libPortPrefix}_din <= bits(${libPortPrefix}_din, ${libWidth - 1}, 0)
|
||||
mem_0_0.${libPortPrefix}_write_en <= and(and(and(${libPortPrefix}_write_en, UInt<1>("h1")), UInt<1>("h1")), UInt<1>("h1"))
|
||||
node ${libPortPrefix}_dout_0 = ${libPortPrefix}_dout_0_0
|
||||
${libPortPrefix}_dout <= mux(UInt<1>("h1"), ${libPortPrefix}_dout_0, UInt<${libWidth}>("h0"))
|
||||
@@ -37,49 +37,66 @@ s"""
|
||||
${libPortPrefix}_dout <= ram.RW_0.rdata
|
||||
ram.RW_0.wdata <= ${libPortPrefix}_din
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no lib, put the flops definition into the body.
|
||||
abstract override def generateBody = {
|
||||
if (this.isInstanceOf[HasNoLibTestGenerator]) generateFlops else super.generateBody
|
||||
}
|
||||
// If there is no lib, put the flops definition into the body.
|
||||
abstract override def generateBody = {
|
||||
if (this.isInstanceOf[HasNoLibTestGenerator]) generateFlops else super.generateBody
|
||||
}
|
||||
|
||||
// If there is no lib, don't generate a footer, since the flops definition
|
||||
// will be in the body.
|
||||
override def generateFooter = {
|
||||
if (this.isInstanceOf[HasNoLibTestGenerator]) "" else
|
||||
s"""
|
||||
// If there is no lib, don't generate a footer, since the flops definition
|
||||
// will be in the body.
|
||||
override def generateFooter = {
|
||||
if (this.isInstanceOf[HasNoLibTestGenerator]) ""
|
||||
else
|
||||
s"""
|
||||
module ${lib_name} :
|
||||
${generateFooterPorts}
|
||||
|
||||
${generateFlops}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Synflops2048x8_noLib extends MacroCompilerSpec with HasSRAMGenerator with HasNoLibTestGenerator with HasSynFlopsTestGenerator {
|
||||
class Synflops2048x8_noLib
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasNoLibTestGenerator
|
||||
with HasSynFlopsTestGenerator {
|
||||
override lazy val memDepth = BigInt(2048)
|
||||
override lazy val memWidth = 8
|
||||
|
||||
compileExecuteAndTest(mem, None, v, output, true)
|
||||
}
|
||||
|
||||
class Synflops2048x16_noLib extends MacroCompilerSpec with HasSRAMGenerator with HasNoLibTestGenerator with HasSynFlopsTestGenerator {
|
||||
class Synflops2048x16_noLib
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasNoLibTestGenerator
|
||||
with HasSynFlopsTestGenerator {
|
||||
override lazy val memDepth = BigInt(2048)
|
||||
override lazy val memWidth = 16
|
||||
|
||||
compileExecuteAndTest(mem, None, v, output, true)
|
||||
}
|
||||
|
||||
class Synflops8192x16_noLib extends MacroCompilerSpec with HasSRAMGenerator with HasNoLibTestGenerator with HasSynFlopsTestGenerator {
|
||||
class Synflops8192x16_noLib
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasNoLibTestGenerator
|
||||
with HasSynFlopsTestGenerator {
|
||||
override lazy val memDepth = BigInt(8192)
|
||||
override lazy val memWidth = 16
|
||||
|
||||
compileExecuteAndTest(mem, None, v, output, true)
|
||||
}
|
||||
|
||||
class Synflops2048x16_depth_Lib extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with HasSynFlopsTestGenerator {
|
||||
class Synflops2048x16_depth_Lib
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with HasSynFlopsTestGenerator {
|
||||
override lazy val memDepth = BigInt(2048)
|
||||
override lazy val libDepth = BigInt(1024)
|
||||
override lazy val width = 16
|
||||
@@ -87,7 +104,11 @@ class Synflops2048x16_depth_Lib extends MacroCompilerSpec with HasSRAMGenerator
|
||||
compileExecuteAndTest(mem, lib, v, output, true)
|
||||
}
|
||||
|
||||
class Synflops2048x64_width_Lib extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator with HasSynFlopsTestGenerator {
|
||||
class Synflops2048x64_width_Lib
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleWidthTestGenerator
|
||||
with HasSynFlopsTestGenerator {
|
||||
override lazy val memWidth = 64
|
||||
override lazy val libWidth = 8
|
||||
override lazy val depth = BigInt(1024)
|
||||
@@ -95,7 +116,11 @@ class Synflops2048x64_width_Lib extends MacroCompilerSpec with HasSRAMGenerator
|
||||
compileExecuteAndTest(mem, lib, v, output, true)
|
||||
}
|
||||
|
||||
class Synflops_SplitPorts_Read_Write extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with HasSynFlopsTestGenerator {
|
||||
class Synflops_SplitPorts_Read_Write
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with HasSynFlopsTestGenerator {
|
||||
import mdf.macrolib._
|
||||
|
||||
override lazy val memDepth = BigInt(2048)
|
||||
@@ -103,29 +128,29 @@ class Synflops_SplitPorts_Read_Write extends MacroCompilerSpec with HasSRAMGener
|
||||
override lazy val width = 8
|
||||
|
||||
override def generateLibSRAM = SRAMMacro(
|
||||
name=lib_name,
|
||||
width=width,
|
||||
depth=libDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(
|
||||
name = lib_name,
|
||||
width = width,
|
||||
depth = libDepth,
|
||||
family = "1r1w",
|
||||
ports = Seq(
|
||||
generateReadPort("innerA", width, libDepth),
|
||||
generateWritePort("innerB", width, libDepth)
|
||||
)
|
||||
)
|
||||
|
||||
override def generateMemSRAM = SRAMMacro(
|
||||
name=mem_name,
|
||||
width=width,
|
||||
depth=memDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(
|
||||
name = mem_name,
|
||||
width = width,
|
||||
depth = memDepth,
|
||||
family = "1r1w",
|
||||
ports = Seq(
|
||||
generateReadPort("outerB", width, memDepth),
|
||||
generateWritePort("outerA", width, memDepth)
|
||||
)
|
||||
)
|
||||
|
||||
override def generateHeader =
|
||||
"""
|
||||
"""
|
||||
circuit target_memory :
|
||||
module target_memory :
|
||||
input outerB_addr : UInt<11>
|
||||
@@ -138,7 +163,7 @@ circuit target_memory :
|
||||
"""
|
||||
|
||||
override def generateBody =
|
||||
"""
|
||||
"""
|
||||
node outerB_addr_sel = bits(outerB_addr, 10, 10)
|
||||
reg outerB_addr_sel_reg : UInt<1>, outerB_clk with :
|
||||
reset => (UInt<1>("h0"), outerB_addr_sel_reg)
|
||||
@@ -166,7 +191,7 @@ circuit target_memory :
|
||||
"""
|
||||
|
||||
override def generateFooterPorts =
|
||||
"""
|
||||
"""
|
||||
input innerA_addr : UInt<10>
|
||||
input innerA_clk : Clock
|
||||
output innerA_dout : UInt<8>
|
||||
@@ -177,7 +202,7 @@ circuit target_memory :
|
||||
"""
|
||||
|
||||
override def generateFlops =
|
||||
"""
|
||||
"""
|
||||
inst mem_0_0 of split_awesome_lib_mem
|
||||
mem_0_0.innerB_clk <= innerB_clk
|
||||
mem_0_0.innerB_addr <= innerB_addr
|
||||
@@ -222,7 +247,11 @@ circuit target_memory :
|
||||
}
|
||||
}
|
||||
|
||||
class Synflops_SplitPorts_MaskedMem_Read_MaskedWrite extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator with HasSynFlopsTestGenerator {
|
||||
class Synflops_SplitPorts_MaskedMem_Read_MaskedWrite
|
||||
extends MacroCompilerSpec
|
||||
with HasSRAMGenerator
|
||||
with HasSimpleDepthTestGenerator
|
||||
with HasSynFlopsTestGenerator {
|
||||
import mdf.macrolib._
|
||||
|
||||
override lazy val memDepth = BigInt(2048)
|
||||
@@ -232,29 +261,29 @@ class Synflops_SplitPorts_MaskedMem_Read_MaskedWrite extends MacroCompilerSpec w
|
||||
override lazy val libMaskGran = Some(1)
|
||||
|
||||
override def generateLibSRAM = SRAMMacro(
|
||||
name=lib_name,
|
||||
width=width,
|
||||
depth=libDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(
|
||||
name = lib_name,
|
||||
width = width,
|
||||
depth = libDepth,
|
||||
family = "1r1w",
|
||||
ports = Seq(
|
||||
generateReadPort("innerA", width, libDepth),
|
||||
generateWritePort("innerB", width, libDepth, libMaskGran)
|
||||
)
|
||||
)
|
||||
|
||||
override def generateMemSRAM = SRAMMacro(
|
||||
name=mem_name,
|
||||
width=width,
|
||||
depth=memDepth,
|
||||
family="1r1w",
|
||||
ports=Seq(
|
||||
name = mem_name,
|
||||
width = width,
|
||||
depth = memDepth,
|
||||
family = "1r1w",
|
||||
ports = Seq(
|
||||
generateReadPort("outerB", width, memDepth),
|
||||
generateWritePort("outerA", width, memDepth, memMaskGran)
|
||||
)
|
||||
)
|
||||
|
||||
override def generateHeader =
|
||||
"""
|
||||
"""
|
||||
circuit target_memory :
|
||||
module target_memory :
|
||||
input outerB_addr : UInt<11>
|
||||
@@ -268,7 +297,7 @@ circuit target_memory :
|
||||
"""
|
||||
|
||||
override def generateBody =
|
||||
"""
|
||||
"""
|
||||
node outerB_addr_sel = bits(outerB_addr, 10, 10)
|
||||
reg outerB_addr_sel_reg : UInt<1>, outerB_clk with :
|
||||
reset => (UInt<1>("h0"), outerB_addr_sel_reg)
|
||||
@@ -298,7 +327,7 @@ circuit target_memory :
|
||||
"""
|
||||
|
||||
override def generateFooterPorts =
|
||||
"""
|
||||
"""
|
||||
input innerA_addr : UInt<10>
|
||||
input innerA_clk : Clock
|
||||
output innerA_dout : UInt<8>
|
||||
@@ -310,7 +339,7 @@ circuit target_memory :
|
||||
"""
|
||||
|
||||
override def generateFlops =
|
||||
"""
|
||||
"""
|
||||
inst mem_0_0 of split_awesome_lib_mem
|
||||
inst mem_0_1 of split_awesome_lib_mem
|
||||
inst mem_0_2 of split_awesome_lib_mem
|
||||
@@ -2,15 +2,14 @@
|
||||
|
||||
package barstools.tapeout.transforms
|
||||
|
||||
import firrtl._
|
||||
import firrtl.ir._
|
||||
import firrtl.Mappers._
|
||||
import firrtl.annotations.{ModuleTarget, SingleTargetAnnotation, CircuitTarget}
|
||||
import firrtl.stage.TransformManager.{TransformDependency}
|
||||
import firrtl.stage.{Forms}
|
||||
import firrtl._
|
||||
import firrtl.annotations.{CircuitTarget, ModuleTarget, SingleTargetAnnotation}
|
||||
import firrtl.ir._
|
||||
import firrtl.stage.Forms
|
||||
import firrtl.stage.TransformManager.TransformDependency
|
||||
|
||||
case class KeepNameAnnotation(target: ModuleTarget)
|
||||
extends SingleTargetAnnotation[ModuleTarget] {
|
||||
case class KeepNameAnnotation(target: ModuleTarget) extends SingleTargetAnnotation[ModuleTarget] {
|
||||
def duplicate(n: ModuleTarget) = this.copy(n)
|
||||
}
|
||||
|
||||
@@ -21,8 +20,8 @@ case class ModuleNameSuffixAnnotation(target: CircuitTarget, suffix: String)
|
||||
|
||||
class AddSuffixToModuleNames extends Transform with DependencyAPIMigration {
|
||||
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.LowForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.LowForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
|
||||
override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters
|
||||
override def invalidates(a: Transform): Boolean = false
|
||||
|
||||
@@ -37,7 +36,7 @@ class AddSuffixToModuleNames extends Transform with DependencyAPIMigration {
|
||||
val excludeSet = state.circuit.modules.flatMap {
|
||||
case e: ExtModule => Some(e.name)
|
||||
case m if (m.name == state.circuit.main) => Some(m.name)
|
||||
case _ => None
|
||||
case _ => None
|
||||
}.toSet
|
||||
|
||||
val renamer = { (name: String) => if (excludeSet(name)) name else name + suffix }
|
||||
@@ -3,18 +3,18 @@
|
||||
package barstools.tapeout.transforms
|
||||
|
||||
import firrtl._
|
||||
import firrtl.annotations.NoTargetAnnotation
|
||||
import firrtl.ir._
|
||||
import firrtl.annotations.{NoTargetAnnotation}
|
||||
import firrtl.options.{Dependency}
|
||||
import firrtl.stage.TransformManager.{TransformDependency}
|
||||
import firrtl.stage.{Forms}
|
||||
import firrtl.passes.memlib.{ReplSeqMem}
|
||||
import firrtl.options.Dependency
|
||||
import firrtl.passes.memlib.ReplSeqMem
|
||||
import firrtl.stage.Forms
|
||||
import firrtl.stage.TransformManager.TransformDependency
|
||||
|
||||
case class LinkExtModulesAnnotation(mustLink: Seq[ExtModule]) extends NoTargetAnnotation
|
||||
|
||||
class AvoidExtModuleCollisions extends Transform with DependencyAPIMigration {
|
||||
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.HighForm
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.HighForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Seq(Dependency[RemoveUnusedModules])
|
||||
override def optionalPrerequisiteOf: Seq[TransformDependency] = {
|
||||
Forms.HighEmitters :+ Dependency[ReplSeqMem]
|
||||
@@ -24,10 +24,9 @@ class AvoidExtModuleCollisions extends Transform with DependencyAPIMigration {
|
||||
def execute(state: CircuitState): CircuitState = {
|
||||
val mustLink = state.annotations.flatMap {
|
||||
case LinkExtModulesAnnotation(mustLink) => mustLink
|
||||
case _ => Nil
|
||||
case _ => Nil
|
||||
}
|
||||
val newAnnos = state.annotations.filterNot(_.isInstanceOf[LinkExtModulesAnnotation])
|
||||
state.copy(circuit = state.circuit.copy(modules = state.circuit.modules ++ mustLink), annotations = newAnnos)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
package barstools.tapeout.transforms
|
||||
|
||||
import firrtl._
|
||||
import firrtl.annotations.{ModuleTarget, ReferenceTarget, SingleTargetAnnotation}
|
||||
import firrtl.ir._
|
||||
import firrtl.annotations.{ModuleTarget, SingleTargetAnnotation, ReferenceTarget}
|
||||
import firrtl.stage.TransformManager.{TransformDependency}
|
||||
import firrtl.stage.{Forms}
|
||||
import firrtl.options.{Dependency}
|
||||
import firrtl.passes.memlib.{ReplSeqMem}
|
||||
import firrtl.options.Dependency
|
||||
import firrtl.passes.memlib.ReplSeqMem
|
||||
import firrtl.stage.Forms
|
||||
import firrtl.stage.TransformManager.TransformDependency
|
||||
|
||||
case class ConvertToExtModAnnotation(target: ModuleTarget)
|
||||
extends SingleTargetAnnotation[ModuleTarget] {
|
||||
case class ConvertToExtModAnnotation(target: ModuleTarget) extends SingleTargetAnnotation[ModuleTarget] {
|
||||
def duplicate(n: ModuleTarget) = this.copy(n)
|
||||
}
|
||||
|
||||
@@ -20,7 +19,7 @@ case class ConvertToExtModAnnotation(target: ModuleTarget)
|
||||
// otherwise it's left alone.
|
||||
class ConvertToExtMod extends Transform with DependencyAPIMigration {
|
||||
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.HighForm
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.HighForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Seq.empty
|
||||
override def optionalPrerequisiteOf: Seq[TransformDependency] = {
|
||||
Forms.HighEmitters ++ Seq(Dependency[RemoveUnusedModules], Dependency[ReplSeqMem])
|
||||
@@ -23,10 +23,12 @@ class EnumerateModulesPass(enumerate: (Module) => Unit) extends Pass {
|
||||
}
|
||||
|
||||
class EnumerateModules(enumerate: (Module) => Unit)
|
||||
extends Transform with SeqTransformBased with DependencyAPIMigration {
|
||||
extends Transform
|
||||
with SeqTransformBased
|
||||
with DependencyAPIMigration {
|
||||
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.LowForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.LowForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
|
||||
override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters
|
||||
override def invalidates(a: Transform): Boolean = false
|
||||
|
||||
@@ -5,7 +5,7 @@ import firrtl.annotations._
|
||||
import firrtl.ir._
|
||||
import firrtl.passes.memlib.ReplSeqMemAnnotation
|
||||
import firrtl.stage.FirrtlCircuitAnnotation
|
||||
import firrtl.transforms.{BlackBoxResourceFileNameAnno, DedupModules}
|
||||
import firrtl.transforms.BlackBoxResourceFileNameAnno
|
||||
import logger.LazyLogging
|
||||
|
||||
trait HasTapeoutOptions { self: ExecutionOptionsManager with HasFirrtlOptions =>
|
||||
@@ -13,130 +13,150 @@ trait HasTapeoutOptions { self: ExecutionOptionsManager with HasFirrtlOptions =>
|
||||
|
||||
parser.note("tapeout options")
|
||||
|
||||
parser.opt[String]("harness-o")
|
||||
parser
|
||||
.opt[String]("harness-o")
|
||||
.abbr("tho")
|
||||
.valueName("<harness-output>")
|
||||
.foreach { x =>
|
||||
tapeoutOptions = tapeoutOptions.copy(
|
||||
harnessOutput = Some(x)
|
||||
)
|
||||
}.text {
|
||||
}
|
||||
.text {
|
||||
"use this to generate a harness at <harness-output>"
|
||||
}
|
||||
|
||||
parser.opt[String]("syn-top")
|
||||
parser
|
||||
.opt[String]("syn-top")
|
||||
.abbr("tst")
|
||||
.valueName("<syn-top>")
|
||||
.foreach { x =>
|
||||
tapeoutOptions = tapeoutOptions.copy(
|
||||
synTop = Some(x)
|
||||
)
|
||||
}.text {
|
||||
}
|
||||
.text {
|
||||
"use this to set synTop"
|
||||
}
|
||||
|
||||
parser.opt[String]("top-fir")
|
||||
parser
|
||||
.opt[String]("top-fir")
|
||||
.abbr("tsf")
|
||||
.valueName("<top-fir>")
|
||||
.foreach { x =>
|
||||
tapeoutOptions = tapeoutOptions.copy(
|
||||
topFir = Some(x)
|
||||
)
|
||||
}.text {
|
||||
}
|
||||
.text {
|
||||
"use this to set topFir"
|
||||
}
|
||||
|
||||
parser.opt[String]("top-anno-out")
|
||||
parser
|
||||
.opt[String]("top-anno-out")
|
||||
.abbr("tsaof")
|
||||
.valueName("<top-anno-out>")
|
||||
.foreach { x =>
|
||||
tapeoutOptions = tapeoutOptions.copy(
|
||||
topAnnoOut = Some(x)
|
||||
)
|
||||
}.text {
|
||||
}
|
||||
.text {
|
||||
"use this to set topAnnoOut"
|
||||
}
|
||||
|
||||
parser.opt[String]("top-dotf-out")
|
||||
parser
|
||||
.opt[String]("top-dotf-out")
|
||||
.abbr("tdf")
|
||||
.valueName("<top-dotf-out>")
|
||||
.foreach { x =>
|
||||
tapeoutOptions = tapeoutOptions.copy(
|
||||
topDotfOut = Some(x)
|
||||
)
|
||||
}.text {
|
||||
}
|
||||
.text {
|
||||
"use this to set the filename for the top resource .f file"
|
||||
}
|
||||
|
||||
parser.opt[String]("harness-top")
|
||||
parser
|
||||
.opt[String]("harness-top")
|
||||
.abbr("tht")
|
||||
.valueName("<harness-top>")
|
||||
.foreach { x =>
|
||||
tapeoutOptions = tapeoutOptions.copy(
|
||||
harnessTop = Some(x)
|
||||
)
|
||||
}.text {
|
||||
}
|
||||
.text {
|
||||
"use this to set harnessTop"
|
||||
}
|
||||
|
||||
parser.opt[String]("harness-fir")
|
||||
parser
|
||||
.opt[String]("harness-fir")
|
||||
.abbr("thf")
|
||||
.valueName("<harness-fir>")
|
||||
.foreach { x =>
|
||||
tapeoutOptions = tapeoutOptions.copy(
|
||||
harnessFir = Some(x)
|
||||
)
|
||||
}.text {
|
||||
}
|
||||
.text {
|
||||
"use this to set harnessFir"
|
||||
}
|
||||
|
||||
parser.opt[String]("harness-anno-out")
|
||||
parser
|
||||
.opt[String]("harness-anno-out")
|
||||
.abbr("thaof")
|
||||
.valueName("<harness-anno-out>")
|
||||
.foreach { x =>
|
||||
tapeoutOptions = tapeoutOptions.copy(
|
||||
harnessAnnoOut = Some(x)
|
||||
)
|
||||
}.text {
|
||||
}
|
||||
.text {
|
||||
"use this to set harnessAnnoOut"
|
||||
}
|
||||
|
||||
parser.opt[String]("harness-dotf-out")
|
||||
parser
|
||||
.opt[String]("harness-dotf-out")
|
||||
.abbr("hdf")
|
||||
.valueName("<harness-dotf-out>")
|
||||
.foreach { x =>
|
||||
tapeoutOptions = tapeoutOptions.copy(
|
||||
harnessDotfOut = Some(x)
|
||||
)
|
||||
}.text {
|
||||
}
|
||||
.text {
|
||||
"use this to set the filename for the harness resource .f file"
|
||||
}
|
||||
|
||||
parser.opt[String]("harness-conf")
|
||||
parser
|
||||
.opt[String]("harness-conf")
|
||||
.abbr("thconf")
|
||||
.valueName ("<harness-conf-file>")
|
||||
.valueName("<harness-conf-file>")
|
||||
.foreach { x =>
|
||||
tapeoutOptions = tapeoutOptions.copy(
|
||||
harnessConf = Some(x)
|
||||
)
|
||||
}.text {
|
||||
}
|
||||
.text {
|
||||
"use this to set the harness conf file location"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case class TapeoutOptions(
|
||||
harnessOutput: Option[String] = None,
|
||||
synTop: Option[String] = None,
|
||||
topFir: Option[String] = None,
|
||||
topAnnoOut: Option[String] = None,
|
||||
topDotfOut: Option[String] = None,
|
||||
harnessTop: Option[String] = None,
|
||||
harnessFir: Option[String] = None,
|
||||
harnessOutput: Option[String] = None,
|
||||
synTop: Option[String] = None,
|
||||
topFir: Option[String] = None,
|
||||
topAnnoOut: Option[String] = None,
|
||||
topDotfOut: Option[String] = None,
|
||||
harnessTop: Option[String] = None,
|
||||
harnessFir: Option[String] = None,
|
||||
harnessAnnoOut: Option[String] = None,
|
||||
harnessDotfOut: Option[String] = None,
|
||||
harnessConf: Option[String] = None
|
||||
) extends LazyLogging
|
||||
harnessConf: Option[String] = None)
|
||||
extends LazyLogging
|
||||
|
||||
// Requires two phases, one to collect modules below synTop in the hierarchy
|
||||
// and a second to remove those modules to generate the test harness
|
||||
@@ -190,9 +210,9 @@ sealed trait GenerateTopAndHarnessApp extends LazyLogging { this: App =>
|
||||
annoFile.foreach { annoPath =>
|
||||
val outputFile = new java.io.PrintWriter(annoPath)
|
||||
outputFile.write(JsonProtocol.serialize(res.circuitState.annotations.filter(_ match {
|
||||
case da: DeletedAnnotation => false
|
||||
case ec: EmittedComponent => false
|
||||
case ea: EmittedAnnotation[_] => false
|
||||
case da: DeletedAnnotation => false
|
||||
case ec: EmittedComponent => false
|
||||
case ea: EmittedAnnotation[_] => false
|
||||
case fca: FirrtlCircuitAnnotation => false
|
||||
case _ => true
|
||||
})))
|
||||
@@ -207,7 +227,7 @@ sealed trait GenerateTopAndHarnessApp extends LazyLogging { this: App =>
|
||||
result match {
|
||||
case x: FirrtlExecutionSuccess =>
|
||||
dump(x, tapeoutOptions.topFir, tapeoutOptions.topAnnoOut)
|
||||
x.circuitState.circuit.modules.collect{ case e: ExtModule => e }
|
||||
x.circuitState.circuit.modules.collect { case e: ExtModule => e }
|
||||
case x =>
|
||||
throw new Exception(s"executeTop failed while executing FIRRTL!\n${x}")
|
||||
}
|
||||
@@ -220,9 +240,9 @@ sealed trait GenerateTopAndHarnessApp extends LazyLogging { this: App =>
|
||||
|
||||
val harnessAnnos =
|
||||
tapeoutOptions.harnessDotfOut.map(BlackBoxResourceFileNameAnno(_)).toSeq ++
|
||||
harnessTop.map(ht => ModuleNameSuffixAnnotation(rootCircuitTarget, s"_in${ht}")) ++
|
||||
synTop.map(st => ConvertToExtModAnnotation(rootCircuitTarget.module(st))) :+
|
||||
LinkExtModulesAnnotation(topExtModules)
|
||||
harnessTop.map(ht => ModuleNameSuffixAnnotation(rootCircuitTarget, s"_in${ht}")) ++
|
||||
synTop.map(st => ConvertToExtModAnnotation(rootCircuitTarget.module(st))) :+
|
||||
LinkExtModulesAnnotation(topExtModules)
|
||||
|
||||
// For harness run, change some firrtlOptions (below) for harness phase
|
||||
// customTransforms: setup harness transforms, add AvoidExtModuleCollisions
|
||||
@@ -233,7 +253,7 @@ sealed trait GenerateTopAndHarnessApp extends LazyLogging { this: App =>
|
||||
outputFileNameOverride = tapeoutOptions.harnessOutput.get,
|
||||
annotations = firrtlOptions.annotations.map({
|
||||
case ReplSeqMemAnnotation(i, o) => ReplSeqMemAnnotation(i, tapeoutOptions.harnessConf.get)
|
||||
case a => a
|
||||
case a => a
|
||||
}) ++ harnessAnnos
|
||||
)
|
||||
val harnessResult = firrtl.Driver.execute(optionsManager)
|
||||
@@ -3,20 +3,18 @@
|
||||
package barstools.tapeout.transforms
|
||||
|
||||
import firrtl._
|
||||
import firrtl.ir._
|
||||
import firrtl.annotations._
|
||||
import firrtl.options.{Dependency}
|
||||
import firrtl.stage.TransformManager.{TransformDependency}
|
||||
import firrtl.stage.{Forms}
|
||||
import firrtl.options.Dependency
|
||||
import firrtl.stage.Forms
|
||||
import firrtl.stage.TransformManager.TransformDependency
|
||||
|
||||
case class ReParentCircuitAnnotation(target: ModuleTarget)
|
||||
extends SingleTargetAnnotation[ModuleTarget] {
|
||||
case class ReParentCircuitAnnotation(target: ModuleTarget) extends SingleTargetAnnotation[ModuleTarget] {
|
||||
def duplicate(n: ModuleTarget) = this.copy(n)
|
||||
}
|
||||
|
||||
class ReParentCircuit extends Transform with DependencyAPIMigration {
|
||||
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.HighForm
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.HighForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Seq.empty
|
||||
override def optionalPrerequisiteOf: Seq[TransformDependency] = {
|
||||
Forms.HighEmitters :+ Dependency[RemoveUnusedModules]
|
||||
@@ -25,8 +23,8 @@ class ReParentCircuit extends Transform with DependencyAPIMigration {
|
||||
|
||||
def execute(state: CircuitState): CircuitState = {
|
||||
val c = state.circuit
|
||||
val newTopName = state.annotations.collectFirst {
|
||||
case ReParentCircuitAnnotation(tgt) => tgt.module
|
||||
val newTopName = state.annotations.collectFirst { case ReParentCircuitAnnotation(tgt) =>
|
||||
tgt.module
|
||||
}
|
||||
val newCircuit = c.copy(main = newTopName.getOrElse(c.main))
|
||||
val mainRename = newTopName.map { s =>
|
||||
@@ -3,18 +3,18 @@
|
||||
package barstools.tapeout.transforms
|
||||
|
||||
import firrtl._
|
||||
import firrtl.annotations.ModuleTarget
|
||||
import firrtl.ir._
|
||||
import firrtl.annotations.{ModuleTarget}
|
||||
import firrtl.stage.TransformManager.{TransformDependency}
|
||||
import firrtl.options.{Dependency}
|
||||
import firrtl.stage.{Forms}
|
||||
import firrtl.passes.memlib.{ReplSeqMem}
|
||||
import firrtl.options.Dependency
|
||||
import firrtl.passes.memlib.ReplSeqMem
|
||||
import firrtl.stage.Forms
|
||||
import firrtl.stage.TransformManager.TransformDependency
|
||||
|
||||
// Removes all the unused modules in a circuit by recursing through every
|
||||
// instance (starting at the main module)
|
||||
class RemoveUnusedModules extends Transform with DependencyAPIMigration {
|
||||
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.HighForm
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.HighForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Seq.empty
|
||||
override def optionalPrerequisiteOf: Seq[TransformDependency] = {
|
||||
Forms.HighEmitters :+ Dependency[ReplSeqMem]
|
||||
@@ -22,8 +22,8 @@ class RemoveUnusedModules extends Transform with DependencyAPIMigration {
|
||||
override def invalidates(a: Transform): Boolean = false
|
||||
|
||||
def execute(state: CircuitState): CircuitState = {
|
||||
val modulesByName = state.circuit.modules.map{
|
||||
case m: Module => (m.name, Some(m))
|
||||
val modulesByName = state.circuit.modules.map {
|
||||
case m: Module => (m.name, Some(m))
|
||||
case m: ExtModule => (m.name, None)
|
||||
}.toMap
|
||||
|
||||
@@ -33,7 +33,7 @@ class RemoveUnusedModules extends Transform with DependencyAPIMigration {
|
||||
def someStatements(statement: Statement): Seq[Statement] =
|
||||
statement match {
|
||||
case b: Block =>
|
||||
b.stmts.map{ someStatements(_) }
|
||||
b.stmts.map { someStatements(_) }
|
||||
.foldLeft(Seq[Statement]())(_ ++ _)
|
||||
case when: Conditionally =>
|
||||
someStatements(when.conseq) ++ someStatements(when.alt)
|
||||
@@ -41,11 +41,11 @@ class RemoveUnusedModules extends Transform with DependencyAPIMigration {
|
||||
case _ => Seq()
|
||||
}
|
||||
|
||||
someStatements(m.body).map{
|
||||
case s: DefInstance => Set(s.module) | getUsedModules(modulesByName(s.module))
|
||||
case _ => Set[String]()
|
||||
}.foldLeft(Set(m.name))(_ | _)
|
||||
}
|
||||
someStatements(m.body).map {
|
||||
case s: DefInstance => Set(s.module) | getUsedModules(modulesByName(s.module))
|
||||
case _ => Set[String]()
|
||||
}.foldLeft(Set(m.name))(_ | _)
|
||||
}
|
||||
|
||||
case None => Set.empty[String]
|
||||
}
|
||||
@@ -57,7 +57,7 @@ class RemoveUnusedModules extends Transform with DependencyAPIMigration {
|
||||
|
||||
val renames = state.renames.getOrElse(RenameMap())
|
||||
|
||||
state.circuit.modules.filterNot { usedModuleSet contains _.name } foreach { x =>
|
||||
state.circuit.modules.filterNot { usedModuleSet contains _.name }.foreach { x =>
|
||||
renames.record(ModuleTarget(state.circuit.main, x.name), Nil)
|
||||
}
|
||||
|
||||
@@ -20,10 +20,9 @@ object ResetN extends Pass {
|
||||
// Only works on Modules with a Bool port named reset
|
||||
def invertReset(mod: Module): Module = {
|
||||
// Check that it actually has reset
|
||||
require(mod.ports.exists(p => p.name == "reset" && p.tpe == Bool),
|
||||
"Can only invert reset on a module with reset!")
|
||||
require(mod.ports.exists(p => p.name == "reset" && p.tpe == Bool), "Can only invert reset on a module with reset!")
|
||||
// Rename "reset" to "reset_n"
|
||||
val portsx = mod.ports map {
|
||||
val portsx = mod.ports.map {
|
||||
case Port(info, "reset", Input, Bool) =>
|
||||
Port(info, "reset_n", Input, Bool)
|
||||
case other => other
|
||||
@@ -34,7 +33,7 @@ object ResetN extends Pass {
|
||||
}
|
||||
|
||||
def run(c: Circuit): Circuit = {
|
||||
c.copy(modules = c.modules map {
|
||||
c.copy(modules = c.modules.map {
|
||||
case mod: Module if mod.name == c.main => invertReset(mod)
|
||||
case other => other
|
||||
})
|
||||
@@ -43,8 +42,8 @@ object ResetN extends Pass {
|
||||
|
||||
class ResetInverterTransform extends Transform with DependencyAPIMigration {
|
||||
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.LowForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.LowForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
|
||||
override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters
|
||||
override def invalidates(a: Transform): Boolean = false
|
||||
|
||||
@@ -64,7 +63,7 @@ trait ResetInverter {
|
||||
def invert[T <: chisel3.internal.LegacyModule](module: T): Unit = {
|
||||
chisel3.experimental.annotate(new chisel3.experimental.ChiselAnnotation with RunFirrtlTransform {
|
||||
def transformClass: Class[_ <: Transform] = classOf[ResetInverterTransform]
|
||||
def toFirrtl: Annotation = ResetInverterAnnotation(module.toNamed)
|
||||
def toFirrtl: Annotation = ResetInverterAnnotation(module.toNamed)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -14,23 +14,24 @@ case class RetimeAnnotation(target: Named) extends SingleTargetAnnotation[Named]
|
||||
|
||||
class RetimeTransform extends Transform with DependencyAPIMigration {
|
||||
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.LowForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.LowForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
|
||||
override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters
|
||||
override def invalidates(a: Transform): Boolean = false
|
||||
|
||||
override def execute(state: CircuitState): CircuitState = {
|
||||
state.annotations.filter(_.isInstanceOf[RetimeAnnotation]) match {
|
||||
case Nil => state
|
||||
case seq => seq.foreach {
|
||||
case RetimeAnnotation(ModuleName(module, CircuitName(_))) =>
|
||||
logger.info(s"Retiming module $module")
|
||||
case RetimeAnnotation(ComponentName(name, ModuleName(module, CircuitName(_)))) =>
|
||||
logger.info(s"Retiming instance $module.$name")
|
||||
case _ =>
|
||||
throw new Exception(s"There should be RetimeAnnotations, got ${seq.mkString(" -- ")}")
|
||||
}
|
||||
state
|
||||
case seq =>
|
||||
seq.foreach {
|
||||
case RetimeAnnotation(ModuleName(module, CircuitName(_))) =>
|
||||
logger.info(s"Retiming module $module")
|
||||
case RetimeAnnotation(ComponentName(name, ModuleName(module, CircuitName(_)))) =>
|
||||
logger.info(s"Retiming instance $module.$name")
|
||||
case _ =>
|
||||
throw new Exception(s"There should be RetimeAnnotations, got ${seq.mkString(" -- ")}")
|
||||
}
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +42,7 @@ trait RetimeLib {
|
||||
def retime[T <: chisel3.internal.LegacyModule](module: T): Unit = {
|
||||
chisel3.experimental.annotate(new chisel3.experimental.ChiselAnnotation with RunFirrtlTransform {
|
||||
def transformClass: Class[_ <: Transform] = classOf[RetimeTransform]
|
||||
def toFirrtl: Annotation = RetimeAnnotation(module.toNamed)
|
||||
def toFirrtl: Annotation = RetimeAnnotation(module.toNamed)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package barstools.tapeout.transforms
|
||||
package barstools.tapeout.transforms.utils
|
||||
|
||||
import chisel3.experimental.{ChiselAnnotation, annotate}
|
||||
import firrtl._
|
||||
@@ -12,7 +12,7 @@ import firrtl.transforms.BlackBoxTargetDirAnno
|
||||
object WriteConfig {
|
||||
def apply(dir: String, file: String, contents: String): Unit = {
|
||||
val writer = new java.io.PrintWriter(new java.io.File(s"$dir/$file"))
|
||||
writer write contents
|
||||
writer.write(contents)
|
||||
writer.close()
|
||||
}
|
||||
}
|
||||
@@ -22,14 +22,14 @@ object GetTargetDir {
|
||||
val annos = state.annotations
|
||||
val destDir = annos.map {
|
||||
case BlackBoxTargetDirAnno(s) => Some(s)
|
||||
case _ => None
|
||||
case _ => None
|
||||
}.flatten
|
||||
val loc = {
|
||||
if (destDir.isEmpty) "."
|
||||
else destDir.head
|
||||
}
|
||||
val targetDir = new java.io.File(loc)
|
||||
if(!targetDir.exists()) FileUtils.makeDirectory(targetDir.getAbsolutePath)
|
||||
if (!targetDir.exists()) FileUtils.makeDirectory(targetDir.getAbsolutePath)
|
||||
loc
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,8 @@ case class TechnologyLocationAnnotation(dir: String) extends SingleTargetAnnotat
|
||||
|
||||
class TechnologyLocation extends Transform with DependencyAPIMigration {
|
||||
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.LowForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
|
||||
override def prerequisites: Seq[TransformDependency] = Forms.LowForm
|
||||
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
|
||||
override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters
|
||||
|
||||
def execute(state: CircuitState): CircuitState = {
|
||||
@@ -65,18 +65,15 @@ class TechnologyLocation extends Transform with DependencyAPIMigration {
|
||||
val annos = state.annotations
|
||||
val dir = annos.flatMap {
|
||||
case TechnologyLocationAnnotation(dir) => Some(dir)
|
||||
case _ => None
|
||||
case _ => None
|
||||
}
|
||||
dir.length match {
|
||||
case 0 => ""
|
||||
case 1 =>
|
||||
val targetDir = new java.io.File(dir.head)
|
||||
if(!targetDir.exists()) throw new Exception(s"Technology yaml directory $targetDir doesn't exist!")
|
||||
if (!targetDir.exists()) throw new Exception(s"Technology yaml directory $targetDir doesn't exist!")
|
||||
dir.head
|
||||
case _ => throw new Exception("Only 1 tech directory annotation allowed!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package barstools.tapeout.transforms
|
||||
package barstools.tapeout.transforms.utils
|
||||
|
||||
object LowerName {
|
||||
def apply(s: String): String = s.replace(".", "_").replace("[", "_")replace("]", "")
|
||||
}
|
||||
def apply(s: String): String = s.replace(".", "_").replace("[", "_").replace("]", "")
|
||||
}
|
||||
@@ -1,20 +1,21 @@
|
||||
package barstools.tapeout.transforms
|
||||
package barstools.tapeout.transforms.utils
|
||||
|
||||
import chisel3._
|
||||
|
||||
import scala.collection.immutable.ListMap
|
||||
|
||||
class CustomBundle[T <: Data](elts: (String, T)*) extends Record {
|
||||
val elements = ListMap(elts map { case (field, elt) => field -> chiselTypeOf(elt) }: _*)
|
||||
val elements = ListMap(elts.map { case (field, elt) => field -> chiselTypeOf(elt) }: _*)
|
||||
def apply(elt: String): T = elements(elt)
|
||||
def apply(elt: Int): T = elements(elt.toString)
|
||||
def apply(elt: Int): T = elements(elt.toString)
|
||||
override def cloneType = (new CustomBundle(elements.toList: _*)).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
class CustomIndexedBundle[T <: Data](elts: (Int, T)*) extends Record {
|
||||
// Must be String, Data
|
||||
val elements = ListMap(elts map { case (field, elt) => field.toString -> chiselTypeOf(elt) }: _*)
|
||||
val elements = ListMap(elts.map { case (field, elt) => field.toString -> chiselTypeOf(elt) }: _*)
|
||||
// TODO: Make an equivalent to the below work publicly (or only on subclasses?)
|
||||
def indexedElements = ListMap(elts map { case (field, elt) => field -> chiselTypeOf(elt) }: _*)
|
||||
def indexedElements = ListMap(elts.map { case (field, elt) => field -> chiselTypeOf(elt) }: _*)
|
||||
def apply(elt: Int): T = elements(elt.toString)
|
||||
override def cloneType = (new CustomIndexedBundle(indexedElements.toList: _*)).asInstanceOf[this.type]
|
||||
}
|
||||
@@ -22,5 +23,7 @@ class CustomIndexedBundle[T <: Data](elts: (Int, T)*) extends Record {
|
||||
object CustomIndexedBundle {
|
||||
def apply[T <: Data](gen: T, idxs: Seq[Int]) = new CustomIndexedBundle(idxs.map(_ -> gen): _*)
|
||||
// Allows Vecs of elements of different types/widths
|
||||
def apply[T <: Data](gen: Seq[T]) = new CustomIndexedBundle(gen.zipWithIndex.map{ case (elt, field) => field -> elt }: _*)
|
||||
def apply[T <: Data](gen: Seq[T]) = new CustomIndexedBundle(gen.zipWithIndex.map { case (elt, field) =>
|
||||
field -> elt
|
||||
}: _*)
|
||||
}
|
||||
@@ -1,21 +1,22 @@
|
||||
package barstools.tapeout.transforms
|
||||
package barstools.tapeout.transforms.utils
|
||||
|
||||
import net.jcazevedo.moultingyaml._
|
||||
|
||||
import java.io.File
|
||||
|
||||
class YamlFileReader(resource: String) {
|
||||
def parse[A](file: String = "")(implicit reader: YamlReader[A]) : Seq[A] = {
|
||||
def parse[A](file: String = "")(implicit reader: YamlReader[A]): Seq[A] = {
|
||||
// If the user doesn't provide a Yaml file name, use defaults
|
||||
val yamlString = file match {
|
||||
case f if f.isEmpty =>
|
||||
case f if f.isEmpty =>
|
||||
// Use example config if no file is provided
|
||||
val stream = getClass.getResourceAsStream(resource)
|
||||
io.Source.fromInputStream(stream).mkString
|
||||
case f if new File(f).exists =>
|
||||
case f if new File(f).exists =>
|
||||
scala.io.Source.fromFile(f).getLines.mkString("\n")
|
||||
case _ =>
|
||||
case _ =>
|
||||
throw new Exception("No valid Yaml file found!")
|
||||
}
|
||||
yamlString.parseYamls.map(x => reader.read(x))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,10 @@
|
||||
package barstools.tapeout.transforms
|
||||
|
||||
import chisel3._
|
||||
import chisel3.stage.{ChiselStage, ChiselGeneratorAnnotation}
|
||||
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
|
||||
import firrtl.{EmittedFirrtlCircuitAnnotation, EmittedFirrtlModuleAnnotation}
|
||||
import org.scalatest.{FreeSpec, Matchers}
|
||||
import org.scalatest.freespec.AnyFreeSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class ExampleModuleNeedsResetInverted extends Module with ResetInverter {
|
||||
val io = IO(new Bundle {
|
||||
@@ -19,7 +20,7 @@ class ExampleModuleNeedsResetInverted extends Module with ResetInverter {
|
||||
invert(this)
|
||||
}
|
||||
|
||||
class ResetNSpec extends FreeSpec with Matchers {
|
||||
class ResetNSpec extends AnyFreeSpec with Matchers {
|
||||
"Inverting reset needs to be done throughout module in Chirrtl" in {
|
||||
val chirrtl = (new ChiselStage).emitChirrtl(new ExampleModuleNeedsResetInverted)
|
||||
chirrtl should include("input reset :")
|
||||
@@ -29,14 +30,17 @@ class ResetNSpec extends FreeSpec with Matchers {
|
||||
|
||||
"Inverting reset needs to be done throughout module when generating firrtl" in {
|
||||
// generate low-firrtl
|
||||
val firrtl = (new ChiselStage).execute(
|
||||
Array("-X", "low"),
|
||||
Seq(ChiselGeneratorAnnotation(() => new ExampleModuleNeedsResetInverted))
|
||||
).collect {
|
||||
case EmittedFirrtlCircuitAnnotation(a) => a
|
||||
case EmittedFirrtlModuleAnnotation(a) => a
|
||||
}.map(_.value)
|
||||
.mkString("")
|
||||
val firrtl = (new ChiselStage)
|
||||
.execute(
|
||||
Array("-X", "low"),
|
||||
Seq(ChiselGeneratorAnnotation(() => new ExampleModuleNeedsResetInverted))
|
||||
)
|
||||
.collect {
|
||||
case EmittedFirrtlCircuitAnnotation(a) => a
|
||||
case EmittedFirrtlModuleAnnotation(a) => a
|
||||
}
|
||||
.map(_.value)
|
||||
.mkString("")
|
||||
|
||||
firrtl should include("input reset_n :")
|
||||
firrtl should include("node reset = not(reset_n)")
|
||||
@@ -1,16 +1,15 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package barstools.tapeout.transforms.retime.test
|
||||
package barstools.tapeout.transforms.retime
|
||||
|
||||
import chisel3._
|
||||
import chisel3.stage.{ChiselStage, ChiselGeneratorAnnotation}
|
||||
import firrtl.{EmittedFirrtlCircuitAnnotation, EmittedFirrtlModuleAnnotation}
|
||||
import barstools.tapeout.transforms.retime.RetimeLib
|
||||
import firrtl.FileUtils
|
||||
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
|
||||
import firrtl.{EmittedFirrtlCircuitAnnotation, EmittedFirrtlModuleAnnotation, FileUtils}
|
||||
import logger.Logger
|
||||
import org.scalatest.{FlatSpec, Matchers}
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class RetimeSpec extends FlatSpec with Matchers {
|
||||
class RetimeSpec extends AnyFlatSpec with Matchers {
|
||||
def normalized(s: String): String = {
|
||||
require(!s.contains("\n"))
|
||||
s.replaceAll("\\s+", " ").trim
|
||||
@@ -21,18 +20,20 @@ class RetimeSpec extends FlatSpec with Matchers {
|
||||
}
|
||||
def getLowFirrtl[T <: RawModule](gen: () => T, extraArgs: Array[String] = Array.empty): String = {
|
||||
// generate low firrtl
|
||||
(new ChiselStage).execute(
|
||||
Array("-X", "low") ++ extraArgs,
|
||||
Seq(ChiselGeneratorAnnotation(gen))
|
||||
).collect {
|
||||
case EmittedFirrtlCircuitAnnotation(a) => a
|
||||
case EmittedFirrtlModuleAnnotation(a) => a
|
||||
}.map(_.value)
|
||||
.mkString("")
|
||||
(new ChiselStage)
|
||||
.execute(
|
||||
Array("-X", "low") ++ extraArgs,
|
||||
Seq(ChiselGeneratorAnnotation(gen))
|
||||
)
|
||||
.collect {
|
||||
case EmittedFirrtlCircuitAnnotation(a) => a
|
||||
case EmittedFirrtlModuleAnnotation(a) => a
|
||||
}
|
||||
.map(_.value)
|
||||
.mkString("")
|
||||
}
|
||||
|
||||
|
||||
behavior of "retime library"
|
||||
behavior.of("retime library")
|
||||
|
||||
it should "pass simple retime module annotation" in {
|
||||
val gen = () => new RetimeModule
|
||||
@@ -43,15 +44,18 @@ class RetimeSpec extends FlatSpec with Matchers {
|
||||
Logger.setOutput(captor.printStream)
|
||||
|
||||
// generate low firrtl
|
||||
val firrtl = getLowFirrtl(gen,
|
||||
Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final", "--log-level", "info"))
|
||||
val firrtl = getLowFirrtl(
|
||||
gen,
|
||||
Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final", "--log-level", "info")
|
||||
)
|
||||
|
||||
firrtl.nonEmpty should be(true)
|
||||
//Make sure we got the RetimeTransform scheduled
|
||||
captor.getOutputAsString should include ("barstools.tapeout.transforms.retime.RetimeTransform")
|
||||
captor.getOutputAsString should include("barstools.tapeout.transforms.retime.RetimeTransform")
|
||||
}
|
||||
|
||||
val lines = FileUtils.getLines(s"test_run_dir/$dir/test_run_dir/$dir/final.anno.json")
|
||||
val lines = FileUtils
|
||||
.getLines(s"test_run_dir/$dir/test_run_dir/$dir/final.anno.json")
|
||||
.map(normalized)
|
||||
.mkString("\n")
|
||||
lines should include("barstools.tapeout.transforms.retime.RetimeAnnotation")
|
||||
@@ -67,15 +71,18 @@ class RetimeSpec extends FlatSpec with Matchers {
|
||||
Logger.setOutput(captor.printStream)
|
||||
|
||||
// generate low firrtl
|
||||
val firrtl = getLowFirrtl(gen,
|
||||
Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final", "--log-level", "info"))
|
||||
val firrtl = getLowFirrtl(
|
||||
gen,
|
||||
Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final", "--log-level", "info")
|
||||
)
|
||||
|
||||
firrtl.nonEmpty should be(true)
|
||||
//Make sure we got the RetimeTransform scheduled
|
||||
captor.getOutputAsString should include ("barstools.tapeout.transforms.retime.RetimeTransform")
|
||||
captor.getOutputAsString should include("barstools.tapeout.transforms.retime.RetimeTransform")
|
||||
}
|
||||
|
||||
val lines = FileUtils.getLines(s"test_run_dir/$dir/test_run_dir/$dir/final.anno.json")
|
||||
val lines = FileUtils
|
||||
.getLines(s"test_run_dir/$dir/test_run_dir/$dir/final.anno.json")
|
||||
.map(normalized)
|
||||
.mkString("\n")
|
||||
lines should include("barstools.tapeout.transforms.retime.RetimeAnnotation")
|
||||
Reference in New Issue
Block a user