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( lazy val commonSettings = Seq(
organization := "edu.berkeley.cs", organization := "edu.berkeley.cs",
version := "0.1-SNAPSHOT", version := "0.4-SNAPSHOT",
scalaVersion := "2.12.10", scalaVersion := "2.12.10",
scalacOptions := Seq("-deprecation", "-feature", "-language:reflectiveCalls", "-Xsource:2.11"), scalacOptions := Seq("-deprecation", "-feature", "-language:reflectiveCalls", "-Xsource:2.11"),
libraryDependencies ++= Seq("chisel3","chisel-iotesters").map { libraryDependencies ++= Seq("chisel3","chisel-iotesters").map {
dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep))
}, },
libraryDependencies in Test ++= Seq( libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "2.2.5" % "test", "org.scalatest" %% "scalatest" % "3.2.2" % "test",
"org.scalacheck" %% "scalacheck" % "1.12.4" % "test"
), ),
resolvers ++= Seq( resolvers ++= Seq(
Resolver.sonatypeRepo("snapshots"), Resolver.sonatypeRepo("snapshots"),
@@ -31,9 +30,6 @@ lazy val macros = (project in file("macros"))
.dependsOn(mdf) .dependsOn(mdf)
.settings(commonSettings) .settings(commonSettings)
.settings( .settings(
libraryDependencies ++= Seq(
"edu.berkeley.cs" %% "firrtl-interpreter" % "1.4.+" % Test
),
mainClass := Some("barstools.macros.MacroCompiler") mainClass := Some("barstools.macros.MacroCompiler")
) )
.enablePlugins(sbtassembly.AssemblyPlugin) .enablePlugins(sbtassembly.AssemblyPlugin)

View File

@@ -6,8 +6,10 @@ import chisel3._
import chisel3.util.{HasBlackBoxResource} import chisel3.util.{HasBlackBoxResource}
import chisel3.experimental.{Analog, IntParam} import chisel3.experimental.{Analog, IntParam}
class AnalogConst(value: Int, width: Int = 1) extends BlackBox(Map("CONST" -> IntParam(value), "WIDTH" -> IntParam(width))) with HasBlackBoxResource{ class AnalogConst(value: Int, width: Int = 1)
val io = IO(new Bundle {val io = Analog(width.W) } ) 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") addResource("/barstools/iocell/vsrc/Analog.v")
} }

View File

@@ -4,7 +4,7 @@ package barstools.iocell.chisel
import chisel3._ import chisel3._
import chisel3.util.{Cat, HasBlackBoxResource} 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 // 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 // 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), // (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. // 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)
* The base IO bundle for an analog signal (typically something with no digital buffers inside) * pad: off-chip (external) connection
* pad: off-chip (external) connection * core: internal connection
* core: internal connection */
*/
class AnalogIOCellBundle extends Bundle { class AnalogIOCellBundle extends Bundle {
val pad = Analog(1.W) // Pad/bump signal (off-chip) val pad = Analog(1.W) // Pad/bump signal (off-chip)
val core = Analog(1.W) // core signal (on-chip) val core = Analog(1.W) // core signal (on-chip)
} }
/** /** The base IO bundle for a signal with runtime-controllable direction
* The base IO bundle for a signal with runtime-controllable direction * pad: off-chip (external) connection
* pad: off-chip (external) connection * i: input to chip logic (output from IO cell)
* i: input to chip logic (output from IO cell) * ie: enable signal for i
* ie: enable signal for i * o: output from chip logic (input to IO cell)
* o: output from chip logic (input to IO cell) * oe: enable signal for o
* oe: enable signal for o */
*/
class DigitalGPIOCellBundle extends Bundle { class DigitalGPIOCellBundle extends Bundle {
val pad = Analog(1.W) val pad = Analog(1.W)
val i = Output(Bool()) val i = Output(Bool())
@@ -39,24 +37,22 @@ class DigitalGPIOCellBundle extends Bundle {
val oe = Input(Bool()) val oe = Input(Bool())
} }
/** /** The base IO bundle for a digital output signal
* The base IO bundle for a digital output signal * pad: off-chip (external) connection
* pad: off-chip (external) connection * o: output from chip logic (input to IO cell)
* o: output from chip logic (input to IO cell) * oe: enable signal for o
* oe: enable signal for o */
*/
class DigitalOutIOCellBundle extends Bundle { class DigitalOutIOCellBundle extends Bundle {
val pad = Output(Bool()) val pad = Output(Bool())
val o = Input(Bool()) val o = Input(Bool())
val oe = Input(Bool()) val oe = Input(Bool())
} }
/** /** The base IO bundle for a digital input signal
* The base IO bundle for a digital input signal * pad: off-chip (external) connection
* pad: off-chip (external) connection * i: input to chip logic (output from IO cell)
* i: input to chip logic (output from IO cell) * ie: enable signal for i
* ie: enable signal for i */
*/
class DigitalInIOCellBundle extends Bundle { class DigitalInIOCellBundle extends Bundle {
val pad = Input(Bool()) val pad = Input(Bool())
val i = Output(Bool()) val i = Output(Bool())
@@ -102,11 +98,10 @@ class GenericDigitalOutIOCell extends GenericIOCell with DigitalOutIOCell {
val io = IO(new DigitalOutIOCellBundle) val io = IO(new DigitalOutIOCellBundle)
} }
trait IOCellTypeParams { trait IOCellTypeParams {
def analog(): AnalogIOCell def analog(): AnalogIOCell
def gpio(): DigitalGPIOCell def gpio(): DigitalGPIOCell
def input(): DigitalInIOCell def input(): DigitalInIOCell
def output(): DigitalOutIOCell def output(): DigitalOutIOCell
} }
@@ -118,47 +113,49 @@ case class GenericIOCellParams() extends IOCellTypeParams {
} }
object IOCell { object IOCell {
/**
* From within a RawModule or MultiIOModule context, generate new module IOs from a given /** 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. * 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 coreSignal The signal onto which to add IO cells
* @param name An optional name or name prefix to use for naming 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 * @param abstractResetAsAsync When set, will coerce abstract resets to
* AsyncReset, and otherwise to Bool (sync reset) * AsyncReset, and otherwise to Bool (sync reset)
* @return A tuple of (the generated IO data node, a Seq of all generated IO cell instances) * @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, def generateIOFromSignal[T <: Data](
typeParams: IOCellTypeParams = GenericIOCellParams(), coreSignal: T,
abstractResetAsAsync: Boolean = false): (T, Seq[IOCell]) = name: String,
{ typeParams: IOCellTypeParams = GenericIOCellParams(),
abstractResetAsAsync: Boolean = false
): (T, Seq[IOCell]) = {
val padSignal = IO(DataMirror.internal.chiselTypeClone[T](coreSignal)).suggestName(name) val padSignal = IO(DataMirror.internal.chiselTypeClone[T](coreSignal)).suggestName(name)
val resetFn = if (abstractResetAsAsync) toAsyncReset else toSyncReset val resetFn = if (abstractResetAsAsync) toAsyncReset else toSyncReset
val iocells = IOCell.generateFromSignal(coreSignal, padSignal, Some(s"iocell_$name"), typeParams, resetFn) val iocells = IOCell.generateFromSignal(coreSignal, padSignal, Some(s"iocell_$name"), typeParams, resetFn)
(padSignal, iocells) (padSignal, iocells)
} }
/** /** Connect two identical signals together by adding IO cells between them and return a Seq
* Connect two identical signals together by adding IO cells between them and return a Seq * containing all generated IO cells.
* containing all generated IO cells. * @param coreSignal The core-side (internal) signal onto which to connect/add 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 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
* @param name An optional name or name prefix to use for naming IO cells * @return A Seq of all generated IO cell instances
* @return A Seq of all generated IO cell instances */
*/ val toSyncReset: (Reset) => Bool = _.toBool
val toSyncReset: (Reset) => Bool = _.toBool
val toAsyncReset: (Reset) => AsyncReset = _.asAsyncReset val toAsyncReset: (Reset) => AsyncReset = _.asAsyncReset
def generateFromSignal[T <: Data, R <: Reset]( def generateFromSignal[T <: Data, R <: Reset](
coreSignal: T, coreSignal: T,
padSignal: T, padSignal: T,
name: Option[String] = None, name: Option[String] = None,
typeParams: IOCellTypeParams = GenericIOCellParams(), typeParams: IOCellTypeParams = GenericIOCellParams(),
concretizeResetFn : (Reset) => R = toSyncReset): Seq[IOCell] = concretizeResetFn: (Reset) => R = toSyncReset
{ ): Seq[IOCell] = {
def genCell[T <: Data]( def genCell[T <: Data](
castToBool: (T) => Bool, castToBool: (T) => Bool,
castFromBool: (Bool) => T)( castFromBool: (Bool) => T
coreSignal: T, )(coreSignal: T,
padSignal: T): Seq[IOCell] = { padSignal: T
): Seq[IOCell] = {
DataMirror.directionOf(coreSignal) match { DataMirror.directionOf(coreSignal) match {
case ActualDirection.Input => { case ActualDirection.Input => {
val iocell = typeParams.input() val iocell = typeParams.input()
@@ -188,7 +185,10 @@ object IOCell {
if (coreSignal.getWidth == 0) { if (coreSignal.getWidth == 0) {
Seq() Seq()
} else { } 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() val iocell = typeParams.analog()
name.foreach(n => iocell.suggestName(n)) name.foreach(n => iocell.suggestName(n))
iocell.io.core <> coreSignal iocell.io.core <> coreSignal
@@ -204,7 +204,7 @@ object IOCell {
// This dummy assignment will prevent invalid firrtl from being emitted // This dummy assignment will prevent invalid firrtl from being emitted
DataMirror.directionOf(coreSignal) match { DataMirror.directionOf(coreSignal) match {
case ActualDirection.Input => coreSignal := 0.U case ActualDirection.Input => coreSignal := 0.U
case _ => {} case _ => {}
} }
Seq() Seq()
} else { } 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 package barstools.macros
/** /** Trait which can calculate the cost of compiling a memory against a certain
* Trait which can calculate the cost of compiling a memory against a certain * library memory macro using a cost function.
* library memory macro using a cost function. */
*/
// TODO: eventually explore compiling a single target memory using multiple // TODO: eventually explore compiling a single target memory using multiple
// different kinds of target memory. // different kinds of target memory.
trait CostMetric extends Serializable { trait CostMetric extends Serializable {
/**
* Cost function that returns the cost of compiling a memory using a certain /** Cost function that returns the cost of compiling a memory using a certain
* macro. * macro.
* *
* @param mem Memory macro to compile (target memory) * @param mem Memory macro to compile (target memory)
* @param lib Library memory macro to use (library memory) * @param lib Library memory macro to use (library memory)
* @return The cost of this compile, defined by this cost metric, or None if * @return The cost of this compile, defined by this cost metric, or None if
* it cannot be compiled. * it cannot be compiled.
*/ */
def cost(mem: Macro, lib: Macro): Option[Double] 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] def commandLineParams(): Map[String, String]
// We also want this to show up for the class itself. // We also want this to show up for the class itself.
@@ -40,8 +38,9 @@ trait CostMetricCompanion {
// Some default cost functions. // Some default cost functions.
/** Palmer's old metric. /** Palmer's old metric.
* TODO: figure out what is the difference between this metric and the current * TODO: figure out what is the difference between this metric and the current
* default metric and either revive or delete this metric. */ * default metric and either revive or delete this metric.
*/
object OldMetric extends CostMetric with CostMetricCompanion { object OldMetric extends CostMetric with CostMetricCompanion {
override def cost(mem: Macro, lib: Macro): Option[Double] = { override def cost(mem: Macro, lib: Macro): Option[Double] = {
/* Palmer: A quick cost function (that must be kept in sync with /* 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 override def construct(m: Map[String, String]) = OldMetric
} }
/** /** An external cost function.
* An external cost function. * Calls the specified path with paths to the JSON MDF representation of the mem
* Calls the specified path with paths to the JSON MDF representation of the mem * and lib macros. The external executable should print a Double.
* and lib macros. The external executable should print a Double. * None will be returned if the external executable does not print a valid
* None will be returned if the external executable does not print a valid * Double.
* Double. */
*/
class ExternalMetric(path: String) extends CostMetric { class ExternalMetric(path: String) extends CostMetric {
import mdf.macrolib.Utils.writeMacroToPath import mdf.macrolib.Utils.writeMacroToPath
import java.io._ import java.io._
import scala.language.postfixOps // for !! postfix op import scala.language.postfixOps
import sys.process._ import sys.process._
override def cost(mem: Macro, lib: Macro): Option[Double] = { override def cost(mem: Macro, lib: Macro): Option[Double] = {
@@ -104,7 +103,7 @@ object ExternalMetric extends CostMetricCompanion {
override def construct(m: Map[String, String]) = { override def construct(m: Map[String, String]) = {
val pathOption = m.get("path") val pathOption = m.get("path")
pathOption match { pathOption match {
case Some(path:String) => new ExternalMetric(path) case Some(path: String) => new ExternalMetric(path)
case _ => throw new IllegalArgumentException("ExternalMetric missing option '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 // TODO: write tests for this function to make sure it selects the right things
object DefaultMetric extends CostMetric with CostMetricCompanion { object DefaultMetric extends CostMetric with CostMetricCompanion {
override def cost(mem: Macro, lib: Macro): Option[Double] = { override def cost(mem: Macro, lib: Macro): Option[Double] = {
val memMask = mem.src.ports map (_.maskGran) find (_.isDefined) map (_.get) val memMask = mem.src.ports.map(_.maskGran).find(_.isDefined).map(_.get)
val libMask = lib.src.ports map (_.maskGran) find (_.isDefined) map (_.get) val libMask = lib.src.ports.map(_.maskGran).find(_.isDefined).map(_.get)
val memWidth = (memMask, libMask) match { val memWidth = (memMask, libMask) match {
case (None, _) => mem.src.width 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)) => { 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) 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 else (mem.src.width / p) * m //Waste the extra maskbits
} }
} }
val depthCost = math.ceil(mem.src.depth.toDouble / lib.src.depth.toDouble) 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 val bitsCost = (lib.src.depth * lib.src.width).toDouble
// Fraction of wasted bits plus const per mem // Fraction of wasted bits plus const per mem
val requestedBits = (mem.src.depth * mem.src.width).toDouble 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 wastedConst = 0.05 // 0 means waste as few bits with no regard for instance count
val costPerInst = wastedConst*depthCost*widthCost val costPerInst = wastedConst * depthCost * widthCost
Some(1.0*bitsWasted/requestedBits+costPerInst) Some(1.0 * bitsWasted / requestedBits + costPerInst)
} }
override def commandLineParams = Map() override def commandLineParams = Map()
@@ -147,10 +149,11 @@ object MacroCompilerUtil {
// Adapted from https://stackoverflow.com/a/134918 // Adapted from https://stackoverflow.com/a/134918
/** Serialize an arbitrary object to String. /** 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 = { def objToString(o: Serializable): String = {
val baos: ByteArrayOutputStream = new ByteArrayOutputStream val baos: ByteArrayOutputStream = new ByteArrayOutputStream
val oos: ObjectOutputStream = new ObjectOutputStream(baos) val oos: ObjectOutputStream = new ObjectOutputStream(baos)
oos.writeObject(o) oos.writeObject(o)
oos.close() oos.close()
return Base64.getEncoder.encodeToString(baos.toByteArray) return Base64.getEncoder.encodeToString(baos.toByteArray)
@@ -167,6 +170,7 @@ object MacroCompilerUtil {
} }
object CostMetric { object CostMetric {
/** Define some default metric. */ /** Define some default metric. */
val default: CostMetric = DefaultMetric val default: CostMetric = DefaultMetric
@@ -177,13 +181,12 @@ object CostMetric {
registerCostMetric(ExternalMetric) registerCostMetric(ExternalMetric)
registerCostMetric(DefaultMetric) registerCostMetric(DefaultMetric)
/** /** Register a cost metric.
* Register a cost metric. * @param createFuncHelper Companion object to fetch the name and construct
* @param createFuncHelper Companion object to fetch the name and construct * the metric.
* the metric. */
*/
def registerCostMetric(createFuncHelper: CostMetricCompanion): Unit = { def registerCostMetric(createFuncHelper: CostMetricCompanion): Unit = {
costMetricCreators.update(createFuncHelper.name, createFuncHelper) costMetricCreators.update(createFuncHelper.name(), createFuncHelper)
} }
/** Select a cost metric from string. */ /** Select a cost metric from string. */
@@ -193,7 +196,7 @@ object CostMetric {
} else if (!costMetricCreators.contains(m)) { } else if (!costMetricCreators.contains(m)) {
throw new IllegalArgumentException("Invalid cost metric " + m) throw new IllegalArgumentException("Invalid cost metric " + m)
} else { } else {
costMetricCreators.get(m).get.construct(params) costMetricCreators(m).construct(params)
} }
} }
} }

View File

@@ -1,25 +1,24 @@
// See LICENSE for license details. // See LICENSE for license details.
/** /** Terminology note:
* Terminology note: * mem - target memory to compile, in design (e.g. Mem() in rocket)
* mem - target memory to compile, in design (e.g. Mem() in rocket) * lib - technology SRAM(s) to use to compile mem
* lib - technology SRAM(s) to use to compile mem */
*/
package barstools.macros package barstools.macros
import firrtl._ import barstools.macros.Utils._
import firrtl.ir._
import firrtl.PrimOps
import firrtl.Utils._ import firrtl.Utils._
import firrtl.annotations._ import firrtl.annotations._
import firrtl.transforms.{NoDCEAnnotation} import firrtl.ir._
import firrtl.CompilerUtils.getLoweringTransforms import firrtl.stage.{FirrtlSourceAnnotation, FirrtlStage, Forms, OutputFileAnnotation, RunFirrtlTransformAnnotation}
import mdf.macrolib.{PolarizedPort, PortPolarity, SRAMMacro, SRAMGroup, SRAMCompiler} import firrtl.transforms.NoDCEAnnotation
import scala.collection.mutable.{ArrayBuffer, HashMap} import firrtl.{PrimOps, _}
import mdf.macrolib._
import java.io.{File, FileWriter} import java.io.{File, FileWriter}
import scala.io.{Source} import scala.collection.mutable.{ArrayBuffer, HashMap}
import Utils._ import scala.io.Source
case class MacroCompilerException(msg: String) extends Exception(msg) 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] 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
* The MacroCompilerAnnotation to trigger the macro compiler. * compilation. It simply holds all the settings for the memory compiler. The
* Note that this annotation does NOT actually target any modules for * actual selection of which memories to compile is set in the Params.
* 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]].
* To use, simply annotate the entire circuit itself with this annotation and */
* include [[MacroCompilerTransform]].
*
*/
object MacroCompilerAnnotation { object MacroCompilerAnnotation {
/** Macro compiler mode. */ /** Macro compiler mode. */
sealed trait CompilerMode sealed trait CompilerMode
/** Strict mode - must compile all memories or error out. */ /** Strict mode - must compile all memories or error out. */
case object Strict extends CompilerMode case object Strict extends CompilerMode
/** Synflops mode - compile all memories with synflops (do not map to lib at all). */ /** Synflops mode - compile all memories with synflops (do not map to lib at all). */
case object Synflops extends CompilerMode case object Synflops extends CompilerMode
/** CompileAndSynflops mode - compile all memories and create mock versions of the target libs with synflops. */ /** CompileAndSynflops mode - compile all memories and create mock versions of the target libs with synflops. */
case object CompileAndSynflops extends CompilerMode 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 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 case object CompileAvailable extends CompilerMode
/** /** The default mode for the macro compiler.
* The default mode for the macro compiler. * TODO: Maybe set the default to FallbackSynflops (typical for
* TODO: Maybe set the default to FallbackSynflops (typical for * vlsi_mem_gen-like scripts) once it's implemented?
* vlsi_mem_gen-like scripts) once it's implemented? */
*/
val Default = CompileAvailable val Default = CompileAvailable
// Options as list of (CompilerMode, command-line name, description) // Options as list of (CompilerMode, command-line name, description)
val options: Seq[(CompilerMode, String, String)] = Seq( val options: Seq[(CompilerMode, String, String)] = Seq(
(Default, "default", "Select the default option from below."), (Default, "default", "Select the default option from below."),
(Strict, "strict", "Compile all memories to library or return an error."), (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."), Synflops,
(FallbackSynflops, "fallbacksynflops", "Compile all memories to library when possible and fall back to synthesizable flop-based memories when library synth is not possible."), "synflops",
(CompileAvailable, "compileavailable", "Compile all memories to library when possible and do nothing in case of errors. (default)") "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. */ /** 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 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 mem Path to memory lib
* @param memFormat Type of memory lib (Some("conf"), Some("mdf"), or None (defaults to mdf)) * @param memFormat Type of memory lib (Some("conf"), Some("mdf"), or None (defaults to mdf))
@@ -90,26 +108,34 @@ object MacroCompilerAnnotation {
* @param forceCompile Set of memories to force compiling to lib regardless of the mode * @param forceCompile Set of memories to force compiling to lib regardless of the mode
* @param forceSynflops Set of memories to force compiling as flops regardless of the mode * @param forceSynflops Set of memories to force compiling as flops regardless of the mode
*/ */
case class Params(mem: String, memFormat: Option[String], lib: Option[String], hammerIR: Option[String], case class Params(
costMetric: CostMetric, mode: CompilerMode, useCompiler: Boolean, mem: String,
forceCompile: Set[String], forceSynflops: Set[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.
* Create a MacroCompilerAnnotation. * @param c Top-level circuit name (see class description)
* @param c Top-level circuit name (see class description) * @param p Parameters (see above).
* @param p Parameters (see above). */
*/
def apply(c: String, p: Params): MacroCompilerAnnotation = def apply(c: String, p: Params): MacroCompilerAnnotation =
MacroCompilerAnnotation(MacroCompilerUtil.objToString(p)) MacroCompilerAnnotation(MacroCompilerUtil.objToString(p))
} }
class MacroCompilerPass(mems: Option[Seq[Macro]], class MacroCompilerPass(
libs: Option[Seq[Macro]], mems: Option[Seq[Macro]],
compilers: Option[SRAMCompiler], libs: Option[Seq[Macro]],
hammerIR: Option[String], compilers: Option[SRAMCompiler],
costMetric: CostMetric = CostMetric.default, hammerIR: Option[String],
mode: MacroCompilerAnnotation.CompilerMode = MacroCompilerAnnotation.Default) extends firrtl.passes.Pass { costMetric: CostMetric = CostMetric.default,
mode: MacroCompilerAnnotation.CompilerMode = MacroCompilerAnnotation.Default)
extends firrtl.passes.Pass {
// Helper function to check the legality of bitPairs. // Helper function to check the legality of bitPairs.
// e.g. ((0,21), (22,43)) is legal // e.g. ((0,21), (22,43)) is legal
// ((0,21), (22,21)) is illegal and will throw an assert // ((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. * This is a list of submemories by width.
* The tuples are (lsb, msb) inclusive. * 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. * 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. * @return Bit pairs or empty list if there was an error.
*/ */
private def calculateBitPairs(mem: Macro, lib: Macro): Seq[(BigInt, BigInt)] = { 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)]() val bitPairs = ArrayBuffer[(BigInt, BigInt)]()
var currentLSB: BigInt = 0 var currentLSB: BigInt = 0
@@ -204,7 +229,9 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
splitMemory(memMask.get) splitMemory(memMask.get)
} else { } else {
// e.g. mem mask = 13, lib width = 8 // 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() return Seq()
} }
} }
@@ -267,9 +294,11 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
} }
def compile(mem: Macro, lib: Macro): Option[(Module, Macro)] = { def compile(mem: Macro, lib: Macro): Option[(Module, Macro)] = {
assert(mem.sortedPorts.lengthCompare(lib.sortedPorts.length) == 0, assert(
"mem and lib should have an equal number of ports") mem.sortedPorts.lengthCompare(lib.sortedPorts.length) == 0,
val pairedPorts = mem.sortedPorts zip lib.sortedPorts "mem and lib should have an equal number of ports"
)
val pairedPorts = mem.sortedPorts.zip(lib.sortedPorts)
// Width mapping. See calculateBitPairs. // Width mapping. See calculateBitPairs.
val bitPairs: Seq[(BigInt, BigInt)] = calculateBitPairs(mem, lib) 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 /* Palmer: If we've got a parallel memory then we've got to take the
* address bits into account. */ * address bits into account. */
if (mem.src.depth > lib.src.depth) { 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 high = MacroCompilerMath.ceilLog2(mem.src.depth)
val low = MacroCompilerMath.ceilLog2(lib.src.depth) val low = MacroCompilerMath.ceilLog2(lib.src.depth)
val ref = WRef(port.address.name) val ref = WRef(port.address.name)
val nodeName = s"${ref.name}_sel" val nodeName = s"${ref.name}_sel"
val tpe = UIntType(IntWidth(high-low)) val tpe = UIntType(IntWidth(high - low))
selects(ref.name) = WRef(nodeName, tpe) 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 // Donggyu: output selection should be piped
if (port.output.isDefined) { if (port.output.isDefined) {
val regName = s"${ref.name}_sel_reg" 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)) and(WRef(ce.name, BoolType), WRef(re.name, BoolType))
case (Some(ce), None) => WRef(ce.name, BoolType) case (Some(ce), None) => WRef(ce.name, BoolType)
case (None, Some(re)) => WRef(re.name, BoolType) case (None, Some(re)) => WRef(re.name, BoolType)
case (None, None) => one case (None, None) => one
} }
selectRegs(ref.name) = WRef(regName, tpe) selectRegs(ref.name) = WRef(regName, tpe)
stmts += DefRegister(NoInfo, regName, tpe, WRef(port.clock.get.name), zero, WRef(regName)) 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. // Create the instance.
stmts += WDefInstance(NoInfo, name, lib.src.name, lib.tpe) stmts += WDefInstance(NoInfo, name, lib.src.name, lib.tpe)
// Connect extra ports of the lib. // 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) Connect(NoInfo, WSubField(WRef(name), portName), portValue)
} }
} }
for ((memPort, libPort) <- pairedPorts) { 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 None => one
case Some(addr) => case Some(addr) =>
val index = UIntLiteral(i, IntWidth(bitWidth(addr.tpe))) val index = UIntLiteral(i, IntWidth(bitWidth(addr.tpe)))
DoPrim(PrimOps.Eq, Seq(addr, index), Nil, index.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 None => one
case Some(reg) => case Some(reg) =>
val index = UIntLiteral(i, IntWidth(bitWidth(reg.tpe))) val index = UIntLiteral(i, IntWidth(bitWidth(reg.tpe)))
@@ -342,29 +371,22 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
for (((low, high), j) <- bitPairs.zipWithIndex) { for (((low, high), j) <- bitPairs.zipWithIndex) {
val inst = WRef(s"mem_${i}_${j}", lib.tpe) val inst = WRef(s"mem_${i}_${j}", lib.tpe)
def connectPorts2(mem: Expression, def connectPorts2(mem: Expression, lib: String, polarity: Option[PortPolarity]): Statement =
lib: String,
polarity: Option[PortPolarity]): Statement =
Connect(NoInfo, WSubField(inst, lib), portToExpression(mem, polarity)) Connect(NoInfo, WSubField(inst, lib), portToExpression(mem, polarity))
def connectPorts(mem: Expression, def connectPorts(mem: Expression, lib: String, polarity: PortPolarity): Statement =
lib: String,
polarity: PortPolarity): Statement =
connectPorts2(mem, lib, Some(polarity)) connectPorts2(mem, lib, Some(polarity))
// Clock port mapping // Clock port mapping
/* Palmer: FIXME: I don't handle memories with read/write clocks yet. */ /* Palmer: FIXME: I don't handle memories with read/write clocks yet. */
/* Colin not all libPorts have clocks but all memPorts do*/ /* Colin not all libPorts have clocks but all memPorts do*/
libPort.src.clock.foreach { cPort => libPort.src.clock.foreach { cPort =>
stmts += connectPorts(WRef(memPort.src.clock.get.name), stmts += connectPorts(WRef(memPort.src.clock.get.name), cPort.name, cPort.polarity)
cPort.name, }
cPort.polarity) }
// Adress port mapping // Adress port mapping
/* Palmer: The address port to a memory is just the low-order bits of /* Palmer: The address port to a memory is just the low-order bits of
* the top address. */ * the top address. */
stmts += connectPorts(WRef(memPort.src.address.name), stmts += connectPorts(WRef(memPort.src.address.name), libPort.src.address.name, libPort.src.address.polarity)
libPort.src.address.name,
libPort.src.address.polarity)
// Output port mapping // Output port mapping
(memPort.src.output, libPort.src.output) match { (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 * done after generating all the memories. This saves up the
* output statements for later. */ * output statements for later. */
val name = s"${mem}_${i}_${j}" // This name is the output from the instance (mem vs ${mem}). val name = s"${mem}_${i}_${j}" // This name is the output from the instance (mem vs ${mem}).
val exp = portToExpression(bits(WSubField(inst, lib), high-low, 0), Some(lib_polarity)) val exp = portToExpression(bits(WSubField(inst, lib), high - low, 0), Some(lib_polarity))
stmts += DefNode(NoInfo, name, exp) stmts += DefNode(NoInfo, name, exp)
cats += WRef(name) cats += WRef(name)
case (None, Some(lib)) => case (None, Some(lib)) =>
/* Palmer: If the inner memory has an output port but the outer /* Palmer: If the inner memory has an output port but the outer
* one doesn't then it's safe to just leave the outer * one doesn't then it's safe to just leave the outer
* port floating. */ * port floating. */
case (None, None) => case (None, None) =>
/* Palmer: If there's no output ports at all (ie, read-only /* Palmer: If there's no output ports at all (ie, read-only
* port on the memory) then just don't worry about it, * port on the memory) then just don't worry about it,
* there's nothing to do. */ * there's nothing to do. */
case (Some(PolarizedPort(mem, _)), None) => case (Some(PolarizedPort(mem, _)), None) =>
System.err println "WARNING: Unable to match output ports on memory" System.err.println("WARNING: Unable to match output ports on memory")
System.err println s" outer output port: ${mem}" System.err.println(s" outer output port: ${mem}")
return None 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, /* Palmer: The input port to a memory just needs to happen in parallel,
* this does a part select to narrow the memory down. */ * this does a part select to narrow the memory down. */
stmts += connectPorts(bits(WRef(mem), high, low), lib, lib_polarity) 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 /* Palmer: If the inner memory has an input port but the other
* one doesn't then it's safe to just leave the inner * one doesn't then it's safe to just leave the inner
* port floating. This should be handled by the * 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 //Firrtl cares about dangling inputs now tie it off
stmts += IsInvalid(NoInfo, WSubField(inst, lib.name)) stmts += IsInvalid(NoInfo, WSubField(inst, lib.name))
case (None, None) => case (None, None) =>
/* Palmer: If there's no input ports at all (ie, read-only /* Palmer: If there's no input ports at all (ie, read-only
* port on the memory) then just don't worry about it, * port on the memory) then just don't worry about it,
* there's nothing to do. */ * there's nothing to do. */
case (Some(PolarizedPort(mem, _)), None) => case (Some(PolarizedPort(mem, _)), None) =>
System.err println "WARNING: Unable to match input ports on memory" System.err.println("WARNING: Unable to match input ports on memory")
System.err println s" outer input port: ${mem}" System.err.println(s" outer input port: ${mem}")
return None 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. // 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. // The other case is if we're using a larger lib than mem.
val usingLessThanLibMaskGran = (memPort.src.maskGran.get < libPort.src.effectiveMaskGran) val usingLessThanLibMaskGran = (memPort.src.maskGran.get < libPort.src.effectiveMaskGran)
val effectiveLibWidth = if (usingLessThanLibMaskGran) val effectiveLibWidth =
memPort.src.maskGran.get if (usingLessThanLibMaskGran)
else memPort.src.maskGran.get
libPort.src.width.get else
libPort.src.width.get
cat(((0 until libPort.src.width.get by libPort.src.effectiveMaskGran) map (i => { cat(
if (usingLessThanLibMaskGran && i >= effectiveLibWidth) { (
// If the memMaskGran is smaller than the lib's gran, then (0 until libPort.src.width.get by libPort.src.effectiveMaskGran)
// zero out the upper bits. .map(i => {
zero if (usingLessThanLibMaskGran && i >= effectiveLibWidth) {
} else { // If the memMaskGran is smaller than the lib's gran, then
if ((low + i) >= memPort.src.width.get) { // zero out the upper bits.
// If our bit is larger than the whole width of the mem, just zero out the upper bits. zero
zero } else {
} else { if ((low + i) >= memPort.src.width.get) {
// Pick the appropriate bit from the mem mask. // If our bit is larger than the whole width of the mem, just zero out the upper bits.
bits(WRef(mem), (low + i) / memPort.src.effectiveMaskGran) zero
} } else {
} // Pick the appropriate bit from the mem mask.
})).reverse) bits(WRef(mem), (low + i) / memPort.src.effectiveMaskGran)
}
}
})
)
.reverse
)
} }
case None => case None =>
/* If there is a lib mask port but no mem mask port, just turn on /* 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 // Chip enable port mapping
val memChipEnable = memPort.src.chipEnable match { val memChipEnable = memPort.src.chipEnable match {
case Some(PolarizedPort(mem, _)) => WRef(mem) case Some(PolarizedPort(mem, _)) => WRef(mem)
case None => one case None => one
} }
// Read enable port mapping // Read enable port mapping
@@ -502,7 +531,11 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
* implement the outer memory's collection of ports using what * implement the outer memory's collection of ports using what
* the inner memory has availiable. */ * the inner memory has availiable. */
((libPort.src.maskPort, libPort.src.writeEnable, libPort.src.chipEnable): @unchecked) match { ((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. */ /* Palmer: This is the simple option: every port exists. */
stmts += connectPorts(memMask, mask, mask_polarity) stmts += connectPorts(memMask, mask, mask_polarity)
stmts += connectPorts(andAddrMatch(memWriteEnable), we, we_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) => 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. */ /* Palmer: If we don't have a chip enable but do have mask ports. */
stmts += connectPorts(memMask, mask, mask_polarity) stmts += connectPorts(memMask, mask, mask_polarity)
stmts += connectPorts(andAddrMatch(and(memWriteEnable, memChipEnable)), stmts += connectPorts(andAddrMatch(and(memWriteEnable, memChipEnable)), we, we_polarity)
we, we_polarity)
case (None, Some(PolarizedPort(we, we_polarity)), chipEnable) => case (None, Some(PolarizedPort(we, we_polarity)), chipEnable) =>
if (bitWidth(memMask.tpe) == 1) { if (bitWidth(memMask.tpe) == 1) {
/* Palmer: If we're expected to provide mask ports without a /* 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. */ * write enable port instead of the mask port. */
chipEnable match { chipEnable match {
case Some(PolarizedPort(en, en_polarity)) => { case Some(PolarizedPort(en, en_polarity)) => {
stmts += connectPorts(andAddrMatch(and(memWriteEnable, memMask)), stmts += connectPorts(andAddrMatch(and(memWriteEnable, memMask)), we, we_polarity)
we, we_polarity)
stmts += connectPorts(andAddrMatch(memChipEnable), en, en_polarity) stmts += connectPorts(andAddrMatch(memChipEnable), en, en_polarity)
} }
case _ => { case _ => {
stmts += connectPorts(andAddrMatch(and(and(memWriteEnable, memChipEnable), memMask)), stmts += connectPorts(
we, we_polarity) andAddrMatch(and(and(memWriteEnable, memChipEnable), memMask)),
we,
we_polarity
)
} }
} }
} else { } else {
@@ -533,8 +567,8 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
return None return None
} }
case (None, None, None) => case (None, None, None) =>
// No write ports to match up (this may be a read-only port). // No write ports to match up (this may be a read-only port).
// This isn't necessarily an error condition. // This isn't necessarily an error condition.
} }
} }
// Cat macro outputs for selection // Cat macro outputs for selection
@@ -542,7 +576,7 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
case Some(PolarizedPort(mem, _)) if cats.nonEmpty => case Some(PolarizedPort(mem, _)) if cats.nonEmpty =>
val name = s"${mem}_${i}" val name = s"${mem}_${i}"
stmts += DefNode(NoInfo, name, cat(cats.toSeq.reverse)) stmts += DefNode(NoInfo, name, cat(cats.toSeq.reverse))
(outputs getOrElseUpdate (mem, ArrayBuffer[(Expression, Expression)]())) += (outputs.getOrElseUpdate(mem, ArrayBuffer[(Expression, Expression)]())) +=
(addrMatchReg -> WRef(name)) (addrMatchReg -> WRef(name))
case _ => case _ =>
} }
@@ -550,15 +584,17 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
} }
// Connect mem outputs // Connect mem outputs
val zeroOutputValue: Expression = UIntLiteral(0, IntWidth(mem.src.width)) val zeroOutputValue: Expression = UIntLiteral(0, IntWidth(mem.src.width))
mem.src.ports foreach { port => mem.src.ports.foreach { port =>
port.output match { port.output match {
case Some(PolarizedPort(mem, _)) => outputs get mem match { case Some(PolarizedPort(mem, _)) =>
case Some(select) => outputs.get(mem) match {
val output = (select foldRight (zeroOutputValue)) { case Some(select) =>
case ((cond, tval), fval) => Mux(cond, tval, fval, fval.tpe) } val output = (select.foldRight(zeroOutputValue)) { case ((cond, tval), fval) =>
stmts += Connect(NoInfo, WRef(mem), output) Mux(cond, tval, fval, fval.tpe)
case None => }
} stmts += Connect(NoInfo, WRef(mem), output)
case None =>
}
case None => case None =>
} }
} }
@@ -573,123 +609,138 @@ class MacroCompilerPass(mems: Option[Seq[Macro]],
// Try to compile each of the memories in mems. // Try to compile each of the memories in mems.
// The 'state' is c.modules, which is a list of all the firrtl modules // The 'state' is c.modules, which is a list of all the firrtl modules
// in the 'circuit'. // in the 'circuit'.
(mems foldLeft c.modules){ (modules, mem) => (mems.foldLeft(c.modules)) { (modules, mem) =>
val sram = mem.src
val sram = mem.src def groupMatchesMask(group: SRAMGroup, mem: SRAMMacro): Boolean = {
def groupMatchesMask(group: SRAMGroup, mem:SRAMMacro): Boolean = { val memMask = mem.ports.map(_.maskGran).find(_.isDefined).map(_.get)
val memMask = mem.ports map (_.maskGran) find (_.isDefined) map (_.get) val libMask = group.ports.map(_.maskGran).find(_.isDefined).map(_.get)
val libMask = group.ports map (_.maskGran) find (_.isDefined) map (_.get) (memMask, libMask) match {
(memMask, libMask) match { case (None, _) => true
case (None, _) => true case (Some(_), None) => false
case (Some(_), None) => false case (Some(m), Some(l)) => l <= m //Ignore memories that don't have nice mask
case (Some(m), Some(l)) => l <= m //Ignore memories that don't have nice mask }
} }
} // Add compiler memories that might map well to libs
// Add compiler memories that might map well to libs val compLibs = compilers match {
val compLibs = compilers match { case Some(SRAMCompiler(_, groups)) => {
case Some(SRAMCompiler(_, groups)) => { groups
groups.filter(g => g.family == sram.family && groupMatchesMask(g, sram)).map( g => { .filter(g => g.family == sram.family && groupMatchesMask(g, sram))
for(w <- g.width; d <- g.depth if((sram.width % w == 0) && (sram.depth % d == 0))) .map(g => {
yield Seq(new Macro(buildSRAMMacro(g, d, w, g.vt.head))) 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 // Try to compile mem against each lib in libs, keeping track of the
// best compiled version, external lib used, and cost. // best compiled version, external lib used, and cost.
val (best, cost) = (fullLibs foldLeft (None: Option[(Module, Macro)], Double.MaxValue)){ val (best, cost) = (fullLibs.foldLeft(None: Option[(Module, Macro)], Double.MaxValue)) {
case ((best, cost), lib) if mem.src.ports.size != lib.src.ports.size => case ((best, cost), lib) if mem.src.ports.size != lib.src.ports.size =>
/* Palmer: FIXME: This just assumes the Chisel and vendor ports are in the same /* Palmer: FIXME: This just assumes the Chisel and vendor ports are in the same
* order, but I'm starting with what actually gets generated. */ * order, but I'm starting with what actually gets generated. */
System.err println s"INFO: unable to compile ${mem.src.name} using ${lib.src.name} port count must match" System.err.println(s"INFO: unable to compile ${mem.src.name} using ${lib.src.name} port count must match")
(best, cost) (best, cost)
case ((best, cost), lib) => case ((best, cost), lib) =>
// Run the cost function to evaluate this potential compile. // Run the cost function to evaluate this potential compile.
costMetric.cost(mem, lib) match { costMetric.cost(mem, lib) match {
case Some(newCost) => { case Some(newCost) => {
//System.err.println(s"Cost of ${lib.src.name} for ${mem.src.name}: ${newCost}") //System.err.println(s"Cost of ${lib.src.name} for ${mem.src.name}: ${newCost}")
// Try compiling // Try compiling
compile(mem, lib) match { compile(mem, lib) match {
// If it was successful and the new cost is lower // If it was successful and the new cost is lower
case Some(p) if (newCost < cost) => (Some(p), newCost) case Some(p) if (newCost < cost) => (Some(p), newCost)
case _ => (best, cost) 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 { // If we were able to compile anything, then replace the original module
case Some(f) => { // in the modules list with a compiled version, as well as the extmodule
val hammerIRWriter = new FileWriter(new File(f), !firstLib) // stub for the lib.
if(firstLib) hammerIRWriter.write("[\n") best match {
hammerIRWriter.write(bb.src.toJSON().toString()) case None => {
hammerIRWriter.write("\n,\n") if (mode == MacroCompilerAnnotation.Strict)
hammerIRWriter.close() throw new MacroCompilerException(
firstLib = false s"Target memory ${mem.src.name} could not be compiled and strict mode is activated - aborting."
} )
case None => 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 case _ => c.modules
} }
c.copy(modules = modules) c.copy(modules = modules)
} }
} }
class MacroCompilerTransform extends Transform { class MacroCompilerTransform extends Transform with DependencyAPIMigration {
def inputForm = MidForm override def prerequisites = Forms.LowForm
def outputForm = MidForm 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 { def execute(state: CircuitState) = state.annotations.collect { case a: MacroCompilerAnnotation => a } match {
case Seq(anno: MacroCompilerAnnotation) => 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) { if (mode == MacroCompilerAnnotation.FallbackSynflops) {
throw new UnsupportedOperationException("Not implemented yet") throw new UnsupportedOperationException("Not implemented yet")
} }
// Check that we don't have any modules both forced to compile and synflops. // 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 // Read, eliminate None, get only SRAM, make firrtl macro
val mems: Option[Seq[Macro]] = (memFileFormat match { val mems: Option[Seq[Macro]] = (memFileFormat match {
case Some("conf") => Utils.readConfFromPath(Some(memFile)) case Some("conf") => Utils.readConfFromPath(Some(memFile))
case _ => mdf.macrolib.Utils.readMDFFromPath(Some(memFile)) case _ => mdf.macrolib.Utils.readMDFFromPath(Some(memFile))
}) match { }) match {
case Some(x:Seq[mdf.macrolib.Macro]) => case Some(x: Seq[mdf.macrolib.Macro]) =>
Some(Utils.filterForSRAM(Some(x)) getOrElse(List()) map {new Macro(_)}) Some(Utils.filterForSRAM(Some(x)).getOrElse(List()).map { new Macro(_) })
case _ => None case _ => None
} }
val libs: Option[Seq[Macro]] = mdf.macrolib.Utils.readMDFFromPath(libFile) match { val libs: Option[Seq[Macro]] = mdf.macrolib.Utils.readMDFFromPath(libFile) match {
case Some(x:Seq[mdf.macrolib.Macro]) => case Some(x: Seq[mdf.macrolib.Macro]) =>
Some(Utils.filterForSRAM(Some(x)) getOrElse(List()) map {new Macro(_)}) Some(Utils.filterForSRAM(Some(x)).getOrElse(List()).map { new Macro(_) })
case _ => None case _ => None
} }
val compilers: Option[mdf.macrolib.SRAMCompiler] = mdf.macrolib.Utils.readMDFFromPath(libFile) match { val compilers: Option[mdf.macrolib.SRAMCompiler] = mdf.macrolib.Utils.readMDFFromPath(libFile) match {
case Some(x:Seq[mdf.macrolib.Macro]) => case Some(x: Seq[mdf.macrolib.Macro]) =>
if(useCompiler){ if (useCompiler) {
findSRAMCompiler(Some(x)) findSRAMCompiler(Some(x))
} } else None
else None
case _ => None case _ => None
} }
// Helper function to turn a set of mem names into a Seq[Macro]. // Helper function to turn a set of mem names into a Seq[Macro].
def setToSeqMacro(names: Set[String]): 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) 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( val transforms = Seq(
new MacroCompilerPass(memCompile, libs, compilers, hammerIR, costMetric, mode), new MacroCompilerPass(memCompile, libs, compilers, hammerIR, costMetric, mode),
new SynFlopsPass(true, memSynflops ++ (if (mode == MacroCompilerAnnotation.CompileAndSynflops) { new SynFlopsPass(
libs.get true,
} else { memSynflops ++ (if (mode == MacroCompilerAnnotation.CompileAndSynflops) {
Seq.empty libs.get
}))) } else {
(transforms foldLeft state) ((s, xform) => xform runTransform s).copy(form = outputForm) Seq.empty
})
)
)
(transforms.foldLeft(state))((s, xform) => xform.runTransform(s))
case _ => state case _ => state
} }
} }
// FIXME: Use firrtl.LowerFirrtlOptimizations class MacroCompilerOptimizations extends SeqTransform with DependencyAPIMigration {
class MacroCompilerOptimizations extends SeqTransform { override def prerequisites = Forms.LowForm
def inputForm: CircuitForm = LowForm override def optionalPrerequisites = Forms.LowFormOptimized
override def optionalPrerequisiteOf = Forms.LowEmitters
def outputForm: CircuitForm = LowForm override def invalidates(a: Transform) = false
def transforms: Seq[Transform] = Seq( def transforms: Seq[Transform] = Seq(
passes.RemoveValidIf, passes.RemoveValidIf,
@@ -730,16 +785,8 @@ class MacroCompilerOptimizations extends SeqTransform {
new firrtl.transforms.ConstantPropagation, new firrtl.transforms.ConstantPropagation,
passes.Legalize, passes.Legalize,
passes.SplitExpressions, passes.SplitExpressions,
passes.CommonSubexpressionElimination) 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)
} }
object MacroCompiler extends App { object MacroCompiler extends App {
@@ -757,8 +804,9 @@ object MacroCompiler extends App {
type MacroParamMap = Map[MacroParam, String] type MacroParamMap = Map[MacroParam, String]
type CostParamMap = Map[String, String] type CostParamMap = Map[String, String]
type ForcedMemories = (Set[String], Set[String]) type ForcedMemories = (Set[String], Set[String])
val modeOptions: Seq[String] = MacroCompilerAnnotation.options val modeOptions: Seq[String] = MacroCompilerAnnotation.options.map { case (_, cmd, description) =>
.map { case (_, cmd, description) => s" $cmd: $description" } s" $cmd: $description"
}
val usage: String = (Seq( val usage: String = (Seq(
"Options:", "Options:",
" -n, --macro-conf: The set of macros to compile in firrtl-generated conf format (exclusive with -m)", " -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-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", " --force-synflops [mem]: Force the given memory to be compiled via synflops regardless of the mode",
" --mode:" " --mode:"
) ++ modeOptions) mkString "\n" ) ++ modeOptions).mkString("\n")
def parseArgs(map: MacroParamMap, costMap: CostParamMap, forcedMemories: ForcedMemories, def parseArgs(
args: List[String]): (MacroParamMap, CostParamMap, ForcedMemories) = map: MacroParamMap,
costMap: CostParamMap,
forcedMemories: ForcedMemories,
args: List[String]
): (MacroParamMap, CostParamMap, ForcedMemories) =
args match { args match {
case Nil => (map, costMap, forcedMemories) case Nil => (map, costMap, forcedMemories)
case ("-n" | "--macro-conf") :: value :: tail => 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 => 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 => case ("-l" | "--library") :: value :: tail =>
parseArgs(map + (Library -> value), costMap, forcedMemories, tail) parseArgs(map + (Library -> value), costMap, forcedMemories, tail)
case ("-u" | "--use-compiler") :: tail => case ("-u" | "--use-compiler") :: tail =>
@@ -810,11 +862,17 @@ object MacroCompiler extends App {
} }
def run(args: List[String]) { 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 { try {
val macros = params.get(MacrosFormat) match { val macros = params.get(MacrosFormat) match {
case Some("conf") => Utils.filterForSRAM(Utils.readConfFromPath(params.get(Macros))).get map (x => (new Macro(x)).blackbox) case Some("conf") =>
case _ => Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(params.get(Macros))).get map (x => (new Macro(x)).blackbox) 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) { if (macros.nonEmpty) {
@@ -822,35 +880,39 @@ object MacroCompiler extends App {
// determined as the firrtl "top-level module". // determined as the firrtl "top-level module".
val circuit = Circuit(NoInfo, macros, macros.last.name) val circuit = Circuit(NoInfo, macros, macros.last.name)
val annotations = AnnotationSeq( val annotations = AnnotationSeq(
Seq(MacroCompilerAnnotation( Seq(
circuit.main, MacroCompilerAnnotation(
MacroCompilerAnnotation.Params( circuit.main,
params.get(Macros).get, params.get(MacrosFormat), params.get(Library), MacroCompilerAnnotation.Params(
params.get(HammerIR), params(Macros),
CostMetric.getCostMetric(params.getOrElse(CostFunc, "default"), costParams), params.get(MacrosFormat),
MacroCompilerAnnotation.stringToCompilerMode(params.getOrElse(Mode, "default")), params.get(Library),
params.contains(UseCompiler), params.get(HammerIR),
forceCompile = forcedMemories._1, forceSynflops = forcedMemories._2 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 // 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) 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 // 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 { params.get(HammerIR) match {
case Some(hammerIRFile: String) => { case Some(hammerIRFile: String) => {
@@ -865,7 +927,7 @@ object MacroCompiler extends App {
} }
} else { } else {
// Warn user // Warn user
System.err println "WARNING: Empty *.mems.conf file. No memories generated." System.err.println("WARNING: Empty *.mems.conf file. No memories generated.")
// Emit empty verilog file if no macros found // Emit empty verilog file if no macros found
params.get(Verilog) match { params.get(Verilog) match {
@@ -879,8 +941,11 @@ object MacroCompiler extends App {
} }
} catch { } catch {
case e: java.util.NoSuchElementException => case e: java.util.NoSuchElementException =>
e.printStackTrace() if (args.isEmpty) {
println(usage) println("Command line arguments must be specified")
} else {
e.printStackTrace()
}
e.printStackTrace() e.printStackTrace()
sys.exit(1) sys.exit(1)
case e: MacroCompilerException => 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", "type": "sram",
"name" : "fake_mem", "name": "fake_mem",
"width" : 64, "width": 64,
"depth" : "512", "depth": "512",
"mux" : 4, "mux": 4,
"family" : "1rw", "family": "1rw",
"ports" : [ { "ports": [
"address port name" : "addr", {
"address port polarity" : "active high", "address port name": "addr",
"clock port name" : "clk", "address port polarity": "active high",
"clock port polarity" : "positive edge", "clock port name": "clk",
"write enable port name" : "wen", "clock port polarity": "positive edge",
"write enable port polarity" : "active high", "write enable port name": "wen",
"read enable port name" : "ren", "write enable port polarity": "active high",
"read enable port polarity" : "active high", "read enable port name": "ren",
"output port name" : "dataout", "read enable port polarity": "active high",
"output port polarity" : "active high", "output port name": "dataout",
"input port name" : "datain", "output port polarity": "active high",
"input port polarity" : "active high", "input port name": "datain",
"mask port name" : "mport", "input port polarity": "active high",
"mask port polarity" : "active low", "mask port name": "mport",
"mask granularity" : 1 "mask port polarity": "active low",
} ] "mask granularity": 1
}
]
} }
] ]

View File

@@ -1,24 +1,26 @@
[ [
{ {
"type" : "sram", "type": "sram",
"name" : "fake_mem", "name": "fake_mem",
"width" : 64, "width": 64,
"depth" : "4096", "depth": "4096",
"mux" : 4, "mux": 4,
"family" : "1rw", "family": "1rw",
"ports" : [ { "ports": [
"address port name" : "addr", {
"address port polarity" : "active high", "address port name": "addr",
"clock port name" : "clk", "address port polarity": "active high",
"clock port polarity" : "positive edge", "clock port name": "clk",
"write enable port name" : "wen", "clock port polarity": "positive edge",
"write enable port polarity" : "active high", "write enable port name": "wen",
"read enable port name" : "ren", "write enable port polarity": "active high",
"read enable port polarity" : "active high", "read enable port name": "ren",
"output port name" : "dataout", "read enable port polarity": "active high",
"output port polarity" : "active high", "output port name": "dataout",
"input port name" : "datain", "output port polarity": "active high",
"input 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. */ /** Tests to check that the cost function mechanism is working properly. */
/** /** A test metric that simply favours memories with smaller widths, to test that
* A test metric that simply favours memories with smaller widths, to test that * the metric is chosen properly.
* the metric is chosen properly. */
*/
object TestMinWidthMetric extends CostMetric with CostMetricCompanion { object TestMinWidthMetric extends CostMetric with CostMetricCompanion {
// Smaller width = lower cost = favoured // Smaller width = lower cost = favoured
override def cost(mem: Macro, lib: Macro): Option[Double] = Some(lib.src.width) 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( val libSRAMs = Seq(
SRAMMacro( SRAMMacro(
name="SRAM_WIDTH_128", name = "SRAM_WIDTH_128",
depth=BigInt(1024), depth = BigInt(1024),
width=128, width = 128,
family="1rw", family = "1rw",
ports=Seq( ports = Seq(
generateReadWritePort("", 128, BigInt(1024)) generateReadWritePort("", 128, BigInt(1024))
) )
), ),
SRAMMacro( SRAMMacro(
name="SRAM_WIDTH_64", name = "SRAM_WIDTH_64",
depth=BigInt(1024), depth = BigInt(1024),
width=64, width = 64,
family="1rw", family = "1rw",
ports=Seq( ports = Seq(
generateReadWritePort("", 64, BigInt(1024)) generateReadWritePort("", 64, BigInt(1024))
) )
), ),
SRAMMacro( SRAMMacro(
name="SRAM_WIDTH_32", name = "SRAM_WIDTH_32",
depth=BigInt(1024), depth = BigInt(1024),
width=32, width = 32,
family="1rw", family = "1rw",
ports=Seq( ports = Seq(
generateReadWritePort("", 32, BigInt(1024)) 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. // Check that the min width SRAM was chosen, even though it is less efficient.
val output = val output =
""" """
circuit target_memory : circuit target_memory :
module target_memory : module target_memory :
input addr : UInt<10> 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 package barstools.macros
import mdf.macrolib._
// Test the ability of the compiler to deal with various mask combinations. // Test the ability of the compiler to deal with various mask combinations.
trait MasksTestSettings { trait MasksTestSettings {
this: MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator => this: MacroCompilerSpec with HasSRAMGenerator with HasSimpleDepthTestGenerator =>
override lazy val memDepth = BigInt(2048) override lazy val memDepth = BigInt(2048)
override lazy val libDepth = BigInt(1024) override lazy val libDepth = BigInt(1024)
} }
// Try all four different kinds of mask config: // Try all four different kinds of mask config:
/** /** Non-masked mem Masked mem
* * ---------------------------------
* Non-masked mem Masked mem * Non-masked lib | | |
* --------------------------------- * ---------------------------------
* Non-masked lib | | | * 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 depth = BigInt(1024)
override lazy val memWidth = 32 override lazy val memWidth = 32
override lazy val memMaskGran = None override lazy val memMaskGran = None
override lazy val libWidth = 8 override lazy val libWidth = 8
override lazy val libMaskGran = None override lazy val libMaskGran = None
compileExecuteAndTest(mem, lib, v, output) 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 depth = BigInt(1024)
override lazy val memWidth = 32 override lazy val memWidth = 32
override lazy val memMaskGran = None override lazy val memMaskGran = None
@@ -41,7 +43,10 @@ class Masks_FourTypes_NonMaskedMem_MaskedLib extends MacroCompilerSpec with HasS
compileExecuteAndTest(mem, lib, v, output) 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 depth = BigInt(1024)
override lazy val memWidth = 32 override lazy val memWidth = 32
override lazy val memMaskGran = Some(8) override lazy val memMaskGran = Some(8)
@@ -51,7 +56,10 @@ class Masks_FourTypes_MaskedMem_NonMaskedLib extends MacroCompilerSpec with HasS
compileExecuteAndTest(mem, lib, v, output) 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 depth = BigInt(1024)
override lazy val memWidth = 32 override lazy val memWidth = 32
override lazy val memMaskGran = Some(4) override lazy val memMaskGran = Some(4)
@@ -61,7 +69,10 @@ class Masks_FourTypes_MaskedMem_NonMaskedLib_SmallerMaskGran extends MacroCompil
compileExecuteAndTest(mem, lib, v, output) 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 depth = BigInt(1024)
override lazy val memWidth = 32 override lazy val memWidth = 32
override lazy val memMaskGran = Some(8) override lazy val memMaskGran = Some(8)
@@ -71,7 +82,10 @@ class Masks_FourTypes_MaskedMem_MaskedLib extends MacroCompilerSpec with HasSRAM
compileExecuteAndTest(mem, lib, v, output) 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 depth = BigInt(1024)
override lazy val memWidth = 32 override lazy val memWidth = 32
override lazy val memMaskGran = Some(8) override lazy val memMaskGran = Some(8)
@@ -81,7 +95,10 @@ class Masks_FourTypes_MaskedMem_MaskedLib_SameMaskGran extends MacroCompilerSpec
compileExecuteAndTest(mem, lib, v, output) 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 depth = BigInt(1024)
override lazy val memWidth = 64 override lazy val memWidth = 64
override lazy val memMaskGran = Some(4) override lazy val memMaskGran = Some(4)
@@ -105,7 +122,11 @@ class Masks_BitMaskedMem_NonMaskedLib extends MacroCompilerSpec with HasSRAMGene
// FPGA-style byte-masked memories. // 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 width = 32
override lazy val memMaskGran = Some(32) override lazy val memMaskGran = Some(32)
override lazy val libMaskGran = Some(8) 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. // 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 width = 64
override lazy val memMaskGran = Some(8) override lazy val memMaskGran = Some(8)
override lazy val libMaskGran = Some(1) 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) 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 width = 64
override lazy val memMaskGran = Some(16) override lazy val memMaskGran = Some(16)
override lazy val libMaskGran = Some(1) 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) 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 width = 64
override lazy val memMaskGran = Some(32) override lazy val memMaskGran = Some(32)
override lazy val libMaskGran = Some(1) 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) 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 width = 64
override lazy val memMaskGran = Some(64) override lazy val memMaskGran = Some(64)
override lazy val libMaskGran = Some(1) 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. // 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 width = 128
override lazy val memMaskGran = Some(32) override lazy val memMaskGran = Some(32)
override lazy val libMaskGran = Some(4) 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) 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 width = 128
override lazy val memMaskGran = Some(32) override lazy val memMaskGran = Some(32)
override lazy val libMaskGran = Some(8) 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) 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 width = 128
override lazy val memMaskGran = Some(8) override lazy val memMaskGran = Some(8)
override lazy val libMaskGran = 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 // 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 width = 20
override lazy val memMaskGran = Some(10) override lazy val memMaskGran = Some(10)
override lazy val libMaskGran = Some(1) override lazy val libMaskGran = Some(1)
@@ -183,16 +236,24 @@ class Masks_IntegerMaskMultiple_20_10 extends MacroCompilerSpec with HasSRAMGene
compileExecuteAndTest(mem, lib, v, output) 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 width = 21
override lazy val memMaskGran = Some(21) override lazy val memMaskGran = Some(21)
override lazy val libMaskGran = Some(7) 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) //~ 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 width = 21
override lazy val memMaskGran = Some(21) override lazy val memMaskGran = Some(21)
override lazy val libMaskGran = Some(1) override lazy val libMaskGran = Some(1)
@@ -200,7 +261,11 @@ class Masks_IntegerMaskMultiple_21_21 extends MacroCompilerSpec with HasSRAMGene
compileExecuteAndTest(mem, lib, v, output) 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 width = 84
override lazy val memMaskGran = Some(21) override lazy val memMaskGran = Some(21)
override lazy val libMaskGran = Some(1) override lazy val libMaskGran = Some(1)
@@ -208,7 +273,11 @@ class Masks_IntegerMaskMultiple_84_21 extends MacroCompilerSpec with HasSRAMGene
compileExecuteAndTest(mem, lib, v, output) 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 width = 92
override lazy val memMaskGran = Some(23) override lazy val memMaskGran = Some(23)
override lazy val libMaskGran = Some(1) override lazy val libMaskGran = Some(1)
@@ -216,7 +285,11 @@ class Masks_IntegerMaskMultiple_92_23 extends MacroCompilerSpec with HasSRAMGene
compileExecuteAndTest(mem, lib, v, output) 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 width = 117
override lazy val memMaskGran = Some(13) override lazy val memMaskGran = Some(13)
override lazy val libMaskGran = Some(1) override lazy val libMaskGran = Some(1)
@@ -224,7 +297,11 @@ class Masks_IntegerMaskMultiple_117_13 extends MacroCompilerSpec with HasSRAMGen
compileExecuteAndTest(mem, lib, v, output) 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 width = 160
override lazy val memMaskGran = Some(20) override lazy val memMaskGran = Some(20)
override lazy val libMaskGran = Some(1) override lazy val libMaskGran = Some(1)
@@ -232,7 +309,11 @@ class Masks_IntegerMaskMultiple_160_20 extends MacroCompilerSpec with HasSRAMGen
compileExecuteAndTest(mem, lib, v, output) 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 width = 184
override lazy val memMaskGran = Some(23) override lazy val memMaskGran = Some(23)
override lazy val libMaskGran = Some(1) 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 // 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 width = 32
override lazy val memMaskGran = Some(3) override lazy val memMaskGran = Some(3)
override lazy val libMaskGran = Some(1) 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) //~ compileExecuteAndTest(mem, lib, v, output)
} }

View File

@@ -13,42 +13,70 @@ class SplitWidth_2rw extends MacroCompilerSpec with HasSRAMGenerator with HasSim
override def generateMemSRAM() = { override def generateMemSRAM() = {
SRAMMacro( SRAMMacro(
name=mem_name, name = mem_name,
width=memWidth, width = memWidth,
depth=memDepth, depth = memDepth,
family="2rw", family = "2rw",
ports=Seq(generateTestPort( ports = Seq(
"portA", memWidth, Some(memDepth), maskGran=memMaskGran, generateTestPort(
write=true, writeEnable=true, "portA",
read=true, readEnable=true memWidth,
), generateTestPort( Some(memDepth),
"portB", memWidth, Some(memDepth), maskGran=memMaskGran, maskGran = memMaskGran,
write=true, writeEnable=true, write = true,
read=true, readEnable=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() = { override def generateLibSRAM() = {
SRAMMacro( SRAMMacro(
name=lib_name, name = lib_name,
width=libWidth, width = libWidth,
depth=libDepth, depth = libDepth,
family="2rw", family = "2rw",
ports=Seq(generateTestPort( ports = Seq(
"portA", libWidth, libDepth, generateTestPort(
write=true, writeEnable=true, "portA",
read=true, readEnable=true libWidth,
), generateTestPort( libDepth,
"portB", libWidth, libDepth, write = true,
write=true, writeEnable=true, writeEnable = true,
read=true, readEnable=true read = true,
)) readEnable = true
),
generateTestPort(
"portB",
libWidth,
libDepth,
write = true,
writeEnable = true,
read = true,
readEnable = true
)
)
) )
} }
override def generateHeaderPorts() = { 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() = { override def generateFooterPorts() = {
@@ -56,7 +84,7 @@ class SplitWidth_2rw extends MacroCompilerSpec with HasSRAMGenerator with HasSim
} }
override def generateBody() = override def generateBody() =
""" """
inst mem_0_0 of awesome_lib_mem inst mem_0_0 of awesome_lib_mem
inst mem_0_1 of awesome_lib_mem inst mem_0_1 of awesome_lib_mem
inst mem_0_2 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() = { override def generateMemSRAM() = {
SRAMMacro( SRAMMacro(
name=mem_name, name = mem_name,
width=memWidth, width = memWidth,
depth=memDepth, depth = memDepth,
family="1r1w", family = "1r1w",
ports=Seq(generateTestPort( ports = Seq(
"portA", memWidth, Some(memDepth), maskGran=memMaskGran, generateTestPort(
write=false, writeEnable=false, "portA",
read=true, readEnable=true memWidth,
), generateTestPort( Some(memDepth),
"portB", memWidth, Some(memDepth), maskGran=memMaskGran, maskGran = memMaskGran,
write=true, writeEnable=true, write = false,
read=false, readEnable=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() = { override def generateLibSRAM() = {
SRAMMacro( SRAMMacro(
name=lib_name, name = lib_name,
width=libWidth, width = libWidth,
depth=libDepth, depth = libDepth,
family="1r1w", family = "1r1w",
ports=Seq(generateTestPort( ports = Seq(
"portA", libWidth, libDepth, generateTestPort(
write=false, writeEnable=false, "portA",
read=true, readEnable=true libWidth,
), generateTestPort( libDepth,
"portB", libWidth, libDepth, write = false,
write=true, writeEnable=true, writeEnable = false,
read=false, readEnable=false read = true,
)) readEnable = true
),
generateTestPort(
"portB",
libWidth,
libDepth,
write = true,
writeEnable = true,
read = false,
readEnable = false
)
)
) )
} }
override def generateHeaderPorts() = { override def generateHeaderPorts() = {
generatePort("portA", mem_addr_width, memWidth, generatePort(
write=false, writeEnable=false, read=true, readEnable=true, Some(memMaskBits)) + "\n" + "portA",
generatePort("portB", mem_addr_width, memWidth, mem_addr_width,
write=true, writeEnable=true, read=false, readEnable=false, Some(memMaskBits)) 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() = { override def generateFooterPorts() = {
generatePort("portA", lib_addr_width, libWidth, generatePort(
write=false, writeEnable=false, read=true, readEnable=true, None) + "\n" + "portA",
generatePort("portB", lib_addr_width, libWidth, lib_addr_width,
write=true, writeEnable=true, read=false, readEnable=false, None) 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() = override def generateBody() =
""" """
inst mem_0_0 of awesome_lib_mem inst mem_0_0 of awesome_lib_mem
inst mem_0_1 of awesome_lib_mem inst mem_0_1 of awesome_lib_mem
inst mem_0_2 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() = { override def generateMemSRAM() = {
println(memMaskGranB) println(memMaskGranB)
SRAMMacro( SRAMMacro(
name=mem_name, name = mem_name,
width=memWidth, width = memWidth,
depth=memDepth, depth = memDepth,
family="2rw", family = "2rw",
ports=Seq(generateTestPort( ports = Seq(
"portA", memWidth, Some(memDepth), maskGran=memMaskGran, generateTestPort(
write=true, writeEnable=true, "portA",
read=true, readEnable=true memWidth,
), generateTestPort( Some(memDepth),
"portB", memWidth, Some(memDepth), maskGran=Some(memMaskGranB), maskGran = memMaskGran,
write=true, writeEnable=true, write = true,
read=true, readEnable=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() = { override def generateLibSRAM() = {
SRAMMacro( SRAMMacro(
name=lib_name, name = lib_name,
width=libWidth, width = libWidth,
depth=libDepth, depth = libDepth,
family="2rw", family = "2rw",
ports=Seq(generateTestPort( ports = Seq(
"portA", libWidth, libDepth, generateTestPort(
write=true, writeEnable=true, "portA",
read=true, readEnable=true libWidth,
), generateTestPort( libDepth,
"portB", libWidth, libDepth, write = true,
write=true, writeEnable=true, writeEnable = true,
read=true, readEnable=true read = true,
)) readEnable = true
),
generateTestPort(
"portB",
libWidth,
libDepth,
write = true,
writeEnable = true,
read = true,
readEnable = true
)
)
) )
} }
override def generateHeaderPorts() = { 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() = { override def generateFooterPorts() = {
@@ -277,7 +389,7 @@ class SplitWidth_2rw_differentMasks extends MacroCompilerSpec with HasSRAMGenera
} }
override def generateBody() = override def generateBody() =
""" """
inst mem_0_0 of awesome_lib_mem inst mem_0_0 of awesome_lib_mem
inst mem_0_1 of awesome_lib_mem inst mem_0_1 of awesome_lib_mem
inst mem_0_2 of awesome_lib_mem inst mem_0_2 of awesome_lib_mem

View File

@@ -1,7 +1,5 @@
package barstools.macros package barstools.macros
import mdf.macrolib._
class SRAMCompiler extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator { class SRAMCompiler extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleWidthTestGenerator {
val compiler = generateSRAMCompiler("awesome", "A") val compiler = generateSRAMCompiler("awesome", "A")
val verilog = s"v-SRAMCompiler.v" val verilog = s"v-SRAMCompiler.v"
@@ -15,8 +13,7 @@ class SRAMCompiler extends MacroCompilerSpec with HasSRAMGenerator with HasSimpl
writeToLib(lib, Seq(compiler)) writeToLib(lib, Seq(compiler))
writeToMem(mem, Seq(generateSRAM("mymem", "X", 8, 16))) 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 package barstools.macros
import mdf.macrolib._
// Test the depth splitting aspect of the memory compiler. // Test the depth splitting aspect of the memory compiler.
// This file is for simple tests: one read-write port, powers of two sizes, etc. // 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. // For example, implementing a 4096x32 memory using four 1024x32 memories.
trait HasSimpleDepthTestGenerator extends HasSimpleTestGenerator { trait HasSimpleDepthTestGenerator extends HasSimpleTestGenerator {
this: MacroCompilerSpec with HasSRAMGenerator => this: MacroCompilerSpec with HasSRAMGenerator =>
def width: Int def width: Int
override lazy val memWidth = width override lazy val memWidth = width
override lazy val libWidth = width override lazy val libWidth = width
// Generate a depth-splitting body. // Generate a depth-splitting body.
override def generateBody(): String = { override def generateBody(): String = {
val output = new StringBuilder val output = new StringBuilder
if (selectBits > 0) { if (selectBits > 0) {
output.append ( output.append(
s""" s"""
node ${memPortPrefix}_addr_sel = bits(${memPortPrefix}_addr, ${mem_addr_width - 1}, $lib_addr_width) node ${memPortPrefix}_addr_sel = bits(${memPortPrefix}_addr, ${mem_addr_width - 1}, $lib_addr_width)
reg ${memPortPrefix}_addr_sel_reg : UInt<${selectBits}>, ${memPortPrefix}_clk with : reg ${memPortPrefix}_addr_sel_reg : UInt<${selectBits}>, ${memPortPrefix}_clk with :
reset => (UInt<1>("h0"), ${memPortPrefix}_addr_sel_reg) reset => (UInt<1>("h0"), ${memPortPrefix}_addr_sel_reg)
${memPortPrefix}_addr_sel_reg <= mux(UInt<1>("h1"), ${memPortPrefix}_addr_sel, ${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) { for (i <- 0 to depthInstances - 1) {
val maskStatement = generateMaskStatement(0, i) 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 enableIdentifier =
val chipEnable = s"""UInt<1>("h1")""" if (selectBits > 0) s"""eq(${memPortPrefix}_addr_sel, UInt<${selectBits}>("h${i.toHexString}"))"""
val writeEnable = if (memMaskGran.isEmpty) s"and(${memPortPrefix}_write_en, ${chipEnable})" else s"${memPortPrefix}_write_en" else "UInt<1>(\"h1\")"
output.append( val chipEnable = s"""UInt<1>("h1")"""
s""" 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} inst mem_${i}_0 of ${lib_name}
mem_${i}_0.${libPortPrefix}_clk <= ${memPortPrefix}_clk mem_${i}_0.${libPortPrefix}_clk <= ${memPortPrefix}_clk
mem_${i}_0.${libPortPrefix}_addr <= ${memPortPrefix}_addr 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}) mem_${i}_0.${libPortPrefix}_write_en <= and(and(${writeEnable}, UInt<1>("h1")), ${enableIdentifier})
node ${memPortPrefix}_dout_${i} = ${memPortPrefix}_dout_${i}_0 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 // Try different widths
@@ -156,7 +160,10 @@ class SplitDepth2048x8_mrw_lib8 extends MacroCompilerSpec with HasSRAMGenerator
} }
// Non-bit level mask // 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 width = 64
override lazy val memDepth = BigInt(2048) override lazy val memDepth = BigInt(2048)
override lazy val libDepth = BigInt(1024) override lazy val libDepth = BigInt(1024)
@@ -167,7 +174,10 @@ class SplitDepth2048x64_mrw_mem32_lib8 extends MacroCompilerSpec with HasSRAMGen
} }
// Bit level mask // 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 width = 32
override lazy val memDepth = BigInt(2048) override lazy val memDepth = BigInt(2048)
override lazy val libDepth = BigInt(1024) 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 memMaskGran = Some(3)
override lazy val libMaskGran = Some(1) 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) //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 memMaskGran = Some(7)
override lazy val libMaskGran = Some(1) 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) //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 memMaskGran = Some(9)
override lazy val libMaskGran = Some(1) 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) //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 memDepth = BigInt(2048)
override lazy val libDepth = BigInt(1024) override lazy val libDepth = BigInt(1024)
override lazy val extraPorts = List( 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 lazy val extraTag = "extraPort"
override def generateOutput(): String = override def generateOutput(): String =
""" """
circuit target_memory : circuit target_memory :
module target_memory : module target_memory :
input outer_addr : UInt<11> 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 v = "split_depth-r-w-split-lib-split-mem.v"
val libMacro = SRAMMacro( val libMacro = SRAMMacro(
name="awesome_lib_mem", name = "awesome_lib_mem",
width=width, width = width,
depth=libDepth, depth = libDepth,
family="1r1w", family = "1r1w",
ports=Seq( ports = Seq(
generateReadPort("innerA", width, libDepth), generateReadPort("innerA", width, libDepth),
generateWritePort("innerB", width, libDepth) generateWritePort("innerB", width, libDepth)
) )
) )
val memMacro = SRAMMacro( val memMacro = SRAMMacro(
name="target_memory", name = "target_memory",
width=width, width = width,
depth=memDepth, depth = memDepth,
family="1r1w", family = "1r1w",
ports=Seq( ports = Seq(
generateReadPort("outerB", width, memDepth), generateReadPort("outerB", width, memDepth),
generateWritePort("outerA", width, memDepth) generateWritePort("outerA", width, memDepth)
) )
@@ -344,7 +354,7 @@ class SplitDepth_SplitPortsNonMasked extends MacroCompilerSpec with HasSRAMGener
writeToLib(lib, Seq(libMacro)) writeToLib(lib, Seq(libMacro))
val output = val output =
""" """
circuit target_memory : circuit target_memory :
module target_memory : module target_memory :
input outerB_addr : UInt<11> input outerB_addr : UInt<11>
@@ -406,11 +416,11 @@ circuit target_memory :
val v = "split_depth-r-w-regular-lib-split-mem.v" val v = "split_depth-r-w-regular-lib-split-mem.v"
val memMacro = SRAMMacro( val memMacro = SRAMMacro(
name="target_memory", name = "target_memory",
width=width, width = width,
depth=memDepth, depth = memDepth,
family="1r1w", family = "1r1w",
ports=Seq( ports = Seq(
generateReadPort("outerB", width, memDepth), generateReadPort("outerB", width, memDepth),
generateWritePort("outerA", width, memDepth) generateWritePort("outerA", width, memDepth)
) )
@@ -420,7 +430,7 @@ circuit target_memory :
writeToLib(lib, Seq(generateSRAM("awesome_lib_mem", "lib", width, libDepth))) writeToLib(lib, Seq(generateSRAM("awesome_lib_mem", "lib", width, libDepth)))
val output = val output =
""" """
TODO TODO
""" """
@@ -439,11 +449,11 @@ TODO
val v = "split_depth-r-w-split-lib-regular-mem.v" val v = "split_depth-r-w-split-lib-regular-mem.v"
val libMacro = SRAMMacro( val libMacro = SRAMMacro(
name="awesome_lib_mem", name = "awesome_lib_mem",
width=width, width = width,
depth=libDepth, depth = libDepth,
family="1rw", family = "1rw",
ports=Seq( ports = Seq(
generateReadPort("innerA", width, libDepth), generateReadPort("innerA", width, libDepth),
generateWritePort("innerB", width, libDepth) generateWritePort("innerB", width, libDepth)
) )
@@ -453,7 +463,7 @@ TODO
writeToLib(lib, Seq(libMacro)) writeToLib(lib, Seq(libMacro))
val output = val output =
""" """
TODO TODO
""" """
@@ -480,22 +490,22 @@ class SplitDepth_SplitPortsMasked extends MacroCompilerSpec with HasSRAMGenerato
val v = "split_depth-r-mw-split-lib-split-mem.v" val v = "split_depth-r-mw-split-lib-split-mem.v"
val libMacro = SRAMMacro( val libMacro = SRAMMacro(
name="awesome_lib_mem", name = "awesome_lib_mem",
width=width, width = width,
depth=libDepth, depth = libDepth,
family="1r1w", family = "1r1w",
ports=Seq( ports = Seq(
generateReadPort("innerA", width, libDepth), generateReadPort("innerA", width, libDepth),
generateWritePort("innerB", width, libDepth, libMaskGran) generateWritePort("innerB", width, libDepth, libMaskGran)
) )
) )
val memMacro = SRAMMacro( val memMacro = SRAMMacro(
name="target_memory", name = "target_memory",
width=width, width = width,
depth=memDepth, depth = memDepth,
family="1r1w", family = "1r1w",
ports=Seq( ports = Seq(
generateReadPort("outerB", width, memDepth), generateReadPort("outerB", width, memDepth),
generateWritePort("outerA", width, memDepth, memMaskGran) generateWritePort("outerA", width, memDepth, memMaskGran)
) )
@@ -505,7 +515,7 @@ class SplitDepth_SplitPortsMasked extends MacroCompilerSpec with HasSRAMGenerato
writeToLib(lib, Seq(libMacro)) writeToLib(lib, Seq(libMacro))
val output = val output =
""" """
circuit target_memory : circuit target_memory :
module target_memory : module target_memory :
input outerB_addr : UInt<11> input outerB_addr : UInt<11>
@@ -571,11 +581,11 @@ circuit target_memory :
val v = "split_depth-r-mw-regular-lib-split-mem.v" val v = "split_depth-r-mw-regular-lib-split-mem.v"
val memMacro = SRAMMacro( val memMacro = SRAMMacro(
name="target_memory", name = "target_memory",
width=width, width = width,
depth=memDepth, depth = memDepth,
family="1r1w", family = "1r1w",
ports=Seq( ports = Seq(
generateReadPort("outerB", width, memDepth), generateReadPort("outerB", width, memDepth),
generateWritePort("outerA", width, memDepth, memMaskGran) generateWritePort("outerA", width, memDepth, memMaskGran)
) )
@@ -585,7 +595,7 @@ circuit target_memory :
writeToLib(lib, Seq(generateSRAM("awesome_lib_mem", "lib", width, libDepth, libMaskGran))) writeToLib(lib, Seq(generateSRAM("awesome_lib_mem", "lib", width, libDepth, libMaskGran)))
val output = val output =
""" """
TODO TODO
""" """
@@ -604,11 +614,11 @@ TODO
val v = "split_depth-r-mw-split-lib-regular-mem.v" val v = "split_depth-r-mw-split-lib-regular-mem.v"
val libMacro = SRAMMacro( val libMacro = SRAMMacro(
name="awesome_lib_mem", name = "awesome_lib_mem",
width=width, width = width,
depth=libDepth, depth = libDepth,
family="1rw", family = "1rw",
ports=Seq( ports = Seq(
generateReadPort("innerA", width, libDepth), generateReadPort("innerA", width, libDepth),
generateWritePort("innerB", width, libDepth, libMaskGran) generateWritePort("innerB", width, libDepth, libMaskGran)
) )
@@ -618,7 +628,7 @@ TODO
writeToLib(lib, Seq(libMacro)) writeToLib(lib, Seq(libMacro))
val output = val output =
""" """
TODO TODO
""" """

View File

@@ -5,43 +5,45 @@ package barstools.macros
trait HasSimpleWidthTestGenerator extends HasSimpleTestGenerator { trait HasSimpleWidthTestGenerator extends HasSimpleTestGenerator {
this: MacroCompilerSpec with HasSRAMGenerator => this: MacroCompilerSpec with HasSRAMGenerator =>
def depth: BigInt def depth: BigInt
override lazy val memDepth = depth override lazy val memDepth = depth
override lazy val libDepth = depth override lazy val libDepth = depth
override def generateBody(): String = { override def generateBody(): String = {
val output = new StringBuilder val output = new StringBuilder
// Generate mem_0_<i> lines for number of width instances. // Generate mem_0_<i> lines for number of width instances.
output.append( output.append(
((0 to widthInstances - 1) map {i:Int => s""" ((0 to widthInstances - 1).map { i: Int =>
s"""
inst mem_0_${i} of ${lib_name} inst mem_0_${i} of ${lib_name}
""" """
}).reduceLeft(_ + _) }).reduceLeft(_ + _)
) )
// Generate submemory connection blocks. // Generate submemory connection blocks.
output append (for (i <- 0 to widthInstances - 1) yield { output.append((for (i <- 0 to widthInstances - 1) yield {
// Width of this submemory. // Width of this submemory.
val myMemWidth = if (i == widthInstances - 1) lastWidthBits else usableLibWidth val myMemWidth = if (i == widthInstances - 1) lastWidthBits else usableLibWidth
// Base bit of this submemory. // Base bit of this submemory.
// e.g. if libWidth is 8 and this is submemory 2 (0-indexed), then this // e.g. if libWidth is 8 and this is submemory 2 (0-indexed), then this
// would be 16. // would be 16.
val myBaseBit = usableLibWidth*i 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 // We need to use writeEnable as a crude "mask" if mem has a mask but
// lib does not. // lib does not.
val writeEnableBit = if (libMaskGran.isEmpty && memMaskGran.isDefined) { val writeEnableBit = if (libMaskGran.isEmpty && memMaskGran.isDefined) {
val outerMaskBit = myBaseBit / memMaskGran.get val outerMaskBit = myBaseBit / memMaskGran.get
s"bits(outer_mask, ${outerMaskBit}, ${outerMaskBit})" s"bits(outer_mask, ${outerMaskBit}, ${outerMaskBit})"
} else """UInt<1>("h1")""" } else """UInt<1>("h1")"""
val chipEnable = s"""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" 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}_clk <= ${memPortPrefix}_clk
mem_0_${i}.${libPortPrefix}_addr <= ${memPortPrefix}_addr mem_0_${i}.${libPortPrefix}_addr <= ${memPortPrefix}_addr
node ${memPortPrefix}_dout_0_${i} = bits(mem_0_${i}.${libPortPrefix}_dout, ${myMemWidth - 1}, 0) node ${memPortPrefix}_dout_0_${i} = bits(mem_0_${i}.${libPortPrefix}_dout, ${myMemWidth - 1}, 0)
@@ -49,24 +51,23 @@ s"""
${maskStatement} ${maskStatement}
mem_0_${i}.${libPortPrefix}_write_en <= and(and(${writeEnableExpr}, ${writeEnableBit}), UInt<1>("h1")) mem_0_${i}.${libPortPrefix}_write_en <= and(and(${writeEnableExpr}, ${writeEnableBit}), UInt<1>("h1"))
""" """
}).reduceLeft(_ + _) }).reduceLeft(_ + _))
// Generate final output that concats together the sub-memories. // 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)) // e.g. cat(outer_dout_0_2, cat(outer_dout_0_1, outer_dout_0_0))
output append { output.append {
val doutStatements = ((widthInstances - 1 to 0 by -1) map (i => s"${memPortPrefix}_dout_0_${i}")) 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)") val catStmt = doutStatements.init.foldRight(doutStatements.last)((l: String, r: String) => s"cat($l, $r)")
s""" s"""
node ${memPortPrefix}_dout_0 = ${catStmt} 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. // Try different widths against a base memory width of 8.
@@ -268,7 +269,10 @@ class SplitWidth1024x16_mem11_rw extends MacroCompilerSpec with HasSRAMGenerator
// Masked RAM // 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 depth = BigInt(1024)
override lazy val memWidth = 8 override lazy val memWidth = 8
override lazy val libWidth = 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) 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 depth = BigInt(1024)
override lazy val memWidth = 16 override lazy val memWidth = 16
override lazy val libWidth = 8 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) 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 depth = BigInt(1024)
override lazy val memWidth = 16 override lazy val memWidth = 16
override lazy val libWidth = 8 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) 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 depth = BigInt(1024)
override lazy val memWidth = 128 override lazy val memWidth = 128
override lazy val libWidth = 32 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) 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 depth = BigInt(1024)
override lazy val memWidth = 16 override lazy val memWidth = 16
override lazy val libWidth = 8 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) 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 depth = BigInt(1024)
override lazy val memWidth = 16 override lazy val memWidth = 16
override lazy val libWidth = 8 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) 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 depth = BigInt(1024)
override lazy val memWidth = 16 override lazy val memWidth = 16
override lazy val libWidth = 8 override lazy val libWidth = 8
@@ -360,7 +382,10 @@ class SplitWidth1024x16_libGran_1_rw extends MacroCompilerSpec with HasSRAMGener
// Non-memMask and non-1 libMask // 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 depth = BigInt(1024)
override lazy val memWidth = 16 override lazy val memWidth = 16
override lazy val libWidth = 8 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 // 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 depth = BigInt(1024)
override lazy val memWidth = 16 override lazy val memWidth = 16
override lazy val libWidth = 8 override lazy val libWidth = 8
override lazy val memMaskGran = Some(9) override lazy val memMaskGran = Some(9)
override lazy val libMaskGran = Some(1) 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) //~ compile(mem, lib, v, false)
//~ execute(mem, lib, false, output) //~ execute(mem, lib, false, output)
} }
// Read enable // 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._ import mdf.macrolib._
override lazy val depth = BigInt(1024) override lazy val depth = BigInt(1024)
@@ -395,20 +426,27 @@ class SplitWidth1024x32_readEnable_Lib extends MacroCompilerSpec with HasSRAMGen
override def generateLibSRAM() = { override def generateLibSRAM() = {
SRAMMacro( SRAMMacro(
name=lib_name, name = lib_name,
width=libWidth, width = libWidth,
depth=libDepth, depth = libDepth,
family="1rw", family = "1rw",
ports=Seq(generateTestPort( ports = Seq(
"lib", Some(libWidth), Some(libDepth), maskGran=libMaskGran, generateTestPort(
write=true, writeEnable=true, "lib",
read=true, readEnable=true Some(libWidth),
)) Some(libDepth),
maskGran = libMaskGran,
write = true,
writeEnable = true,
read = true,
readEnable = true
)
)
) )
} }
override def generateBody() = override def generateBody() =
""" """
inst mem_0_0 of awesome_lib_mem inst mem_0_0 of awesome_lib_mem
inst mem_0_1 of awesome_lib_mem inst mem_0_1 of awesome_lib_mem
inst mem_0_2 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) 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._ import mdf.macrolib._
override lazy val depth = BigInt(1024) override lazy val depth = BigInt(1024)
@@ -453,15 +494,22 @@ class SplitWidth1024x32_readEnable_Mem extends MacroCompilerSpec with HasSRAMGen
override def generateMemSRAM() = { override def generateMemSRAM() = {
SRAMMacro( SRAMMacro(
name=mem_name, name = mem_name,
width=memWidth, width = memWidth,
depth=memDepth, depth = memDepth,
family="1rw", family = "1rw",
ports=Seq(generateTestPort( ports = Seq(
"outer", Some(memWidth), Some(memDepth), maskGran=memMaskGran, generateTestPort(
write=true, writeEnable=true, "outer",
read=true, readEnable=true 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) 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._ import mdf.macrolib._
override lazy val depth = BigInt(1024) override lazy val depth = BigInt(1024)
@@ -479,34 +530,48 @@ class SplitWidth1024x32_readEnable_LibMem extends MacroCompilerSpec with HasSRAM
override def generateLibSRAM() = { override def generateLibSRAM() = {
SRAMMacro( SRAMMacro(
name=lib_name, name = lib_name,
width=libWidth, width = libWidth,
depth=libDepth, depth = libDepth,
family="1rw", family = "1rw",
ports=Seq(generateTestPort( ports = Seq(
"lib", Some(libWidth), Some(libDepth), maskGran=libMaskGran, generateTestPort(
write=true, writeEnable=true, "lib",
read=true, readEnable=true Some(libWidth),
)) Some(libDepth),
maskGran = libMaskGran,
write = true,
writeEnable = true,
read = true,
readEnable = true
)
)
) )
} }
override def generateMemSRAM() = { override def generateMemSRAM() = {
SRAMMacro( SRAMMacro(
name=mem_name, name = mem_name,
width=memWidth, width = memWidth,
depth=memDepth, depth = memDepth,
family="1rw", family = "1rw",
ports=Seq(generateTestPort( ports = Seq(
"outer", Some(memWidth), Some(memDepth), maskGran=memMaskGran, generateTestPort(
write=true, writeEnable=true, "outer",
read=true, readEnable=true Some(memWidth),
)) Some(memDepth),
maskGran = memMaskGran,
write = true,
writeEnable = true,
read = true,
readEnable = true
)
)
) )
} }
override def generateBody() = override def generateBody() =
""" """
inst mem_0_0 of awesome_lib_mem inst mem_0_0 of awesome_lib_mem
inst mem_0_1 of awesome_lib_mem inst mem_0_1 of awesome_lib_mem
inst mem_0_2 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" override val libPrefix = "macros/src/test/resources"
val memSRAMs = mdf.macrolib.Utils.readMDFFromString( val memSRAMs = mdf.macrolib.Utils
""" .readMDFFromString("""
[ { [ {
"type" : "sram", "type" : "sram",
"name" : "cc_banks_0_ext", "name" : "cc_banks_0_ext",
@@ -58,7 +58,7 @@ class WriteEnableTest extends MacroCompilerSpec with HasSRAMGenerator {
writeToMem(mem, memSRAMs) writeToMem(mem, memSRAMs)
val output = val output =
""" """
circuit cc_banks_0_ext : circuit cc_banks_0_ext :
module cc_banks_0_ext : module cc_banks_0_ext :
input RW0_addr : UInt<12> input RW0_addr : UInt<12>
@@ -99,8 +99,8 @@ class MaskPortTest extends MacroCompilerSpec with HasSRAMGenerator {
override val libPrefix = "macros/src/test/resources" override val libPrefix = "macros/src/test/resources"
val memSRAMs = mdf.macrolib.Utils.readMDFFromString( val memSRAMs = mdf.macrolib.Utils
""" .readMDFFromString("""
[ { [ {
"type" : "sram", "type" : "sram",
"name" : "cc_dir_ext", "name" : "cc_dir_ext",
@@ -131,7 +131,7 @@ class MaskPortTest extends MacroCompilerSpec with HasSRAMGenerator {
writeToMem(mem, memSRAMs) writeToMem(mem, memSRAMs)
val output = val output =
""" """
circuit cc_dir_ext : circuit cc_dir_ext :
module cc_dir_ext : module cc_dir_ext :
input RW0_addr : UInt<9> input RW0_addr : UInt<9>
@@ -183,8 +183,8 @@ class BOOMTest extends MacroCompilerSpec with HasSRAMGenerator {
override val libPrefix = "macros/src/test/resources" override val libPrefix = "macros/src/test/resources"
val memSRAMs = mdf.macrolib.Utils.readMDFFromString( val memSRAMs = mdf.macrolib.Utils
""" .readMDFFromString("""
[ { [ {
"type" : "sram", "type" : "sram",
"name" : "_T_182_ext", "name" : "_T_182_ext",
@@ -354,7 +354,7 @@ class BOOMTest extends MacroCompilerSpec with HasSRAMGenerator {
writeToMem(mem, memSRAMs) writeToMem(mem, memSRAMs)
val output = // TODO: check correctness... val output = // TODO: check correctness...
""" """
circuit smem_0_ext : circuit smem_0_ext :
module _T_182_ext : module _T_182_ext :
input R0_addr : UInt<6> input R0_addr : UInt<6>
@@ -1350,14 +1350,14 @@ circuit smem_0_ext :
class SmallTagArrayTest extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleTestGenerator { class SmallTagArrayTest extends MacroCompilerSpec with HasSRAMGenerator with HasSimpleTestGenerator {
// Test that mapping a smaller memory using a larger lib can still work. // Test that mapping a smaller memory using a larger lib can still work.
override def memWidth: Int = 26 override def memWidth: Int = 26
override def memDepth: BigInt = BigInt(2) override def memDepth: BigInt = BigInt(2)
override def memMaskGran: Option[Int] = Some(26) override def memMaskGran: Option[Int] = Some(26)
override def memPortPrefix: String = "" override def memPortPrefix: String = ""
override def libWidth: Int = 32 override def libWidth: Int = 32
override def libDepth: BigInt = BigInt(64) override def libDepth: BigInt = BigInt(64)
override def libMaskGran: Option[Int] = Some(1) override def libMaskGran: Option[Int] = Some(1)
override def libPortPrefix: String = "" override def libPortPrefix: String = ""
override def extraPorts: Seq[MacroExtraPort] = Seq( override def extraPorts: Seq[MacroExtraPort] = Seq(
@@ -1388,73 +1388,73 @@ class RocketChipTest extends MacroCompilerSpec with HasSRAMGenerator {
val libSRAMs = Seq( val libSRAMs = Seq(
SRAMMacro( SRAMMacro(
name="SRAM1RW1024x8", name = "SRAM1RW1024x8",
depth=1024, depth = 1024,
width=8, width = 8,
family="1rw", family = "1rw",
ports=Seq( ports = Seq(
generateReadWritePort("", 8, BigInt(1024)) generateReadWritePort("", 8, BigInt(1024))
) )
), ),
SRAMMacro( SRAMMacro(
name="SRAM1RW512x32", name = "SRAM1RW512x32",
depth=512, depth = 512,
width=32, width = 32,
family="1rw", family = "1rw",
ports=Seq( ports = Seq(
generateReadWritePort("", 32, BigInt(512)) generateReadWritePort("", 32, BigInt(512))
) )
), ),
SRAMMacro( SRAMMacro(
name="SRAM1RW64x128", name = "SRAM1RW64x128",
depth=64, depth = 64,
width=128, width = 128,
family="1rw", family = "1rw",
ports=Seq( ports = Seq(
generateReadWritePort("", 128, BigInt(64)) generateReadWritePort("", 128, BigInt(64))
) )
), ),
SRAMMacro( SRAMMacro(
name="SRAM1RW64x32", name = "SRAM1RW64x32",
depth=64, depth = 64,
width=32, width = 32,
family="1rw", family = "1rw",
ports=Seq( ports = Seq(
generateReadWritePort("", 32, BigInt(64)) generateReadWritePort("", 32, BigInt(64))
) )
), ),
SRAMMacro( SRAMMacro(
name="SRAM1RW64x8", name = "SRAM1RW64x8",
depth=64, depth = 64,
width=8, width = 8,
family="1rw", family = "1rw",
ports=Seq( ports = Seq(
generateReadWritePort("", 8, BigInt(64)) generateReadWritePort("", 8, BigInt(64))
) )
), ),
SRAMMacro( SRAMMacro(
name="SRAM1RW512x8", name = "SRAM1RW512x8",
depth=512, depth = 512,
width=8, width = 8,
family="1rw", family = "1rw",
ports=Seq( ports = Seq(
generateReadWritePort("", 8, BigInt(512)) generateReadWritePort("", 8, BigInt(512))
) )
), ),
SRAMMacro( SRAMMacro(
name="SRAM2RW64x32", name = "SRAM2RW64x32",
depth=64, depth = 64,
width=32, width = 32,
family="1r1w", family = "1r1w",
ports=Seq( ports = Seq(
generateReadPort("portA", 32, BigInt(64)), generateReadPort("portA", 32, BigInt(64)),
generateWritePort("portB", 32, BigInt(64)) generateWritePort("portB", 32, BigInt(64))
) )
) )
) )
val memSRAMs = mdf.macrolib.Utils.readMDFFromString( val memSRAMs = mdf.macrolib.Utils
""" .readMDFFromString("""
[ [
{ {
"type": "sram", "type": "sram",
@@ -1537,7 +1537,7 @@ class RocketChipTest extends MacroCompilerSpec with HasSRAMGenerator {
writeToMem(mem, memSRAMs) writeToMem(mem, memSRAMs)
val output = // TODO: check correctness... val output = // TODO: check correctness...
""" """
circuit T_2172_ext : circuit T_2172_ext :
module tag_array_ext : module tag_array_ext :
input RW0_addr : UInt<6> input RW0_addr : UInt<6>

View File

@@ -4,13 +4,13 @@ package barstools.macros
trait HasSynFlopsTestGenerator extends HasSimpleTestGenerator { trait HasSynFlopsTestGenerator extends HasSimpleTestGenerator {
this: MacroCompilerSpec with HasSRAMGenerator => this: MacroCompilerSpec with HasSRAMGenerator =>
def generateFlops: String = { def generateFlops: String = {
s""" s"""
inst mem_0_0 of split_${lib_name} inst mem_0_0 of split_${lib_name}
mem_0_0.${libPortPrefix}_clk <= ${libPortPrefix}_clk mem_0_0.${libPortPrefix}_clk <= ${libPortPrefix}_clk
mem_0_0.${libPortPrefix}_addr <= ${libPortPrefix}_addr mem_0_0.${libPortPrefix}_addr <= ${libPortPrefix}_addr
node ${libPortPrefix}_dout_0_0 = bits(mem_0_0.${libPortPrefix}_dout, ${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}_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")) 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 node ${libPortPrefix}_dout_0 = ${libPortPrefix}_dout_0_0
${libPortPrefix}_dout <= mux(UInt<1>("h1"), ${libPortPrefix}_dout_0, UInt<${libWidth}>("h0")) ${libPortPrefix}_dout <= mux(UInt<1>("h1"), ${libPortPrefix}_dout_0, UInt<${libWidth}>("h0"))
@@ -37,49 +37,66 @@ s"""
${libPortPrefix}_dout <= ram.RW_0.rdata ${libPortPrefix}_dout <= ram.RW_0.rdata
ram.RW_0.wdata <= ${libPortPrefix}_din ram.RW_0.wdata <= ${libPortPrefix}_din
""" """
} }
// If there is no lib, put the flops definition into the body. // If there is no lib, put the flops definition into the body.
abstract override def generateBody = { abstract override def generateBody = {
if (this.isInstanceOf[HasNoLibTestGenerator]) generateFlops else super.generateBody if (this.isInstanceOf[HasNoLibTestGenerator]) generateFlops else super.generateBody
} }
// If there is no lib, don't generate a footer, since the flops definition // If there is no lib, don't generate a footer, since the flops definition
// will be in the body. // will be in the body.
override def generateFooter = { override def generateFooter = {
if (this.isInstanceOf[HasNoLibTestGenerator]) "" else if (this.isInstanceOf[HasNoLibTestGenerator]) ""
s""" else
s"""
module ${lib_name} : module ${lib_name} :
${generateFooterPorts} ${generateFooterPorts}
${generateFlops} ${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 memDepth = BigInt(2048)
override lazy val memWidth = 8 override lazy val memWidth = 8
compileExecuteAndTest(mem, None, v, output, true) 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 memDepth = BigInt(2048)
override lazy val memWidth = 16 override lazy val memWidth = 16
compileExecuteAndTest(mem, None, v, output, true) 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 memDepth = BigInt(8192)
override lazy val memWidth = 16 override lazy val memWidth = 16
compileExecuteAndTest(mem, None, v, output, true) 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 memDepth = BigInt(2048)
override lazy val libDepth = BigInt(1024) override lazy val libDepth = BigInt(1024)
override lazy val width = 16 override lazy val width = 16
@@ -87,7 +104,11 @@ class Synflops2048x16_depth_Lib extends MacroCompilerSpec with HasSRAMGenerator
compileExecuteAndTest(mem, lib, v, output, true) 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 memWidth = 64
override lazy val libWidth = 8 override lazy val libWidth = 8
override lazy val depth = BigInt(1024) override lazy val depth = BigInt(1024)
@@ -95,7 +116,11 @@ class Synflops2048x64_width_Lib extends MacroCompilerSpec with HasSRAMGenerator
compileExecuteAndTest(mem, lib, v, output, true) 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._ import mdf.macrolib._
override lazy val memDepth = BigInt(2048) 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 lazy val width = 8
override def generateLibSRAM = SRAMMacro( override def generateLibSRAM = SRAMMacro(
name=lib_name, name = lib_name,
width=width, width = width,
depth=libDepth, depth = libDepth,
family="1r1w", family = "1r1w",
ports=Seq( ports = Seq(
generateReadPort("innerA", width, libDepth), generateReadPort("innerA", width, libDepth),
generateWritePort("innerB", width, libDepth) generateWritePort("innerB", width, libDepth)
) )
) )
override def generateMemSRAM = SRAMMacro( override def generateMemSRAM = SRAMMacro(
name=mem_name, name = mem_name,
width=width, width = width,
depth=memDepth, depth = memDepth,
family="1r1w", family = "1r1w",
ports=Seq( ports = Seq(
generateReadPort("outerB", width, memDepth), generateReadPort("outerB", width, memDepth),
generateWritePort("outerA", width, memDepth) generateWritePort("outerA", width, memDepth)
) )
) )
override def generateHeader = override def generateHeader =
""" """
circuit target_memory : circuit target_memory :
module target_memory : module target_memory :
input outerB_addr : UInt<11> input outerB_addr : UInt<11>
@@ -138,7 +163,7 @@ circuit target_memory :
""" """
override def generateBody = override def generateBody =
""" """
node outerB_addr_sel = bits(outerB_addr, 10, 10) node outerB_addr_sel = bits(outerB_addr, 10, 10)
reg outerB_addr_sel_reg : UInt<1>, outerB_clk with : reg outerB_addr_sel_reg : UInt<1>, outerB_clk with :
reset => (UInt<1>("h0"), outerB_addr_sel_reg) reset => (UInt<1>("h0"), outerB_addr_sel_reg)
@@ -166,7 +191,7 @@ circuit target_memory :
""" """
override def generateFooterPorts = override def generateFooterPorts =
""" """
input innerA_addr : UInt<10> input innerA_addr : UInt<10>
input innerA_clk : Clock input innerA_clk : Clock
output innerA_dout : UInt<8> output innerA_dout : UInt<8>
@@ -177,7 +202,7 @@ circuit target_memory :
""" """
override def generateFlops = override def generateFlops =
""" """
inst mem_0_0 of split_awesome_lib_mem inst mem_0_0 of split_awesome_lib_mem
mem_0_0.innerB_clk <= innerB_clk mem_0_0.innerB_clk <= innerB_clk
mem_0_0.innerB_addr <= innerB_addr 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._ import mdf.macrolib._
override lazy val memDepth = BigInt(2048) 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 lazy val libMaskGran = Some(1)
override def generateLibSRAM = SRAMMacro( override def generateLibSRAM = SRAMMacro(
name=lib_name, name = lib_name,
width=width, width = width,
depth=libDepth, depth = libDepth,
family="1r1w", family = "1r1w",
ports=Seq( ports = Seq(
generateReadPort("innerA", width, libDepth), generateReadPort("innerA", width, libDepth),
generateWritePort("innerB", width, libDepth, libMaskGran) generateWritePort("innerB", width, libDepth, libMaskGran)
) )
) )
override def generateMemSRAM = SRAMMacro( override def generateMemSRAM = SRAMMacro(
name=mem_name, name = mem_name,
width=width, width = width,
depth=memDepth, depth = memDepth,
family="1r1w", family = "1r1w",
ports=Seq( ports = Seq(
generateReadPort("outerB", width, memDepth), generateReadPort("outerB", width, memDepth),
generateWritePort("outerA", width, memDepth, memMaskGran) generateWritePort("outerA", width, memDepth, memMaskGran)
) )
) )
override def generateHeader = override def generateHeader =
""" """
circuit target_memory : circuit target_memory :
module target_memory : module target_memory :
input outerB_addr : UInt<11> input outerB_addr : UInt<11>
@@ -268,7 +297,7 @@ circuit target_memory :
""" """
override def generateBody = override def generateBody =
""" """
node outerB_addr_sel = bits(outerB_addr, 10, 10) node outerB_addr_sel = bits(outerB_addr, 10, 10)
reg outerB_addr_sel_reg : UInt<1>, outerB_clk with : reg outerB_addr_sel_reg : UInt<1>, outerB_clk with :
reset => (UInt<1>("h0"), outerB_addr_sel_reg) reset => (UInt<1>("h0"), outerB_addr_sel_reg)
@@ -298,7 +327,7 @@ circuit target_memory :
""" """
override def generateFooterPorts = override def generateFooterPorts =
""" """
input innerA_addr : UInt<10> input innerA_addr : UInt<10>
input innerA_clk : Clock input innerA_clk : Clock
output innerA_dout : UInt<8> output innerA_dout : UInt<8>
@@ -310,7 +339,7 @@ circuit target_memory :
""" """
override def generateFlops = override def generateFlops =
""" """
inst mem_0_0 of split_awesome_lib_mem inst mem_0_0 of split_awesome_lib_mem
inst mem_0_1 of split_awesome_lib_mem inst mem_0_1 of split_awesome_lib_mem
inst mem_0_2 of split_awesome_lib_mem inst mem_0_2 of split_awesome_lib_mem

View File

@@ -2,15 +2,14 @@
package barstools.tapeout.transforms package barstools.tapeout.transforms
import firrtl._
import firrtl.ir._
import firrtl.Mappers._ import firrtl.Mappers._
import firrtl.annotations.{ModuleTarget, SingleTargetAnnotation, CircuitTarget} import firrtl._
import firrtl.stage.TransformManager.{TransformDependency} import firrtl.annotations.{CircuitTarget, ModuleTarget, SingleTargetAnnotation}
import firrtl.stage.{Forms} import firrtl.ir._
import firrtl.stage.Forms
import firrtl.stage.TransformManager.TransformDependency
case class KeepNameAnnotation(target: ModuleTarget) case class KeepNameAnnotation(target: ModuleTarget) extends SingleTargetAnnotation[ModuleTarget] {
extends SingleTargetAnnotation[ModuleTarget] {
def duplicate(n: ModuleTarget) = this.copy(n) def duplicate(n: ModuleTarget) = this.copy(n)
} }
@@ -21,8 +20,8 @@ case class ModuleNameSuffixAnnotation(target: CircuitTarget, suffix: String)
class AddSuffixToModuleNames extends Transform with DependencyAPIMigration { class AddSuffixToModuleNames extends Transform with DependencyAPIMigration {
override def prerequisites: Seq[TransformDependency] = Forms.LowForm override def prerequisites: Seq[TransformDependency] = Forms.LowForm
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters
override def invalidates(a: Transform): Boolean = false override def invalidates(a: Transform): Boolean = false
@@ -37,7 +36,7 @@ class AddSuffixToModuleNames extends Transform with DependencyAPIMigration {
val excludeSet = state.circuit.modules.flatMap { val excludeSet = state.circuit.modules.flatMap {
case e: ExtModule => Some(e.name) case e: ExtModule => Some(e.name)
case m if (m.name == state.circuit.main) => Some(m.name) case m if (m.name == state.circuit.main) => Some(m.name)
case _ => None case _ => None
}.toSet }.toSet
val renamer = { (name: String) => if (excludeSet(name)) name else name + suffix } val renamer = { (name: String) => if (excludeSet(name)) name else name + suffix }

View File

@@ -3,18 +3,18 @@
package barstools.tapeout.transforms package barstools.tapeout.transforms
import firrtl._ import firrtl._
import firrtl.annotations.NoTargetAnnotation
import firrtl.ir._ import firrtl.ir._
import firrtl.annotations.{NoTargetAnnotation} import firrtl.options.Dependency
import firrtl.options.{Dependency} import firrtl.passes.memlib.ReplSeqMem
import firrtl.stage.TransformManager.{TransformDependency} import firrtl.stage.Forms
import firrtl.stage.{Forms} import firrtl.stage.TransformManager.TransformDependency
import firrtl.passes.memlib.{ReplSeqMem}
case class LinkExtModulesAnnotation(mustLink: Seq[ExtModule]) extends NoTargetAnnotation case class LinkExtModulesAnnotation(mustLink: Seq[ExtModule]) extends NoTargetAnnotation
class AvoidExtModuleCollisions extends Transform with DependencyAPIMigration { 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 optionalPrerequisites: Seq[TransformDependency] = Seq(Dependency[RemoveUnusedModules])
override def optionalPrerequisiteOf: Seq[TransformDependency] = { override def optionalPrerequisiteOf: Seq[TransformDependency] = {
Forms.HighEmitters :+ Dependency[ReplSeqMem] Forms.HighEmitters :+ Dependency[ReplSeqMem]
@@ -24,10 +24,9 @@ class AvoidExtModuleCollisions extends Transform with DependencyAPIMigration {
def execute(state: CircuitState): CircuitState = { def execute(state: CircuitState): CircuitState = {
val mustLink = state.annotations.flatMap { val mustLink = state.annotations.flatMap {
case LinkExtModulesAnnotation(mustLink) => mustLink case LinkExtModulesAnnotation(mustLink) => mustLink
case _ => Nil case _ => Nil
} }
val newAnnos = state.annotations.filterNot(_.isInstanceOf[LinkExtModulesAnnotation]) val newAnnos = state.annotations.filterNot(_.isInstanceOf[LinkExtModulesAnnotation])
state.copy(circuit = state.circuit.copy(modules = state.circuit.modules ++ mustLink), annotations = newAnnos) state.copy(circuit = state.circuit.copy(modules = state.circuit.modules ++ mustLink), annotations = newAnnos)
} }
} }

View File

@@ -3,15 +3,14 @@
package barstools.tapeout.transforms package barstools.tapeout.transforms
import firrtl._ import firrtl._
import firrtl.annotations.{ModuleTarget, ReferenceTarget, SingleTargetAnnotation}
import firrtl.ir._ import firrtl.ir._
import firrtl.annotations.{ModuleTarget, SingleTargetAnnotation, ReferenceTarget} import firrtl.options.Dependency
import firrtl.stage.TransformManager.{TransformDependency} import firrtl.passes.memlib.ReplSeqMem
import firrtl.stage.{Forms} import firrtl.stage.Forms
import firrtl.options.{Dependency} import firrtl.stage.TransformManager.TransformDependency
import firrtl.passes.memlib.{ReplSeqMem}
case class ConvertToExtModAnnotation(target: ModuleTarget) case class ConvertToExtModAnnotation(target: ModuleTarget) extends SingleTargetAnnotation[ModuleTarget] {
extends SingleTargetAnnotation[ModuleTarget] {
def duplicate(n: ModuleTarget) = this.copy(n) def duplicate(n: ModuleTarget) = this.copy(n)
} }
@@ -20,7 +19,7 @@ case class ConvertToExtModAnnotation(target: ModuleTarget)
// otherwise it's left alone. // otherwise it's left alone.
class ConvertToExtMod extends Transform with DependencyAPIMigration { 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 optionalPrerequisites: Seq[TransformDependency] = Seq.empty
override def optionalPrerequisiteOf: Seq[TransformDependency] = { override def optionalPrerequisiteOf: Seq[TransformDependency] = {
Forms.HighEmitters ++ Seq(Dependency[RemoveUnusedModules], Dependency[ReplSeqMem]) 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) 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 prerequisites: Seq[TransformDependency] = Forms.LowForm
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters
override def invalidates(a: Transform): Boolean = false override def invalidates(a: Transform): Boolean = false

View File

@@ -5,7 +5,7 @@ import firrtl.annotations._
import firrtl.ir._ import firrtl.ir._
import firrtl.passes.memlib.ReplSeqMemAnnotation import firrtl.passes.memlib.ReplSeqMemAnnotation
import firrtl.stage.FirrtlCircuitAnnotation import firrtl.stage.FirrtlCircuitAnnotation
import firrtl.transforms.{BlackBoxResourceFileNameAnno, DedupModules} import firrtl.transforms.BlackBoxResourceFileNameAnno
import logger.LazyLogging import logger.LazyLogging
trait HasTapeoutOptions { self: ExecutionOptionsManager with HasFirrtlOptions => trait HasTapeoutOptions { self: ExecutionOptionsManager with HasFirrtlOptions =>
@@ -13,130 +13,150 @@ trait HasTapeoutOptions { self: ExecutionOptionsManager with HasFirrtlOptions =>
parser.note("tapeout options") parser.note("tapeout options")
parser.opt[String]("harness-o") parser
.opt[String]("harness-o")
.abbr("tho") .abbr("tho")
.valueName("<harness-output>") .valueName("<harness-output>")
.foreach { x => .foreach { x =>
tapeoutOptions = tapeoutOptions.copy( tapeoutOptions = tapeoutOptions.copy(
harnessOutput = Some(x) harnessOutput = Some(x)
) )
}.text { }
.text {
"use this to generate a harness at <harness-output>" "use this to generate a harness at <harness-output>"
} }
parser.opt[String]("syn-top") parser
.opt[String]("syn-top")
.abbr("tst") .abbr("tst")
.valueName("<syn-top>") .valueName("<syn-top>")
.foreach { x => .foreach { x =>
tapeoutOptions = tapeoutOptions.copy( tapeoutOptions = tapeoutOptions.copy(
synTop = Some(x) synTop = Some(x)
) )
}.text { }
.text {
"use this to set synTop" "use this to set synTop"
} }
parser.opt[String]("top-fir") parser
.opt[String]("top-fir")
.abbr("tsf") .abbr("tsf")
.valueName("<top-fir>") .valueName("<top-fir>")
.foreach { x => .foreach { x =>
tapeoutOptions = tapeoutOptions.copy( tapeoutOptions = tapeoutOptions.copy(
topFir = Some(x) topFir = Some(x)
) )
}.text { }
.text {
"use this to set topFir" "use this to set topFir"
} }
parser.opt[String]("top-anno-out") parser
.opt[String]("top-anno-out")
.abbr("tsaof") .abbr("tsaof")
.valueName("<top-anno-out>") .valueName("<top-anno-out>")
.foreach { x => .foreach { x =>
tapeoutOptions = tapeoutOptions.copy( tapeoutOptions = tapeoutOptions.copy(
topAnnoOut = Some(x) topAnnoOut = Some(x)
) )
}.text { }
.text {
"use this to set topAnnoOut" "use this to set topAnnoOut"
} }
parser.opt[String]("top-dotf-out") parser
.opt[String]("top-dotf-out")
.abbr("tdf") .abbr("tdf")
.valueName("<top-dotf-out>") .valueName("<top-dotf-out>")
.foreach { x => .foreach { x =>
tapeoutOptions = tapeoutOptions.copy( tapeoutOptions = tapeoutOptions.copy(
topDotfOut = Some(x) topDotfOut = Some(x)
) )
}.text { }
.text {
"use this to set the filename for the top resource .f file" "use this to set the filename for the top resource .f file"
} }
parser.opt[String]("harness-top") parser
.opt[String]("harness-top")
.abbr("tht") .abbr("tht")
.valueName("<harness-top>") .valueName("<harness-top>")
.foreach { x => .foreach { x =>
tapeoutOptions = tapeoutOptions.copy( tapeoutOptions = tapeoutOptions.copy(
harnessTop = Some(x) harnessTop = Some(x)
) )
}.text { }
.text {
"use this to set harnessTop" "use this to set harnessTop"
} }
parser.opt[String]("harness-fir") parser
.opt[String]("harness-fir")
.abbr("thf") .abbr("thf")
.valueName("<harness-fir>") .valueName("<harness-fir>")
.foreach { x => .foreach { x =>
tapeoutOptions = tapeoutOptions.copy( tapeoutOptions = tapeoutOptions.copy(
harnessFir = Some(x) harnessFir = Some(x)
) )
}.text { }
.text {
"use this to set harnessFir" "use this to set harnessFir"
} }
parser.opt[String]("harness-anno-out") parser
.opt[String]("harness-anno-out")
.abbr("thaof") .abbr("thaof")
.valueName("<harness-anno-out>") .valueName("<harness-anno-out>")
.foreach { x => .foreach { x =>
tapeoutOptions = tapeoutOptions.copy( tapeoutOptions = tapeoutOptions.copy(
harnessAnnoOut = Some(x) harnessAnnoOut = Some(x)
) )
}.text { }
.text {
"use this to set harnessAnnoOut" "use this to set harnessAnnoOut"
} }
parser.opt[String]("harness-dotf-out") parser
.opt[String]("harness-dotf-out")
.abbr("hdf") .abbr("hdf")
.valueName("<harness-dotf-out>") .valueName("<harness-dotf-out>")
.foreach { x => .foreach { x =>
tapeoutOptions = tapeoutOptions.copy( tapeoutOptions = tapeoutOptions.copy(
harnessDotfOut = Some(x) harnessDotfOut = Some(x)
) )
}.text { }
.text {
"use this to set the filename for the harness resource .f file" "use this to set the filename for the harness resource .f file"
} }
parser.opt[String]("harness-conf") parser
.opt[String]("harness-conf")
.abbr("thconf") .abbr("thconf")
.valueName ("<harness-conf-file>") .valueName("<harness-conf-file>")
.foreach { x => .foreach { x =>
tapeoutOptions = tapeoutOptions.copy( tapeoutOptions = tapeoutOptions.copy(
harnessConf = Some(x) harnessConf = Some(x)
) )
}.text { }
.text {
"use this to set the harness conf file location" "use this to set the harness conf file location"
} }
} }
case class TapeoutOptions( case class TapeoutOptions(
harnessOutput: Option[String] = None, harnessOutput: Option[String] = None,
synTop: Option[String] = None, synTop: Option[String] = None,
topFir: Option[String] = None, topFir: Option[String] = None,
topAnnoOut: Option[String] = None, topAnnoOut: Option[String] = None,
topDotfOut: Option[String] = None, topDotfOut: Option[String] = None,
harnessTop: Option[String] = None, harnessTop: Option[String] = None,
harnessFir: Option[String] = None, harnessFir: Option[String] = None,
harnessAnnoOut: Option[String] = None, harnessAnnoOut: Option[String] = None,
harnessDotfOut: Option[String] = None, harnessDotfOut: Option[String] = None,
harnessConf: Option[String] = None harnessConf: Option[String] = None)
) extends LazyLogging extends LazyLogging
// Requires two phases, one to collect modules below synTop in the hierarchy // Requires two phases, one to collect modules below synTop in the hierarchy
// and a second to remove those modules to generate the test harness // 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 => annoFile.foreach { annoPath =>
val outputFile = new java.io.PrintWriter(annoPath) val outputFile = new java.io.PrintWriter(annoPath)
outputFile.write(JsonProtocol.serialize(res.circuitState.annotations.filter(_ match { outputFile.write(JsonProtocol.serialize(res.circuitState.annotations.filter(_ match {
case da: DeletedAnnotation => false case da: DeletedAnnotation => false
case ec: EmittedComponent => false case ec: EmittedComponent => false
case ea: EmittedAnnotation[_] => false case ea: EmittedAnnotation[_] => false
case fca: FirrtlCircuitAnnotation => false case fca: FirrtlCircuitAnnotation => false
case _ => true case _ => true
}))) })))
@@ -207,7 +227,7 @@ sealed trait GenerateTopAndHarnessApp extends LazyLogging { this: App =>
result match { result match {
case x: FirrtlExecutionSuccess => case x: FirrtlExecutionSuccess =>
dump(x, tapeoutOptions.topFir, tapeoutOptions.topAnnoOut) 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 => case x =>
throw new Exception(s"executeTop failed while executing FIRRTL!\n${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 = val harnessAnnos =
tapeoutOptions.harnessDotfOut.map(BlackBoxResourceFileNameAnno(_)).toSeq ++ tapeoutOptions.harnessDotfOut.map(BlackBoxResourceFileNameAnno(_)).toSeq ++
harnessTop.map(ht => ModuleNameSuffixAnnotation(rootCircuitTarget, s"_in${ht}")) ++ harnessTop.map(ht => ModuleNameSuffixAnnotation(rootCircuitTarget, s"_in${ht}")) ++
synTop.map(st => ConvertToExtModAnnotation(rootCircuitTarget.module(st))) :+ synTop.map(st => ConvertToExtModAnnotation(rootCircuitTarget.module(st))) :+
LinkExtModulesAnnotation(topExtModules) LinkExtModulesAnnotation(topExtModules)
// For harness run, change some firrtlOptions (below) for harness phase // For harness run, change some firrtlOptions (below) for harness phase
// customTransforms: setup harness transforms, add AvoidExtModuleCollisions // customTransforms: setup harness transforms, add AvoidExtModuleCollisions
@@ -233,7 +253,7 @@ sealed trait GenerateTopAndHarnessApp extends LazyLogging { this: App =>
outputFileNameOverride = tapeoutOptions.harnessOutput.get, outputFileNameOverride = tapeoutOptions.harnessOutput.get,
annotations = firrtlOptions.annotations.map({ annotations = firrtlOptions.annotations.map({
case ReplSeqMemAnnotation(i, o) => ReplSeqMemAnnotation(i, tapeoutOptions.harnessConf.get) case ReplSeqMemAnnotation(i, o) => ReplSeqMemAnnotation(i, tapeoutOptions.harnessConf.get)
case a => a case a => a
}) ++ harnessAnnos }) ++ harnessAnnos
) )
val harnessResult = firrtl.Driver.execute(optionsManager) val harnessResult = firrtl.Driver.execute(optionsManager)

View File

@@ -3,20 +3,18 @@
package barstools.tapeout.transforms package barstools.tapeout.transforms
import firrtl._ import firrtl._
import firrtl.ir._
import firrtl.annotations._ import firrtl.annotations._
import firrtl.options.{Dependency} import firrtl.options.Dependency
import firrtl.stage.TransformManager.{TransformDependency} import firrtl.stage.Forms
import firrtl.stage.{Forms} import firrtl.stage.TransformManager.TransformDependency
case class ReParentCircuitAnnotation(target: ModuleTarget) case class ReParentCircuitAnnotation(target: ModuleTarget) extends SingleTargetAnnotation[ModuleTarget] {
extends SingleTargetAnnotation[ModuleTarget] {
def duplicate(n: ModuleTarget) = this.copy(n) def duplicate(n: ModuleTarget) = this.copy(n)
} }
class ReParentCircuit extends Transform with DependencyAPIMigration { 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 optionalPrerequisites: Seq[TransformDependency] = Seq.empty
override def optionalPrerequisiteOf: Seq[TransformDependency] = { override def optionalPrerequisiteOf: Seq[TransformDependency] = {
Forms.HighEmitters :+ Dependency[RemoveUnusedModules] Forms.HighEmitters :+ Dependency[RemoveUnusedModules]
@@ -25,8 +23,8 @@ class ReParentCircuit extends Transform with DependencyAPIMigration {
def execute(state: CircuitState): CircuitState = { def execute(state: CircuitState): CircuitState = {
val c = state.circuit val c = state.circuit
val newTopName = state.annotations.collectFirst { val newTopName = state.annotations.collectFirst { case ReParentCircuitAnnotation(tgt) =>
case ReParentCircuitAnnotation(tgt) => tgt.module tgt.module
} }
val newCircuit = c.copy(main = newTopName.getOrElse(c.main)) val newCircuit = c.copy(main = newTopName.getOrElse(c.main))
val mainRename = newTopName.map { s => val mainRename = newTopName.map { s =>

View File

@@ -3,18 +3,18 @@
package barstools.tapeout.transforms package barstools.tapeout.transforms
import firrtl._ import firrtl._
import firrtl.annotations.ModuleTarget
import firrtl.ir._ import firrtl.ir._
import firrtl.annotations.{ModuleTarget} import firrtl.options.Dependency
import firrtl.stage.TransformManager.{TransformDependency} import firrtl.passes.memlib.ReplSeqMem
import firrtl.options.{Dependency} import firrtl.stage.Forms
import firrtl.stage.{Forms} import firrtl.stage.TransformManager.TransformDependency
import firrtl.passes.memlib.{ReplSeqMem}
// Removes all the unused modules in a circuit by recursing through every // Removes all the unused modules in a circuit by recursing through every
// instance (starting at the main module) // instance (starting at the main module)
class RemoveUnusedModules extends Transform with DependencyAPIMigration { 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 optionalPrerequisites: Seq[TransformDependency] = Seq.empty
override def optionalPrerequisiteOf: Seq[TransformDependency] = { override def optionalPrerequisiteOf: Seq[TransformDependency] = {
Forms.HighEmitters :+ Dependency[ReplSeqMem] Forms.HighEmitters :+ Dependency[ReplSeqMem]
@@ -22,8 +22,8 @@ class RemoveUnusedModules extends Transform with DependencyAPIMigration {
override def invalidates(a: Transform): Boolean = false override def invalidates(a: Transform): Boolean = false
def execute(state: CircuitState): CircuitState = { def execute(state: CircuitState): CircuitState = {
val modulesByName = state.circuit.modules.map{ val modulesByName = state.circuit.modules.map {
case m: Module => (m.name, Some(m)) case m: Module => (m.name, Some(m))
case m: ExtModule => (m.name, None) case m: ExtModule => (m.name, None)
}.toMap }.toMap
@@ -33,7 +33,7 @@ class RemoveUnusedModules extends Transform with DependencyAPIMigration {
def someStatements(statement: Statement): Seq[Statement] = def someStatements(statement: Statement): Seq[Statement] =
statement match { statement match {
case b: Block => case b: Block =>
b.stmts.map{ someStatements(_) } b.stmts.map { someStatements(_) }
.foldLeft(Seq[Statement]())(_ ++ _) .foldLeft(Seq[Statement]())(_ ++ _)
case when: Conditionally => case when: Conditionally =>
someStatements(when.conseq) ++ someStatements(when.alt) someStatements(when.conseq) ++ someStatements(when.alt)
@@ -41,11 +41,11 @@ class RemoveUnusedModules extends Transform with DependencyAPIMigration {
case _ => Seq() case _ => Seq()
} }
someStatements(m.body).map{ someStatements(m.body).map {
case s: DefInstance => Set(s.module) | getUsedModules(modulesByName(s.module)) case s: DefInstance => Set(s.module) | getUsedModules(modulesByName(s.module))
case _ => Set[String]() case _ => Set[String]()
}.foldLeft(Set(m.name))(_ | _) }.foldLeft(Set(m.name))(_ | _)
} }
case None => Set.empty[String] case None => Set.empty[String]
} }
@@ -57,7 +57,7 @@ class RemoveUnusedModules extends Transform with DependencyAPIMigration {
val renames = state.renames.getOrElse(RenameMap()) 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) 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 // Only works on Modules with a Bool port named reset
def invertReset(mod: Module): Module = { def invertReset(mod: Module): Module = {
// Check that it actually has reset // Check that it actually has reset
require(mod.ports.exists(p => p.name == "reset" && p.tpe == Bool), require(mod.ports.exists(p => p.name == "reset" && p.tpe == Bool), "Can only invert reset on a module with reset!")
"Can only invert reset on a module with reset!")
// Rename "reset" to "reset_n" // Rename "reset" to "reset_n"
val portsx = mod.ports map { val portsx = mod.ports.map {
case Port(info, "reset", Input, Bool) => case Port(info, "reset", Input, Bool) =>
Port(info, "reset_n", Input, Bool) Port(info, "reset_n", Input, Bool)
case other => other case other => other
@@ -34,7 +33,7 @@ object ResetN extends Pass {
} }
def run(c: Circuit): Circuit = { 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 mod: Module if mod.name == c.main => invertReset(mod)
case other => other case other => other
}) })
@@ -43,8 +42,8 @@ object ResetN extends Pass {
class ResetInverterTransform extends Transform with DependencyAPIMigration { class ResetInverterTransform extends Transform with DependencyAPIMigration {
override def prerequisites: Seq[TransformDependency] = Forms.LowForm override def prerequisites: Seq[TransformDependency] = Forms.LowForm
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters
override def invalidates(a: Transform): Boolean = false override def invalidates(a: Transform): Boolean = false
@@ -64,7 +63,7 @@ trait ResetInverter {
def invert[T <: chisel3.internal.LegacyModule](module: T): Unit = { def invert[T <: chisel3.internal.LegacyModule](module: T): Unit = {
chisel3.experimental.annotate(new chisel3.experimental.ChiselAnnotation with RunFirrtlTransform { chisel3.experimental.annotate(new chisel3.experimental.ChiselAnnotation with RunFirrtlTransform {
def transformClass: Class[_ <: Transform] = classOf[ResetInverterTransform] 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 { class RetimeTransform extends Transform with DependencyAPIMigration {
override def prerequisites: Seq[TransformDependency] = Forms.LowForm override def prerequisites: Seq[TransformDependency] = Forms.LowForm
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters
override def invalidates(a: Transform): Boolean = false override def invalidates(a: Transform): Boolean = false
override def execute(state: CircuitState): CircuitState = { override def execute(state: CircuitState): CircuitState = {
state.annotations.filter(_.isInstanceOf[RetimeAnnotation]) match { state.annotations.filter(_.isInstanceOf[RetimeAnnotation]) match {
case Nil => state case Nil => state
case seq => seq.foreach { case seq =>
case RetimeAnnotation(ModuleName(module, CircuitName(_))) => seq.foreach {
logger.info(s"Retiming module $module") case RetimeAnnotation(ModuleName(module, CircuitName(_))) =>
case RetimeAnnotation(ComponentName(name, ModuleName(module, CircuitName(_)))) => logger.info(s"Retiming module $module")
logger.info(s"Retiming instance $module.$name") case RetimeAnnotation(ComponentName(name, ModuleName(module, CircuitName(_)))) =>
case _ => logger.info(s"Retiming instance $module.$name")
throw new Exception(s"There should be RetimeAnnotations, got ${seq.mkString(" -- ")}") case _ =>
} throw new Exception(s"There should be RetimeAnnotations, got ${seq.mkString(" -- ")}")
state }
state
} }
} }
} }
@@ -41,7 +42,7 @@ trait RetimeLib {
def retime[T <: chisel3.internal.LegacyModule](module: T): Unit = { def retime[T <: chisel3.internal.LegacyModule](module: T): Unit = {
chisel3.experimental.annotate(new chisel3.experimental.ChiselAnnotation with RunFirrtlTransform { chisel3.experimental.annotate(new chisel3.experimental.ChiselAnnotation with RunFirrtlTransform {
def transformClass: Class[_ <: Transform] = classOf[RetimeTransform] 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. // See LICENSE for license details.
package barstools.tapeout.transforms package barstools.tapeout.transforms.utils
import chisel3.experimental.{ChiselAnnotation, annotate} import chisel3.experimental.{ChiselAnnotation, annotate}
import firrtl._ import firrtl._
@@ -12,7 +12,7 @@ import firrtl.transforms.BlackBoxTargetDirAnno
object WriteConfig { object WriteConfig {
def apply(dir: String, file: String, contents: String): Unit = { def apply(dir: String, file: String, contents: String): Unit = {
val writer = new java.io.PrintWriter(new java.io.File(s"$dir/$file")) val writer = new java.io.PrintWriter(new java.io.File(s"$dir/$file"))
writer write contents writer.write(contents)
writer.close() writer.close()
} }
} }
@@ -22,14 +22,14 @@ object GetTargetDir {
val annos = state.annotations val annos = state.annotations
val destDir = annos.map { val destDir = annos.map {
case BlackBoxTargetDirAnno(s) => Some(s) case BlackBoxTargetDirAnno(s) => Some(s)
case _ => None case _ => None
}.flatten }.flatten
val loc = { val loc = {
if (destDir.isEmpty) "." if (destDir.isEmpty) "."
else destDir.head else destDir.head
} }
val targetDir = new java.io.File(loc) val targetDir = new java.io.File(loc)
if(!targetDir.exists()) FileUtils.makeDirectory(targetDir.getAbsolutePath) if (!targetDir.exists()) FileUtils.makeDirectory(targetDir.getAbsolutePath)
loc loc
} }
} }
@@ -53,8 +53,8 @@ case class TechnologyLocationAnnotation(dir: String) extends SingleTargetAnnotat
class TechnologyLocation extends Transform with DependencyAPIMigration { class TechnologyLocation extends Transform with DependencyAPIMigration {
override def prerequisites: Seq[TransformDependency] = Forms.LowForm override def prerequisites: Seq[TransformDependency] = Forms.LowForm
override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized override def optionalPrerequisites: Seq[TransformDependency] = Forms.LowFormOptimized
override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters override def optionalPrerequisiteOf: Seq[TransformDependency] = Forms.LowEmitters
def execute(state: CircuitState): CircuitState = { def execute(state: CircuitState): CircuitState = {
@@ -65,18 +65,15 @@ class TechnologyLocation extends Transform with DependencyAPIMigration {
val annos = state.annotations val annos = state.annotations
val dir = annos.flatMap { val dir = annos.flatMap {
case TechnologyLocationAnnotation(dir) => Some(dir) case TechnologyLocationAnnotation(dir) => Some(dir)
case _ => None case _ => None
} }
dir.length match { dir.length match {
case 0 => "" case 0 => ""
case 1 => case 1 =>
val targetDir = new java.io.File(dir.head) 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 dir.head
case _ => throw new Exception("Only 1 tech directory annotation allowed!") 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 { 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 chisel3._
import scala.collection.immutable.ListMap import scala.collection.immutable.ListMap
class CustomBundle[T <: Data](elts: (String, T)*) extends Record { 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: 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] override def cloneType = (new CustomBundle(elements.toList: _*)).asInstanceOf[this.type]
} }
class CustomIndexedBundle[T <: Data](elts: (Int, T)*) extends Record { class CustomIndexedBundle[T <: Data](elts: (Int, T)*) extends Record {
// Must be String, Data // 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?) // 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) def apply(elt: Int): T = elements(elt.toString)
override def cloneType = (new CustomIndexedBundle(indexedElements.toList: _*)).asInstanceOf[this.type] 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 { object CustomIndexedBundle {
def apply[T <: Data](gen: T, idxs: Seq[Int]) = new CustomIndexedBundle(idxs.map(_ -> gen): _*) def apply[T <: Data](gen: T, idxs: Seq[Int]) = new CustomIndexedBundle(idxs.map(_ -> gen): _*)
// Allows Vecs of elements of different types/widths // 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 net.jcazevedo.moultingyaml._
import java.io.File import java.io.File
class YamlFileReader(resource: String) { 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 // If the user doesn't provide a Yaml file name, use defaults
val yamlString = file match { val yamlString = file match {
case f if f.isEmpty => case f if f.isEmpty =>
// Use example config if no file is provided // Use example config if no file is provided
val stream = getClass.getResourceAsStream(resource) val stream = getClass.getResourceAsStream(resource)
io.Source.fromInputStream(stream).mkString 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") scala.io.Source.fromFile(f).getLines.mkString("\n")
case _ => case _ =>
throw new Exception("No valid Yaml file found!") throw new Exception("No valid Yaml file found!")
} }
yamlString.parseYamls.map(x => reader.read(x)) yamlString.parseYamls.map(x => reader.read(x))
} }
} }

View File

@@ -3,9 +3,10 @@
package barstools.tapeout.transforms package barstools.tapeout.transforms
import chisel3._ import chisel3._
import chisel3.stage.{ChiselStage, ChiselGeneratorAnnotation} import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
import firrtl.{EmittedFirrtlCircuitAnnotation, EmittedFirrtlModuleAnnotation} 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 { class ExampleModuleNeedsResetInverted extends Module with ResetInverter {
val io = IO(new Bundle { val io = IO(new Bundle {
@@ -19,7 +20,7 @@ class ExampleModuleNeedsResetInverted extends Module with ResetInverter {
invert(this) 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 { "Inverting reset needs to be done throughout module in Chirrtl" in {
val chirrtl = (new ChiselStage).emitChirrtl(new ExampleModuleNeedsResetInverted) val chirrtl = (new ChiselStage).emitChirrtl(new ExampleModuleNeedsResetInverted)
chirrtl should include("input reset :") 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 { "Inverting reset needs to be done throughout module when generating firrtl" in {
// generate low-firrtl // generate low-firrtl
val firrtl = (new ChiselStage).execute( val firrtl = (new ChiselStage)
Array("-X", "low"), .execute(
Seq(ChiselGeneratorAnnotation(() => new ExampleModuleNeedsResetInverted)) Array("-X", "low"),
).collect { Seq(ChiselGeneratorAnnotation(() => new ExampleModuleNeedsResetInverted))
case EmittedFirrtlCircuitAnnotation(a) => a )
case EmittedFirrtlModuleAnnotation(a) => a .collect {
}.map(_.value) case EmittedFirrtlCircuitAnnotation(a) => a
.mkString("") case EmittedFirrtlModuleAnnotation(a) => a
}
.map(_.value)
.mkString("")
firrtl should include("input reset_n :") firrtl should include("input reset_n :")
firrtl should include("node reset = not(reset_n)") firrtl should include("node reset = not(reset_n)")

View File

@@ -1,16 +1,15 @@
// See LICENSE for license details. // See LICENSE for license details.
package barstools.tapeout.transforms.retime.test package barstools.tapeout.transforms.retime
import chisel3._ import chisel3._
import chisel3.stage.{ChiselStage, ChiselGeneratorAnnotation} import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
import firrtl.{EmittedFirrtlCircuitAnnotation, EmittedFirrtlModuleAnnotation} import firrtl.{EmittedFirrtlCircuitAnnotation, EmittedFirrtlModuleAnnotation, FileUtils}
import barstools.tapeout.transforms.retime.RetimeLib
import firrtl.FileUtils
import logger.Logger 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 = { def normalized(s: String): String = {
require(!s.contains("\n")) require(!s.contains("\n"))
s.replaceAll("\\s+", " ").trim 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 = { def getLowFirrtl[T <: RawModule](gen: () => T, extraArgs: Array[String] = Array.empty): String = {
// generate low firrtl // generate low firrtl
(new ChiselStage).execute( (new ChiselStage)
Array("-X", "low") ++ extraArgs, .execute(
Seq(ChiselGeneratorAnnotation(gen)) Array("-X", "low") ++ extraArgs,
).collect { Seq(ChiselGeneratorAnnotation(gen))
case EmittedFirrtlCircuitAnnotation(a) => a )
case EmittedFirrtlModuleAnnotation(a) => a .collect {
}.map(_.value) case EmittedFirrtlCircuitAnnotation(a) => a
.mkString("") case EmittedFirrtlModuleAnnotation(a) => a
}
.map(_.value)
.mkString("")
} }
behavior.of("retime library")
behavior of "retime library"
it should "pass simple retime module annotation" in { it should "pass simple retime module annotation" in {
val gen = () => new RetimeModule val gen = () => new RetimeModule
@@ -43,15 +44,18 @@ class RetimeSpec extends FlatSpec with Matchers {
Logger.setOutput(captor.printStream) Logger.setOutput(captor.printStream)
// generate low firrtl // generate low firrtl
val firrtl = getLowFirrtl(gen, val firrtl = getLowFirrtl(
Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final", "--log-level", "info")) gen,
Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final", "--log-level", "info")
)
firrtl.nonEmpty should be(true) firrtl.nonEmpty should be(true)
//Make sure we got the RetimeTransform scheduled //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) .map(normalized)
.mkString("\n") .mkString("\n")
lines should include("barstools.tapeout.transforms.retime.RetimeAnnotation") lines should include("barstools.tapeout.transforms.retime.RetimeAnnotation")
@@ -67,15 +71,18 @@ class RetimeSpec extends FlatSpec with Matchers {
Logger.setOutput(captor.printStream) Logger.setOutput(captor.printStream)
// generate low firrtl // generate low firrtl
val firrtl = getLowFirrtl(gen, val firrtl = getLowFirrtl(
Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final", "--log-level", "info")) gen,
Array("-td", s"test_run_dir/$dir", "-foaf", s"test_run_dir/$dir/final", "--log-level", "info")
)
firrtl.nonEmpty should be(true) firrtl.nonEmpty should be(true)
//Make sure we got the RetimeTransform scheduled //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) .map(normalized)
.mkString("\n") .mkString("\n")
lines should include("barstools.tapeout.transforms.retime.RetimeAnnotation") lines should include("barstools.tapeout.transforms.retime.RetimeAnnotation")