aboutsummaryrefslogtreecommitdiffstats
path: root/icebox/icebox_vlog.py
diff options
context:
space:
mode:
authorClifford Wolf <clifford@clifford.at>2015-07-18 13:05:02 +0200
committerClifford Wolf <clifford@clifford.at>2015-07-18 13:05:02 +0200
commitdfeb92a46b2228b4f3886a9c06502ccd0dde5562 (patch)
tree20cebe482ae2954f4597489658b27828f5316921 /icebox/icebox_vlog.py
downloadicestorm-dfeb92a46b2228b4f3886a9c06502ccd0dde5562.tar.gz
icestorm-dfeb92a46b2228b4f3886a9c06502ccd0dde5562.tar.bz2
icestorm-dfeb92a46b2228b4f3886a9c06502ccd0dde5562.zip
Import of icestorm-snapshot-150322.zip
Diffstat (limited to 'icebox/icebox_vlog.py')
-rwxr-xr-xicebox/icebox_vlog.py367
1 files changed, 367 insertions, 0 deletions
diff --git a/icebox/icebox_vlog.py b/icebox/icebox_vlog.py
new file mode 100755
index 0000000..ded96a6
--- /dev/null
+++ b/icebox/icebox_vlog.py
@@ -0,0 +1,367 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2015 Clifford Wolf <clifford@clifford.at>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+from __future__ import division
+from __future__ import print_function
+
+import icebox
+import getopt, sys, re
+
+strip_comments = False
+strip_interconn = False
+lookup_pins = False
+pcf_data = dict()
+portnames = set()
+unmatched_ports = set()
+auto_clk = False
+auto_en = False
+
+def usage():
+ print("""
+Usage: icebox_vlog [options] <bitmap.txt>
+
+ -s
+ strip comments from output
+
+ -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
+
+ -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.
+""")
+ sys.exit(0)
+
+try:
+ opts, args = getopt.getopt(sys.argv[1:], "sSlap:P:")
+except:
+ usage()
+
+for o, a in opts:
+ if o == "-s":
+ strip_comments = True
+ elif o == "-S":
+ strip_interconn = True
+ elif o == "-l":
+ lookup_pins = True
+ elif o == "-a":
+ auto_clk = True
+ auto_en = True
+ elif o in ("-p", "-P"):
+ with open(a, "r") as f:
+ for line in f:
+ 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("_obuf", "")
+ p = p.replace("_gb_io", "")
+ portnames.add(p)
+ if not re.match(r"[a-zA-Z_][a-zA-Z0-9_]*$", p):
+ p = "\\%s " % p
+ unmatched_ports.add(p)
+ pinloc = tuple([int(s) for s in line[2:]])
+ pcf_data[pinloc] = p
+ else:
+ usage()
+
+if not strip_comments:
+ print("// Reading file '%s'.." % args[0])
+ic = icebox.iceconfig()
+ic.read_file(args[0])
+print()
+
+text_wires = list()
+text_ports = list()
+
+luts_queue = set()
+text_func = list()
+
+netidx = [0]
+nets = dict()
+seg2net = dict()
+
+auto_clk_nets = set()
+auto_en_nets = set()
+
+def is_interconn(netname):
+ if netname.startswith("sp4_"): return True
+ if netname.startswith("sp12_"): return True
+ if netname.startswith("span4_"): return True
+ if netname.startswith("span12_"): return True
+ if netname.startswith("logic_op_"): return True
+ if netname.startswith("neigh_op_"): return True
+ if netname.startswith("local_"): return True
+ return False
+
+for segs in sorted(ic.group_segments()):
+ while True:
+ netidx[0] += 1
+ n = "n%d" % netidx[0]
+ if n not in portnames: break
+
+ net_segs = set()
+ renamed_net_to_port = False
+
+ for s in segs:
+ match = re.match("io_(\d+)/D_(IN|OUT)_(\d+)$", 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)
+ 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 (entry[0],) in pcf_data:
+ p = pcf_data[(entry[0],)]
+ unmatched_ports.discard(p)
+ elif (entry[1], entry[2], entry[3]) in pcf_data:
+ p = pcf_data[(entry[1], entry[2], entry[3])]
+ 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":
+ text_ports.append("input %s" % p)
+ else:
+ text_ports.append("output %s" % p)
+ text_wires.append("wire %s;" % n)
+ renamed_net_to_port = True
+ elif match.group(2) == "IN":
+ text_ports.append("input %s" % p)
+ text_wires.append("assign %s = %s;" % (n, p))
+ else:
+ text_ports.append("output %s" % p)
+ text_wires.append("assign %s = %s;" % (p, n))
+
+ match = re.match("lutff_(\d+)/", s[2])
+ if match:
+ luts_queue.add((s[0], s[1], int(match.group(1))))
+
+ nets[n] = segs
+
+ for s in segs:
+ seg2net[s] = n
+
+ if not renamed_net_to_port:
+ text_wires.append("wire %s;" % n)
+
+ for s in segs:
+ if not strip_interconn or not is_interconn(s[2]):
+ if s[2].startswith("glb_netwk_"):
+ net_segs.add((0, 0, s[2]))
+ 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)
+
+ if not strip_comments:
+ for s in sorted(net_segs):
+ text_wires.append("// %s" % (s,))
+ 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
+ nets[n] = set([seg])
+ seg2net[seg] = n
+ text_wires.append("wire %s;" % n)
+ if not strip_comments:
+ if not strip_interconn or not is_interconn(seg[2]):
+ text_wires.append("// %s" % (seg,))
+ text_wires.append("")
+ return seg2net[seg]
+
+wire_to_reg = set()
+lut_assigns = list()
+const_assigns = list()
+carry_assigns = list()
+always_stmts = list()
+max_net_len = 0
+
+for lut in luts_queue:
+ seq_bits = icebox.get_lutff_seq_bits(ic.logic_tiles[(lut[0], lut[1])], lut[2])
+ if seq_bits[0] == "1":
+ seg_to_net((lut[0], lut[1], "lutff_%d/cout" % lut[2]))
+
+for lut in luts_queue:
+ tile = ic.logic_tiles[(lut[0], lut[1])]
+ lut_bits = icebox.get_lutff_lut_bits(tile, lut[2])
+ seq_bits = icebox.get_lutff_seq_bits(tile, lut[2])
+ net_in0 = seg_to_net((lut[0], lut[1], "lutff_%d/in_0" % lut[2]), "0")
+ net_in1 = seg_to_net((lut[0], lut[1], "lutff_%d/in_1" % lut[2]), "0")
+ net_in2 = seg_to_net((lut[0], lut[1], "lutff_%d/in_2" % lut[2]), "0")
+ net_in3 = seg_to_net((lut[0], lut[1], "lutff_%d/in_3" % lut[2]), "0")
+ net_out = seg_to_net((lut[0], lut[1], "lutff_%d/out" % lut[2]))
+ if seq_bits[0] == "1":
+ net_cout = seg_to_net((lut[0], lut[1], "lutff_%d/cout" % lut[2]))
+ net_in1 = seg_to_net((lut[0], lut[1], "lutff_%d/in_1" % lut[2]), "0")
+ net_in2 = seg_to_net((lut[0], lut[1], "lutff_%d/in_2" % lut[2]), "0")
+ if lut[2] == 0:
+ net_cin = seg_to_net((lut[0], lut[1], "carry_in_mux"))
+ if icebox.get_carry_cascade_bit(tile) == "0":
+ if not strip_comments:
+ text_wires.append("// Carry-In for (%d %d)" % (lut[0], lut[1]))
+ text_wires.append("assign %s = %s;" % (net_cin, icebox.get_carry_bit(tile)))
+ if not strip_comments:
+ text_wires.append("")
+ else:
+ net_cin = seg_to_net((lut[0], lut[1], "lutff_%d/cout" % (lut[2]-1)), "0")
+ 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
+ text_wires.append("wire %s;" % n)
+ if not strip_comments:
+ text_wires.append("// FF %s" % (lut,))
+ text_wires.append("")
+ net_cen = seg_to_net((lut[0], lut[1], "lutff_global/cen"), "1")
+ net_clk = seg_to_net((lut[0], lut[1], "lutff_global/clk"), "0")
+ net_sr = seg_to_net((lut[0], lut[1], "lutff_global/s_r"), "0")
+ if seq_bits[3] == "0":
+ always_stmts.append("/* FF %2d %2d %2d */ always @(%sedge %s) if (%s) %s <= %s ? %s : %s;" %
+ (lut[0], lut[1], lut[2], "neg" if icebox.get_negclk_bit(tile) == "1" else "pos",
+ net_clk, net_cen, net_out, net_sr, seq_bits[2], n))
+ else:
+ always_stmts.append("/* FF %2d %2d %2d */ always @(%sedge %s, posedge %s) if (%s) %s <= %s; else if (%s) %s <= %s;" %
+ (lut[0], lut[1], lut[2], "neg" if icebox.get_negclk_bit(tile) == "1" else "pos",
+ net_clk, net_sr, net_sr, net_out, seq_bits[2], net_cen, net_out, n))
+ wire_to_reg.add(net_out)
+ net_out = n
+ if not "1" in lut_bits:
+ const_assigns.append([net_out, "1'b0"])
+ elif not "0" in lut_bits:
+ const_assigns.append([net_out, "1'b1"])
+ else:
+ def make_lut_expr(bits, sigs):
+ if not sigs:
+ return "%s" % bits[0]
+ l_expr = make_lut_expr(bits[0:len(bits)//2], sigs[1:])
+ h_expr = make_lut_expr(bits[len(bits)//2:len(bits)], sigs[1:])
+ if h_expr == l_expr: return h_expr
+ if sigs[0] == "0": return l_expr
+ if sigs[0] == "1": return h_expr
+ if h_expr == "1" and l_expr == "0": return sigs[0]
+ if h_expr == "0" and l_expr == "1": return "!" + sigs[0]
+ return "%s ? %s : %s" % (sigs[0], h_expr, l_expr)
+ lut_expr = make_lut_expr(lut_bits, [net_in3, net_in2, net_in1, net_in0])
+ lut_assigns.append([net_out, "/* LUT %2d %2d %2d */ %s" % (lut[0], lut[1], lut[2], lut_expr)])
+ max_net_len = max(max_net_len, len(net_out))
+
+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))
+
+new_text_wires = list()
+for line in text_wires:
+ match = re.match(r"wire ([^ ;]+)(.*)", line)
+ if match and match.group(1) in wire_to_reg:
+ line = "reg " + match.group(1) + match.group(2)
+ if strip_comments:
+ if new_text_wires and new_text_wires[-1].split()[0] == line.split()[0] and new_text_wires[-1][-1] == ";":
+ new_text_wires[-1] = new_text_wires[-1][0:-1] + "," + line[len(line.split()[0]):]
+ else:
+ new_text_wires.append(line)
+ else:
+ print(line)
+for line in new_text_wires:
+ print(line)
+if strip_comments:
+ print()
+
+for line in text_func:
+ print(line)
+for line in always_stmts:
+ print(line)
+print()
+
+for p in unmatched_ports:
+ print("// Warning: unmatched port '%s'" %p)
+if unmatched_ports:
+ print()
+
+print("endmodule")
+print()
+