Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apycula/attrids.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
'TRI_MUX': 27,
'TRIMUX_PADDT': 28,
'IOBUF_PADDI': 29,
'USED': 30, # *
'USED': 30,
'IOBUF_OVERDRIVE': 31,
'IOBUF_UNDERDRIVE': 32,
'IOBUF_LVDS25_VCCIO': 33,
Expand All @@ -45,7 +45,7 @@
'LPRX_A2': 37,
'MIPI': 38,
'LVDS_SEL': 39,
'VLDS_ON': 40,
'LVDS_ON': 40,
'IOBUF_MIPI_LP': 41,
'IOBUF_ODT_RESISTOR': 42,
'IOBUF_CIB_CONTROL': 43,
Expand Down
62 changes: 62 additions & 0 deletions apycula/chipdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class Device:
# - ref to hclk_pips
# - disabled blocks
# - BUF(G)
# - MIPI
extra_func: Dict[Tuple[int, int], Dict[str, Any]] = field(default_factory=dict)
# Chip features currently related to block memory like "HAS_SP32", "NEED_SP_FIX", etc
chip_flags: List[str] = field(default_factory=list)
Expand Down Expand Up @@ -1806,6 +1807,65 @@ def fse_create_diff_types(dev, device):
elif device not in {'GW2A-18', 'GW2A-18C', 'GW1N-4'}:
dev.diff_io_types.remove('TLVDS_IOBUF')

def fse_create_mipi(dev, device, dat: Datfile):
# The MIPI OBUF is a slightly modified differential TBUF, such units are
# located on the bottom or right side of the chip depending on the series.
# We use the extra_func mechanism because these blocks do not depend on the
# cell type, but only on the coordinates.
# The same applies to MIPI_IBUF but here two neighbouring cells are used
# per primitive.
df = dev.extra_func
wire_type = 'X0'
if device in {'GW1N-9', 'GW1N-9C'}:
for i in chain(range(1, 18, 2), range(20, 34, 2), range(38, 46, 2)):
df.setdefault((dev.rows - 1, i), {})['mipi_obuf'] = {}
for i in range(1, 44, 2):
node_name = f'X{i}Y0/MIPIOL'
add_node(dev, node_name, wire_type, 0, i, 'MIPIOL')
add_node(dev, node_name, wire_type, 0, i + 1, wirenames[dat.portmap['IobufAOut']])
df.setdefault((0, i), {})['mipi_ibuf'] = {'HSREN': wirenames[dat.portmap['IologicBIn'][40]]}
# These two signals are noticed when MIPI input buffers are used. The
# purpose is unclear, but will be repeated.
node_name = f'X0Y0/MIPIEN0'
add_node(dev, node_name, wire_type, 0, i, 'MIPIEN0')
add_node(dev, node_name, wire_type, 0, 0, 'A4')
node_name = f'X0Y0/MIPIEN1'
add_node(dev, node_name, wire_type, 0, i, 'MIPIEN1')
add_node(dev, node_name, wire_type, 0, 0, 'A5')
elif device in {'GW1NS-4'}:
for i in {1, 3, 5, 7, 10, 11, 14, 16}:
df.setdefault((i, dev.cols - 1), {})['mipi_obuf'] = {}
for i in chain(range(1, 9, 2), range(10, 17, 2), range(19, 26, 2), range(28, 35, 2)):
node_name = f'X{i}Y0/MIPIOL'
add_node(dev, node_name, wire_type, 0, i, 'MIPIOL')
add_node(dev, node_name, wire_type, 0, i + 1, wirenames[dat.portmap['IobufAOut']])
df.setdefault((0, i), {})['mipi_ibuf'] = {'HSREN': wirenames[dat.portmap['IologicBIn'][40]]}
# These two signals are noticed when MIPI input buffers are used. The
# purpose is unclear, but will be repeated.
node_name = f'X37Y0/MIPIEN0'
add_node(dev, node_name, wire_type, 0, i, 'MIPIEN0')
add_node(dev, node_name, wire_type, 0, 0, 'D2')
node_name = f'X37Y0/MIPIEN1'
add_node(dev, node_name, wire_type, 0, i, 'MIPIEN1')
add_node(dev, node_name, wire_type, 0, 0, 'D3')

