diff options
| -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) - //} } |
