Merge pull request #97 from ucb-bar/fix-deprecations-1

Fix deprecations 1
This commit is contained in:
Chick Markley
2021-02-10 11:15:24 -08:00
committed by GitHub
37 changed files with 2416 additions and 1859 deletions

View File

@@ -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)

View File

@@ -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")
}

View File

@@ -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 {

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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)
}
}
}

View File

@@ -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 =>

View 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)
}
}

View 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
}

View File

@@ -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
}
]
}
]

View File

@@ -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"
}
]
}
]

View File

@@ -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 = ""
}

View File

@@ -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>

View 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 = ""
}

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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
"""

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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 }

View File

@@ -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)
}
}

View File

@@ -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])

View File

@@ -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

View File

@@ -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)

View File

@@ -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 =>

View File

@@ -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)
}

View File

@@ -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)
})
}
}

View File

@@ -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)
})
}
}

View File

@@ -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!")
}
}
}

View File

@@ -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("]", "")
}

View File

@@ -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
}: _*)
}

View File

@@ -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))
}
}
}

View File

@@ -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)")

View File

@@ -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")