aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlbert Magyar2019-10-01 14:16:58 -0700
committerGitHub2019-10-01 14:16:58 -0700
commit1ced6cf929b436380364e9b893e8de7edc3205fd (patch)
treec0998198ebc9036308754826ffb863e43259f3fa /src
parent4ca2b859473e0a88723463eac2821cfbd3249c43 (diff)
parent082bc994457cc5f6780b58fb914a6ab3eb8a021f (diff)
Merge pull request #1183 from freechipsproject/mem-read-under-write
Implement read-first memory behavior in Verilog
Diffstat (limited to 'src')
-rw-r--r--src/main/antlr4/FIRRTL.g42
-rw-r--r--src/main/proto/firrtl.proto8
-rw-r--r--src/main/scala/firrtl/Visitor.scala21
-rw-r--r--src/main/scala/firrtl/WIR.scala3
-rw-r--r--src/main/scala/firrtl/ir/IR.scala11
-rw-r--r--src/main/scala/firrtl/passes/RemoveCHIRRTL.scala2
-rw-r--r--src/main/scala/firrtl/passes/memlib/MemIR.scala2
-rw-r--r--src/main/scala/firrtl/passes/memlib/MemTransformUtils.scala2
-rw-r--r--src/main/scala/firrtl/passes/memlib/MemUtils.scala2
-rw-r--r--src/main/scala/firrtl/passes/memlib/ToMemIR.scala7
-rw-r--r--src/main/scala/firrtl/passes/memlib/VerilogMemDelays.scala99
-rw-r--r--src/main/scala/firrtl/proto/FromProto.scala11
-rw-r--r--src/main/scala/firrtl/proto/ToProto.scala13
-rw-r--r--src/test/scala/firrtlTests/ProtoBufSpec.scala15
-rw-r--r--src/test/scala/firrtlTests/VerilogMemDelaySpec.scala49
15 files changed, 197 insertions, 50 deletions
diff --git a/src/main/antlr4/FIRRTL.g4 b/src/main/antlr4/FIRRTL.g4
index c3b4e74e..be15ab7c 100644
--- a/src/main/antlr4/FIRRTL.g4
+++ b/src/main/antlr4/FIRRTL.g4
@@ -94,7 +94,7 @@ stmt
| 'reg' id ':' type exp ('with' ':' reset_block)? info?
| 'mem' id ':' info? INDENT memField* DEDENT
| 'cmem' id ':' type info?
- | 'smem' id ':' type info?
+ | 'smem' id ':' type ruw? info?
| mdir 'mport' id '=' id '[' exp ']' exp info?
| 'inst' id 'of' id info?
| 'node' id '=' exp info?
diff --git a/src/main/proto/firrtl.proto b/src/main/proto/firrtl.proto
index b8f6db98..0cf14f41 100644
--- a/src/main/proto/firrtl.proto
+++ b/src/main/proto/firrtl.proto
@@ -104,6 +104,12 @@ message Firrtl {
Expression init = 5;
}
+ enum ReadUnderWrite {
+ UNDEFINED = 0;
+ OLD = 1;
+ NEW = 2;
+ }
+
message Memory {
// Required.
string id = 1;
@@ -121,6 +127,7 @@ message Firrtl {
repeated string reader_id = 6;
repeated string writer_id = 7;
repeated string readwriter_id = 8;
+ ReadUnderWrite read_under_write = 10;
}
message CMemory {
@@ -138,6 +145,7 @@ message Firrtl {
}
// Required.
bool sync_read = 3;
+ ReadUnderWrite read_under_write = 5;
}
message Instance {
diff --git a/src/main/scala/firrtl/Visitor.scala b/src/main/scala/firrtl/Visitor.scala
index 6d9f0d31..01de8f15 100644
--- a/src/main/scala/firrtl/Visitor.scala
+++ b/src/main/scala/firrtl/Visitor.scala
@@ -164,15 +164,23 @@ class Visitor(infoMode: InfoMode) extends AbstractParseTreeVisitor[FirrtlNode] w
private def visitSuite(ctx: SuiteContext): Statement =
Block(ctx.simple_stmt().asScala.flatMap(x => Option(x.stmt).map(visitStmt)))
+ private def visitRuw(ctx: Option[RuwContext]): ReadUnderWrite.Value = ctx match {
+ case None => ReadUnderWrite.Undefined
+ case Some(ctx) => ctx.getText match {
+ case "undefined" => ReadUnderWrite.Undefined
+ case "old" => ReadUnderWrite.Old
+ case "new" => ReadUnderWrite.New
+ }
+ }
// Memories are fairly complicated to translate thus have a dedicated method
private def visitMem(ctx: StmtContext): Statement = {
val readers = mutable.ArrayBuffer.empty[String]
val writers = mutable.ArrayBuffer.empty[String]
val readwriters = mutable.ArrayBuffer.empty[String]
- case class ParamValue(typ: Option[Type] = None, lit: Option[BigInt] = None, ruw: Option[String] = None, unique: Boolean = true)
+ case class ParamValue(typ: Option[Type] = None, lit: Option[BigInt] = None, ruw: ReadUnderWrite.Value = ReadUnderWrite.Undefined, unique: Boolean = true)
val fieldMap = mutable.HashMap[String, ParamValue]()
-
+ val memName = ctx.id(0).getText
def parseMemFields(memFields: Seq[MemFieldContext]): Unit =
memFields.foreach { field =>
val fieldName = field.children.asScala(0).getText
@@ -184,7 +192,7 @@ class Visitor(infoMode: InfoMode) extends AbstractParseTreeVisitor[FirrtlNode] w
case _ =>
val paramDef = fieldName match {
case "data-type" => ParamValue(typ = Some(visitType(field.`type`())))
- case "read-under-write" => ParamValue(ruw = Some(field.ruw().getText)) // TODO
+ case "read-under-write" => ParamValue(ruw = visitRuw(Option(field.ruw)))
case _ => ParamValue(lit = Some(BigInt(field.intLit().getText)))
}
if (fieldMap.contains(fieldName))
@@ -210,10 +218,11 @@ class Visitor(infoMode: InfoMode) extends AbstractParseTreeVisitor[FirrtlNode] w
}
def lit(param: String) = fieldMap(param).lit.get
- val ruw = fieldMap.get("read-under-write").map(_.ruw).getOrElse(None)
+ val ruw = fieldMap.get("read-under-write").map(_.ruw).getOrElse(ir.ReadUnderWrite.Undefined)
DefMemory(info,
- name = ctx.id(0).getText, dataType = fieldMap("data-type").typ.get,
+ name = memName,
+ dataType = fieldMap("data-type").typ.get,
depth = lit("depth"),
writeLatency = lit("write-latency").toInt,
readLatency = lit("read-latency").toInt,
@@ -269,7 +278,7 @@ class Visitor(infoMode: InfoMode) extends AbstractParseTreeVisitor[FirrtlNode] w
CDefMemory(info, ctx.id(0).getText, tpe, size, seq = false)
case "smem" =>
val (tpe, size) = visitCMemType(ctx.`type`())
- CDefMemory(info, ctx.id(0).getText, tpe, size, seq = true)
+ CDefMemory(info, ctx.id(0).getText, tpe, size, seq = true, readUnderWrite = visitRuw(Option(ctx.ruw)))
case "inst" => DefInstance(info, ctx.id(0).getText, ctx.id(1).getText)
case "node" => DefNode(info, ctx.id(0).getText, visitExp(ctx_exp(0)))
diff --git a/src/main/scala/firrtl/WIR.scala b/src/main/scala/firrtl/WIR.scala
index 73a20d18..475f5e9c 100644
--- a/src/main/scala/firrtl/WIR.scala
+++ b/src/main/scala/firrtl/WIR.scala
@@ -341,7 +341,8 @@ case class CDefMemory(
name: String,
tpe: Type,
size: BigInt,
- seq: Boolean) extends Statement with HasInfo {
+ seq: Boolean,
+ readUnderWrite: ReadUnderWrite.Value = ReadUnderWrite.Undefined) extends Statement with HasInfo {
def serialize: String = (if (seq) "smem" else "cmem") +
s" $name : ${tpe.serialize} [$size]" + info.serialize
def mapExpr(f: Expression => Expression): Statement = this
diff --git a/src/main/scala/firrtl/ir/IR.scala b/src/main/scala/firrtl/ir/IR.scala
index 9268865b..b4631473 100644
--- a/src/main/scala/firrtl/ir/IR.scala
+++ b/src/main/scala/firrtl/ir/IR.scala
@@ -285,6 +285,13 @@ case class DefInstance(info: Info, name: String, module: String) extends Stateme
def foreachString(f: String => Unit): Unit = f(name)
def foreachInfo(f: Info => Unit): Unit = f(info)
}
+
+object ReadUnderWrite extends Enumeration {
+ val Undefined = Value("undefined")
+ val Old = Value("old")
+ val New = Value("new")
+}
+
case class DefMemory(
info: Info,
name: String,
@@ -296,7 +303,7 @@ case class DefMemory(
writers: Seq[String],
readwriters: Seq[String],
// TODO: handle read-under-write
- readUnderWrite: Option[String] = None) extends Statement with IsDeclaration {
+ readUnderWrite: ReadUnderWrite.Value = ReadUnderWrite.Undefined) extends Statement with IsDeclaration {
def serialize: String =
s"mem $name :" + info.serialize +
indent(
@@ -307,7 +314,7 @@ case class DefMemory(
(readers map ("reader => " + _)) ++
(writers map ("writer => " + _)) ++
(readwriters map ("readwriter => " + _)) ++
- Seq("read-under-write => undefined")) mkString "\n")
+ Seq(s"read-under-write => ${readUnderWrite}")) mkString "\n")
def mapStmt(f: Statement => Statement): Statement = this
def mapExpr(f: Expression => Expression): Statement = this
def mapType(f: Type => Type): Statement = this.copy(dataType = f(dataType))
diff --git a/src/main/scala/firrtl/passes/RemoveCHIRRTL.scala b/src/main/scala/firrtl/passes/RemoveCHIRRTL.scala
index d0498cf0..5e93b3b9 100644
--- a/src/main/scala/firrtl/passes/RemoveCHIRRTL.scala
+++ b/src/main/scala/firrtl/passes/RemoveCHIRRTL.scala
@@ -99,7 +99,7 @@ object RemoveCHIRRTL extends Transform {
set_enable(rws, "en") ++
set_write(rws, "wdata", "wmask")
val mem = DefMemory(sx.info, sx.name, sx.tpe, sx.size, 1, if (sx.seq) 1 else 0,
- rds map (_.name), wrs map (_.name), rws map (_.name))
+ rds map (_.name), wrs map (_.name), rws map (_.name), sx.readUnderWrite)
Block(mem +: stmts)
case sx: CDefMPort =>
types.get(sx.mem) match {
diff --git a/src/main/scala/firrtl/passes/memlib/MemIR.scala b/src/main/scala/firrtl/passes/memlib/MemIR.scala
index 2379feab..afba7535 100644
--- a/src/main/scala/firrtl/passes/memlib/MemIR.scala
+++ b/src/main/scala/firrtl/passes/memlib/MemIR.scala
@@ -34,7 +34,7 @@ case class DefAnnotatedMemory(
readers: Seq[String],
writers: Seq[String],
readwriters: Seq[String],
- readUnderWrite: Option[String],
+ readUnderWrite: ReadUnderWrite.Value,
maskGran: Option[BigInt],
memRef: Option[(String, String)] /* (Module, Mem) */
//pins: Seq[Pin],
diff --git a/src/main/scala/firrtl/passes/memlib/MemTransformUtils.scala b/src/main/scala/firrtl/passes/memlib/MemTransformUtils.scala
index e490c11a..b6a9a23d 100644
--- a/src/main/scala/firrtl/passes/memlib/MemTransformUtils.scala
+++ b/src/main/scala/firrtl/passes/memlib/MemTransformUtils.scala
@@ -42,6 +42,6 @@ object MemTransformUtils {
}
def defaultPortSeq(mem: DefAnnotatedMemory): Seq[Field] = MemPortUtils.defaultPortSeq(mem.toMem)
- def memPortField(s: DefAnnotatedMemory, p: String, f: String): Expression =
+ def memPortField(s: DefAnnotatedMemory, p: String, f: String): WSubField =
MemPortUtils.memPortField(s.toMem, p, f)
}
diff --git a/src/main/scala/firrtl/passes/memlib/MemUtils.scala b/src/main/scala/firrtl/passes/memlib/MemUtils.scala
index b16a7424..bb441ebb 100644
--- a/src/main/scala/firrtl/passes/memlib/MemUtils.scala
+++ b/src/main/scala/firrtl/passes/memlib/MemUtils.scala
@@ -79,7 +79,7 @@ object MemPortUtils {
(mem.readwriters map (Field(_, Flip, rwType))))
}
- def memPortField(s: DefMemory, p: String, f: String): Expression = {
+ def memPortField(s: DefMemory, p: String, f: String): WSubField = {
val mem = WRef(s.name, memType(s), MemKind, UnknownFlow)
val t1 = field_type(mem.tpe, p)
val t2 = field_type(t1, f)
diff --git a/src/main/scala/firrtl/passes/memlib/ToMemIR.scala b/src/main/scala/firrtl/passes/memlib/ToMemIR.scala
index a9f4b330..554a3572 100644
--- a/src/main/scala/firrtl/passes/memlib/ToMemIR.scala
+++ b/src/main/scala/firrtl/passes/memlib/ToMemIR.scala
@@ -11,14 +11,15 @@ import firrtl.ir._
* - read latency and write latency of one
* - only one readwrite port or write port
* - zero or one read port
+ * - undefined read-under-write behavior
*/
object ToMemIR extends Pass {
/** Only annotate memories that are candidates for memory macro replacements
- * i.e. rw, w + r (read, write 1 cycle delay)
+ * i.e. rw, w + r (read, write 1 cycle delay) and read-under-write "undefined."
*/
+ import ReadUnderWrite._
def updateStmts(s: Statement): Statement = s match {
- case m: DefMemory if m.readLatency == 1 && m.writeLatency == 1 &&
- (m.writers.length + m.readwriters.length) == 1 && m.readers.length <= 1 =>
+ case m @ DefMemory(_,_,_,_,1,1,r,w,rw,Undefined) if (w.length + rw.length) == 1 && r.length <= 1 =>
DefAnnotatedMemory(m)
case sx => sx map updateStmts
}
diff --git a/src/main/scala/firrtl/passes/memlib/VerilogMemDelays.scala b/src/main/scala/firrtl/passes/memlib/VerilogMemDelays.scala
index 335e1121..328e6caa 100644
--- a/src/main/scala/firrtl/passes/memlib/VerilogMemDelays.scala
+++ b/src/main/scala/firrtl/passes/memlib/VerilogMemDelays.scala
@@ -13,6 +13,22 @@ import MemPortUtils._
import collection.mutable
+object DelayPipe {
+ private case class PipeState(ref: Expression, decl: Statement = EmptyStmt, connect: Statement = EmptyStmt, idx: Int = 0)
+
+ def apply(ns: Namespace)(e: Expression, delay: Int, clock: Expression): (Expression, Seq[Statement]) = {
+ def addStage(prev: PipeState): PipeState = {
+ val idx = prev.idx + 1
+ val name = ns.newName(s"${e.serialize}_r${idx}".replace('.', '_'))
+ val regRef = WRef(name, e.tpe, RegKind)
+ val regDecl = DefRegister(NoInfo, name, e.tpe, clock, zero, regRef)
+ PipeState(regRef, regDecl, Connect(NoInfo, regRef, prev.ref), idx)
+ }
+ val pipeline = Seq.iterate(PipeState(e), delay+1)(addStage)
+ (pipeline.last.ref, pipeline.map(_.decl) ++ pipeline.map(_.connect))
+ }
+}
+
/** This pass generates delay reigsters for memories for verilog */
object VerilogMemDelays extends Pass {
val ug = UnknownFlow
@@ -49,7 +65,7 @@ object VerilogMemDelays extends Pass {
readers = sx.readers ++ (sx.readwriters map (rw => rwMap(rw)._1)),
writers = sx.writers ++ (sx.readwriters map (rw => rwMap(rw)._2)),
readwriters = Nil, readLatency = 0, writeLatency = 1)
- def pipe(e: Expression, // Expression to be piped
+ def prependPipe(e: Expression, // Expression to be piped
n: Int, // pipe depth
clk: Expression, // clock expression
cond: Expression // condition for pipes
@@ -96,40 +112,69 @@ object VerilogMemDelays extends Pass {
)
stmts ++= ((sx.readers flatMap {reader =>
- // generate latency pipes for read ports (enable & addr)
val clk = netlist(memPortField(sx, reader, "clk"))
- val (en, ss1) = pipe(memPortField(sx, reader, "en"), sx.readLatency - 1, clk, one)
- val (addr, ss2) = pipe(memPortField(sx, reader, "addr"), sx.readLatency, clk, en)
- ss1 ++ ss2 ++ readPortConnects(reader, clk, en, addr)
+ if (sx.readUnderWrite == ReadUnderWrite.Old) {
+ // For a read-first ("old") mem, read data gets delayed, so don't delay read address/en
+ val rdata = memPortField(sx, reader, "data")
+ val enDriver = netlist(memPortField(sx, reader, "en"))
+ val addrDriver = netlist(memPortField(sx, reader, "addr"))
+ readPortConnects(reader, clk, enDriver, addrDriver)
+ } else {
+ // For a write-first ("new") or undefined mem, delay read control inputs
+ val (en, ss1) = prependPipe(memPortField(sx, reader, "en"), sx.readLatency - 1, clk, one)
+ val (addr, ss2) = prependPipe(memPortField(sx, reader, "addr"), sx.readLatency, clk, en)
+ ss1 ++ ss2 ++ readPortConnects(reader, clk, en, addr)
+ }
}) ++ (sx.writers flatMap {writer =>
// generate latency pipes for write ports (enable, mask, addr, data)
val clk = netlist(memPortField(sx, writer, "clk"))
- val (en, ss1) = pipe(memPortField(sx, writer, "en"), sx.writeLatency - 1, clk, one)
- val (mask, ss2) = pipe(memPortField(sx, writer, "mask"), sx.writeLatency - 1, clk, one)
- val (addr, ss3) = pipe(memPortField(sx, writer, "addr"), sx.writeLatency - 1, clk, one)
- val (data, ss4) = pipe(memPortField(sx, writer, "data"), sx.writeLatency - 1, clk, one)
+ val (en, ss1) = prependPipe(memPortField(sx, writer, "en"), sx.writeLatency - 1, clk, one)
+ val (mask, ss2) = prependPipe(memPortField(sx, writer, "mask"), sx.writeLatency - 1, clk, one)
+ val (addr, ss3) = prependPipe(memPortField(sx, writer, "addr"), sx.writeLatency - 1, clk, one)
+ val (data, ss4) = prependPipe(memPortField(sx, writer, "data"), sx.writeLatency - 1, clk, one)
ss1 ++ ss2 ++ ss3 ++ ss4 ++ writePortConnects(writer, clk, en, mask, addr, data)
}) ++ (sx.readwriters flatMap {readwriter =>
val (reader, writer) = rwMap(readwriter)
val clk = netlist(memPortField(sx, readwriter, "clk"))
// generate latency pipes for readwrite ports (enable, addr, wmode, wmask, wdata)
- val (en, ss1) = pipe(memPortField(sx, readwriter, "en"), sx.readLatency - 1, clk, one)
- val (wmode, ss2) = pipe(memPortField(sx, readwriter, "wmode"), sx.writeLatency - 1, clk, one)
- val (wmask, ss3) = pipe(memPortField(sx, readwriter, "wmask"), sx.writeLatency - 1, clk, one)
- val (wdata, ss4) = pipe(memPortField(sx, readwriter, "wdata"), sx.writeLatency - 1, clk, one)
- val (raddr, ss5) = pipe(memPortField(sx, readwriter, "addr"), sx.readLatency, clk, AND(en, NOT(wmode)))
- val (waddr, ss6) = pipe(memPortField(sx, readwriter, "addr"), sx.writeLatency - 1, clk, one)
- repl(memPortField(sx, readwriter, "rdata")) = memPortField(mem, reader, "data")
- ss1 ++ ss2 ++ ss3 ++ ss4 ++ ss5 ++ ss6 ++
- readPortConnects(reader, clk, en, raddr) ++
- writePortConnects(writer, clk, AND(en, wmode), wmask, waddr, wdata)
+ val (en, ss1) = prependPipe(memPortField(sx, readwriter, "en"), sx.readLatency - 1, clk, one)
+ val (wmode, ss2) = prependPipe(memPortField(sx, readwriter, "wmode"), sx.writeLatency - 1, clk, one)
+ val (wmask, ss3) = prependPipe(memPortField(sx, readwriter, "wmask"), sx.writeLatency - 1, clk, one)
+ val (wdata, ss4) = prependPipe(memPortField(sx, readwriter, "wdata"), sx.writeLatency - 1, clk, one)
+ val (waddr, ss5) = prependPipe(memPortField(sx, readwriter, "addr"), sx.writeLatency - 1, clk, one)
+ val stmts = ss1 ++ ss2 ++ ss3 ++ ss4 ++ ss5 ++ writePortConnects(writer, clk, AND(en, wmode), wmask, waddr, wdata)
+ if (sx.readUnderWrite == ReadUnderWrite.Old) {
+ // For a read-first ("old") mem, read data gets delayed, so don't delay read address/en
+ val enDriver = netlist(memPortField(sx, readwriter, "en"))
+ val addrDriver = netlist(memPortField(sx, readwriter, "addr"))
+ val wmodeDriver = netlist(memPortField(sx, readwriter, "wmode"))
+ stmts ++ readPortConnects(reader, clk, AND(enDriver, NOT(wmodeDriver)), addrDriver)
+ } else {
+ // For a write-first ("new") or undefined mem, delay read control inputs
+ val (raddr, raddrPipeStmts) = prependPipe(memPortField(sx, readwriter, "addr"), sx.readLatency, clk, AND(en, NOT(wmode)))
+ repl(memPortField(sx, readwriter, "rdata")) = memPortField(mem, reader, "data")
+ stmts ++ raddrPipeStmts ++ readPortConnects(reader, clk, en, raddr)
+ }
}))
- mem // The mem stays put
- case sx: Connect => kind(sx.loc) match {
- case MemKind => EmptyStmt
- case _ => sx
- }
- case sx => sx
+
+ def pipeReadData(p: String): Seq[Statement] = {
+ val newName = rwMap.get(p).map(_._1).getOrElse(p) // Name of final read port, whether renamed (rw port) or not
+ val rdataNew = memPortField(mem, newName, "data")
+ val rdataOld = rwMap.get(p).map(rw => memPortField(sx, p, "rdata")).getOrElse(rdataNew)
+ val clk = netlist(rdataOld.copy(name = "clk"))
+ val (rdataPipe, rdataPipeStmts) = DelayPipe(namespace)(rdataNew, sx.readLatency, clk) // TODO: use enable
+ repl(rdataOld) = rdataPipe
+ rdataPipeStmts
+ }
+
+ // We actually pipe the read data here; this groups it with the mem declaration to keep declarations early
+ if (sx.readUnderWrite == ReadUnderWrite.Old) {
+ Block(mem +: (sx.readers ++ sx.readwriters).flatMap(pipeReadData(_)))
+ } else {
+ mem
+ }
+ case sx: Connect if kind(sx.loc) == MemKind => EmptyStmt
+ case sx => sx map replaceExp(repl)
}
def replaceExp(repl: Netlist)(e: Expression): Expression = e match {
@@ -140,9 +185,6 @@ object VerilogMemDelays extends Pass {
case ex => ex map replaceExp(repl)
}
- def replaceStmt(repl: Netlist)(s: Statement): Statement =
- s map replaceStmt(repl) map replaceExp(repl)
-
def appendStmts(sx: Seq[Statement])(s: Statement): Statement = Block(s +: sx)
def memDelayMod(m: DefModule): DefModule = {
@@ -152,7 +194,6 @@ object VerilogMemDelays extends Pass {
val extraStmts = mutable.ArrayBuffer.empty[Statement]
m.foreach(buildNetlist(netlist))
m.map(memDelayStmt(netlist, namespace, repl, extraStmts))
- .map(replaceStmt(repl))
.map(appendStmts(extraStmts))
}
diff --git a/src/main/scala/firrtl/proto/FromProto.scala b/src/main/scala/firrtl/proto/FromProto.scala
index 22c90316..ef2ee5bd 100644
--- a/src/main/scala/firrtl/proto/FromProto.scala
+++ b/src/main/scala/firrtl/proto/FromProto.scala
@@ -8,6 +8,7 @@ import java.io.{File, FileInputStream, InputStream}
import collection.JavaConverters._
import FirrtlProtos._
import com.google.protobuf.CodedInputStream
+import Firrtl.Statement.ReadUnderWrite
object FromProto {
@@ -133,6 +134,12 @@ object FromProto {
ir.Conditionally(convert(info), convert(when.getPredicate), conseq, alt)
}
+ def convert(ruw: ReadUnderWrite): ir.ReadUnderWrite.Value = ruw match {
+ case ReadUnderWrite.UNDEFINED => ir.ReadUnderWrite.Undefined
+ case ReadUnderWrite.OLD => ir.ReadUnderWrite.Old
+ case ReadUnderWrite.NEW => ir.ReadUnderWrite.New
+ }
+
def convert(dt: Firrtl.Statement.CMemory.TypeAndDepth): (ir.Type, BigInt) =
(convert(dt.getDataType), convert(dt.getDepth))
@@ -145,7 +152,7 @@ object FromProto {
case TYPE_AND_DEPTH_FIELD_NUMBER =>
convert(cmem.getTypeAndDepth)
}
- CDefMemory(convert(info), cmem.getId, tpe, depth, cmem.getSyncRead)
+ CDefMemory(convert(info), cmem.getId, tpe, depth, cmem.getSyncRead, convert(cmem.getReadUnderWrite))
}
import Firrtl.Statement.MemoryPort.Direction._
@@ -181,7 +188,7 @@ object FromProto {
case BIGINT_DEPTH_FIELD_NUMBER => convert(mem.getBigintDepth)
}
ir.DefMemory(convert(info), mem.getId, dtype, depth, mem.getWriteLatency, mem.getReadLatency,
- rs, ws, rws, None)
+ rs, ws, rws, convert(mem.getReadUnderWrite))
}
def convert(attach: Firrtl.Statement.Attach, info: Firrtl.SourceInfo): ir.Attach = {
diff --git a/src/main/scala/firrtl/proto/ToProto.scala b/src/main/scala/firrtl/proto/ToProto.scala
index 17adb698..70de3ccd 100644
--- a/src/main/scala/firrtl/proto/ToProto.scala
+++ b/src/main/scala/firrtl/proto/ToProto.scala
@@ -6,6 +6,7 @@ package proto
import java.io.OutputStream
import FirrtlProtos._
+import Firrtl.Statement.ReadUnderWrite
import Firrtl.Expression.PrimOp.Op
import com.google.protobuf.{CodedOutputStream, WireFormat}
import firrtl.PrimOps._
@@ -103,6 +104,12 @@ object ToProto {
BPSet -> Op.OP_SET_BINARY_POINT
)
+ def convert(ruw: ir.ReadUnderWrite.Value): ReadUnderWrite = ruw match {
+ case ir.ReadUnderWrite.Undefined => ReadUnderWrite.UNDEFINED
+ case ir.ReadUnderWrite.Old => ReadUnderWrite.OLD
+ case ir.ReadUnderWrite.New => ReadUnderWrite.NEW
+ }
+
def convertToIntegerLiteral(value: BigInt): Firrtl.Expression.IntegerLiteral.Builder = {
Firrtl.Expression.IntegerLiteral.newBuilder()
.setValue(value.toString)
@@ -260,22 +267,24 @@ object ToProto {
val ib = Firrtl.Statement.IsInvalid.newBuilder()
.setExpression(convert(expr))
sb.setIsInvalid(ib)
- case ir.DefMemory(_, name, dtype, depth, wlat, rlat, rs, ws, rws, _) =>
+ case ir.DefMemory(_, name, dtype, depth, wlat, rlat, rs, ws, rws, ruw) =>
val mem = Firrtl.Statement.Memory.newBuilder()
.setId(name)
.setType(convert(dtype))
.setBigintDepth(convertToBigInt(depth))
.setWriteLatency(wlat)
.setReadLatency(rlat)
+ .setReadUnderWrite(convert(ruw))
mem.addAllReaderId(rs.asJava)
mem.addAllWriterId(ws.asJava)
mem.addAllReadwriterId(rws.asJava)
sb.setMemory(mem)
- case CDefMemory(_, name, tpe, size, seq) =>
+ case CDefMemory(_, name, tpe, size, seq, ruw) =>
val mb = Firrtl.Statement.CMemory.newBuilder()
.setId(name)
.setTypeAndDepth(convert(tpe, size))
.setSyncRead(seq)
+ .setReadUnderWrite(convert(ruw))
sb.setCmemory(mb)
case CDefMPort(_, name, _, mem, exprs, dir) =>
val pb = Firrtl.Statement.MemoryPort.newBuilder()
diff --git a/src/test/scala/firrtlTests/ProtoBufSpec.scala b/src/test/scala/firrtlTests/ProtoBufSpec.scala
index 2f347c6d..7f41fb26 100644
--- a/src/test/scala/firrtlTests/ProtoBufSpec.scala
+++ b/src/test/scala/firrtlTests/ProtoBufSpec.scala
@@ -176,6 +176,21 @@ class ProtoBufSpec extends FirrtlFlatSpec {
oldCMem should equal (cmem)
}
+ // readunderwrite support
+ it should "support readunderwrite parameters" in {
+ val m1 = DefMemory(NoInfo, "m", UIntType(IntWidth(8)), 128, 1, 1, List("r"), List("w"), Nil, ir.ReadUnderWrite.Old)
+ FromProto.convert(ToProto.convert(m1).head.build) should equal (m1)
+
+ val m2 = m1.copy(readUnderWrite = ir.ReadUnderWrite.New)
+ FromProto.convert(ToProto.convert(m2).head.build) should equal (m2)
+
+ val cm1 = CDefMemory(NoInfo, "m", UIntType(IntWidth(8)), 128, true, ir.ReadUnderWrite.Old)
+ FromProto.convert(ToProto.convert(cm1).head.build) should equal (cm1)
+
+ val cm2 = cm1.copy(readUnderWrite = ir.ReadUnderWrite.New)
+ FromProto.convert(ToProto.convert(cm2).head.build) should equal (cm2)
+ }
+
it should "support AsyncResetTypes" in {
val port = ir.Port(ir.NoInfo, "reset", ir.Input, ir.AsyncResetType)
FromProto.convert(ToProto.convert(port).build) should equal (port)
diff --git a/src/test/scala/firrtlTests/VerilogMemDelaySpec.scala b/src/test/scala/firrtlTests/VerilogMemDelaySpec.scala
index 405f5ab5..e7f27d0e 100644
--- a/src/test/scala/firrtlTests/VerilogMemDelaySpec.scala
+++ b/src/test/scala/firrtlTests/VerilogMemDelaySpec.scala
@@ -49,4 +49,53 @@ class VerilogMemDelaySpec extends FreeSpec with Matchers {
CheckHighForm.run(result2)
//result.circuit.serialize.length > 0 should be (true)
}
+
+ "Using a read-first memory should be allowed in VerilogMemDelays" in {
+ val input =
+ """
+ |circuit Test :
+ | module Test :
+ | input clock : Clock
+ | input waddr : UInt<5>
+ | input wdata : UInt<32>
+ | input raddr : UInt<5>
+ | input rw_wen : UInt<1>
+ | output rdata : UInt<32>
+ | output rw_rdata : UInt<32>
+ | mem m :
+ | data-type => UInt<32>
+ | depth => 32
+ | read-latency => 1
+ | write-latency => 1
+ | read-under-write => old
+ | reader => read
+ | writer => write
+ | readwriter => rw
+ | m.read.clk <= clock
+ | m.read.en <= UInt<1>(1)
+ | m.read.addr <= raddr
+ | rdata <= m.read.data
+ |
+ | m.write.clk <= clock
+ | m.write.en <= UInt<1>(1)
+ | m.write.mask <= UInt<1>(1)
+ | m.write.addr <= waddr
+ | m.write.data <= wdata
+ |
+ | m.rw.clk <= clock
+ | m.rw.en <= UInt<1>(1)
+ | m.rw.wmode <= rw_wen
+ | m.rw.wmask <= UInt<1>(1)
+ | m.rw.addr <= waddr
+ | m.rw.wdata <= wdata
+ | rw_rdata <= m.rw.rdata
+ """.stripMargin
+
+ val circuit = Parser.parse(input)
+ val compiler = new LowFirrtlCompiler
+
+ val result = compiler.compile(CircuitState(circuit, ChirrtlForm), Seq.empty)
+ val result2 = VerilogMemDelays.run(result.circuit)
+ CheckHighForm.run(result2)
+ }
}