From 13eedd30c8c9180726aa914768f4ba5c36b69265 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Sat, 25 Dec 2021 02:49:21 +1000 Subject: [PATCH] Get the IOB encoding from the vendor tables (#70) * Get the IOB encoding from the vendor tables The database is populated from tables for the following I/O attributes: - PULL_MODE - HYSTERESIS - SLEW_RATE - DRIVE - OPEN_DRAIN The SINGLE_RESISTOR attribute and the LVCMOS12 I/O standard are currently disabled. Also the unpacking of the IOB is probably broken. Signed-off-by: YRabbit * Add support for GW1NS-4 to the IOB table reader This won't actually allow you to build the base for GW1NS-4 from the tables by itself --- that requires changes to pindef and a bunch of other places, but I want to make it easier to merge with pepijndevos' work. Signed-off-by: YRabbit * Use the different package for the GW1NS-4 fuzzing Previously a partnumber was used, which was missing in our chip database. For the user the replacement does not matter, but for internal testing it is critical: I compare the generated images with the vendor tool and the free one and the packages must match. Signed-off-by: YRabbit * Read the tables only for the allowed modes Modes are used, the correctness of which has already been confirmed by fuzzing. This is due to the fact that some chips have defective pins that do not support all IO standards. Hence the requirement to read tables only after general fuzzing. Signed-off-by: YRabbit * Process simplified IO GW1N-1 ultra simple IO cells, which are IOBUFs only and turn into IBUFs or OBUFs by connecting the OEN input to VCC or VSS respectively, require special processing. Here fuzzig detects the involvement of the routing bits and in this case marks these cells as a special type of IOBS (iob simplified). nextpnr can then generate the necessary routes for the OEN input connection in order to obtain the desired I/O buffer type. Signed-off-by: YRabbit --- apycula/chipdb.py | 18 ++ apycula/gowin_bba.py | 5 +- apycula/gowin_pack.py | 35 +++- apycula/gowin_unpack.py | 15 +- apycula/tiled_fuzzer.py | 408 +++++++++++++++++++++++++++++----------- 5 files changed, 365 insertions(+), 116 deletions(-) diff --git a/apycula/chipdb.py b/apycula/chipdb.py index efd3db18..187e2b9a 100644 --- a/apycula/chipdb.py +++ b/apycula/chipdb.py @@ -40,6 +40,9 @@ class Bel: flags: Dict[Union[int, str], Set[Coord]] = field(default_factory=dict) # { iostd: { mode : IOBMode}} iob_flags: Dict[str, Dict[str, IOBMode]] = field(default_factory=dict) + lvcmos121518_bits: Set[Coord] = field(default_factory = set) + # this Bel is IOBUF and needs routing to become IBUF or OBUF + simplified_iob: bool = field(default = False) # banks bank_mask: Set[Coord] = field(default_factory=set) bank_flags: Dict[str, Set[Coord]] = field(default_factory=dict) @@ -404,6 +407,14 @@ def dff_clean(dev): for mode, bits in bel.modes.items(): bits -= extra_bits +def get_route_bits(db, row, col): + """ All routing bits for the cell """ + bits = set() + for w in db.grid[row][col].pips.values(): + for v in w.values(): + bits.update(v) + return bits + def diff2flag(dev): """ Minimize bits for flag values and calc flag bitmask""" seen_bels = [] @@ -414,11 +425,18 @@ def diff2flag(dev): if not bel.iob_flags or bel in seen_bels: continue seen_bels.append(bel) + # get routing bits for cell + rbits = get_route_bits(dev, idx, jdx) # If for a given mode all possible values of one flag # contain some bit, then this bit is "noise" --- this bit # belongs to the default value of another flag. Remove. for iostd, iostd_rec in bel.iob_flags.items(): for mode, mode_rec in iostd_rec.items(): + # if encoding has routing + r_encoding = mode_rec.encode_bits & rbits + mode_rec.encode_bits -= rbits + if r_encoding and mode != 'IOBUF': + bel.simplified_iob = True mode_rec.decode_bits = mode_rec.encode_bits.copy() for flag, flag_rec in mode_rec.flags.items(): noise_bits = None diff --git a/apycula/gowin_bba.py b/apycula/gowin_bba.py index 40f36ba6..d001e245 100644 --- a/apycula/gowin_bba.py +++ b/apycula/gowin_bba.py @@ -68,7 +68,10 @@ def write_pips(b, pips): def write_bels(b, bels): with b.block("bels") as blk: for typ, bel in bels.items(): - b.u16(id_string(typ)) + if bel.simplified_iob: + b.u16(id_string(f'{typ}S')) + else: + b.u16(id_string(typ)) with b.block("portmap") as port_blk: for dest, src in bel.portmap.items(): b.u16(id_string(dest)) diff --git a/apycula/gowin_pack.py b/apycula/gowin_pack.py index 339bbca9..d2c6ef8b 100644 --- a/apycula/gowin_pack.py +++ b/apycula/gowin_pack.py @@ -2,6 +2,7 @@ import os import re import pickle +import itertools import numpy as np import json import argparse @@ -60,6 +61,13 @@ def infovaluemap(infovalue, start=2): "SSTL33_II" : "SSTL33_I", "LVTTL33" : "LVCMOS33", } +# For each bank, remember the Bels used, mark whether Outs were among them and the standard. +class BankDesc: + def __init__(self, iostd, inputs_only, bels_tiles): + self.iostd = iostd + self.inputs_only = inputs_only + self.bels_tiles = bels_tiles + _banks = {} _sides = "AB" def place(db, tilemap, bels, cst, args): @@ -98,7 +106,7 @@ def place(db, tilemap, bels, cst, args): # XXX skip power if not cellname.startswith('\$PACKER'): cst.cells[cellname] = (row, col, int(num) // 2, _sides[int(num) % 2]) - elif typ == "IOB": + elif typ[:3] == "IOB": edge = 'T' idx = col; if row == db.rows: @@ -124,7 +132,7 @@ def place(db, tilemap, bels, cst, args): pinless_io = False try: bank = chipdb.loc2bank(db, row - 1, col - 1) - iostd = _banks.setdefault(bank, None) + iostd = _banks.setdefault(bank, BankDesc(None, True, [])).iostd except KeyError: if not args.allow_pinless_io: raise Exception(f"IO{edge}{idx}{num} is not allowed for a given package") @@ -148,11 +156,19 @@ def place(db, tilemap, bels, cst, args): if not iostd: iostd = "LVCMOS18" if not pinless_io: - _banks[bank] = iostd + _banks[bank].iostd = iostd + if mode == 'IBUF': + _banks[bank].bels_tiles.append((iob, tile)) + else: + _banks[bank].inputs_only = False cst.attrs.setdefault(cellname, {}).update({"IO_TYPE": iostd}) # collect flag bits bits = iob.iob_flags[iostd][mode].encode_bits.copy() + # XXX OPEN_DRAIN must be after DRIVE + attrs_keys = attrs.keys() + if 'OPEN_DRAIN=ON' in attrs_keys: + attrs_keys = itertools.chain(attrs_keys, ['OPEN_DRAIN=ON']) for flag in attrs.keys(): flag_name_val = flag.split("=") if len(flag_name_val) < 2: @@ -161,6 +177,10 @@ def place(db, tilemap, bels, cst, args): continue if flag_name_val[0] == chipdb.mode_attr_sep + "IO_TYPE": continue + # skip OPEN_DRAIN=OFF can't clear by mask and OFF is the default + if flag_name_val[0] == chipdb.mode_attr_sep + "OPEN_DRAIN" \ + and flag_name_val[1] == 'OFF': + continue # set flag mode_desc = iob.iob_flags[iostd][mode] try: @@ -191,7 +211,14 @@ def place(db, tilemap, bels, cst, args): bits |= bank_bel.bank_flags[iostd] for row, col in bits: tile[row][col] = 1 - + # If the entire bank has only inputs, the LVCMOS12/15/18 bit is set + # in each IBUF regardless of the actual I/O standard. + for _, bank_desc in _banks.items(): + if bank_desc.inputs_only: + if bank_desc.iostd in {'LVCMOS33', 'LVCMOS25'}: + for bel, tile in bank_desc.bels_tiles: + for row, col in bel.lvcmos121518_bits: + tile[row][col] = 1 def route(db, tilemap, pips): for row, col, src, dest in pips: diff --git a/apycula/gowin_unpack.py b/apycula/gowin_unpack.py index 20c351a8..051aebb7 100644 --- a/apycula/gowin_unpack.py +++ b/apycula/gowin_unpack.py @@ -30,6 +30,14 @@ def zero_bits(mode, all_modes): m_mask.update(flag.mask) return res.difference(all_modes[mode].decode_bits).difference(m_mask) +# If the length of the bit pattern is equal, start the comparison with IOBUF +def _io_mode_sort_func(mode): + l = len(mode[1].decode_bits) * 10 + if mode[0] == 'IOBUF': + l += 2 + elif mode[1] == 'OBUF': + l += 1 + return l # noiostd --- this is the case when the function is called # with iostd by default, e.g. from the clock fuzzer # With normal gowun_unpack io standard is determined first and it is known. @@ -39,6 +47,7 @@ def parse_tile_(db, row, col, tile, default=True, noalias=False, noiostd = True) bels = {} for name, bel in tiledata.bels.items(): if name[0:3] == "IOB": + #print(name) if noiostd: iostd = '' else: @@ -49,7 +58,7 @@ def parse_tile_(db, row, col, tile, default=True, noalias=False, noiostd = True) # Here we don't use a mask common to all modes (it didn't work), # instead we try the longest bit sequence first. for mode, mode_rec in sorted(bel.iob_flags[iostd].items(), - key = lambda m: len(m[1].decode_bits), reverse = True): + key = _io_mode_sort_func, reverse = True): # print(mode, mode_rec.decode_bits) mode_bits = {(row, col) for row, col in mode_rec.decode_bits @@ -249,7 +258,7 @@ def tile2verilog(dbrow, dbcol, bels, pips, clock_pips, mod, cfg, cst, db): cst.cells[name] = (row, col, int(idx) // 2, _sides[int(idx) % 2]) make_muxes(row, col, idx, db, mod) elif typ == "ALU": - print(flags) + #print(flags) kind, = flags # ALU only have one flag idx = int(idx) name = f"R{row}C{col}_ALU_{idx}" @@ -277,7 +286,7 @@ def tile2verilog(dbrow, dbcol, bels, pips, clock_pips, mod, cfg, cst, db): mod.primitives[name] = alu elif typ == "DFF": - print(flags) + #print(flags) kind, = flags # DFF only have one flag idx = int(idx) port = dffmap[kind] diff --git a/apycula/tiled_fuzzer.py b/apycula/tiled_fuzzer.py index 6b8f4dc2..1cd32564 100644 --- a/apycula/tiled_fuzzer.py +++ b/apycula/tiled_fuzzer.py @@ -28,34 +28,78 @@ if not gowinhome: raise Exception("GOWINHOME not set") +# XXX +# The indexes of the flag values depend on the device. +# So far I have not found where it is described in the tables +def recode_idx_gw1n1(idx): + return idx + +def recode_idx_gw1ns_2(idx): + new_idx = idx + 1 + if idx >= 69: + new_idx += 3 + if idx >= 80: + new_idx += 1 + return new_idx + +def recode_idx_gw1ns_4(idx): + new_idx = idx + if idx >= 48: + new_idx -= 1 + if idx >= 55: + new_idx -= 1 + if idx >= 70: + new_idx -= 3 + return new_idx + +def recode_idx_gw1n9(idx): + new_idx = idx + if idx >= 69: + new_idx += 3 + return new_idx + +def recode_idx_gw1n4(idx): + new_idx = idx + if idx >= 48: + new_idx -= 1 + if idx >= 55: + new_idx -= 1 + if idx >= 70: + new_idx -= 2 + return new_idx + # device = os.getenv("DEVICE") device = sys.argv[1] - params = { "GW1NS-2": { "package": "LQFP144", - "device": "GW1NS-2C-LQ144-5", + "device": "GW1NS-2C-LQFP144-5", "partnumber": "GW1NS-UX2CLQ144C5/I4", + "recode_idx": recode_idx_gw1ns_2, }, "GW1NS-4": { "package": "QFN48", - "device": "GW1NS-4C-QFN48-6", - "partnumber": "GW1NS-LV4CQN48C6/I5", + "device": "GW1NSR-4C-QFN48-7", + "partnumber": "GW1NSR-LV4CQN48PC7/I6", + "recode_idx": recode_idx_gw1ns_4, }, "GW1N-9": { "package": "PBGA256", "device": "GW1N-9-PBGA256-6", "partnumber": "GW1N-LV9PG256C6/I5", + "recode_idx": recode_idx_gw1n9, }, "GW1N-4": { "package": "PBGA256", "device": "GW1N-4-PBGA256-6", "partnumber": "GW1N-LV4PG256C6/I5", + "recode_idx": recode_idx_gw1n4, }, "GW1N-1": { "package": "LQFP144", "device": "GW1N-1-LQFP144-6", "partnumber": "GW1N-LV1LQ144C6/I5", + "recode_idx": recode_idx_gw1n1, }, }[device] @@ -159,88 +203,25 @@ def is_illegal(iostd, pin, attr): "IOBUF": {"wires": ["I", "O", "OEN"], "inouts": ["IO"]}, } -iostd_drive = { - "" : ["4", "8", "12"], - "LVTTL33" : ["4", "8", "12", "16", "24"], - "LVCMOS33" : ["4", "8", "12", "16", "24"], - "LVCMOS25" : ["4", "8", "12", "16"], - "LVCMOS18" : ["4", "8", "12"], - "LVCMOS15" : ["4", "8"], - "LVCMOS12" : ["4", "8"], - "SSTL25_I" : ["8"], - "SSTL25_II" : ["8"], - "SSTL33_I" : ["8"], - "SSTL33_II" : ["8"], - "SSTL18_I" : ["8"], - "SSTL18_II" : ["8"], - "SSTL15" : ["8"], - "HSTL18_I" : ["8"], - "HSTL18_II" : ["8"], - "HSTL15_I" : ["8"], - "PCI33" : [], - } iostd_open_drain = { - "" : ["ON", "OFF"], - "LVTTL33" : ["ON", "OFF"], - "LVCMOS33" : ["ON", "OFF"], - "LVCMOS25" : ["ON", "OFF"], - "LVCMOS18" : ["ON", "OFF"], - "LVCMOS15" : ["ON", "OFF"], - "LVCMOS12" : ["ON", "OFF"], - "SSTL25_I" : [], - "SSTL25_II" : [], - "SSTL33_I" : [], - "SSTL33_II" : [], - "SSTL18_I" : [], - "SSTL18_II" : [], - "SSTL15" : [], - "HSTL18_I" : [], - "HSTL18_II" : [], - "HSTL15_I" : [], - "PCI33" : [], + "" , + "LVCMOS33", + "LVCMOS25", + "LVCMOS18", + "LVCMOS15", + "LVCMOS12", } iostd_histeresis = { - "" : ["NONE", "H2L", "L2H", "HIGH"], - "LVTTL33" : ["NONE", "H2L", "L2H", "HIGH"], - "LVCMOS33" : ["NONE", "H2L", "L2H", "HIGH"], - "LVCMOS25" : ["NONE", "H2L", "L2H", "HIGH"], - "LVCMOS18" : ["NONE", "H2L", "L2H", "HIGH"], - "LVCMOS15" : ["NONE", "H2L", "L2H", "HIGH"], - "LVCMOS12" : ["NONE", "H2L", "L2H", "HIGH"], - "SSTL25_I" : [], - "SSTL25_II" : [], - "SSTL33_I" : [], - "SSTL33_II" : [], - "SSTL18_I" : [], - "SSTL18_II" : [], - "SSTL15" : [], - "HSTL18_I" : [], - "HSTL18_II" : [], - "HSTL15_I" : [], - "PCI33" : ["NONE", "H2L", "L2H", "HIGH"], - } -iostd_pull_mode = { - "" : ["NONE", "UP", "DOWN", "KEEPER"], - "LVTTL33" : ["NONE", "UP", "DOWN", "KEEPER"], - "LVCMOS33" : ["NONE", "UP", "DOWN", "KEEPER"], - "LVCMOS25" : ["NONE", "UP", "DOWN", "KEEPER"], - "LVCMOS18" : ["NONE", "UP", "DOWN", "KEEPER"], - "LVCMOS15" : ["NONE", "UP", "DOWN", "KEEPER"], - "LVCMOS12" : ["NONE", "UP", "DOWN", "KEEPER"], - "SSTL25_I" : [], - "SSTL25_II" : [], - "SSTL33_I" : [], - "SSTL33_II" : [], - "SSTL18_I" : [], - "SSTL18_II" : [], - "SSTL15" : [], - "HSTL18_I" : [], - "HSTL18_II" : [], - "HSTL15_I" : [], - "PCI33" : [], + "" , + "LVCMOS33", + "LVCMOS25", + "LVCMOS18", + "LVCMOS15", + "LVCMOS12", + "PCI33" , } -iostandards = ["", "LVCMOS18", "LVCMOS33", "LVCMOS25", "LVCMOS15", "LVCMOS12", +iostandards = ["", "LVCMOS18", "LVCMOS33", "LVCMOS25", "LVCMOS15", "SSTL25_I", "SSTL33_I", "SSTL15", "HSTL18_I", "PCI33"] AttrValues = namedtuple('ModeAttr', [ @@ -251,14 +232,231 @@ def is_illegal(iostd, pin, attr): iobattrs = { "IO_TYPE" : AttrValues(["IBUF", "OBUF", "IOBUF"], [""], None), - "OPEN_DRAIN" : AttrValues([ "OBUF", "IOBUF"], None, iostd_open_drain), - "HYSTERESIS" : AttrValues(["IBUF", "IOBUF"], None, iostd_histeresis), - "PULL_MODE" : AttrValues(["IBUF", "OBUF", "IOBUF"], None, iostd_pull_mode), - "SLEW_RATE" : AttrValues([ "OBUF", "IOBUF"], ["SLOW", "FAST"], None), - "DRIVE" : AttrValues([ "OBUF", "IOBUF"], None, iostd_drive), - "SINGLE_RESISTOR" : AttrValues(["IBUF", "IOBUF"], ["ON", "OFF"], None), + #"SINGLE_RESISTOR" : AttrValues(["IBUF", "IOBUF"], ["ON", "OFF"], None), } +def tbrl2rc(fse, side, num): + if side == 'T': + row = 0 + col = int(num) - 1 + elif side == 'B': + row = len(fse['header']['grid'][61])-1 + col = int(num) - 1 + elif side == 'L': + row = int(num) - 1 + col = 0 + elif side == 'R': + row = int(num) - 1 + col = len(fse['header']['grid'][61][0])-1 + return (row, col) + +# get fuse bits from longval table +# the key is automatically sorted and appended with zeros. +# If ignore_key_elem is set, the initial elements in the table record keys +# is ignored when searching. +def get_longval(fse, ttyp, table, key, ignore_key_elem = 0): + bits = set() + sorted_key = (sorted(key) + [0] * 16)[:16 - ignore_key_elem] + for rec in fse[ttyp]['longval'][table]: + k = rec[ignore_key_elem:16] + if k == sorted_key: + fuses = [f for f in rec[16:] if f != -1] + for fuse in fuses: + bits.update({fuse_h4x.fuse_lookup(fse, ttyp, fuse)}) + break + return bits + +# diff boards have diff key indexes +def recode_key(key): + return set(map(params['recode_idx'], key)) + +# IOB from tables +# (code, {option values}, is cmos-like mode, GW1N-4 aliases) +_iostd_codes = { + # XXX default LVCMOS18 + "" : ( 66, {'4', '8', '12'}, True, {'4': None, '8': 51, '12': 53}), + "LVCMOS33" : ( 68, {'4', '8', '12', '16', '24'}, True, {'4': 48, '8': None, '12': 50, '16': 51, '24': 53}), + "LVCMOS25" : ( 67, {'4', '8', '12', '16'}, True, {'4': None, '8': 50, '12': 51, '16': 53}), + "LVCMOS18" : ( 66, {'4', '8', '12'}, True, {'4': None, '8': 51, '12': 53}), + "LVCMOS15" : ( 65, {'4', '8'}, True, {'4': 50, '8': 53}), + "LVCMOS12" : ( 64, {'4', '8'}, True, {'4': 50, '8': 53}), + "SSTL25_I" : ( 71, {'8'}, False, {'8': 50}), + "SSTL25_II" : ( 71, {'8'}, False, {'8': 50}), + "SSTL33_I" : ( -1, {'8'}, False, {'8': None}), + "SSTL33_II" : ( -1, {'8'}, False, {'8': None}), + "SSTL18_I" : ( 72, {'8'}, False, {'8': 51}), + "SSTL18_II" : ( 72, {'8'}, False, {'8': 51}), + "SSTL15" : ( 74, {'8'}, False, {'8': 51}), + "HSTL18_I" : ( 72, {'8'}, False, {'8': 53}), + "HSTL18_II" : ( 72, {'8'}, False, {'8': 53}), + "HSTL15_I" : ( 74, {'8'}, False, {'8': 51}), + "PCI33" : ( 69, {'4', '8'}, False, {'4':48, '8': None}), + } + +# PULL_MODE +_pin_mode_longval = {'A':23, 'B':24, 'C':40, 'D':41, 'E':42, 'F':43, 'G':44, 'H':45, 'I':46, 'J':47} +_pull_mode_iob = ["IBUF", "OBUF", "IOBUF"] +_tbrlre = re.compile(r"IO([TBRL])(\d+)") +_pull_mode_idx = { 'UP' : -1, 'NONE' : 45, 'KEEPER' : 44, 'DOWN' : 43} +def fse_pull_mode(fse, db, pin_locations): + for ttyp, tiles in pin_locations.items(): + pin_loc = list(tiles.keys())[0] + side, num = _tbrlre.match(pin_loc).groups() + row, col = tbrl2rc(fse, side, num) + bels = {name[-1] for loc in tiles.values() for name in loc} + for bel_idx in bels: + bel = db.grid[row][col].bels.setdefault(f"IOB{bel_idx}", chipdb.Bel()) + for iostd, b_iostd in bel.iob_flags.items(): + for io_mode in _pull_mode_iob: + b_mode = b_iostd.setdefault(io_mode, chipdb.IOBMode()) + b_attr = b_mode.flags.setdefault('PULL_MODE', chipdb.IOBFlag()) + for opt_name, val in _pull_mode_idx.items(): + if val == -1: + loc = set() + else: + loc = get_longval(fse, ttyp, _pin_mode_longval[bel_idx], recode_key({val})) + b_attr.options[opt_name] = loc + +# LVCMOS12/15/18 fuse +def get_12_15_18_bits(fse, ttyp, pin): + return get_longval(fse, ttyp, _pin_mode_longval[pin], recode_key({66})) + +# SLEW_RATE +_slew_rate_iob = [ "OBUF", "IOBUF"] +_slew_rate_idx = { 'SLOW' : -1, 'FAST' : 42} +def fse_slew_rate(fse, db, pin_locations): + for ttyp, tiles in pin_locations.items(): + pin_loc = list(tiles.keys())[0] + side, num = _tbrlre.match(pin_loc).groups() + row, col = tbrl2rc(fse, side, num) + bels = {name[-1] for loc in tiles.values() for name in loc} + for bel_idx in bels: + bel = db.grid[row][col].bels.setdefault(f"IOB{bel_idx}", chipdb.Bel()) + for iostd, b_iostd in bel.iob_flags.items(): + for io_mode in _slew_rate_iob: + b_mode = b_iostd.setdefault(io_mode, chipdb.IOBMode()) + b_attr = b_mode.flags.setdefault('SLEW_RATE', chipdb.IOBFlag()) + for opt_name, val in _slew_rate_idx.items(): + if val == -1: + loc = set() + else: + loc = get_longval(fse, ttyp, _pin_mode_longval[bel_idx], recode_key({val})) + b_attr.options[opt_name] = loc + +# DRIVE +_drive_iob = [ "OBUF", "IOBUF"] +_drive_idx = {'4': {48}, '8': {50}, '12': {51}, '16': {52}, '24': {54}} +_drive_key = {56} +def fse_drive(fse, db, pin_locations): + for ttyp, tiles in pin_locations.items(): + pin_loc = list(tiles.keys())[0] + side, num = _tbrlre.match(pin_loc).groups() + row, col = tbrl2rc(fse, side, num) + bels = {name[-1] for loc in tiles.values() for name in loc} + for bel_idx in bels: + bel = db.grid[row][col].bels.setdefault(f"IOB{bel_idx}", chipdb.Bel()) + for iostd, b_iostd in bel.iob_flags.items(): + for io_mode in _drive_iob: + b_mode = b_iostd.setdefault(io_mode, chipdb.IOBMode()) + b_attr = b_mode.flags.setdefault('DRIVE', chipdb.IOBFlag()) + for opt_name, val in _drive_idx.items(): + iostd_key, iostd_vals, iostd_cmos, gw1n4_aliases = _iostd_codes[iostd] + if opt_name not in iostd_vals: + continue + # XXX + if iostd_key == -1 or (iostd == "PCI33" and opt_name == '8'): + loc = set() + else: + if device in ['GW1N-4', 'GW1NS-4']: + opt_key = gw1n4_aliases[opt_name] + if opt_key: + val = _drive_key.union({opt_key}) + loc = get_longval(fse, ttyp, _pin_mode_longval[bel_idx], + recode_key(val), 1) + else: + loc = set() + else: + val = {iostd_key}.union(_drive_key) + if iostd_cmos: + val = val.union(_drive_idx[opt_name]) + loc = get_longval(fse, ttyp, _pin_mode_longval[bel_idx], + recode_key(val), 1) + b_attr.options[opt_name] = loc + +# OPEN_DRAIN +_open_drain_iob = [ "OBUF", "IOBUF"] +_open_drain_key = {"ON": {55, 70}, "NOISE": {55, 72}} +_open_drain_gw1n4_key = {"ON": {49, 54}, "NOISE": {51, 54}} +def fse_open_drain(fse, db, pin_locations): + for ttyp, tiles in pin_locations.items(): + pin_loc = list(tiles.keys())[0] + side, num = _tbrlre.match(pin_loc).groups() + row, col = tbrl2rc(fse, side, num) + bels = {name[-1] for loc in tiles.values() for name in loc} + for bel_idx in bels: + bel = db.grid[row][col].bels.setdefault(f"IOB{bel_idx}", chipdb.Bel()) + for iostd, b_iostd in bel.iob_flags.items(): + if iostd not in iostd_open_drain: + continue + # XXX presumably OPEN_DRAIN is another DRIVE mode, strange as it may sound. + # Three fuses are used: ON=100, i.e. one is set and the other two are cleared, + # OFF=xxx (xxx != 100) + # These are the same fuses that are used for DRIVE and in the future you can + # come up with a smarter way to find them. + # XXX Below is a very shamanic method of determining the fuses, + iostd33_key, _, _, gw1n4_aliases = _iostd_codes["LVCMOS33"] + if device in ['GW1N-4', 'GW1NS-4']: + cur16ma_key = _drive_key.union({gw1n4_aliases["16"]}) + keys = _open_drain_gw1n4_key + else: + cur16ma_key = {iostd33_key}.union(_drive_key).union(_drive_idx["16"]) + keys = _open_drain_key + # ON fuse is simple + on_fuse = get_longval(fse, ttyp, _pin_mode_longval[bel_idx], + recode_key(keys['ON']), 1) + # the mask to clear is diff between 16mA fuses of LVCMOS33 standard and + # some key + cur16ma_fuse = get_longval(fse, ttyp, _pin_mode_longval[bel_idx], + recode_key(cur16ma_key), 1) + noise_fuse = get_longval(fse, ttyp, _pin_mode_longval[bel_idx], + recode_key(keys['NOISE']), 1) + clear_mask = cur16ma_fuse - noise_fuse - on_fuse; + for io_mode in _open_drain_iob: + b_mode = b_iostd.setdefault(io_mode, chipdb.IOBMode()) + b_attr = b_mode.flags.setdefault('OPEN_DRAIN', chipdb.IOBFlag()) + # bits of this attribute are the same as the DRIVE bits + # so make a flag mask here, also never use OFF when encoding, only ON + b_attr.mask = clear_mask.union(on_fuse) + b_attr.options["OFF"] = set() + b_attr.options["ON"] = on_fuse.copy() + #print(b_attr.options) + +# HYSTERESIS +_hysteresis_iob = [ "IBUF", "IOBUF"] +_hysteresis_idx = { 'NONE': -1, 'HIGH': {57, 85}, 'H2L': {58, 85}, 'L2H': {59, 85}} +def fse_hysteresis(fse, db, pin_locations): + for ttyp, tiles in pin_locations.items(): + pin_loc = list(tiles.keys())[0] + side, num = _tbrlre.match(pin_loc).groups() + row, col = tbrl2rc(fse, side, num) + bels = {name[-1] for loc in tiles.values() for name in loc} + for bel_idx in bels: + bel = db.grid[row][col].bels.setdefault(f"IOB{bel_idx}", chipdb.Bel()) + for iostd, b_iostd in bel.iob_flags.items(): + if iostd not in iostd_histeresis: + continue + for io_mode in _hysteresis_iob: + b_mode = b_iostd.setdefault(io_mode, chipdb.IOBMode()) + b_attr = b_mode.flags.setdefault('HYSTERESIS', chipdb.IOBFlag()) + for opt_name, val in _hysteresis_idx.items(): + if val == -1: + loc = set() + else: + loc = get_longval(fse, ttyp, _pin_mode_longval[bel_idx], + recode_key(val), 1) + b_attr.options[opt_name] = loc + +# IOB fuzzer def find_next_loc(pin, locs): # find the next location that has pin # or make a new module @@ -269,7 +467,6 @@ def find_next_loc(pin, locs): return name return None - def iob(locations): for iostd in iostandards: for ttyp, tiles in locations.items(): # for each tile of this type @@ -280,10 +477,10 @@ def iob(locations): bels = {name[-1] for loc in tiles.values() for name in loc} for pin in bels: # [A, B, C, D, ...] for attr, attr_values in iobattrs.items(): # each IOB attribute + # XXX remove if iostd == "PCI33" and attr == "SINGLE_RESISTOR": continue attr_vals = attr_values.values - # drive is special if attr_vals == None: attr_vals = attr_values.table[iostd] for attr_val in attr_vals: # each value of the attribute @@ -525,7 +722,6 @@ def run_pnr(mod, constr, config): configs += [reduce(lambda a, b: {**a, **b}, c, {}) for c in zip_longest(*data.cfgmap.values(), fillvalue={})] - type_re = re.compile(r"inst\d+_([A-Z]+)_([A-Z]+)") pnr_empty = run_pnr(codegen.Module(), codegen.Constraints(), {}) @@ -558,20 +754,8 @@ def run_pnr(mod, constr, config): row = row-1 col = col-1 elif cst_type == "place": - side, numx, pin = info - num = int(numx) - if side == 'T': - row = 0 - col = num-1 - elif side == 'B': - row = len(fse['header']['grid'][61])-1 - col = num-1 - elif side == 'L': - row = num-1 - col = 0 - elif side == 'R': - row = num-1 - col = len(fse['header']['grid'][61][0])-1 + side, num, pin = info + row, col = tbrl2rc(fse, side, num) print(name, row, col, side, num, pin) typ = fse['header']['grid'][61][row][col] @@ -609,8 +793,7 @@ def run_pnr(mod, constr, config): } elif bel_type == "IOB": bel = db.grid[row][col].bels.setdefault(f"IOB{pin}", chipdb.Bel()) - if cell_type == "IOBUF": - loc -= route_bits(db, row, col) + bel.lvcmos121518_bits = get_12_15_18_bits(fse, typ, pin) pnr_attrs = pnr.attrs.get(name) if pnr_attrs: # first get iostd @@ -630,6 +813,8 @@ def run_pnr(mod, constr, config): # set mode bits b_iostd = bel.iob_flags.setdefault(iostd, {}) b_mode = b_iostd.setdefault(cell_type, chipdb.IOBMode()) + if cell_type == "IBUF" and iostd in {'LVCMOS25', 'LVCMOS33'}: + loc -= bel.lvcmos121518_bits b_mode.encode_bits = loc else: # IO_TYPE and some attr @@ -677,12 +862,19 @@ def run_pnr(mod, constr, config): else: bel.modes["ENABLE"] = loc + # Fill the IOB encodings from fse tables + fse_pull_mode(fse, db, pin_locations) + fse_slew_rate(fse, db, pin_locations) + fse_hysteresis(fse, db, pin_locations) + fse_drive(fse, db, pin_locations) + chipdb.dat_portmap(dat, db) chipdb.dat_aliases(dat, db) chipdb.diff2flag(db) + + # must be after diff2flags in order to make clean mask for OPEN_DRAIN + fse_open_drain(fse, db, pin_locations) chipdb.dff_clean(db) - # XXX not used for IOB but... - #chipdb.shared2flag(db) db.grid[0][0].bels['CFG'].flags['UNK0'] = {(3, 1)} db.grid[0][0].bels['CFG'].flags['UNK1'] = {(3, 2)}