aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/antlr4/FIRRTL.g42
-rw-r--r--src/main/scala/firrtl/Emitter.scala33
-rw-r--r--src/main/scala/firrtl/Visitor.scala2
-rw-r--r--src/main/scala/firrtl/WIR.scala20
-rw-r--r--src/main/scala/firrtl/ir/IR.scala6
-rw-r--r--src/main/scala/firrtl/passes/CheckWidths.scala8
-rw-r--r--src/main/scala/firrtl/passes/Checks.scala16
-rw-r--r--src/main/scala/firrtl/passes/ExpandWhens.scala58
-rw-r--r--src/main/scala/firrtl/passes/InferWidths.scala6
-rw-r--r--src/main/scala/firrtl/passes/Passes.scala99
-rw-r--r--src/test/scala/firrtlTests/AttachSpec.scala349
11 files changed, 329 insertions, 270 deletions
diff --git a/src/main/antlr4/FIRRTL.g4 b/src/main/antlr4/FIRRTL.g4
index f5bcb685..9e6a46ee 100644
--- a/src/main/antlr4/FIRRTL.g4
+++ b/src/main/antlr4/FIRRTL.g4
@@ -108,7 +108,7 @@ stmt
| 'stop(' exp exp IntLit ')' info?
| 'printf(' exp exp StringLit ( exp)* ')' info?
| 'skip' info?
- | 'attach' exp 'to' '(' exp* ')' info?
+ | 'attach' '(' exp+ ')' info?
;
memField
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala
index a290ced4..36e4929a 100644
--- a/src/main/scala/firrtl/Emitter.scala
+++ b/src/main/scala/firrtl/Emitter.scala
@@ -240,6 +240,8 @@ class VerilogEmitter extends Emitter with PassBased {
val declares = ArrayBuffer[Seq[Any]]()
val instdeclares = ArrayBuffer[Seq[Any]]()
val assigns = ArrayBuffer[Seq[Any]]()
+ val attachSynAssigns = ArrayBuffer.empty[Seq[Any]]
+ val attachAliases = ArrayBuffer.empty[Seq[Any]]
val at_clock = mutable.LinkedHashMap[Expression,ArrayBuffer[Seq[Any]]]()
val initials = ArrayBuffer[Seq[Any]]()
val simulates = ArrayBuffer[Seq[Any]]()
@@ -424,18 +426,30 @@ class VerilogEmitter extends Emitter with PassBased {
case sx: Print =>
simulate(sx.clk, sx.en, printf(sx.string, sx.args), Some("PRINTF_COND"))
sx
+ // If we are emitting an Attach, it must not have been removable in VerilogPrep
+ case sx: Attach =>
+ // For Synthesis
+ // Note that this is quadratic in the number of things attached
+ for (set <- sx.exprs.toSet.subsets(2)) {
+ val (a, b) = set.toSeq match { case Seq(x, y) => (x, y) }
+ // Synthesizable ones as well
+ attachSynAssigns += Seq("assign ", a, " = ", b, ";")
+ attachSynAssigns += Seq("assign ", b, " = ", a, ";")
+ }
+ // alias implementation for everything else
+ attachAliases += Seq("alias ", sx.exprs.flatMap(e => Seq(e, " = ")).init, ";")
+ sx
case sx: WDefInstanceConnector =>
val (module, params) = moduleMap(sx.module) match {
case ExtModule(_, _, _, extname, params) => (extname, params)
case Module(_, name, _, _) => (name, Seq.empty)
}
- val es = create_exps(WRef(sx.name, sx.tpe, InstanceKind, MALE))
val ps = if (params.nonEmpty) params map stringify mkString ("#(", ", ", ") ") else ""
instdeclares += Seq(module, " ", ps, sx.name ," (")
- (es zip sx.exprs).zipWithIndex foreach {case ((l, r), i) =>
- val s = Seq(tab, ".", remove_root(l), "(", r, ")")
- if (i != es.size - 1) instdeclares += Seq(s, ",")
- else instdeclares += s
+ for (((port, ref), i) <- sx.portCons.zipWithIndex) {
+ val line = Seq(tab, ".", remove_root(port), "(", ref, ")")
+ if (i != sx.portCons.size - 1) instdeclares += Seq(line, ",")
+ else instdeclares += line
}
instdeclares += Seq(");")
sx
@@ -515,6 +529,15 @@ class VerilogEmitter extends Emitter with PassBased {
for (x <- declares) emit(Seq(tab, x))
for (x <- instdeclares) emit(Seq(tab, x))
for (x <- assigns) emit(Seq(tab, x))
+ if (attachAliases.nonEmpty) {
+ emit(Seq("`ifdef SYNTHESIS"))
+ for (x <- attachSynAssigns) emit(Seq(tab, x))
+ emit(Seq("`elseif verilator"))
+ emit(Seq(tab, "`error \"Verilator does not support alias and thus cannot arbirarily connect bidirectional wires and ports\""))
+ emit(Seq("`else"))
+ for (x <- attachAliases) emit(Seq(tab, x))
+ emit(Seq("`endif"))
+ }
if (initials.nonEmpty) {
emit(Seq("`ifdef RANDOMIZE"))
emit(Seq(" integer initvar;"))
diff --git a/src/main/scala/firrtl/Visitor.scala b/src/main/scala/firrtl/Visitor.scala
index 1cdf7e1f..592cfa8e 100644
--- a/src/main/scala/firrtl/Visitor.scala
+++ b/src/main/scala/firrtl/Visitor.scala
@@ -268,7 +268,7 @@ class Visitor(infoMode: InfoMode) extends FIRRTLBaseVisitor[FirrtlNode] {
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 "attach" => Attach(info, visitExp(ctx.exp.head), ctx.exp.tail map visitExp)
+ case "attach" => Attach(info, ctx.exp map visitExp)
case "printf(" => Print(info, visitStringLit(ctx.StringLit), ctx.exp.drop(2).map(visitExp),
visitExp(ctx.exp(0)), visitExp(ctx.exp(1)))
case "skip" => EmptyStmt
diff --git a/src/main/scala/firrtl/WIR.scala b/src/main/scala/firrtl/WIR.scala
index 0ce8f61e..bc256b68 100644
--- a/src/main/scala/firrtl/WIR.scala
+++ b/src/main/scala/firrtl/WIR.scala
@@ -30,6 +30,12 @@ case class WRef(name: String, tpe: Type, kind: Kind, gender: Gender) extends Exp
def mapType(f: Type => Type): Expression = this.copy(tpe = f(tpe))
def mapWidth(f: Width => Width): Expression = this
}
+object WRef {
+ /** Creates a WRef from a Wire */
+ def apply(wire: DefWire): WRef = new WRef(wire.name, wire.tpe, WireKind, UNKNOWNGENDER)
+ /** Creates a WRef from a Register */
+ def apply(reg: DefRegister): WRef = new WRef(reg.name, reg.tpe, RegKind, UNKNOWNGENDER)
+}
case class WSubField(exp: Expression, name: String, tpe: Type, gender: Gender) extends Expression {
def serialize: String = s"${exp.serialize}.$name"
def mapExpr(f: Expression => Expression): Expression = this.copy(exp = f(exp))
@@ -77,15 +83,21 @@ case class WDefInstance(info: Info, name: String, module: String, tpe: Type) ext
def mapType(f: Type => Type): Statement = this.copy(tpe = f(tpe))
def mapString(f: String => String): Statement = this.copy(name = f(name))
}
-case class WDefInstanceConnector(info: Info, name: String, module: String, tpe: Type, exprs: Seq[Expression]) extends Statement with IsDeclaration {
- def serialize: String = s"inst $name of $module with ${tpe.serialize} connected to (" + exprs.map(_.serialize).mkString(", ") + ")" + info.serialize
- def mapExpr(f: Expression => Expression): Statement = this.copy(exprs = exprs map f)
+case class WDefInstanceConnector(
+ info: Info,
+ name: String,
+ module: String,
+ tpe: Type,
+ portCons: Seq[(Expression, Expression)]) extends Statement with IsDeclaration {
+ def serialize: String = s"inst $name of $module with ${tpe.serialize} connected to " +
+ portCons.map(_._2.serialize).mkString("(", ", ", ")") + info.serialize
+ def mapExpr(f: Expression => Expression): Statement =
+ this.copy(portCons = portCons map { case (e1, e2) => (f(e1), f(e2)) })
def mapStmt(f: Statement => Statement): Statement = this
def mapType(f: Type => Type): Statement = this.copy(tpe = f(tpe))
def mapString(f: String => String): Statement = this.copy(name = f(name))
}
-
// Resultant width is the same as the maximum input width
case object Addw extends PrimOp { override def toString = "addw" }
// Resultant width is the same as the maximum input width
diff --git a/src/main/scala/firrtl/ir/IR.scala b/src/main/scala/firrtl/ir/IR.scala
index ca518a5a..71ad9366 100644
--- a/src/main/scala/firrtl/ir/IR.scala
+++ b/src/main/scala/firrtl/ir/IR.scala
@@ -248,10 +248,10 @@ case class IsInvalid(info: Info, expr: Expression) extends Statement with HasInf
def mapType(f: Type => Type): Statement = this
def mapString(f: String => String): Statement = this
}
-case class Attach(info: Info, source: Expression, exprs: Seq[Expression]) extends Statement with HasInfo {
- def serialize: String = "attach " + source.serialize + " to (" + exprs.map(_.serialize).mkString(", ") + ")"
+case class Attach(info: Info, exprs: Seq[Expression]) extends Statement with HasInfo {
+ def serialize: String = "attach " + exprs.map(_.serialize).mkString("(", ", ", ")")
def mapStmt(f: Statement => Statement): Statement = this
- def mapExpr(f: Expression => Expression): Statement = Attach(info, f(source), exprs map f)
+ def mapExpr(f: Expression => Expression): Statement = Attach(info, exprs map f)
def mapType(f: Type => Type): Statement = this
def mapString(f: String => String): Statement = this
}
diff --git a/src/main/scala/firrtl/passes/CheckWidths.scala b/src/main/scala/firrtl/passes/CheckWidths.scala
index ab07e830..f9166a6f 100644
--- a/src/main/scala/firrtl/passes/CheckWidths.scala
+++ b/src/main/scala/firrtl/passes/CheckWidths.scala
@@ -87,10 +87,10 @@ object CheckWidths extends Pass {
def check_width_s(minfo: Info, mname: String)(s: Statement): Statement = {
val info = get_info(s) match { case NoInfo => minfo case x => x }
s map check_width_e(info, mname) map check_width_s(info, mname) map check_width_t(info, mname) match {
- case Attach(infox, source, exprs) =>
- exprs foreach ( e =>
- if (bitWidth(e.tpe) != bitWidth(source.tpe))
- errors append new AttachWidthsNotEqual(infox, mname, e.serialize, source.serialize)
+ case Attach(infox, exprs) =>
+ exprs.tail.foreach ( e =>
+ if (bitWidth(e.tpe) != bitWidth(exprs.head.tpe))
+ errors.append(new AttachWidthsNotEqual(infox, mname, e.serialize, exprs.head.serialize))
)
s
case _ => s
diff --git a/src/main/scala/firrtl/passes/Checks.scala b/src/main/scala/firrtl/passes/Checks.scala
index 9be92f85..03d6a98c 100644
--- a/src/main/scala/firrtl/passes/Checks.scala
+++ b/src/main/scala/firrtl/passes/Checks.scala
@@ -258,10 +258,8 @@ object CheckTypes extends Pass {
s"$info: [module $mname] A validif condition must be of type UInt.")
class IllegalAnalogDeclaration(info: Info, mname: String, decName: String) extends PassException(
s"$info: [module $mname] Cannot declare a reg, node, or memory with an Analog type: $decName.")
- class IllegalAttachSource(info: Info, mname: String, sourceName: String) extends PassException(
- s"$info: [module $mname] Attach source must be a wire or port with an analog type: $sourceName.")
class IllegalAttachExp(info: Info, mname: String, expName: String) extends PassException(
- s"$info: [module $mname] Attach expression must be an instance: $expName.")
+ s"$info: [module $mname] Attach expression must be an port, wire, or port of instance: $expName.")
//;---------------- Helper Functions --------------
def ut: UIntType = UIntType(UnknownWidth)
@@ -431,18 +429,14 @@ object CheckTypes extends Pass {
case t =>
}
case sx: Attach =>
- (sx.source.tpe, kind(sx.source)) match {
- case (AnalogType(w), PortKind | WireKind) =>
- case _ => errors append new IllegalAttachSource(info, mname, sx.source.serialize)
- }
- sx.exprs foreach { e =>
+ for (e <- sx.exprs) {
e.tpe match {
case _: AnalogType =>
- case _ => errors append new OpNotAnalog(info, mname, e.serialize)
+ case _ => errors.append(new OpNotAnalog(info, mname, e.serialize))
}
kind(e) match {
- case InstanceKind =>
- case _ => errors append new IllegalAttachExp(info, mname, e.serialize)
+ case (InstanceKind | PortKind | WireKind) =>
+ case _ => errors.append(new IllegalAttachExp(info, mname, e.serialize))
}
}
case sx: Stop =>
diff --git a/src/main/scala/firrtl/passes/ExpandWhens.scala b/src/main/scala/firrtl/passes/ExpandWhens.scala
index 4d02e192..a2845f43 100644
--- a/src/main/scala/firrtl/passes/ExpandWhens.scala
+++ b/src/main/scala/firrtl/passes/ExpandWhens.scala
@@ -10,21 +10,27 @@ import firrtl.PrimOps._
import firrtl.WrappedExpression._
import annotation.tailrec
+import collection.mutable
+import collection.immutable.ListSet
/** Expand Whens
*
-* @note This pass does three things: remove last connect semantics,
-* remove conditional blocks, and eliminate concept of scoping.
+* This pass does the following things:
+* $ - Remove last connect semantics
+* $ - Remove conditional blocks
+* $ - Eliminate concept of scoping
+* $ - Consolidate attaches
+*
* @note Assumes bulk connects and isInvalids have been expanded
* @note Assumes all references are declared
*/
object ExpandWhens extends Pass {
def name = "Expand Whens"
- type NodeMap = collection.mutable.HashMap[MemoizedHash[Expression], String]
- type Netlist = collection.mutable.LinkedHashMap[WrappedExpression, Expression]
- type Simlist = collection.mutable.ArrayBuffer[Statement]
- type Attachlist = collection.mutable.ArrayBuffer[Statement]
- type Defaults = Seq[collection.mutable.Map[WrappedExpression, Expression]]
+ type NodeMap = mutable.HashMap[MemoizedHash[Expression], String]
+ type Netlist = mutable.LinkedHashMap[WrappedExpression, Expression]
+ type Simlist = mutable.ArrayBuffer[Statement]
+ // Defaults ideally would be immutable.Map but conversion from mutable.LinkedHashMap to mutable.Map is VERY slow
+ type Defaults = Seq[mutable.Map[WrappedExpression, Expression]]
// ========== Expand When Utilz ==========
private def getFemaleRefs(n: String, t: Type, g: Gender): Seq[Expression] = {
@@ -45,6 +51,27 @@ object ExpandWhens extends Pass {
case (k, WInvalid) => IsInvalid(NoInfo, k.e1)
case (k, v) => Connect(NoInfo, k.e1, v)
}
+ /** Combines Attaches
+ * @todo Preserve Info
+ */
+ private def combineAttaches(attaches: Seq[Attach]): Seq[Attach] = {
+ // Helper type to add an ordering index to attached Expressions
+ case class AttachAcc(exprs: Seq[Expression], idx: Int)
+ // Map from every attached expression to its corresponding AttachAcc
+ // (many keys will point to same value)
+ val attachMap = mutable.HashMap.empty[WrappedExpression, AttachAcc]
+ for (Attach(_, exprs) <- attaches) {
+ val acc = exprs.map(attachMap.get(_)).flatten match {
+ case Seq() => // None of these expressions is present in the attachMap
+ AttachAcc(exprs, attachMap.size)
+ case accs => // At least one expression present in the attachMap
+ val sorted = accs sortBy (_.idx)
+ AttachAcc((sorted.map(_.exprs) :+ exprs).flatten.distinct, sorted.head.idx)
+ }
+ attachMap ++= acc.exprs.map(e => (we(e) -> acc))
+ }
+ attachMap.values.toList.distinct.map(acc => Attach(NoInfo, acc.exprs))
+ }
// Searches nested scopes of defaults for lvalue
// defaults uses mutable Map because we are searching LinkedHashMaps and conversion to immutable is VERY slow
@tailrec
@@ -65,12 +92,13 @@ object ExpandWhens extends Pass {
// ------------ Pass -------------------
def run(c: Circuit): Circuit = {
- def expandWhens(m: Module): (Netlist, Simlist, Statement) = {
+ def expandWhens(m: Module): (Netlist, Simlist, Seq[Attach], Statement) = {
val namespace = Namespace(m)
val simlist = new Simlist
val nodes = new NodeMap
+ // Seq of attaches in order
+ lazy val attaches = mutable.ArrayBuffer.empty[Attach]
- // defaults ideally would be immutable.Map but conversion from mutable.LinkedHashMap to mutable.Map is VERY slow
def expandWhens(netlist: Netlist,
defaults: Defaults,
p: Expression)
@@ -90,7 +118,9 @@ object ExpandWhens extends Pass {
case c: IsInvalid =>
netlist(c.expr) = WInvalid
EmptyStmt
- case c: Attach => c
+ case a: Attach =>
+ attaches += a
+ EmptyStmt
case sx: Conditionally =>
val conseqNetlist = new Netlist
val altNetlist = new Netlist
@@ -150,13 +180,15 @@ object ExpandWhens extends Pass {
netlist ++= (m.ports flatMap { case Port(_, name, dir, tpe) =>
getFemaleRefs(name, tpe, to_gender(dir)) map (ref => we(ref) -> WVoid)
})
- (netlist, simlist, expandWhens(netlist, Seq(netlist), one)(m.body))
+ val bodyx = expandWhens(netlist, Seq(netlist), one)(m.body)
+ (netlist, simlist, attaches, bodyx)
}
val modulesx = c.modules map {
case m: ExtModule => m
case m: Module =>
- val (netlist, simlist, bodyx) = expandWhens(m)
- val newBody = Block(Seq(squashEmpty(bodyx)) ++ expandNetlist(netlist) ++ simlist)
+ val (netlist, simlist, attaches, bodyx) = expandWhens(m)
+ val newBody = Block(Seq(squashEmpty(bodyx)) ++ expandNetlist(netlist) ++
+ combineAttaches(attaches) ++ simlist)
Module(m.info, m.name, m.ports, newBody)
}
Circuit(c.info, modulesx, c.main)
diff --git a/src/main/scala/firrtl/passes/InferWidths.scala b/src/main/scala/firrtl/passes/InferWidths.scala
index 397a5d91..f3f1e945 100644
--- a/src/main/scala/firrtl/passes/InferWidths.scala
+++ b/src/main/scala/firrtl/passes/InferWidths.scala
@@ -288,8 +288,10 @@ object InferWidths extends Pass {
case (s:Conditionally) => v ++=
get_constraints_t(s.pred.tpe, UIntType(IntWidth(1))) ++
get_constraints_t(UIntType(IntWidth(1)), s.pred.tpe)
- case (s: Attach) =>
- v += WGeq(getWidth(s.source), MaxWidth(s.exprs map (e => getWidth(e.tpe))))
+ case Attach(_, exprs) =>
+ // All widths must be equal
+ val widths = exprs map (e => getWidth(e.tpe))
+ v ++= widths.tail map (WGeq(widths.head, _))
case _ =>
}
s map get_constraints_e map get_constraints_s
diff --git a/src/main/scala/firrtl/passes/Passes.scala b/src/main/scala/firrtl/passes/Passes.scala
index 0e91c642..2dc71364 100644
--- a/src/main/scala/firrtl/passes/Passes.scala
+++ b/src/main/scala/firrtl/passes/Passes.scala
@@ -10,6 +10,8 @@ import firrtl.Utils._
import firrtl.Mappers._
import firrtl.PrimOps._
+import scala.collection.mutable
+
trait Pass extends LazyLogging {
def name: String
def run(c: Circuit): Circuit
@@ -283,42 +285,85 @@ object VerilogRename extends Pass {
c copy (modules = c.modules map (_ map verilogRenameP map verilogRenameS))
}
-
+/** Makes changes to the Firrtl AST to make Verilog emission easier
+ *
+ * - For each instance, adds wires to connect to each port
+ * - Note that no Namespace is required because Uniquify ensures that there will be no
+ * collisions with the lowered names of instance ports
+ * - Also removes Attaches where a single Port OR Wire connects to 1 or more instance ports
+ * - These are expressed in the portCons of WDefInstConnectors
+ *
+ * @note The result of this pass is NOT legal Firrtl
+ */
object VerilogPrep extends Pass {
def name = "Verilog Prep"
- type InstAttaches = collection.mutable.HashMap[String, Expression]
- def run(c: Circuit): Circuit = {
- def buildS(attaches: InstAttaches)(s: Statement): Statement = s match {
- case Attach(_, source, exps) =>
- exps foreach { e => attaches(e.serialize) = source }
- s
- case _ => s map buildS(attaches)
+
+ type AttachSourceMap = Map[WrappedExpression, Expression]
+
+ // Finds attaches with only a single source (Port or Wire)
+ // - Creates a map of attached expressions to their source
+ // - Removes the Attach
+ private def collectAndRemoveAttach(m: DefModule): (DefModule, AttachSourceMap) = {
+ val sourceMap = mutable.HashMap.empty[WrappedExpression, Expression]
+ lazy val namespace = Namespace(m)
+
+ def onStmt(stmt: Statement): Statement = stmt map onStmt match {
+ case attach: Attach =>
+ val wires = attach.exprs groupBy kind
+ val sources = wires.getOrElse(PortKind, Seq.empty) ++ wires.getOrElse(WireKind, Seq.empty)
+ val instPorts = wires.getOrElse(InstanceKind, Seq.empty)
+ // Sanity check (Should be caught by CheckTypes)
+ assert(sources.size + instPorts.size == attach.exprs.size)
+
+ sources match {
+ case Seq() => // Zero sources, can add a wire to connect and remove
+ val name = namespace.newTemp
+ val wire = DefWire(NoInfo, name, instPorts.head.tpe)
+ val ref = WRef(wire)
+ for (inst <- instPorts) sourceMap(inst) = ref
+ wire // Replace the attach with new source wire definition
+ case Seq(source) => // One source can be removed
+ assert(!sourceMap.contains(source)) // should have been merged
+ for (inst <- instPorts) sourceMap(inst) = source
+ EmptyStmt
+ case moreThanOne =>
+ attach
+ }
+ case s => s
}
+
+ (m map onStmt, sourceMap.toMap)
+ }
+
+ def run(c: Circuit): Circuit = {
def lowerE(e: Expression): Expression = e match {
- case _: WRef|_: WSubField if kind(e) == InstanceKind =>
+ case (_: WRef | _: WSubField) if kind(e) == InstanceKind =>
WRef(LowerTypes.loweredName(e), e.tpe, kind(e), gender(e))
case _ => e map lowerE
}
- def lowerS(attaches: InstAttaches)(s: Statement): Statement = s match {
+
+ def lowerS(attachMap: AttachSourceMap)(s: Statement): Statement = s match {
case WDefInstance(info, name, module, tpe) =>
- val exps = create_exps(WRef(name, tpe, ExpKind, MALE))
- val wcon = WDefInstanceConnector(info, name, module, tpe, exps.map( e => e.tpe match {
- case AnalogType(w) => attaches(e.serialize)
- case _ => WRef(LowerTypes.loweredName(e), e.tpe, WireKind, MALE)
- }))
- val wires = exps.map ( e => e.tpe match {
- case AnalogType(w) => EmptyStmt
- case _ => DefWire(info, LowerTypes.loweredName(e), e.tpe)
- })
- Block(Seq(wcon) ++ wires)
- case Attach(info, source, exps) => EmptyStmt
- case _ => s map lowerS(attaches) map lowerE
+ val portRefs = create_exps(WRef(name, tpe, ExpKind, MALE))
+ val (portCons, wires) = portRefs.map { p =>
+ attachMap.get(p) match {
+ // If it has a source in attachMap use that
+ case Some(ref) => (p -> ref, None)
+ // If no source, create a wire corresponding to the port and connect it up
+ case None =>
+ val wire = DefWire(info, LowerTypes.loweredName(p), p.tpe)
+ (p -> WRef(wire), Some(wire))
+ }
+ }.unzip
+ val newInst = WDefInstanceConnector(info, name, module, tpe, portCons)
+ Block(wires.flatten :+ newInst)
+ case other => other map lowerS(attachMap) map lowerE
}
- def prepModule(m: DefModule): DefModule = {
- val attaches = new InstAttaches
- m map buildS(attaches)
- m map lowerS(attaches)
+
+ val modulesx = c.modules map { mod =>
+ val (modx, attachMap) = collectAndRemoveAttach(mod)
+ modx map lowerS(attachMap)
}
- c.copy(modules = c.modules.map(prepModule))
+ c.copy(modules = modulesx)
}
}
diff --git a/src/test/scala/firrtlTests/AttachSpec.scala b/src/test/scala/firrtlTests/AttachSpec.scala
index 2eba9b96..32dc00a0 100644
--- a/src/test/scala/firrtlTests/AttachSpec.scala
+++ b/src/test/scala/firrtlTests/AttachSpec.scala
@@ -11,7 +11,7 @@ import firrtl.ir.Circuit
import firrtl.passes._
import firrtl.Parser.IgnoreInfo
-class InoutVerilog extends FirrtlFlatSpec {
+class InoutVerilogSpec extends FirrtlFlatSpec {
private def executeTest(input: String, expected: Seq[String], compiler: Compiler) = {
val writer = new StringWriter()
compiler.compile(CircuitState(parse(input), ChirrtlForm), writer)
@@ -20,132 +20,175 @@ class InoutVerilog extends FirrtlFlatSpec {
lines should contain(e)
}
}
- "Circuit" should "attach a module input source" in {
+
+ behavior of "Analog"
+
+ it should "attach a module input source directly" in {
+ val compiler = new VerilogCompiler
+ val input =
+ """circuit Attaching :
+ | module Attaching :
+ | input an: Analog<3>
+ | inst a of A
+ | inst b of B
+ | attach (an, a.an1, b.an2)
+ | module A:
+ | input an1: Analog<3>
+ | module B:
+ | input an2: Analog<3> """.stripMargin
+ val check =
+ """module Attaching(
+ | inout [2:0] an
+ |);
+ | A a (
+ | .an1(an)
+ | );
+ | B b (
+ | .an2(an)
+ | );
+ |endmodule
+ |module A(
+ | inout [2:0] an1
+ |);
+ |endmodule
+ |module B(
+ | inout [2:0] an2
+ |);
+ |endmodule
+ |""".stripMargin.split("\n") map normalized
+ executeTest(input, check, compiler)
+ }
+
+ it should "attach two instances" in {
+ val compiler = new VerilogCompiler
+ val input =
+ """circuit Attaching :
+ | module Attaching :
+ | inst a of A
+ | inst b of B
+ | attach (a.an, b.an)
+ | module A:
+ | input an: Analog<3>
+ | module B:
+ | input an: Analog<3> """.stripMargin
+ val check =
+ """module Attaching(
+ |);
+ | wire [2:0] _GEN_0;
+ | A a (
+ | .an(_GEN_0)
+ | );
+ | A b (
+ | .an(_GEN_0)
+ | );
+ |endmodule
+ |module A(
+ | inout [2:0] an
+ |);
+ |endmodule
+ |""".stripMargin.split("\n") map normalized
+ executeTest(input, check, compiler)
+ }
+
+ it should "attach a wire source" in {
val compiler = new VerilogCompiler
val input =
- """circuit Attaching :
- | module Attaching :
- | input an: Analog<3>
+ """circuit Attaching :
+ | module Attaching :
+ | wire x: Analog
| inst a of A
- | inst b of B
- | attach an to (a.an1, b.an2)
- | module A:
- | input an1: Analog<3>
- | module B:
- | input an2: Analog<3> """.stripMargin
- val check =
+ | attach (x, a.an)
+ | module A:
+ | input an: Analog<3> """.stripMargin
+ val check =
"""module Attaching(
- | inout [2:0] an
|);
+ | wire [2:0] x;
| A a (
- | .an1(an)
- | );
- | B b (
- | .an2(an)
+ | .an(x)
| );
|endmodule
- |module A(
- | inout [2:0] an1
- |);
- |endmodule
- |module B(
- | inout [2:0] an2
- |);
- |endmodule
|""".stripMargin.split("\n") map normalized
- executeTest(input, check, compiler)
- }
+ executeTest(input, check, compiler)
+ }
- "Circuit" should "attach a module output source" in {
+ it should "attach multiple sources" in {
val compiler = new VerilogCompiler
val input =
- """circuit Attaching :
- | module Attaching :
- | output an: Analog<3>
- | inst a of A
- | inst b of B
- | attach an to (a.an1, b.an2)
- | module A:
- | input an1: Analog<3>
- | module B:
- | input an2: Analog<3> """.stripMargin
- val check =
+ """circuit Attaching :
+ | module Attaching :
+ | input a1 : Analog<3>
+ | input a2 : Analog<3>
+ | wire x: Analog<3>
+ | attach (x, a1, a2)""".stripMargin
+ val check =
"""module Attaching(
- | inout [2:0] an
- |);
- | A a (
- | .an1(an)
- | );
- | B b (
- | .an2(an)
- | );
- |endmodule
- |module A(
- | inout [2:0] an1
- |);
- |endmodule
- |module B(
- | inout [2:0] an2
+ | inout [2:0] a1,
+ | inout [2:0] a2
|);
+ | wire [2:0] x;
+ | `ifdef SYNTHESIS
+ | assign x = a1;
+ | assign a1 = x;
+ | assign x = a2;
+ | assign a2 = x;
+ | assign a1 = a2;
+ | assign a2 = a1;
+ | `elseif verilator
+ | `error "Verilator does not support alias and thus cannot arbirarily connect bidirectional wires and ports"
+ | `else
+ | alias x = a1 = a2;
+ | `endif
|endmodule
|""".stripMargin.split("\n") map normalized
- executeTest(input, check, compiler)
- }
-
- "Circuit" should "not attach an instance input source" in {
- val compiler = new VerilogCompiler
- val input =
- """circuit Attaching :
- | module Attaching :
- | inst a of A
- | inst b of B
- | attach a.an to (b.an)
- | module A:
- | input an: Analog<3>
- | module B:
- | input an: Analog<3> """.stripMargin
- intercept[CheckTypes.IllegalAttachSource] {
- executeTest(input, Seq.empty, compiler)
- }
- }
-
- "Circuit" should "attach an instance output source" in {
- val compiler = new VerilogCompiler
- val input =
- """circuit Attaching :
- | module Attaching :
- | inst a of A
- | inst b of B
- | attach b.an to (a.an)
- | module A:
- | input an: Analog<3>
- | module B:
- | input an: Analog<3> """.stripMargin
- intercept[CheckTypes.IllegalAttachSource] {
- executeTest(input, Seq.empty, compiler)
- }
+ executeTest(input, check, compiler)
}
- "Circuit" should "attach a wire source" in {
+ it should "preserve attach order" in {
val compiler = new VerilogCompiler
val input =
- """circuit Attaching :
- | module Attaching :
- | wire x: Analog
- | inst a of A
- | attach x to (a.an)
- | module A:
- | input an: Analog<3> """.stripMargin
- val check =
+ """circuit Attaching :
+ | module Attaching :
+ | input a : Analog<32>
+ | input b : Analog<32>
+ | input c : Analog<32>
+ | input d : Analog<32>
+ | attach (a, b)
+ | attach (c, b)
+ | attach (a, d)""".stripMargin
+ val check =
"""module Attaching(
+ | inout [31:0] a,
+ | inout [31:0] b,
+ | inout [31:0] c,
+ | inout [31:0] d
|);
- | wire [2:0] x;
- | A a (
- | .an(x)
- | );
+ | alias a = b = c = d;
|endmodule
|""".stripMargin.split("\n") map normalized
executeTest(input, check, compiler)
+
+ val input2 =
+ """circuit Attaching :
+ | module Attaching :
+ | input a : Analog<32>
+ | input b : Analog<32>
+ | input c : Analog<32>
+ | input d : Analog<32>
+ | attach (a, b)
+ | attach (c, d)
+ | attach (d, a)""".stripMargin
+ val check2 =
+ """module Attaching(
+ | inout [31:0] a,
+ | inout [31:0] b,
+ | inout [31:0] c,
+ | inout [31:0] d
+ |);
+ | alias a = b = c = d;
+ |endmodule
+ |""".stripMargin.split("\n") map normalized
+ executeTest(input2, check2, compiler)
}
}
@@ -211,7 +254,7 @@ class AttachAnalogSpec extends FirrtlFlatSpec {
"""circuit Unit :
| module Unit :
| input clock: Clock
- | mem m:
+ | mem m:
| data-type => Analog<2>
| depth => 4
| read-latency => 0
@@ -243,7 +286,7 @@ class AttachAnalogSpec extends FirrtlFlatSpec {
}
}
- "Attaching a non-analog source" should "not be ok" in {
+ "Attaching a non-analog expression" should "not be ok" in {
val passes = Seq(
ToWorkingIR,
CheckHighForm,
@@ -256,80 +299,12 @@ class AttachAnalogSpec extends FirrtlFlatSpec {
| input source: UInt<2>
| inst a of A
| inst b of B
- | attach source to (a.o, b.o)
- | extmodule A :
- | output o: Analog<2>
- | extmodule B:
- | input o: Analog<2>""".stripMargin
- intercept[CheckTypes.IllegalAttachSource] {
- passes.foldLeft(parse(input)) {
- (c: Circuit, p: Pass) => p.run(c)
- }
- }
- }
-
- "Attach instance analog male source" should "not be ok." in {
- val passes = Seq(
- ToWorkingIR,
- CheckHighForm,
- ResolveKinds,
- InferTypes,
- CheckTypes)
- val input =
- """circuit Unit :
- | module Unit :
- | inst a of A
- | inst b of B
- | attach a.o to (b.o)
+ | attach (source, a.o, b.o)
| extmodule A :
| output o: Analog<2>
| extmodule B:
| input o: Analog<2>""".stripMargin
- intercept[CheckTypes.IllegalAttachSource] {
- passes.foldLeft(parse(input)) {
- (c: Circuit, p: Pass) => p.run(c)
- }
- }
- }
-
- "Attach instance analog female source" should "not be ok." in {
- val passes = Seq(
- ToWorkingIR,
- CheckHighForm,
- ResolveKinds,
- InferTypes,
- CheckTypes)
- val input =
- """circuit Unit :
- | module Unit :
- | inst a of A
- | inst b of B
- | attach b.o to (a.o)
- | extmodule A :
- | output o: Analog<2>
- | extmodule B:
- | input o: Analog<2>""".stripMargin
- intercept[CheckTypes.IllegalAttachSource] {
- passes.foldLeft(parse(input)) {
- (c: Circuit, p: Pass) => p.run(c)
- }
- }
- }
-
- "Attach port analog expr" should "throw an exception" in {
- val passes = Seq(
- ToWorkingIR,
- CheckHighForm,
- ResolveKinds,
- InferTypes,
- CheckTypes)
- val input =
- """circuit Unit :
- | module Unit :
- | input i: Analog<2>
- | input j: Analog<2>
- | attach j to (i) """.stripMargin
- intercept[CheckTypes.IllegalAttachExp] {
+ intercept[CheckTypes.OpNotAnalog] {
passes.foldLeft(parse(input)) {
(c: Circuit, p: Pass) => p.run(c)
}
@@ -350,7 +325,7 @@ class AttachAnalogSpec extends FirrtlFlatSpec {
| module Unit :
| input i: Analog<3>
| inst a of A
- | attach i to (a.o)
+ | attach (i, a.o)
| extmodule A :
| output o: Analog<2> """.stripMargin
intercept[CheckWidths.AttachWidthsNotEqual] {
@@ -359,28 +334,4 @@ class AttachAnalogSpec extends FirrtlFlatSpec {
}
}
}
-
- //"Simple compound expressions" should "be split" in {
- // val passes = Seq(
- // ToWorkingIR,
- // ResolveKinds,
- // InferTypes,
- // ResolveGenders,
- // InferWidths,
- // SplitExpressions
- // )
- // val input =
- // """circuit Top :
- // | module Top :
- // | input a : UInt<32>
- // | input b : UInt<32>
- // | input d : UInt<32>
- // | output c : UInt<1>
- // | c <= geq(add(a, b),d)""".stripMargin
- // val check = Seq(
- // "node GEN_0 = add(a, b)",
- // "c <= geq(GEN_0, d)"
- // )
- // executeTest(input, check, passes)
- //}
}