def fse_create_i3c(dev, device, dat: Datfile):
# The I3C_IOBUF is a slightly modified IOBUF, such units are
# located on the bottom or right side of the chip depending on the series.
# We use the extra_func mechanism because these blocks do not depend on the
# cell type, but only on the coordinates.
df = dev.extra_func
wire_type = ''
if device in {'GW1N-9', 'GW1N-9C'}:
for i in range(1, dev.cols - 1):
df.setdefault((0, i), {})['i3c_capable'] = {}
df.setdefault((dev.rows - 1, i), {})['i3c_capable'] = {}
elif device in {'GW1NS-4'}:
for i in range(1, dev.cols - 1):
df.setdefault((0, i), {})['i3c_capable'] = {}
for i in range(1, dev.rows - 1):
df.setdefault((i, dev.cols - 1), {})['i3c_capable'] = {}

def fse_create_io16(dev, device):
# 16-bit serialization/deserialization primitives occupy two consecutive
# cells. For the top and bottom sides of the chip, this means that the
Expand Down Expand Up @@ -2366,6 +2426,8 @@ def from_fse(device, fse, dat: Datfile):
fse_create_tile_types(dev, dat)
fse_create_diff_types(dev, device)
fse_create_hclk_nodes(dev, device, fse, dat)
fse_create_mipi(dev, device, dat)
fse_create_i3c(dev, device, dat)
fse_create_io16(dev, device)
fse_create_osc(dev, device, fse)
fse_create_gsr(dev, device)
Expand Down
82 changes: 70 additions & 12 deletions apycula/gowin_pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ def extra_pll_bels(cell, row, col, num, cellname):
yield ('RPLLB', int(row), int(col) + offx * off, num,
cell['parameters'], cell['attributes'], sanitize_name(cellname) + f'B{off}', cell)

def extra_mipi_bels(cell, row, col, num, cellname):
yield ('MIPI_IBUF_AUX', int(row), int(col) + 1, num,
cell['parameters'], cell['attributes'], sanitize_name(cellname) + 'AUX', cell)

def extra_bsram_bels(cell, row, col, num, cellname):
for off in [1, 2]:
yield ('BSRAM_AUX', int(row), int(col) + off, num,
Expand Down Expand Up @@ -185,7 +189,7 @@ def get_bits(init_data):
def get_bels(data):
later = []
if is_himbaechel:
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)(\w*)")
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*)")
else:
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*)")

Expand Down Expand Up @@ -220,6 +224,8 @@ def get_bels(data):
yield from extra_bsram_bels(cell, row, col, num, cellname)
if cell_type in _dsp_cell_types:
yield from extra_dsp_bels(cell, row, col, num, cellname)
if cell_type == 'MIPI_IBUF':
yield from extra_mipi_bels(cell, row, col, num, cellname)
yield (cell_type, int(row), int(col), num,
cell['parameters'], cell['attributes'], sanitize_name(cellname), cell)

