Skip to content

Commit 9e8f061

Browse files
authored
Merge pull request #314 from yrabbit/seg-clocks
Add segmented clock wires. Gowin chips have an interesting mechanism - wires that run vertically through several rows (at least 10) in each column of the chip. In each row a particular wire has branches to the left and right, covering on average 4 neighboring cells in the row. For lack of a better term, I further call such a wire a segment. So a segment can provide a direct connection in a local rectangle. There are no special restrictions on the sinks, so segment networks can be used for ClockEnable, LocalSetReset, as well as for LUT and DFF inputs. The sources are not so simple - the sources can be the upper or lower end of the segment, which in theory can lead to unfortunate consequences if the signal is applied from both ends. The matter is complicated by the fact that there are default connections, i.e. in the absence of any set fuse the segment input is still connected to something (VCC for example) and to disable the unused end of the segment you need to set a special combination of fuses. Taking into account which end of which segment is used is one of the tasks of this router. In addition, segment ends can physically coincide with PLL, DSP and BSRAM inputs, which can also lead to unexpected effects. Some of these things are tracked when generating the base, some in this router, some when packing in gowin_pack.
2 parents 8401e32 + aa5722b commit 9e8f061

File tree

2 files changed

+199
-1
lines changed

2 files changed

+199
-1
lines changed

apycula/chipdb.py

Lines changed: 169 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ class Device:
129129
extra_func: Dict[Tuple[int, int], Dict[str, Any]] = field(default_factory=dict)
130130
# Chip features currently related to block memory like "HAS_SP32", "NEED_SP_FIX", etc
131131
chip_flags: List[str] = field(default_factory=list)
132+
# Segmented clock columns description
133+
# { (y, x, idx) : {min_x, min_y, max_x, max_y, top_row, bottom_row, top_wire, bottom_wire,
134+
# top_gate_wire[name, ], bottom_gate_wire[name,]}}
135+
segments: Dict[Tuple[int, int, int], Dict[str, Any]] = field(default_factory=dict)
132136

133137
@property
134138
def rows(self):
@@ -1832,6 +1836,169 @@ def fse_create_clocks(dev, device, dat: Datfile, fse):
18321836
dcs[f'selforce'] = 'D3'
18331837
dcs['clksel'] = ['D2', 'A3', 'B3', 'C3']
18341838

