diff options
Diffstat (limited to 'src/main/scala/firrtl/passes')
| -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 |
5 files changed, 130 insertions, 57 deletions
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) } } |
