GeneratorState struct #RefType { comp Compilation symbols Map typeMap Map constMap Map entities List errors List es EmulatorState inst ModuleInstance evalCtxOutput int evalCtxField int isNonIndexable bool out CodeBuilder first CodeBuilder assignments CodeBuilder regUpdates CodeBuilder globals Set } CodeBuilder struct #RefType { sb StringBuilder indent int lineSep string skipSep bool create() { return CodeBuilder { sb: new StringBuilder{} } } } NodeWithCtx struct { node Node ctxOutput int ctxField int hash(self NodeWithCtx) { return xor(xor(transmute(pointer_cast(self.node, pointer), uint), cast(self.ctxOutput, uint) << 25), cast(self.ctxField, uint)) } equals(a NodeWithCtx, b NodeWithCtx) { return a.node == b.node && a.ctxOutput == b.ctxOutput && a.ctxField == b.ctxField } } VerilogGenerator { comp(comp Compilation, es EmulatorState) { s := new GeneratorState { comp: comp, symbols: comp.symbols, typeMap: comp.typeMap, constMap: comp.constMap, entities: comp.entities, errors: comp.errors, es: es, evalCtxOutput: -1, evalCtxField: -1, out: new CodeBuilder.create(), globals: new Set.create(), } s.globals.add("module") s.globals.add("endmodule") s.globals.add("begin") s.globals.add("end") s.globals.add("if") s.globals.add("else") s.globals.add("posedge") s.globals.add("negedge") s.globals.add("wire") s.globals.add("reg") s.globals.add("input") s.globals.add("output") s.globals.add("inout") s.globals.add("case") s.globals.add("signed") s.globals.add("cell") for u in s.comp.units { for node in u.contents { if node.is(ModuleDef) { def := node.as(ModuleDef) if def.blackboxKeyword != null { s.globals.tryAdd(def.name.value) } } } } for mi in es.moduleInstances { if mi.def.blackboxKeyword == null { mi.genLocals = new Map.create() mi.genLocalsSet = new Set.create() name := mi.fullName != "" ? mi.fullName.replace(".", "_") : mi.def.name.value mi.genGlobalName = uniqueName(s, mi, s.globals, name) } else { mi.genGlobalName = mi.def.name.value } } for mi in es.moduleInstances { s.globals.tryRemove(mi.genGlobalName) } write(s, format("// Generated by Wyre compiler {}\n", compilerVersion)) for mi in es.moduleInstances { if mi.def.blackboxKeyword == null { module(s, mi) } } return s.out } module(s GeneratorState, inst ModuleInstance) { s.inst = inst def := inst.def for i := 1; i < inst.calls.count { mi := s.es.moduleInstances[inst.calls[i]] mi.genLocalName = uniqueName(s, s.inst, s.inst.genLocalsSet, mi.localName) } write(s, "\nmodule ") write(s, inst.genGlobalName) write(s, "(") indent(s, ",") for inp in def.inputs { if inp.flags & ModuleInputFlags.static == 0 { tag := s.typeMap.get(inp) if tag.kind == TagKind.number { input(s, inp, nameOf(s, s.inst, inp, -1, -1), tag) } else if tag.kind == TagKind.struct_ { sdef := s.entities[tag.q].as(StructDef) for f, fi in sdef.fields { input(s, inp, nameOf(s, s.inst, inp, -1, fi), s.typeMap.get(f)) } } else { abandon() } } } for o in def.outputs { tag := s.typeMap.get(o) if tag.kind == TagKind.number { output(s, o, nameOf(s, s.inst, o, -1, -1), tag) } else if tag.kind == TagKind.struct_ { sdef := s.entities[tag.q].as(StructDef) for f, fi in sdef.fields { s.evalCtxField = fi output(s, o, nameOf(s, s.inst, o, -1, fi), s.typeMap.get(f)) } s.evalCtxField = -1 } else { abandon() } } unindent(s) beginLine(s) write(s, ");\n") s.assignments = new CodeBuilder.create() s.regUpdates = new CodeBuilder.create() s.first = push(s, s.regUpdates) firstPos := s.first.sb.count block(s, def.body) if s.regUpdates.sb.count > 0 { beginLine(s) } for i := 1; i < inst.calls.count { callInst(s, s.es.moduleInstances[inst.calls[i]]) } restore(s, s.first) if s.first.sb.count != firstPos { beginLine(s) } str := s.assignments.sb.compactToString() if str != "" { write(s, str) beginLine(s) } str = s.regUpdates.sb.compactToString() if str != "" { write(s, str) } if inst.calls.count == 0 { beginLine(s) } write(s, "\nendmodule\n") } input(s GeneratorState, inp ModuleInputDef, genName string, tag Tag) { beginLine(s) write(s, "input ") numberTag(s, tag) write(s, genName) } output(s GeneratorState, o AssignStatement, genName string, tag Tag) { isReg := o.regKeyword != null beginLine(s) write(s, "output ") if isReg { write(s, "reg ") } numberTag(s, tag) write(s, genName) if isReg { if o.expr != null && o.flags & AssignFlags.regUpdate == 0 { write(s, " = ") staticValue(s, s.inst, o.localId, tag) } else { write(s, " = 0") } } } callInst(s GeneratorState, target ModuleInstance) { def := target.def callExpr := target.callExpr beginLine(s) write(s, "(* keep *) ") write(s, target.genGlobalName) write(s, " ") if def.blackboxKeyword != null { if hasStaticInputs(s, def) { write(s, "#(") indent(s, ",") for inp in def.inputs { if inp.flags & ModuleInputFlags.static != 0 { tag := s.typeMap.get(inp) beginLine(s) write(s, ".") write(s, trimHash(inp.name.value)) write(s, "(") staticValue(s, target, inp.localId, tag) write(s, ")") } } unindent(s) beginLine(s) write(s, ") ") } } write(s, target.genLocalName) write(s, "(") indent(s, ",") for inp in def.inputs { if inp.flags & ModuleInputFlags.static == 0 { tag := s.typeMap.get(inp) if tag.kind == TagKind.number { beginLine(s) write(s, ".") write(s, def.blackboxKeyword == null ? nameOf(s, target, inp, -1, -1) : inp.name.value) write(s, "(") expression(s, callExpr.args[callExpr.calleeLocalIdToArgIndex[inp.localId]].expr) write(s, ")") } else if tag.kind == TagKind.struct_ { sdef := unpackStruct(s, tag) for f, i in sdef.fields { beginLine(s) write(s, ".") write(s, def.blackboxKeyword == null ? nameOf(s, target, inp, -1, i) : inp.name.value) write(s, "(") s.evalCtxField = i expression(s, callExpr.args[callExpr.calleeLocalIdToArgIndex[inp.localId]].expr) write(s, ")") } s.evalCtxField = -1 } else { abandon() } } } for o, oi in def.outputs { tag := s.typeMap.get(o) if tag.kind == TagKind.number { beginLine(s) write(s, ".") write(s, def.blackboxKeyword == null ? nameOf(s, target, o, -1, -1) : o.nameExpr.as(Token).value) write(s, "(") write(s, nameOf(s, s.inst, callExpr, oi, -1)) write(s, ")") } else if tag.kind == TagKind.struct_ { sdef := unpackStruct(s, tag) for f, fi in sdef.fields { beginLine(s) write(s, ".") write(s, def.blackboxKeyword == null ? nameOf(s, target, o, -1, fi) : o.nameExpr.as(Token).value) write(s, "(") write(s, nameOf(s, s.inst, callExpr, oi, fi)) write(s, ")") } } else { abandon() } } unindent(s) beginLine(s) write(s, ");") prev := push(s, s.first) for o, oi in def.outputs { tag := s.typeMap.get(o) if tag.kind == TagKind.number { beginLine(s) write(s, "wire ") numberTag(s, tag) write(s, nameOf(s, s.inst, callExpr, oi, -1)) write(s, ";") } else if tag.kind == TagKind.struct_ { sdef := unpackStruct(s, tag) for f, fi in sdef.fields { beginLine(s) write(s, "wire ") numberTag(s, s.typeMap.get(f)) write(s, nameOf(s, s.inst, callExpr, oi, fi)) write(s, ";") } } else { abandon() } } restore(s, prev) beginLine(s) } block(s GeneratorState, block Block) { for n in block.contents { match n { ClockStatement: clock(s, n) IfStatement: if_(s, n) AssignStatement: assign(s, n) } } } clock(s GeneratorState, st ClockStatement) { beginLine(s) write(s, "always @(") write(s, st.keyword.value) write(s, " ") write(s, st.name.value) write(s, ") begin") indent(s, "") block(s, st.body) unindent(s) beginLine(s) write(s, "end") } if_(s GeneratorState, st IfStatement) { beginLine(s) write(s, "if (") expression(s, st.expr) write(s, ") begin") indent(s, "") block(s, st.ifBody) unindent(s) if st.elseBranch != null { beginLine(s) write(s, "end else begin") indent(s, "") if st.elseBranch.is(IfStatement) { if_(s, st.elseBranch.as(IfStatement)) } else if st.elseBranch.is(Block) { block(s, st.elseBranch.as(Block)) } unindent(s) } beginLine(s) write(s, "end") } assign(s GeneratorState, st AssignStatement) { if (st.flags & AssignFlags.reg) != 0 && st.outKeyword == null { prev := push(s, s.first) name := st.nameExpr.as(Token).value tag := s.typeMap.get(st) if tag.kind == TagKind.number { reg(s, st, nameOf(s, s.inst, st, -1, -1), tag) } else if tag.kind == TagKind.struct_ { def := s.entities[tag.q].as(StructDef) for f, fi in def.fields { s.evalCtxField = fi reg(s, st, nameOf(s, s.inst, st, -1, fi), s.typeMap.get(f)) } s.evalCtxField = -1 } else { abandon() } restore(s, prev) } if (st.flags & AssignFlags.wire) != 0 && (st.flags & AssignFlags.static) == 0 { prev := push(s, s.first) tag := s.typeMap.get(st) if tag.kind == TagKind.number { wire(s, st, nameOf(s, s.inst, st, -1, -1), tag) } else if tag.kind == TagKind.struct_ { def := s.entities[tag.q].as(StructDef) for f, fi in def.fields { s.evalCtxField = fi wire(s, st, nameOf(s, s.inst, st, -1, fi), s.typeMap.get(f)) } s.evalCtxField = -1 } else if tag.kind == TagKind.moduleOut { // OK } else { abandon() } restore(s, prev) } if st.flags & AssignFlags.regUpdate != 0 { nameExpr := st.nameExpr match nameExpr { Token: { sym := s.inst.def.symbols.get(nameExpr.value) tag := s.typeMap.get(sym) if tag.kind == TagKind.number { regUpdate(s, nameOf(s, s.inst, sym, -1, -1), st.expr) } else if tag.kind == TagKind.struct_ { def := s.entities[tag.q].as(StructDef) for f, fi in def.fields { s.evalCtxField = fi regUpdate(s, nameOf(s, s.inst, sym, -1, fi), st.expr) } s.evalCtxField = -1 } else { abandon() } } DotExpression: { dot := nameExpr nameToken := dot.lhs.as(Token) sym := s.inst.def.symbols.getOrDefault(nameToken.value) tag := s.typeMap.get(sym) sdef := unpackStruct(s, tag) fieldIndex := sdef.symbols.get(dot.rhs.value).fieldIndex regUpdate(s, nameOf(s, s.inst, sym, -1, fieldIndex), st.expr) } } } } reg(s GeneratorState, st AssignStatement, genName string, tag Tag) { beginLine(s) write(s, "(* keep *) reg ") numberTag(s, tag) write(s, genName) if st.expr != null && st.flags & AssignFlags.regUpdate == 0 { write(s, " = ") staticValue(s, s.inst, st.localId, tag) } else { write(s, " = 0") } write(s, ";") } wire(s GeneratorState, st AssignStatement, genName string, tag Tag) { if st.outKeyword == null { beginLine(s) write(s, "wire ") numberTag(s, tag) write(s, genName) write(s, ";") } cb := new CodeBuilder.create() prev := push(s, cb) beginLine(s) write(s, "assign ") write(s, genName) write(s, " = ") expression(s, st.expr) write(s, ";") s.out = s.assignments write(s, cb.sb.compactToString()) restore(s, prev) } regUpdate(s GeneratorState, genName string, expr Node) { beginLine(s) write(s, genName) write(s, " <= ") expression(s, expr) write(s, ";") } expression(s GeneratorState, e Node) { val := s.constMap.getOrDefault(e) if val.kind != ValueKind.none { assert(s.evalCtxField == -1 && s.evalCtxOutput == -1) tag := s.typeMap.get(e) value(s, tag, val) } else { expressionInner(s, e) } } expressionInner(s GeneratorState, e Node) { match e { Token: token(s, e) NumberExpression: number(s, e) UnaryOperatorExpression: unaryOperator(s, e) BinaryOperatorExpression: binaryOperator(s, e) DotExpression: dot(s, e) TernaryOperatorExpression: ternaryOperator(s, e) MatchExpression: match_(s, e) ParenExpression: paren(s, e) IndexExpression: index(s, e) CallExpression: call(s, e) StructInitializerExpression: structInit(s, e) BraceExpression: brace(s, e) } } token(s GeneratorState, e Token) { name := e.value sym := s.inst.def.symbols.getOrDefault(name) if s.evalCtxOutput == -1 { match sym { ModuleInputDef: { if sym.flags & ModuleInputFlags.static == 0 { write(s, nameOf(s, s.inst, sym, -1, s.evalCtxField)) } else { staticValue(s, s.inst, sym.localId, s.typeMap.get(sym)) } } AssignStatement: { if sym.flags & AssignFlags.static == 0 { write(s, nameOf(s, s.inst, sym, -1, s.evalCtxField)) } else { staticValue(s, s.inst, sym.localId, s.typeMap.get(sym)) } } } } else { assign := sym.as(AssignStatement) assert(assign.expr.is(CallExpression)) expression(s, assign.expr) } } number(s GeneratorState, e NumberExpression) { s.isNonIndexable = true sb := s.out.sb assert(s.evalCtxField == -1 && s.evalCtxOutput == -1) assert(e.dontCare != 0) tag := s.typeMap.get(e) assert(tag.kind == TagKind.number) assert(tag.q > 0) mask := 1_uL << (tag.q - 1) tag.q.writeTo(sb) sb.write("'b") while mask != 0 { if mask & e.dontCare != 0 { sb.write("x") } else if mask & e.value != 0 { sb.write("1") } else { sb.write("0") } mask >>= 1 } } unaryOperator(s GeneratorState, e UnaryOperatorExpression) { if e.op.value == "zx" { expression(s, e.expr) return } s.isNonIndexable = true write(s, e.op.value) write(s, "(") expression(s, e.expr) write(s, ")") } binaryOperator(s GeneratorState, e BinaryOperatorExpression) { s.isNonIndexable = true write(s, "(") expression(s, e.lhs) write(s, ") ") write(s, e.op.value) write(s, " (") expression(s, e.rhs) write(s, ")") } dot(s GeneratorState, e DotExpression) { tag := s.typeMap.get(e.lhs) if tag.kind == TagKind.struct_ { assert(s.evalCtxField == -1) def := s.entities[tag.q].as(StructDef) s.evalCtxField = def.symbols.get(e.rhs.value).fieldIndex expression(s, e.lhs) s.evalCtxField = -1 } else if tag.kind == TagKind.moduleOut { assert(s.evalCtxOutput == -1) def := s.entities[tag.q].as(ModuleDef) s.evalCtxOutput = def.symbols.get(e.rhs.value).as(AssignStatement).outputIndex expression(s, e.lhs) s.evalCtxOutput = -1 } else { abandon() } } ternaryOperator(s GeneratorState, e TernaryOperatorExpression) { s.isNonIndexable = true write(s, "(") expression(s, e.conditionExpr) write(s, ") ? (") expression(s, e.trueExpr) write(s, ") : (") expression(s, e.falseExpr) write(s, ")") } match_(s GeneratorState, e MatchExpression) { eb := new CodeBuilder.create() prev := push(s, eb) s.isNonIndexable = false expression(s, e.target) restore(s, prev) target := "" if !s.isNonIndexable { target = eb.sb.compactToString() } else { target = newLocal(s, s.typeMap.get(e.target), eb.sb.compactToString()) } indent(s, "") for cs in e.cases { beginLine(s) write(s, "(((") write(s, target) write(s, ") == ") expression(s, cs.valueExpr) write(s, ") ? (") expression(s, cs.resultExpr) write(s, ") :") } write(s, " ") resultTag := s.typeMap.get(e) write(s, format("{}'b{}", resultTag.q, string.repeatChar('x', resultTag.q))) write(s, string.repeatChar(')', e.cases.count)) unindent(s) s.isNonIndexable = true } paren(s GeneratorState, e ParenExpression) { s.isNonIndexable = true write(s, "(") expression(s, e.expr) write(s, ")") } index(s GeneratorState, e IndexExpression) { eb := new CodeBuilder.create() prev := push(s, eb) s.isNonIndexable = false expression(s, e.target) restore(s, prev) if !s.isNonIndexable { write(s, eb.sb.compactToString()) } else { write(s, newLocal(s, s.typeMap.get(e.target), eb.sb.compactToString())) } sb := s.out.sb sb.write("[") upper := s.constMap.get(e.upperExpr).z upper.writeTo(sb) if e.lowerExpr != null { sb.write(":") lower := s.constMap.get(e.lowerExpr).z lower.writeTo(sb) } sb.write("]") s.isNonIndexable = true } call(s GeneratorState, e CallExpression) { if e.builtin == BuiltinCall.rep { rep(s, e) return } assert(e.builtin == BuiltinCall.none) assert(s.evalCtxOutput >= 0) write(s, nameOf(s, s.inst, e, s.evalCtxOutput, s.evalCtxField)) } rep(s GeneratorState, e CallExpression) { s.isNonIndexable = true n := TypeChecker.unpackInt(s.constMap.get(e.args[1].expr)) write(s, "{ ") sep := false for i := 0; i < n { if sep { write(s, ", ") } else { sep = true } expression(s, e.args[0].expr) } write(s, " }") } structInit(s GeneratorState, e StructInitializerExpression) { assert(s.evalCtxField >= 0) def := unpackStruct(s, s.typeMap.get(e)) argIndex := e.fieldIndexToArgIndex[s.evalCtxField] if argIndex >= 0 { arg := e.args[argIndex].expr prevCtxField := s.evalCtxField s.evalCtxField = -1 expression(s, arg) s.evalCtxField = prevCtxField } else { s.isNonIndexable = true write(s, "0") } } brace(s GeneratorState, e BraceExpression) { s.isNonIndexable = true write(s, "{ ") sep := false for arg in e.args { if sep { write(s, ", ") } else { sep = true } expression(s, arg) } write(s, " }") } newLocal(s GeneratorState, tag Tag, valStr string) { prev := push(s, s.first) beginLine(s) write(s, "wire ") numberTag(s, tag) name := uniqueName(s, s.inst, s.inst.genLocalsSet, "local") write(s, name) write(s, ";") restore(s, prev) prev = push(s, s.assignments) beginLine(s) write(s, "assign ") write(s, name) write(s, " = ") write(s, valStr) write(s, ";") restore(s, prev) return name } staticValue(s GeneratorState, instance ModuleInstance, localId int, tag Tag) { si := instance.localState[localId] + max(0, s.evalCtxField) value(s, tag, s.es.rs[si]) } value(s GeneratorState, tag Tag, value Value) { s.isNonIndexable = true assert(tag.kind == TagKind.number) if tag.q == 0 { write(s, "(((compiler_bug:invalid_num") } if value.kind == ValueKind.ulong_ { write(s, format("{}'d{}", tag.q, value.z)) } else if value.kind == ValueKind.byteArray { data := EmulatorRunner.unpackArray(value) write(s, format("{}'h", tag.q)) from := min((tag.q + 7) / 8, data.count) if from > 0 { for i := from - 1; i >= 0; i -= 1 { Util.writeByteHexTo(data[i], s.out.sb) } } else { write(s, "0") } } else { abandon() } } numberTag(s GeneratorState, tag Tag) { assert(tag.kind == TagKind.number) assert(tag.q > 0) if tag.q > 1 { write(s, format("[{}:0] ", tag.q - 1)) } } nameOf(s GeneratorState, inst ModuleInstance, node Node, ctxOutput int, ctxField int) string { nc := NodeWithCtx { node: node, ctxOutput: ctxOutput, ctxField: ctxField } existingName := inst.genLocals.maybeGet(nc) if existingName.hasValue { return existingName.value } sb := StringBuilder{} match node { ModuleInputDef: { sb.write(trimHash(node.name.value)) if ctxField != -1 { sb.write("_") sb.write(unpackStruct(s, s.typeMap.get(node)).fields[ctxField].name.value) } } AssignStatement: { assert(s.typeMap.get(node).kind != TagKind.moduleOut) sb.write(trimHash(node.nameExpr.as(Token).value)) if ctxField != -1 { sb.write("_") sb.write(unpackStruct(s, s.typeMap.get(node)).fields[ctxField].name.value) } } CallExpression: { target := s.es.moduleInstances[inst.calls[node.callId]] output := target.def.outputs[ctxOutput] sb.write(target.genLocalName) sb.write("_") sb.write(target.def.blackboxKeyword == null ? nameOf(s, target, output, -1, ctxField) : output.nameExpr.as(Token).value) } } name := uniqueName(s, inst, inst.genLocalsSet, sb.compactToString()) inst.genLocals.add(nc, name) return name } uniqueName(s GeneratorState, inst ModuleInstance, set Set, origName string) { name := origName i := 1 while true { if !s.globals.contains(name) && !inst.genLocalsSet.contains(name) { set.add(name) return name } name = format("{}_{}", origName, i) i += 1 } } hasStaticInputs(s GeneratorState, def ModuleDef) { result := false for inp in def.inputs { result ||= (inp.flags & ModuleInputFlags.static) != 0 } return result } trimHash(s string) { if s[0] == '#' { return s.slice(1, s.length) } return s } beginLine(s GeneratorState) { if !s.out.skipSep { write(s, s.out.lineSep) } else { s.out.skipSep = false } write(s, "\n") for i := 0; i < s.out.indent { s.out.sb.writeChar('\t') } } write(s GeneratorState, str string) { s.out.sb.write(str) } indent(s GeneratorState, lineSep string) { s.out.lineSep = lineSep s.out.skipSep = true s.out.indent += 1 } unindent(s GeneratorState) { s.out.lineSep = "" s.out.indent -= 1 } push(s GeneratorState, cb CodeBuilder) { prev := s.out s.out = cb return prev } restore(s GeneratorState, cb CodeBuilder) { s.out = cb } unpackStruct(s GeneratorState, tag Tag) { assert(tag.kind == TagKind.struct_) return s.entities[tag.q].as(StructDef) } unpackModule(s GeneratorState, tag Tag) { assert(tag.kind == TagKind.moduleOut) return s.entities[tag.q].as(ModuleDef) } }