Expand Down Expand Up @@ -2239,16 +2245,16 @@ def __init__(self, row, col, idx, attrs, flags, connections):
'ELVDS_IOBUF': 'LVCMOS33D',
}
_vcc_ios = {'LVCMOS12': '1.2', 'LVCMOS15': '1.5', 'LVCMOS18': '1.8', 'LVCMOS25': '2.5',
'LVCMOS33': '3.3', 'LVDS25': '2.5', 'LVCMOS33D': '3.3', 'LVCMOS_D': '3.3'}
'LVCMOS33': '3.3', 'LVDS25': '2.5', 'LVCMOS33D': '3.3', 'LVCMOS_D': '3.3', 'MIPI': '1.2'}
_init_io_attrs = {
'IBUF': {'PADDI': 'PADDI', 'HYSTERESIS': 'NONE', 'PULLMODE': 'UP', 'SLEWRATE': 'SLOW',
'DRIVE': '0', 'CLAMP': 'OFF', 'OPENDRAIN': 'OFF', 'DIFFRESISTOR': 'OFF',
'VREF': 'OFF', 'LVDS_OUT': 'OFF'},
'OBUF': {'ODMUX_1': '1', 'PULLMODE': 'UP', 'SLEWRATE': 'FAST',
'DRIVE': '8', 'HYSTERESIS': 'NONE', 'CLAMP': 'OFF', 'DIFFRESISTOR': 'OFF',
'DRIVE': '8', 'HYSTERESIS': 'NONE', 'CLAMP': 'OFF',
'SINGLERESISTOR': 'OFF', 'VCCIO': '1.8', 'LVDS_OUT': 'OFF', 'DDR_DYNTERM': 'NA', 'TO': 'INV', 'OPENDRAIN': 'OFF'},
'TBUF': {'ODMUX_1': 'UNKNOWN', 'PULLMODE': 'UP', 'SLEWRATE': 'FAST',
'DRIVE': '8', 'HYSTERESIS': 'NONE', 'CLAMP': 'OFF', 'DIFFRESISTOR': 'OFF',
'DRIVE': '8', 'HYSTERESIS': 'NONE', 'CLAMP': 'OFF',
'SINGLERESISTOR': 'OFF', 'VCCIO': '1.8', 'LVDS_OUT': 'OFF', 'DDR_DYNTERM': 'NA',
'TO': 'INV', 'PERSISTENT': 'OFF', 'ODMUX': 'TRIMUX', 'OPENDRAIN': 'OFF'},
'IOBUF': {'ODMUX_1': 'UNKNOWN', 'PULLMODE': 'UP', 'SLEWRATE': 'FAST',
Expand Down Expand Up @@ -2325,6 +2331,12 @@ def place_slice(db, tiledata, tile, parms, num):
mode = str(parms['FF_TYPE']).strip('E')
place_dff(db, tiledata, tile, parms, num, mode)

_mipi_aux_attrs = {
'A': {('IO_TYPE', 'LVDS25'), ('LPRX_A2', 'ENABLE'), ('ODMUX', 'TRIMUX'), ('OPENDRAIN', 'OFF'),
('DIFFRESISTOR', 'OFF'), ('VCCIO', '2.5')},
'B': {('IO_TYPE', 'LVDS25'), ('VCCIO', '2.5')},
}

_sides = "AB"
def place(db, tilemap, bels, cst, args):
for typ, row, col, num, parms, attrs, cellname, cell in bels:
Expand Down Expand Up @@ -2383,6 +2395,19 @@ def place(db, tilemap, bels, cst, args):
pass
elif typ.startswith('MUX2_'):
pass
elif typ.startswith("MIPI_OBUF"):
pass
elif typ.startswith("MIPI_IBUF_AUX"):
for iob_idx in ['A', 'B']:
iob_attrs = set()
for k, val in _mipi_aux_attrs[iob_idx]:
add_attr_val(db, 'IOB', iob_attrs, attrids.iob_attrids[k], attrids.iob_attrvals[val])
bits = get_longval_fuses(db, tiledata.ttyp, iob_attrs, f'IOB{iob_idx}')
for row_, col_ in bits:
tile[row_][col_] = 1
pass
elif typ.startswith("MIPI_IBUF"):
pass
elif typ == "BUFS":
# fuses must be reset in order to activate so remove them
bits2zero = set()
Expand Down Expand Up @@ -2433,6 +2458,8 @@ def place(db, tilemap, bels, cst, args):
bel_name = f"IO{edge}{idx}{num}"
cst.ports[cellname] = bel_name
iob = tiledata.bels[f'IOB{num}']
if 'MIPI_IBUF' in parms and num == 'B':
continue
if 'DIFF' in parms:
# skip negative pin for lvds
if parms['DIFF'] == 'N':
Expand Down Expand Up @@ -2491,6 +2518,11 @@ def place(db, tilemap, bels, cst, args):
else:
io_desc.attrs[flag_name_val[0][1:]] = flag_name_val[1]
io_desc.attrs['IO_TYPE'] = iostd
if 'DIFF' in parms and 'MIPI_OBUF' in parms:
io_desc.attrs['MIPI'] = 'ENABLE'
if 'I3C_IOBUF' in parms:
io_desc.attrs['I3C_IOBUF'] = 'ENABLE'

if pinless_io:
return
elif typ.startswith("RAM16SDP") or typ == "RAMW":
Expand Down Expand Up @@ -2626,7 +2658,7 @@ def place(db, tilemap, bels, cst, args):
iob.attrs['IO_TYPE'] = get_iostd_alias(iob.attrs['IO_TYPE'])
if iob.attrs.get('SINGLERESISTOR', 'OFF') != 'OFF':
iob.attrs['DDR_DYNTERM'] = 'ON'
if iob.flags['mode'] in {'OBUF', 'IOBUF', 'TLVDS_IOBUF', 'ELVDS_IOBUF'}:
if iob.flags['mode'] in {'OBUF', 'IOBUF', 'TLVDS_OBUF', 'TLVDS_IOBUF', 'TLVDS_TBUF', 'TLVDS_TBUF', 'ELVDS_OBUF', 'ELVDS_IOBUF'}:
if not vccio:
iostd = iob.attrs['IO_TYPE']
vccio = _vcc_ios[iostd]
Expand Down Expand Up @@ -2674,19 +2706,34 @@ def place(db, tilemap, bels, cst, args):
k = refine_io_attrs(k)
in_iob_attrs[k] = val
in_iob_attrs['VCCIO'] = in_bank_attrs['VCCIO']
#print(in_iob_attrs)
#print(name, in_iob_attrs)

# lvds
if iob.flags['mode'] in {'TLVDS_OBUF', 'TLVDS_TBUF', 'TLVDS_IOBUF'}:
in_iob_attrs.update({'LVDS_OUT': 'ON', 'ODMUX_1': 'UNKNOWN', 'ODMUX': 'TRIMUX',
'SLEWRATE': 'FAST', 'DRIVE': '0', 'PERSISTENT': 'OFF'})
'SLEWRATE': 'FAST', 'PERSISTENT': 'OFF'})
elif iob.flags['mode'] in {'ELVDS_OBUF', 'ELVDS_TBUF', 'ELVDS_IOBUF'}:
in_iob_attrs.update({'ODMUX_1': 'UNKNOWN', 'ODMUX': 'TRIMUX',
'PERSISTENT': 'OFF'})
in_iob_attrs['IO_TYPE'] = get_iostd_alias(in_iob_attrs['IO_TYPE'])
if iob.flags['mode'] in {'TLVDS_IBUF', 'ELVDS_IBUF'}:
in_iob_attrs['ODMUX_1'] = 'UNKNOWN'
in_iob_attrs.pop('VCCIO', None)
if 'IO_TYPE' in in_iob_attrs and in_iob_attrs['IO_TYPE'] == 'MIPI':
in_iob_attrs['LPRX_A1'] = 'ENABLE'
in_iob_attrs.pop('SLEWRATE', None)
in_iob_attrs.pop('VCCIO', None)
in_iob_attrs['PULLMODE'] = 'NONE'
in_iob_attrs['LVDS_ON'] = 'ENABLE'
in_iob_attrs['IOBUF_MIPI_LP'] = 'ENABLE'
if 'I3C_IOBUF' in in_iob_attrs:
in_iob_attrs.pop('I3C_IOBUF', None)
in_iob_attrs['PULLMODE'] = 'NONE'
in_iob_attrs['OPENDRAIN'] = 'OFF'
in_iob_attrs['OD'] = 'ENABLE'
in_iob_attrs['DIFFRESISTOR'] = 'NA'
in_iob_attrs['SINGLERESISTOR'] = 'NA'
in_iob_attrs['DRIVE'] = '16'

