Skip to content

Commit bbd4b3d

Browse files
yrabbitpepijndevos
andauthored
Add initial PLL support (#133)
* Add initial PLL support This PLL only works on the GW1N-1 chip and understands only two fixed frequencies: 52 and 56 MHz. Dynamic divider tuning is not supported. Signed-off-by: YRabbit <[email protected]> * Add an example for PLL Only for owners of Tangnano board and oscilloscope. Signed-off-by: YRabbit <[email protected]> * Add unpacker for PLL At the moment it is not clear how it will behave with large chips with multiple PLLs and individual parts arranged differently in relation to each other, but for GW1N-1 it will work reasonably well as a pair to gowin_pack. And keep in mind that unpacking images made with vendor IDE will be wrong in terms of wiring - remember we are currently using routing over general purpose wires, not special (mostly unexplored) lines. Signed-off-by: YRabbit <[email protected]> * Add support for a more common chip The GW1N-1 and GW1NZ-1 have a similar PLL, but the board with the former chip is already very hard to buy, so let's experiment with a more affordable chip. Signed-off-by: YRabbit <[email protected]> * Add missing files for PLL examples Signed-off-by: YRabbit <[email protected]> Signed-off-by: YRabbit <[email protected]> Co-authored-by: Pepijn de Vos <[email protected]>
1 parent 6cced15 commit bbd4b3d

File tree

8 files changed

+1135
-9
lines changed

8 files changed

+1135
-9
lines changed

apycula/attrids.py

+603
Large diffs are not rendered by default.

apycula/chipdb.py

+179-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from dataclasses import dataclass, field
22
from typing import Dict, List, Set, Tuple, Union, ByteString
3+
from itertools import chain
34
import re
45
from functools import reduce
56
from collections import namedtuple
@@ -64,6 +65,10 @@ class Tile:
6465
for this specific tile type"""
6566
width: int
6667
height: int
68+
# At the time of packing/unpacking the information about the types of cells
69+
# is already lost, it is critical to work through the 'logicinfo' table so
70+
# store it.
71+
ttyp: int
6772
# a mapping from dest, source wire to bit coordinates
6873
pips: Dict[str, Dict[str, Set[Coord]]] = field(default_factory=dict)
6974
clock_pips: Dict[str, Dict[str, Set[Coord]]] = field(default_factory=dict)
@@ -83,6 +88,15 @@ class Device:
8388
cmd_hdr: List[ByteString] = field(default_factory=list)
8489
cmd_ftr: List[ByteString] = field(default_factory=list)
8590
template: np.ndarray = None
91+
# allowable values of bel attributes
92+
# {table_name: [(attr_id, attr_value)]}
93+
logicinfo: Dict[str, List[Tuple[int, int]]] = field(default_factory=dict)
94+
# fuses for a pair of the "features" (or pairs of parameter values)
95+
# {ttype: {table_name: {(feature_A, feature_B): {bits}}}
96+
shortval: Dict[int, Dict[str, Dict[Tuple[int, int], Set[Coord]]]] = field(default_factory=dict)
97+
# fuses for 16 of the "features"
98+
# {ttype: {table_name: {(feature_0, feature_1, ..., feature_15): {bits}}}
99+
longval: Dict[int, Dict[str, Dict[Tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int], Set[Coord]]]] = field(default_factory=dict)
86100
# always-connected dest, src aliases
87101
aliases: Dict[Tuple[int, int, str], Tuple[int, int, str]] = field(default_factory=dict)
88102

@@ -145,6 +159,17 @@ def fse_pips(fse, ttyp, table=2, wn=wirenames):
145159

146160
return pips
147161

162+
# make PLL bels
163+
def fse_pll(device, fse, ttyp):
164+
bels = {}
165+
# XXX use second grid in order to find PLL ttypes
166+
if device in ['GW1N-1', 'GW1NZ-1']:
167+
if ttyp == 89:
168+
bel = bels.setdefault('RPLLB', Bel())
169+
else:
170+
bel = bels.setdefault('RPLLA', Bel())
171+
return bels
172+
148173
# add the ALU mode
149174
# new_mode_bits: string like "0110000010011010"
150175
def add_alu_mode(base_mode, modes, lut, new_alu_mode, new_mode_bits):
@@ -319,25 +344,145 @@ def set_banks(fse, db):
319344
for rd in fse[ttyp]['longval'][37]:
320345
db.grid[row][col].bels.setdefault(f"BANK{rd[0]}", Bel())
321346

347+
_known_logic_tables = {
348+
8: 'DCS',
349+
9: 'GSR',
350+
10: 'IOLOGIC',
351+
11: 'IOB',
352+
12: 'SLICE',
353+
13: 'BSRAM',
354+
14: 'DSP',
355+
15: 'PLL',
356+
62: 'USB',
357+
}
358+
359+
_known_tables = {
360+
4: 'CONST',
361+
5: 'LUT',
362+
21: 'IOLOGICA',
363+
22: 'IOLOGICB',
364+
23: 'IOBA',
365+
24: 'IOBB',
366+
25: 'CLS0',
367+
26: 'CLS1',
368+
27: 'CLS2',
369+
28: 'CLS3',
370+
35: 'PLL',
371+
37: 'BANK',
372+
40: 'IOBC',
373+
41: 'IOBD',
374+
42: 'IOBE',
375+
43: 'IOBF',
376+
44: 'IOBG',
377+
45: 'IOBH',
378+
46: 'IOBI',
379+
47: 'IOBJ',
380+
53: 'DLLDEL0',
381+
54: 'DLLDEL1',
382+
56: 'DLL0',
383+
64: 'USB',
384+
66: 'EFLASH',
385+
68: 'ADC',
386+
80: 'DLL1',
387+
}
388+
389+
def fse_fill_logic_tables(dev, fse):
390+
# logicinfo
391+
for ltable in fse['header']['logicinfo'].keys():
392+
if ltable in _known_logic_tables.keys():
393+
table = dev.logicinfo.setdefault(_known_logic_tables[ltable], [])
394+
else:
395+
table = dev.logicinfo.setdefault(f"unknown_{ltable}", [])
396+
for attr, val, _ in fse['header']['logicinfo'][ltable]:
397+
table.append((attr, val))
398+
# shortval
399+
ttypes = {t for row in fse['header']['grid'][61] for t in row}
400+
for ttyp in ttypes:
401+
if 'shortval' in fse[ttyp].keys():
402+
ttyp_rec = dev.shortval.setdefault(ttyp, {})
403+
for stable in fse[ttyp]['shortval'].keys():
404+
if stable in _known_tables:
405+
table = ttyp_rec.setdefault(_known_tables[stable], {})
406+
else:
407+
table = ttyp_rec.setdefault(f"unknown_{stable}", {})
408+
for f_a, f_b, *fuses in fse[ttyp]['shortval'][stable]:
409+
table[(f_a, f_b)] = {fuse.fuse_lookup(fse, ttyp, f) for f in unpad(fuses)}
410+
if 'longval' in fse[ttyp].keys():
411+
ttyp_rec = dev.longval.setdefault(ttyp, {})
412+
for ltable in fse[ttyp]['longval'].keys():
413+
if ltable in _known_tables:
414+
table = ttyp_rec.setdefault(_known_tables[ltable], {})
415+
else:
416+
table = ttyp_rec.setdefault(f"unknown_{ltable}", {})
417+
for f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, *fuses in fse[ttyp]['longval'][ltable]:
418+
table[(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15)] = {fuse.fuse_lookup(fse, ttyp, f) for f in unpad(fuses)}
419+
322420
def from_fse(device, fse):
323421
dev = Device()
324422
ttypes = {t for row in fse['header']['grid'][61] for t in row}
325423
tiles = {}
326424
for ttyp in ttypes:
327425
w = fse[ttyp]['width']
328426
h = fse[ttyp]['height']
329-
tile = Tile(w, h)
427+
tile = Tile(w, h, ttyp)
330428
tile.pips = fse_pips(fse, ttyp, 2, wirenames)
331429
tile.clock_pips = fse_pips(fse, ttyp, 38, clknames)
332430
if 5 in fse[ttyp]['shortval']:
333431
tile.bels = fse_luts(fse, ttyp)
334432
if 51 in fse[ttyp]['shortval']:
335433
tile.bels = fse_osc(device, fse, ttyp)
434+
if ttyp in [88, 89]:
435+
tile.bels = fse_pll(device, fse, ttyp)
336436
tiles[ttyp] = tile
337437

438+
fse_fill_logic_tables(dev, fse)
338439
dev.grid = [[tiles[ttyp] for ttyp in row] for row in fse['header']['grid'][61]]
339440
return dev
340441

442+
# get fuses for attr/val set using short/longval table
443+
# returns a bit set
444+
def get_table_fuses(attrs, table):
445+
bits = set()
446+
for key, fuses in table.items():
447+
# all 16 "features" must be present to be able to use a set of bits from the record
448+
have_full_key = True
449+
for attrval in key:
450+
if attrval == 0: # no "feature"
451+
continue
452+
if attrval > 0:
453+
# this "feature" must present
454+
if attrval not in attrs:
455+
have_full_key = False
456+
break
457+
continue
458+
if attrval < 0:
459+
# this "feature" is set by default and can only be unset
460+
if abs(attrval) in attrs:
461+
have_full_key = False
462+
break
463+
if not have_full_key:
464+
continue
465+
bits.update(fuses)
466+
return bits
467+
468+
# get fuses for attr/val set using shortval table for ttyp
469+
# returns a bit set
470+
def get_shortval_fuses(dev, ttyp, attrs, table_name):
471+
return get_table_fuses(attrs, dev.shortval[ttyp][table_name])
472+
473+
# get fuses for attr/val set using longval table for ttyp
474+
# returns a bit set
475+
def get_longval_fuses(dev, ttyp, attrs, table_name):
476+
return get_table_fuses(attrs, dev.longval[ttyp][table_name])
477+
478+
# add the attribute/value pair into an set, which is then passed to
479+
# get_longval_fuses() and get_shortval_fuses()
480+
def add_attr_val(dev, logic_table, attrs, attr, val):
481+
for idx, attr_val in enumerate(dev.logicinfo[logic_table]):
482+
if attr_val[0] == attr and attr_val[1] == val:
483+
attrs.add(idx)
484+
break
485+
341486
def get_pins(device):
342487
if device not in {"GW1N-1", "GW1NZ-1", "GW1N-4", "GW1N-9", "GW1NR-9", "GW1N-9C", "GW1NR-9C", "GW1NS-2", "GW1NS-2C", "GW1NS-4", "GW1NSR-4C"}:
343488
raise Exception(f"unsupported device {device}")
@@ -425,6 +570,16 @@ def json_pinout(device):
425570
raise Exception("unsupported device")
426571

427572

573+
574+
_pll_inputs = [(5, 'CLKFB'), (6, 'FBDSEL0'), (7, 'FBDSEL1'), (8, 'FBDSEL2'), (9, 'FBDSEL3'),
575+
(10, 'FBDSEL4'), (11, 'FBDSEL5'),
576+
(12, 'IDSEL0'), (13, 'IDSEL1'), (14, 'IDSEL2'), (15, 'IDSEL3'), (16, 'IDSEL4'),
577+
(17, 'IDSEL5'),
578+
(18, 'ODSEL0'), (19, 'ODSEL1'), (20, 'ODSEL2'), (21, 'ODSEL3'), (22, 'ODSEL4'),
579+
(24, 'PSDA0'), (25, 'PSDA1'), (26, 'PSDA2'), (27, 'PSDA3'),
580+
(28, 'DUTYDA0'), (29, 'DUTYDA1'), (30, 'DUTYDA2'), (31, 'DUTYDA3'),
581+
(32, 'FDLY0'), (33, 'FDLY1'), (34, 'FDLY2'), (35, 'FDLY3')]
582+
_pll_outputs = [(0, 'CLKOUT'), (1, 'LOCK'), (2, 'CLKOUTP'), (3, 'CLKOUTD'), (4, 'CLKOUTD3')]
428583
def dat_portmap(dat, dev):
429584
for row in dev.grid:
430585
for tile in row:
@@ -447,6 +602,28 @@ def dat_portmap(dat, dev):
447602
bel.portmap['I'] = out
448603
oe = wirenames[dat[f'Iobuf{pin}OE']]
449604
bel.portmap['OE'] = oe
605+
elif name.startswith("ODDR"):
606+
d0 = wirenames[dat[f'Iologic{pin}In'][1]]
607+
bel.portmap['D0'] = d0
608+
d1 = wirenames[dat[f'Iologic{pin}In'][2]]
609+
bel.portmap['D1'] = d1
610+
tx = wirenames[dat[f'Iologic{pin}In'][27]]
611+
bel.portmap['TX'] = tx
612+
elif name == 'RPLLA':
613+
for idx, nam in _pll_inputs:
614+
wire = wirenames[dat['PllIn'][idx]]
615+
bel.portmap[nam] = wire
616+
for idx, nam in _pll_outputs:
617+
wire = wirenames[dat['PllOut'][idx]]
618+
bel.portmap[nam] = wire
619+
bel.portmap['CLKIN'] = wirenames[124];
620+
elif name == 'RPLLB':
621+
reset = wirenames[dat['PllIn'][0]]
622+
bel.portmap['RESET'] = reset
623+
reset_p = wirenames[dat['PllIn'][1]]
624+
bel.portmap['RESET_P'] = reset_p
625+
odsel5 = wirenames[dat['PllIn'][23]]
626+
bel.portmap['ODSEL5'] = odsel5
450627

451628
def dat_aliases(dat, dev):
452629
for row in dev.grid:
@@ -655,3 +832,4 @@ def loc2bank(db, row, col):
655832
bank = db.pin_bank[name + 'B']
656833
return bank
657834

835+

0 commit comments

Comments
 (0)