Skip to content

Commit ec90ea2

Browse files
authored
Add the long wires (#107)
* Add the long wires It becomes possible to use fast wires that go into each cell of the chip. To do this, just mark the wire in the .CST file as follows: CLOCK_LOC "auto_net" BUFS; CLOCK_LOC "net_with_number" BUFS[6]; the first option is used when the wire number used is indifferent (and there are only 8 of them), the second option specifies a number. One can use long wires to OBUF, but other IO primitives will probably switch to normal routing for now. See doc/longwires.md Signed-off-by: YRabbit <[email protected]> * Use data from the clock fuzzer Signed-off-by: YRabbit <[email protected]> * GW1NZ-1 hack Signed-off-by: YRabbit <[email protected]> * Separate example for long wires And also remove unnecessary examples Signed-off-by: YRabbit <[email protected]> * Remove the extra CR from Makefile Signed-off-by: YRabbit <[email protected]>
1 parent a090608 commit ec90ea2

23 files changed

+429
-225
lines changed

apycula/clock_fuzzer.py

+133-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from apycula import chipdb
1212
from apycula import fuse_h4x
1313
from apycula import gowin_unpack
14-
from apycula.wirenames import wirenames
14+
from apycula.wirenames import wirenames, clknames, wirenumbers, clknumbers
1515

1616
def dff(mod, cst, row, col, clk=None):
1717
"make a dff with optional clock"
@@ -307,6 +307,134 @@ def branch_aliases(quads, clks):
307307

308308
return aliases
309309

310+
def get_bufs_bits(fse, ttyp, win, wout):
311+
wi = clknumbers[win]
312+
wo = clknumbers[wout]
313+
fuses = []
314+
for rec in fse[ttyp]['wire'][38]:
315+
if rec[0] == wi and rec[1] == wo:
316+
fuses = chipdb.unpad(rec[2:])
317+
break
318+
return {fuse_h4x.fuse_lookup(fse, ttyp, f) for f in fuses}
319+
320+
# create aliases and pips for long wires
321+
def make_lw_aliases(fse, dat, db, quads, clks):
322+
# type 81, 82, 83, 84 tiles have source muxes
323+
center_row, col82 = dat['center']
324+
center_row -= 1
325+
last_row = db.rows - 1
326+
col82 -= 1
327+
col81 = col82 - 1
328+
col83 = col82 + 1
329+
col84 = col82 + 2
330+
# type 91 and 92 tiles activate the quadrants
331+
# XXX GW1NS-4 have different types
332+
type91 = fse['header']['grid'][61][0][col82]
333+
type92 = fse['header']['grid'][61][last_row][col82]
334+
col91 = col82
335+
has_bottom_quadrants = len(quads) > 2
336+
337+
# quadrants activation bels
338+
# delete direct pips "long wire->spine" because the artificial bel will be used,
339+
# which replaces this direct pip
340+
rows = {(0, 'T', type91)}
341+
if has_bottom_quadrants:
342+
rows.update({ (last_row, 'B', type92) })
343+
for row, half, ttyp in rows:
344+
for idx in range(8):
345+
bel = db.grid[row][col91].bels.setdefault(f'BUFS{idx}', chipdb.Bel())
346+
del db.grid[row][col91].clock_pips[f'LWSPINE{half}L{idx}']
347+
del db.grid[row][col91].clock_pips[f'LWSPINE{half}R{idx}']
348+
src = f'LW{half}{idx}'
349+
db.grid[row][col91].clock_pips.setdefault(f'LWI{idx}', {})[src] = {}
350+
db.grid[row][col91].clock_pips.setdefault(f'LWSPINE{half}L{idx}', {})[f'LWO{idx}'] = {}
351+
bel.flags['L'] = get_bufs_bits(fse, ttyp, f'LW{half}{idx}', f'LWSPINE{half}L{idx}')
352+
db.grid[row][col91].clock_pips.setdefault(f'LWSPINE{half}R{idx}', {})[f'LWO{idx}'] = {}
353+
bel.flags['R'] = get_bufs_bits(fse, ttyp, f'LW{half}{idx}', f'LWSPINE{half}R{idx}')
354+
bel.portmap['I'] = f'LWI{idx}'
355+
bel.portmap['O'] = f'LWO{idx}'
356+
# aliases for long wire origins (center muxes)
357+
# If we have only two quadrants, then do not create aliases in the bottom tile 92,
358+
# thereby excluding these wires from the candidates for routing
359+
if half == 'B' and not has_bottom_quadrants:
360+
continue
361+
if half == 'T':
362+
if idx != 7:
363+
db.aliases.update({(row, col82, src) : (center_row, col82, src)})
364+
else:
365+
db.aliases.update({(row, col82, src) : (center_row, col81, src)})
366+
else:
367+
if idx != 7:
368+
db.aliases.update({(row, col82, src) : (center_row, col83, src)})
369+
else:
370+
db.aliases.update({(row, col82, src) : (center_row, col84, src)})
371+
# branches
372+
# {lw#: {tap_col: {cols}}
373+
taps = {}
374+
lw_taps = [-1, -1, -1, -1]
375+
any_mux = list(clks.keys())[0]
376+
for gclk in range(4):
377+
if gclk not in clks[any_mux].keys():
378+
# XXX
379+
continue
380+
lw_taps[gclk] = min(clks[any_mux][gclk].keys())
381+
382+
if -1 in lw_taps:
383+
# XXX GW1NZ-1 temporary hack
384+
if lw_taps.count(-1) != 1:
385+
raise Exception("Inconsistent clock tap columns, something is went wrong with the clock detection.")
386+
else:
387+
lw_taps[lw_taps.index(-1)] = 0 + 1 + 2 + 3 - 1 - sum(lw_taps)
388+
print(" lw_taps = ", lw_taps)
389+
390+
for lw in range(4):
391+
tap_col = lw_taps[lw]
392+
for col in range(db.cols):
393+
if (col > tap_col + 2) and (tap_col + 4 < db.cols):
394+
tap_col += 4
395+
taps.setdefault(lw, {}).setdefault(tap_col, set()).add(col)
396+
397+
for row in range(db.rows):
398+
for lw, tap_desc in taps.items():
399+
for tap_col, cols in tap_desc.items():
400+
tap_row = 0
401+
if row > (center_row * 2):
402+
tap_row = last_row
403+
db.aliases.update({(row, tap_col, 'LT01') : (tap_row, tap_col, 'LT02')})
404+
db.aliases.update({(row, tap_col, 'LT04') : (tap_row, tap_col, 'LT13')})
405+
for col in cols:
406+
db.aliases.update({(row, col, f'LB{lw}1') : (row, tap_col, f'LBO0')})
407+
db.aliases.update({(row, col, f'LB{lw + 4}1') : (row, tap_col, f'LBO1')})
408+
409+
# tap sources
410+
rows = { (0, 'T') }
411+
if has_bottom_quadrants:
412+
rows.update({ (last_row, 'B') })
413+
for row, qd in rows:
414+
for lw, tap_desc in taps.items():
415+
for tap_col, cols in tap_desc.items():
416+
if tap_col <= col91:
417+
half = 'L'
418+
else:
419+
half = 'R'
420+
db.aliases.update({ (row, tap_col, 'SS00') : (row, col91, f'LWSPINE{qd}{half}{lw}') })
421+
db.aliases.update({ (row, tap_col, 'SS40') : (row, col91, f'LWSPINE{qd}{half}{lw + 4}') })
422+
# XXX remove all pips except SS00 and SS40
423+
pip2keep = {'SS00', 'SS40'}
424+
for tp in ['LT02', 'LT13']:
425+
for pip in [p for p in db.grid[row][tap_col].pips[tp] if p not in pip2keep]:
426+
del db.grid[row][tap_col].pips[tp][pip]
427+
# logic entries
428+
srcs = {}
429+
for i, src in enumerate(dat['UfbIns']):
430+
row, col, pip = src
431+
if pip == 126: # CLK2
432+
db.aliases.update({ (center_row, col82, f'UNK{i + 104}'): (row - 1, col -1, 'CLK2')})
433+
db.aliases.update({ (center_row, col81, f'UNK{i + 104}'): (row - 1, col -1, 'CLK2')})
434+
if has_bottom_quadrants:
435+
db.aliases.update({ (center_row, col83, f'UNK{i + 104}'): (row - 1, col -1, 'CLK2')})
436+
db.aliases.update({ (center_row, col84, f'UNK{i + 104}'): (row - 1, col -1, 'CLK2')})
437+
310438
if __name__ == "__main__":
311439
quads = quadrants()
312440

@@ -343,6 +471,10 @@ def branch_aliases(quads, clks):
343471
db.aliases.update(ta)
344472
db.aliases.update(ba)
345473

474+
# long wires
475+
make_lw_aliases(fse, dat, db, quads, clks)
476+
477+
346478
with open(f"{tiled_fuzzer.device}_stage2.pickle", 'wb') as f:
347479
pickle.dump(db, f)
348480

apycula/gowin_pack.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def sanitize_name(name):
2828

2929
def get_bels(data):
3030
later = []
31-
belre = re.compile(r"R(\d+)C(\d+)_(?:GSR|SLICE|IOB|MUX2_LUT5|MUX2_LUT6|MUX2_LUT7|MUX2_LUT8|ODDR|OSC[ZFH]?)(\w*)")
31+
belre = re.compile(r"R(\d+)C(\d+)_(?:GSR|SLICE|IOB|MUX2_LUT5|MUX2_LUT6|MUX2_LUT7|MUX2_LUT8|ODDR|OSC[ZFH]?|BUFS)(\w*)")
3232
for cellname, cell in data['modules']['top']['cells'].items():
3333
bel = cell['attributes']['NEXTPNR_BEL']
3434
if bel in {"VCC", "GND"}: continue
@@ -91,6 +91,14 @@ def place(db, tilemap, bels, cst, args):
9191
tile = tilemap[(row-1, col-1)]
9292
if typ == "GSR":
9393
pass
94+
if typ == "BUFS":
95+
# fuses must be reset in order to activate so remove them
96+
bits2zero = set()
97+
for fuses in [fuses for fuses in parms.keys() if fuses in {'L', 'R'}]:
98+
bits2zero.update(tiledata.bels[f'BUFS{num}'].flags[fuses])
99+
for r, c in bits2zero:
100+
tile[r][c] = 0
101+
94102
if typ in {'OSC', 'OSCZ', 'OSCF', 'OSCH'}:
95103
divisor = int(parms['FREQ_DIV'], 2)
96104
if divisor % 2 == 1:

apycula/gowin_unpack.py

+20-8
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,13 @@ def _io_mode_sort_func(mode):
4141

4242
# noiostd --- this is the case when the function is called
4343
# with iostd by default, e.g. from the clock fuzzer
44-
# With normal gowun_unpack io standard is determined first and it is known.
44+
# With normal gowin_unpack io standard is determined first and it is known.
4545
def parse_tile_(db, row, col, tile, default=True, noalias=False, noiostd = True):
4646
# TLVDS takes two BUF bels, so skip the B bels.
4747
skip_bels = set()
4848
#print((row, col))
4949
tiledata = db.grid[row][col]
50+
clock_pips = {}
5051
bels = {}
5152
for name, bel in tiledata.bels.items():
5253
if name[0:3] == "IOB":
@@ -61,15 +62,15 @@ def parse_tile_(db, row, col, tile, default=True, noalias=False, noiostd = True)
6162
# Here we don't use a mask common to all modes (it didn't work),
6263
# instead we try the longest bit sequence first.
6364
for mode, mode_rec in sorted(bel.iob_flags[iostd].items(),
64-
key = _io_mode_sort_func, reverse = True):
65-
# print(mode, mode_rec.decode_bits)
65+
key = _io_mode_sort_func, reverse = True):
66+
#print(mode, mode_rec.decode_bits)
6667
mode_bits = {(row, col)
6768
for row, col in mode_rec.decode_bits
6869
if tile[row][col] == 1}
69-
# print("read", mode_bits)
70+
#print("read", mode_bits)
7071
if mode_rec.decode_bits == mode_bits:
7172
zeros = zero_bits(mode, bel.iob_flags[iostd])
72-
# print("zeros", zeros)
73+
#print("zeros", zeros)
7374
used_bits = {tile[row][col] for row, col in zeros}
7475
if not any(used_bits):
7576
bels.setdefault(name, set()).add(mode)
@@ -92,7 +93,7 @@ def parse_tile_(db, row, col, tile, default=True, noalias=False, noiostd = True)
9293
for row, col in bel.mode_bits
9394
if tile[row][col] == 1}
9495
#print(name, sorted(bel.mode_bits))
95-
#print("read", sorted(mode_bits))
96+
#print("read mode:", sorted(mode_bits))
9697
for mode, bits in bel.modes.items():
9798
#print(mode, sorted(bits))
9899
if bits == mode_bits and (default or bits):
@@ -117,6 +118,17 @@ def parse_tile_(db, row, col, tile, default=True, noalias=False, noiostd = True)
117118
if name == "RAM16" and not name in bels:
118119
continue
119120
bels.setdefault(name, set()).add(flag)
121+
# revert BUFS flags
122+
if name.startswith('BUFS'):
123+
flags = bels.get(name, set()) ^ {'R', 'L'}
124+
if flags:
125+
num = name[4:]
126+
half = 'T'
127+
if row != 0:
128+
half = 'B'
129+
for qd in flags:
130+
clock_pips[f'LWSPINE{half}{qd}{num}'] = f'LW{half}{num}'
131+
#print("flags:", sorted(bels.get(name, set())))
120132