1839+
# Segmented wires are those that run along each column of the chip and have
1840+
# taps in each row about 4 cells wide. The height of the segment wires varies
1841+
# from chip to chip: from full chip height for GW1N-1 to two strips in GW1N-9
1842+
# and three strips in GW2A-18.
1843+
# The MUXes for the sources on these wires can switch between signals from
1844+
# "spines" (long horizontal wires up to half a chip in length) or from input
1845+
# points from the logic.
1846+
# These MUXes are placed on both ends of the segmented wire, which is very
1847+
# flexible but at the same time requires some care not to signal both ends of
1848+
# the wire.
1849+
# The coverage areas between segment wires i and i + 4 are the same, only the
1850+
# MUXes differ, so we create a pair of segments at once.
1851+
1852+
# tap_start = describes where in the 4-cell area the main wire column with index is located
1853+
# rows = top and bottom rows of segments
1854+
# top_wires = [(MUX wire for i segment, MUX wire for i + 4 segment), next_row]
1855+
# bottom_wires = [(MUX wire for i segment, MUX wire for i + 4 segment), next_row]
1856+
# top_gate_wires = [(MUX wire for i segment, MUX wire for i + 4 segment), next_row][2]
1857+
# bottom_gate_wires = [(MUX wire for i segment, MUX wire for i + 4 segment), next_row][2]
1858+
_segment_data = {
1859+
'GW1N-1': { 'tap_start': [1, 0, 3, 2], 'rows': [(0, 10)],
1860+
'top_wires': [('LT02', 'LT13')], 'bottom_wires': [('LT02', 'LT13')],
1861+
'top_gate_wires': [[('A6', 'A7')], [('B6', 'B7')]],
1862+
'bottom_gate_wires': [[('A6', 'A7')], [('B6', 'B7')]],
1863+
'reserved_wires': {(0, 17, 'A6'), (0, 18, 'A6'), (0, 17, 'A7'), (0, 18, 'A7'),
1864+
(0, 17, 'B6'), (0, 18, 'B6'), (0, 17, 'B7'), (0, 18, 'B7')}},
1865+
'GW1NZ-1': { 'tap_start': [1, 0, 3, 2], 'rows': [(0, 10)],
1866+
'top_wires': [('LT02', 'LT13')], 'bottom_wires': [('LT02', 'LT13')],
1867+
'top_gate_wires': [[('A6', 'A7')], [('B6', 'B7')]],
1868+
'bottom_gate_wires': [[('A6', 'A7')], [('B6', 'B7')]],
1869+
'reserved_wires': {(0, 17, 'A6'), (0, 18, 'A6'), (0, 17, 'A7'), (0, 18, 'A7'),
1870+
(0, 17, 'B6'), (0, 18, 'B6'), (0, 17, 'B7'), (0, 18, 'B7')}},
1871+
'GW1N-4': { 'tap_start': [2, 1, 0, 3], 'rows': [(0, 19)],
1872+
'top_wires': [('LT02', 'LT13')], 'bottom_wires': [('LT02', 'LT13')],
1873+
'top_gate_wires': [[('A6', 'A7')], [('B6', 'B7')]],
1874+
'bottom_gate_wires': [[('A6', 'A7')], [('B6', 'B7')]],
1875+
'reserved_wires': {(0, 9, 'A6'), (0, 10, 'A6'), (0, 9, 'A7'), (0, 10, 'A7'),
1876+
(0, 9, 'B6'), (0, 10, 'B6'), (0, 9, 'B7'), (0, 10, 'B7'),
1877+
(0, 27, 'A6'), (0, 28, 'A6'), (0, 27, 'A7'), (0, 28, 'A7'),
1878+
(0, 27, 'B6'), (0, 28, 'B6'), (0, 27, 'B7'), (0, 28, 'B7')}},
1879+
'GW1NS-4': { 'tap_start': [2, 1, 0, 3], 'rows': [(0, 19)],
1880+
'top_wires': [('LT02', 'LT13')], 'bottom_wires': [('LT02', 'LT13')],
1881+
'top_gate_wires': [[('A6', 'A7')], [('B6', 'B7')]],
1882+
'bottom_gate_wires': [[('A6', 'A7')], [('B6', 'B7')]],
1883+
'reserved_wires': {(0, 27, 'A6'), (0, 36, 'A6'), (0, 27, 'A7'), (0, 36, 'A7'),
1884+
(0, 27, 'B6'), (0, 36, 'B6'), (0, 27, 'B7'), (0, 36, 'B7')}},
1885+
'GW1N-9': { 'tap_start': [3, 2, 1, 0], 'rows': [(0, 18), (19, 28)],
1886+
'top_wires': [('LT02', 'LT13'), ('LT00', 'LT10')],
1887+
'bottom_wires': [('LT20', 'LT30'), ('LT02', 'LT13')],
1888+
'top_gate_wires': [[('A6', 'A7'), (None, None)], [('B6', 'B7'), None]],
1889+
'bottom_gate_wires': [[(None, 'B7'), (None, 'A7')], [None, None]],
1890+
'reserved_wires': {}},
1891+
'GW1N-9C': { 'tap_start': [3, 2, 1, 0], 'rows': [(0, 18), (19, 28)],
1892+
'top_wires': [('LT02', 'LT13'), ('LT00', 'LT10')],
1893+
'bottom_wires': [('LT20', 'LT30'), ('LT02', 'LT13')],
1894+
'top_gate_wires': [[('A6', 'A7'), (None, None)], [('B6', 'B7'), None]],
1895+
'bottom_gate_wires': [[(None, 'B7'), ('A6', 'A7')], [None, None]],
1896+
'reserved_wires': {}},
1897+
'GW2A-18': { 'tap_start': [3, 2, 1, 0], 'rows': [(0, 18), (19, 36), (37, 54)],
1898+
'top_wires': [('LT02', 'LT13'), ('LT00', 'LT10'), ('LT00', 'LT10')],
1899+
'bottom_wires': [('LT20', 'LT30'), ('LT20', 'LT30'), ('LT02', 'LT13')],
1900+
'top_gate_wires': [[('A6', 'A7'), (None, None), (None, None)], [('B6', 'B7'), None, None]],
1901+
'bottom_gate_wires': [[(None, 'B7'), (None, 'B7'), ('A6', 'A7')], [None, None, ('B6', 'B7')]],
1902+
'reserved_wires': {}},
1903+
'GW2A-18C': { 'tap_start': [3, 2, 1, 0], 'rows': [(0, 18), (19, 36), (37, 54)],
1904+
'top_wires': [('LT02', 'LT13'), ('LT00', 'LT10'), ('LT00', 'LT10')],
1905+
'bottom_wires': [('LT20', 'LT30'), ('LT20', 'LT30'), ('LT02', 'LT13')],
1906+
'top_gate_wires': [[('A6', 'A7'), (None, None), (None, None)], [('B6', 'B7'), None, None]],
1907+
'bottom_gate_wires': [[(None, 'B7'), (None, 'B7'), ('A6', 'A7')], [None, None, ('B6', 'B7')]],
1908+
'reserved_wires': {}},
1909+
}
1910+
def create_segments(dev, device):
1911+
if device not in _segment_data:
1912+
return
1913+
1914+
dev_desc = _segment_data[device]
1915+
top_gate_row = dev_desc['rows'][0][0]
1916+
for row_idx, tb_row in enumerate(dev_desc['rows']):
1917+
t_row, b_row = tb_row
1918+
for s_col in range(dev.cols):
1919+
# new segment i
1920+
seg_idx = dev_desc['tap_start'][s_col % 4]
1921+
seg = dev.segments.setdefault((top_gate_row, s_col, seg_idx), {})
1922+
# controlled area
1923+
seg['min_x'] = max(0, s_col - 1)
1924+
seg['min_y'] = t_row
1925+
seg['max_x'] = min(dev.cols - 1, s_col + 2)
1926+
if dev.cols - 1 - seg['max_x'] == 1:
1927+
# The main wire of the segment is repeated every 4 cells, if
1928+
# there is no space on the right side for the next wire, the
1929+
# service area is extended to the very edge
1930+
seg['max_x'] = dev.cols - 1
1931+
seg['max_y'] = b_row
1932+
# MUX's positions and wires
1933+
seg['top_row'] = top_gate_row
1934+
seg['bottom_row'] = b_row
1935+
seg['top_wire'] = dev_desc['top_wires'][row_idx][0]
1936+
seg['bottom_wire'] = dev_desc['bottom_wires'][row_idx][0]
1937+
# gate wires
1938+
seg['top_gate_wire'] = [dev_desc['top_gate_wires'][0][row_idx][0]]
1939+
second_gate = dev_desc['top_gate_wires'][1][row_idx]
1940+
seg['top_gate_wire'].append(second_gate)
1941+
if second_gate:
1942+
seg['top_gate_wire'][1] = second_gate[0]
1943+
seg['bottom_gate_wire'] = [dev_desc['bottom_gate_wires'][0][row_idx][0]]
1944+
second_gate = dev_desc['bottom_gate_wires'][1][row_idx]
1945+
seg['bottom_gate_wire'].append(second_gate)
1946+
if second_gate:
1947+
seg['bottom_gate_wire'][1] = second_gate[0]
1948+
# check reserved
1949+
if (top_gate_row, s_col, seg['top_gate_wire'][0]) in dev_desc['reserved_wires']:
1950+
seg['top_gate_wire'][0] = None
1951+
if (top_gate_row, s_col, seg['top_gate_wire'][1]) in dev_desc['reserved_wires']:
1952+
seg['top_gate_wire'][1] = None
1953+
if (b_row, s_col, seg['bottom_gate_wire'][0]) in dev_desc['reserved_wires']:
1954+
seg['bottom_gate_wire'][0] = None
1955+
if (b_row, s_col, seg['bottom_gate_wire'][1]) in dev_desc['reserved_wires']:
1956+
seg['bottom_gate_wire'][1] = None
1957+
1958+
# new segment i + 1
1959+
seg_idx += 4
1960+
seg_1 = dev.segments.setdefault((top_gate_row, s_col, seg_idx), {})
1961+
# controlled area
1962+
seg_1['min_x'] = seg['min_x']
1963+
seg_1['min_y'] = seg['min_y']
1964+
seg_1['max_x'] = seg['max_x']
1965+
seg_1['max_y'] = seg['max_y']
1966+
# MUX's positions and wires
1967+
seg_1['top_row'] = seg['top_row']
1968+
seg_1['bottom_row'] = seg['bottom_row']
1969+
seg_1['top_wire'] = dev_desc['top_wires'][row_idx][1]
1970+
seg_1['bottom_wire'] = dev_desc['bottom_wires'][row_idx][1]
1971+
# gate wires
1972+
seg_1['top_gate_wire'] = [dev_desc['top_gate_wires'][0][row_idx][1]]
1973+
second_gate = dev_desc['top_gate_wires'][1][row_idx]
1974+
seg_1['top_gate_wire'].append(second_gate)
1975+
if second_gate:
1976+
seg_1['top_gate_wire'][1] = second_gate[1]
1977+
seg_1['bottom_gate_wire'] = [dev_desc['bottom_gate_wires'][0][row_idx][1]]
1978+
second_gate = dev_desc['bottom_gate_wires'][1][row_idx]
1979+
seg_1['bottom_gate_wire'].append(second_gate)
1980+
if second_gate:
1981+
seg_1['bottom_gate_wire'][1] = second_gate[1]
1982+
# check reserved
1983+
if (top_gate_row, s_col, seg_1['top_gate_wire'][0]) in dev_desc['reserved_wires']:
1984+
seg_1['top_gate_wire'][0] = None
1985+
if (top_gate_row, s_col, seg_1['top_gate_wire'][1]) in dev_desc['reserved_wires']:
1986+
seg_1['top_gate_wire'][1] = None
1987+
if (b_row, s_col, seg_1['bottom_gate_wire'][0]) in dev_desc['reserved_wires']:
1988+
seg_1['bottom_gate_wire'][0] = None
1989+
if (b_row, s_col, seg_1['bottom_gate_wire'][1]) in dev_desc['reserved_wires']:
1990+
seg_1['bottom_gate_wire'][1] = None
1991+
1992+
# 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)]
1996+
if (not seg_1['top_gate_wire'][0] and not seg_1['top_gate_wire'][1]
1997+
and not seg_1['bottom_gate_wire'][0] and not seg_1['bottom_gate_wire'][1]):
1998+
del dev.segments[(top_gate_row, s_col, seg_idx)]
1999+
2000+
top_gate_row = b_row
2001+
18352002
# These features of IO on the underside of the chip were revealed during
18362003
# operation. The first (normal) mode was found in a report by @LoneTech on
18372004
# 4/1/2022, when it turned out that the pins on the bottom edge of the GW1NR-9
@@ -2554,9 +2721,10 @@ def from_fse(device, fse, dat: Datfile):
25542721
fse_create_emcu(dev, device, dat)
25552722
fse_create_logic2clk(dev, device, dat)
25562723
fse_create_dhcen(dev, device, fse, dat)
2724+
create_segments(dev, device)
25572725
disable_plls(dev, device)
25582726
sync_extra_func(dev)
2559-
set_chip_flags(dev, device);
2727+
set_chip_flags(dev, device)
25602728
return dev
25612729

