aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorJack Koenig2021-06-22 09:52:53 -0700
committerGitHub2021-06-22 09:52:53 -0700
commit11128d93ea5412508b2616fda862abf05a59b435 (patch)
treeb49535602228ced1d4891c675b9f3021dad144ea /src/main
parentef4d9fc765ec1ccd336104c72bce7659bc9f4b64 (diff)
Fix VerilogMemDelays use before declaration (#2278)
The pass injects pipe registers immediately after the declaration of the memory. This can be problematic if the clock for the associated memory port is defined after the declaration of the memory. For any memory port clocks that are driven by non-ports, we now inject a wire before the pipe register declarations to be sure there are no use-before-declaration issues.
Diffstat (limited to 'src/main')
-rw-r--r--src/main/scala/firrtl/passes/memlib/VerilogMemDelays.scala32
1 files changed, 29 insertions, 3 deletions
diff --git a/src/main/scala/firrtl/passes/memlib/VerilogMemDelays.scala b/src/main/scala/firrtl/passes/memlib/VerilogMemDelays.scala
index a9b42eba..11184e60 100644
--- a/src/main/scala/firrtl/passes/memlib/VerilogMemDelays.scala
+++ b/src/main/scala/firrtl/passes/memlib/VerilogMemDelays.scala
@@ -56,6 +56,14 @@ object MemDelayAndReadwriteTransformer {
private val metaChars = raw"[\[\]\.]".r
private def flatName(e: Expression) = metaChars.replaceAllIn(e.serialize, "_")
+ /** Determines if all `RefLikeExpression` in this Expression are of kind [[PortKind]] */
+ def allPortKinds(expr: Expression): Boolean = expr match {
+ case reflike: RefLikeExpression => kind(expr) == PortKind
+ case other =>
+ other.foreach { (e: Expression) => if (!allPortKinds(e)) return false } // Early out
+ true
+ }
+
// Pipeline a group of signals with an associated valid signal. Gate registers when possible.
def pipelineWithValid(
ns: Namespace
@@ -126,12 +134,28 @@ class MemDelayAndReadwriteTransformer(m: DefModule, passthroughSimpleSyncReadMem
val rCmdDelay = if (mem.readUnderWrite == ReadUnderWrite.Old) 0 else mem.readLatency
val rRespDelay = if (mem.readUnderWrite == ReadUnderWrite.Old) mem.readLatency else 0
val wCmdDelay = mem.writeLatency - 1
+ val clockWires = new mutable.LinkedHashMap[WrappedExpression, (DefWire, Connect, Reference)]
+ // Memory port clocks may depend on something defined after the memory, create wires for clock
+ // Expressions that contain non-port references
+ def maybeInsertWire(expr: Expression): Expression = {
+ if (allPortKinds(expr)) expr
+ else {
+ def mkWire() = {
+ val wire = DefWire(NoInfo, ns.newName(s"${mem.name}_clock"), expr.tpe)
+ val ref = Reference(wire).copy(flow = SourceFlow)
+ val con = Connect(NoInfo, ref.copy(flow = SinkFlow), expr)
+ (wire, con, ref)
+ }
+ clockWires.getOrElseUpdate(we(expr), mkWire())._3
+ }
+ }
val readStmts = (mem.readers ++ mem.readwriters).map {
case r =>
def oldDriver(f: String) = swapMemRefs(netlist(we(memPortField(mem, r, f))))
def newField(f: String) = memPortField(newMem, rMap.getOrElse(r, r), f)
- val clk = oldDriver("clk")
+ // The memory port clock could depend on something defined after the memory
+ val clk = maybeInsertWire(oldDriver("clk"))
// Pack sources of read command inputs into WithValid object -> different for readwriter
val enSrc = if (rMap.contains(r)) AND(oldDriver("en"), NOT(oldDriver("wmode"))) else oldDriver("en")
@@ -160,7 +184,8 @@ class MemDelayAndReadwriteTransformer(m: DefModule, passthroughSimpleSyncReadMem
case w =>
def oldDriver(f: String) = swapMemRefs(netlist(we(memPortField(mem, w, f))))
def newField(f: String) = memPortField(newMem, wMap.getOrElse(w, w), f)
- val clk = oldDriver("clk")
+ // The memory port clock could depend on something defined after the memory
+ val clk = maybeInsertWire(oldDriver("clk"))
// Pack sources of write command inputs into WithValid object -> different for readwriter
val cmdSrc = if (wMap.contains(w)) {
@@ -180,8 +205,9 @@ class MemDelayAndReadwriteTransformer(m: DefModule, passthroughSimpleSyncReadMem
SplitStatements(cmdDecls, cmdConns ++ cmdPortConns)
}
+ newConns ++= clockWires.values.map(_._2)
newConns ++= (readStmts ++ writeStmts).flatMap(_.conns)
- Block(newMem +: (readStmts ++ writeStmts).flatMap(_.decls))
+ Block(newMem +: clockWires.values.map(_._1) ++: (readStmts ++ writeStmts).flatMap(_.decls))
case sx: Connect if kind(sx.loc) == MemKind =>
val (memRef, _) = Utils.splitRef(sx.loc)
// Filter old mem connections for *transformed* memories only