package firrtl import com.typesafe.scalalogging.LazyLogging import java.nio.file.{Paths, Files} import java.io.Writer import java.io.Reader import scala.sys.process._ import scala.io.Source import Utils._ import firrtl.passes._ import WrappedExpression._ // Datastructures import scala.collection.mutable.LinkedHashMap import scala.collection.mutable.ArrayBuffer 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 VIndent() case class VRandom() object VerilogEmitter extends Emitter { val tab = " " val ran = VRandom() var w:Option[Writer] = None var mname = "" def wref (n:String,t:Type) = WRef(n,t,ExpKind(),UNKNOWNGENDER) def escape (s:String) : String = { val sx = ArrayBuffer[String]() //sx += '"'.toString var percent:Boolean = false for (c <- s) { if (c == '\n') sx += "\\n" else if (c == '"') sx += '\\'.toString + '"'.toString else { if((c == 'x') && percent) sx += "h" else sx += c.toString } percent = (c == '%') } //sx += '"'.toString sx.reduce(_ + _) } def remove_root (ex:Expression) : Expression = { (ex.as[WSubField].get.exp) match { case (e:WSubField) => remove_root(e) case (e:WRef) => WRef(ex.as[WSubField].get.name,tpe(ex),InstanceKind(),UNKNOWNGENDER) } } def not_empty (s:ArrayBuffer[_]) : Boolean = if (s.size == 0) false else true def rand_string (t:Type) : Seq[Any] = { val wx = ((long_BANG(t) + 31) / 32).toInt Seq("{",wx.toString,"{",ran,"}}") } def emit (x:Any) = emit2(x,0) def emit2 (x:Any, top:Int) : Unit = { def cast (e:Expression) : Any = { (tpe(e)) match { case (t:UIntType) => e case (t:SIntType) => Seq("$signed(",e,")") } } (x) match { case (e:Expression) => { (e) match { case (e:DoPrim) => emit2(op_stream(e), top + 1) case (e:Mux) => emit2(Seq(e.cond," ? ",cast(e.tval)," : ",cast(e.fval)),top + 1) case (e:ValidIf) => emit2(Seq(cast(e.value)),top + 1) case (e:WRef) => w.get.write(e.serialize()) case (e:WSubField) => w.get.write(lowered_name(e)) case (e:WSubAccess) => w.get.write(lowered_name(e.exp) + "[" + lowered_name(e.index) + "]") case (e:WSubIndex) => w.get.write(e.serialize()) case (_:UIntValue|_:SIntValue) => v_print(e) } } case (t:Type) => { (t) match { case (_:UIntType|_:SIntType) => val wx = long_BANG(t) - 1 if (wx > 0) w.get.write("[" + wx + ":0]") else w.get.write("") case (t:ClockType) => w.get.write("") case (t:VectorType) => emit2(t.tpe, top + 1) w.get.write("[" + (t.size - 1) + ":0]") case (t) => error("Shouldn't be here"); w.get.write(t.serialize()) } } case (p:Direction) => { p match { case INPUT => w.get.write("input") case OUTPUT => w.get.write("output") } } case (s:String) => w.get.write(s) case (i:Int) => w.get.write(i.toString) case (i:Long) => w.get.write(i.toString) case (t:VIndent) => w.get.write(" ") case (r:VRandom) => w.get.write("$random") case (s:Seq[Any]) => { s.foreach((x:Any) => emit2(x.as[Any].get, top + 1)) if (top == 0) w.get.write("\n") } } } //;------------- PASS ----------------- def v_print (e:Expression) = { e match { case (e:UIntValue) => { val str = e.value.toString(16) w.get.write(long_BANG(tpe(e)).toString + "'h" + str) } case (e:SIntValue) => { val str = e.value.toString(16) w.get.write(long_BANG(tpe(e)).toString + "'sh" + str) } } } def op_stream (doprim:DoPrim) : Seq[Any] = { def cast_if (e:Expression) : Any = { val signed = doprim.args.find(x => tpe(x).typeof[SIntType]) if (signed == None) e else tpe(e) match { case (t:SIntType) => Seq("$signed(",e,")") case (t: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 = { (tpe(e)) match { case (t:UIntType) => e case (t:SIntType) => Seq("$signed(",e,")") } } def a0 () : Expression = doprim.args(0) def a1 () : Expression = doprim.args(1) def a2 () : Expression = doprim.args(2) def c0 () : Int = doprim.consts(0).toInt def c1 () : Int = doprim.consts(1).toInt doprim.op match { case ADD_OP => Seq(cast_if(a0())," + ", cast_if(a1())) case ADDW_OP => Seq(cast_if(a0())," + ", cast_if(a1())) case SUB_OP => Seq(cast_if(a0())," - ", cast_if(a1())) case SUBW_OP => Seq(cast_if(a0())," - ", cast_if(a1())) case MUL_OP => Seq(cast_if(a0())," * ", cast_if(a1()) ) case DIV_OP => Seq(cast_if(a0())," / ", cast_if(a1()) ) case REM_OP => Seq(cast_if(a0())," % ", cast_if(a1()) ) case LESS_OP => Seq(cast_if(a0())," < ", cast_if(a1())) case LESS_EQ_OP => Seq(cast_if(a0())," <= ", cast_if(a1())) case GREATER_OP => Seq(cast_if(a0())," > ", cast_if(a1())) case GREATER_EQ_OP => Seq(cast_if(a0())," >= ", cast_if(a1())) case EQUAL_OP => Seq(cast_if(a0())," == ", cast_if(a1())) case NEQUAL_OP => Seq(cast_if(a0())," != ", cast_if(a1())) case PAD_OP => { val w = long_BANG(tpe(a0())) val diff = (c0() - w) if (w == 0) Seq(a0()) else doprim.tpe match { case (t:SIntType) => Seq("{{",diff,"{",a0(),"[",w - 1,"]}}, ",a0()," }") case (t) => Seq("{{",diff,"'d0 }, ",a0()," }") } } case AS_UINT_OP => Seq("$unsigned(",a0(),")") case AS_SINT_OP => Seq("$signed(",a0(),")") case AS_CLOCK_OP => Seq("$unsigned(",a0(),")") case DYN_SHIFT_LEFT_OP => Seq(cast(a0())," << ", a1()) case DYN_SHIFT_RIGHT_OP => { (doprim.tpe) match { case (t:SIntType) => Seq(cast(a0())," >>> ",a1()) case (t) => Seq(cast(a0())," >> ",a1()) } } case SHIFT_LEFT_OP => Seq(cast(a0())," << ",c0()) case SHIFT_RIGHT_OP => Seq(a0(),"[", long_BANG(tpe(a0())) - 1,":",c0(),"]") case NEG_OP => Seq("-{",cast(a0()),"}") case CONVERT_OP => { tpe(a0()) match { case (t:UIntType) => Seq("{1'b0,",cast(a0()),"}") case (t:SIntType) => Seq(cast(a0())) } } case NOT_OP => Seq("~ ",a0()) case AND_OP => Seq(cast_as(a0())," & ", cast_as(a1())) case OR_OP => Seq(cast_as(a0())," | ", cast_as(a1())) case XOR_OP => Seq(cast_as(a0())," ^ ", cast_as(a1())) case AND_REDUCE_OP => { val v = ArrayBuffer[Seq[Any]]() for (b <- 0 until long_BANG(doprim.tpe).toInt) { v += Seq(cast(a0()),"[",b,"]") } v.reduce(_ + " & " + _) } case OR_REDUCE_OP => { val v = ArrayBuffer[Seq[Any]]() for (b <- 0 until long_BANG(doprim.tpe).toInt) { v += Seq(cast(a0()),"[",b,"]") } v.reduce(_ + " | " + _) } case XOR_REDUCE_OP => { val v = ArrayBuffer[Seq[Any]]() for (b <- 0 until long_BANG(doprim.tpe).toInt) { v += Seq(cast(a0()),"[",b,"]") } v.reduce(_ + " ^ " + _) } case CONCAT_OP => Seq("{",cast(a0()),",",cast(a1()),"}") case BITS_SELECT_OP => { if (c0() == c1()) Seq(a0(),"[",c0(),"]") else Seq(a0(),"[",c0(),":",c1(),"]") } case HEAD_OP => { val w = long_BANG(tpe(a0())) val high = w - 1 val low = w - c0() Seq(a0(),"[",high,":",low,"]") } case TAIL_OP => { val w = long_BANG(tpe(a0())) val low = w - c0() - 1 Seq(a0(),"[",low,":",0,"]") } } } def emit_verilog (m:InModule) : Module = { mname = m.name val netlist = LinkedHashMap[WrappedExpression,Expression]() val simlist = ArrayBuffer[Stmt]() def build_netlist (s:Stmt) : Stmt = { s match { case (s:Connect) => netlist(s.loc) = s.exp case (s:IsInvalid) => { val n = firrtl_gensym_module(mname) val e = wref(n,tpe(s.exp)) netlist(s.exp) = e } case (s:Conditionally) => simlist += s case (s:DefNode) => { val e = WRef(s.name,get_type(s),NodeKind(),MALE) netlist(e) = s.value } case (s) => sMap(build_netlist,s) } s } val portdefs = ArrayBuffer[Seq[Any]]() val declares = ArrayBuffer[Seq[Any]]() val instdeclares = ArrayBuffer[Seq[Any]]() val assigns = ArrayBuffer[Seq[Any]]() val at_clock = 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 (t:VectorType) => declares += Seq(b," ",t.tpe," ",n," [0:",t.size - 1,"];") case (t) => declares += Seq(b," ",t," ",n,";") } } def assign (e:Expression,value:Expression) = assigns += Seq("assign ",e," = ",value,";") def update_and_reset (r:Expression,clk:Expression,reset:Expression,init:Expression) = { if (!at_clock.contains(clk)) { at_clock(clk) = ArrayBuffer[Seq[Any]]() } def add_update (e:Expression,tabs:String) : Unit = { e match { case (e:Mux) => { at_clock(clk) += Seq(tabs,"if(",e.cond,") begin") add_update(e.tval,tabs + tab) at_clock(clk) += Seq(tabs,"end else begin") add_update(e.fval,tabs + tab) at_clock(clk) += Seq(tabs,"end") } case (e) => { if (weq(e,r)) at_clock(clk) += Seq(tabs,";") else at_clock(clk) += Seq(tabs,r," <= ",e,";") } } } val tv = init val fv = netlist(r) add_update(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") } } def initialize (e:Expression) = initials += Seq(e," = ",rand_string(tpe(e)),";") def initialize_mem (n:String,i:Int,t:Type) = { initials += Seq("for (initvar = 0; initvar < ",i,"; initvar = initvar+1)") val index = WRef("initvar",UnknownType(),ExpKind(),UNKNOWNGENDER) initials += Seq(tab,WSubAccess(wref(n,t),index,UnknownType(),FEMALE), " = ",rand_string(t),";") } def instantiate (n:String,m:String,es:Seq[Expression]) = { instdeclares += Seq(m," ",n," (") (es,0 until es.size).zipped.foreach{ (e,i) => { val s = Seq(tab,".",remove_root(e),"(",lowered_name(e),")") if (i != es.size - 1) instdeclares += Seq(s,",") else instdeclares += s }} instdeclares += Seq(");") for (e <- es) { declare("wire",lowered_name(e),tpe(e)) val ex = WRef(lowered_name(e),tpe(e),kind(e),gender(e)) if (gender(e) == FEMALE) { assign(ex,netlist(e)) } } } def simulate (clk:Expression,en:Expression,s:Seq[Any]) = { if (!at_clock.contains(clk)) at_clock(clk) = ArrayBuffer[Seq[Any]]() at_clock(clk) += Seq("`ifndef SYNTHESIS") at_clock(clk) += Seq(tab,"if(",en,") begin") at_clock(clk) += Seq(tab,tab,s) at_clock(clk) += Seq(tab,"end") at_clock(clk) += Seq("`endif") } def stop (ret:Int) : Seq[Any] = { Seq("$fdisplay(32'h80000002,\"",ret,"\");$finish;") } def printf (str:String,args:Seq[Expression]) : Seq[Any] = { val q = '"'.toString val strx = (Seq(q + escape(str) + q) ++ args.map(x => escape(x.serialize()))).reduce(_ + "," + _) Seq("$fwrite(32'h80000002,",strx,");") } def delay (e:Expression, n:Int, clk:Expression) : Expression = { var ex = e for (i <- 0 until n) { val name = firrtl_gensym_module(mname) declare("reg",name,tpe(e)) val exx = WRef(name,tpe(e),ExpKind(),UNKNOWNGENDER) update(exx,ex,clk,one) ex = exx } ex } def build_ports () = { (m.ports,0 until m.ports.size).zipped.foreach{(p,i) => { var end = ",\n" if (m.ports.size - 1 == i) end = "\n);\n" p.direction match { case INPUT => portdefs += Seq(p.direction," ",p.tpe," ",p.name) case OUTPUT => { portdefs += Seq(p.direction," ",p.tpe," ",p.name) val ex = WRef(p.name,p.tpe,PortKind(),FEMALE) assign(ex,netlist(ex)) } } }} if (m.ports.size == 0) w.get.write(");\n") } def build_streams (s:Stmt) : Stmt = { s match { case (s:Connect) => s case (s:DefWire) => declare("wire",s.name,s.tpe) val e = wref(s.name,s.tpe) assign(e,netlist(e)) case (s:DefRegister) => { declare("reg",s.name,s.tpe) val e = wref(s.name,s.tpe) update_and_reset(e,s.clock,s.reset,s.init) initialize(e) } case (s:IsInvalid) => { val wref = netlist(s.exp).as[WRef].get declare("reg",wref.name,tpe(s.exp)) initialize(wref) } case (s:DefPoison) => { val n = s.name val e = wref(n,s.tpe) declare("reg",n,tpe(e)) initialize(e) } case (s:DefNode) => { declare("wire",s.name,tpe(s.value)) assign(WRef(s.name,tpe(s.value),NodeKind(),MALE),s.value) } case (s:Stop) => simulate(s.clk,s.en,stop(s.ret)) case (s:Print) => simulate(s.clk,s.en,printf(s.string,s.args)) case (s:WDefInstance) => { val es = create_exps(WRef(s.name,s.tpe,InstanceKind(),MALE)) instantiate(s.name,s.module,es) } case (s:DefMemory) => { val mem = WRef(s.name,get_type(s),MemKind(s.readers ++ s.writers ++ s.readwriters),UNKNOWNGENDER) def mem_exp (p:String,f:String) = { val t1 = field_type(mem.tpe,p) val t2 = field_type(t1,f) val x = WSubField(mem,p,t1,UNKNOWNGENDER) WSubField(x,f,t2,UNKNOWNGENDER) } declare("reg",s.name,VectorType(s.data_type,s.depth)) initialize_mem(s.name,s.depth,s.data_type) for (r <- s.readers ) { val data = mem_exp(r,"data") val addr = mem_exp(r,"addr") val en = mem_exp(r,"en") val clk = mem_exp(r,"clk") declare("wire",lowered_name(data),tpe(data)) declare("wire",lowered_name(addr),tpe(addr)) declare("wire",lowered_name(en),tpe(en)) declare("wire",lowered_name(clk),tpe(clk)) //; Read port assign(addr,netlist(addr)) //;Connects value to m.r.addr assign(en,netlist(en)) //;Connects value to m.r.en assign(clk,netlist(clk)) //;Connects value to m.r.clk val addrx = delay(addr,s.read_latency,clk) val enx = delay(en,s.read_latency,clk) val mem_port = WSubAccess(mem,addrx,UnknownType(),UNKNOWNGENDER) assign(data,mem_port) } for (w <- s.writers ) { val data = mem_exp(w,"data") val addr = mem_exp(w,"addr") val mask = mem_exp(w,"mask") val en = mem_exp(w,"en") val clk = mem_exp(w,"clk") declare("wire",lowered_name(data),tpe(data)) declare("wire",lowered_name(addr),tpe(addr)) declare("wire",lowered_name(mask),tpe(mask)) declare("wire",lowered_name(en),tpe(en)) declare("wire",lowered_name(clk),tpe(clk)) //; Write port assign(data,netlist(data)) assign(addr,netlist(addr)) assign(mask,netlist(mask)) assign(en,netlist(en)) assign(clk,netlist(clk)) val datax = delay(data,s.write_latency - 1,clk) val addrx = delay(addr,s.write_latency - 1,clk) val maskx = delay(mask,s.write_latency - 1,clk) val enx = delay(en,s.write_latency - 1,clk) val mem_port = WSubAccess(mem,addrx,UnknownType(),UNKNOWNGENDER) update(mem_port,datax,clk,AND(enx,maskx)) } for (rw <- s.readwriters) { val wmode = mem_exp(rw,"wmode") val rdata = mem_exp(rw,"rdata") val data = mem_exp(rw,"data") val mask = mem_exp(rw,"mask") val addr = mem_exp(rw,"addr") val en = mem_exp(rw,"en") val clk = mem_exp(rw,"clk") declare("wire",lowered_name(wmode),tpe(wmode)) declare("wire",lowered_name(rdata),tpe(rdata)) declare("wire",lowered_name(data),tpe(data)) declare("wire",lowered_name(mask),tpe(mask)) declare("wire",lowered_name(addr),tpe(addr)) declare("wire",lowered_name(en),tpe(en)) declare("wire",lowered_name(clk),tpe(clk)) //; Assigned to lowered wires of each assign(clk,netlist(clk)) assign(addr,netlist(addr)) assign(data,netlist(data)) assign(addr,netlist(addr)) assign(mask,netlist(mask)) assign(en,netlist(en)) assign(wmode,netlist(wmode)) //; Delay new signals by latency val raddrx = delay(addr,s.read_latency,clk) val waddrx = delay(addr,s.write_latency - 1,clk) val enx = delay(en,s.write_latency - 1,clk) val rmodx = delay(wmode,s.write_latency - 1,clk) val datax = delay(data,s.write_latency - 1,clk) val maskx = delay(mask,s.write_latency - 1,clk) //; Write val rmem_port = WSubAccess(mem,raddrx,UnknownType(),UNKNOWNGENDER) assign(rdata,rmem_port) val wmem_port = WSubAccess(mem,waddrx,UnknownType(),UNKNOWNGENDER) update(wmem_port,datax,clk,AND(AND(enx,maskx),wmode)) } } case (s:Begin) => sMap(build_streams _,s) } s } def emit_streams () = { emit(Seq("module ",m.name,"(")) if (not_empty(portdefs)) { (portdefs,0 until portdefs.size).zipped.foreach{ (x,i) => { if (i != portdefs.size - 1) emit(Seq(tab,x,",")) else emit(Seq(tab,x)) }} } emit(Seq(");")) if (not_empty(declares)) { for (x <- declares) emit(Seq(tab,x)) } if (not_empty(instdeclares)) { for (x <- instdeclares) emit(Seq(tab,x)) } if (not_empty(assigns)) { for (x <- assigns) emit(Seq(tab,x)) } if (not_empty(initials)) { emit(Seq("`ifndef SYNTHESIS")) emit(Seq(" integer initvar;")) emit(Seq(" initial begin")) emit(Seq(" #0.002;")) for (x <- initials) { emit(Seq(tab,x)) } emit(Seq(" end")) emit(Seq("`endif")) } for (clk_stream <- at_clock) { if (not_empty(clk_stream._2)) { 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 run(c: Circuit, w: Writer) = { this.w = Some(w) for (m <- c.modules) { m match { case (m:InModule) => emit_verilog(m) case (m:ExModule) => false } } } //def run(c: Circuit, w: Writer) //{ // logger.debug(s"Verilog Emitter is not yet implemented in Scala") // val toStanza = Files.createTempFile(Paths.get(""), "verilog", ".fir") // val fromStanza = Files.createTempFile(Paths.get(""), "verilog", ".fir") // Files.write(toStanza, c.serialize.getBytes) // val cmd = Seq("firrtl-stanza", "-i", toStanza.toString, "-o", fromStanza.toString, "-b", "verilog") // logger.debug(cmd.mkString(" ")) // val ret = cmd.! // // Copy from Stanza output to user requested outputFile (we can't get filename from Writer) // Source.fromFile(fromStanza.toString) foreach { w.write(_) } // Files.delete(toStanza) // Files.delete(fromStanza) //} }