121133
pips = {}
122134
for dest, srcs in tiledata.pips.items():
@@ -129,7 +141,6 @@ def parse_tile_(db, row, col, tile, default=True, noalias=False, noiostd = True)
129141
if bits == used_bits and (default or bits):
130142
pips[dest] = src
131143

132-
clock_pips = {}
133144
for dest, srcs in tiledata.clock_pips.items():
134145
pip_bits = set().union(*srcs.values())
135146
used_bits = {(row, col)
@@ -139,6 +150,7 @@ def parse_tile_(db, row, col, tile, default=True, noalias=False, noiostd = True)
139150
# only report connection aliased to by a spine
140151
if bits == used_bits and (noalias or (row, col, src) in db.aliases):
141152
clock_pips[dest] = src
153+
142154
return {name: bel for name, bel in bels.items() if name not in skip_bels}, pips, clock_pips
143155

144156

@@ -268,7 +280,7 @@ def tile2verilog(dbrow, dbcol, bels, pips, clock_pips, mod, cst, db):
268280
mod.wires.update({srcg, destg})
269281
mod.assigns.append((destg, srcg))
270282

271-
belre = re.compile(r"(IOB|LUT|DFF|BANK|CFG|ALU|RAM16|ODDR|OSC[ZFH]?)(\w*)")
283+
belre = re.compile(r"(IOB|LUT|DFF|BANK|CFG|ALU|RAM16|ODDR|OSC[ZFH]?|BUFS)(\w*)")
272284
if have_iologic(bels):
273285
bels_items = move_iologic(bels)
274286
else:

apycula/tiled_fuzzer.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from apycula import bslib
2020
from apycula import pindef
2121
from apycula import fuse_h4x
22-
from apycula.wirenames import wirenames, clknames
22+
from apycula.wirenames import wirenames, clknames, wirenumbers, clknumbers
2323
#TODO proper API
2424
#from apycula import dat19_h4x
2525
from apycula import tm_h4x
@@ -118,7 +118,7 @@ def recode_idx_gw1nz_1(idx):
118118
"package": "UBGA332",
119119
"device": "GW1N-9C-UBGA332-6",
120120
"partnumber": "GW1N-LV9UG332C6/I5",
121-
"recode_idx": recode_idx_gw1n9, # TODO: recheck
121+
"recode_idx": recode_idx_gw1n9,
122122
},
123123
"GW1N-4": {
124124
"package": "PBGA256",
@@ -136,10 +136,12 @@ def recode_idx_gw1nz_1(idx):
136136
"package": "QFN48",
137137
"device": "GW1NZ-1-QFN48-6",
138138
"partnumber": "GW1NZ-LV1QN48C6/I5",
139-
"recode_idx": recode_idx_gw1nz_1, # TODO: check
139+
"recode_idx": recode_idx_gw1nz_1,
140140
},
141141
}[device]
142142

