1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
|
#!/usr/bin/env python3
import os, sys, re
device = "u4k"
pins = "2 3 4 6 9 10 11 12 13 14 15 16 17 18 19 20 21 23 25 26 27 28 31 32 34 35 36 37 38 42 43 44 45 46 47 48".split()
#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 is the master IP reverse engineering script for three similar IPs: I2C, SPI
ip_types = ["I2C", "SPI"]
ip_locs = { }
ip_locs["I2C"] = [(0, 21, 0), (25, 21, 0)]
ip_locs["SPI"] = [(0, 0, 0), (25, 0, 1)]
#spram_locs = [(0, 0, 1)]
ip_data = { }
#up5k
# ip_types = ["I2C", "SPI", "LEDDA_IP"]
# ip_locs = { }
# ip_locs["I2C"] = [(0, 31, 0), (25, 31, 0)]
# ip_locs["SPI"] = [(0, 0, 0), (25, 0, 1)]
# ip_locs["LEDDA_IP"] = [(0, 31, 2)]
# #spram_locs = [(0, 0, 1)]
# ip_data = { }
#signals[x][0] -> inputs, signals[x][1] ->outputs
ip_signals = {}
ip_signals["I2C"] = [["SBCLKI", "SBRWI", "SBSTBI", "SCLI", "SDAI"],
["SBACKO", "I2CIRQ", "I2CWKUP", "SCLO", "SCLOE", "SDAO", "SDAOE"]]
ip_signals["SPI"] = [["SBCLKI", "SBRWI", "SBSTBI", "MI", "SI", "SCKI", "SCSNI"],
["SBACKO", "SPIIRQ", "SPIWKUP", "SO", "SOE", "MO", "MOE", "SCKO", "SCKOE"]]
fixed_cbits = {}
fixed_cbits[("I2C", (0, 21, 0))] = ["BUS_ADDR74_0", "I2C_SLAVE_INIT_ADDR_0"]
fixed_cbits[("I2C", (25, 21, 0))] = ["BUS_ADDR74_0", "BUS_ADDR74_1", "I2C_SLAVE_INIT_ADDR_1"]
fixed_cbits[("SPI", (0, 0, 0))] = []
fixed_cbits[("SPI", (25, 0, 1))] = ["BUS_ADDR74_1"] # WARNING: this is documented as BUS_ADDR74_0, but this is wrong and will cause icecube to fail. May be the same across devices
#up5k
# fixed_cbits[("I2C", (0, 31, 0))] = ["BUS_ADDR74_0", "I2C_SLAVE_INIT_ADDR_0"]
# fixed_cbits[("I2C", (25, 31, 0))] = ["BUS_ADDR74_0", "BUS_ADDR74_1", "I2C_SLAVE_INIT_ADDR_1"]
# fixed_cbits[("SPI", (0, 10, 0))] = []
# fixed_cbits[("SPI", (25, 10, 1))] = ["BUS_ADDR74_1"] # WARNING: this is documented as BUS_ADDR74_0, but this is wrong and will cause icecube to fail. May be the same across devices
fuzz_cbits = {}
fuzz_cbits["I2C"] = ["SDA_INPUT_DELAYED", "SDA_OUTPUT_DELAYED"]
# Don't add slave address to the list, despite confusing primitive declaration,
# it's only set in registers not the bitstream
#for i in range(2, 10):
#fuzz_cbits["I2C"].append("I2C_SLAVE_INIT_ADDR_%d" % i)
for i in range(8):
ip_signals["I2C"][0].append("SBADRI%d" % i)
ip_signals["SPI"][0].append("SBADRI%d" % i)
for i in range(8):
ip_signals["I2C"][0].append("SBDATI%d" % i)
ip_signals["SPI"][0].append("SBDATI%d" % i)
for i in range(8):
ip_signals["I2C"][1].append("SBDATO%d" % i)
ip_signals["SPI"][1].append("SBDATO%d" % i)
for i in range(4):
ip_signals["SPI"][1].append("MCSNO%d" % i)
ip_signals["SPI"][1].append("MCSNOE%d" % i)
fuzz_net_options = {}
fuzz_net_options["I2C"] = ["SBADRI", "SBDATI", "SBDATO"]
fuzz_net_options["SPI"] = ["SBADRI", "SBDATI", "SBDATO", "MCSN"]
available_cbits = {}
available_cbits["I2C"] = [("BUS_ADDR74", 4), ("I2C_SLAVE_INIT_ADDR", 10)]
available_cbits["SPI"] = [("BUS_ADDR74", 4)]
# Return a param value in "Lattice style"
def get_param_value(param_size, param_name, set_cbits):
val = "\"0b"
for i in range(param_size):
if param_name + "_" + str((param_size - 1) - i) in set_cbits:
val += "1"
else:
val += "0"
val += "\""
return val
# Build the output files for a given IP and config, returning
# the pin2net map
def make_ip(ip_type, ip_loc, fuzz_opt, set_cbits):
used_inputs = [ ]
used_outputs = [ ]
for insig in ip_signals[ip_type][0]:
ignore = False
for o in fuzz_net_options[ip_type]:
if o != fuzz_opt and insig.startswith(o):
ignore = True
if not ignore:
used_inputs.append(insig)
for outsig in ip_signals[ip_type][1]:
ignore = False
for o in fuzz_net_options[ip_type]:
if o != fuzz_opt and outsig.startswith(o):
ignore = True
if not ignore:
used_outputs.append(outsig)
all_sigs = used_inputs + used_outputs
all_cbits = set()
all_cbits.update(set_cbits)
if (ip_type, ip_loc) in fixed_cbits:
all_cbits.update(fixed_cbits[(ip_type, ip_loc)])
with open("./work_ip/ip.v", "w") as f:
print("module top(", file=f)
for s in used_inputs:
print("input %s," % s, file=f)
for s in used_outputs[:-1]:
print("output %s," % s, file=f)
print("output %s);" % used_outputs[-1], file=f)
print("SB_%s" % ip_type, file=f)
if ip_type in available_cbits:
print("\t#(", file=f)
for p in available_cbits[ip_type]:
name, width = p
comma = "," if p != available_cbits[ip_type][-1] else ""
print("\t\t.%s(%s)%s" % (name, get_param_value(width, name, all_cbits), comma), file=f)
print("\t)", file=f)
print("\tip_inst (",file=f)
for sig in all_sigs[:-1]:
print("\t\t.%s(%s)," % (sig, sig), file=f)
print("\t\t.%s(%s)" % (all_sigs[-1], all_sigs[-1]), file=f)
print("\t)", file=f)
if "SDA_INPUT_DELAYED" in all_cbits:
print("\t/* synthesis SDA_INPUT_DELAYED=1 */", file=f)
else:
print("\t/* synthesis SDA_INPUT_DELAYED=0 */", file=f)
if "SDA_OUTPUT_DELAYED" in all_cbits:
print("\t/* synthesis SDA_OUTPUT_DELAYED=1 */", file=f)
else:
print("\t/* synthesis SDA_OUTPUT_DELAYED=0 */", file=f)
print(";", file=f)
print("endmodule", file=f)
pin2net = {}
with open("./work_ip/ip.pcf","w") as f:
temp_pins = list(pins)
for sig in all_sigs:
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 ip_inst %d %d %d" % ip_loc, file=f)
return pin2net
def check_for_pin_assignment(pin):
out = None
with open("./work_ip/ip.vlog", "r") as f:
for l in f:
if l.startswith("assign pin_{}".format(pin)):
rhs = l.split("=")
o_cen = rhs[1].split(" ")[1]
out = rhs[1].split(" ")[3]
return out
#Parse the output of an icebox vlog file to determine connectivity
def parse_vlog(f, pin2net, net_map):
wires_to_check = dict()
current_net = None
for line in f:
if line == "\n":
current_net = None
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
#search for assignment
data_input = check_for_pin_assignment(pin)
if data_input:
wires_to_check[data_input] = pin
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))
f.seek(0)
for line in f:
if line == "\n":
current_net = None
m = re.match(r"wire ([a-zA-Z0-9]+);", line)
if m:
net = m.group(1)
if net in wires_to_check:
pin = wires_to_check[net]
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":
bits.add((current_x, current_y, splitline[1].strip()))
return bits
if not os.path.exists("./work_ip"):
os.mkdir("./work_ip")
for ip in ip_types:
ip_data[ip] = {}
for loc in ip_locs[ip]:
x, y, z = loc
net_cbit_map = {}
init_cbits = []
for sig in ip_signals[ip][0]:
net_cbit_map[sig] = set()
for sig in ip_signals[ip][1]:
net_cbit_map[sig] = set()
first = True
for state in ["FUZZ_NETS", "FUZZ_CBITS"]:
fuzz_options = None
if state == "FUZZ_NETS":
fuzz_options = fuzz_net_options[ip]
else:
if ip in fuzz_cbits:
fuzz_options = fuzz_cbits[ip]
else:
fuzz_options = []
for n in fuzz_options:
# if n != "SBDATO":
# continue
print("Fuzzing %s (%d, %d, %d) %s" % (ip, x, y, z, n))
fuzz_nets = fuzz_net_options[ip][0]
if state == "FUZZ_NETS":
fuzz_nets = n
set_cbits = set()
if state == "FUZZ_CBITS":
set_cbits.add(n)
pin2net = make_ip(ip, loc, fuzz_nets, set_cbits)
retval = os.system("bash ../../icecube.sh -" + device + " ./work_ip/ip.v > ./work_ip/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_ip/ip.asc > ./work_ip/ip.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_ip/ip.asc > ./work_ip/ip.vlog")
if retval != 0:
sys.stderr.write('ERROR: icebox_vlog returned non-zero error code\n')
sys.exit(1)
with open("./work_ip/ip.vlog", "r") as f:
parse_vlog(f, pin2net, net_cbit_map)
bits = []
with open("./work_ip/ip.exp", "r") as f:
bits = parse_exp(f)
if first:
idx = 0
for bit in bits:
init_cbits.append(bit)
if len(bits) == 1:
net_cbit_map[ip + "_ENABLE"] = [bit]
else:
net_cbit_map[ip + "_ENABLE_" + str(idx)] = [bit]
idx += 1
for bit in init_cbits:
if bit not in bits:
bx, by, bn = bit
print('WARNING: while fuzzing %s (%d, %d, %d) bit (%d, %d, %s) has unknown function (not always set)' %
(ip, x, y, z, bx, by, bn))
new_bits = []
for bit in bits:
if bit not in init_cbits:
new_bits.append(bit)
if state == "FUZZ_NETS" and len(new_bits) != 0:
for bit in new_bits:
bx, by, bn = bit
print('WARNING: while fuzzing %s (%d, %d, %d) bit (%d, %d, %s) has unknown function (not always set)' %
(ip, x, y, z, bx, by, bn))
elif state == "FUZZ_CBITS":
if len(new_bits) == 0:
print('WARNING: while fuzzing %s (%d, %d, %d) param %s causes no change' %
(ip, x, y, z, n))
else:
idx = 0
for bit in new_bits:
if len(new_bits) == 1:
net_cbit_map[n] = [bit]
else:
net_cbit_map[n + "_" + str(idx)] = [bit]
idx += 1
first = False
# if n == "SBDATO":
# exit()
ip_data[ip][loc] = net_cbit_map
with open(device + "_" + ip + "_data.txt", "w") as f:
for loc in ip_data[ip]:
x, y, z = loc
print("\t(\"%s\", (%d, %d, %d)): {" % (ip, x, y, z), file=f)
data = ip_data[ip][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)
|