152 lines
5.9 KiB
Scala
152 lines
5.9 KiB
Scala
// See LICENSE for license details.
|
|
|
|
package barstools.macros
|
|
|
|
import barstools.macros.Utils._
|
|
import firrtl.Utils.{zero, one}
|
|
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 = firrtl.Utils.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)
|
|
}
|
|
}
|