aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Laeufer2021-02-17 12:16:52 -0800
committerGitHub2021-02-17 20:16:52 +0000
commit5a89fca6090948d0a99c217a09c692e58a20d1df (patch)
tree7996829e3589205607862cbbf578a4e9a9d6e623
parent856226416cfa2d770c7205efad5331297c2e3a32 (diff)
Allow Side Effecting Statement to have Names (#2057)
* firrtl: add optional statement labels for stop, printf, assert, assume and cover * test: parsing of statement labels * ir: ensure that name is properly retained * SymbolTable: add support for labled statements * test: parsing statement labels * test: lower types name collisions with named statements * ignore empty names * Inline: deal with named and unnamed statements * RemoveWires: treat stop, printf and verification statements as "others" * test: fix InlineInstance tests * DeadCodeEliminations: statements are now als declarations * CheckHighForm: ensure that statement names are not used as references * CheckSpec: throw error if statement name collides * add pass to automatically add missing statement names * check: make sure that two statements cannot have the same name * stmtLabel -> stmtName * scalafmt * add statement names to spec * spec: meta data -> metadata * EnsureStatementNames: explain naming algorithm * remove returns * better namespace use * ir: add CanBeReferenced trait * ir: add newline as jack requested
-rw-r--r--spec/spec.pdfbin338200 -> 338944 bytes
-rw-r--r--spec/spec.tex29
-rw-r--r--src/main/antlr4/FIRRTL.g414
-rw-r--r--src/main/scala/firrtl/Namespace.scala8
-rw-r--r--src/main/scala/firrtl/Visitor.scala16
-rw-r--r--src/main/scala/firrtl/WIR.scala3
-rw-r--r--src/main/scala/firrtl/analyses/SymbolTable.scala4
-rw-r--r--src/main/scala/firrtl/graph/DiGraph.scala3
-rw-r--r--src/main/scala/firrtl/ir/IR.scala56
-rw-r--r--src/main/scala/firrtl/ir/Serializer.scala18
-rw-r--r--src/main/scala/firrtl/passes/CheckHighForm.scala27
-rw-r--r--src/main/scala/firrtl/passes/Inline.scala27
-rw-r--r--src/main/scala/firrtl/transforms/DeadCodeElimination.scala10
-rw-r--r--src/main/scala/firrtl/transforms/EnsureNamedStatements.scala39
-rw-r--r--src/main/scala/firrtl/transforms/RemoveWires.scala5
-rw-r--r--src/test/scala/firrtl/testutils/FirrtlSpec.scala11
-rw-r--r--src/test/scala/firrtl/transforms/EnsureNamedStatementsSpec.scala39
-rw-r--r--src/test/scala/firrtlTests/CheckSpec.scala44
-rw-r--r--src/test/scala/firrtlTests/InlineInstancesTests.scala54
-rw-r--r--src/test/scala/firrtlTests/LowerTypesSpec.scala19
-rw-r--r--src/test/scala/firrtlTests/ParserSpec.scala21
-rw-r--r--src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala160
22 files changed, 456 insertions, 151 deletions
diff --git a/spec/spec.pdf b/spec/spec.pdf
index 45dd4879..e392dec6 100644
--- a/spec/spec.pdf
+++ b/spec/spec.pdf
Binary files differ
diff --git a/spec/spec.tex b/spec/spec.tex
index 41de5d5e..8201b32e 100644
--- a/spec/spec.tex
+++ b/spec/spec.tex
@@ -989,10 +989,15 @@ is determined by their syntactic order in the enclosing module. The order of exe
side-effect-having statements in different modules or with different clocks that trigger
concurrently is undefined.
+The stop statement has an optional name attribute which can be used to
+attach metadata to the statement. The name is part of the module level
+namespace. However it can never be used in a reference since it is not of
+any valid type.
+
\begin{lstlisting}
wire clk:Clock
wire halt:UInt<1>
-stop(clk,halt,42)
+stop(clk,halt,42) : optional_name
...
\end{lstlisting}
@@ -1004,12 +1009,17 @@ A printf statement requires a clock signal, a print condition signal, a format s
For information about execution ordering of clocked statements with observable environmental side
effects, see section \ref{stop_stmt}.
+The printf statement has an optional name attribute which can be used to
+attach metadata to the statement. The name is part of the module level
+namespace. However it can never be used in a reference since it is not of
+any valid type.
+
\begin{lstlisting}
wire clk:Clock
wire condition:UInt<1>
wire a:UInt
wire b:UInt
-printf(clk, condition, "a in hex: %x, b in decimal:%d.\n", a, b)
+printf(clk, condition, "a in hex: %x, b in decimal:%d.\n", a, b) : optional_name
...
\end{lstlisting}
@@ -1043,6 +1053,11 @@ Backends are free to generate the corresponding model checking constructs in the
For information about execution ordering of clocked statements with observable environmental side
effects, see section \ref{stop_stmt}.
+Any verification statement has an optional name attribute which can be used to
+attach metadata to the statement. The name is part of the module level
+namespace. However it can never be used in a reference since it is not of
+any valid type.
+
\subsubsection{Assert}
The assert statement verifies that the predicate is true on the rising edge of any clock cycle when the enable is true. In other words, it verifies that enable implies predicate.
@@ -1053,7 +1068,7 @@ wire pred:UInt<1>
wire en:UInt<1>
pred <= eq(X, Y)
en <= Z_valid
-assert(clk, pred, en, "X equals Y when Z is valid")
+assert(clk, pred, en, "X equals Y when Z is valid") : optional_name
\end{lstlisting}
\subsubsection{Assume}
@@ -1066,7 +1081,7 @@ wire pred:UInt<1>
wire en:UInt<1>
pred <= eq(X, Y)
en <= Z_valid
-assume(clk, pred, en, "X equals Y when Z is valid")
+assume(clk, pred, en, "X equals Y when Z is valid") : optional_name
\end{lstlisting}
\subsubsection{Cover}
@@ -1079,7 +1094,7 @@ wire pred:UInt<1>
wire en:UInt<1>
pred <= eq(X, Y)
en <= Z_valid
-cover(clk, pred, en, "X equals Y when Z is valid")
+cover(clk, pred, en, "X equals Y when Z is valid") : optional_name
\end{lstlisting}
\section{Expressions}
@@ -2108,8 +2123,8 @@ The concrete syntax of FIRRTL is defined in section \ref{syntax_tree}. Productio
&\pipe &\pd{exp} \vv{is invalid} \opt{\pd{info}} &\text{Invalidate}\\
&\pipe &\vv{attach}\vv{(}\rpt{\pd{exp}}\vv{)} \opt{\pd{info}} &\text{Attach}\\
&\pipe &\vv{when} \pd{exp} \vv{:} \opt{\pd{info}} \pd{stmt} \opt{\vv{else} \vv{:} \pd{stmt}} &\text{Conditional}\\
- &\pipe &\vv{stop(}\pd{exp}, \pd{exp}, \pd{int})\opt{\pd{info}} &\text{Stop}\\
- &\pipe &\vv{printf(}\pd{exp}, \pd{exp}, \pd{string}, \rpt{\pd{exp}}\vv{)} \opt{\pd{info}} &\text{Printf}\\
+ &\pipe &\vv{stop(}\pd{exp}, \pd{exp}, \pd{int}) \opt{\vv{:}\pd{id}} \opt{\pd{info}} &\text{Stop}\\
+ &\pipe &\vv{printf(}\pd{exp}, \pd{exp}, \pd{string}, \rpt{\pd{exp}}\vv{)} \opt{\vv{:}\pd{id}} \opt{\pd{info}} &\text{Printf}\\
&\pipe &\vv{skip} \opt{\pd{info}} &\text{Empty}\\
&\pipe &\vv{(}\rpt{\pd{stmt}}\vv{)} &\text{Statement Group}\\
\pd{ruw} &= &\vv{old} \pipe \vv{ new} \pipe \vv{ undefined} &\text{Read Under Write Flag}\\
diff --git a/src/main/antlr4/FIRRTL.g4 b/src/main/antlr4/FIRRTL.g4
index 54ad8d0e..aa53f2f0 100644
--- a/src/main/antlr4/FIRRTL.g4
+++ b/src/main/antlr4/FIRRTL.g4
@@ -103,13 +103,17 @@ stmt
| exp '<-' exp info?
| exp 'is' 'invalid' info?
| when
- | 'stop(' exp exp intLit ')' info?
- | 'printf(' exp exp StringLit ( exp)* ')' info?
+ | 'stop(' exp exp intLit ')' stmtName? info?
+ | 'printf(' exp exp StringLit ( exp)* ')' stmtName? info?
| 'skip' info?
| 'attach' '(' exp+ ')' info?
- | 'assert' '(' exp exp exp StringLit ')' info?
- | 'assume' '(' exp exp exp StringLit ')' info?
- | 'cover' '(' exp exp exp StringLit ')' info?
+ | 'assert' '(' exp exp exp StringLit ')' stmtName? info?
+ | 'assume' '(' exp exp exp StringLit ')' stmtName? info?
+ | 'cover' '(' exp exp exp StringLit ')' stmtName? info?
+ ;
+
+stmtName
+ : ':' id
;
memField
diff --git a/src/main/scala/firrtl/Namespace.scala b/src/main/scala/firrtl/Namespace.scala
index 25f4a805..a4b7bc7a 100644
--- a/src/main/scala/firrtl/Namespace.scala
+++ b/src/main/scala/firrtl/Namespace.scala
@@ -53,9 +53,11 @@ object Namespace {
val namespace = new Namespace
def buildNamespaceStmt(s: Statement): Seq[String] = s match {
- case s: IsDeclaration => Seq(s.name)
- case s: Conditionally => buildNamespaceStmt(s.conseq) ++ buildNamespaceStmt(s.alt)
- case s: Block => s.stmts.flatMap(buildNamespaceStmt)
+ // Empty names are allowed for backwards compatibility reasons and
+ // indicate that the entity has essentially no name.
+ case s: IsDeclaration if s.name.nonEmpty => Seq(s.name)
+ case s: Conditionally => buildNamespaceStmt(s.conseq) ++ buildNamespaceStmt(s.alt)
+ case s: Block => s.stmts.flatMap(buildNamespaceStmt)
case _ => Nil
}
namespace.namespace ++= m.ports.map(_.name)
diff --git a/src/main/scala/firrtl/Visitor.scala b/src/main/scala/firrtl/Visitor.scala
index dadcac46..f1b3a5c2 100644
--- a/src/main/scala/firrtl/Visitor.scala
+++ b/src/main/scala/firrtl/Visitor.scala
@@ -318,6 +318,7 @@ class Visitor(infoMode: InfoMode) extends AbstractParseTreeVisitor[FirrtlNode] w
private def visitStmt(ctx: StmtContext): Statement = {
val ctx_exp = ctx.exp.asScala
val info = visitInfo(Option(ctx.info), ctx)
+ def stmtName = Option(ctx.stmtName).map(_.id.getText).getOrElse("")
ctx.getChild(0) match {
case when: WhenContext => visitWhen(when)
case term: TerminalNode =>
@@ -346,7 +347,8 @@ class Visitor(infoMode: InfoMode) extends AbstractParseTreeVisitor[FirrtlNode] w
case "inst" => DefInstance(info, ctx.id(0).getText, ctx.id(1).getText)
case "node" => DefNode(info, ctx.id(0).getText, visitExp(ctx_exp(0)))
- case "stop(" => Stop(info, string2Int(ctx.intLit().getText), visitExp(ctx_exp(0)), visitExp(ctx_exp(1)))
+ case "stop(" =>
+ Stop(info, string2Int(ctx.intLit().getText), visitExp(ctx_exp(0)), visitExp(ctx_exp(1)), name = stmtName)
case "attach" => Attach(info, ctx_exp.map(visitExp).toSeq)
case "printf(" =>
Print(
@@ -354,7 +356,8 @@ class Visitor(infoMode: InfoMode) extends AbstractParseTreeVisitor[FirrtlNode] w
visitStringLit(ctx.StringLit),
ctx_exp.drop(2).map(visitExp).toSeq,
visitExp(ctx_exp(0)),
- visitExp(ctx_exp(1))
+ visitExp(ctx_exp(1)),
+ name = stmtName
)
// formal
case "assert" =>
@@ -364,7 +367,8 @@ class Visitor(infoMode: InfoMode) extends AbstractParseTreeVisitor[FirrtlNode] w
visitExp(ctx_exp(0)),
visitExp(ctx_exp(1)),
visitExp(ctx_exp(2)),
- visitStringLit(ctx.StringLit)
+ visitStringLit(ctx.StringLit),
+ name = stmtName
)
case "assume" =>
Verification(
@@ -373,7 +377,8 @@ class Visitor(infoMode: InfoMode) extends AbstractParseTreeVisitor[FirrtlNode] w
visitExp(ctx_exp(0)),
visitExp(ctx_exp(1)),
visitExp(ctx_exp(2)),
- visitStringLit(ctx.StringLit)
+ visitStringLit(ctx.StringLit),
+ name = stmtName
)
case "cover" =>
Verification(
@@ -382,7 +387,8 @@ class Visitor(infoMode: InfoMode) extends AbstractParseTreeVisitor[FirrtlNode] w
visitExp(ctx_exp(0)),
visitExp(ctx_exp(1)),
visitExp(ctx_exp(2)),
- visitStringLit(ctx.StringLit)
+ visitStringLit(ctx.StringLit),
+ name = stmtName
)
// end formal
case "skip" => EmptyStmt
diff --git a/src/main/scala/firrtl/WIR.scala b/src/main/scala/firrtl/WIR.scala
index a0b85007..78536a36 100644
--- a/src/main/scala/firrtl/WIR.scala
+++ b/src/main/scala/firrtl/WIR.scala
@@ -119,6 +119,7 @@ case class WDefInstanceConnector(
portCons: Seq[(Expression, Expression)])
extends Statement
with IsDeclaration
+ with CanBeReferenced
with UseSerializer {
def mapExpr(f: Expression => Expression): Statement =
this.copy(portCons = portCons.map { case (e1, e2) => (f(e1), f(e2)) })
@@ -346,6 +347,7 @@ case class CDefMemory(
readUnderWrite: ReadUnderWrite.Value = ReadUnderWrite.Undefined)
extends Statement
with HasInfo
+ with CanBeReferenced
with UseSerializer {
def mapExpr(f: Expression => Expression): Statement = this
def mapStmt(f: Statement => Statement): Statement = this
@@ -361,6 +363,7 @@ case class CDefMemory(
case class CDefMPort(info: Info, name: String, tpe: Type, mem: String, exps: Seq[Expression], direction: MPortDir)
extends Statement
with HasInfo
+ with CanBeReferenced
with UseSerializer {
def mapExpr(f: Expression => Expression): Statement = this.copy(exps = exps.map(f))
def mapStmt(f: Statement => Statement): Statement = this
diff --git a/src/main/scala/firrtl/analyses/SymbolTable.scala b/src/main/scala/firrtl/analyses/SymbolTable.scala
index 3b304bc1..e4a53444 100644
--- a/src/main/scala/firrtl/analyses/SymbolTable.scala
+++ b/src/main/scala/firrtl/analyses/SymbolTable.scala
@@ -87,6 +87,10 @@ object SymbolTable {
case d: DefNode => table.declare(d)
case d: DefWire => table.declare(d)
case d: DefRegister => table.declare(d)
+ // Matches named statements like printf, stop, assert, assume, cover if the name is not empty.
+ // Empty names are allowed for backwards compatibility reasons and
+ // indicate that the entity has essentially no name.
+ case s: IsDeclaration if s.name.nonEmpty => table.declare(s.name, UnknownType, firrtl.UnknownKind)
case other => other.foreachStmt(scanStatement)
}
}
diff --git a/src/main/scala/firrtl/graph/DiGraph.scala b/src/main/scala/firrtl/graph/DiGraph.scala
index 9f6ffeb2..99bf8403 100644
--- a/src/main/scala/firrtl/graph/DiGraph.scala
+++ b/src/main/scala/firrtl/graph/DiGraph.scala
@@ -423,8 +423,7 @@ class DiGraph[T](private[graph] val edges: LinkedHashMap[T, LinkedHashSet[T]]) {
rec(nextTab, nodex, nextMark + " ", acc)
}
}
- this.findSources
- .toList // Convert LinkedHashSet to List to avoid determinism issues
+ this.findSources.toList // Convert LinkedHashSet to List to avoid determinism issues
.sortBy(_.toString) // Make order deterministic
.foldLeft(Nil: List[String]) {
case (acc, root) => rec("", root, "", acc)
diff --git a/src/main/scala/firrtl/ir/IR.scala b/src/main/scala/firrtl/ir/IR.scala
index 1b564d42..9c3d6186 100644
--- a/src/main/scala/firrtl/ir/IR.scala
+++ b/src/main/scala/firrtl/ir/IR.scala
@@ -4,7 +4,7 @@ package firrtl
package ir
import Utils.{dec2string, trim}
-import dataclass.data
+import dataclass.{data, since}
import firrtl.constraint.{Constraint, IsKnown, IsVar}
import org.apache.commons.text.translate.{AggregateTranslator, JavaUnicodeEscaper, LookupTranslator}
@@ -227,6 +227,13 @@ abstract class Expression extends FirrtlNode {
*/
sealed trait RefLikeExpression extends Expression { def flow: Flow }
+/** Represents a statement that can be referenced in a firrtl expression.
+ * This explicitly excludes named side-effecting statements like Print, Stop and Verification.
+ * Note: This trait cannot be sealed since the memory ports are declared in WIR.scala.
+ * Once we fully remove all WIR, this trait could be sealed.
+ */
+trait CanBeReferenced
+
object Reference {
/** Creates a Reference from a Wire */
@@ -387,7 +394,11 @@ abstract class Statement extends FirrtlNode {
def foreachString(f: String => Unit): Unit
def foreachInfo(f: Info => Unit): Unit
}
-case class DefWire(info: Info, name: String, tpe: Type) extends Statement with IsDeclaration with UseSerializer {
+case class DefWire(info: Info, name: String, tpe: Type)
+ extends Statement
+ with IsDeclaration
+ with CanBeReferenced
+ with UseSerializer {
def mapStmt(f: Statement => Statement): Statement = this
def mapExpr(f: Expression => Expression): Statement = this
def mapType(f: Type => Type): Statement = DefWire(info, name, f(tpe))
@@ -408,6 +419,7 @@ case class DefRegister(
init: Expression)
extends Statement
with IsDeclaration
+ with CanBeReferenced
with UseSerializer {
def mapStmt(f: Statement => Statement): Statement = this
def mapExpr(f: Expression => Expression): Statement =
@@ -429,6 +441,7 @@ object DefInstance {
case class DefInstance(info: Info, name: String, module: String, tpe: Type = UnknownType)
extends Statement
with IsDeclaration
+ with CanBeReferenced
with UseSerializer {
def mapExpr(f: Expression => Expression): Statement = this
def mapStmt(f: Statement => Statement): Statement = this
@@ -462,6 +475,7 @@ case class DefMemory(
readUnderWrite: ReadUnderWrite.Value = ReadUnderWrite.Undefined)
extends Statement
with IsDeclaration
+ with CanBeReferenced
with UseSerializer {
def mapStmt(f: Statement => Statement): Statement = this
def mapExpr(f: Expression => Expression): Statement = this
@@ -477,6 +491,7 @@ case class DefMemory(
case class DefNode(info: Info, name: String, value: Expression)
extends Statement
with IsDeclaration
+ with CanBeReferenced
with UseSerializer {
def mapStmt(f: Statement => Statement): Statement = this
def mapExpr(f: Expression => Expression): Statement = DefNode(info, name, f(value))
@@ -594,22 +609,24 @@ case class Attach(info: Info, exprs: Seq[Expression]) extends Statement with Has
def foreachString(f: String => Unit): Unit = ()
def foreachInfo(f: Info => Unit): Unit = f(info)
}
-@data class Stop(info: Info, ret: Int, clk: Expression, en: Expression)
+
+@data class Stop(info: Info, ret: Int, clk: Expression, en: Expression, @since("FIRRTL 1.5") name: String = "")
extends Statement
with HasInfo
+ with IsDeclaration
with UseSerializer {
def mapStmt(f: Statement => Statement): Statement = this
- def mapExpr(f: Expression => Expression): Statement = Stop(info, ret, f(clk), f(en))
+ def mapExpr(f: Expression => Expression): Statement = Stop(info, ret, f(clk), f(en), name)
def mapType(f: Type => Type): Statement = this
- def mapString(f: String => String): Statement = this
+ def mapString(f: String => String): Statement = withName(f(name))
def mapInfo(f: Info => Info): Statement = this.copy(info = f(info))
def foreachStmt(f: Statement => Unit): Unit = ()
def foreachExpr(f: Expression => Unit): Unit = { f(clk); f(en) }
def foreachType(f: Type => Unit): Unit = ()
- def foreachString(f: String => Unit): Unit = ()
+ def foreachString(f: String => Unit): Unit = f(name)
def foreachInfo(f: Info => Unit): Unit = f(info)
def copy(info: Info = info, ret: Int = ret, clk: Expression = clk, en: Expression = en): Stop = {
- Stop(info, ret, clk, en)
+ Stop(info, ret, clk, en, name)
}
}
object Stop {
@@ -622,19 +639,22 @@ object Stop {
string: StringLit,
args: Seq[Expression],
clk: Expression,
- en: Expression)
+ en: Expression,
+ @since("FIRRTL 1.5")
+ name: String = "")
extends Statement
with HasInfo
+ with IsDeclaration
with UseSerializer {
def mapStmt(f: Statement => Statement): Statement = this
- def mapExpr(f: Expression => Expression): Statement = Print(info, string, args.map(f), f(clk), f(en))
+ def mapExpr(f: Expression => Expression): Statement = Print(info, string, args.map(f), f(clk), f(en), name)
def mapType(f: Type => Type): Statement = this
- def mapString(f: String => String): Statement = this
+ def mapString(f: String => String): Statement = withName(f(name))
def mapInfo(f: Info => Info): Statement = this.copy(info = f(info))
def foreachStmt(f: Statement => Unit): Unit = ()
def foreachExpr(f: Expression => Unit): Unit = { args.foreach(f); f(clk); f(en) }
def foreachType(f: Type => Unit): Unit = ()
- def foreachString(f: String => Unit): Unit = ()
+ def foreachString(f: String => Unit): Unit = f(name)
def foreachInfo(f: Info => Unit): Unit = f(info)
def copy(
info: Info = info,
@@ -643,7 +663,7 @@ object Stop {
clk: Expression = clk,
en: Expression = en
): Print = {
- Print(info, string, args, clk, en)
+ Print(info, string, args, clk, en, name)
}
}
object Print {
@@ -665,20 +685,23 @@ object Formal extends Enumeration {
clk: Expression,
pred: Expression,
en: Expression,
- msg: StringLit)
+ msg: StringLit,
+ @since("FIRRTL 1.5")
+ name: String = "")
extends Statement
with HasInfo
+ with IsDeclaration
with UseSerializer {
def mapStmt(f: Statement => Statement): Statement = this
def mapExpr(f: Expression => Expression): Statement =
copy(clk = f(clk), pred = f(pred), en = f(en))
def mapType(f: Type => Type): Statement = this
- def mapString(f: String => String): Statement = this
+ def mapString(f: String => String): Statement = withName(f(name))
def mapInfo(f: Info => Info): Statement = copy(info = f(info))
def foreachStmt(f: Statement => Unit): Unit = ()
def foreachExpr(f: Expression => Unit): Unit = { f(clk); f(pred); f(en); }
def foreachType(f: Type => Unit): Unit = ()
- def foreachString(f: String => Unit): Unit = ()
+ def foreachString(f: String => Unit): Unit = f(name)
def foreachInfo(f: Info => Unit): Unit = f(info)
def copy(
op: Formal.Value = op,
@@ -688,7 +711,7 @@ object Formal extends Enumeration {
en: Expression = en,
msg: StringLit = msg
): Verification = {
- Verification(op, info, clk, pred, en, msg)
+ Verification(op, info, clk, pred, en, msg, name)
}
}
object Verification {
@@ -1016,6 +1039,7 @@ case class Port(
tpe: Type)
extends FirrtlNode
with IsDeclaration
+ with CanBeReferenced
with UseSerializer {
def mapType(f: Type => Type): Port = Port(info, name, direction, f(tpe))
def mapString(f: String => String): Port = Port(info, f(name), direction, tpe)
diff --git a/src/main/scala/firrtl/ir/Serializer.scala b/src/main/scala/firrtl/ir/Serializer.scala
index 983a7866..caea0a9c 100644
--- a/src/main/scala/firrtl/ir/Serializer.scala
+++ b/src/main/scala/firrtl/ir/Serializer.scala
@@ -102,11 +102,13 @@ object Serializer {
s(it.next())
if (it.hasNext) newLineAndIndent()
}
- case Stop(info, ret, clk, en) =>
- b ++= "stop("; s(clk); b ++= ", "; s(en); b ++= ", "; b ++= ret.toString; b += ')'; s(info)
- case Print(info, string, args, clk, en) =>
+ case stop @ Stop(info, ret, clk, en) =>
+ b ++= "stop("; s(clk); b ++= ", "; s(en); b ++= ", "; b ++= ret.toString; b += ')'
+ sStmtName(stop.name); s(info)
+ case print @ Print(info, string, args, clk, en) =>
b ++= "printf("; s(clk); b ++= ", "; s(en); b ++= ", "; b ++= string.escape
- if (args.nonEmpty) b ++= ", "; s(args, ", "); b += ')'; s(info)
+ if (args.nonEmpty) b ++= ", "; s(args, ", "); b += ')'
+ sStmtName(print.name); s(info)
case IsInvalid(info, expr) => s(expr); b ++= " is invalid"; s(info)
case DefWire(info, name, tpe) => b ++= "wire "; b ++= name; b ++= " : "; s(tpe); s(info)
case DefRegister(info, name, tpe, clock, reset, init) =>
@@ -138,9 +140,9 @@ object Serializer {
case Attach(info, exprs) =>
// exprs should never be empty since the attach statement takes *at least* two signals according to the spec
b ++= "attach ("; s(exprs, ", "); b += ')'; s(info)
- case Verification(op, info, clk, pred, en, msg) =>
+ case veri @ Verification(op, info, clk, pred, en, msg) =>
b ++= op.toString; b += '('; s(List(clk, pred, en), ", ", false); b ++= msg.escape
- b += ')'; s(info)
+ b += ')'; sStmtName(veri.name); s(info)
// WIR
case firrtl.CDefMemory(info, name, tpe, size, seq, readUnderWrite) =>
@@ -155,6 +157,10 @@ object Serializer {
case other => b ++= other.serialize // Handle user-defined nodes
}
+ private def sStmtName(lbl: String)(implicit b: StringBuilder): Unit = {
+ if (lbl.nonEmpty) { b ++= s" : $lbl" }
+ }
+
private def s(node: Width)(implicit b: StringBuilder, indent: Int): Unit = node match {
case IntWidth(width) => b += '<'; b ++= width.toString(); b += '>'
case UnknownWidth => // empty string
diff --git a/src/main/scala/firrtl/passes/CheckHighForm.scala b/src/main/scala/firrtl/passes/CheckHighForm.scala
index e3468c4e..05635d00 100644
--- a/src/main/scala/firrtl/passes/CheckHighForm.scala
+++ b/src/main/scala/firrtl/passes/CheckHighForm.scala
@@ -22,6 +22,10 @@ trait CheckHighFormLike { this: Pass =>
moduleNS += name
scopes.head += name
}
+ // ensures that the name cannot be used again, but prevent references to this name
+ def addToNamespace(name: String): Unit = {
+ moduleNS += name
+ }
def expandMPortVisibility(port: CDefMPort): Unit = {
// Legacy CHIRRTL ports are visible in any scope where their parent memory is visible
scopes.find(_.contains(port.mem)).getOrElse(scopes.head) += port.name
@@ -250,10 +254,19 @@ trait CheckHighFormLike { this: Pass =>
e.foreach(checkHighFormE(info, mname, names))
}
- def checkName(info: Info, mname: String, names: ScopeView)(name: String): Unit = {
- if (!names.legalDecl(name))
- errors.append(new NotUniqueException(info, mname, name))
- names.declare(name)
+ def checkName(info: Info, mname: String, names: ScopeView, canBeReference: Boolean)(name: String): Unit = {
+ // Empty names are allowed for backwards compatibility reasons and
+ // indicate that the entity has essentially no name.
+ if (name.isEmpty) { assert(!canBeReference, "A statement with an empty name cannot be used as a reference!") }
+ else {
+ if (!names.legalDecl(name))
+ errors.append(new NotUniqueException(info, mname, name))
+ if (canBeReference) {
+ names.declare(name)
+ } else {
+ names.addToNamespace(name)
+ }
+ }
}
def checkInstance(info: Info, child: String, parent: String): Unit = {
@@ -270,7 +283,11 @@ trait CheckHighFormLike { this: Pass =>
case NoInfo => minfo
case x => x
}
- s.foreach(checkName(info, mname, names))
+ val canBeReference = s match {
+ case _: CanBeReferenced => true
+ case _ => false
+ }
+ s.foreach(checkName(info, mname, names, canBeReference))
s match {
case DefRegister(info, name, tpe, _, reset, init) =>
if (hasFlip(tpe))
diff --git a/src/main/scala/firrtl/passes/Inline.scala b/src/main/scala/firrtl/passes/Inline.scala
index 4eba5d59..912acf8e 100644
--- a/src/main/scala/firrtl/passes/Inline.scala
+++ b/src/main/scala/firrtl/passes/Inline.scala
@@ -187,18 +187,23 @@ class InlineInstances extends Transform with DependencyAPIMigration with Registe
renameMap: RenameMap
)(s: Statement
): Statement = {
- def onName(ofModuleOpt: Option[String])(name: String) = {
- if (prefix.nonEmpty && !ns.tryName(prefix + name)) {
- throw new Exception(s"Inlining failed. Inlined name '${prefix + name}' already exists")
- }
- ofModuleOpt match {
- case None =>
- renameMap.record(currentModule.ref(name), nextModule.ref(prefix + name))
- case Some(ofModule) =>
- renameMap.record(currentModule.instOf(name, ofModule), nextModule.instOf(prefix + name, ofModule))
+ def onName(ofModuleOpt: Option[String])(name: String): String = {
+ // Empty names are allowed for backwards compatibility reasons and
+ // indicate that the entity has essentially no name and thus cannot be prefixed.
+ if (name.isEmpty) { name }
+ else {
+ if (prefix.nonEmpty && !ns.tryName(prefix + name)) {
+ throw new Exception(s"Inlining failed. Inlined name '${prefix + name}' already exists")
+ }
+ ofModuleOpt match {
+ case None =>
+ renameMap.record(currentModule.ref(name), nextModule.ref(prefix + name))
+ case Some(ofModule) =>
+ renameMap.record(currentModule.instOf(name, ofModule), nextModule.instOf(prefix + name, ofModule))
+ }
+ renames(name) = prefix + name
+ prefix + name
}
- renames(name) = prefix + name
- prefix + name
}
s match {
diff --git a/src/main/scala/firrtl/transforms/DeadCodeElimination.scala b/src/main/scala/firrtl/transforms/DeadCodeElimination.scala
index 13173fdd..1d9bfd0e 100644
--- a/src/main/scala/firrtl/transforms/DeadCodeElimination.scala
+++ b/src/main/scala/firrtl/transforms/DeadCodeElimination.scala
@@ -258,6 +258,11 @@ class DeadCodeElimination extends Transform with RegisteredTransform with Depend
renames.delete(inst.name)
EmptyStmt
}
+ case print: Print => deleteIfNotEnabled(print, print.en)
+ case stop: Stop => deleteIfNotEnabled(stop, stop.en)
+ case formal: Verification => deleteIfNotEnabled(formal, formal.en)
+ // Statements are also declarations and thus this case needs to come *after* checking the
+ // print, stop and verification statements.
case decl: IsDeclaration =>
val node = LogicNode(mod.name, decl.name)
if (deadNodes.contains(node)) {
@@ -265,10 +270,7 @@ class DeadCodeElimination extends Transform with RegisteredTransform with Depend
renames.delete(decl.name)
EmptyStmt
} else decl
- case print: Print => deleteIfNotEnabled(print, print.en)
- case stop: Stop => deleteIfNotEnabled(stop, stop.en)
- case formal: Verification => deleteIfNotEnabled(formal, formal.en)
- case con: Connect =>
+ case con: Connect =>
val node = getDeps(con.loc) match { case Seq(elt) => elt }
if (deadNodes.contains(node)) EmptyStmt else con
case Attach(info, exprs) => // If any exprs are dead then all are
diff --git a/src/main/scala/firrtl/transforms/EnsureNamedStatements.scala b/src/main/scala/firrtl/transforms/EnsureNamedStatements.scala
new file mode 100644
index 00000000..a40409f9
--- /dev/null
+++ b/src/main/scala/firrtl/transforms/EnsureNamedStatements.scala
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package firrtl.transforms
+
+import firrtl._
+import firrtl.ir._
+
+/** Adds default names to print, stop and verification statements if their name is empty. */
+object EnsureNamedStatements extends Transform with DependencyAPIMigration {
+ override def invalidates(a: Transform) = false
+
+ override protected def execute(state: CircuitState): CircuitState = {
+ val c = state.circuit.mapModule(onModule)
+ state.copy(circuit = c)
+ }
+
+ private def onModule(m: DefModule): DefModule = m match {
+ case e: ExtModule => e
+ case mod: Module =>
+ val namespace = Namespace(mod)
+ // Ensure we always start with _0 suffix
+ val prefixes = Seq("cover", "assert", "assume", "print", "stop")
+ prefixes.filterNot(namespace.contains).foreach(namespace.newName)
+ mod.mapStmt(onStmt(namespace))
+ }
+
+ private def onStmt(namespace: Namespace)(stmt: Statement): Statement = stmt match {
+ case s: Print if s.name.isEmpty => s.withName(namespace.newName("print"))
+ case s: Stop if s.name.isEmpty => s.withName(namespace.newName("stop"))
+ case s: Verification if s.name.isEmpty =>
+ val baseName = s.op match {
+ case Formal.Cover => "cover"
+ case Formal.Assert => "assert"
+ case Formal.Assume => "assume"
+ }
+ s.withName(namespace.newName(baseName))
+ case other => other.mapStmt(onStmt(namespace))
+ }
+}
diff --git a/src/main/scala/firrtl/transforms/RemoveWires.scala b/src/main/scala/firrtl/transforms/RemoveWires.scala
index ee03ad30..f2907db2 100644
--- a/src/main/scala/firrtl/transforms/RemoveWires.scala
+++ b/src/main/scala/firrtl/transforms/RemoveWires.scala
@@ -115,7 +115,10 @@ class RemoveWires extends Transform with DependencyAPIMigration {
val initDep = Some(reg.init).filter(we(WRef(reg)) != we(_)) // Dependency exists IF reg doesn't init itself
regInfo(we(WRef(reg))) = reg
netlist(we(WRef(reg))) = (Seq(reg.clock) ++ resetDep ++ initDep, reg.info)
- case decl: IsDeclaration => // Keep all declarations except for nodes and non-Analog wires
+ case decl: CanBeReferenced =>
+ // Keep all declarations except for nodes and non-Analog wires and "other" statements.
+ // Thus this is expected to match DefInstance and DefMemory which both do not connect to
+ // any signals directly (instead a separate Connect is used).
decls += decl
case con @ Connect(cinfo, lhs, rhs) =>
kind(lhs) match {
diff --git a/src/test/scala/firrtl/testutils/FirrtlSpec.scala b/src/test/scala/firrtl/testutils/FirrtlSpec.scala
index 6de2af1e..24793437 100644
--- a/src/test/scala/firrtl/testutils/FirrtlSpec.scala
+++ b/src/test/scala/firrtl/testutils/FirrtlSpec.scala
@@ -123,21 +123,21 @@ trait FirrtlRunners extends BackendCompilationUtilities {
}
/** Check equivalence of Firrtl with reference Verilog
- *
+ *
* @note the name of the reference Verilog module is grabbed via regex
* @param inputFirrtl string containing Firrtl source
* @param referenceVerilog Verilog that will be used as reference for LEC
* @param timesteps the maximum number of timesteps to consider
*/
def firrtlEquivalenceWithVerilog(
- inputFirrtl: String,
- referenceVerilog: String,
- timesteps: Int = 1
+ inputFirrtl: String,
+ referenceVerilog: String,
+ timesteps: Int = 1
): Unit = {
val VerilogModule = """(?s).*module\s(\w+).*""".r
val refName = referenceVerilog match {
case VerilogModule(name) => name
- case _ => throw new Exception(s"Reference Verilog must match simple regex! $VerilogModule")
+ case _ => throw new Exception(s"Reference Verilog must match simple regex! $VerilogModule")
}
val circuit = Parser.parse(inputFirrtl.split("\n").toIterator)
val inputName = circuit.main
@@ -163,7 +163,6 @@ trait FirrtlRunners extends BackendCompilationUtilities {
assert(BackendCompilationUtilities.yosysExpectSuccess(inputName, refName, testDir, timesteps))
}
-
/** Compiles input Firrtl to Verilog */
def compileToVerilog(input: String, annotations: AnnotationSeq = Seq.empty): String = {
val circuit = Parser.parse(input.split("\n").toIterator)
diff --git a/src/test/scala/firrtl/transforms/EnsureNamedStatementsSpec.scala b/src/test/scala/firrtl/transforms/EnsureNamedStatementsSpec.scala
new file mode 100644
index 00000000..4c993994
--- /dev/null
+++ b/src/test/scala/firrtl/transforms/EnsureNamedStatementsSpec.scala
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package firrtl.transforms
+
+import firrtl.options.Dependency
+import firrtl.testutils.LeanTransformSpec
+
+class EnsureNamedStatementsSpec extends LeanTransformSpec(Seq(Dependency(EnsureNamedStatements))) {
+ behavior.of("EnsureNamedStatements")
+
+ it should "automatically name statements that do not have a name yet" in {
+ val src = """circuit test :
+ | module test :
+ | input clock : Clock
+ | input stop_ : UInt<1>
+ | assert(clock, UInt(1), not(UInt(0)), "")
+ | stop(clock, UInt(1), 1) : stop_123
+ | stop(clock, UInt(1), 1)
+ | assert(clock, UInt(0), UInt(0), "")
+ | assume(clock, UInt(0), UInt(0), "")
+ | cover(clock, UInt(0), UInt(0), "")
+ | cover(clock, UInt(0), UInt(0), "")
+ |
+ |""".stripMargin
+
+ val result = compile(src, List()).circuit.serialize.split('\n').map(_.trim)
+
+ val expected = List(
+ """assert(clock, UInt<1>("h1"), not(UInt<1>("h0")), "") : assert_0""",
+ """stop(clock, UInt<1>("h1"), 1) : stop_123""",
+ """stop(clock, UInt<1>("h1"), 1) : stop_0""",
+ """assert(clock, UInt<1>("h0"), UInt<1>("h0"), "") : assert_1""",
+ """assume(clock, UInt<1>("h0"), UInt<1>("h0"), "") : assume_0""",
+ """cover(clock, UInt<1>("h0"), UInt<1>("h0"), "") : cover_0""",
+ """cover(clock, UInt<1>("h0"), UInt<1>("h0"), "") : cover_1"""
+ )
+ expected.foreach(e => assert(result.contains(e)))
+ }
+}
diff --git a/src/test/scala/firrtlTests/CheckSpec.scala b/src/test/scala/firrtlTests/CheckSpec.scala
index 1137f8cd..a3efc784 100644
--- a/src/test/scala/firrtlTests/CheckSpec.scala
+++ b/src/test/scala/firrtlTests/CheckSpec.scala
@@ -384,6 +384,36 @@ class CheckSpec extends AnyFlatSpec with Matchers {
}
}
+ "Attempting to shadow a statement name" should "throw an error" in {
+ val input =
+ s"""|circuit scopes:
+ | module scopes:
+ | input c: Clock
+ | input i: UInt<1>
+ | output o: UInt<1>
+ | wire x: UInt<1>
+ | when i:
+ | stop(c, UInt(1), 1) : x
+ | o <= and(x, i)
+ |""".stripMargin
+ assertThrows[CheckHighForm.NotUniqueException] {
+ checkHighInput(input)
+ }
+ }
+
+ "Colliding statement names" should "throw an error" in {
+ val input =
+ s"""|circuit test:
+ | module test:
+ | input c: Clock
+ | stop(c, UInt(1), 1) : x
+ | stop(c, UInt(1), 1) : x
+ |""".stripMargin
+ assertThrows[CheckHighForm.NotUniqueException] {
+ checkHighInput(input)
+ }
+ }
+
"Conditionally statements" should "create separate consequent and alternate scopes" in {
val input =
s"""|circuit scopes:
@@ -536,6 +566,20 @@ class CheckSpec extends AnyFlatSpec with Matchers {
}
}
+ it should "throw an exception if a statement name is used as a reference" in {
+ val src = """
+ |circuit test:
+ | module test:
+ | input clock: Clock
+ | output a: UInt<2>
+ | stop(clock, UInt(1), 1) : hello
+ | a <= hello
+ |""".stripMargin
+ assertThrows[CheckHighForm.UndeclaredReferenceException] {
+ checkHighInput(src)
+ }
+ }
+
}
object CheckSpec {
diff --git a/src/test/scala/firrtlTests/InlineInstancesTests.scala b/src/test/scala/firrtlTests/InlineInstancesTests.scala
index 6bee2b77..cc7257d2 100644
--- a/src/test/scala/firrtlTests/InlineInstancesTests.scala
+++ b/src/test/scala/firrtlTests/InlineInstancesTests.scala
@@ -460,6 +460,60 @@ class InlineInstancesTests extends LowTransformSpec {
)
}
+ "inlining named statements" should "work" in {
+ val input =
+ """circuit Top :
+ | module Top :
+ | input clock : Clock
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | inst i of Inline
+ | i.clock <= clock
+ | i.a <= a
+ | b <= i.b
+ | module Inline :
+ | input clock : Clock
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | b <= a
+ | assert(clock, UInt(1), eq(a,b), "a == b") : assert1
+ | assert(clock, UInt(1), not(eq(a,b)), "a != b")
+ | stop(clock, UInt(0), 0)
+ |""".stripMargin
+ val check =
+ """circuit Top :
+ | module Top :
+ | input clock : Clock
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | wire i_clock : Clock
+ | wire i_a : UInt<32>
+ | wire i_b : UInt<32>
+ | i_b <= i_a
+ | assert(i_clock, UInt(1), eq(i_a, i_b), "a == b") : i_assert1
+ | assert(i_clock, UInt(1), not(eq(i_a, i_b)), "a != b")
+ | stop(i_clock, UInt(0), 0)
+ | b <= i_b
+ | i_clock <= clock
+ | i_a <= a
+ |""".stripMargin
+ val top = CircuitTarget("Top").module("Top")
+ val inlined = top.instOf("i", "Inline")
+
+ executeWithAnnos(
+ input,
+ check,
+ Seq(
+ inline("Inline"),
+ NoCircuitDedupAnnotation,
+ DummyAnno(inlined.ref("assert1"))
+ ),
+ Seq(
+ DummyAnno(top.ref("i_assert1"))
+ )
+ )
+ }
+
"inlining both grandparent and grandchild" should "should work" in {
val input =
"""circuit Top :
diff --git a/src/test/scala/firrtlTests/LowerTypesSpec.scala b/src/test/scala/firrtlTests/LowerTypesSpec.scala
index da84b362..78d03e68 100644
--- a/src/test/scala/firrtlTests/LowerTypesSpec.scala
+++ b/src/test/scala/firrtlTests/LowerTypesSpec.scala
@@ -321,6 +321,25 @@ class LowerTypesUniquifySpec extends FirrtlFlatSpec {
executeTest(input, expected)
}
+ it should "rename nodes colliding with labled statements" in {
+ val input =
+ """circuit Test :
+ | module Test :
+ | input clock : Clock
+ | reg x : { b : UInt<1>, c : { d : UInt<2>, e : UInt<3>}[2], c_1_e : UInt<4>}[2], clock
+ | node a = x
+ | printf(clock, UInt(1), "") : a_0_c_
+ | assert(clock, UInt(1), UInt(1), "") : a__0
+ """.stripMargin
+ val expected = Seq(
+ "node a___0_b = x_0_b",
+ "node a___1_c__1_e = x_1_c__1_e",
+ "node a___1_c_1_e = x_1_c_1_e"
+ )
+
+ executeTest(input, expected)
+ }
+
it should "rename DefRegister expressions: clock, reset, and init" in {
val input =
"""circuit Test :
diff --git a/src/test/scala/firrtlTests/ParserSpec.scala b/src/test/scala/firrtlTests/ParserSpec.scala
index 373b960c..ba61b134 100644
--- a/src/test/scala/firrtlTests/ParserSpec.scala
+++ b/src/test/scala/firrtlTests/ParserSpec.scala
@@ -147,6 +147,27 @@ class ParserSpec extends FirrtlFlatSpec {
}
}
+ // ********** Statement labels **********
+ it should "allow certain statement to have a label" in {
+ val prelude = Seq("circuit top :", " module top :", " input c : Clock")
+ val statements = Seq("stop(c, UInt(1), 0)", "printf(c, UInt(1), \"\")") ++
+ Seq("assert", "assume", "cover").map(_ + "(c, UInt(1), UInt(1), \"\")")
+ val validLabels = Seq(":test" -> "test", " :test" -> "test", " : test" -> "test", " : test01" -> "test01")
+ statements.foreach { stmt =>
+ validLabels.foreach {
+ case (lbl, expected) =>
+ val line = " " + stmt + lbl
+ val src = (prelude :+ line).mkString("\n") + "\n"
+ val res = firrtl.Parser.parse(src)
+ CircuitState(res, Nil) should containTree {
+ case s: Stop => s.name == expected
+ case s: Print => s.name == expected
+ case s: Verification => s.name == expected
+ }
+ }
+ }
+ }
+
// ********** Keywords **********
"Keywords" should "be allowed as Ids" in {
import KeywordTests._
diff --git a/src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala b/src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala
index d88309ce..747f6689 100644
--- a/src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala
+++ b/src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala
@@ -7,116 +7,116 @@ import firrtl.testutils._
class VerilogEquivalenceSpec extends FirrtlFlatSpec {
"mul followed by cat" should "be correct" in {
val header = s"""
- |circuit Multiply :
- | module Multiply :
- | input x : UInt<4>
- | input y : UInt<2>
- | input z : UInt<2>
- | output out : UInt<8>
- |""".stripMargin
+ |circuit Multiply :
+ | module Multiply :
+ | input x : UInt<4>
+ | input y : UInt<2>
+ | input z : UInt<2>
+ | output out : UInt<8>
+ |""".stripMargin
val input1 = header + """
- | out <= cat(z, mul(x, y))""".stripMargin
+ | out <= cat(z, mul(x, y))""".stripMargin
val input2 = header + """
- | node n = mul(x, y)
- | node m = cat(z, n)
- | out <= m""".stripMargin
+ | node n = mul(x, y)
+ | node m = cat(z, n)
+ | out <= m""".stripMargin
val expected = s"""
- |module MultiplyRef(
- | input [3:0] x,
- | input [1:0] y,
- | input [1:0] z,
- | output [7:0] out
- |);
- | wire [5:0] w = x * y;
- | assign out = {z, w};
- |endmodule""".stripMargin
+ |module MultiplyRef(
+ | input [3:0] x,
+ | input [1:0] y,
+ | input [1:0] z,
+ | output [7:0] out
+ |);
+ | wire [5:0] w = x * y;
+ | assign out = {z, w};
+ |endmodule""".stripMargin
firrtlEquivalenceWithVerilog(input1, expected)
firrtlEquivalenceWithVerilog(input2, expected)
}
"div followed by cat" should "be correct" in {
val header = s"""
- |circuit Divide :
- | module Divide :
- | input x : UInt<4>
- | input y : UInt<2>
- | input z : UInt<2>
- | output out : UInt<6>
- |""".stripMargin
+ |circuit Divide :
+ | module Divide :
+ | input x : UInt<4>
+ | input y : UInt<2>
+ | input z : UInt<2>
+ | output out : UInt<6>
+ |""".stripMargin
val input1 = header + """
- | out <= cat(z, div(x, y))""".stripMargin
+ | out <= cat(z, div(x, y))""".stripMargin
val input2 = header + """
- | node n = div(x, y)
- | node m = cat(z, n)
- | out <= m""".stripMargin
+ | node n = div(x, y)
+ | node m = cat(z, n)
+ | out <= m""".stripMargin
val expected = s"""
- |module DivideRef(
- | input [3:0] x,
- | input [1:0] y,
- | input [1:0] z,
- | output [5:0] out
- |);
- | wire [3:0] w = x / y;
- | assign out = {z, w};
- |endmodule""".stripMargin
+ |module DivideRef(
+ | input [3:0] x,
+ | input [1:0] y,
+ | input [1:0] z,
+ | output [5:0] out
+ |);
+ | wire [3:0] w = x / y;
+ | assign out = {z, w};
+ |endmodule""".stripMargin
firrtlEquivalenceWithVerilog(input1, expected)
firrtlEquivalenceWithVerilog(input2, expected)
}
"signed mul followed by cat" should "be correct" in {
val header = s"""
- |circuit SignedMultiply :
- | module SignedMultiply :
- | input x : SInt<4>
- | input y : SInt<2>
- | input z : SInt<2>
- | output out : UInt<8>
- |""".stripMargin
+ |circuit SignedMultiply :
+ | module SignedMultiply :
+ | input x : SInt<4>
+ | input y : SInt<2>
+ | input z : SInt<2>
+ | output out : UInt<8>
+ |""".stripMargin
val input1 = header + """
- | out <= cat(z, mul(x, y))""".stripMargin
+ | out <= cat(z, mul(x, y))""".stripMargin
val input2 = header + """
- | node n = mul(x, y)
- | node m = cat(z, n)
- | out <= m""".stripMargin
+ | node n = mul(x, y)
+ | node m = cat(z, n)
+ | out <= m""".stripMargin
val expected = s"""
- |module SignedMultiplyRef(
- | input signed [3:0] x,
- | input signed [1:0] y,
- | input signed [1:0] z,
- | output [7:0] out
- |);
- | wire [5:0] w = x * y;
- | assign out = {z, w};
- |endmodule""".stripMargin
+ |module SignedMultiplyRef(
+ | input signed [3:0] x,
+ | input signed [1:0] y,
+ | input signed [1:0] z,
+ | output [7:0] out
+ |);
+ | wire [5:0] w = x * y;
+ | assign out = {z, w};
+ |endmodule""".stripMargin
firrtlEquivalenceWithVerilog(input1, expected)
firrtlEquivalenceWithVerilog(input2, expected)
}
"signed div followed by cat" should "be correct" in {
val header = s"""
- |circuit SignedDivide :
- | module SignedDivide :
- | input x : SInt<4>
- | input y : SInt<2>
- | input z : SInt<2>
- | output out : UInt<7>
- |""".stripMargin
+ |circuit SignedDivide :
+ | module SignedDivide :
+ | input x : SInt<4>
+ | input y : SInt<2>
+ | input z : SInt<2>
+ | output out : UInt<7>
+ |""".stripMargin
val input1 = header + """
- | out <= cat(z, div(x, y))""".stripMargin
+ | out <= cat(z, div(x, y))""".stripMargin
val input2 = header + """
- | node n = div(x, y)
- | node m = cat(z, n)
- | out <= m""".stripMargin
+ | node n = div(x, y)
+ | node m = cat(z, n)
+ | out <= m""".stripMargin
val expected = s"""
- |module SignedDivideRef(
- | input signed [3:0] x,
- | input signed [1:0] y,
- | input signed [1:0] z,
- | output [6:0] out
- |);
- | wire [4:0] w = x / y;
- | assign out = {z, w};
- |endmodule""".stripMargin
+ |module SignedDivideRef(
+ | input signed [3:0] x,
+ | input signed [1:0] y,
+ | input signed [1:0] z,
+ | output [6:0] out
+ |);
+ | wire [4:0] w = x / y;
+ | assign out = {z, w};
+ |endmodule""".stripMargin
firrtlEquivalenceWithVerilog(input1, expected)
firrtlEquivalenceWithVerilog(input2, expected)
}