diff options
| -rw-r--r-- | src/main/scala/firrtl/backends/verilog/VerilogEmitter.scala | 113 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/InfoSpec.scala | 10 |
2 files changed, 77 insertions, 46 deletions
diff --git a/src/main/scala/firrtl/backends/verilog/VerilogEmitter.scala b/src/main/scala/firrtl/backends/verilog/VerilogEmitter.scala index f854b33a..66df12e0 100644 --- a/src/main/scala/firrtl/backends/verilog/VerilogEmitter.scala +++ b/src/main/scala/firrtl/backends/verilog/VerilogEmitter.scala @@ -1065,32 +1065,53 @@ class VerilogEmitter extends SeqTransform with Emitter { val decl = if (fullSize > (1 << 29)) "reg /* sparse */" else "reg" declareVectorType(decl, sx.name, sx.dataType, sx.depth, sx.info) initialize_mem(sx, options) - if (sx.readLatency != 0 || sx.writeLatency != 1) + // Currently, no idiomatic way to directly emit write-first RW ports + val hasComplexRW = (sx.readwriters.nonEmpty && + (sx.readLatency != 1 || sx.readUnderWrite == ReadUnderWrite.New)) + if (sx.readLatency > 1 || sx.writeLatency != 1 || hasComplexRW) throw EmitterException( - "All memories should be transformed into " + - "blackboxes or combinational by previous passses" + Seq( + s"Memory ${sx.name} is too complex to emit directly.", + "Consider running VerilogMemDelays to simplify complex memories.", + "Alternatively, add the --repl-seq-mem flag to replace memories with blackboxes." + ).mkString(" ") ) + def createMemWire(name: String, tpe: Type, rhs: InfoExpr): Unit = { + declare("wire", name, tpe, MultiInfo(sx.info, rhs.info), rhs.expr) + } + for (r <- sx.readers) { val data = memPortField(sx, r, "data") val addr = memPortField(sx, r, "addr") - // Ports should share an always@posedge, so can't have intermediary wire - - declare("wire", LowerTypes.loweredName(data), data.tpe, sx.info) - declare("wire", LowerTypes.loweredName(addr), addr.tpe, sx.info) - // declare("wire", LowerTypes.loweredName(en), en.tpe) - - //; Read port - assign(addr, netlist(addr)) - // assign(en, netlist(en)) //;Connects value to m.r.en - val mem = WRef(sx.name, memType(sx), MemKind, UnknownFlow) - val memPort = WSubAccess(mem, addr, sx.dataType, UnknownFlow) + val en = memPortField(sx, r, "en") + val memPort = WSubAccess(WRef(sx), addr, sx.dataType, UnknownFlow) val depthValue = UIntLiteral(sx.depth, IntWidth(sx.depth.bitLength)) val garbageGuard = DoPrim(Geq, Seq(addr, depthValue), Seq(), UnknownType) - if ((sx.depth & (sx.depth - 1)) == 0) - assign(data, memPort, sx.info) - else - garbageAssign(data, memPort, garbageGuard, sx.info) + val clkSource = netlist(memPortField(sx, r, "clk")).expr + + createMemWire(LowerTypes.loweredName(en), en.tpe, netlist(en)) + + if (sx.readLatency == 1 && sx.readUnderWrite != ReadUnderWrite.Old) { + val InfoExpr(addrInfo, addrDriver) = netlist(addr) + declare("reg", LowerTypes.loweredName(addr), addr.tpe, sx.info) + initialize(WRef(LowerTypes.loweredName(addr), addr.tpe), zero, zero) + update(addr, addrDriver, clkSource, en, addrInfo) + } else { + createMemWire(LowerTypes.loweredName(addr), addr.tpe, netlist(addr)) + } + + if (sx.readLatency == 1 && sx.readUnderWrite == ReadUnderWrite.Old) { + declare("reg", LowerTypes.loweredName(data), data.tpe, sx.info) + initialize(WRef(LowerTypes.loweredName(data), data.tpe), zero, zero) + update(data, memPort, clkSource, en, sx.info) + } else { + declare("wire", LowerTypes.loweredName(data), data.tpe, sx.info) + if ((sx.depth & (sx.depth - 1)) == 0) + assign(data, memPort, sx.info) + else + garbageAssign(data, memPort, garbageGuard, sx.info) + } } for (w <- sx.writers) { @@ -1098,31 +1119,41 @@ class VerilogEmitter extends SeqTransform with Emitter { val addr = memPortField(sx, w, "addr") val mask = memPortField(sx, w, "mask") val en = memPortField(sx, w, "en") - //Ports should share an always@posedge, so can't have intermediary wire - // TODO should we use the info here for anything? - val InfoExpr(_, clk) = netlist(memPortField(sx, w, "clk")) - - declare("wire", LowerTypes.loweredName(data), data.tpe, sx.info) - declare("wire", LowerTypes.loweredName(addr), addr.tpe, sx.info) - declare("wire", LowerTypes.loweredName(mask), mask.tpe, sx.info) - declare("wire", LowerTypes.loweredName(en), en.tpe, sx.info) - - // Write port - assign(data, netlist(data)) - assign(addr, netlist(addr)) - assign(mask, netlist(mask)) - assign(en, netlist(en)) - - val mem = WRef(sx.name, memType(sx), MemKind, UnknownFlow) - val memPort = WSubAccess(mem, addr, sx.dataType, UnknownFlow) - update(memPort, data, clk, AND(en, mask), sx.info) + + val clkSource = netlist(memPortField(sx, w, "clk")).expr + + createMemWire(LowerTypes.loweredName(data), data.tpe, netlist(data)) + createMemWire(LowerTypes.loweredName(addr), addr.tpe, netlist(addr)) + createMemWire(LowerTypes.loweredName(mask), mask.tpe, netlist(mask)) + createMemWire(LowerTypes.loweredName(en), en.tpe, netlist(en)) + + val memPort = WSubAccess(WRef(sx), addr, sx.dataType, UnknownFlow) + update(memPort, data, clkSource, AND(en, mask), sx.info) + } + + for (rw <- sx.readwriters) { + val rdata = memPortField(sx, rw, "rdata") + val wdata = memPortField(sx, rw, "wdata") + val addr = memPortField(sx, rw, "addr") + val en = memPortField(sx, rw, "en") + val wmode = memPortField(sx, rw, "wmode") + val wmask = memPortField(sx, rw, "wmask") + val memPort = WSubAccess(WRef(sx), addr, sx.dataType, UnknownFlow) + + val clkSource = netlist(memPortField(sx, rw, "clk")).expr + + createMemWire(LowerTypes.loweredName(wdata), wdata.tpe, netlist(wdata)) + createMemWire(LowerTypes.loweredName(addr), addr.tpe, netlist(addr)) + createMemWire(LowerTypes.loweredName(wmode), wmode.tpe, netlist(wmode)) + createMemWire(LowerTypes.loweredName(wmask), wmask.tpe, netlist(wmask)) + createMemWire(LowerTypes.loweredName(en), en.tpe, netlist(en)) + + declare("reg", LowerTypes.loweredName(rdata), rdata.tpe, sx.info) + initialize(WRef(LowerTypes.loweredName(rdata), rdata.tpe), zero, zero) + update(rdata, memPort, clkSource, en, sx.info) + update(memPort, wdata, clkSource, AND(en, AND(wmode, wmask)), sx.info) } - if (sx.readwriters.nonEmpty) - throw EmitterException( - "All readwrite ports should be transformed into " + - "read & write ports by previous passes" - ) case _ => } } diff --git a/src/test/scala/firrtlTests/InfoSpec.scala b/src/test/scala/firrtlTests/InfoSpec.scala index 43fb6ee1..db4828f6 100644 --- a/src/test/scala/firrtlTests/InfoSpec.scala +++ b/src/test/scala/firrtlTests/InfoSpec.scala @@ -91,11 +91,11 @@ class InfoSpec extends FirrtlFlatSpec with FirrtlMatchers { result should containTree { case DefMemory(Info1, "m", _, _, _, _, _, _, _, _) => true } result should containLine(s"reg [7:0] m [0:31]; //$Info1") result should containLine(s"wire [7:0] m_r_data; //$Info1") - result should containLine(s"wire [4:0] m_r_addr; //$Info1") - result should containLine(s"wire [7:0] m_w_data; //$Info1") - result should containLine(s"wire [4:0] m_w_addr; //$Info1") - result should containLine(s"wire m_w_mask; //$Info1") - result should containLine(s"wire m_w_en; //$Info1") + result should containLine(s"wire [4:0] m_r_addr = addr; //$Info1") + result should containLine(s"wire [7:0] m_w_data = 8'h0; //$Info1") + result should containLine(s"wire [4:0] m_w_addr = addr; //$Info1") + result should containLine(s"wire m_w_mask = 1'h0; //$Info1") + result should containLine(s"wire m_w_en = 1'h0; //$Info1") result should containLine(s"assign m_r_data = m[m_r_addr]; //$Info1") result should containLine(s"m[m_w_addr] <= m_w_data; //$Info1") } |
