Merge pull request #97 from ucb-bar/fix-deprecations-1
Fix deprecations 1
This commit is contained in:
10
build.sbt
10
build.sbt
@@ -7,15 +7,14 @@ val defaultVersions = Map(
|
|||||||
|
|
||||||
lazy val commonSettings = Seq(
|
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)
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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 {
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
// See LICENSE for license details.
|
|
||||||
|
|
||||||
package barstools.macros
|
|
||||||
|
|
||||||
import firrtl._
|
|
||||||
import firrtl.ir._
|
|
||||||
import firrtl.Utils._
|
|
||||||
import firrtl.passes.MemPortUtils.{memPortField, memType}
|
|
||||||
import Utils._
|
|
||||||
|
|
||||||
class SynFlopsPass(synflops: Boolean, libs: Seq[Macro]) extends firrtl.passes.Pass {
|
|
||||||
val extraMods = scala.collection.mutable.ArrayBuffer.empty[Module]
|
|
||||||
lazy val libMods = (libs map { lib => lib.src.name -> {
|
|
||||||
val (dataType, dataWidth) = (lib.src.ports foldLeft (None: Option[BigInt]))((res, port) =>
|
|
||||||
(res, port.maskPort) match {
|
|
||||||
case (_, None) =>
|
|
||||||
res
|
|
||||||
case (None, Some(_)) =>
|
|
||||||
Some(port.effectiveMaskGran)
|
|
||||||
case (Some(x), Some(_)) =>
|
|
||||||
assert(x == port.effectiveMaskGran)
|
|
||||||
res
|
|
||||||
}
|
|
||||||
) match {
|
|
||||||
case None => (UIntType(IntWidth(lib.src.width)), lib.src.width)
|
|
||||||
case Some(gran) => (UIntType(IntWidth(gran)), gran.intValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
val maxDepth = min(lib.src.depth, 1<<26)
|
|
||||||
val numMems = lib.src.depth / maxDepth
|
|
||||||
|
|
||||||
// Change macro to be mapped onto to look like the below mem
|
|
||||||
// by changing its depth, and width
|
|
||||||
val lib_macro = new Macro(lib.src.copy(name="split_"+lib.src.name,
|
|
||||||
depth = maxDepth, width = dataWidth, ports = lib.src.ports.map(p =>
|
|
||||||
p.copy(width = p.width.map(_ => dataWidth), depth = p.depth.map(_ => maxDepth),
|
|
||||||
maskGran = p.maskGran.map(_ => dataWidth)))))
|
|
||||||
val mod_macro = (new MacroCompilerPass(None,None,None,None)).compile(lib, lib_macro)
|
|
||||||
val (real_mod, real_macro) = mod_macro.get
|
|
||||||
|
|
||||||
val mem = DefMemory(
|
|
||||||
NoInfo,
|
|
||||||
"ram",
|
|
||||||
dataType,
|
|
||||||
maxDepth,
|
|
||||||
1, // writeLatency
|
|
||||||
1, // readLatency. This is possible because of VerilogMemDelays
|
|
||||||
real_macro.readers.indices map (i => s"R_$i"),
|
|
||||||
real_macro.writers.indices map (i => s"W_$i"),
|
|
||||||
real_macro.readwriters.indices map (i => s"RW_$i")
|
|
||||||
)
|
|
||||||
|
|
||||||
val readConnects = real_macro.readers.zipWithIndex flatMap { case (r, i) =>
|
|
||||||
val clock = portToExpression(r.src.clock.get)
|
|
||||||
val address = portToExpression(r.src.address)
|
|
||||||
val enable = (r.src chipEnable, r.src readEnable) match {
|
|
||||||
case (Some(en_port), Some(re_port)) =>
|
|
||||||
and(portToExpression(en_port),
|
|
||||||
portToExpression(re_port))
|
|
||||||
case (Some(en_port), None) => portToExpression(en_port)
|
|
||||||
case (None, Some(re_port)) => portToExpression(re_port)
|
|
||||||
case (None, None) => one
|
|
||||||
}
|
|
||||||
val data = memPortField(mem, s"R_$i", "data")
|
|
||||||
val read = data
|
|
||||||
Seq(
|
|
||||||
Connect(NoInfo, memPortField(mem, s"R_$i", "clk"), clock),
|
|
||||||
Connect(NoInfo, memPortField(mem, s"R_$i", "addr"), address),
|
|
||||||
Connect(NoInfo, memPortField(mem, s"R_$i", "en"), enable),
|
|
||||||
Connect(NoInfo, WRef(r.src.output.get.name), read)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val writeConnects = real_macro.writers.zipWithIndex flatMap { case (w, i) =>
|
|
||||||
val clock = portToExpression(w.src.clock.get)
|
|
||||||
val address = portToExpression(w.src.address)
|
|
||||||
val enable = (w.src.chipEnable, w.src.writeEnable) match {
|
|
||||||
case (Some(en), Some(we)) =>
|
|
||||||
and(portToExpression(en),
|
|
||||||
portToExpression(we))
|
|
||||||
case (Some(en), None) => portToExpression(en)
|
|
||||||
case (None, Some(we)) => portToExpression(we)
|
|
||||||
case (None, None) => zero // is it possible?
|
|
||||||
}
|
|
||||||
val mask = w.src.maskPort match {
|
|
||||||
case Some(m) => portToExpression(m)
|
|
||||||
case None => one
|
|
||||||
}
|
|
||||||
val data = memPortField(mem, s"W_$i", "data")
|
|
||||||
val write = portToExpression(w.src.input.get)
|
|
||||||
Seq(
|
|
||||||
Connect(NoInfo, memPortField(mem, s"W_$i", "clk"), clock),
|
|
||||||
Connect(NoInfo, memPortField(mem, s"W_$i", "addr"), address),
|
|
||||||
Connect(NoInfo, memPortField(mem, s"W_$i", "en"), enable),
|
|
||||||
Connect(NoInfo, memPortField(mem, s"W_$i", "mask"), mask),
|
|
||||||
Connect(NoInfo, data, write)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val readwriteConnects = real_macro.readwriters.zipWithIndex flatMap { case (rw, i) =>
|
|
||||||
val clock = portToExpression(rw.src.clock.get)
|
|
||||||
val address = portToExpression(rw.src.address)
|
|
||||||
val wmode = rw.src.writeEnable match {
|
|
||||||
case Some(we) => portToExpression(we)
|
|
||||||
case None => zero // is it possible?
|
|
||||||
}
|
|
||||||
val wmask = rw.src.maskPort match {
|
|
||||||
case Some(wm) => portToExpression(wm)
|
|
||||||
case None => one
|
|
||||||
}
|
|
||||||
val enable = (rw.src.chipEnable, rw.src.readEnable) match {
|
|
||||||
case (Some(en), Some(re)) =>
|
|
||||||
and(portToExpression(en), or(portToExpression(re), wmode))
|
|
||||||
case (Some(en), None) => portToExpression(en)
|
|
||||||
case (None, Some(re)) => or(portToExpression(re), wmode)
|
|
||||||
case (None, None) => one
|
|
||||||
}
|
|
||||||
val wdata = memPortField(mem, s"RW_$i", "wdata")
|
|
||||||
val rdata = memPortField(mem, s"RW_$i", "rdata")
|
|
||||||
val write = portToExpression(rw.src.input.get)
|
|
||||||
val read = rdata
|
|
||||||
Seq(
|
|
||||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "clk"), clock),
|
|
||||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "addr"), address),
|
|
||||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "en"), enable),
|
|
||||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "wmode"), wmode),
|
|
||||||
Connect(NoInfo, memPortField(mem, s"RW_$i", "wmask"), wmask),
|
|
||||||
Connect(NoInfo, WRef(rw.src.output.get.name), read),
|
|
||||||
Connect(NoInfo, wdata, write)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
extraMods.append(real_macro.module(Block(mem +: (readConnects ++ writeConnects ++ readwriteConnects))))
|
|
||||||
real_mod
|
|
||||||
}}).toMap
|
|
||||||
|
|
||||||
def run(c: Circuit): Circuit = {
|
|
||||||
if (!synflops) c
|
|
||||||
else c.copy(modules = (c.modules map (m => libMods.getOrElse(m.name, m))) ++ extraMods)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
// See LICENSE for license details.
|
|
||||||
|
|
||||||
package barstools.macros
|
|
||||||
|
|
||||||
import firrtl._
|
|
||||||
import firrtl.ir._
|
|
||||||
import firrtl.PrimOps
|
|
||||||
import firrtl.passes.memlib.{MemConf, MemPort, ReadPort, WritePort, ReadWritePort, MaskedWritePort, MaskedReadWritePort}
|
|
||||||
import firrtl.Utils.BoolType
|
|
||||||
import mdf.macrolib.{Constant, MacroPort, SRAMMacro}
|
|
||||||
import mdf.macrolib.{PolarizedPort, PortPolarity, ActiveLow, ActiveHigh, NegativeEdge, PositiveEdge, MacroExtraPort}
|
|
||||||
import java.io.File
|
|
||||||
import scala.language.implicitConversions
|
|
||||||
|
|
||||||
object MacroCompilerMath {
|
|
||||||
def ceilLog2(x: BigInt): Int = (x-1).bitLength
|
|
||||||
}
|
|
||||||
|
|
||||||
class FirrtlMacroPort(port: MacroPort) {
|
|
||||||
val src = port
|
|
||||||
|
|
||||||
val isReader = port.output.nonEmpty && port.input.isEmpty
|
|
||||||
val isWriter = port.input.nonEmpty && port.output.isEmpty
|
|
||||||
val isReadWriter = port.input.nonEmpty && port.output.nonEmpty
|
|
||||||
|
|
||||||
val addrType = UIntType(IntWidth(MacroCompilerMath.ceilLog2(port.depth.get) max 1))
|
|
||||||
val dataType = UIntType(IntWidth(port.width.get))
|
|
||||||
val maskType = UIntType(IntWidth(port.width.get / port.effectiveMaskGran))
|
|
||||||
|
|
||||||
// Bundle representing this macro port.
|
|
||||||
val tpe = BundleType(Seq(
|
|
||||||
Field(port.address.name, Flip, addrType)) ++
|
|
||||||
(port.clock map (p => Field(p.name, Flip, ClockType))) ++
|
|
||||||
(port.input map (p => Field(p.name, Flip, dataType))) ++
|
|
||||||
(port.output map (p => Field(p.name, Default, dataType))) ++
|
|
||||||
(port.chipEnable map (p => Field(p.name, Flip, BoolType))) ++
|
|
||||||
(port.readEnable map (p => Field(p.name, Flip, BoolType))) ++
|
|
||||||
(port.writeEnable map (p => Field(p.name, Flip, BoolType))) ++
|
|
||||||
(port.maskPort map (p => Field(p.name, Flip, maskType)))
|
|
||||||
)
|
|
||||||
val ports = tpe.fields map (f => Port(
|
|
||||||
NoInfo, f.name, f.flip match { case Default => Output case Flip => Input }, f.tpe))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reads an SRAMMacro and generates firrtl blackboxes.
|
|
||||||
class Macro(srcMacro: SRAMMacro) {
|
|
||||||
val src = srcMacro
|
|
||||||
|
|
||||||
val firrtlPorts = srcMacro.ports map { new FirrtlMacroPort(_) }
|
|
||||||
|
|
||||||
val writers = firrtlPorts filter (p => p.isWriter)
|
|
||||||
val readers = firrtlPorts filter (p => p.isReader)
|
|
||||||
val readwriters = firrtlPorts filter (p => p.isReadWriter)
|
|
||||||
|
|
||||||
val sortedPorts = writers ++ readers ++ readwriters
|
|
||||||
val extraPorts = srcMacro.extraPorts map { p =>
|
|
||||||
assert(p.portType == Constant) // TODO: release it?
|
|
||||||
val name = p.name
|
|
||||||
val width = BigInt(p.width.toLong)
|
|
||||||
val value = BigInt(p.value.toLong)
|
|
||||||
(name -> UIntLiteral(value, IntWidth(width)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bundle representing this memory blackbox
|
|
||||||
val tpe = BundleType(firrtlPorts flatMap (_.tpe.fields))
|
|
||||||
|
|
||||||
private val modPorts = (firrtlPorts flatMap (_.ports)) ++
|
|
||||||
(extraPorts map { case (name, value) => Port(NoInfo, name, Input, value.tpe) })
|
|
||||||
val blackbox = ExtModule(NoInfo, srcMacro.name, modPorts, srcMacro.name, Nil)
|
|
||||||
def module(body: Statement) = Module(NoInfo, srcMacro.name, modPorts, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
object Utils {
|
|
||||||
def filterForSRAM(s: Option[Seq[mdf.macrolib.Macro]]): Option[Seq[mdf.macrolib.SRAMMacro]] = {
|
|
||||||
s match {
|
|
||||||
case Some(l:Seq[mdf.macrolib.Macro]) => Some(l filter { _.isInstanceOf[mdf.macrolib.SRAMMacro] } map { m => m.asInstanceOf[mdf.macrolib.SRAMMacro] })
|
|
||||||
case _ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This utility reads a conf in and returns MDF like mdf.macrolib.Utils.readMDFFromPath
|
|
||||||
def readConfFromPath(path: Option[String]): Option[Seq[mdf.macrolib.Macro]] = {
|
|
||||||
path.map((p) => Utils.readConfFromString(scala.io.Source.fromFile(p).mkString))
|
|
||||||
}
|
|
||||||
def readConfFromString(str: String): Seq[mdf.macrolib.Macro] = {
|
|
||||||
MemConf.fromString(str).map { m:MemConf =>
|
|
||||||
val ports = m.ports.map { case (port, num) => Seq.fill(num)(port) } reduce (_ ++ _)
|
|
||||||
SRAMMacro(m.name, m.width, m.depth, Utils.portSpecToFamily(ports), Utils.portSpecToMacroPort(m.width, m.depth, m.maskGranularity, ports))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def portSpecToFamily(ports: Seq[MemPort]): String = {
|
|
||||||
val numR = ports.count(_ match { case ReadPort => true; case _ => false})
|
|
||||||
val numW = ports.count(_ match { case WritePort|MaskedWritePort => true; case _ => false})
|
|
||||||
val numRW = ports.count(_ match { case ReadWritePort|MaskedReadWritePort => true; case _ => false})
|
|
||||||
val numRStr = if(numR > 0) s"${numR}r" else ""
|
|
||||||
val numWStr = if(numW > 0) s"${numW}w" else ""
|
|
||||||
val numRWStr = if(numRW > 0) s"${numRW}rw" else ""
|
|
||||||
return numRStr + numWStr + numRWStr
|
|
||||||
}
|
|
||||||
// This translates between two represenations of ports
|
|
||||||
def portSpecToMacroPort(width: Int, depth: BigInt, maskGran: Option[Int], ports: Seq[MemPort]): Seq[MacroPort] = {
|
|
||||||
var numR = 0
|
|
||||||
var numW = 0
|
|
||||||
var numRW = 0
|
|
||||||
ports.map { _ match {
|
|
||||||
case ReadPort => {
|
|
||||||
val portName = s"R${numR}"
|
|
||||||
numR += 1
|
|
||||||
MacroPort(
|
|
||||||
width=Some(width), depth=Some(depth),
|
|
||||||
address=PolarizedPort(s"${portName}_addr", ActiveHigh),
|
|
||||||
clock=Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
|
||||||
readEnable=Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
|
||||||
output=Some(PolarizedPort(s"${portName}_data", ActiveHigh))
|
|
||||||
) }
|
|
||||||
case WritePort => {
|
|
||||||
val portName = s"W${numW}"
|
|
||||||
numW += 1
|
|
||||||
MacroPort(
|
|
||||||
width=Some(width), depth=Some(depth),
|
|
||||||
address=PolarizedPort(s"${portName}_addr", ActiveHigh),
|
|
||||||
clock=Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
|
||||||
writeEnable=Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
|
||||||
input=Some(PolarizedPort(s"${portName}_data", ActiveHigh))
|
|
||||||
) }
|
|
||||||
case MaskedWritePort => {
|
|
||||||
val portName = s"W${numW}"
|
|
||||||
numW += 1
|
|
||||||
MacroPort(
|
|
||||||
width=Some(width), depth=Some(depth),
|
|
||||||
address=PolarizedPort(s"${portName}_addr", ActiveHigh),
|
|
||||||
clock=Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
|
||||||
writeEnable=Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
|
||||||
maskPort=Some(PolarizedPort(s"${portName}_mask", ActiveHigh)),
|
|
||||||
maskGran=maskGran,
|
|
||||||
input=Some(PolarizedPort(s"${portName}_data", ActiveHigh))
|
|
||||||
) }
|
|
||||||
case ReadWritePort => {
|
|
||||||
val portName = s"RW${numRW}"
|
|
||||||
numRW += 1
|
|
||||||
MacroPort(
|
|
||||||
width=Some(width), depth=Some(depth),
|
|
||||||
address=PolarizedPort(s"${portName}_addr", ActiveHigh),
|
|
||||||
clock=Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
|
||||||
chipEnable=Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
|
||||||
writeEnable=Some(PolarizedPort(s"${portName}_wmode", ActiveHigh)),
|
|
||||||
input=Some(PolarizedPort(s"${portName}_wdata", ActiveHigh)),
|
|
||||||
output=Some(PolarizedPort(s"${portName}_rdata", ActiveHigh))
|
|
||||||
) }
|
|
||||||
case MaskedReadWritePort => {
|
|
||||||
val portName = s"RW${numRW}"
|
|
||||||
numRW += 1
|
|
||||||
MacroPort(
|
|
||||||
width=Some(width), depth=Some(depth),
|
|
||||||
address=PolarizedPort(s"${portName}_addr", ActiveHigh),
|
|
||||||
clock=Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
|
||||||
chipEnable=Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
|
||||||
writeEnable=Some(PolarizedPort(s"${portName}_wmode", ActiveHigh)),
|
|
||||||
maskPort=Some(PolarizedPort(s"${portName}_wmask", ActiveHigh)),
|
|
||||||
maskGran=maskGran,
|
|
||||||
input=Some(PolarizedPort(s"${portName}_wdata", ActiveHigh)),
|
|
||||||
output=Some(PolarizedPort(s"${portName}_rdata", ActiveHigh))
|
|
||||||
) }
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
def findSRAMCompiler(s: Option[Seq[mdf.macrolib.Macro]]): Option[mdf.macrolib.SRAMCompiler] = {
|
|
||||||
s match {
|
|
||||||
case Some(l:Seq[mdf.macrolib.Macro]) =>
|
|
||||||
l collectFirst {
|
|
||||||
case x: mdf.macrolib.SRAMCompiler => x
|
|
||||||
}
|
|
||||||
case _ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def buildSRAMMacros(s: mdf.macrolib.SRAMCompiler): Seq[mdf.macrolib.SRAMMacro] = {
|
|
||||||
for (g <- s.groups; d <- g.depth; w <- g.width; vt <- g.vt)
|
|
||||||
yield mdf.macrolib.SRAMMacro(makeName(g, d, w, vt), w, d, g.family, g.ports.map(_.copy(width=Some(w), depth=Some(d))), vt, g.mux, g.extraPorts)
|
|
||||||
}
|
|
||||||
def buildSRAMMacro(g: mdf.macrolib.SRAMGroup, d: Int, w: Int, vt: String): mdf.macrolib.SRAMMacro = {
|
|
||||||
return mdf.macrolib.SRAMMacro(makeName(g, d, w, vt), w, d, g.family, g.ports.map(_.copy(width=Some(w), depth=Some(d))), vt, g.mux, g.extraPorts)
|
|
||||||
}
|
|
||||||
def makeName(g: mdf.macrolib.SRAMGroup, depth: Int, width: Int, vt: String): String = {
|
|
||||||
g.name.foldLeft(""){ (builder, next) =>
|
|
||||||
next match {
|
|
||||||
case "depth"|"DEPTH" => builder + depth
|
|
||||||
case "width"|"WIDTH" => builder + width
|
|
||||||
case "vt" => builder + vt.toLowerCase
|
|
||||||
case "VT" => builder + vt.toUpperCase
|
|
||||||
case "family" => builder + g.family.toLowerCase
|
|
||||||
case "FAMILY" => builder + g.family.toUpperCase
|
|
||||||
case "mux"|"MUX" => builder + g.mux
|
|
||||||
case other => builder + other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def and(e1: Expression, e2: Expression) =
|
|
||||||
DoPrim(PrimOps.And, Seq(e1, e2), Nil, e1.tpe)
|
|
||||||
def or(e1: Expression, e2: Expression) =
|
|
||||||
DoPrim(PrimOps.Or, Seq(e1, e2), Nil, e1.tpe)
|
|
||||||
def bits(e: Expression, high: BigInt, low: BigInt): Expression =
|
|
||||||
DoPrim(PrimOps.Bits, Seq(e), Seq(high, low), UIntType(IntWidth(high-low+1)))
|
|
||||||
def bits(e: Expression, idx: BigInt): Expression = bits(e, idx, idx)
|
|
||||||
def cat(es: Seq[Expression]): Expression =
|
|
||||||
if (es.size == 1) es.head
|
|
||||||
else DoPrim(PrimOps.Cat, Seq(es.head, cat(es.tail)), Nil, UnknownType)
|
|
||||||
def not(e: Expression) =
|
|
||||||
DoPrim(PrimOps.Not, Seq(e), Nil, e.tpe)
|
|
||||||
|
|
||||||
// Convert a port to a FIRRTL expression, handling polarity along the way.
|
|
||||||
def portToExpression(pp: PolarizedPort): Expression =
|
|
||||||
portToExpression(WRef(pp.name), Some(pp.polarity))
|
|
||||||
|
|
||||||
def portToExpression(exp: Expression, polarity: Option[PortPolarity]): Expression =
|
|
||||||
polarity match {
|
|
||||||
case Some(ActiveLow) | Some(NegativeEdge) => not(exp)
|
|
||||||
case _ => exp
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a number is a power of two
|
|
||||||
def isPowerOfTwo(x: Int): Boolean = (x & (x - 1)) == 0
|
|
||||||
}
|
|
||||||
@@ -2,27 +2,25 @@
|
|||||||
|
|
||||||
package barstools.macros
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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 =>
|
||||||
151
macros/src/main/scala/barstools/macros/SynFlops.scala
Normal file
151
macros/src/main/scala/barstools/macros/SynFlops.scala
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package barstools.macros
|
||||||
|
|
||||||
|
import barstools.macros.Utils._
|
||||||
|
import firrtl.Utils._
|
||||||
|
import firrtl._
|
||||||
|
import firrtl.ir._
|
||||||
|
import firrtl.passes.MemPortUtils.memPortField
|
||||||
|
|
||||||
|
class SynFlopsPass(synflops: Boolean, libs: Seq[Macro]) extends firrtl.passes.Pass {
|
||||||
|
val extraMods = scala.collection.mutable.ArrayBuffer.empty[Module]
|
||||||
|
lazy val libMods = (libs.map { lib =>
|
||||||
|
lib.src.name -> {
|
||||||
|
val (dataType, dataWidth) = (lib.src.ports.foldLeft(None: Option[BigInt]))((res, port) =>
|
||||||
|
(res, port.maskPort) match {
|
||||||
|
case (_, None) =>
|
||||||
|
res
|
||||||
|
case (None, Some(_)) =>
|
||||||
|
Some(port.effectiveMaskGran)
|
||||||
|
case (Some(x), Some(_)) =>
|
||||||
|
assert(x == port.effectiveMaskGran)
|
||||||
|
res
|
||||||
|
}
|
||||||
|
) match {
|
||||||
|
case None => (UIntType(IntWidth(lib.src.width)), lib.src.width)
|
||||||
|
case Some(gran) => (UIntType(IntWidth(gran)), gran.intValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
val maxDepth = min(lib.src.depth, 1 << 26)
|
||||||
|
val numMems = lib.src.depth / maxDepth
|
||||||
|
|
||||||
|
// Change macro to be mapped onto to look like the below mem
|
||||||
|
// by changing its depth, and width
|
||||||
|
val lib_macro = new Macro(
|
||||||
|
lib.src.copy(
|
||||||
|
name = "split_" + lib.src.name,
|
||||||
|
depth = maxDepth,
|
||||||
|
width = dataWidth,
|
||||||
|
ports = lib.src.ports.map(p =>
|
||||||
|
p.copy(
|
||||||
|
width = p.width.map(_ => dataWidth),
|
||||||
|
depth = p.depth.map(_ => maxDepth),
|
||||||
|
maskGran = p.maskGran.map(_ => dataWidth)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val mod_macro = (new MacroCompilerPass(None, None, None, None)).compile(lib, lib_macro)
|
||||||
|
val (real_mod, real_macro) = mod_macro.get
|
||||||
|
|
||||||
|
val mem = DefMemory(
|
||||||
|
NoInfo,
|
||||||
|
"ram",
|
||||||
|
dataType,
|
||||||
|
maxDepth,
|
||||||
|
1, // writeLatency
|
||||||
|
1, // readLatency. This is possible because of VerilogMemDelays
|
||||||
|
real_macro.readers.indices.map(i => s"R_$i"),
|
||||||
|
real_macro.writers.indices.map(i => s"W_$i"),
|
||||||
|
real_macro.readwriters.indices.map(i => s"RW_$i")
|
||||||
|
)
|
||||||
|
|
||||||
|
val readConnects = real_macro.readers.zipWithIndex.flatMap { case (r, i) =>
|
||||||
|
val clock = portToExpression(r.src.clock.get)
|
||||||
|
val address = portToExpression(r.src.address)
|
||||||
|
val enable = (r.src chipEnable, r.src readEnable) match {
|
||||||
|
case (Some(en_port), Some(re_port)) =>
|
||||||
|
and(portToExpression(en_port), portToExpression(re_port))
|
||||||
|
case (Some(en_port), None) => portToExpression(en_port)
|
||||||
|
case (None, Some(re_port)) => portToExpression(re_port)
|
||||||
|
case (None, None) => one
|
||||||
|
}
|
||||||
|
val data = memPortField(mem, s"R_$i", "data")
|
||||||
|
val read = data
|
||||||
|
Seq(
|
||||||
|
Connect(NoInfo, memPortField(mem, s"R_$i", "clk"), clock),
|
||||||
|
Connect(NoInfo, memPortField(mem, s"R_$i", "addr"), address),
|
||||||
|
Connect(NoInfo, memPortField(mem, s"R_$i", "en"), enable),
|
||||||
|
Connect(NoInfo, WRef(r.src.output.get.name), read)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val writeConnects = real_macro.writers.zipWithIndex.flatMap { case (w, i) =>
|
||||||
|
val clock = portToExpression(w.src.clock.get)
|
||||||
|
val address = portToExpression(w.src.address)
|
||||||
|
val enable = (w.src.chipEnable, w.src.writeEnable) match {
|
||||||
|
case (Some(en), Some(we)) =>
|
||||||
|
and(portToExpression(en), portToExpression(we))
|
||||||
|
case (Some(en), None) => portToExpression(en)
|
||||||
|
case (None, Some(we)) => portToExpression(we)
|
||||||
|
case (None, None) => zero // is it possible?
|
||||||
|
}
|
||||||
|
val mask = w.src.maskPort match {
|
||||||
|
case Some(m) => portToExpression(m)
|
||||||
|
case None => one
|
||||||
|
}
|
||||||
|
val data = memPortField(mem, s"W_$i", "data")
|
||||||
|
val write = portToExpression(w.src.input.get)
|
||||||
|
Seq(
|
||||||
|
Connect(NoInfo, memPortField(mem, s"W_$i", "clk"), clock),
|
||||||
|
Connect(NoInfo, memPortField(mem, s"W_$i", "addr"), address),
|
||||||
|
Connect(NoInfo, memPortField(mem, s"W_$i", "en"), enable),
|
||||||
|
Connect(NoInfo, memPortField(mem, s"W_$i", "mask"), mask),
|
||||||
|
Connect(NoInfo, data, write)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val readwriteConnects = real_macro.readwriters.zipWithIndex.flatMap { case (rw, i) =>
|
||||||
|
val clock = portToExpression(rw.src.clock.get)
|
||||||
|
val address = portToExpression(rw.src.address)
|
||||||
|
val wmode = rw.src.writeEnable match {
|
||||||
|
case Some(we) => portToExpression(we)
|
||||||
|
case None => zero // is it possible?
|
||||||
|
}
|
||||||
|
val wmask = rw.src.maskPort match {
|
||||||
|
case Some(wm) => portToExpression(wm)
|
||||||
|
case None => one
|
||||||
|
}
|
||||||
|
val enable = (rw.src.chipEnable, rw.src.readEnable) match {
|
||||||
|
case (Some(en), Some(re)) =>
|
||||||
|
and(portToExpression(en), or(portToExpression(re), wmode))
|
||||||
|
case (Some(en), None) => portToExpression(en)
|
||||||
|
case (None, Some(re)) => or(portToExpression(re), wmode)
|
||||||
|
case (None, None) => one
|
||||||
|
}
|
||||||
|
val wdata = memPortField(mem, s"RW_$i", "wdata")
|
||||||
|
val rdata = memPortField(mem, s"RW_$i", "rdata")
|
||||||
|
val write = portToExpression(rw.src.input.get)
|
||||||
|
val read = rdata
|
||||||
|
Seq(
|
||||||
|
Connect(NoInfo, memPortField(mem, s"RW_$i", "clk"), clock),
|
||||||
|
Connect(NoInfo, memPortField(mem, s"RW_$i", "addr"), address),
|
||||||
|
Connect(NoInfo, memPortField(mem, s"RW_$i", "en"), enable),
|
||||||
|
Connect(NoInfo, memPortField(mem, s"RW_$i", "wmode"), wmode),
|
||||||
|
Connect(NoInfo, memPortField(mem, s"RW_$i", "wmask"), wmask),
|
||||||
|
Connect(NoInfo, WRef(rw.src.output.get.name), read),
|
||||||
|
Connect(NoInfo, wdata, write)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
extraMods.append(real_macro.module(Block(mem +: (readConnects ++ writeConnects ++ readwriteConnects))))
|
||||||
|
real_mod
|
||||||
|
}
|
||||||
|
}).toMap
|
||||||
|
|
||||||
|
def run(c: Circuit): Circuit = {
|
||||||
|
if (!synflops) c
|
||||||
|
else c.copy(modules = (c.modules.map(m => libMods.getOrElse(m.name, m))) ++ extraMods)
|
||||||
|
}
|
||||||
|
}
|
||||||
269
macros/src/main/scala/barstools/macros/Utils.scala
Normal file
269
macros/src/main/scala/barstools/macros/Utils.scala
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package barstools.macros
|
||||||
|
|
||||||
|
import firrtl.Utils.BoolType
|
||||||
|
import firrtl.ir._
|
||||||
|
import firrtl.passes.memlib._
|
||||||
|
import firrtl.{PrimOps, _}
|
||||||
|
import mdf.macrolib.{Input => _, Output => _, _}
|
||||||
|
|
||||||
|
import scala.language.implicitConversions
|
||||||
|
|
||||||
|
object MacroCompilerMath {
|
||||||
|
def ceilLog2(x: BigInt): Int = (x - 1).bitLength
|
||||||
|
}
|
||||||
|
|
||||||
|
class FirrtlMacroPort(port: MacroPort) {
|
||||||
|
val src = port
|
||||||
|
|
||||||
|
val isReader = port.output.nonEmpty && port.input.isEmpty
|
||||||
|
val isWriter = port.input.nonEmpty && port.output.isEmpty
|
||||||
|
val isReadWriter = port.input.nonEmpty && port.output.nonEmpty
|
||||||
|
|
||||||
|
val addrType = UIntType(IntWidth(MacroCompilerMath.ceilLog2(port.depth.get).max(1)))
|
||||||
|
val dataType = UIntType(IntWidth(port.width.get))
|
||||||
|
val maskType = UIntType(IntWidth(port.width.get / port.effectiveMaskGran))
|
||||||
|
|
||||||
|
// Bundle representing this macro port.
|
||||||
|
val tpe = BundleType(
|
||||||
|
Seq(Field(port.address.name, Flip, addrType)) ++
|
||||||
|
(port.clock.map(p => Field(p.name, Flip, ClockType))) ++
|
||||||
|
(port.input.map(p => Field(p.name, Flip, dataType))) ++
|
||||||
|
(port.output.map(p => Field(p.name, Default, dataType))) ++
|
||||||
|
(port.chipEnable.map(p => Field(p.name, Flip, BoolType))) ++
|
||||||
|
(port.readEnable.map(p => Field(p.name, Flip, BoolType))) ++
|
||||||
|
(port.writeEnable.map(p => Field(p.name, Flip, BoolType))) ++
|
||||||
|
(port.maskPort.map(p => Field(p.name, Flip, maskType)))
|
||||||
|
)
|
||||||
|
val ports = tpe.fields.map(f =>
|
||||||
|
Port(
|
||||||
|
NoInfo,
|
||||||
|
f.name,
|
||||||
|
f.flip match {
|
||||||
|
case Default => Output
|
||||||
|
case Flip => Input
|
||||||
|
},
|
||||||
|
f.tpe
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads an SRAMMacro and generates firrtl blackboxes.
|
||||||
|
class Macro(srcMacro: SRAMMacro) {
|
||||||
|
val src = srcMacro
|
||||||
|
|
||||||
|
val firrtlPorts = srcMacro.ports.map { new FirrtlMacroPort(_) }
|
||||||
|
|
||||||
|
val writers = firrtlPorts.filter(p => p.isWriter)
|
||||||
|
val readers = firrtlPorts.filter(p => p.isReader)
|
||||||
|
val readwriters = firrtlPorts.filter(p => p.isReadWriter)
|
||||||
|
|
||||||
|
val sortedPorts = writers ++ readers ++ readwriters
|
||||||
|
val extraPorts = srcMacro.extraPorts.map { p =>
|
||||||
|
assert(p.portType == Constant) // TODO: release it?
|
||||||
|
val name = p.name
|
||||||
|
val width = BigInt(p.width.toLong)
|
||||||
|
val value = BigInt(p.value.toLong)
|
||||||
|
(name -> UIntLiteral(value, IntWidth(width)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bundle representing this memory blackbox
|
||||||
|
val tpe = BundleType(firrtlPorts.flatMap(_.tpe.fields))
|
||||||
|
|
||||||
|
private val modPorts = (firrtlPorts.flatMap(_.ports)) ++
|
||||||
|
(extraPorts.map { case (name, value) => Port(NoInfo, name, Input, value.tpe) })
|
||||||
|
val blackbox = ExtModule(NoInfo, srcMacro.name, modPorts, srcMacro.name, Nil)
|
||||||
|
def module(body: Statement) = Module(NoInfo, srcMacro.name, modPorts, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Utils {
|
||||||
|
def filterForSRAM(s: Option[Seq[mdf.macrolib.Macro]]): Option[Seq[mdf.macrolib.SRAMMacro]] = {
|
||||||
|
s match {
|
||||||
|
case Some(l: Seq[mdf.macrolib.Macro]) =>
|
||||||
|
Some(l.filter { _.isInstanceOf[mdf.macrolib.SRAMMacro] }.map { m => m.asInstanceOf[mdf.macrolib.SRAMMacro] })
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This utility reads a conf in and returns MDF like mdf.macrolib.Utils.readMDFFromPath
|
||||||
|
def readConfFromPath(path: Option[String]): Option[Seq[mdf.macrolib.Macro]] = {
|
||||||
|
path.map((p) => Utils.readConfFromString(scala.io.Source.fromFile(p).mkString))
|
||||||
|
}
|
||||||
|
def readConfFromString(str: String): Seq[mdf.macrolib.Macro] = {
|
||||||
|
MemConf.fromString(str).map { m: MemConf =>
|
||||||
|
val ports = m.ports.map { case (port, num) => Seq.fill(num)(port) }.reduce(_ ++ _)
|
||||||
|
SRAMMacro(
|
||||||
|
m.name,
|
||||||
|
m.width,
|
||||||
|
m.depth,
|
||||||
|
Utils.portSpecToFamily(ports),
|
||||||
|
Utils.portSpecToMacroPort(m.width, m.depth, m.maskGranularity, ports)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def portSpecToFamily(ports: Seq[MemPort]): String = {
|
||||||
|
val numR = ports.count(_ match { case ReadPort => true; case _ => false })
|
||||||
|
val numW = ports.count(_ match { case WritePort | MaskedWritePort => true; case _ => false })
|
||||||
|
val numRW = ports.count(_ match { case ReadWritePort | MaskedReadWritePort => true; case _ => false })
|
||||||
|
val numRStr = if (numR > 0) s"${numR}r" else ""
|
||||||
|
val numWStr = if (numW > 0) s"${numW}w" else ""
|
||||||
|
val numRWStr = if (numRW > 0) s"${numRW}rw" else ""
|
||||||
|
return numRStr + numWStr + numRWStr
|
||||||
|
}
|
||||||
|
// This translates between two represenations of ports
|
||||||
|
def portSpecToMacroPort(width: Int, depth: BigInt, maskGran: Option[Int], ports: Seq[MemPort]): Seq[MacroPort] = {
|
||||||
|
var numR = 0
|
||||||
|
var numW = 0
|
||||||
|
var numRW = 0
|
||||||
|
ports.map {
|
||||||
|
_ match {
|
||||||
|
case ReadPort => {
|
||||||
|
val portName = s"R${numR}"
|
||||||
|
numR += 1
|
||||||
|
MacroPort(
|
||||||
|
width = Some(width),
|
||||||
|
depth = Some(depth),
|
||||||
|
address = PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||||
|
clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||||
|
readEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||||
|
output = Some(PolarizedPort(s"${portName}_data", ActiveHigh))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case WritePort => {
|
||||||
|
val portName = s"W${numW}"
|
||||||
|
numW += 1
|
||||||
|
MacroPort(
|
||||||
|
width = Some(width),
|
||||||
|
depth = Some(depth),
|
||||||
|
address = PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||||
|
clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||||
|
writeEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||||
|
input = Some(PolarizedPort(s"${portName}_data", ActiveHigh))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case MaskedWritePort => {
|
||||||
|
val portName = s"W${numW}"
|
||||||
|
numW += 1
|
||||||
|
MacroPort(
|
||||||
|
width = Some(width),
|
||||||
|
depth = Some(depth),
|
||||||
|
address = PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||||
|
clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||||
|
writeEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||||
|
maskPort = Some(PolarizedPort(s"${portName}_mask", ActiveHigh)),
|
||||||
|
maskGran = maskGran,
|
||||||
|
input = Some(PolarizedPort(s"${portName}_data", ActiveHigh))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case ReadWritePort => {
|
||||||
|
val portName = s"RW${numRW}"
|
||||||
|
numRW += 1
|
||||||
|
MacroPort(
|
||||||
|
width = Some(width),
|
||||||
|
depth = Some(depth),
|
||||||
|
address = PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||||
|
clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||||
|
chipEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||||
|
writeEnable = Some(PolarizedPort(s"${portName}_wmode", ActiveHigh)),
|
||||||
|
input = Some(PolarizedPort(s"${portName}_wdata", ActiveHigh)),
|
||||||
|
output = Some(PolarizedPort(s"${portName}_rdata", ActiveHigh))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case MaskedReadWritePort => {
|
||||||
|
val portName = s"RW${numRW}"
|
||||||
|
numRW += 1
|
||||||
|
MacroPort(
|
||||||
|
width = Some(width),
|
||||||
|
depth = Some(depth),
|
||||||
|
address = PolarizedPort(s"${portName}_addr", ActiveHigh),
|
||||||
|
clock = Some(PolarizedPort(s"${portName}_clk", PositiveEdge)),
|
||||||
|
chipEnable = Some(PolarizedPort(s"${portName}_en", ActiveHigh)),
|
||||||
|
writeEnable = Some(PolarizedPort(s"${portName}_wmode", ActiveHigh)),
|
||||||
|
maskPort = Some(PolarizedPort(s"${portName}_wmask", ActiveHigh)),
|
||||||
|
maskGran = maskGran,
|
||||||
|
input = Some(PolarizedPort(s"${portName}_wdata", ActiveHigh)),
|
||||||
|
output = Some(PolarizedPort(s"${portName}_rdata", ActiveHigh))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def findSRAMCompiler(s: Option[Seq[mdf.macrolib.Macro]]): Option[mdf.macrolib.SRAMCompiler] = {
|
||||||
|
s match {
|
||||||
|
case Some(l: Seq[mdf.macrolib.Macro]) =>
|
||||||
|
l.collectFirst { case x: mdf.macrolib.SRAMCompiler =>
|
||||||
|
x
|
||||||
|
}
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def buildSRAMMacros(s: mdf.macrolib.SRAMCompiler): Seq[mdf.macrolib.SRAMMacro] = {
|
||||||
|
for {
|
||||||
|
g <- s.groups
|
||||||
|
d <- g.depth
|
||||||
|
w <- g.width
|
||||||
|
vt <- g.vt
|
||||||
|
} yield mdf.macrolib.SRAMMacro(
|
||||||
|
makeName(g, d, w, vt),
|
||||||
|
w,
|
||||||
|
d,
|
||||||
|
g.family,
|
||||||
|
g.ports.map(_.copy(width = Some(w), depth = Some(d))),
|
||||||
|
vt,
|
||||||
|
g.mux,
|
||||||
|
g.extraPorts
|
||||||
|
)
|
||||||
|
}
|
||||||
|
def buildSRAMMacro(g: mdf.macrolib.SRAMGroup, d: Int, w: Int, vt: String): mdf.macrolib.SRAMMacro = {
|
||||||
|
return mdf.macrolib.SRAMMacro(
|
||||||
|
makeName(g, d, w, vt),
|
||||||
|
w,
|
||||||
|
d,
|
||||||
|
g.family,
|
||||||
|
g.ports.map(_.copy(width = Some(w), depth = Some(d))),
|
||||||
|
vt,
|
||||||
|
g.mux,
|
||||||
|
g.extraPorts
|
||||||
|
)
|
||||||
|
}
|
||||||
|
def makeName(g: mdf.macrolib.SRAMGroup, depth: Int, width: Int, vt: String): String = {
|
||||||
|
g.name.foldLeft("") { (builder, next) =>
|
||||||
|
next match {
|
||||||
|
case "depth" | "DEPTH" => builder + depth
|
||||||
|
case "width" | "WIDTH" => builder + width
|
||||||
|
case "vt" => builder + vt.toLowerCase
|
||||||
|
case "VT" => builder + vt.toUpperCase
|
||||||
|
case "family" => builder + g.family.toLowerCase
|
||||||
|
case "FAMILY" => builder + g.family.toUpperCase
|
||||||
|
case "mux" | "MUX" => builder + g.mux
|
||||||
|
case other => builder + other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def and(e1: Expression, e2: Expression) =
|
||||||
|
DoPrim(PrimOps.And, Seq(e1, e2), Nil, e1.tpe)
|
||||||
|
def or(e1: Expression, e2: Expression) =
|
||||||
|
DoPrim(PrimOps.Or, Seq(e1, e2), Nil, e1.tpe)
|
||||||
|
def bits(e: Expression, high: BigInt, low: BigInt): Expression =
|
||||||
|
DoPrim(PrimOps.Bits, Seq(e), Seq(high, low), UIntType(IntWidth(high - low + 1)))
|
||||||
|
def bits(e: Expression, idx: BigInt): Expression = bits(e, idx, idx)
|
||||||
|
def cat(es: Seq[Expression]): Expression =
|
||||||
|
if (es.size == 1) es.head
|
||||||
|
else DoPrim(PrimOps.Cat, Seq(es.head, cat(es.tail)), Nil, UnknownType)
|
||||||
|
def not(e: Expression) =
|
||||||
|
DoPrim(PrimOps.Not, Seq(e), Nil, e.tpe)
|
||||||
|
|
||||||
|
// Convert a port to a FIRRTL expression, handling polarity along the way.
|
||||||
|
def portToExpression(pp: PolarizedPort): Expression =
|
||||||
|
portToExpression(WRef(pp.name), Some(pp.polarity))
|
||||||
|
|
||||||
|
def portToExpression(exp: Expression, polarity: Option[PortPolarity]): Expression =
|
||||||
|
polarity match {
|
||||||
|
case Some(ActiveLow) | Some(NegativeEdge) => not(exp)
|
||||||
|
case _ => exp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a number is a power of two
|
||||||
|
def isPowerOfTwo(x: Int): Boolean = (x & (x - 1)) == 0
|
||||||
|
}
|
||||||
@@ -1,27 +1,29 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type" : "sram",
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,453 +0,0 @@
|
|||||||
// See LICENSE for license details.
|
|
||||||
|
|
||||||
package barstools.macros
|
|
||||||
|
|
||||||
import firrtl.ir.{Circuit, NoInfo}
|
|
||||||
import firrtl.passes.RemoveEmpty
|
|
||||||
import firrtl.Parser.parse
|
|
||||||
|
|
||||||
import java.io.{File, StringWriter}
|
|
||||||
|
|
||||||
import mdf.macrolib.SRAMMacro
|
|
||||||
|
|
||||||
abstract class MacroCompilerSpec extends org.scalatest.FlatSpec with org.scalatest.Matchers {
|
|
||||||
import scala.language.implicitConversions
|
|
||||||
implicit def String2SomeString(i: String): Option[String] = Some(i)
|
|
||||||
val testDir: String = "test_run_dir/macros"
|
|
||||||
new File(testDir).mkdirs // Make sure the testDir exists
|
|
||||||
|
|
||||||
// Override these to change the prefixing of macroDir and testDir
|
|
||||||
val memPrefix: String = testDir
|
|
||||||
val libPrefix: String = testDir
|
|
||||||
val vPrefix: String = testDir
|
|
||||||
|
|
||||||
// Override this to use a different cost metric.
|
|
||||||
// If this is None, the compile() call will not have any -c/-cp arguments, and
|
|
||||||
// execute() will use CostMetric.default.
|
|
||||||
val costMetric: Option[CostMetric] = None
|
|
||||||
private def getCostMetric: CostMetric = costMetric.getOrElse(CostMetric.default)
|
|
||||||
|
|
||||||
private def costMetricCmdLine = {
|
|
||||||
costMetric match {
|
|
||||||
case None => Nil
|
|
||||||
case Some(m) => {
|
|
||||||
val name = m.name
|
|
||||||
val params = m.commandLineParams
|
|
||||||
List("-c", name) ++ params.flatMap{ case (key, value) => List("-cp", key, value) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def args(mem: String, lib: Option[String], v: String, synflops: Boolean, useCompiler: Boolean) =
|
|
||||||
List("-m", mem.toString, "-v", v) ++
|
|
||||||
(lib match { case None => Nil case Some(l) => List("-l", l.toString) }) ++
|
|
||||||
costMetricCmdLine ++
|
|
||||||
(if (synflops) List("--mode", "synflops") else Nil) ++
|
|
||||||
(if (useCompiler) List("--use-compiler") else Nil)
|
|
||||||
|
|
||||||
// Run the full compiler as if from the command line interface.
|
|
||||||
// Generates the Verilog; useful in testing since an error will throw an
|
|
||||||
// exception.
|
|
||||||
def compile(mem: String, lib: String, v: String, synflops: Boolean) {
|
|
||||||
compile(mem, Some(lib), v, synflops)
|
|
||||||
}
|
|
||||||
def compile(mem: String, lib: Option[String], v: String, synflops: Boolean, useCompiler: Boolean = false) {
|
|
||||||
var mem_full = concat(memPrefix, mem)
|
|
||||||
var lib_full = concat(libPrefix, lib)
|
|
||||||
var v_full = concat(vPrefix, v)
|
|
||||||
|
|
||||||
MacroCompiler.run(args(mem_full, lib_full, v_full, synflops, useCompiler))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper functions to write macro libraries to the given files.
|
|
||||||
def writeToLib(lib: String, libs: Seq[mdf.macrolib.Macro]) = {
|
|
||||||
mdf.macrolib.Utils.writeMDFToPath(Some(concat(libPrefix, lib)), libs)
|
|
||||||
}
|
|
||||||
|
|
||||||
def writeToMem(mem: String, mems: Seq[mdf.macrolib.Macro]) = {
|
|
||||||
mdf.macrolib.Utils.writeMDFToPath(Some(concat(memPrefix, mem)), mems)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convenience function for running both compile, execute, and test at once.
|
|
||||||
def compileExecuteAndTest(mem: String, lib: Option[String], v: String, output: String, synflops: Boolean = false, useCompiler: Boolean = false): Unit = {
|
|
||||||
compile(mem, lib, v, synflops, useCompiler)
|
|
||||||
val result = execute(mem, lib, synflops, useCompiler)
|
|
||||||
test(result, output)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare FIRRTL outputs after reparsing output with ScalaTest ("should be").
|
|
||||||
def test(result: Circuit, output: String): Unit = {
|
|
||||||
val gold = RemoveEmpty run parse(output)
|
|
||||||
(result.serialize) should be (gold.serialize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the macro compiler and returns a Circuit containing the output of
|
|
||||||
// the memory compiler.
|
|
||||||
def execute(memFile: Option[String], libFile: Option[String], synflops: Boolean): Circuit = execute(memFile, libFile, synflops, false)
|
|
||||||
def execute(memFile: Option[String], libFile: Option[String], synflops: Boolean, useCompiler: Boolean): Circuit = {
|
|
||||||
var mem_full = concat(memPrefix, memFile)
|
|
||||||
var lib_full = concat(libPrefix, libFile)
|
|
||||||
|
|
||||||
require(memFile.isDefined)
|
|
||||||
val mems: Seq[Macro] = Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(mem_full)).get map (new Macro(_))
|
|
||||||
val libs: Option[Seq[Macro]] = if(useCompiler) {
|
|
||||||
Utils.findSRAMCompiler(mdf.macrolib.Utils.readMDFFromPath(lib_full)).map{x => Utils.buildSRAMMacros(x).map(new Macro(_)) }
|
|
||||||
} else {
|
|
||||||
Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(lib_full)) match {
|
|
||||||
case Some(x) => Some(x map (new Macro(_)))
|
|
||||||
case None => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val macros = mems map (_.blackbox)
|
|
||||||
val circuit = Circuit(NoInfo, macros, macros.last.name)
|
|
||||||
val passes = Seq(
|
|
||||||
new MacroCompilerPass(Some(mems), libs, None, None, getCostMetric, if (synflops) MacroCompilerAnnotation.Synflops else MacroCompilerAnnotation.Default),
|
|
||||||
new SynFlopsPass(synflops, libs getOrElse mems),
|
|
||||||
RemoveEmpty)
|
|
||||||
val result: Circuit = (passes foldLeft circuit)((c, pass) => pass run c)
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Helper method to deal with String + Option[String]
|
|
||||||
private def concat(a: String, b: String): String = {a + "/" + b}
|
|
||||||
private def concat(a: String, b: Option[String]): Option[String] = {
|
|
||||||
b match {
|
|
||||||
case Some(b2:String) => Some(a + "/" + b2)
|
|
||||||
case _ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A collection of standard SRAM generators.
|
|
||||||
trait HasSRAMGenerator {
|
|
||||||
import mdf.macrolib._
|
|
||||||
import scala.language.implicitConversions
|
|
||||||
implicit def Int2SomeInt(i: Int): Option[Int] = Some(i)
|
|
||||||
implicit def BigInt2SomeBigInt(i: BigInt): Option[BigInt] = Some(i)
|
|
||||||
|
|
||||||
|
|
||||||
// Generate a standard (read/write/combo) port for testing.
|
|
||||||
// Helper methods for optional width argument
|
|
||||||
def generateTestPort(
|
|
||||||
prefix: String,
|
|
||||||
width: Option[Int],
|
|
||||||
depth: Option[BigInt],
|
|
||||||
maskGran: Option[Int] = None,
|
|
||||||
read: Boolean,
|
|
||||||
readEnable: Boolean = false,
|
|
||||||
write: Boolean,
|
|
||||||
writeEnable: Boolean = false
|
|
||||||
): MacroPort = {
|
|
||||||
val realPrefix = if (prefix == "") "" else prefix + "_"
|
|
||||||
|
|
||||||
MacroPort(
|
|
||||||
address = PolarizedPort(name = realPrefix + "addr", polarity = ActiveHigh),
|
|
||||||
clock = Some(PolarizedPort(name = realPrefix + "clk", polarity = PositiveEdge)),
|
|
||||||
|
|
||||||
readEnable = if (readEnable) Some(PolarizedPort(name = realPrefix + "read_en", polarity = ActiveHigh)) else None,
|
|
||||||
writeEnable = if (writeEnable) Some(PolarizedPort(name = realPrefix + "write_en", polarity = ActiveHigh)) else None,
|
|
||||||
|
|
||||||
output = if (read) Some(PolarizedPort(name = realPrefix + "dout", polarity = ActiveHigh)) else None,
|
|
||||||
input = if (write) Some(PolarizedPort(name = realPrefix + "din", polarity = ActiveHigh)) else None,
|
|
||||||
|
|
||||||
maskPort = maskGran match {
|
|
||||||
case Some(x: Int) => Some(PolarizedPort(name = realPrefix + "mask", polarity = ActiveHigh))
|
|
||||||
case _ => None
|
|
||||||
},
|
|
||||||
maskGran = maskGran,
|
|
||||||
|
|
||||||
width = width, depth = depth // These numbers don't matter here.
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a read port for testing.
|
|
||||||
def generateReadPort(prefix: String, width: Option[Int], depth: Option[BigInt], readEnable: Boolean = false): MacroPort = {
|
|
||||||
generateTestPort(prefix, width, depth, write = false, read = true, readEnable = readEnable)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a write port for testing.
|
|
||||||
def generateWritePort(prefix: String, width: Option[Int], depth: Option[BigInt], maskGran: Option[Int] = None, writeEnable: Boolean = true): MacroPort = {
|
|
||||||
generateTestPort(prefix, width, depth, maskGran = maskGran, write = true, read = false, writeEnable = writeEnable)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a simple read-write port for testing.
|
|
||||||
def generateReadWritePort(prefix: String, width: Option[Int], depth: Option[BigInt], maskGran: Option[Int] = None): MacroPort = {
|
|
||||||
generateTestPort(
|
|
||||||
prefix, width, depth, maskGran = maskGran,
|
|
||||||
write = true, writeEnable = true,
|
|
||||||
read = true, readEnable = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a "simple" SRAM (active high/positive edge, 1 read-write port).
|
|
||||||
def generateSRAM(name: String, prefix: String, width: Int, depth: BigInt, maskGran: Option[Int] = None, extraPorts: Seq[MacroExtraPort] = List()): SRAMMacro = {
|
|
||||||
SRAMMacro(
|
|
||||||
name = name,
|
|
||||||
width = width,
|
|
||||||
depth = depth,
|
|
||||||
family = "1rw",
|
|
||||||
ports = Seq(generateReadWritePort(prefix, width, depth, maskGran)),
|
|
||||||
extraPorts = extraPorts
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a "simple" SRAM group (active high/positive edge, 1 read-write port).
|
|
||||||
def generateSimpleSRAMGroup(prefix: String, mux: Int, depth: Range, width: Range, maskGran: Option[Int] = None, extraPorts: Seq[MacroExtraPort] = List()): SRAMGroup = {
|
|
||||||
SRAMGroup(Seq("mygroup_", "width", "x", "depth", "_", "VT"), "1rw", Seq("svt", "lvt", "ulvt"), mux, depth, width, Seq(generateReadWritePort(prefix, None, None, maskGran)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 'vt': ('svt','lvt','ulvt'), 'mux': 2, 'depth': range(16,513,8), 'width': range(8,289,2), 'ports': 1
|
|
||||||
// 'vt': ('svt','lvt','ulvt'), 'mux': 4, 'depth': range(32,1025,16), 'width': range(4,145), 'ports': 1}
|
|
||||||
def generateSRAMCompiler(name: String, prefix: String): mdf.macrolib.SRAMCompiler = {
|
|
||||||
SRAMCompiler(name, Seq(
|
|
||||||
generateSimpleSRAMGroup(prefix, 2, Range(16, 512, 8), Range(8, 288, 2)),
|
|
||||||
generateSimpleSRAMGroup(prefix, 4, Range(32, 1024, 16), Range(4, 144, 1))
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic "simple" test generator.
|
|
||||||
// Set up scaffolding for generating memories, files, etc.
|
|
||||||
// Override this generator to specify the expected FIRRTL output.
|
|
||||||
trait HasSimpleTestGenerator {
|
|
||||||
this: MacroCompilerSpec with HasSRAMGenerator =>
|
|
||||||
// Override these with "override lazy val".
|
|
||||||
// Why lazy? These are used in the constructor here so overriding non-lazily
|
|
||||||
// would be too late.
|
|
||||||
def useCompiler: Boolean = false
|
|
||||||
def memWidth: Int
|
|
||||||
def libWidth: Int
|
|
||||||
def memDepth: BigInt
|
|
||||||
def libDepth: BigInt
|
|
||||||
def memMaskGran: Option[Int] = None
|
|
||||||
def libMaskGran: Option[Int] = None
|
|
||||||
def extraPorts: Seq[mdf.macrolib.MacroExtraPort] = List()
|
|
||||||
def extraTag: String = ""
|
|
||||||
|
|
||||||
// "Effective" libMaskGran by considering write_enable.
|
|
||||||
val effectiveLibMaskGran = libMaskGran.getOrElse(libWidth)
|
|
||||||
|
|
||||||
// Override this in the sub-generator if you need a more specific name.
|
|
||||||
// Defaults to using reflection to pull the name of the test using this
|
|
||||||
// generator.
|
|
||||||
def generatorType: String = this.getClass.getSimpleName
|
|
||||||
|
|
||||||
//require (memDepth >= libDepth)
|
|
||||||
|
|
||||||
// Convenience variables to check if a mask exists.
|
|
||||||
val memHasMask = memMaskGran != None
|
|
||||||
val libHasMask = libMaskGran != None
|
|
||||||
// We need to figure out how many mask bits there are in the mem.
|
|
||||||
val memMaskBits = if (memHasMask) memWidth / memMaskGran.get else 0
|
|
||||||
val libMaskBits = if (libHasMask) libWidth / libMaskGran.get else 0
|
|
||||||
|
|
||||||
val extraTagPrefixed = if (extraTag == "") "" else ("-" + extraTag)
|
|
||||||
|
|
||||||
val mem = s"mem-${generatorType}${extraTagPrefixed}.json"
|
|
||||||
val lib = s"lib-${generatorType}${extraTagPrefixed}.json"
|
|
||||||
val v = s"${generatorType}${extraTagPrefixed}.v"
|
|
||||||
|
|
||||||
lazy val mem_name = "target_memory"
|
|
||||||
val mem_addr_width = MacroCompilerMath.ceilLog2(memDepth)
|
|
||||||
|
|
||||||
lazy val lib_name = "awesome_lib_mem"
|
|
||||||
val lib_addr_width = MacroCompilerMath.ceilLog2(libDepth)
|
|
||||||
|
|
||||||
// Override these to change the port prefixes if needed.
|
|
||||||
def libPortPrefix: String = "lib"
|
|
||||||
def memPortPrefix: String = "outer"
|
|
||||||
|
|
||||||
// These generate "simple" SRAMs (1 masked read-write port) by default,
|
|
||||||
// but can be overridden if need be.
|
|
||||||
def generateLibSRAM() = generateSRAM(lib_name, libPortPrefix, libWidth, libDepth, libMaskGran, extraPorts)
|
|
||||||
def generateMemSRAM() = generateSRAM(mem_name, memPortPrefix, memWidth, memDepth, memMaskGran)
|
|
||||||
|
|
||||||
def libSRAM = generateLibSRAM
|
|
||||||
def memSRAM = generateMemSRAM
|
|
||||||
|
|
||||||
def libSRAMs: Seq[SRAMMacro] = Seq(libSRAM)
|
|
||||||
def memSRAMs: Seq[SRAMMacro] = Seq(memSRAM)
|
|
||||||
|
|
||||||
writeToLib(lib, libSRAMs)
|
|
||||||
writeToMem(mem, memSRAMs)
|
|
||||||
|
|
||||||
// For masks, width it's a bit tricky since we have to consider cases like
|
|
||||||
// memMaskGran = 4 and libMaskGran = 8.
|
|
||||||
// Consider the actually usable libWidth in cases like the above.
|
|
||||||
val usableLibWidth = if (memMaskGran.getOrElse(Int.MaxValue) < effectiveLibMaskGran) memMaskGran.get else libWidth
|
|
||||||
|
|
||||||
// Number of lib instances needed to hold the mem, in both directions.
|
|
||||||
// Round up (e.g. 1.5 instances = effectively 2 instances)
|
|
||||||
val depthInstances = math.ceil(memDepth.toFloat / libDepth.toFloat).toInt
|
|
||||||
val widthInstances = math.ceil(memWidth.toFloat / usableLibWidth).toInt
|
|
||||||
|
|
||||||
// Number of width bits in the last width-direction memory.
|
|
||||||
// e.g. if memWidth = 16 and libWidth = 8, this would be 8 since the last memory 0_1 has 8 bits of input width.
|
|
||||||
// e.g. if memWidth = 9 and libWidth = 8, this would be 1 since the last memory 0_1 has 1 bit of input width.
|
|
||||||
lazy val lastWidthBits = if (memWidth % usableLibWidth == 0) usableLibWidth else (memWidth % usableLibWidth)
|
|
||||||
lazy val selectBits = mem_addr_width - lib_addr_width
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience function to generate a mask statement.
|
|
||||||
* @param widthInst Width instance (mem_0_x)
|
|
||||||
* @param depthInst Depth instance (mem_x_0)
|
|
||||||
*/
|
|
||||||
def generateMaskStatement(widthInst: Int, depthInst: Int): String = {
|
|
||||||
// Width of this submemory.
|
|
||||||
val myMemWidth = if (widthInst == widthInstances - 1) lastWidthBits else usableLibWidth
|
|
||||||
// Base bit of this submemory.
|
|
||||||
// e.g. if libWidth is 8 and this is submemory 2 (0-indexed), then this
|
|
||||||
// would be 16.
|
|
||||||
val myBaseBit = usableLibWidth*widthInst
|
|
||||||
|
|
||||||
if (libMaskGran.isDefined) {
|
|
||||||
if (memMaskGran.isEmpty) {
|
|
||||||
// If there is no memory mask, we should just turn all the lib mask
|
|
||||||
// bits high.
|
|
||||||
s"""mem_${depthInst}_${widthInst}.lib_mask <= UInt<${libMaskBits}>("h${((1 << libMaskBits) - 1).toHexString}")"""
|
|
||||||
} else {
|
|
||||||
// Calculate which bit of outer_mask contains the given bit.
|
|
||||||
// e.g. if memMaskGran = 2, libMaskGran = 1 and libWidth = 4, then
|
|
||||||
// calculateMaskBit({0, 1}) = 0 and calculateMaskBit({1, 2}) = 1
|
|
||||||
def calculateMaskBit(bit:Int): Int = bit / memMaskGran.getOrElse(memWidth)
|
|
||||||
|
|
||||||
val bitsArr = ((libMaskBits - 1 to 0 by -1) map (x => {
|
|
||||||
if (x*libMaskGran.get > myMemWidth) {
|
|
||||||
// If we have extra mask bits leftover after the effective width,
|
|
||||||
// disable those bits.
|
|
||||||
"""UInt<1>("h0")"""
|
|
||||||
} else {
|
|
||||||
val outerMaskBit = calculateMaskBit(x*libMaskGran.get + myBaseBit)
|
|
||||||
s"bits(outer_mask, ${outerMaskBit}, ${outerMaskBit})"
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
val maskVal = bitsArr.reduceRight((bit, rest) => s"cat($bit, $rest)")
|
|
||||||
s"mem_${depthInst}_${widthInst}.lib_mask <= ${maskVal}"
|
|
||||||
}
|
|
||||||
} else ""
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Helper function to generate a port.
|
|
||||||
*
|
|
||||||
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
|
||||||
* @param addrWidth Address port width
|
|
||||||
* @param width data width
|
|
||||||
* @param write Has a write port?
|
|
||||||
* @param writeEnable Has a write enable port?
|
|
||||||
* @param read Has a read port?
|
|
||||||
* @param readEnable Has a read enable port?
|
|
||||||
* @param mask Mask granularity (# bits) of the port or None.
|
|
||||||
* @param extraPorts Extra ports (name, # bits)
|
|
||||||
*/
|
|
||||||
def generatePort(prefix: String, addrWidth: Int, width: Int, write: Boolean, writeEnable: Boolean, read: Boolean, readEnable: Boolean, mask: Option[Int], extraPorts: Seq[(String, Int)] = Seq()): String = {
|
|
||||||
val realPrefix = if (prefix == "") "" else prefix + "_"
|
|
||||||
|
|
||||||
val readStr = if (read) s"output ${realPrefix}dout : UInt<$width>" else ""
|
|
||||||
val writeStr = if (write) s"input ${realPrefix}din : UInt<$width>" else ""
|
|
||||||
val readEnableStr = if (readEnable) s"input ${realPrefix}read_en : UInt<1>" else ""
|
|
||||||
val writeEnableStr = if (writeEnable) s"input ${realPrefix}write_en : UInt<1>" else ""
|
|
||||||
val maskStr = mask match {
|
|
||||||
case Some(maskBits: Int) => s"input ${realPrefix}mask : UInt<$maskBits>"
|
|
||||||
case _ => ""
|
|
||||||
}
|
|
||||||
val extraPortsStr = extraPorts.map { case (name, bits) => s" input $name : UInt<$bits>" }.mkString("\n")
|
|
||||||
s"""
|
|
||||||
input ${realPrefix}addr : UInt<$addrWidth>
|
|
||||||
input ${realPrefix}clk : Clock
|
|
||||||
$writeStr
|
|
||||||
$readStr
|
|
||||||
$readEnableStr
|
|
||||||
$writeEnableStr
|
|
||||||
$maskStr
|
|
||||||
$extraPortsStr
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to generate a RW footer port.
|
|
||||||
*
|
|
||||||
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
|
||||||
* @param readEnable Has a read enable port?
|
|
||||||
* @param mask Mask granularity (# bits) of the port or None.
|
|
||||||
* @param extraPorts Extra ports (name, # bits)
|
|
||||||
*/
|
|
||||||
def generateReadWriteFooterPort(prefix: String, readEnable: Boolean, mask: Option[Int], extraPorts: Seq[(String, Int)] = Seq()): String = {
|
|
||||||
generatePort(prefix, lib_addr_width, libWidth,
|
|
||||||
write = true, writeEnable = true, read = true, readEnable = readEnable, mask = mask, extraPorts = extraPorts)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Helper function to generate a RW header port.
|
|
||||||
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
|
||||||
* @param readEnable Has a read enable port?
|
|
||||||
* @param mask Mask granularity (# bits) of the port or None. */
|
|
||||||
def generateReadWriteHeaderPort(prefix: String, readEnable: Boolean, mask: Option[Int]): String = {
|
|
||||||
generatePort(prefix, mem_addr_width, memWidth,
|
|
||||||
write=true, writeEnable=true, read=true, readEnable=readEnable, mask)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the header memory ports.
|
|
||||||
def generateHeaderPorts(): String = {
|
|
||||||
require (memSRAM.ports.size == 1, "Header generator only supports single RW port mem")
|
|
||||||
generateReadWriteHeaderPort(memPortPrefix, memSRAM.ports(0).readEnable.isDefined, if (memHasMask) Some(memMaskBits) else None)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the header (contains the circuit statement and the target memory
|
|
||||||
// module.
|
|
||||||
def generateHeader(): String = {
|
|
||||||
s"""
|
|
||||||
circuit $mem_name :
|
|
||||||
module $mem_name :
|
|
||||||
${generateHeaderPorts}
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the target memory ports.
|
|
||||||
def generateFooterPorts(): String = {
|
|
||||||
require(libSRAM.ports.size == 1, "Footer generator only supports single RW port mem")
|
|
||||||
generateReadWriteFooterPort(libPortPrefix, libSRAM.ports(0).readEnable.isDefined,
|
|
||||||
if (libHasMask) Some(libMaskBits) else None, extraPorts.map(p => (p.name, p.width)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the footer (contains the target memory extmodule declaration by default).
|
|
||||||
def generateFooter(): String = {
|
|
||||||
s"""
|
|
||||||
extmodule $lib_name :
|
|
||||||
${generateFooterPorts}
|
|
||||||
|
|
||||||
defname = $lib_name
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Abstract method to generate body; to be overridden by specific generator type.
|
|
||||||
def generateBody(): String
|
|
||||||
|
|
||||||
// Generate the entire output from header, body, and footer.
|
|
||||||
def generateOutput(): String = {
|
|
||||||
s"""
|
|
||||||
${generateHeader}
|
|
||||||
${generateBody}
|
|
||||||
${generateFooter}
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
val output = generateOutput()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use this trait for tests that invoke the memory compiler without lib.
|
|
||||||
trait HasNoLibTestGenerator extends HasSimpleTestGenerator {
|
|
||||||
this: MacroCompilerSpec with HasSRAMGenerator =>
|
|
||||||
|
|
||||||
// If there isn't a lib, then the "lib" will become a FIRRTL "mem", which
|
|
||||||
// in turn becomes synthesized flops.
|
|
||||||
// Therefore, make "lib" width/depth equal to the mem.
|
|
||||||
override lazy val libDepth = memDepth
|
|
||||||
override lazy val libWidth = memWidth
|
|
||||||
override lazy val lib_name = mem_name
|
|
||||||
// Do the same for port names.
|
|
||||||
override lazy val libPortPrefix = memPortPrefix
|
|
||||||
|
|
||||||
// If there is no lib, don't generate a body.
|
|
||||||
override def generateBody = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -4,10 +4,9 @@ import mdf.macrolib._
|
|||||||
|
|
||||||
/** Tests to check that the cost function mechanism is working properly. */
|
/** 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>
|
||||||
556
macros/src/test/scala/barstools/macros/MacroCompilerSpec.scala
Normal file
556
macros/src/test/scala/barstools/macros/MacroCompilerSpec.scala
Normal file
@@ -0,0 +1,556 @@
|
|||||||
|
// See LICENSE for license details.
|
||||||
|
|
||||||
|
package barstools.macros
|
||||||
|
|
||||||
|
import firrtl.Parser.parse
|
||||||
|
import firrtl.ir.{Circuit, NoInfo}
|
||||||
|
import firrtl.passes.RemoveEmpty
|
||||||
|
import mdf.macrolib.SRAMMacro
|
||||||
|
import org.scalatest.flatspec.AnyFlatSpec
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class MacroCompilerSpec extends AnyFlatSpec with Matchers {
|
||||||
|
import scala.language.implicitConversions
|
||||||
|
implicit def String2SomeString(i: String): Option[String] = Some(i)
|
||||||
|
val testDir: String = "test_run_dir/macros"
|
||||||
|
new File(testDir).mkdirs // Make sure the testDir exists
|
||||||
|
|
||||||
|
// Override these to change the prefixing of macroDir and testDir
|
||||||
|
val memPrefix: String = testDir
|
||||||
|
val libPrefix: String = testDir
|
||||||
|
val vPrefix: String = testDir
|
||||||
|
|
||||||
|
// Override this to use a different cost metric.
|
||||||
|
// If this is None, the compile() call will not have any -c/-cp arguments, and
|
||||||
|
// execute() will use CostMetric.default.
|
||||||
|
val costMetric: Option[CostMetric] = None
|
||||||
|
private def getCostMetric: CostMetric = costMetric.getOrElse(CostMetric.default)
|
||||||
|
|
||||||
|
private def costMetricCmdLine = {
|
||||||
|
costMetric match {
|
||||||
|
case None => Nil
|
||||||
|
case Some(m) => {
|
||||||
|
val name = m.name
|
||||||
|
val params = m.commandLineParams
|
||||||
|
List("-c", name) ++ params.flatMap { case (key, value) => List("-cp", key, value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def args(mem: String, lib: Option[String], v: String, synflops: Boolean, useCompiler: Boolean) =
|
||||||
|
List("-m", mem.toString, "-v", v) ++
|
||||||
|
(lib match {
|
||||||
|
case None => Nil
|
||||||
|
case Some(l) => List("-l", l.toString)
|
||||||
|
}) ++
|
||||||
|
costMetricCmdLine ++
|
||||||
|
(if (synflops) List("--mode", "synflops") else Nil) ++
|
||||||
|
(if (useCompiler) List("--use-compiler") else Nil)
|
||||||
|
|
||||||
|
// Run the full compiler as if from the command line interface.
|
||||||
|
// Generates the Verilog; useful in testing since an error will throw an
|
||||||
|
// exception.
|
||||||
|
def compile(mem: String, lib: String, v: String, synflops: Boolean) {
|
||||||
|
compile(mem, Some(lib), v, synflops)
|
||||||
|
}
|
||||||
|
def compile(mem: String, lib: Option[String], v: String, synflops: Boolean, useCompiler: Boolean = false) {
|
||||||
|
var mem_full = concat(memPrefix, mem)
|
||||||
|
var lib_full = concat(libPrefix, lib)
|
||||||
|
var v_full = concat(vPrefix, v)
|
||||||
|
|
||||||
|
MacroCompiler.run(args(mem_full, lib_full, v_full, synflops, useCompiler))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions to write macro libraries to the given files.
|
||||||
|
def writeToLib(lib: String, libs: Seq[mdf.macrolib.Macro]) = {
|
||||||
|
mdf.macrolib.Utils.writeMDFToPath(Some(concat(libPrefix, lib)), libs)
|
||||||
|
}
|
||||||
|
|
||||||
|
def writeToMem(mem: String, mems: Seq[mdf.macrolib.Macro]) = {
|
||||||
|
mdf.macrolib.Utils.writeMDFToPath(Some(concat(memPrefix, mem)), mems)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience function for running both compile, execute, and test at once.
|
||||||
|
def compileExecuteAndTest(
|
||||||
|
mem: String,
|
||||||
|
lib: Option[String],
|
||||||
|
v: String,
|
||||||
|
output: String,
|
||||||
|
synflops: Boolean = false,
|
||||||
|
useCompiler: Boolean = false
|
||||||
|
): Unit = {
|
||||||
|
compile(mem, lib, v, synflops, useCompiler)
|
||||||
|
val result = execute(mem, lib, synflops, useCompiler)
|
||||||
|
test(result, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare FIRRTL outputs after reparsing output with ScalaTest ("should be").
|
||||||
|
def test(result: Circuit, output: String): Unit = {
|
||||||
|
val gold = RemoveEmpty.run(parse(output))
|
||||||
|
(result.serialize) should be(gold.serialize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the macro compiler and returns a Circuit containing the output of
|
||||||
|
// the memory compiler.
|
||||||
|
def execute(memFile: Option[String], libFile: Option[String], synflops: Boolean): Circuit =
|
||||||
|
execute(memFile, libFile, synflops, false)
|
||||||
|
def execute(memFile: Option[String], libFile: Option[String], synflops: Boolean, useCompiler: Boolean): Circuit = {
|
||||||
|
var mem_full = concat(memPrefix, memFile)
|
||||||
|
var lib_full = concat(libPrefix, libFile)
|
||||||
|
|
||||||
|
require(memFile.isDefined)
|
||||||
|
val mems: Seq[Macro] = Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(mem_full)).get.map(new Macro(_))
|
||||||
|
val libs: Option[Seq[Macro]] = if (useCompiler) {
|
||||||
|
Utils.findSRAMCompiler(mdf.macrolib.Utils.readMDFFromPath(lib_full)).map { x =>
|
||||||
|
Utils.buildSRAMMacros(x).map(new Macro(_))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Utils.filterForSRAM(mdf.macrolib.Utils.readMDFFromPath(lib_full)) match {
|
||||||
|
case Some(x) => Some(x.map(new Macro(_)))
|
||||||
|
case None => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val macros = mems.map(_.blackbox)
|
||||||
|
val circuit = Circuit(NoInfo, macros, macros.last.name)
|
||||||
|
val passes = Seq(
|
||||||
|
new MacroCompilerPass(
|
||||||
|
Some(mems),
|
||||||
|
libs,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
getCostMetric,
|
||||||
|
if (synflops) MacroCompilerAnnotation.Synflops else MacroCompilerAnnotation.Default
|
||||||
|
),
|
||||||
|
new SynFlopsPass(synflops, libs.getOrElse(mems)),
|
||||||
|
RemoveEmpty
|
||||||
|
)
|
||||||
|
val result: Circuit = (passes.foldLeft(circuit))((c, pass) => pass.run(c))
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to deal with String + Option[String]
|
||||||
|
private def concat(a: String, b: String): String = { a + "/" + b }
|
||||||
|
private def concat(a: String, b: Option[String]): Option[String] = {
|
||||||
|
b match {
|
||||||
|
case Some(b2: String) => Some(a + "/" + b2)
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A collection of standard SRAM generators.
|
||||||
|
trait HasSRAMGenerator {
|
||||||
|
import mdf.macrolib._
|
||||||
|
|
||||||
|
import scala.language.implicitConversions
|
||||||
|
implicit def Int2SomeInt(i: Int): Option[Int] = Some(i)
|
||||||
|
implicit def BigInt2SomeBigInt(i: BigInt): Option[BigInt] = Some(i)
|
||||||
|
|
||||||
|
// Generate a standard (read/write/combo) port for testing.
|
||||||
|
// Helper methods for optional width argument
|
||||||
|
def generateTestPort(
|
||||||
|
prefix: String,
|
||||||
|
width: Option[Int],
|
||||||
|
depth: Option[BigInt],
|
||||||
|
maskGran: Option[Int] = None,
|
||||||
|
read: Boolean,
|
||||||
|
readEnable: Boolean = false,
|
||||||
|
write: Boolean,
|
||||||
|
writeEnable: Boolean = false
|
||||||
|
): MacroPort = {
|
||||||
|
val realPrefix = if (prefix == "") "" else prefix + "_"
|
||||||
|
|
||||||
|
MacroPort(
|
||||||
|
address = PolarizedPort(name = realPrefix + "addr", polarity = ActiveHigh),
|
||||||
|
clock = Some(PolarizedPort(name = realPrefix + "clk", polarity = PositiveEdge)),
|
||||||
|
readEnable = if (readEnable) Some(PolarizedPort(name = realPrefix + "read_en", polarity = ActiveHigh)) else None,
|
||||||
|
writeEnable =
|
||||||
|
if (writeEnable) Some(PolarizedPort(name = realPrefix + "write_en", polarity = ActiveHigh)) else None,
|
||||||
|
output = if (read) Some(PolarizedPort(name = realPrefix + "dout", polarity = ActiveHigh)) else None,
|
||||||
|
input = if (write) Some(PolarizedPort(name = realPrefix + "din", polarity = ActiveHigh)) else None,
|
||||||
|
maskPort = maskGran match {
|
||||||
|
case Some(x: Int) => Some(PolarizedPort(name = realPrefix + "mask", polarity = ActiveHigh))
|
||||||
|
case _ => None
|
||||||
|
},
|
||||||
|
maskGran = maskGran,
|
||||||
|
width = width,
|
||||||
|
depth = depth // These numbers don't matter here.
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a read port for testing.
|
||||||
|
def generateReadPort(
|
||||||
|
prefix: String,
|
||||||
|
width: Option[Int],
|
||||||
|
depth: Option[BigInt],
|
||||||
|
readEnable: Boolean = false
|
||||||
|
): MacroPort = {
|
||||||
|
generateTestPort(prefix, width, depth, write = false, read = true, readEnable = readEnable)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a write port for testing.
|
||||||
|
def generateWritePort(
|
||||||
|
prefix: String,
|
||||||
|
width: Option[Int],
|
||||||
|
depth: Option[BigInt],
|
||||||
|
maskGran: Option[Int] = None,
|
||||||
|
writeEnable: Boolean = true
|
||||||
|
): MacroPort = {
|
||||||
|
generateTestPort(prefix, width, depth, maskGran = maskGran, write = true, read = false, writeEnable = writeEnable)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a simple read-write port for testing.
|
||||||
|
def generateReadWritePort(
|
||||||
|
prefix: String,
|
||||||
|
width: Option[Int],
|
||||||
|
depth: Option[BigInt],
|
||||||
|
maskGran: Option[Int] = None
|
||||||
|
): MacroPort = {
|
||||||
|
generateTestPort(
|
||||||
|
prefix,
|
||||||
|
width,
|
||||||
|
depth,
|
||||||
|
maskGran = maskGran,
|
||||||
|
write = true,
|
||||||
|
writeEnable = true,
|
||||||
|
read = true,
|
||||||
|
readEnable = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a "simple" SRAM (active high/positive edge, 1 read-write port).
|
||||||
|
def generateSRAM(
|
||||||
|
name: String,
|
||||||
|
prefix: String,
|
||||||
|
width: Int,
|
||||||
|
depth: BigInt,
|
||||||
|
maskGran: Option[Int] = None,
|
||||||
|
extraPorts: Seq[MacroExtraPort] = List()
|
||||||
|
): SRAMMacro = {
|
||||||
|
SRAMMacro(
|
||||||
|
name = name,
|
||||||
|
width = width,
|
||||||
|
depth = depth,
|
||||||
|
family = "1rw",
|
||||||
|
ports = Seq(generateReadWritePort(prefix, width, depth, maskGran)),
|
||||||
|
extraPorts = extraPorts
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a "simple" SRAM group (active high/positive edge, 1 read-write port).
|
||||||
|
def generateSimpleSRAMGroup(
|
||||||
|
prefix: String,
|
||||||
|
mux: Int,
|
||||||
|
depth: Range,
|
||||||
|
width: Range,
|
||||||
|
maskGran: Option[Int] = None,
|
||||||
|
extraPorts: Seq[MacroExtraPort] = List()
|
||||||
|
): SRAMGroup = {
|
||||||
|
SRAMGroup(
|
||||||
|
Seq("mygroup_", "width", "x", "depth", "_", "VT"),
|
||||||
|
"1rw",
|
||||||
|
Seq("svt", "lvt", "ulvt"),
|
||||||
|
mux,
|
||||||
|
depth,
|
||||||
|
width,
|
||||||
|
Seq(generateReadWritePort(prefix, None, None, maskGran))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'vt': ('svt','lvt','ulvt'), 'mux': 2, 'depth': range(16,513,8), 'width': range(8,289,2), 'ports': 1
|
||||||
|
// 'vt': ('svt','lvt','ulvt'), 'mux': 4, 'depth': range(32,1025,16), 'width': range(4,145), 'ports': 1}
|
||||||
|
def generateSRAMCompiler(name: String, prefix: String): mdf.macrolib.SRAMCompiler = {
|
||||||
|
SRAMCompiler(
|
||||||
|
name,
|
||||||
|
Seq(
|
||||||
|
generateSimpleSRAMGroup(prefix, 2, Range(16, 512, 8), Range(8, 288, 2)),
|
||||||
|
generateSimpleSRAMGroup(prefix, 4, Range(32, 1024, 16), Range(4, 144, 1))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic "simple" test generator.
|
||||||
|
// Set up scaffolding for generating memories, files, etc.
|
||||||
|
// Override this generator to specify the expected FIRRTL output.
|
||||||
|
trait HasSimpleTestGenerator {
|
||||||
|
this: MacroCompilerSpec with HasSRAMGenerator =>
|
||||||
|
// Override these with "override lazy val".
|
||||||
|
// Why lazy? These are used in the constructor here so overriding non-lazily
|
||||||
|
// would be too late.
|
||||||
|
def useCompiler: Boolean = false
|
||||||
|
def memWidth: Int
|
||||||
|
def libWidth: Int
|
||||||
|
def memDepth: BigInt
|
||||||
|
def libDepth: BigInt
|
||||||
|
def memMaskGran: Option[Int] = None
|
||||||
|
def libMaskGran: Option[Int] = None
|
||||||
|
def extraPorts: Seq[mdf.macrolib.MacroExtraPort] = List()
|
||||||
|
def extraTag: String = ""
|
||||||
|
|
||||||
|
// "Effective" libMaskGran by considering write_enable.
|
||||||
|
val effectiveLibMaskGran = libMaskGran.getOrElse(libWidth)
|
||||||
|
|
||||||
|
// Override this in the sub-generator if you need a more specific name.
|
||||||
|
// Defaults to using reflection to pull the name of the test using this
|
||||||
|
// generator.
|
||||||
|
def generatorType: String = this.getClass.getSimpleName
|
||||||
|
|
||||||
|
//require (memDepth >= libDepth)
|
||||||
|
|
||||||
|
// Convenience variables to check if a mask exists.
|
||||||
|
val memHasMask = memMaskGran != None
|
||||||
|
val libHasMask = libMaskGran != None
|
||||||
|
// We need to figure out how many mask bits there are in the mem.
|
||||||
|
val memMaskBits = if (memHasMask) memWidth / memMaskGran.get else 0
|
||||||
|
val libMaskBits = if (libHasMask) libWidth / libMaskGran.get else 0
|
||||||
|
|
||||||
|
val extraTagPrefixed = if (extraTag == "") "" else ("-" + extraTag)
|
||||||
|
|
||||||
|
val mem = s"mem-${generatorType}${extraTagPrefixed}.json"
|
||||||
|
val lib = s"lib-${generatorType}${extraTagPrefixed}.json"
|
||||||
|
val v = s"${generatorType}${extraTagPrefixed}.v"
|
||||||
|
|
||||||
|
lazy val mem_name = "target_memory"
|
||||||
|
val mem_addr_width = MacroCompilerMath.ceilLog2(memDepth)
|
||||||
|
|
||||||
|
lazy val lib_name = "awesome_lib_mem"
|
||||||
|
val lib_addr_width = MacroCompilerMath.ceilLog2(libDepth)
|
||||||
|
|
||||||
|
// Override these to change the port prefixes if needed.
|
||||||
|
def libPortPrefix: String = "lib"
|
||||||
|
def memPortPrefix: String = "outer"
|
||||||
|
|
||||||
|
// These generate "simple" SRAMs (1 masked read-write port) by default,
|
||||||
|
// but can be overridden if need be.
|
||||||
|
def generateLibSRAM() = generateSRAM(lib_name, libPortPrefix, libWidth, libDepth, libMaskGran, extraPorts)
|
||||||
|
def generateMemSRAM() = generateSRAM(mem_name, memPortPrefix, memWidth, memDepth, memMaskGran)
|
||||||
|
|
||||||
|
def libSRAM = generateLibSRAM
|
||||||
|
def memSRAM = generateMemSRAM
|
||||||
|
|
||||||
|
def libSRAMs: Seq[SRAMMacro] = Seq(libSRAM)
|
||||||
|
def memSRAMs: Seq[SRAMMacro] = Seq(memSRAM)
|
||||||
|
|
||||||
|
writeToLib(lib, libSRAMs)
|
||||||
|
writeToMem(mem, memSRAMs)
|
||||||
|
|
||||||
|
// For masks, width it's a bit tricky since we have to consider cases like
|
||||||
|
// memMaskGran = 4 and libMaskGran = 8.
|
||||||
|
// Consider the actually usable libWidth in cases like the above.
|
||||||
|
val usableLibWidth = if (memMaskGran.getOrElse(Int.MaxValue) < effectiveLibMaskGran) memMaskGran.get else libWidth
|
||||||
|
|
||||||
|
// Number of lib instances needed to hold the mem, in both directions.
|
||||||
|
// Round up (e.g. 1.5 instances = effectively 2 instances)
|
||||||
|
val depthInstances = math.ceil(memDepth.toFloat / libDepth.toFloat).toInt
|
||||||
|
val widthInstances = math.ceil(memWidth.toFloat / usableLibWidth).toInt
|
||||||
|
|
||||||
|
// Number of width bits in the last width-direction memory.
|
||||||
|
// e.g. if memWidth = 16 and libWidth = 8, this would be 8 since the last memory 0_1 has 8 bits of input width.
|
||||||
|
// e.g. if memWidth = 9 and libWidth = 8, this would be 1 since the last memory 0_1 has 1 bit of input width.
|
||||||
|
lazy val lastWidthBits = if (memWidth % usableLibWidth == 0) usableLibWidth else (memWidth % usableLibWidth)
|
||||||
|
lazy val selectBits = mem_addr_width - lib_addr_width
|
||||||
|
|
||||||
|
/** Convenience function to generate a mask statement.
|
||||||
|
* @param widthInst Width instance (mem_0_x)
|
||||||
|
* @param depthInst Depth instance (mem_x_0)
|
||||||
|
*/
|
||||||
|
def generateMaskStatement(widthInst: Int, depthInst: Int): String = {
|
||||||
|
// Width of this submemory.
|
||||||
|
val myMemWidth = if (widthInst == widthInstances - 1) lastWidthBits else usableLibWidth
|
||||||
|
// Base bit of this submemory.
|
||||||
|
// e.g. if libWidth is 8 and this is submemory 2 (0-indexed), then this
|
||||||
|
// would be 16.
|
||||||
|
val myBaseBit = usableLibWidth * widthInst
|
||||||
|
|
||||||
|
if (libMaskGran.isDefined) {
|
||||||
|
if (memMaskGran.isEmpty) {
|
||||||
|
// If there is no memory mask, we should just turn all the lib mask
|
||||||
|
// bits high.
|
||||||
|
s"""mem_${depthInst}_${widthInst}.lib_mask <= UInt<${libMaskBits}>("h${((1 << libMaskBits) - 1).toHexString}")"""
|
||||||
|
} else {
|
||||||
|
// Calculate which bit of outer_mask contains the given bit.
|
||||||
|
// e.g. if memMaskGran = 2, libMaskGran = 1 and libWidth = 4, then
|
||||||
|
// calculateMaskBit({0, 1}) = 0 and calculateMaskBit({1, 2}) = 1
|
||||||
|
def calculateMaskBit(bit: Int): Int = bit / memMaskGran.getOrElse(memWidth)
|
||||||
|
|
||||||
|
val bitsArr = ((libMaskBits - 1 to 0 by -1).map(x => {
|
||||||
|
if (x * libMaskGran.get > myMemWidth) {
|
||||||
|
// If we have extra mask bits leftover after the effective width,
|
||||||
|
// disable those bits.
|
||||||
|
"""UInt<1>("h0")"""
|
||||||
|
} else {
|
||||||
|
val outerMaskBit = calculateMaskBit(x * libMaskGran.get + myBaseBit)
|
||||||
|
s"bits(outer_mask, ${outerMaskBit}, ${outerMaskBit})"
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
val maskVal = bitsArr.reduceRight((bit, rest) => s"cat($bit, $rest)")
|
||||||
|
s"mem_${depthInst}_${widthInst}.lib_mask <= ${maskVal}"
|
||||||
|
}
|
||||||
|
} else ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Helper function to generate a port.
|
||||||
|
*
|
||||||
|
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
||||||
|
* @param addrWidth Address port width
|
||||||
|
* @param width data width
|
||||||
|
* @param write Has a write port?
|
||||||
|
* @param writeEnable Has a write enable port?
|
||||||
|
* @param read Has a read port?
|
||||||
|
* @param readEnable Has a read enable port?
|
||||||
|
* @param mask Mask granularity (# bits) of the port or None.
|
||||||
|
* @param extraPorts Extra ports (name, # bits)
|
||||||
|
*/
|
||||||
|
def generatePort(
|
||||||
|
prefix: String,
|
||||||
|
addrWidth: Int,
|
||||||
|
width: Int,
|
||||||
|
write: Boolean,
|
||||||
|
writeEnable: Boolean,
|
||||||
|
read: Boolean,
|
||||||
|
readEnable: Boolean,
|
||||||
|
mask: Option[Int],
|
||||||
|
extraPorts: Seq[(String, Int)] = Seq()
|
||||||
|
): String = {
|
||||||
|
val realPrefix = if (prefix == "") "" else prefix + "_"
|
||||||
|
|
||||||
|
val readStr = if (read) s"output ${realPrefix}dout : UInt<$width>" else ""
|
||||||
|
val writeStr = if (write) s"input ${realPrefix}din : UInt<$width>" else ""
|
||||||
|
val readEnableStr = if (readEnable) s"input ${realPrefix}read_en : UInt<1>" else ""
|
||||||
|
val writeEnableStr = if (writeEnable) s"input ${realPrefix}write_en : UInt<1>" else ""
|
||||||
|
val maskStr = mask match {
|
||||||
|
case Some(maskBits: Int) => s"input ${realPrefix}mask : UInt<$maskBits>"
|
||||||
|
case _ => ""
|
||||||
|
}
|
||||||
|
val extraPortsStr = extraPorts.map { case (name, bits) => s" input $name : UInt<$bits>" }.mkString("\n")
|
||||||
|
s"""
|
||||||
|
input ${realPrefix}addr : UInt<$addrWidth>
|
||||||
|
input ${realPrefix}clk : Clock
|
||||||
|
$writeStr
|
||||||
|
$readStr
|
||||||
|
$readEnableStr
|
||||||
|
$writeEnableStr
|
||||||
|
$maskStr
|
||||||
|
$extraPortsStr
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Helper function to generate a RW footer port.
|
||||||
|
*
|
||||||
|
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
||||||
|
* @param readEnable Has a read enable port?
|
||||||
|
* @param mask Mask granularity (# bits) of the port or None.
|
||||||
|
* @param extraPorts Extra ports (name, # bits)
|
||||||
|
*/
|
||||||
|
def generateReadWriteFooterPort(
|
||||||
|
prefix: String,
|
||||||
|
readEnable: Boolean,
|
||||||
|
mask: Option[Int],
|
||||||
|
extraPorts: Seq[(String, Int)] = Seq()
|
||||||
|
): String = {
|
||||||
|
generatePort(
|
||||||
|
prefix,
|
||||||
|
lib_addr_width,
|
||||||
|
libWidth,
|
||||||
|
write = true,
|
||||||
|
writeEnable = true,
|
||||||
|
read = true,
|
||||||
|
readEnable = readEnable,
|
||||||
|
mask = mask,
|
||||||
|
extraPorts = extraPorts
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Helper function to generate a RW header port.
|
||||||
|
* @param prefix Memory port prefix (e.g. "x" for ports like "x_clk")
|
||||||
|
* @param readEnable Has a read enable port?
|
||||||
|
* @param mask Mask granularity (# bits) of the port or None.
|
||||||
|
*/
|
||||||
|
def generateReadWriteHeaderPort(prefix: String, readEnable: Boolean, mask: Option[Int]): String = {
|
||||||
|
generatePort(
|
||||||
|
prefix,
|
||||||
|
mem_addr_width,
|
||||||
|
memWidth,
|
||||||
|
write = true,
|
||||||
|
writeEnable = true,
|
||||||
|
read = true,
|
||||||
|
readEnable = readEnable,
|
||||||
|
mask
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the header memory ports.
|
||||||
|
def generateHeaderPorts(): String = {
|
||||||
|
require(memSRAM.ports.size == 1, "Header generator only supports single RW port mem")
|
||||||
|
generateReadWriteHeaderPort(
|
||||||
|
memPortPrefix,
|
||||||
|
memSRAM.ports(0).readEnable.isDefined,
|
||||||
|
if (memHasMask) Some(memMaskBits) else None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the header (contains the circuit statement and the target memory
|
||||||
|
// module.
|
||||||
|
def generateHeader(): String = {
|
||||||
|
s"""
|
||||||
|
circuit $mem_name :
|
||||||
|
module $mem_name :
|
||||||
|
${generateHeaderPorts}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the target memory ports.
|
||||||
|
def generateFooterPorts(): String = {
|
||||||
|
require(libSRAM.ports.size == 1, "Footer generator only supports single RW port mem")
|
||||||
|
generateReadWriteFooterPort(
|
||||||
|
libPortPrefix,
|
||||||
|
libSRAM.ports(0).readEnable.isDefined,
|
||||||
|
if (libHasMask) Some(libMaskBits) else None,
|
||||||
|
extraPorts.map(p => (p.name, p.width))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the footer (contains the target memory extmodule declaration by default).
|
||||||
|
def generateFooter(): String = {
|
||||||
|
s"""
|
||||||
|
extmodule $lib_name :
|
||||||
|
${generateFooterPorts}
|
||||||
|
|
||||||
|
defname = $lib_name
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abstract method to generate body; to be overridden by specific generator type.
|
||||||
|
def generateBody(): String
|
||||||
|
|
||||||
|
// Generate the entire output from header, body, and footer.
|
||||||
|
def generateOutput(): String = {
|
||||||
|
s"""
|
||||||
|
${generateHeader}
|
||||||
|
${generateBody}
|
||||||
|
${generateFooter}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
val output = generateOutput()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use this trait for tests that invoke the memory compiler without lib.
|
||||||
|
trait HasNoLibTestGenerator extends HasSimpleTestGenerator {
|
||||||
|
this: MacroCompilerSpec with HasSRAMGenerator =>
|
||||||
|
|
||||||
|
// If there isn't a lib, then the "lib" will become a FIRRTL "mem", which
|
||||||
|
// in turn becomes synthesized flops.
|
||||||
|
// Therefore, make "lib" width/depth equal to the mem.
|
||||||
|
override lazy val libDepth = memDepth
|
||||||
|
override lazy val libWidth = memWidth
|
||||||
|
override lazy val lib_name = mem_name
|
||||||
|
// Do the same for port names.
|
||||||
|
override lazy val libPortPrefix = memPortPrefix
|
||||||
|
|
||||||
|
// If there is no lib, don't generate a body.
|
||||||
|
override def generateBody = ""
|
||||||
|
}
|
||||||
@@ -1,27 +1,26 @@
|
|||||||
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
|
||||||
@@ -31,7 +30,10 @@ class Masks_FourTypes_NonMaskedMem_NonMaskedLib extends MacroCompilerSpec with H
|
|||||||
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)
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -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>
|
||||||
@@ -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
|
||||||
@@ -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 }
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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])
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -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)
|
||||||
@@ -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 =>
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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("]", "")
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
}: _*)
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
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 =>
|
||||||
@@ -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)")
|
||||||
@@ -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")
|
||||||
Reference in New Issue
Block a user