# XXX may be here do GW9 pins also
if device == 'GW1N-1':
Expand All @@ -2696,6 +2743,13 @@ def place(db, tilemap, bels, cst, args):
if mode[1:].startswith('LVDS') and in_iob_attrs['DRIVE'] != '0':
in_iob_attrs['DRIVE'] = 'UNKNOWN'
in_iob_b_attrs = {}
if 'IO_TYPE' in in_iob_attrs and in_iob_attrs['IO_TYPE'] == 'MIPI':
in_iob_attrs['IO_TYPE'] = 'LVDS25'
in_iob_b_attrs['IO_TYPE'] = 'LVDS25'
in_iob_b_attrs['PULLMODE'] = 'NONE'
in_iob_b_attrs['OPENDRAIN'] = 'OFF'
in_iob_b_attrs['IOBUF_MIPI_LP'] = 'ENABLE'
in_iob_b_attrs['PERSISTENT'] = 'OFF'
if iob.flags['mode'] in {'TLVDS_OBUF', 'TLVDS_TBUF', 'TLVDS_IOBUF'}:
in_iob_b_attrs = in_iob_attrs.copy()
elif iob.flags['mode'] in {'TLVDS_IBUF', 'ELVDS_IBUF'}:
Expand All @@ -2712,36 +2766,40 @@ def place(db, tilemap, bels, cst, args):
in_iob_b_attrs = in_iob_attrs.copy()

