diff options
| author | Adam Izraelevitz | 2016-09-25 20:35:09 -0700 |
|---|---|---|
| committer | GitHub | 2016-09-25 20:35:09 -0700 |
| commit | e54fb610c6bf0a7fe5c9c0f0e0b3acbb3728cfd0 (patch) | |
| tree | 7c186c96c782f488a9ceea21abb3f60594bf98c7 | |
| parent | 7c4fa71a062f0c18a3af13c9e8853fdec2818da9 (diff) | |
Spec features added: AnalogType and Attach (#295)
* Spec features added: AnalogType and Attach
AnalogType(width: Width):
- Concrete syntax:
wire x: AnalogType<10>
- New groundtype, very restricted in use cases.
- Can only declare ports and wires with Analog type
- Analog types are never equivalent, thus if x and y have Analog
types: x <= y is never legal.
Attach(info: Info, source: Expression, exprs: Seq[Expression]):
- Concrete syntax:
attach x to (y, z)
- New statement
- Source can be any groundtyped expression (UInt, SInt, Analog, Clock)
- Exprs must have an Analog type reference an instance port
- Source and exprs must have identical widths
Included WDefInstanceConnector to enable emission of Verilog inout
Should be mostly feature complete.
Need to update spec if PR gets accepted.
* Fixed bug where invalidated ports aren't handled
* Bugfix for VerilogPrep
Intermediate wires for invalidated instance ports were not invalidated
* Bugfix: calling create_exp with name/tpe
Returns unknown gender, which was passing through
Caused temporary wire to not be declared
Because Verilog is dumb, undeclared wires are assumed to be 1bit signals
* Addressed donggyukim's style comments
* Reworked pass to only allow analog types in attach
Restrict source to be only wire or port kind
Much simpler implementation, almost identical functionality
Clearer semantics (i think?)
* Fixup bugs from pulling in new changes from master
* comments for type eqs and small style fixes
| -rw-r--r-- | src/main/antlr4/FIRRTL.g4 | 2 | ||||
| -rw-r--r-- | src/main/scala/firrtl/Emitter.scala | 47 | ||||
| -rw-r--r-- | src/main/scala/firrtl/LoweringCompilers.scala | 3 | ||||
| -rw-r--r-- | src/main/scala/firrtl/PrimOps.scala | 3 | ||||
| -rw-r--r-- | src/main/scala/firrtl/Utils.scala | 1 | ||||
| -rw-r--r-- | src/main/scala/firrtl/Visitor.scala | 3 | ||||
| -rw-r--r-- | src/main/scala/firrtl/WIR.scala | 13 | ||||
| -rw-r--r-- | src/main/scala/firrtl/ir/IR.scala | 11 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/Checks.scala | 66 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/ExpandWhens.scala | 11 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/InferWidths.scala | 14 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/MemUtils.scala | 3 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/Passes.scala | 40 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/Resolves.scala | 1 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/AttachSpec.scala | 413 |
15 files changed, 584 insertions, 47 deletions
diff --git a/src/main/antlr4/FIRRTL.g4 b/src/main/antlr4/FIRRTL.g4 index ef0fd7eb..1232b65f 100644 --- a/src/main/antlr4/FIRRTL.g4 +++ b/src/main/antlr4/FIRRTL.g4 @@ -79,6 +79,7 @@ type : 'UInt' ('<' IntLit '>')? | 'SInt' ('<' IntLit '>')? | 'Clock' + | 'Analog' ('<' IntLit '>')? | '{' field* '}' // Bundle | type '[' IntLit ']' // Vector ; @@ -119,6 +120,7 @@ stmt | 'stop(' exp exp IntLit ')' info? | 'printf(' exp exp StringLit ( exp)* ')' info? | 'skip' info? + | 'attach' exp 'to' '(' exp* ')' info? ; memField diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala index 0269a4fc..0a3068bc 100644 --- a/src/main/scala/firrtl/Emitter.scala +++ b/src/main/scala/firrtl/Emitter.scala @@ -64,6 +64,7 @@ case class VRandom(width: BigInt) extends Expression { def mapType(f: Type => Type): Expression = this def mapWidth(f: Width => Width): Expression = this } + class VerilogEmitter extends Emitter { val tab = " " def AND(e1: WrappedExpression, e2: WrappedExpression): Expression = { @@ -87,6 +88,7 @@ class VerilogEmitter extends Emitter { case (t: UIntType) => e case (t: SIntType) => Seq("$signed(",e,")") case ClockType => e + case AnalogType(w) => e } (x) match { case (e: DoPrim) => emit(op_stream(e), top + 1) @@ -106,6 +108,9 @@ class VerilogEmitter extends Emitter { val wx = bitWidth(t) - 1 if (wx > 0) w write s"[$wx:0]" case ClockType => + case t: AnalogType => + val wx = bitWidth(t) - 1 + if (wx > 0) w write s"[$wx:0]" case (t: VectorType) => emit(t.tpe, top + 1) w write s"[${t.size - 1}:0]" @@ -371,21 +376,6 @@ class VerilogEmitter extends Emitter { initials += Seq("`endif") } - def instantiate(n: String,m: String, es: Seq[Expression]) { - instdeclares += Seq(m, " ", n, " (") - es.zipWithIndex foreach {case (e, i) => - val s = Seq(tab, ".", remove_root(e), "(", LowerTypes.loweredName(e), ")") - if (i != es.size - 1) instdeclares += Seq(s, ",") - else instdeclares += s - } - instdeclares += Seq(");") - es foreach { e => - declare("wire",LowerTypes.loweredName(e), e.tpe) - val ex = WRef(LowerTypes.loweredName(e), e.tpe, kind(e), gender(e)) - if (gender(e) == FEMALE) assign(ex,netlist(e)) - } - } - def simulate(clk: Expression, en: Expression, s: Seq[Any], cond: Option[String]) { if (!at_clock.contains(clk)) at_clock(clk) = ArrayBuffer[Seq[Any]]() at_clock(clk) += Seq("`ifndef SYNTHESIS") @@ -415,10 +405,12 @@ class VerilogEmitter extends Emitter { } def build_ports(): Unit = portdefs ++= m.ports.zipWithIndex map { - case (p, i) => p.direction match { - case Input => + case (p, i) => (p.tpe, p.direction) match { + case (AnalogType(_), _) => + Seq("inout", " ", p.tpe, " ", p.name) + case (_, Input) => Seq(p.direction, " ", p.tpe, " ", p.name) - case Output => + case (_, Output) => val ex = WRef(p.name, p.tpe, PortKind, FEMALE) assign(ex, netlist(ex)) Seq(p.direction, " ", p.tpe, " ", p.name) @@ -427,9 +419,12 @@ class VerilogEmitter extends Emitter { def build_streams(s: Statement): Statement = s map build_streams match { case (s: DefWire) => - declare("wire", s.name, s.tpe) - val e = wref(s.name, s.tpe) - assign(e,netlist(e)) + declare("wire",s.name,s.tpe) + val e = wref(s.name,s.tpe) + netlist get e match { + case Some(n) => assign(e,n) + case None => + } s case (s: DefRegister) => declare("reg", s.name, s.tpe) @@ -453,9 +448,15 @@ class VerilogEmitter extends Emitter { case (s: Print) => simulate(s.clk, s.en, printf(s.string, s.args), Some("PRINTF_COND")) s - case (s: WDefInstance) => + case (s: WDefInstanceConnector) => val es = create_exps(WRef(s.name, s.tpe, InstanceKind, MALE)) - instantiate(s.name, s.module, es) + instdeclares += Seq(s.module, " ", s.name, " (") + (es zip s.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 + } + instdeclares += Seq(");") s case (s: DefMemory) => declare("reg", s.name, VectorType(s.dataType, s.depth)) diff --git a/src/main/scala/firrtl/LoweringCompilers.scala b/src/main/scala/firrtl/LoweringCompilers.scala index c7b7f5dd..eb44b4c2 100644 --- a/src/main/scala/firrtl/LoweringCompilers.scala +++ b/src/main/scala/firrtl/LoweringCompilers.scala @@ -150,7 +150,8 @@ class EmitVerilogFromLowFirrtl(val writer: Writer) extends Transform with Simple passes.SplitExpressions, passes.CommonSubexpressionElimination, passes.DeadCodeElimination, - passes.VerilogRename) + passes.VerilogRename, + passes.VerilogPrep) def execute(circuit: Circuit, annotationMap: AnnotationMap): TransformResult = { val result = run(circuit, passSeq) (new VerilogEmitter).run(result.circuit, writer) diff --git a/src/main/scala/firrtl/PrimOps.scala b/src/main/scala/firrtl/PrimOps.scala index a736dc3d..09cae392 100644 --- a/src/main/scala/firrtl/PrimOps.scala +++ b/src/main/scala/firrtl/PrimOps.scala @@ -226,18 +226,21 @@ object PrimOps extends LazyLogging { case _: UIntType => UIntType(w1) case _: SIntType => UIntType(w1) case ClockType => UIntType(IntWidth(1)) + case AnalogType(w) => UIntType(w1) case _ => UnknownType } case AsSInt => t1 match { case _: UIntType => SIntType(w1) case _: SIntType => SIntType(w1) case ClockType => SIntType(IntWidth(1)) + case _: AnalogType => SIntType(w1) case _ => UnknownType } case AsClock => t1 match { case _: UIntType => ClockType case _: SIntType => ClockType case ClockType => ClockType + case _: AnalogType => ClockType case _ => UnknownType } case Shl => t1 match { diff --git a/src/main/scala/firrtl/Utils.scala b/src/main/scala/firrtl/Utils.scala index 453e9a0c..d7a1e44d 100644 --- a/src/main/scala/firrtl/Utils.scala +++ b/src/main/scala/firrtl/Utils.scala @@ -240,6 +240,7 @@ object Utils extends LazyLogging { ilen + get_size(t1.tpe), jlen + get_size(t2.tpe)) })._1 case (ClockType, ClockType) => if (flip1 == flip2) Seq((0, 0)) else Nil + case (AnalogType(w1), AnalogType(w2)) => Nil case _ => error("shouldn't be here") } } diff --git a/src/main/scala/firrtl/Visitor.scala b/src/main/scala/firrtl/Visitor.scala index 00ef8f1b..b8850e53 100644 --- a/src/main/scala/firrtl/Visitor.scala +++ b/src/main/scala/firrtl/Visitor.scala @@ -137,6 +137,8 @@ class Visitor(infoMode: InfoMode) extends FIRRTLBaseVisitor[FirrtlNode] { case "SInt" => if (ctx.getChildCount > 1) SIntType(IntWidth(string2BigInt(ctx.IntLit.getText))) else SIntType(UnknownWidth) case "Clock" => ClockType + case "Analog" => if (ctx.getChildCount > 1) AnalogType(IntWidth(string2BigInt(ctx.IntLit.getText))) + else AnalogType(UnknownWidth) case "{" => BundleType(ctx.field.map(visitField)) } case typeContext: TypeContext => new VectorType(visitType(ctx.`type`), string2Int(ctx.IntLit.getText)) @@ -272,6 +274,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 "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 4ca75858..956d7b07 100644 --- a/src/main/scala/firrtl/WIR.scala +++ b/src/main/scala/firrtl/WIR.scala @@ -102,6 +102,14 @@ 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) + 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" } @@ -180,6 +188,11 @@ class WrappedType(val t: Type) { case (_: UIntType, _: UIntType) => true case (_: SIntType, _: SIntType) => true case (ClockType, ClockType) => true + // Analog totally skips out of the Firrtl type system. + // The only way Analog can play with another Analog component is through Attach. + // Ohterwise, we'd need to special case it during ExpandWhens, Lowering, + // ExpandConnects, etc. + case (_: AnalogType, _: AnalogType) => false case (t1: VectorType, t2: VectorType) => t1.size == t2.size && wt(t1.tpe) == wt(t2.tpe) case (t1: BundleType, t2: BundleType) => diff --git a/src/main/scala/firrtl/ir/IR.scala b/src/main/scala/firrtl/ir/IR.scala index 87072eec..35a81c14 100644 --- a/src/main/scala/firrtl/ir/IR.scala +++ b/src/main/scala/firrtl/ir/IR.scala @@ -250,6 +250,13 @@ 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(", ") + ")" + def mapStmt(f: Statement => Statement): Statement = this + def mapExpr(f: Expression => Expression): Statement = Attach(info, f(source), exprs map f) + def mapType(f: Type => Type): Statement = this + def mapString(f: String => String): Statement = this +} case class Stop(info: Info, ret: Int, clk: Expression, en: Expression) extends Statement with HasInfo { def serialize: String = s"stop(${clk.serialize}, ${en.serialize}, $ret)" + info.serialize def mapStmt(f: Statement => Statement): Statement = this @@ -388,6 +395,10 @@ case object ClockType extends GroundType { def serialize: String = "Clock" def mapWidth(f: Width => Width): Type = this } +case class AnalogType(width: Width) extends GroundType { + def serialize: String = "Analog" + width.serialize + def mapWidth(f: Width => Width): Type = AnalogType(f(width)) +} case object UnknownType extends Type { def serialize: String = "?" def mapType(f: Type => Type): Type = this diff --git a/src/main/scala/firrtl/passes/Checks.scala b/src/main/scala/firrtl/passes/Checks.scala index 03ea106c..e3c85edb 100644 --- a/src/main/scala/firrtl/passes/Checks.scala +++ b/src/main/scala/firrtl/passes/Checks.scala @@ -265,6 +265,8 @@ object CheckTypes extends Pass { s"$info: [module $mname] Primop $op requires all arguments to be UInt type.") class OpNotAllSameType(info: Info, mname: String, op: String) extends PassException( s"$info: [module $mname] Primop $op requires all operands to have the same type.") + class OpNotAnalog(info: Info, mname: String, exp: String) extends PassException( + s"$info: [module $mname] Attach requires all arguments to be Analog type: $exp.") class NodePassiveType(info: Info, mname: String) extends PassException( s"$info: [module $mname] Node must be a passive type.") class MuxSameType(info: Info, mname: String) extends PassException( @@ -277,6 +279,12 @@ object CheckTypes extends Pass { s"$info: [module $mname] Must validif a passive type.") class ValidIfCondUInt(info: Info, mname: String) extends PassException( 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.") //;---------------- Helper Functions -------------- def ut: UIntType = UIntType(UnknownWidth) @@ -291,13 +299,12 @@ object CheckTypes extends Pass { case (t: BundleType) => t.fields forall (x => x.flip == Default && passive(x.tpe)) case (t) => true } - def check_types_primop(info: Info, mname: String, e: DoPrim) { def all_same_type (ls:Seq[Expression]) { if (ls exists (x => wt(ls.head.tpe) != wt(e.tpe))) errors append new OpNotAllSameType(info, mname, e.op.serialize) } - def all_ground (ls: Seq[Expression]) { + def allInt(ls: Seq[Expression]) { if (ls exists (x => x.tpe match { case _: UIntType | _: SIntType => false case _ => true @@ -315,12 +322,11 @@ object CheckTypes extends Pass { case _ => true }) errors append new OpNotUInt(info, mname, e.op.serialize, x.serialize) } - e.op match { case AsUInt | AsSInt | AsClock => - case Dshl => is_uint(e.args(1)); all_ground(e.args) - case Dshr => is_uint(e.args(1)); all_ground(e.args) - case _ => all_ground(e.args) + case Dshl => is_uint(e.args(1)); allInt(e.args) + case Dshr => is_uint(e.args(1)); allInt(e.args) + case _ => allInt(e.args) } } @@ -377,6 +383,7 @@ object CheckTypes extends Pass { case (ClockType, ClockType) => flip1 == flip2 case (_: UIntType, _: UIntType) => flip1 == flip2 case (_: SIntType, _: SIntType) => flip1 == flip2 + case (_: AnalogType, _: AnalogType) => false case (t1: BundleType, t2: BundleType) => val t1_fields = (t1.fields foldLeft Map[String, (Type, Orientation)]())( (map, f1) => map + (f1.name -> (f1.tpe, f1.flip))) @@ -397,14 +404,35 @@ object CheckTypes extends Pass { s match { case (s: Connect) if wt(s.loc.tpe) != wt(s.expr.tpe) => errors append new InvalidConnect(info, mname, s.loc.serialize, s.expr.serialize) - case (s:PartialConnect) if !bulk_equals(s.loc.tpe, s.expr.tpe, Default, Default) => + case (s: PartialConnect) if !bulk_equals(s.loc.tpe, s.expr.tpe, Default, Default) => errors append new InvalidConnect(info, mname, s.loc.serialize, s.expr.serialize) - case (s: DefRegister) if wt(s.tpe) != wt(s.init.tpe) => - errors append new InvalidRegInit(info, mname) + case (s: DefRegister) => s.tpe match { + case AnalogType(w) => errors append new IllegalAnalogDeclaration(info, mname, s.name) + case t if (wt(s.tpe) != wt(s.init.tpe)) => errors append new InvalidRegInit(info, mname) + case t => + } case (s: Conditionally) if wt(s.pred.tpe) != wt(ut) => errors append new PredNotUInt(info, mname) - case (s: DefNode) if !passive(s.value.tpe) => - errors append new NodePassiveType(info, mname) + case (s: DefNode) => s.value.tpe match { + case AnalogType(w) => errors append new IllegalAnalogDeclaration(info, mname, s.name) + case t if !passive(s.value.tpe) => errors append new NodePassiveType(info, mname) + case t => + } + case (s: Attach) => + (s.source.tpe, kind(s.source)) match { + case (AnalogType(w), PortKind | WireKind) => + case _ => errors append new IllegalAttachSource(info, mname, s.source.serialize) + } + (s.exprs foreach) { e => + e.tpe match { + case _: AnalogType => + case _ => errors append new OpNotAnalog(info, mname, e.serialize) + } + kind(e) match { + case InstanceKind => + case _ => errors append new IllegalAttachExp(info, mname, e.serialize) + } + } case (s: Stop) => if (wt(s.clk.tpe) != wt(ClockType)) errors append new ReqClk(info, mname) if (wt(s.en.tpe) != wt(ut)) errors append new EnNotUInt(info, mname) @@ -413,6 +441,10 @@ object CheckTypes extends Pass { errors append new PrintfArgNotGround(info, mname) if (wt(s.clk.tpe) != wt(ClockType)) errors append new ReqClk(info, mname) if (wt(s.en.tpe) != wt(ut)) errors append new EnNotUInt(info, mname) + case (s: DefMemory) => s.dataType match { + case AnalogType(w) => errors append new IllegalAnalogDeclaration(info, mname, s.name) + case t => + } case _ => } s map check_types_e(info, mname) map check_types_s(info, mname) @@ -541,6 +573,8 @@ object CheckWidths extends Pass { s"$info: [module $mname] Parameter $n in head operator is larger than input width $width.") class TailWidthException(info: Info, mname: String, n: BigInt, width: BigInt) extends PassException( s"$info: [module $mname] Parameter $n in tail operator is larger than input width $width.") + class AttachWidthsNotEqual(info: Info, mname: String, eName: String, source: String) extends PassException( + s"$info: [module $mname] Attach source $source and expression $eName must have identical widths.") def run(c: Circuit): Circuit = { val errors = new Errors() @@ -581,7 +615,15 @@ 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) + s map check_width_e(info, mname) map check_width_s(info, mname) match { + case Attach(info, source, exprs) => + exprs foreach ( e => + if (bitWidth(e.tpe) != bitWidth(source.tpe)) + errors append new AttachWidthsNotEqual(info, mname, e.serialize, source.serialize) + ) + s + case _ => s + } } def check_width_p(minfo: Info, mname: String)(p: Port): Port = { diff --git a/src/main/scala/firrtl/passes/ExpandWhens.scala b/src/main/scala/firrtl/passes/ExpandWhens.scala index cd85c18a..f063e6eb 100644 --- a/src/main/scala/firrtl/passes/ExpandWhens.scala +++ b/src/main/scala/firrtl/passes/ExpandWhens.scala @@ -48,6 +48,7 @@ object ExpandWhens extends Pass { 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]] // ========== Expand When Utilz ========== @@ -55,9 +56,12 @@ object ExpandWhens extends Pass { def getGender(t: Type, i: Int, g: Gender): Gender = times(g, get_flip(t, i, Default)) val exps = create_exps(WRef(n, t, ExpKind, g)) (exps.zipWithIndex foldLeft Seq[Expression]()){ - case (expsx, (exp, j)) => getGender(t, j, g) match { - case (BIGENDER | FEMALE) => expsx :+ exp - case _ => expsx + case (expsx, (exp, j)) => exp.tpe match { + case AnalogType(w) => expsx + case _ => getGender(t, j, g) match { + case (BIGENDER | FEMALE) => expsx :+ exp + case _ => expsx + } } } } @@ -108,6 +112,7 @@ object ExpandWhens extends Pass { case c: IsInvalid => netlist(c.expr) = WInvalid EmptyStmt + case c: Attach => c case s: Conditionally => val conseqNetlist = new Netlist val altNetlist = new Netlist diff --git a/src/main/scala/firrtl/passes/InferWidths.scala b/src/main/scala/firrtl/passes/InferWidths.scala index 1a8cc343..bf1f53b4 100644 --- a/src/main/scala/firrtl/passes/InferWidths.scala +++ b/src/main/scala/firrtl/passes/InferWidths.scala @@ -253,6 +253,8 @@ object InferWidths extends Pass { WGeq(getWidth(s.pred), IntWidth(1)), WGeq(IntWidth(1), getWidth(s.pred)) ) + case (s: Attach) => + v += WGeq(getWidth(s.source), MaxWidth(s.exprs map (e => getWidth(e.tpe)))) case _ => } s map get_constraints_e map get_constraints_s @@ -260,13 +262,13 @@ object InferWidths extends Pass { c.modules foreach (_ map get_constraints_s) - //println-debug("======== ALL CONSTRAINTS ========") - //for x in v do : println-debug(x) - //println-debug("=================================") + //println("======== ALL CONSTRAINTS ========") + //for(x <- v) println(x) + //println("=================================") val h = solve_constraints(v) - //println-debug("======== SOLVED CONSTRAINTS ========") - //for x in h do : println-debug(x) - //println-debug("====================================") + //println("======== SOLVED CONSTRAINTS ========") + //for(x <- h) println(x) + //println("====================================") def evaluate(w: Width): Width = { def map2(a: Option[BigInt], b: Option[BigInt], f: (BigInt,BigInt) => BigInt): Option[BigInt] = diff --git a/src/main/scala/firrtl/passes/MemUtils.scala b/src/main/scala/firrtl/passes/MemUtils.scala index 7e2623c4..92673433 100644 --- a/src/main/scala/firrtl/passes/MemUtils.scala +++ b/src/main/scala/firrtl/passes/MemUtils.scala @@ -140,8 +140,7 @@ object createMask { def apply(dt: Type): Type = dt match { case t: VectorType => VectorType(apply(t.tpe), t.size) case t: BundleType => BundleType(t.fields map (f => f copy (tpe=apply(f.tpe)))) - case t: UIntType => BoolType - case t: SIntType => BoolType + case t: GroundType => BoolType } } diff --git a/src/main/scala/firrtl/passes/Passes.scala b/src/main/scala/firrtl/passes/Passes.scala index a182b2af..dc0eb2a0 100644 --- a/src/main/scala/firrtl/passes/Passes.scala +++ b/src/main/scala/firrtl/passes/Passes.scala @@ -308,3 +308,43 @@ object VerilogRename extends Pass { def run(c: Circuit): Circuit = c copy (modules = (c.modules map (_ map verilogRenameP map verilogRenameS))) } + + +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) + } + def lowerE(e: Expression): Expression = e match { + 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 { + 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 + } + def prepModule(m: DefModule): DefModule = { + val attaches = new InstAttaches + m map buildS(attaches) + m map lowerS(attaches) + } + c copy (modules = (c.modules map prepModule)) + } +} diff --git a/src/main/scala/firrtl/passes/Resolves.scala b/src/main/scala/firrtl/passes/Resolves.scala index 616e30f3..49df9ba6 100644 --- a/src/main/scala/firrtl/passes/Resolves.scala +++ b/src/main/scala/firrtl/passes/Resolves.scala @@ -87,6 +87,7 @@ object ResolveGenders extends Pass { } def resolve_s(s: Statement): Statement = s match { + //TODO(azidar): pretty sure don't need to do anything for Attach, but not positive... case IsInvalid(info, expr) => IsInvalid(info, resolve_e(FEMALE)(expr)) case Connect(info, loc, expr) => diff --git a/src/test/scala/firrtlTests/AttachSpec.scala b/src/test/scala/firrtlTests/AttachSpec.scala new file mode 100644 index 00000000..d1e07eae --- /dev/null +++ b/src/test/scala/firrtlTests/AttachSpec.scala @@ -0,0 +1,413 @@ +/* +Copyright (c) 2014 - 2016 The Regents of the University of +California (Regents). All Rights Reserved. Redistribution and use in +source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + two paragraphs of disclaimer in the documentation and/or other materials + provided with the distribution. + * Neither the name of the Regents nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. +IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF +REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF +ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION +TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +MODIFICATIONS. +*/ + +package firrtlTests + +import java.io._ +import org.scalatest._ +import org.scalatest.prop._ +import firrtl._ +import firrtl.Annotations._ +import firrtl.ir.Circuit +import firrtl.passes._ +import firrtl.Parser.IgnoreInfo + +class InoutVerilog extends FirrtlFlatSpec { + def parse (input:String) = Parser.parse(input.split("\n").toIterator, IgnoreInfo) + private def executeTest(input: String, expected: Seq[String], compiler: Compiler) = { + val writer = new StringWriter() + compiler.compile(parse(input), new AnnotationMap(Seq.empty), writer) + val lines = writer.toString().split("\n") map normalized + expected foreach { e => + lines should contain(e) + } + } + "Circuit" should "attach a module input source" 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 to (a.an, b.an) + | module A: + | input an: Analog<3> + | module B: + | input an: Analog<3> """.stripMargin + val check = + """module Attaching( + | inout [2:0] an + |); + | A a ( + | .an(an) + | ); + | B b ( + | .an(an) + | ); + |endmodule + |module A( + | inout [2:0] an + |); + |endmodule + |module B( + | inout [2:0] an + |); + |endmodule + |""".stripMargin.split("\n") map normalized + executeTest(input, check, compiler) + } + + "Circuit" should "attach a module output source" 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.an, b.an) + | module A: + | input an: Analog<3> + | module B: + | input an: Analog<3> """.stripMargin + val check = + """module Attaching( + | inout [2:0] an + |); + | A a ( + | .an(an) + | ); + | B b ( + | .an(an) + | ); + |endmodule + |module A( + | inout [2:0] an + |); + |endmodule + |module B( + | inout [2:0] an + |); + |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) + } + } + + "Circuit" should "attach a wire source" 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 = + """module Attaching( + |); + | wire [2:0] x; + | A a ( + | .an(x) + | ); + |endmodule + |""".stripMargin.split("\n") map normalized + executeTest(input, check, compiler) + } +} + +class AttachAnalogSpec extends FirrtlFlatSpec { + def parse (input:String) = Parser.parse(input.split("\n").toIterator, IgnoreInfo) + private def executeTest(input: String, expected: Seq[String], passes: Seq[Pass]) = { + val c = passes.foldLeft(Parser.parse(input.split("\n").toIterator)) { + (c: Circuit, p: Pass) => p.run(c) + } + val lines = c.serialize.split("\n") map normalized + + expected foreach { e => + lines should contain(e) + } + } + + "Connecting analog types" should "throw an exception" in { + val passes = Seq( + ToWorkingIR, + CheckHighForm, + ResolveKinds, + InferTypes, + CheckTypes) + val input = + """circuit Unit : + | module Unit : + | input y: Analog<1> + | output x: Analog<1> + | x <= y""".stripMargin + intercept[CheckTypes.InvalidConnect] { + passes.foldLeft(parse(input)) { + (c: Circuit, p: Pass) => p.run(c) + } + } + } + + "Declaring register with analog types" should "throw an exception" in { + val passes = Seq( + ToWorkingIR, + CheckHighForm, + ResolveKinds, + InferTypes, + CheckTypes) + val input = + """circuit Unit : + | module Unit : + | input clk: Clock + | reg r: Analog<2>, clk""".stripMargin + intercept[CheckTypes.IllegalAnalogDeclaration] { + passes.foldLeft(parse(input)) { + (c: Circuit, p: Pass) => p.run(c) + } + } + } + + "Declaring memory with analog types" should "throw an exception" in { + val passes = Seq( + ToWorkingIR, + CheckHighForm, + ResolveKinds, + InferTypes, + CheckTypes) + val input = + """circuit Unit : + | module Unit : + | input clk: Clock + | mem m: + | data-type => Analog<2> + | depth => 4 + | read-latency => 0 + | write-latency => 1 + | read-under-write => undefined""".stripMargin + intercept[CheckTypes.IllegalAnalogDeclaration] { + passes.foldLeft(parse(input)) { + (c: Circuit, p: Pass) => p.run(c) + } + } + } + + "Declaring node with analog types" should "throw an exception" in { + val passes = Seq( + ToWorkingIR, + CheckHighForm, + ResolveKinds, + InferTypes, + CheckTypes) + val input = + """circuit Unit : + | module Unit : + | input in: Analog<2> + | node n = in """.stripMargin + intercept[CheckTypes.IllegalAnalogDeclaration] { + passes.foldLeft(parse(input)) { + (c: Circuit, p: Pass) => p.run(c) + } + } + } + + "Attaching a non-analog source" should "not be ok" in { + val passes = Seq( + ToWorkingIR, + CheckHighForm, + ResolveKinds, + InferTypes, + CheckTypes) + val input = + """circuit Unit : + | module Unit : + | 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) + | 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] { + passes.foldLeft(parse(input)) { + (c: Circuit, p: Pass) => p.run(c) + } + } + } + + "Inequal attach widths" should "throw an exception" in { + val passes = Seq( + ToWorkingIR, + CheckHighForm, + ResolveKinds, + InferTypes, + CheckTypes, + InferWidths, + CheckWidths) + val input = + """circuit Unit : + | module Unit : + | input i: Analog<3> + | inst a of A + | attach i to (a.o) + | extmodule A : + | output o: Analog<2> """.stripMargin + intercept[CheckWidths.AttachWidthsNotEqual] { + passes.foldLeft(parse(input)) { + (c: Circuit, p: Pass) => p.run(c) + } + } + } + + //"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) + //} +} |
