| #!/usr/bin/env python3 |
| |
| import os, sys, re |
| |
| device = "up5k" |
| |
| pins = "2 3 4 6 9 10 11 12 13 18 19 20 21 25 26 27 28 31 32 34 35 36 37 38 42 43 44 45 46 47 48".split() |
| |
| # This script is designed to determine the routing of 5k SPRAM signals, |
| # and the location of the enable config bits |
| |
| spram_locs = [(0, 0, 1), (0, 0, 2), (25, 0, 3), (25, 0, 4)] |
| #spram_locs = [(0, 0, 1)] |
| spram_data = { } |
| |
| spram_signals = ["WREN", "CHIPSELECT", "CLOCK", "STANDBY", "SLEEP", "POWEROFF"] |
| |
| for i in range(14): |
| spram_signals.append("ADDRESS[%d]" % i) |
| |
| for i in range(16): |
| spram_signals.append("DATAIN[%d]" % i) |
| |
| for i in range(16): |
| spram_signals.append("DATAOUT[%d]" % i) |
| |
| for i in range(4): |
| spram_signals.append("MASKWREN[%d]" % i) |
| |
| fuzz_options = ["ADDRESS", "DATAIN", "MASKWREN", "DATAOUT"] |
| |
| #Parse the output of an icebox vlog file to determine connectivity |
| def parse_vlog(f, pin2net, net_map): |
| current_net = None |
| |
| for line in f: |
| m = re.match(r"wire ([a-zA-Z0-9_]+);", line) |
| if m: |
| net = m.group(1) |
| mp = re.match(r"pin_([a-zA-Z0-9]+)", net) |
| if mp: |
| pin = mp.group(1) |
| if pin in pin2net: |
| current_net = pin2net[pin] |
| else: |
| current_net = None |
| else: |
| current_net = None |
| elif current_net is not None: |
| m = re.match(r"// \((\d+), (\d+), '([a-zA-Z0-9_/]+)'\)", line) |
| if m: |
| x = int(m.group(1)) |
| y = int(m.group(2)) |
| net = m.group(3) |
| if not (net.startswith("sp") or net.startswith("glb") or net.startswith("neigh") or net.startswith("io") or net.startswith("local") or net.startswith("fabout")): |
| net_map[current_net].add((x, y, net)) |
| def parse_exp(f): |
| current_x = 0 |
| current_y = 0 |
| bits = set() |
| for line in f: |
| splitline = line.split(' ') |
| if splitline[0].endswith("_tile"): |
| current_x = int(splitline[1]) |
| current_y = int(splitline[2]) |
| elif splitline[0] == "IpConfig": |
| if splitline[1][:5] == "CBIT_": |
| bitidx = int(splitline[1][5:]) |
| bits.add((current_x, current_y, splitline[1].strip())) |
| return bits |
| |
| if not os.path.exists("./work_spram"): |
| os.mkdir("./work_spram") |
| |
| for loc in spram_locs: |
| x, y, z = loc |
| net_map = {} |
| for sig in spram_signals: |
| net_map[sig] = set() |
| net_map["SPRAM_EN"] = set() # actually a CBIT not a net |
| |
| for n in fuzz_options: |
| with open("./work_spram/spram.v","w") as f: |
| print(""" |
| module top( |
| input WREN, |
| input CHIPSELECT, |
| input CLOCK, |
| input STANDBY, |
| input SLEEP, |
| input POWEROFF, |
| """, file=f) |
| if n == "ADDRESS": |
| print("\t\t\tinput [13:0] ADDRESS,", file=f) |
| if n == "DATAIN": |
| print("\t\t\tinput [15:0] DATAIN,", file=f) |
| if n == "MASKWREN": |
| print("\t\t\tinput [3:0] MASKWREN,", file=f) |
| if n == "DATAOUT": |
| print("\t\t\toutput [15:0] DATAOUT);", file=f) |
| else: |
| print("\t\t\toutput [0:0] DATAOUT);", file=f) #some dataout is always required to prevent optimisation away |
| |
| addr_net = "ADDRESS" if n == "ADDRESS" else "" |
| din_net = "DATAIN" if n == "DATAIN" else "" |
| mwren_net = "MASKWREN" if n == "MASKWREN" else "" |
| |
| print(""" |
| SB_SPRAM256KA spram_i |
| ( |
| .ADDRESS(%s), |
| .DATAIN(%s), |
| .MASKWREN(%s), |
| .WREN(WREN), |
| .CHIPSELECT(CHIPSELECT), |
| .CLOCK(CLOCK), |
| .STANDBY(STANDBY), |
| .SLEEP(SLEEP), |
| .POWEROFF(POWEROFF), |
| .DATAOUT(DATAOUT) |
| ); |
| """ % (addr_net, din_net, mwren_net), file=f) |
| print("endmodule",file=f) |
| pin2net = {} |
| with open("./work_spram/spram.pcf","w") as f: |
| temp_pins = list(pins) |
| for sig in spram_signals: |
| if sig.startswith("ADDRESS") and n != "ADDRESS": |
| continue |
| if sig.startswith("DATAIN") and n != "DATAIN": |
| continue |
| if sig.startswith("MASKWREN") and n != "MASKWREN": |
| continue |
| if sig.startswith("DATAOUT") and n != "DATAOUT" and sig != "DATAOUT[0]": |
| continue |
| |
| if len(temp_pins) == 0: |
| sys.stderr.write("ERROR: no remaining pins to alloc") |
| sys.exit(1) |
| |
| pin = temp_pins.pop() |
| pin2net[pin] = sig |
| print("set_io %s %s" % (sig, pin), file=f) |
| print("set_location spram_i %d %d %d" % loc, file=f) |
| retval = os.system("bash ../../icecube.sh -" + device + " ./work_spram/spram.v > ./work_spram/icecube.log 2>&1") |
| if retval != 0: |
| sys.stderr.write('ERROR: icecube returned non-zero error code\n') |
| sys.exit(1) |
| retval = os.system("../../../icebox/icebox_explain.py ./work_spram/spram.asc > ./work_spram/spram.exp") |
| if retval != 0: |
| sys.stderr.write('ERROR: icebox_explain returned non-zero error code\n') |
| sys.exit(1) |
| retval = os.system("../../../icebox/icebox_vlog.py -l ./work_spram/spram.asc > ./work_spram/spram.vlog") |
| if retval != 0: |
| sys.stderr.write('ERROR: icebox_vlog returned non-zero error code\n') |
| sys.exit(1) |
| with open("./work_spram/spram.vlog", "r") as f: |
| parse_vlog(f, pin2net, net_map) |
| bits = [] |
| with open("./work_spram/spram.exp", "r") as f: |
| bits = parse_exp(f) |
| net_map["SPRAM_EN"].update(bits) |
| spram_data[loc] = net_map |
| |
| with open(device + "_spram_data.txt", "w") as f: |
| for loc in spram_data: |
| print("\t(%d, %d, %d): {" % loc, file=f) |
| data = spram_data[loc] |
| for net in sorted(data): |
| cnets = [] |
| for cnet in data[net]: |
| cnets.append("(%d, %d, \"%s\")" % cnet) |
| print("\t\t%s %s, " % (("\"" + net.replace("[","_").replace("]","") + "\":").ljust(24), " ".join(cnets)), file=f) |
| print("\t},", file=f) |