diff options
| author | Jack Koenig | 2017-02-07 16:54:35 -0800 |
|---|---|---|
| committer | Adam Izraelevitz | 2017-02-07 16:54:35 -0800 |
| commit | 2f4ae9b4164cf415600de970b3ac5a1b9760efa5 (patch) | |
| tree | 6e131cd16157f921bb3ae83cb15caa0f53feb51a /src | |
| parent | 16238da2b50706e511f22f257402a3c2c009c004 (diff) | |
Rework Attach to work on arbitrary Analog hierarchies (#415)
* Rework Attach to work on arbitrary Analog hierarchies
If there are zero or one Analog sources in an Attach (source meaning
wire or parent module port), then the Attach will be emitted as a simple
point to point connection. In the general case, alias is used for
simulation while forwards and backwards assigns for synthesis. Verilator
does not currently support the general case so an `ifdef Verilator
`error is emitted.
* Add helper functions for creating WRef from Reg and Wire
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/antlr4/FIRRTL.g4 | 2 | ||||
| -rw-r--r-- | src/main/scala/firrtl/Emitter.scala | 33 | ||||
| -rw-r--r-- | src/main/scala/firrtl/Visitor.scala | 2 | ||||
| -rw-r--r-- | src/main/scala/firrtl/WIR.scala | 20 | ||||
| -rw-r--r-- | src/main/scala/firrtl/ir/IR.scala | 6 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/CheckWidths.scala | 8 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/Checks.scala | 16 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/ExpandWhens.scala | 58 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/InferWidths.scala | 6 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/Passes.scala | 99 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/AttachSpec.scala | 349 |
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) - //} } |
