Skip to content

Commit fedeada

Browse files
authored
Merge pull request #318 from yrabbit/dlldly
Implement the DLLDLY primitive.
2 parents 9e8f061 + 870985d commit fedeada

File tree

4 files changed

+155
-5
lines changed

4 files changed

+155
-5
lines changed

apycula/attrids.py

+24
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,30 @@
916916
'VCC': 22,
917917
}
918918

919+
# DLLDLY
920+
dlldly_attrids = {
921+
'ENABLED': 0,
922+
'LOADN': 2,
923+
'SIGN': 3,
924+
'MODE': 4,
925+
'ADJ0': 5,
926+
'ADJ1': 6,
927+
'ADJ2': 7,
928+
'ADJ3': 8,
929+
'ADJ4': 9,
930+
'ADJ5': 10,
931+
'ADJ6': 11,
932+
'ADJ7': 12,
933+
}
934+
935+
dlldly_attrvals = {
936+
'UNKNOWN': 0,
937+
'ENABLE': 1,
938+
'NORMAL': 3,
939+
'1': 4,
940+
'NEG': 5,
941+
}
942+
919943
# DLL
920944
dll_attrids = {
921945
'CLKSEL': 0,

apycula/chipdb.py

+83-3
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ def set_banks(fse, db):
477477
15: 'PLL',
478478
39: 'BSRAM_INIT',
479479
49: 'HCLK',
480+
52: 'DLLDLY',
480481
59: 'CFG',
481482
62: 'OSC',
482483
63: 'USB',
@@ -1438,6 +1439,64 @@ def fse_create_dhcen(dev, device, fse, dat: Datfile):
14381439
hclkin.update({ 'ce' : wire})
14391440
dhcen.append(hclkin)
14401441

1442+
# DLLDLY
1443+
# from Gowin doc "DLLDLY is the clock delay module that adjusts the input clock according to the DLLSTEP"
1444+
# In practice the following peculiarities were discovered: the input for the
1445+
# clock cannot be arbitrary things, but only specialised pins of the chip and
1446+
# the delay line is cut in between the pin and the clock MUX.
1447+
# { bel_loc : ([('io_loc', 'io_output_wire', (row, col, flag_wirea))], [(fuse_row, fuse_col)])
1448+
1449+
_dlldly = {
1450+
'GW1N-1': {
1451+
(10, 19) : {
1452+
'fuse_bels': {(10, 0), (10, 19)},
1453+
'ios' : [('X9Y10', 'IOBA', (10, 0, 'F1')), ('X10Y10', 'IOBA', (10, 0, 'F0'))],
1454+
},
1455+
},
1456+
'GW1NZ-1': {
1457+
( 0, 19) : {
1458+
'fuse_bels': {(0, 5)},
1459+
'ios' : [('X9Y0', 'IOBA', (0, 19, 'F1')), ('X10Y0', 'IOBA', (0, 19, 'F0'))],
1460+
},
1461+
(10, 19) : {
1462+
'fuse_bels' : {(5, 19)},
1463+
'ios' : [('X19Y4', 'IOBA', (5, 19, 'F0')), ('X19Y6', 'IOBA', (5, 19, 'F2'))],
1464+
},
1465+
}
1466+
}
1467+
1468+
def fse_create_dlldly(dev, device):
1469+
if device in _dlldly:
1470+
for bel, fuse_ios in _dlldly[device].items():
1471+
row, col = bel
1472+
fuse_bels = fuse_ios['fuse_bels']
1473+
ios = fuse_ios['ios']
1474+
extra = dev.extra_func.setdefault((row, col), {})
1475+
dlldly = extra.setdefault(f'dlldly', {})
1476+
for idx in range(2):
1477+
dlldly[idx] = {'io_loc': ios[idx][0], 'io_bel': ios[idx][1]}
1478+
# FLAG output
1479+
nodename = f'X{col}Y{row}/DLLDLY_FLAG{idx}'
1480+
nodename = add_node(dev, nodename, "", row, col, f'DLLDLY_FLAG{idx}')
1481+
add_node(dev, nodename, "", ios[idx][2][0], ios[idx][2][1], ios[idx][2][2])
1482+
add_node(dev, f'{ios[idx][0]}/DLLDLY_IN', "TILE_CLK", row, col, f'DLLDLY_CLKIN{idx}')
1483+
add_node(dev, f'{ios[idx][0]}/DLLDLY_OUT', "DLLDLY_O", row, col, f'DLLDLY_CLKOUT{idx}')
1484+
1485+
# STEP wires
1486+
wires = dlldly[idx].setdefault('in_wires', {})
1487+
prefix = ["CB", "DC"][idx]
1488+
for wire_idx in range(8):
1489+
wires[f'DLLSTEP{wire_idx}'] = f"{prefix[wire_idx // 4]}{(wire_idx + 4) % 8}"
1490+
wires['DIR'] = ["A1", "B4"][idx]
1491+
wires['LOADN'] = ["A0", "B7"][idx]
1492+
wires['MOVE'] = ["B6", "B5"][idx]
1493+
wires['CLKIN'] = f'DLLDLY_CLKIN{idx}'
1494+
1495+
wires = dlldly[idx].setdefault('out_wires', {})
1496+
wires['FLAG'] = f'DLLDLY_FLAG{idx}'
1497+
wires['CLKOUT'] = f'DLLDLY_CLKOUT{idx}'
1498+
dlldly_bels = extra.setdefault(f'dlldly_fusebels', set())
1499+
dlldly_bels.update(fuse_bels)
14411500

14421501
_pll_loc = {
14431502
'GW1N-1':
@@ -1685,6 +1744,19 @@ def fse_create_clocks(dev, device, dat: Datfile, fse):
16851744
add_node(dev, f'{clknames[clk_idx]}-9C', "GLOBAL_CLK", row, dev.cols - 1, 'LWT6')
16861745
else:
16871746
add_node(dev, f'{clknames[clk_idx]}-9C', "GLOBAL_CLK", row, 0, 'LWT6')
1747+
elif (device == 'GW1NZ-1' and (row == 0 or col == dev.cols - 1)) or (device == 'GW1N-1' and row == dev.rows - 1):
1748+
# Do not connect the IO output to the clock node because DLLDLY
1749+
# may be located at these positions, which, if used, will be
1750+
# the source for the clock. However, if DLLDLY is not used
1751+
# (mostly), we need to have a way to connect them - for this we
1752+
# add two PIPs - one to connect the IO output to the clock and
1753+
# one to connect the IO output to the DLLDLY input.
1754+
# Both are non-fuseable, but allow the router to work.
1755+
add_node(dev, clknames[clk_idx], "GLOBAL_CLK", row, col, 'PCLK_DUMMY')
1756+
dev.grid[row][col].pips['PCLK_DUMMY'] = {wirenames[wire_idx]: set(), 'DLLDLY_OUT': set()}
1757+
add_node(dev, f'X{col}Y{row}/DLLDLY_OUT', "DLLDLY_O", row, col, 'DLLDLY_OUT')
1758+
add_node(dev, f'X{col}Y{row}/DLLDLY_IN', "TILE_CLK", row, col, 'DLLDLY_IN')
1759+
dev.grid[row][col].pips['DLLDLY_IN'] = {wirenames[wire_idx]: set()}
16881760
else:
16891761
add_node(dev, clknames[clk_idx], "GLOBAL_CLK", row, col, wirenames[wire_idx])
16901762
add_buf_bel(dev, row, col, wirenames[wire_idx])
@@ -1955,8 +2027,16 @@ def create_segments(dev, device):
19552027
if (b_row, s_col, seg['bottom_gate_wire'][1]) in dev_desc['reserved_wires']:
19562028
seg['bottom_gate_wire'][1] = None
19572029

2030+
# remove isolated segments (these are in the DSP area of -9, -9C, -18, -18C)
2031+
if (not seg['top_gate_wire'][0] and not seg['top_gate_wire'][1]
2032+
and not seg['bottom_gate_wire'][0] and not seg['bottom_gate_wire'][1]):
2033+
del dev.segments[(top_gate_row, s_col, seg_idx)]
2034+
19582035
# new segment i + 1
19592036
seg_idx += 4
2037+
# XXX 6 and 7 need static DCS, disable for now
2038+
if seg_idx in [6, 7]:
2039+
continue
19602040
seg_1 = dev.segments.setdefault((top_gate_row, s_col, seg_idx), {})
19612041
# controlled area
19622042
seg_1['min_x'] = seg['min_x']
@@ -1990,9 +2070,6 @@ def create_segments(dev, device):
19902070
seg_1['bottom_gate_wire'][1] = None
19912071

19922072
# remove isolated segments (these are in the DSP area of -9, -9C, -18, -18C)
1993-
if (not seg['top_gate_wire'][0] and not seg['top_gate_wire'][1]
1994-
and not seg['bottom_gate_wire'][0] and not seg['bottom_gate_wire'][1]):
1995-
del dev.segments[(top_gate_row, s_col, seg_idx - 4)]
19962073
if (not seg_1['top_gate_wire'][0] and not seg_1['top_gate_wire'][1]
19972074
and not seg_1['bottom_gate_wire'][0] and not seg_1['bottom_gate_wire'][1]):
19982075
del dev.segments[(top_gate_row, s_col, seg_idx)]
@@ -2721,6 +2798,7 @@ def from_fse(device, fse, dat: Datfile):
27212798
fse_create_emcu(dev, device, dat)
27222799
fse_create_logic2clk(dev, device, dat)
27232800
fse_create_dhcen(dev, device, fse, dat)
2801+
fse_create_dlldly(dev, device)
27242802
create_segments(dev, device)
27252803
disable_plls(dev, device)
27262804
sync_extra_func(dev)
@@ -4131,6 +4209,8 @@ def fse_wire_delays(db):
41314209
db.wire_delay[clknames[i]] = "CENT_SPINE_PCLK"
41324210
for i in range(1000, 1010): # HCLK
41334211
db.wire_delay[clknames[i]] = "X0" # XXX
4212+
for wire in {'DLLDLY_OUT', 'DLLDLY_CLKOUT', 'DLLDLY_CLKOUT0', 'DLLDLY_CLKOUT1'}:
4213+
db.wire_delay[wire] = "X0" # XXX
41344214

41354215
# assign pads with plls
41364216
# for now use static table and store the bel name although it is always PLL without a number

apycula/gowin_pack.py

+38-2
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def get_bits(init_data):
201201
def get_bels(data):
202202
later = []
203203
if is_himbaechel:
204-
belre = re.compile(r"X(\d+)Y(\d+)/(?:GSR|LUT|DFF|IOB|MUX|ALU|ODDR|OSC[ZFHWO]?|BUF[GS]|RAM16SDP4|RAM16SDP2|RAM16SDP1|PLL|IOLOGIC|CLKDIV2|CLKDIV|BSRAM|ALU|MULTALU18X18|MULTALU36X18|MULTADDALU18X18|MULT36X36|MULT18X18|MULT9X9|PADD18|PADD9|BANDGAP|DQCE|DCS|USERFLASH|EMCU|DHCEN|MIPI_OBUF|MIPI_IBUF)(\w*)")
204+
belre = re.compile(r"X(\d+)Y(\d+)/(?:GSR|LUT|DFF|IOB|MUX|ALU|ODDR|OSC[ZFHWO]?|BUF[GS]|RAM16SDP4|RAM16SDP2|RAM16SDP1|PLL|IOLOGIC|CLKDIV2|CLKDIV|BSRAM|ALU|MULTALU18X18|MULTALU36X18|MULTADDALU18X18|MULT36X36|MULT18X18|MULT9X9|PADD18|PADD9|BANDGAP|DQCE|DCS|USERFLASH|EMCU|DHCEN|MIPI_OBUF|MIPI_IBUF|DLLDLY)(\w*)")
205205
else:
206206
belre = re.compile(r"R(\d+)C(\d+)_(?:GSR|SLICE|IOB|MUX2_LUT5|MUX2_LUT6|MUX2_LUT7|MUX2_LUT8|ODDR|OSC[ZFHWO]?|BUFS|RAMW|rPLL|PLLVR|IOLOGIC)(\w*)")
207207

@@ -2038,6 +2038,34 @@ def set_osc_attrs(db, typ, params):
20382038
add_attr_val(db, 'OSC', fin_attrs, attrids.osc_attrids[attr], val)
20392039
return fin_attrs
20402040

2041+
def set_dlldly_attrs(db, typ, params, cell):
2042+
dlldly_attrs = dict()
2043+
dlldly_attrs['DLL_INSEL'] = params.get('DLL_INSEL', "1")
2044+
dlldly_attrs['DLY_SIGN'] = params.get('DLY_SIGN', "0")
2045+
dlldly_attrs['DLY_ADJ'] = params.get('DLY_ADJ', "00000000000000000000000000000000")
2046+
2047+
if dlldly_attrs['DLL_INSEL'] != '1':
2048+
raise Exception(f"DLL_INSEL parameter values other than 1 are not supported")
2049+
dlldly_attrs.pop('DLL_INSEL')
2050+
dlldly_attrs['ENABLED'] = 'ENABLE'
2051+
dlldly_attrs['MODE'] = 'NORMAL'
2052+
2053+
if dlldly_attrs['DLY_SIGN'] == '1':
2054+
dlldly_attrs['SIGN'] = 'NEG'
2055+
dlldly_attrs.pop('DLY_SIGN')
2056+
2057+
for i, ch in enumerate(dlldly_attrs['DLY_ADJ'][-1::-1]):
2058+
if ch == '1':
2059+
dlldly_attrs[f'ADJ{i}'] = '1'
2060+
dlldly_attrs.pop('DLY_ADJ')
2061+
2062+
fin_attrs = set()
2063+
for attr, val in dlldly_attrs.items():
2064+
if isinstance(val, str):
2065+
val = attrids.dlldly_attrvals[val]
2066+
add_attr_val(db, 'DLLDLY', fin_attrs, attrids.dlldly_attrids[attr], val)
2067+
return fin_attrs
2068+
20412069
_wire2attr_val = {
20422070
'HCLK_IN0': ('HSB0MUX0_HSTOP', 'HCLKCIBSTOP0'),
20432071
'HCLK_IN1': ('HSB1MUX0_HSTOP', 'HCLKCIBSTOP2'),
@@ -2642,7 +2670,15 @@ def place(db, tilemap, bels, cst, args):
26422670
cfg_tile = tilemap[(0, 37)]
26432671
for r, c in bits:
26442672
cfg_tile[r][c] = 1
2645-
elif typ == "DHCEN":
2673+
elif typ == 'DLLDLY':
2674+
dlldly_attrs = set_dlldly_attrs(db, typ, parms, cell)
2675+
for dlldly_row, dlldly_col in db.extra_func[row - 1, col -1]['dlldly_fusebels']:
2676+
dlldly_tiledata = db.grid[dlldly_row][dlldly_col]
2677+
dlldly_tile = tilemap[(dlldly_row, dlldly_col)]
2678+
bits = get_long_fuses(db, dlldly_tiledata.ttyp, dlldly_attrs, f'DLLDEL{num}')
2679+
for r, c in bits:
2680+
dlldly_tile[r][c] = 1
2681+
elif typ == 'DHCEN':
26462682
if 'DHCEN_USED' not in attrs:
26472683
continue
26482684
# DHCEN as such is just a control wire and does not have a fuse

apycula/gowin_unpack.py

+10
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,16 @@ def parse_tile_(db, row, col, tile, default=True, noalias=False, noiostd = True)
338338
# if attrvals:
339339
# print(row, col, attrvals)
340340

341+
#if tiledata.ttyp in db.longfuses:
342+
# if 'DLLDEL0' in db.longfuses[tiledata.ttyp].keys():
343+
# attrvals =parse_attrvals(tile, db.logicinfo['DLLDLY'], db.longfuses[tiledata.ttyp]['DLLDEL0'], attrids.dlldly_attrids)
344+
# if attrvals:
345+
# print(row, col, attrvals)
346+
# if 'DLLDEL1' in db.longfuses[tiledata.ttyp].keys():
347+
# attrvals =parse_attrvals(tile, db.logicinfo['DLLDLY'], db.longfuses[tiledata.ttyp]['DLLDEL1'], attrids.dlldly_attrids)
348+
# if attrvals:
349+
# print(row, col, attrvals)
350+
341351
clock_pips = {}
342352
bels = {}
343353
for name, bel in tiledata.bels.items():

0 commit comments

Comments
 (0)