143+
144+
143145
name_idx = 0
144146
def make_name(bel, typ):
145147
global name_idx
@@ -1117,10 +1119,6 @@ def run_pnr(mod, constr, config):
11171119
diff_cap_info = pindef.get_diff_cap_info(device, params['package'], True)
11181120
fse_diff_iob(fse, db, pin_locations, diff_cap_info);
11191121

1120-
# diff IOB
1121-
diff_cap_info = pindef.get_diff_cap_info(device, params['package'], True)
1122-
fse_diff_iob(fse, db, pin_locations, diff_cap_info);
1123-
11241122
# bank modes
11251123
fse_banks(fse, db, corners)
11261124

apycula/wirenames.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,17 @@
2323
279: "LT00", 280: "LT10", 281: "LT20", 282: "LT30", 283: "LT02", 284: "LT13", 285: "LT01", 286: "LT04", 287: "LBO0", 288: "LBO1", 289: "SS00", 290: "SS40",
2424
291: "GT00", 292: "GT10", 293: "GBO0", 294: "GBO1", 295: "DI0", 296: "DI1", 297: "DI2", 298: "DI3", 299: "DI4", 300: "DI5", 301: "DI6", 302: "DI7",
2525
303: "CIN0", 304: "CIN1", 305: "CIN2", 306: "CIN3", 307: "CIN4", 308: "CIN5", 309: "COUT0", 310: "COUT1", 311: "COUT2", 312: "COUT3", 313: "COUT4", 314: "COUT5"}
26+
wirenames.update({n: f"LWSPINETL{n - 1001}" for n in range(1001, 1009)})
27+
wirenames.update({n: f"LWSPINETR{n - 1009}" for n in range(1009, 1017)})
28+
wirenames.update({n: f"LWSPINEBL{n - 1033}" for n in range(1033, 1041)})
29+
wirenames.update({n: f"LWSPINEBR{n - 1041}" for n in range(1041, 1049)})
2630

2731
wirenumbers = {v: k for k, v in wirenames.items()}
2832

2933
clknames = wirenames.copy()
3034
clknames.update({n: f"SPINE{n}" for n in range(32)})
31-
clknames.update({n: f"UNK{n}" for n in range(32, 261)})
35+
clknames.update({n: f"LWT{n - 32}" for n in range(32, 40)})
36+
clknames.update({n: f"LWB{n - 40}" for n in range(40, 48)})
37+
clknames.update({n: f"UNK{n}" for n in range(48, 261)})
38+
39+
clknumbers = {v: k for k, v in clknames.items()}

0 commit comments

Comments
 (0)