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)}