diff options
Diffstat (limited to 'icebox/icebox_vlog.py')
-rwxr-xr-x | icebox/icebox_vlog.py | 346 |
1 files changed, 268 insertions, 78 deletions
diff --git a/icebox/icebox_vlog.py b/icebox/icebox_vlog.py index ded96a6..acf7913 100755 --- a/icebox/icebox_vlog.py +++ b/icebox/icebox_vlog.py @@ -24,15 +24,16 @@ import getopt, sys, re strip_comments = False strip_interconn = False lookup_pins = False +check_ieren = False +check_driver = False pcf_data = dict() portnames = set() unmatched_ports = set() -auto_clk = False -auto_en = False +modname = "chip" def usage(): print(""" -Usage: icebox_vlog [options] <bitmap.txt> +Usage: icebox_vlog [options] [bitmap.txt] -s strip comments from output @@ -40,24 +41,29 @@ Usage: icebox_vlog [options] <bitmap.txt> -S strip comments about interconn wires from output - -a - auto-detect global clock and enable signals - (require ports "clk" and "en" in pcf file) - -l convert io tile port names to chip pin numbers + -n <module-name> + name for the exported module (default: "chip") + -p <pcf-file> use the set_io command from the specified pcf file -P <pcf-file> like -p, enable some hacks for pcf files created by the iCEcube2 placer. + + -R + enable IeRen database checks + + -D + enable exactly-one-driver checks """) sys.exit(0) try: - opts, args = getopt.getopt(sys.argv[1:], "sSlap:P:") + opts, args = getopt.getopt(sys.argv[1:], "sSlap:P:n:RD") except: usage() @@ -68,18 +74,22 @@ for o, a in opts: strip_interconn = True elif o == "-l": lookup_pins = True + elif o == "-n": + modname = a elif o == "-a": - auto_clk = True - auto_en = True + pass # ignored for backward compatibility elif o in ("-p", "-P"): with open(a, "r") as f: for line in f: + if o == "-P" and not re.search(" # ICE_(GB_)?IO", line): + continue line = re.sub(r"#.*", "", line.strip()).split() if len(line) and line[0] == "set_io": p = line[1] if o == "-P": p = p.lower() p = p.replace("_ibuf", "") + p = p.replace("_obuft", "") p = p.replace("_obuf", "") p = p.replace("_gb_io", "") portnames.add(p) @@ -88,9 +98,19 @@ for o, a in opts: unmatched_ports.add(p) pinloc = tuple([int(s) for s in line[2:]]) pcf_data[pinloc] = p + elif o == "-R": + check_ieren = True + elif o == "-D": + check_driver = True else: usage() +if len(args) == 0: + args.append("/dev/stdin") + +if len(args) != 1: + usage() + if not strip_comments: print("// Reading file '%s'.." % args[0]) ic = icebox.iceconfig() @@ -102,13 +122,19 @@ text_ports = list() luts_queue = set() text_func = list() +failed_drivers_check = list() netidx = [0] nets = dict() seg2net = dict() -auto_clk_nets = set() -auto_en_nets = set() +iocells = set() +iocells_in = set() +iocells_out = set() +iocells_special = set() +iocells_type = dict() +iocells_negclk = set() +iocells_inbufs = set() def is_interconn(netname): if netname.startswith("sp4_"): return True @@ -120,27 +146,93 @@ def is_interconn(netname): if netname.startswith("local_"): return True return False +extra_connections = list() +extra_segments = list() + +for bit in ic.extra_bits: + entry = ic.lookup_extra_bit(bit) + if entry[0] == "padin_glb_netwk": + glb = int(entry[1]) + pin_entry = ic.padin_pio_db()[glb] + iocells.add((pin_entry[0], pin_entry[1], pin_entry[2])) + iocells_in.add((pin_entry[0], pin_entry[1], pin_entry[2])) + s1 = (pin_entry[0], pin_entry[1], "io_%d/PAD" % pin_entry[2]) + s2 = (pin_entry[0], pin_entry[1], "wire_gbuf/padin_%d" % pin_entry[2]) + extra_connections.append((s1, s2)) + +for idx, tile in ic.io_tiles.items(): + tc = icebox.tileconfig(tile) + iocells_type[(idx[0], idx[1], 0)] = ["0" for i in range(6)] + iocells_type[(idx[0], idx[1], 1)] = ["0" for i in range(6)] + for entry in ic.tile_db(idx[0], idx[1]): + if check_ieren and entry[1] == "IoCtrl" and entry[2].startswith("IE_") and not tc.match(entry[0]): + iren_idx = (idx[0], idx[1], 0 if entry[2] == "IE_0" else 1) + for iren_entry in ic.ieren_db(): + if iren_idx[0] == iren_entry[3] and iren_idx[1] == iren_entry[4] and iren_idx[2] == iren_entry[5]: + iocells_inbufs.add((iren_entry[0], iren_entry[1], iren_entry[2])) + if entry[1] == "NegClk" and tc.match(entry[0]): + iocells_negclk.add((idx[0], idx[1], 0)) + iocells_negclk.add((idx[0], idx[1], 1)) + if entry[1].startswith("IOB_") and entry[2].startswith("PINTYPE_") and tc.match(entry[0]): + match1 = re.match("IOB_(\d+)", entry[1]) + match2 = re.match("PINTYPE_(\d+)", entry[2]) + assert match1 and match2 + iocells_type[(idx[0], idx[1], int(match1.group(1)))][int(match2.group(1))] = "1" + iocells_type[(idx[0], idx[1], 0)] = "".join(iocells_type[(idx[0], idx[1], 0)]) + iocells_type[(idx[0], idx[1], 1)] = "".join(iocells_type[(idx[0], idx[1], 1)]) + for segs in sorted(ic.group_segments()): + for seg in segs: + if ic.tile_type(seg[0], seg[1]) == "IO": + match = re.match("io_(\d+)/D_(IN|OUT)_(\d+)", seg[2]) + if match: + cell = (seg[0], seg[1], int(match.group(1))) + iocells.add(cell) + if match.group(2) == "IN": + if check_ieren: + assert cell in iocells_inbufs + if iocells_type[cell] != "100000" or match.group(3) != "0": + iocells_special.add(cell) + iocells_in.add(cell) + if match.group(2) == "OUT" and iocells_type[cell][2:6] != "0000": + if iocells_type[cell] != "100110" or match.group(3) != "0": + iocells_special.add(cell) + iocells_out.add(cell) + extra_segments.append((seg[0], seg[1], "io_%d/PAD" % int(match.group(1)))) + +for cell in iocells: + if iocells_type[cell] == "100110" and not cell in iocells_special: + s1 = (cell[0], cell[1], "io_%d/PAD" % cell[2]) + s2 = (cell[0], cell[1], "io_%d/D_OUT_0" % cell[2]) + extra_connections.append((s1, s2)) + del iocells_type[cell] + elif iocells_type[cell] == "100000" and not cell in iocells_special: + s1 = (cell[0], cell[1], "io_%d/PAD" % cell[2]) + s2 = (cell[0], cell[1], "io_%d/D_IN_0" % cell[2]) + extra_connections.append((s1, s2)) + del iocells_type[cell] + +def next_netname(): while True: netidx[0] += 1 n = "n%d" % netidx[0] - if n not in portnames: break + if n not in portnames: + return n +for segs in sorted(ic.group_segments(extra_connections=extra_connections, extra_segments=extra_segments)): + n = next_netname() net_segs = set() renamed_net_to_port = False for s in segs: - match = re.match("io_(\d+)/D_(IN|OUT)_(\d+)$", s[2]) + match = re.match("io_(\d+)/PAD", s[2]) if match: - if match.group(2) == "IN": - p = "io_%d_%d_%s_din_%s" % (s[0], s[1], match.group(1), match.group(3)) - net_segs.add(p) - else: - p = "io_%d_%d_%s_dout_%s" % (s[0], s[1], match.group(1), match.group(3)) - net_segs.add(p) + idx = (s[0], s[1], int(match.group(1))) + p = "io_%d_%d_%d" % idx + net_segs.add(p) if lookup_pins or pcf_data: for entry in icebox.pinloc_db: - if s[0] == entry[1] and s[1] == entry[2] and int(match.group(1)) == entry[3]: + if idx[0] == entry[1] and idx[1] == entry[2] and idx[2] == entry[3]: if (entry[0],) in pcf_data: p = pcf_data[(entry[0],)] unmatched_ports.discard(p) @@ -149,24 +241,25 @@ for segs in sorted(ic.group_segments()): unmatched_ports.discard(p) elif lookup_pins: p = "pin_%d" % entry[0] - if p == "clk": - auto_clk = False - if p == "en": - auto_en = False if not renamed_net_to_port: n = p - if match.group(2) == "IN": + if idx in iocells_in and idx not in iocells_out: text_ports.append("input %s" % p) - else: + elif idx not in iocells_in and idx in iocells_out: text_ports.append("output %s" % p) + else: + text_ports.append("inout %s" % p) text_wires.append("wire %s;" % n) renamed_net_to_port = True - elif match.group(2) == "IN": + elif idx in iocells_in and idx not in iocells_out: text_ports.append("input %s" % p) text_wires.append("assign %s = %s;" % (n, p)) - else: + elif idx not in iocells_in and idx in iocells_out: text_ports.append("output %s" % p) text_wires.append("assign %s = %s;" % (p, n)) + else: + text_ports.append("inout %s" % p) + text_wires.append("assign %s = %s;" % (p, n)) match = re.match("lutff_(\d+)/", s[2]) if match: @@ -187,60 +280,27 @@ for segs in sorted(ic.group_segments()): else: net_segs.add(s) - if not renamed_net_to_port: - has_clk = False - has_cen = False - has_global = False - has_driver = False - for s in sorted(net_segs): - if s[2].startswith("glb_netwk_"): - has_global = True - elif re.search(r"/out", s[2]): - has_driver = True - elif s[2] == "lutff_global/clk": - has_clk = True - elif s[2] == "lutff_global/cen": - has_cen = True - if has_global and not has_driver: - if has_clk: - auto_clk_nets.add(n) - if has_cen and not has_clk: - auto_en_nets.add(n) + count_drivers = 0 + for s in segs: + if re.match(r"ram/RDATA_", s[2]): count_drivers += 1 + if re.match(r"io_./D_IN_", s[2]): count_drivers += 1 + if re.match(r"lutff_./out", s[2]): count_drivers += 1 + + if count_drivers != 1 and check_driver: + failed_drivers_check.append(n) if not strip_comments: for s in sorted(net_segs): text_wires.append("// %s" % (s,)) + if count_drivers != 1 and check_driver: + text_wires.append("// Number of drivers: %d" % count_drivers) text_wires.append("") -for p in unmatched_ports: - text_ports.append("input %s" % p) - -if auto_clk and auto_clk_nets and "clk" in unmatched_ports: - assert len(auto_clk_nets) == 1 - if not strip_comments: - text_wires.append("// automatically detected clock network") - text_wires.append("assign %s = clk;" % auto_clk_nets.pop()) - if not strip_comments: - text_wires.append("") - unmatched_ports.remove("clk") - -if auto_en and auto_en_nets and "en" in unmatched_ports: - assert len(auto_en_nets) == 1 - if not strip_comments: - text_wires.append("// automatically detected enable network") - text_wires.append("assign %s = en;" % auto_en_nets.pop()) - if not strip_comments: - text_wires.append("") - unmatched_ports.remove("en") - def seg_to_net(seg, default=None): if seg not in seg2net: if default is not None: return default - while True: - netidx[0] += 1 - n = "n%d" % netidx[0] - if n not in portnames: break + n = next_netname() nets[n] = set([seg]) seg2net[seg] = n text_wires.append("wire %s;" % n) @@ -250,6 +310,134 @@ def seg_to_net(seg, default=None): text_wires.append("") return seg2net[seg] +for cell in iocells: + if cell in iocells_type: + net_pad = seg_to_net((cell[0], cell[1], "io_%d/PAD" % cell[2])) + net_din0 = seg_to_net((cell[0], cell[1], "io_%d/D_IN_0" % cell[2]), "") + net_din1 = seg_to_net((cell[0], cell[1], "io_%d/D_IN_1" % cell[2]), "") + net_dout0 = seg_to_net((cell[0], cell[1], "io_%d/D_OUT_0" % cell[2]), "0") + net_dout1 = seg_to_net((cell[0], cell[1], "io_%d/D_OUT_1" % cell[2]), "0") + net_oen = seg_to_net((cell[0], cell[1], "io_%d/OUT_ENB" % cell[2]), "1") + net_cen = seg_to_net((cell[0], cell[1], "io_global/cen"), "1") + net_iclk = seg_to_net((cell[0], cell[1], "io_global/inclk"), "0") + net_oclk = seg_to_net((cell[0], cell[1], "io_global/outclk"), "0") + net_latch = seg_to_net((cell[0], cell[1], "io_global/latch"), "0") + iotype = iocells_type[cell] + + if cell in iocells_negclk: + posedge = "negedge" + negedge = "posedge" + else: + posedge = "posedge" + negedge = "negedge" + + text_func.append("// IO Cell %s" % (cell,)) + if not strip_comments: + text_func.append("// PAD = %s" % net_pad) + text_func.append("// D_IN_0 = %s" % net_din0) + text_func.append("// D_IN_1 = %s" % net_din1) + text_func.append("// D_OUT_0 = %s" % net_dout0) + text_func.append("// D_OUT_1 = %s" % net_dout1) + text_func.append("// OUT_ENB = %s" % net_oen) + text_func.append("// CLK_EN = %s" % net_cen) + text_func.append("// IN_CLK = %s" % net_iclk) + text_func.append("// OUT_CLK = %s" % net_oclk) + text_func.append("// LATCH = %s" % net_latch) + text_func.append("// TYPE = %s (LSB:MSB)" % iotype) + + if net_din0 != "" or net_din1 != "": + if net_cen == "1": + icen_cond = "" + else: + icen_cond = "if (%s) " % net_cen + + if net_din0 != "": + if iotype[1] == "0" and iotype[0] == "0": + reg_din0 = next_netname() + text_func.append("reg %s;" % reg_din0) + text_func.append("always @(%s %s) %s%s <= %s;" % (posedge, net_iclk, icen_cond, reg_din0, net_pad)) + text_func.append("assign %s = %s;" % (net_din0, reg_din0)) + + if iotype[1] == "0" and iotype[0] == "1": + text_func.append("assign %s = %s;" % (net_din0, net_pad)) + + if iotype[1] == "1" and iotype[0] == "0": + reg_din0 = next_netname() + reg_din0_latched = next_netname() + text_func.append("reg %s, %s;" % (reg_din0, reg_din0_latched)) + text_func.append("always @(%s %s) %s%s <= %s;" % (posedge, net_iclk, icen_cond, reg_din0, net_pad)) + text_func.append("always @* if (!%s) %s = %s;" % (net_latch, reg_din0_latched, reg_din0)) + text_func.append("assign %s = %s;" % (net_din0, reg_din0_latched)) + + if iotype[1] == "1" and iotype[0] == "1": + reg_din0 = next_netname() + text_func.append("reg %s;" % reg_din0) + text_func.append("always @* if (!%s) %s = %s;" % (net_latch, reg_din0, net_pad)) + text_func.append("assign %s = %s;" % (net_din0, reg_din0)) + + if net_din1 != "": + reg_din1 = next_netname() + text_func.append("reg %s;" % reg_din1) + text_func.append("always @(%s %s) %s%s <= %s;" % (negedge, net_iclk, icen_cond, reg_din1, net_pad)) + text_func.append("assign %s = %s;" % (net_din1, reg_din1)) + + if iotype[5] != "0" or iotype[4] != "0": + if net_cen == "1": + ocen_cond = "" + else: + ocen_cond = "if (%s) " % net_cen + + # effective OEN: iotype[4], iotype[5] + + if iotype[5] == "0" and iotype[4] == "1": + eff_oen = "1" + + if iotype[5] == "1" and iotype[4] == "0": + eff_oen = net_oen + + if iotype[5] == "1" and iotype[4] == "1": + eff_oen = next_netname() + text_func.append("reg %s;" % eff_oen) + text_func.append("always @(%s %s) %s%s <= %s;" % (posedge, net_oclk, ocen_cond, eff_oen, net_oen)) + + # effective DOUT: iotype[2], iotype[3] + + if iotype[2] == "0" and iotype[3] == "0": + ddr_posedge = next_netname() + ddr_negedge = next_netname() + text_func.append("reg %s, %s;" % (ddr_posedge, ddr_negedge)) + text_func.append("always @(%s %s) %s%s <= %s;" % (posedge, net_oclk, ocen_cond, ddr_posedge, net_dout0)) + text_func.append("always @(%s %s) %s%s <= %s;" % (negedge, net_oclk, ocen_cond, ddr_negedge, net_dout1)) + eff_dout = next_netname() + text_func.append("wire %s;" % (eff_dout)) + if cell in iocells_negclk: + text_func.append("assign %s = %s ? %s : %s;" % (eff_dout, net_oclk, ddr_negedge, ddr_posedge)) + else: + text_func.append("assign %s = %s ? %s : %s;" % (eff_dout, net_oclk, ddr_posedge, ddr_negedge)) + + if iotype[2] == "0" and iotype[3] == "1": + eff_dout = net_dout0 + + if iotype[2] == "1" and iotype[3] == "0": + eff_dout = next_netname() + text_func.append("reg %s;" % eff_dout) + text_func.append("always @(%s %s) %s%s <= %s;" % (posedge, net_oclk, ocen_cond, eff_dout, net_dout0)) + + if iotype[2] == "1" and iotype[3] == "1": + eff_dout = next_netname() + text_func.append("reg %s;" % eff_dout) + text_func.append("always @(%s %s) %s%s <= !%s;" % (posedge, net_oclk, ocen_cond, eff_dout, net_dout0)) + + if eff_oen == "1": + text_func.append("assign %s = %s;" % (net_pad, eff_dout)) + else: + text_func.append("assign %s = %s ? %s : 1'bz;" % (net_pad, eff_oen, eff_dout)) + + text_func.append("") + +for p in unmatched_ports: + text_ports.append("input %s" % p) + wire_to_reg = set() lut_assigns = list() const_assigns = list() @@ -288,10 +476,7 @@ for lut in luts_queue: carry_assigns.append([net_cout, "/* CARRY %2d %2d %2d */ (%s & %s) | ((%s | %s) & %s)" % (lut[0], lut[1], lut[2], net_in1, net_in2, net_in1, net_in2, net_cin)]) if seq_bits[1] == "1": - while True: - netidx[0] += 1 - n = "n%d" % netidx[0] - if n not in portnames: break + n = next_netname() text_wires.append("wire %s;" % n) if not strip_comments: text_wires.append("// FF %s" % (lut,)) @@ -332,7 +517,7 @@ for lut in luts_queue: for a in const_assigns + lut_assigns + carry_assigns: text_func.append("assign %-*s = %s;" % (max_net_len, a[0], a[1])) -print("module chip (%s);\n" % ", ".join(text_ports)) +print("module %s (%s);\n" % (modname, ", ".join(text_ports))) new_text_wires = list() for line in text_wires: @@ -365,3 +550,8 @@ if unmatched_ports: print("endmodule") print() +if failed_drivers_check: + print("// Single-driver-check failed for %d nets:" % len(failed_drivers_check)) + print("// %s" % " ".join(failed_drivers_check)) + assert False + |