/* 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 firrtl import com.typesafe.scalalogging.LazyLogging import java.nio.file.{Paths, Files} import java.io.{Reader, Writer} import scala.collection.mutable import scala.sys.process._ import scala.io.Source import firrtl.ir._ import firrtl.passes._ import firrtl.Mappers._ import firrtl.PrimOps._ import firrtl.WrappedExpression._ import Utils._ import MemPortUtils.{memPortField, memType} // Datastructures import scala.collection.mutable.{ArrayBuffer, LinkedHashMap, HashSet} case class EmitterException(message: String) extends PassException(message) trait Emitter extends LazyLogging { def run(c: Circuit, w: Writer) } object FIRRTLEmitter extends Emitter { def run(c: Circuit, w: Writer) = w.write(c.serialize) } case class VRandom(width: BigInt) extends Expression { def tpe = UIntType(IntWidth(width)) def nWords = (width + 31) / 32 def realWidth = nWords * 32 def serialize: String = "RANDOM" def mapExpr(f: Expression => Expression): Expression = this 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 = { if (e1 == e2) e1.e1 else if ((e1 == we(zero)) | (e2 == we(zero))) zero else if (e1 == we(one)) e2.e1 else if (e2 == we(one)) e1.e1 else DoPrim(And, Seq(e1.e1, e2.e1), Nil, UIntType(IntWidth(1))) } def wref(n: String, t: Type) = WRef(n, t, ExpKind, UNKNOWNGENDER) def remove_root(ex: Expression): Expression = ex match { case ex: WSubField => ex.exp match { case (e: WSubField) => remove_root(e) case (_: WRef) => WRef(ex.name, ex.tpe, InstanceKind, UNKNOWNGENDER) } case _ => error("Shouldn't be here") } /** Turn Params into Verilog Strings */ def stringify(param: Param): String = param match { case IntParam(name, value) => s".$name($value)" case DoubleParam(name, value) => s".$name($value)" case StringParam(name, value) => val strx = "\"" + VerilogStringLitHandler.escape(value) + "\"" s".${name}($strx)" case RawStringParam(name, value) => s".$name($value)" } def emit(x: Any)(implicit w: Writer) { emit(x, 0) } def emit(x: Any, top: Int)(implicit w: Writer) { def cast(e: Expression): Any = e.tpe match { case (t: UIntType) => e case (t: SIntType) => Seq("$signed(",e,")") case ClockType => e case AnalogType(_) => e } x match { case (e: DoPrim) => emit(op_stream(e), top + 1) case (e: Mux) => emit(Seq(e.cond," ? ",cast(e.tval)," : ",cast(e.fval)),top + 1) case (e: ValidIf) => emit(Seq(cast(e.value)),top + 1) case (e: WRef) => w write e.serialize case (e: WSubField) => w write LowerTypes.loweredName(e) case (e: WSubAccess) => w write s"${LowerTypes.loweredName(e.exp)}[${LowerTypes.loweredName(e.index)}]" case (e: WSubIndex) => w write e.serialize case (e: Literal) => v_print(e) case (e: VRandom) => w write s"{${e.nWords}{$$random}}" case (t: UIntType) => val wx = bitWidth(t) - 1 if (wx > 0) w write s"[$wx:0]" case (t: SIntType) => 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]" case Input => w write "input" case Output => w write "output" case (s: String) => w write s case (i: Int) => w write i.toString case (i: Long) => w write i.toString case (i: BigInt) => w write i.toString case (s: Seq[Any]) => s foreach (emit(_, top + 1)) if (top == 0) w write "\n" case _ => error("Shouldn't be here") } } //;------------- PASS ----------------- def v_print(e: Expression)(implicit w: Writer) = e match { case UIntLiteral(value, IntWidth(width)) => w write s"$width'h${value.toString(16)}" case SIntLiteral(value, IntWidth(width)) => val unsignedValue = value + (if (value < 0) BigInt(1) << width.toInt else 0) w write s"$width'sh${unsignedValue.toString(16)}" } def op_stream(doprim: DoPrim): Seq[Any] = { def cast_if(e: Expression): Any = { doprim.args find (_.tpe match { case (_: SIntType) => true case (_) => false }) match { case None => e case Some(_) => e.tpe match { case (_: SIntType) => Seq("$signed(", e, ")") case (_: UIntType) => Seq("$signed({1'b0,", e, "})") } } } def cast(e: Expression): Any = doprim.tpe match { case (t: UIntType) => e case (t: SIntType) => Seq("$signed(",e,")") } def cast_as(e: Expression): Any = e.tpe match { case (t: UIntType) => e case (t: SIntType) => Seq("$signed(",e,")") } def a0: Expression = doprim.args.head def a1: Expression = doprim.args(1) def c0: Int = doprim.consts.head.toInt def c1: Int = doprim.consts(1).toInt def checkArgumentLegality(e: Expression) = e match { case _: UIntLiteral | _: SIntLiteral | _: WRef | _: WSubField => case _ => throw EmitterException(s"Can't emit ${e.getClass.getName} as PrimOp argument") } doprim.args foreach checkArgumentLegality doprim.op match { case Add => Seq(cast_if(a0), " + ", cast_if(a1)) case Addw => Seq(cast_if(a0), " + ", cast_if(a1)) case Sub => Seq(cast_if(a0), " - ", cast_if(a1)) case Subw => Seq(cast_if(a0), " - ", cast_if(a1)) case Mul => Seq(cast_if(a0), " * ", cast_if(a1)) case Div => Seq(cast_if(a0), " / ", cast_if(a1)) case Rem => Seq(cast_if(a0), " % ", cast_if(a1)) case Lt => Seq(cast_if(a0), " < ", cast_if(a1)) case Leq => Seq(cast_if(a0), " <= ", cast_if(a1)) case Gt => Seq(cast_if(a0), " > ", cast_if(a1)) case Geq => Seq(cast_if(a0), " >= ", cast_if(a1)) case Eq => Seq(cast_if(a0), " == ", cast_if(a1)) case Neq => Seq(cast_if(a0), " != ", cast_if(a1)) case Pad => val w = bitWidth(a0.tpe) val diff = c0 - w if (w == BigInt(0)) Seq(a0) else doprim.tpe match { // Either sign extend or zero extend. // If width == BigInt(1), don't extract bit case (_: SIntType) if w == BigInt(1) => Seq("{", c0, "{", a0, "}}") case (_: SIntType) => Seq("{{", diff, "{", a0, "[", w - 1, "]}},", a0, "}") case (_) => Seq("{{", diff, "'d0}, ", a0, "}") } case AsUInt => Seq("$unsigned(", a0, ")") case AsSInt => Seq("$signed(", a0, ")") case AsClock => Seq("$unsigned(", a0, ")") case Dshlw => Seq(cast(a0), " << ", a1) case Dshl => Seq(cast(a0), " << ", a1) case Dshr => doprim.tpe match { case (_: SIntType) => Seq(cast(a0)," >>> ", a1) case (_) => Seq(cast(a0), " >> ", a1) } case Shlw => Seq(cast(a0), " << ", c0) case Shl => Seq(cast(a0), " << ", c0) case Shr if c0 >= bitWidth(a0.tpe) => error("Verilog emitter does not support SHIFT_RIGHT >= arg width") case Shr => Seq(a0,"[", bitWidth(a0.tpe) - 1, ":", c0, "]") case Neg => Seq("-{", cast(a0), "}") case Cvt => a0.tpe match { case (_: UIntType) => Seq("{1'b0,", cast(a0), "}") case (_: SIntType) => Seq(cast(a0)) } case Not => Seq("~ ", a0) case And => Seq(cast_as(a0), " & ", cast_as(a1)) case Or => Seq(cast_as(a0), " | ", cast_as(a1)) case Xor => Seq(cast_as(a0), " ^ ", cast_as(a1)) case Andr => Seq("&", cast(a0)) case Orr => Seq("|", cast(a0)) case Xorr => Seq("^", cast(a0)) case Cat => Seq("{", cast(a0), ",", cast(a1), "}") // If selecting zeroth bit and single-bit wire, just emit the wire case Bits if c0 == 0 && c1 == 0 && bitWidth(a0.tpe) == BigInt(1) => Seq(a0) case Bits if c0 == c1 => Seq(a0, "[", c0, "]") case Bits => Seq(a0, "[", c0, ":", c1, "]") case Head => val w = bitWidth(a0.tpe) val high = w - 1 val low = w - c0 Seq(a0, "[", high, ":", low, "]") case Tail => val w = bitWidth(a0.tpe) val low = w - c0 - 1 Seq(a0, "[", low, ":", 0, "]") } } def emit_verilog(m: Module, moduleMap: Map[String, DefModule])(implicit w: Writer): DefModule = { val netlist = mutable.LinkedHashMap[WrappedExpression, Expression]() val addrRegs = mutable.HashSet[WrappedExpression]() val namespace = Namespace(m) def build_netlist(s: Statement): Statement = s map build_netlist match { case sx: Connect => netlist(sx.loc) = sx.expr (kind(sx.loc), kind(sx.expr)) match { case (MemKind, RegKind) => addrRegs += sx.expr case _ => } sx case sx: IsInvalid => netlist(sx.expr) = wref(namespace.newTemp, sx.expr.tpe) sx case sx: DefNode => val e = WRef(sx.name, sx.value.tpe, NodeKind, MALE) netlist(e) = sx.value sx case sx => sx } val portdefs = ArrayBuffer[Seq[Any]]() val declares = ArrayBuffer[Seq[Any]]() val instdeclares = ArrayBuffer[Seq[Any]]() val assigns = ArrayBuffer[Seq[Any]]() val at_clock = mutable.LinkedHashMap[Expression,ArrayBuffer[Seq[Any]]]() val initials = ArrayBuffer[Seq[Any]]() val simulates = ArrayBuffer[Seq[Any]]() def declare(b: String, n: String, t: Type) = t match { case tx: VectorType => declares += Seq(b, " ", tx.tpe, " ", n, " [0:", tx.size - 1, "];") case tx => declares += Seq(b, " ", tx, " ", n,";") } def assign(e: Expression, value: Expression) { assigns += Seq("assign ", e, " = ", value, ";") } // In simulation, assign garbage under a predicate def garbageAssign(e: Expression, syn: Expression, garbageCond: Expression) = { assigns += Seq("`ifndef RANDOMIZE_GARBAGE_ASSIGN") assigns += Seq("assign ", e, " = ", syn, ";") assigns += Seq("`else") assigns += Seq("assign ", e, " = ", garbageCond, " ? ", rand_string(syn.tpe), " : ", syn, ";") assigns += Seq("`endif") } def invalidAssign(e: Expression) = { assigns += Seq("`ifdef RANDOMIZE_INVALID_ASSIGN") assigns += Seq("assign ", e, " = ", rand_string(e.tpe), ";") assigns += Seq("`endif") } def update_and_reset(r: Expression, clk: Expression, reset: Expression, init: Expression) = { // We want to flatten Mux trees for reg updates into if-trees for // improved QoR for conditional updates. However, unbounded recursion // would take exponential time, so don't redundantly flatten the same // Mux more than a bounded number of times, preserving linear runtime. // The threshold is empirical but ample. val flattenThreshold = 4 val numTimesFlattened = collection.mutable.HashMap[Mux, Int]() def canFlatten(m: Mux) = { val n = numTimesFlattened.getOrElse(m, 0) numTimesFlattened(m) = n + 1 n < flattenThreshold } def addUpdate(e: Expression, tabs: String): Seq[Seq[Any]] = { if (weq(e, r)) Nil else netlist getOrElse (e, e) match { case m: Mux if canFlatten(m) => val ifStatement = Seq(tabs, "if (", m.cond, ") begin") val trueCase = // Don't generate mux trees for mem addr pipes if (addrRegs(r)) Seq(Seq(tabs + tab, r, " <= ", m.tval, ";")) else addUpdate(m.tval, tabs + tab) val elseStatement = Seq(tabs, "end else begin") val ifNotStatement = Seq(tabs, "if (!(", m.cond, ")) begin") val falseCase = addUpdate(m.fval, tabs + tab) val endStatement = Seq(tabs, "end") ((trueCase.nonEmpty, falseCase.nonEmpty): @ unchecked) match { case (true, true) => ifStatement +: trueCase ++: elseStatement +: falseCase :+ endStatement case (true, false) => ifStatement +: trueCase :+ endStatement case (false, true) => ifNotStatement +: falseCase :+ endStatement } case _ => Seq(Seq(tabs, r, " <= ", e, ";")) } } at_clock.getOrElseUpdate(clk, ArrayBuffer[Seq[Any]]()) ++= { val tv = init val fv = netlist(r) if (weq(tv, r)) addUpdate(fv, "") else addUpdate(Mux(reset, tv, fv, mux_type_and_widths(tv, fv)), "") } } def update(e: Expression, value: Expression, clk: Expression, en: Expression) { if (!at_clock.contains(clk)) at_clock(clk) = ArrayBuffer[Seq[Any]]() if (weq(en,one)) at_clock(clk) += Seq(e," <= ",value,";") else { at_clock(clk) += Seq("if(",en,") begin") at_clock(clk) += Seq(tab,e," <= ",value,";") at_clock(clk) += Seq("end") } } // Declares an intermediate wire to hold a large enough random number. // Then, return the correct number of bits selected from the random value def rand_string(t: Type) : Seq[Any] = { val nx = namespace.newTemp val rand = VRandom(bitWidth(t)) val tx = SIntType(IntWidth(rand.realWidth)) declare("reg",nx, tx) initials += Seq(wref(nx, tx), " = ", VRandom(bitWidth(t)), ";") Seq(nx, "[", bitWidth(t) - 1, ":0]") } def initialize(e: Expression) = { initials += Seq("`ifdef RANDOMIZE_REG_INIT") initials += Seq(e, " = ", rand_string(e.tpe), ";") initials += Seq("`endif") } def initialize_mem(s: DefMemory) { val index = wref("initvar", s.dataType) val rstring = rand_string(s.dataType) initials += Seq("`ifdef RANDOMIZE_MEM_INIT") initials += Seq("for (initvar = 0; initvar < ", s.depth, "; initvar = initvar+1)") initials += Seq(tab, WSubAccess(wref(s.name, s.dataType), index, s.dataType, FEMALE), " = ", rstring,";") initials += Seq("`endif") } 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") if (cond.nonEmpty) { at_clock(clk) += Seq(s"`ifdef ${cond.get}") at_clock(clk) += Seq(tab, s"if (`${cond.get}) begin") at_clock(clk) += Seq("`endif") } at_clock(clk) += Seq(tab,tab,"if (",en,") begin") at_clock(clk) += Seq(tab,tab,tab,s) at_clock(clk) += Seq(tab,tab,"end") if (cond.nonEmpty) { at_clock(clk) += Seq(s"`ifdef ${cond.get}") at_clock(clk) += Seq(tab,"end") at_clock(clk) += Seq("`endif") } at_clock(clk) += Seq("`endif") } def stop(ret: Int): Seq[Any] = Seq(if (ret == 0) "$finish;" else "$fatal;") def printf(str: StringLit, args: Seq[Expression]): Seq[Any] = { val q = '"'.toString val strx = s"""$q${VerilogStringLitHandler escape str}$q""" +: (args flatMap (Seq("," , _))) Seq("$fwrite(32'h80000002,", strx, ");") } def build_ports(): Unit = portdefs ++= m.ports.zipWithIndex map { 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) => val ex = WRef(p.name, p.tpe, PortKind, FEMALE) assign(ex, netlist(ex)) Seq(p.direction, " ", p.tpe, " ", p.name) } } def build_streams(s: Statement): Statement = s map build_streams match { case sx: DefWire => declare("wire",sx.name,sx.tpe) val e = wref(sx.name,sx.tpe) netlist get e match { case Some(n) => assign(e,n) case None => } sx case sx: DefRegister => declare("reg", sx.name, sx.tpe) val e = wref(sx.name, sx.tpe) update_and_reset(e, sx.clock, sx.reset, sx.init) initialize(e) sx case sx: IsInvalid => val wref = netlist(sx.expr) match { case e: WRef => e } declare("reg", wref.name, sx.expr.tpe) initialize(wref) sx case sx: DefNode => declare("wire", sx.name, sx.value.tpe) assign(WRef(sx.name, sx.value.tpe, NodeKind, MALE), sx.value) sx case sx: Stop => val errorString = StringLit(s"${sx.ret}\n".getBytes) simulate(sx.clk, sx.en, stop(sx.ret), Some("STOP_COND")) sx case sx: Print => simulate(sx.clk, sx.en, printf(sx.string, sx.args), Some("PRINTF_COND")) 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 } instdeclares += Seq(");") sx case sx: DefMemory => declare("reg", sx.name, VectorType(sx.dataType, sx.depth)) initialize_mem(sx) if (sx.readLatency != 0 || sx.writeLatency != 1) throw EmitterException("All memories should be transformed into " + "blackboxes or combinational by previous passses") for (r <- sx.readers) { val data = memPortField(sx, r, "data") val addr = memPortField(sx, r, "addr") val en = memPortField(sx, r, "en") // Ports should share an always@posedge, so can't have intermediary wire val clk = netlist(memPortField(sx, r, "clk")) declare("wire", LowerTypes.loweredName(data), data.tpe) declare("wire", LowerTypes.loweredName(addr), addr.tpe) // declare("wire", LowerTypes.loweredName(en), en.tpe) //; Read port assign(addr, netlist(addr)) //;Connects value to m.r.addr // assign(en, netlist(en)) //;Connects value to m.r.en val mem = WRef(sx.name, memType(sx), MemKind, UNKNOWNGENDER) val memPort = WSubAccess(mem, addr, sx.dataType, UNKNOWNGENDER) val depthValue = UIntLiteral(sx.depth, IntWidth(BigInt(sx.depth).bitLength)) val garbageGuard = DoPrim(Geq, Seq(addr, depthValue), Seq(), UnknownType) if ((sx.depth & (sx.depth - 1)) == 0) assign(data, memPort) else garbageAssign(data, memPort, garbageGuard) } for (w <- sx.writers) { val data = memPortField(sx, w, "data") val addr = memPortField(sx, w, "addr") val mask = memPortField(sx, w, "mask") val en = memPortField(sx, w, "en") //Ports should share an always@posedge, so can't have intermediary wire val clk = netlist(memPortField(sx, w, "clk")) declare("wire", LowerTypes.loweredName(data), data.tpe) declare("wire", LowerTypes.loweredName(addr), addr.tpe) declare("wire", LowerTypes.loweredName(mask), mask.tpe) declare("wire", LowerTypes.loweredName(en), en.tpe) //; Write port assign(data, netlist(data)) assign(addr, netlist(addr)) assign(mask, netlist(mask)) assign(en, netlist(en)) val mem = WRef(sx.name, memType(sx), MemKind, UNKNOWNGENDER) val memPort = WSubAccess(mem, addr, sx.dataType, UNKNOWNGENDER) update(memPort, data, clk, AND(en, mask)) } if (sx.readwriters.nonEmpty) throw EmitterException("All readwrite ports should be transformed into " + "read & write ports by previous passes") sx case sx => sx } def emit_streams() { emit(Seq("module ", m.name, "(")) for ((x, i) <- portdefs.zipWithIndex) { if (i != portdefs.size - 1) emit(Seq(tab, x, ",")) else emit(Seq(tab, x)) } emit(Seq(");")) if (declares.isEmpty && assigns.isEmpty) emit(Seq(tab, "always @(*) begin end")) for (x <- declares) emit(Seq(tab, x)) for (x <- instdeclares) emit(Seq(tab, x)) for (x <- assigns) emit(Seq(tab, x)) if (initials.nonEmpty) { emit(Seq("`ifdef RANDOMIZE")) emit(Seq(" integer initvar;")) emit(Seq(" initial begin")) // This enables test benches to set the random values at time 0.001, // then start the simulation later // Verilator does not support delay statements, so they are omitted. emit(Seq(" `ifndef verilator")) emit(Seq(" #0.002 begin end")) emit(Seq(" `endif")) for (x <- initials) emit(Seq(tab, x)) emit(Seq(" end")) emit(Seq("`endif")) } for (clk_stream <- at_clock if clk_stream._2.nonEmpty) { emit(Seq(tab, "always @(posedge ", clk_stream._1, ") begin")) for (x <- clk_stream._2) emit(Seq(tab, tab, x)) emit(Seq(tab, "end")) } emit(Seq("endmodule")) } build_netlist(m.body) build_ports() build_streams(m.body) emit_streams() m } def emit_preamble(implicit w: Writer) { emit(Seq( "`ifdef RANDOMIZE_GARBAGE_ASSIGN\n", "`define RANDOMIZE\n", "`endif\n", "`ifdef RANDOMIZE_INVALID_ASSIGN\n", "`define RANDOMIZE\n", "`endif\n", "`ifdef RANDOMIZE_REG_INIT\n", "`define RANDOMIZE\n", "`endif\n", "`ifdef RANDOMIZE_MEM_INIT\n", "`define RANDOMIZE\n", "`endif\n")) } def run(c: Circuit, w: Writer) = { emit_preamble(w) val moduleMap = (c.modules map (m => m.name -> m)).toMap c.modules foreach { case (m: Module) => emit_verilog(m, moduleMap)(w) case (m: ExtModule) => } } }