for iob_idx, atr in [(idx, in_iob_attrs), ('B', in_iob_b_attrs)]:
#print(name, iob.pos, atr)
iob_attrs = set()
for k, val in atr.items():
if k not in attrids.iob_attrids:
print(f'XXX IO: add {k} key handle')
elif k == 'OPENDRAIN' and val == 'OFF' and 'LVDS' not in iob.flags['mode'] and 'IBUF' not in iob.flags['mode']:
continue
#elif k == 'OPENDRAIN' and val == 'OFF' and 'LVDS' not in iob.flags['mode'] and 'IBUF' not in iob.flags['mode']:
#continue
else:
add_attr_val(db, 'IOB', iob_attrs, attrids.iob_attrids[k], attrids.iob_attrvals[val])
if k in {'VCCIO'}:
continue
if k == 'LVDS_OUT' and val not in {'ENABLE', 'ON'}:
continue
if k == 'IO_TYPE' and k in in_bank_attrs and in_bank_attrs[k].startswith('LVDS'):
continue
in_bank_attrs[k] = val
#print(row, col, atr)
bits = get_longval_fuses(db, tiledata.ttyp, iob_attrs, f'IOB{iob_idx}')
tile = tilemap[(row, col)]
for row_, col_ in bits:
tile[row_][col_] = 1
if idx == 'B':
break

# bank bits
brow, bcol = db.bank_tiles[bank]
tiledata = db.grid[brow][bcol]

bank_attrs = set()
for k, val in in_bank_attrs.items():
#print(k, val)
if k not in attrids.iob_attrids:
print(f'XXX BANK: add {k} key handle')
else:
add_attr_val(db, 'IOB', bank_attrs, attrids.iob_attrids[k], attrids.iob_attrvals[val])
if k in {'VCCIO', 'IO_TYPE'}:
add_attr_val(db, 'IOB', bank_attrs, attrids.iob_attrids[k], attrids.iob_attrvals[val])
bits = get_bank_fuses(db, tiledata.ttyp, bank_attrs, 'BANK', int(bank))
btile = tilemap[(brow, bcol)]
for row, col in bits:
Expand Down
Loading
Loading