25622730
# get fuses for attr/val set using short/longval table

apycula/gowin_pack.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,35 @@ def get_pips(data):
278278
elif pip and "DUMMY" not in pip:
279279
print("Invalid pip:", pip)
280280

281+
# Because of the default connection, the segment may end up being enabled at
282+
# both ends. Nextpnr detects and lists the wires that need to be isolated, here
283+
# we parse this information and disconnect using the "alonenode" table.
284+
def isolate_segments(pnr, db, tilemap):
285+
wire_re = re.compile(r"X(\d+)Y(\d+)/([\w]+)")
286+
for net in pnr['modules']['top']['netnames'].values():
287+
if 'SEG_WIRES_TO_ISOLATE' not in net['attributes']:
288+
continue
289+
val = net['attributes']['SEG_WIRES_TO_ISOLATE']
290+
wires = val.split(';')
291+
for wire_ex in wires:
292+
if not wire_ex:
293+
continue
294+
res = wire_re.fullmatch(wire_ex)
295+
if res:
296+
s_col, s_row, wire = res.groups()
297+
row = int(s_row)
298+
col = int(s_col)
299+
tiledata = db.grid[row][col]
300+
tile = tilemap[(row, col)]
301+
if wire not in tiledata.alonenode_6:
302+
raise Exception(f"Wire {wire} is not in alonenode fuse table")
303+
bits = tiledata.alonenode_6[wire][1]
304+
#print(wire_ex, bits)
305+
for row, col in bits:
306+
tile[row][col] = 1
307+
else:
308+
raise Exception(f"Invalid isolated wire:{wire_ex}")
309+
281310
def infovaluemap(infovalue, start=2):
282311
return {tuple(iv[:start]):iv[start:] for iv in infovalue}
283312

@@ -3031,6 +3060,7 @@ def main():
30313060
cst = codegen.Constraints()
30323061
pips = get_pips(pnr)
30333062
route(db, tilemap, pips)
3063+
isolate_segments(pnr, db, tilemap)
30343064
bels = get_bels(pnr)
30353065
# routing can add pass-through LUTs
30363066
place(db, tilemap, itertools.chain(bels, _pip_bels) , cst, args)

0 commit comments

Comments
 (0)