1#!/usr/bin/env python3
2
3import os, sys, re
4
5device = "up5k"
6
7pins = "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()
8
9# This script is designed to determine the routing of 5k SPRAM signals,
10# and the location of the enable config bits
11
12spram_locs = [(0, 0, 1), (0, 0, 2), (25, 0, 3), (25, 0, 4)]
13#spram_locs = [(0, 0, 1)]
14spram_data = { }
15
16spram_signals = ["WREN", "CHIPSELECT", "CLOCK", "STANDBY", "SLEEP", "POWEROFF"]
17
18for i in range(14):
19    spram_signals.append("ADDRESS[%d]" % i)
20
21for i in range(16):
22    spram_signals.append("DATAIN[%d]" % i)
23
24for i in range(16):
25    spram_signals.append("DATAOUT[%d]" % i)
26
27for i in range(4):
28    spram_signals.append("MASKWREN[%d]" % i)
29
30fuzz_options = ["ADDRESS", "DATAIN", "MASKWREN", "DATAOUT"]
31
32#Parse the output of an icebox vlog file to determine connectivity
33def parse_vlog(f, pin2net, net_map):
34    current_net = None
35
36    for line in f:
37        m = re.match(r"wire ([a-zA-Z0-9_]+);", line)
38        if m:
39            net = m.group(1)
40            mp = re.match(r"pin_([a-zA-Z0-9]+)", net)
41            if mp:
42                pin = mp.group(1)
43                if pin in pin2net:
44                    current_net = pin2net[pin]
45                else:
46                    current_net = None
47            else:
48                current_net = None
49        elif current_net is not None:
50            m = re.match(r"// \((\d+), (\d+), '([a-zA-Z0-9_/]+)'\)", line)
51            if m:
52                x = int(m.group(1))
53                y = int(m.group(2))
54                net = m.group(3)
55                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")):
56                    net_map[current_net].add((x, y, net))
57def parse_exp(f):
58    current_x = 0
59    current_y = 0
60    bits = set()
61    for line in f:
62        splitline = line.split(' ')
63        if splitline[0].endswith("_tile"):
64            current_x = int(splitline[1])
65            current_y = int(splitline[2])
66        elif splitline[0] == "IpConfig":
67            if splitline[1][:5] == "CBIT_":
68                bitidx = int(splitline[1][5:])
69                bits.add((current_x, current_y, splitline[1].strip()))
70    return bits
71
72if not os.path.exists("./work_spram"):
73    os.mkdir("./work_spram")
74
75for loc in spram_locs:
76    x, y, z = loc
77    net_map = {}
78    for sig in spram_signals:
79        net_map[sig] = set()
80    net_map["SPRAM_EN"] = set() # actually a CBIT not a net
81
82    for n in fuzz_options:
83        with open("./work_spram/spram.v","w") as f:
84            print("""
85            module top(
86            input WREN,
87            input CHIPSELECT,
88            input CLOCK,
89            input STANDBY,
90            input SLEEP,
91            input POWEROFF,
92            """, file=f)
93            if n == "ADDRESS":
94                print("\t\t\tinput [13:0] ADDRESS,", file=f)
95            if n == "DATAIN":
96                print("\t\t\tinput [15:0] DATAIN,", file=f)
97            if n == "MASKWREN":
98                print("\t\t\tinput [3:0] MASKWREN,", file=f)
99            if n == "DATAOUT":
100                print("\t\t\toutput [15:0] DATAOUT);", file=f)
101            else:
102                print("\t\t\toutput [0:0] DATAOUT);", file=f) #some dataout is always required to prevent optimisation away
103
104            addr_net = "ADDRESS" if n == "ADDRESS" else ""
105            din_net = "DATAIN" if n == "DATAIN" else ""
106            mwren_net = "MASKWREN" if n == "MASKWREN" else ""
107
108            print("""
109            SB_SPRAM256KA spram_i
110              (
111                .ADDRESS(%s),
112                .DATAIN(%s),
113                .MASKWREN(%s),
114                .WREN(WREN),
115                .CHIPSELECT(CHIPSELECT),
116                .CLOCK(CLOCK),
117                .STANDBY(STANDBY),
118                .SLEEP(SLEEP),
119                .POWEROFF(POWEROFF),
120                .DATAOUT(DATAOUT)
121              );
122            """ % (addr_net, din_net, mwren_net), file=f)
123            print("endmodule",file=f)
124        pin2net = {}
125        with open("./work_spram/spram.pcf","w") as f:
126            temp_pins = list(pins)
127            for sig in spram_signals:
128                if sig.startswith("ADDRESS") and n != "ADDRESS":
129                    continue
130                if sig.startswith("DATAIN") and n != "DATAIN":
131                    continue
132                if sig.startswith("MASKWREN") and n != "MASKWREN":
133                    continue
134                if sig.startswith("DATAOUT") and n != "DATAOUT" and sig != "DATAOUT[0]":
135                    continue
136
137                if len(temp_pins) == 0:
138                    sys.stderr.write("ERROR: no remaining pins to alloc")
139                    sys.exit(1)
140
141                pin = temp_pins.pop()
142                pin2net[pin] = sig
143                print("set_io %s %s" % (sig, pin), file=f)
144            print("set_location spram_i %d %d %d" % loc, file=f)
145        retval = os.system("bash ../../icecube.sh -" + device + " ./work_spram/spram.v > ./work_spram/icecube.log 2>&1")
146        if retval != 0:
147            sys.stderr.write('ERROR: icecube returned non-zero error code\n')
148            sys.exit(1)
149        retval = os.system("../../../icebox/icebox_explain.py ./work_spram/spram.asc > ./work_spram/spram.exp")
150        if retval != 0:
151            sys.stderr.write('ERROR: icebox_explain returned non-zero error code\n')
152            sys.exit(1)
153        retval = os.system("../../../icebox/icebox_vlog.py -l ./work_spram/spram.asc > ./work_spram/spram.vlog")
154        if retval != 0:
155            sys.stderr.write('ERROR: icebox_vlog returned non-zero error code\n')
156            sys.exit(1)
157        with open("./work_spram/spram.vlog", "r") as f:
158            parse_vlog(f, pin2net, net_map)
159        bits = []
160        with open("./work_spram/spram.exp", "r") as f:
161            bits = parse_exp(f)
162        net_map["SPRAM_EN"].update(bits)
163    spram_data[loc] = net_map
164
165with open(device + "_spram_data.txt", "w") as f:
166    for loc in spram_data:
167        print("\t(%d, %d, %d): {" % loc, file=f)
168        data = spram_data[loc]
169        for net in sorted(data):
170            cnets = []
171            for cnet in data[net]:
172                cnets.append("(%d, %d, \"%s\")" % cnet)
173            print("\t\t%s %s, " % (("\"" + net.replace("[","_").replace("]","") + "\":").ljust(24), " ".join(cnets)), file=f)
174        print("\t},", file=f)
175