defpackage firrtl/verilog : import core import verse import firrtl/ir-utils import firrtl/ir2 defn width! (w:Width) -> Long : match(w) : (w:IntWidth) : width(w) (w) : error("Non-supported width type.") defn width! (t:Type) -> Long : match(t) : (t:UIntType) : width!(width(t)) (t:SIntType) : width!(width(t)) (t) : error("Non-supported type.") defn emit (w:Width) -> String : match(w) : (w:IntWidth) : if width(w) >= to-long(1) : string-join $ ["[" width(w) - to-long(1) ":0]"] ;TODO check if need to special case 0 or 1 width wires else : "" (w) : error("Non-supported width type.") defn remove-subfield-s (s:Stmt) -> Stmt : defn remove-subfield-e (e:Expression) -> Expression : match(map(remove-subfield-e,e)) : (e:SubField) : Ref(to-symbol $ string-join $ [emit(exp(e)) bundle-expand-delin name(e)],type(e)) (e) : e map{remove-subfield-e,_ } $ map(remove-subfield-s,s) defn get-width (t:Type) -> String : match(t) : (t:UIntType) : emit(width(t)) (t:SIntType) : emit(width(t)) (t:ClockType) : emit(IntWidth(1)) (t) : error("Non-supported type.") defn get-name (e:Expression) -> Symbol : match(e) : (e:Ref) : name(e) (e:SubField) : error("Shouldn't be here") (e) : error("Shouldn't be here") defn escape (s:String) -> String : val s* = Vector() add(s*,"\"");" var percent = false for c in s do : if c == '\n' : add(s*,"\\n") else : if c == 'x' and percent : add(s*,"h") else : add(s*,to-string(c)) percent = c == '%' add(s*,"\"");" string-join(s*) ;============ Verilog Backend ============= defn emit-as-type (e:Expression,t:Type) -> String : match(t) : (t:UIntType) : emit(e) (t:SIntType) : string-join(["$signed(" emit(e) ")"]) defn emit-signed-if-any (e:Expression,ls:List) -> String : var signed? = false for x in ls do : if type(x) typeof SIntType : signed? = true if not signed? : emit(e) else : match(type(e)) : (t:SIntType) : string-join(["$signed(" emit(e) ")"]) (t:UIntType) : string-join(["$signed({1'b0," emit(e) "})"]) defn emit (e:Expression) -> String : match(e) : (e:Ref) : to-string $ name(e) (e:UIntValue) : val str = to-string(value(e)) val out = substring(str,1,length(str) - 1) string-join $ [width!(type(e)) "'" out] (e:SIntValue) : ;string-join $ [width!(type(e)) "'s" value(e)] val str = to-string(value(e)) val out = substring(str,1,length(str) - 1) string-join $ [width!(type(e)) "'s" out] (e:SubField) : error(string-join(["Non-supported expression: " to-string(e)])) (e:SubIndex) : error(string-join(["Non-supported expression: " to-string(e)])) (e:DoPrim) : ;val sargs = map(emit-as-type{_,type(e)},args(e)) ;val xargs = map(emit-signed-if-any{_,args(e)},args(e)) string-join $ switch {_ == op(e)} : ADD-OP : [emit-signed-if-any(args(e)[0],args(e)) " + " emit-signed-if-any(args(e)[1],args(e))] SUB-OP : [emit-signed-if-any(args(e)[0],args(e)) " - " emit-signed-if-any(args(e)[1],args(e))] MUL-OP : [emit-signed-if-any(args(e)[0],args(e)) " * " emit-signed-if-any(args(e)[1],args(e)) ] DIV-OP : [emit-signed-if-any(args(e)[0],args(e)) " / " emit-signed-if-any(args(e)[1],args(e)) ] MOD-OP : [emit-signed-if-any(args(e)[0],args(e)) " % " emit-signed-if-any(args(e)[1],args(e)) ] QUO-OP : [emit-signed-if-any(args(e)[0],args(e)) " / " emit-signed-if-any(args(e)[1],args(e)) ] REM-OP : [emit-signed-if-any(args(e)[0],args(e)) " % " emit-signed-if-any(args(e)[1],args(e)) ] ADD-WRAP-OP : [emit-signed-if-any(args(e)[0],args(e)), " + " emit-signed-if-any(args(e)[1],args(e))] SUB-WRAP-OP : [emit-signed-if-any(args(e)[0],args(e)), " - " emit-signed-if-any(args(e)[1],args(e))] LESS-OP : [emit-signed-if-any(args(e)[0],args(e)) " < " emit-signed-if-any(args(e)[1],args(e))] LESS-EQ-OP : [emit-signed-if-any(args(e)[0],args(e)) " <= " emit-signed-if-any(args(e)[1],args(e))] GREATER-OP : [emit-signed-if-any(args(e)[0],args(e)) " > " emit-signed-if-any(args(e)[1],args(e))] GREATER-EQ-OP : [emit-signed-if-any(args(e)[0],args(e)) " >= " emit-signed-if-any(args(e)[1],args(e))] NEQUIV-OP : [emit-signed-if-any(args(e)[0],args(e)) " != " emit-signed-if-any(args(e)[1],args(e))] EQUIV-OP : [emit-signed-if-any(args(e)[0],args(e)) " == " emit-signed-if-any(args(e)[1],args(e))] NEQUAL-OP : [emit-signed-if-any(args(e)[0],args(e)) " != " emit-signed-if-any(args(e)[1],args(e))] EQUAL-OP : [emit-signed-if-any(args(e)[0],args(e)) " == " emit-signed-if-any(args(e)[1],args(e))] MUX-OP : val en = emit(args(e)[0]) [en " ? " emit-as-type(args(e)[1],type(e)) " : " emit-as-type(args(e)[2],type(e))] PAD-OP : val x = args(e)[0] val w = width!(type(x)) val diff = (to-long(consts(e)[0]) - w) if w == to-long(0) : [ emit(x) ] else : if type(e) typeof SIntType : ["{{" diff "{" emit(x) "[" w - to-long(1) "]}}, " emit(x) " }"] else : ["{{" diff "'d0 }, " emit(x) " }"] AS-UINT-OP : ["$unsigned(" emit(args(e)[0]) ")"] AS-SINT-OP : ["$signed(" emit(args(e)[0]) ")"] DYN-SHIFT-LEFT-OP : [emit-as-type(args(e)[0],type(e)) " << " emit(args(e)[1])] DYN-SHIFT-RIGHT-OP : if type(e) typeof SIntType : [emit-as-type(args(e)[0],type(e)) " >>> " emit(args(e)[1])] else : [emit-as-type(args(e)[0],type(e)) " >> " emit(args(e)[1])] SHIFT-LEFT-OP : [emit-as-type(args(e)[0],type(e)) " << " consts(e)[0]] SHIFT-RIGHT-OP : [emit(args(e)[0]) "[" width!(type(args(e)[0])) - to-long(1) ":" consts(e)[0] "]"] ;if type(e) typeof SIntType : [emit-as-type(args(e)[0],type(e)) " >>> " consts(e)[0]] ;else : [emit-as-type(args(e)[0],type(e)) " >> " consts(e)[0]] NEG-OP : ["-{" emit-as-type(args(e)[0],type(e)) "}"] CONVERT-OP : match(type(args(e)[0])) : (t:UIntType) : ["{1'b0," emit-as-type(args(e)[0],type(e)) "}"] (t:SIntType) : [emit-as-type(args(e)[0],type(e))] BIT-NOT-OP : ["~ " emit-as-type(args(e)[0],type(e))] BIT-AND-OP : [emit-as-type(args(e)[0],type(e)) " & " emit-as-type(args(e)[1],type(e))] BIT-OR-OP : [emit-as-type(args(e)[0],type(e)) " | " emit-as-type(args(e)[1],type(e))] BIT-XOR-OP : [emit-as-type(args(e)[0],type(e)) " ^ " emit-as-type(args(e)[1],type(e))] CONCAT-OP : ["{" emit-as-type(args(e)[0],type(e)) "," emit-as-type(args(e)[1],type(e)) "}"] BIT-SELECT-OP : [emit(args(e)[0]) "[" consts(e)[0] "]"] BITS-SELECT-OP : [emit(args(e)[0]) "[" consts(e)[0] ":" consts(e)[1] "]"] BIT-AND-REDUCE-OP : var v = emit-as-type(args(e)[0],type(e)) for x in tail(args(e)) do : v = concat(v, [" & " emit(x)]) v BIT-OR-REDUCE-OP : var v = emit-as-type(args(e)[0],type(e)) for x in tail(args(e)) do : v = concat(v, [" | " emit(x)]) v BIT-XOR-REDUCE-OP : var v = emit-as-type(args(e)[0],type(e)) for x in tail(args(e)) do : v = concat(v, [" ^ " emit(x)]) v (e) : println(["Expression:" e]) error("Unknown expression") defn emit-module (m:InModule) : val body* = remove-subfield-s(body(m)) val vdecs = Vector>() ; all declarations in order, to preserve ordering val decs = HashTable(symbol-hash) ; all declarations, for fast lookups val cons = HashTable(symbol-hash) ; all connections val ens = HashTable(symbol-hash) ; all enables val simuls = HashTable>(symbol-hash) defn build-table (s:Stmt) -> False : match(s) : (s:DefWire|DefPoison|DefRegister|DefMemory|DefNode|DefInstance) : add(vdecs,name(s) => s) decs[name(s)] = s (s:Conditionally) : match(conseq(s)) : (c:Connect) : val n = get-name(loc(c)) ens[n] = pred(s) cons[n] = exp(conseq(s) as Connect) (c:PrintfStmt) : val my-clk-simuls = get?(simuls,get-name(clk(c)),Vector()) add(my-clk-simuls,["if(" emit(pred(s)) ") begin"]) add(my-clk-simuls,[" $fwrite(STDERR," string-join(List(escape(string(c)),map(emit,args(c))), ", ") ");"]) add(my-clk-simuls,["end"]) simuls[get-name(clk(c))] = my-clk-simuls (c:StopStmt) : val my-clk-simuls = get?(simuls,get-name(clk(c)),Vector()) add(my-clk-simuls,["if(" emit(pred(s)) ") begin"]) add(my-clk-simuls,[" $fdisplay(STDERR,\"" ret(c) "\");"]) add(my-clk-simuls,[" $finish;"]) add(my-clk-simuls,["end"]) simuls[get-name(clk(c))] = my-clk-simuls (s:PrintfStmt) : val my-clk-simuls = get?(simuls,get-name(clk(s)),Vector()) add(my-clk-simuls,["$fwrite(STDERR," string-join(List(escape(string(s)),map(emit,args(s))), ", ") ");"]) simuls[get-name(clk(s))] = my-clk-simuls (c:StopStmt) : val my-clk-simuls = get?(simuls,get-name(clk(c)),Vector()) add(my-clk-simuls,["$fdisplay(STDERR,\"" ret(c) "\");"]) add(my-clk-simuls,["$finish;"]) simuls[get-name(clk(c))] = my-clk-simuls (s:Connect) : val n = get-name(loc(s)) cons[n] = exp(s) (s:Begin) : do(build-table,s) (s) : false build-table(body*) val wires = Vector() val regs = Vector() val inits = Vector() val assigns = Vector() val updates = HashTable>(symbol-hash) val insts = HashTable(symbol-hash) ; inst -> module val inst-ports = HashTable>(symbol-hash) val sh = get-sym-hash(m) val rand-value = "$random" ;"0" defn rand-string (w:Long) -> String : string-join(["{" ((w + to-long(31)) / to-long(32)) "{" rand-value "}};"]) for x in vdecs do : val sym = key(x) match(value(x)) : (s:DefPoison) : add(regs,["reg " get-width(type(s)) " " sym ";"]) add(inits,[sym " = " rand-string(width!(type(s)))]) (s:DefWire) : add(wires,["wire " get-width(type(s)) " " sym ";"]) add(assigns,["assign " sym " = " emit(cons[sym]) ";"]) (s:DefRegister) : add(regs,["reg " get-width(type(s)) " " sym ";"]) val my-clk-update = get?(updates,get-name(clock(s)),Vector()) if key?(ens,sym) : add(my-clk-update,["if(" emit(ens[sym]) ") begin"]) val x = cons[sym] val y = emit(x) add(my-clk-update,[" " sym " <= " y ";"]) add(my-clk-update,["end"]) else : add(my-clk-update,[sym " <= " emit(cons[sym]) ";"]) updates[get-name(clock(s))] = my-clk-update val w = width!(type(s)) add(inits,[sym " = " rand-string(w)]) (s:DefMemory) : add(regs,["reg " get-width(type(s)) " " sym " [0:" size(s) - 1 "];"]) add(inits,["for (initvar = 0; initvar < " size(s) "; initvar = initvar+1)"]) add(inits,[" " sym "[initvar] = " rand-string(width!(type(s))) ]) (s:DefNode) : add(wires,["wire " get-width(type(value(s))) " " sym ";"]) add(assigns,["assign " sym " = " emit(value(s)) ";"]) (s:DefInstance) : inst-ports[sym] = Vector() insts[sym] = name(module(s) as Ref) for f in fields(type(module(s)) as BundleType) do : val n* = to-symbol $ string-join $ [sym bundle-expand-delin name(f)] add(wires,["wire " get-width(type(f)) " " n* ";"]) add(inst-ports[sym], ["." name(f) "( " n* " )"]) if flip(f) == REVERSE : add(assigns,["assign " n* " = " emit(cons[n*]) ";"]) ;(s:DefAccessor) : ; val mem-declaration = decs[name(source(s) as Ref)] as DefMemory ; switch {_ == acc-dir(s)} : ; READ : ; if seq?(mem-declaration) : ; val index* = Ref(firrtl-gensym(name(index(s) as Ref),sh),type(index(s))) ; add(regs,[ "reg " get-width(type(index*)) " " name(index*) ";"]) ; register index for an additional cycle ; val w = width!(type(index*)) ; add(inits,[name(index*) " = " rand-string(w)]) ; initialize registered index ; val my-clk-update = get?(updates,get-name(clock(mem-declaration)),Vector()) ; add(my-clk-update,[name(index*) " <= " emit(index(s)) ";"]) ; register index ; updates[get-name(clock(mem-declaration))] = my-clk-update ; ; emit read accessor ; add(wires,["wire " get-width(type(source(s))) " " sym ";"]) ; add(assigns,["assign " sym " = " emit(source(s)) "[" emit(index*) "];"]) ; else : ; ; emit read accessor ; add(wires,["wire " get-width(type(source(s))) " " sym ";"]) ; add(assigns,["assign " sym " = " emit(source(s)) "[" emit(index(s)) "];"]) ; WRITE : ; val my-clk-update = get?(updates,get-name(clock(mem-declaration)),Vector()) ; if key?(ens,sym) : ; add(my-clk-update,["if(" emit(ens[sym]) ") begin"]) ; add(my-clk-update,[" " emit(source(s)) "[" emit(index(s)) "] <= " emit(cons[sym]) ";"]) ; add(my-clk-update,["end"]) ; else : ; add(my-clk-update,[emit(source(s)) "[" emit(index(s)) "] <= " emit(cons[sym]) ";"]) ; updates[get-name(clock(mem-declaration))] = my-clk-update ; RDWR : ; if seq?(mem-declaration) : ; ; to make it sequential, register the index for an additional cycle ; val index* = Ref(firrtl-gensym(name(index(s) as Ref),sh),type(index(s))) ; add(regs,[ "reg " get-width(type(index*)) " " name(index*) ";"]) ; val w = width!(type(index*)) ; add(inits,[name(index*) " = " rand-string(w)]) ; val my-clk-update = get?(updates,get-name(clock(mem-declaration)),Vector()) ; add(my-clk-update,[name(index*) " <= " emit(index(s)) ";"]) ; updates[get-name(clock(mem-declaration))] = my-clk-update ; ; emit read accessor ; add(wires,["wire " get-width(type(source(s))) " " sym ";"]) ; add(assigns,["assign " sym " = " emit(source(s)) "[" emit(index*) "];"]) ; else : ; ; emit read accessor ; add(wires,["wire " get-width(type(source(s))) " " sym ";"]) ; add(assigns,["assign " sym " = " emit(source(s)) "[" emit(index(s)) "];"]) ; val my-clk-update = get?(updates,get-name(clock(mem-declaration)),Vector()) ; if key?(ens,sym) : ; add(my-clk-update,["if(" emit(ens[sym]) ") begin"]) ; add(my-clk-update,[" " emit(source(s)) "[" emit(index(s)) "] <= " emit(cons[sym]) ";"]) ; add(my-clk-update,["end"]) ; else : ; add(my-clk-update,[emit(source(s)) "[" emit(index(s)) "] <= " emit(cons[sym]) ";"]) ; updates[get-name(clock(mem-declaration))] = my-clk-update ;==== Actually printing module ===== val port-indent = " " print-all(["module " name(m) "(\n"]) for (p in ports(m),i in 1 to false) do : var end = ",\n" if length(ports(m)) == i : end = "\n);\n" switch {_ == direction(p)} : INPUT : print-all([port-indent "input " get-width(type(p)) " " name(p) end]) OUTPUT : print-all([port-indent "output " get-width(type(p)) " " name(p) end]) add(assigns,["assign " name(p) " = " emit(cons[name(p)]) ";"]) if length(ports(m)) == 0 : print(");\n") if length(simuls) != 0 : print-all([" integer STDERR = 32'h80000002;\n"]) for w in wires do : print(" ") println-all(w) for r in regs do : print(" ") println-all(r) if length(inits) != 0 : println("`ifndef SYNTHESIS") println(" integer initvar;") println(" initial begin") println(" #0.002;") for i in inits do : print-all(" ") println-all(i) println(" end") println("`endif") for a in assigns do : print(" ") println-all(a) for x in insts do : println-all([" " value(x) " " key(x) " ("]) ;print-all([".clk( clk )"]) for (y in inst-ports[key(x)],i in 1 to false) do : print(" ") print-all(y) if length(inst-ports[key(x)]) != i : print(",\n") println("\n );") for x in updates do : if length(value(x)) != 0 : println-all([" always @(posedge " key(x) ") begin"]) for u in value(x) do : print(" ") println-all(u) println(" end") for x in simuls do : if length(value(x)) != 0 : println("`ifndef SYNTHESIS") println-all([" always @(posedge " key(x) ") begin"]) for u in value(x) do : print(" ") println-all(u) println(" end") println("`endif") println("endmodule") public defn emit-verilog (with-output:(() -> False) -> False, c:Circuit) : with-output $ fn () : for m in modules(c) do : match(m) : (m:InModule) : emit-module(m) (m:ExModule) : false c