aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/firrtl/passes
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/firrtl/passes')
-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
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)
}
}