From bea46f9bdbec1ddeb864d5f8a91e062fcc99af01 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Sat, 20 Jul 2024 11:05:16 +1000 Subject: [PATCH] Add DHCEN primitive. Added parts necessary to generate chip databases with DHCEN support for all supported boards. The packing was made with the necessary fuses set. DHCEN is provided as a wire to disable the input MUXes of HCLK, effectively turning off everything that is "beyond" those MUXes. Most of the work happens in the nextpnr part. Signed-off-by: YRabbit --- apycula/attrids.py | 64 ++++++++++++++++++++++++++++++++++++++ apycula/chipdb.py | 71 +++++++++++++++++++++++++++++++++++++++++++ apycula/gowin_pack.py | 45 ++++++++++++++++++++++++++- 3 files changed, 179 insertions(+), 1 deletion(-) diff --git a/apycula/attrids.py b/apycula/attrids.py index bb5d45f6..294dbdf8 100644 --- a/apycula/attrids.py +++ b/apycula/attrids.py @@ -1140,6 +1140,70 @@ 'SYNC': 2, } +# HCLK +hclk_attrids = { + 'BK00DIV2_RST': 0, + 'BK01DIV2_RST': 1, + 'BK0MUX0_OUTSEL': 2, + 'BK0MUX1_OUTSEL': 3, + 'BK10DIV2_RST': 4, + 'BK11DIV2_RST': 5, + 'BK1MUX0_OUTSEL': 6, + 'BK1MUX1_OUTSEL': 7, + 'BRGMUX0_BRGOUT': 8, + 'BRGMUX0_INSEL': 9, + 'BRGMUX1_BRGOUT': 10, + 'BRGMUX1_INSEL': 11, + 'BRGMUX0_BRGSTOP': 12, + 'BRGMUX1_BRGSTOP': 13, + 'HCLKDIV0_DIV': 14, + 'HCLKDIV0_RST': 15, + 'HCLKDIV1_DIV': 16, + 'HCLKDIV1_RST': 17, + 'HSB0MUX0_HSTOP': 18, + 'HSB0MUX1_HSTOP': 19, + 'HSB1MUX0_HSTOP': 20, + 'HSB1MUX1_HSTOP': 21, + 'HCLKDCS0_SEL': 22, + 'HCLKDCS1_SEL': 23, + 'DCC0': 24, + 'DCC1': 25, + 'DLYMUX': 26, + 'HCLKDIV_DIV': 27, + 'HCLKDIV_RST': 28, + } + +hclk_attrvals = { + 'UNKNOWN': 0, + 'DIVCIBRST2': 1, + 'DIVCIBRST3': 2, + 'DIV2': 3, + 'DIVCIBRST0': 4, + 'DIVCIBRST1': 5, + 'DIVCIBRST4': 6, + 'DIVCIBRST5': 7, + 'ENABLE': 8, + 'BRGCIBSEL0': 9, + 'BRGCIBSEL1': 10, + 'BRGCIBSTOP0': 11, + 'BRGCIBSTOP1': 12, + '2': 13, + '3.5': 14, + '4': 15, + '5': 16, + 'HCLKCIBSTOP0': 17, + 'HCLKCIBSTOP1': 18, + 'HCLKCIBSTOP2': 19, + 'HCLKCIBSTOP3': 20, + 'HCLKBK10': 21, + 'HCLKBK11': 22, + '8': 23, + 'DIVCIBRST': 24, + 'NEG_80': 25, + '80': 26, + } + + # iologic iologic_attrids = { 'INMODE': 0, diff --git a/apycula/chipdb.py b/apycula/chipdb.py index ab8623d0..9c846c38 100644 --- a/apycula/chipdb.py +++ b/apycula/chipdb.py @@ -454,6 +454,7 @@ def set_banks(fse, db): 14: 'DSP', 15: 'PLL', 39: 'BSRAM_INIT', + 49: 'HCLK', 59: 'CFG', 62: 'OSC', 63: 'USB', @@ -487,6 +488,7 @@ def set_banks(fse, db): 45: 'IOBH', 46: 'IOBI', 47: 'IOBJ', + 50: 'HCLK', 51: 'OSC', 53: 'DLLDEL0', 54: 'DLLDEL1', @@ -1064,6 +1066,74 @@ def fse_create_hclk_nodes(dev, device, fse, dat: Datfile): if src.startswith('HCLK'): hclks[src].add((row, col, src)) +# DHCEN (as I imagine) is an additional control input of the HCLK input +# multiplexer. We have four input multiplexers - HCLK_IN0, HCLK_IN1, HCLK_IN2, +# HCLK_IN3 (GW1N-9C with its additional four multiplexers stands separately, +# but we will deal with it separately). +# Creating images using IDE where we use the maximum allowable number of DHCEN, +# the CE port of which is connected to the IO ports, then we trace the route +# from IO to the final wire, which will be the CE port of the DHCEN primitive. +# We are not interested in the CLKIN and CLKOUT ports because we are supposed +# to simply disable/enable one of the input multiplexers. +# Let's summarize the experimental data in a table. +# There are 4 multiplexers on each side of the chip (sides: Right Bottom Left Top). +_dhcen_ce = { + 'GW1N-1': + {'B' : [( 0, 19, 'D5'), ( 0, 19, 'D3'), ( 0, 19, 'D4'), ( 0, 19, 'D2')]}, + 'GW1NZ-1': + {'R' : [( 0, 19, 'A2'), ( 0, 19, 'A4'), ( 0, 19, 'A3'), ( 0, 19, 'A5')], + 'T' : [(10, 19, 'A2'), (10, 19, 'A4'), (10, 19, 'A3'), (10, 19, 'A5')]}, + 'GW1NS-2': + {'R' : [(10, 19, 'A4'), (10, 19, 'A6'), (10, 19, 'A5'), (10, 19, 'A7')], + 'B' : [(11, 19, 'A4'), (11, 19, 'A6'), (11, 19, 'A5'), (11, 19, 'A7')], + 'L' : [( 9, 0, 'A0'), ( 9, 0, 'A2'), ( 9, 0, 'A1'), ( 9, 0, 'A3')], + 'T' : [( 0, 19, 'D5'), ( 0, 19, 'D3'), ( 0, 19, 'D4'), ( 0, 19, 'D2')]}, + 'GW1N-4': + {'R' : [(18, 37, 'C6'), (18, 37, 'D7'), (18, 37, 'C7'), (18, 37, 'D6')], + 'B' : [(19, 37, 'A2'), (19, 37, 'A4'), (19, 37, 'A3'), (19, 37, 'A5')], + 'L' : [(18, 0, 'C6'), (18, 0, 'D7'), (18, 0, 'C7'), (18, 0, 'D6')]}, + 'GW1NS-4': + {'R' : [(18, 37, 'C6'), (18, 37, 'D7'), (18, 37, 'C7'), (18, 37, 'D6')], + 'B' : [(19, 37, 'A2'), (19, 37, 'A4'), (19, 37, 'A3'), (19, 37, 'A5')], + 'T' : [( 1, 0, 'B6'), ( 1, 0, 'A0'), ( 1, 0, 'B7'), ( 1, 0, 'A1')]}, + 'GW1N-9C': + {'R' : [(18, 46, 'C6'), (18, 46, 'D7'), (18, 46, 'C7'), (18, 46, 'D6')], + 'B' : [(28, 46, 'A2'), (28, 46, 'A4'), (28, 46, 'A3'), (28, 46, 'A5')], + 'L' : [(18, 0, 'C6'), (18, 0, 'D7'), (18, 0, 'C7'), (18, 0, 'D6')], + 'T' : [( 9, 0, 'C6'), ( 9, 0, 'D7'), ( 9, 0, 'C7'), ( 9, 0, 'D6')]}, + 'GW1N-9C': + {'R' : [(18, 46, 'C6'), (18, 46, 'D7'), (18, 46, 'C7'), (18, 46, 'D6')], + 'B' : [(28, 46, 'A2'), (28, 46, 'A4'), (28, 46, 'A3'), (28, 46, 'A5')], + 'L' : [(18, 0, 'C6'), (18, 0, 'D7'), (18, 0, 'C7'), (18, 0, 'D6')], + 'T' : [( 9, 0, 'C6'), ( 9, 0, 'D7'), ( 9, 0, 'C7'), ( 9, 0, 'D6')]}, + 'GW2A-18': + {'R' : [(27, 55, 'A2'), (27, 55, 'A3'), (27, 55, 'D2'), (27, 55, 'D3')], + 'B' : [(54, 27, 'A2'), (54, 27, 'A3'), (54, 27, 'D2'), (54, 27, 'D3')], + 'L' : [(27, 0, 'A2'), (27, 0, 'A3'), (27, 0, 'D2'), (27, 0, 'D3')], + 'T' : [( 0, 27, 'A2'), ( 0, 27, 'A3'), ( 0, 27, 'D2'), ( 0, 27, 'D3')]}, + 'GW2A-18C': + {'R' : [(27, 55, 'A2'), (27, 55, 'A3'), (27, 55, 'D2'), (27, 55, 'D3')], + 'B' : [(54, 27, 'A2'), (54, 27, 'A3'), (54, 27, 'D2'), (54, 27, 'D3')], + 'L' : [(27, 0, 'A2'), (27, 0, 'A3'), (27, 0, 'D2'), (27, 0, 'D3')], + 'T' : [( 0, 27, 'A2'), ( 0, 27, 'A3'), ( 0, 27, 'D2'), ( 0, 27, 'D3')]}, + } +def fse_create_dhcen(dev, device, fse, dat: Datfile): + if device not in _dhcen_ce: + print(f'No DHCEN for {device} for now.') + return + for side, ces in _dhcen_ce[device].items(): + for idx, ce_wire in enumerate(ces): + row, col, wire = ce_wire + extra = dev.extra_func.setdefault((row, col), {}) + dhcen = extra.setdefault('dhcen', []) + # use db.hclk_pips in order to find HCLK_IN cells + for hclk_loc in _hclk_to_fclk[device][side]['hclk']: + if f'HCLK_IN{idx}' in dev.hclk_pips[hclk_loc]: + hclkin = {'hclkin' : [f'X{hclk_loc[1]}Y{hclk_loc[0]}', f'HCLK_IN{idx}', side]} + hclkin.update({ 'ce' : wire}) + dhcen.append(hclkin) + + _pll_loc = { 'GW1N-1': {'TRPLL0CLK0': (0, 17, 'F4'), 'TRPLL0CLK1': (0, 17, 'F5'), @@ -1722,6 +1792,7 @@ def from_fse(device, fse, dat: Datfile): fse_create_gsr(dev, device) fse_create_bandgap(dev, device) fse_create_logic2clk(dev, device, dat) + fse_create_dhcen(dev, device, fse, dat) disable_plls(dev, device) sync_extra_func(dev) set_chip_flags(dev, device); diff --git a/apycula/gowin_pack.py b/apycula/gowin_pack.py index d1a40c86..646b3128 100644 --- a/apycula/gowin_pack.py +++ b/apycula/gowin_pack.py @@ -185,7 +185,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|BSRAM|ALU|MULTALU18X18|MULTALU36X18|MULTADDALU18X18|MULT36X36|MULT18X18|MULT9X9|PADD18|PADD9|BANDGAP)(\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|BSRAM|ALU|MULTALU18X18|MULTALU36X18|MULTADDALU18X18|MULT36X36|MULT18X18|MULT9X9|PADD18|PADD9|BANDGAP|DHCEN)(\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*)") @@ -1968,6 +1968,41 @@ def set_osc_attrs(db, typ, params): add_attr_val(db, 'OSC', fin_attrs, attrids.osc_attrids[attr], val) return fin_attrs +_wire2attr_val = { + 'HCLK_IN0': ('HSB0MUX0_HSTOP', 'HCLKCIBSTOP0'), + 'HCLK_IN1': ('HSB1MUX0_HSTOP', 'HCLKCIBSTOP2'), + 'HCLK_IN2': ('HSB0MUX1_HSTOP', 'HCLKCIBSTOP1'), + 'HCLK_IN3': ('HSB1MUX1_HSTOP', 'HCLKCIBSTOP3'), + } +def find_and_set_dhcen_hclk_fuses(db, tilemap, wire, side): + fin_attrs = set() + attr, attr_val = _wire2attr_val[wire] + val = attrids.hclk_attrvals[attr_val] + add_attr_val(db, 'HCLK', fin_attrs, attrids.hclk_attrids[attr], val) + + def set_fuse(): + ttyp = db.grid[row][col].ttyp + if 'HCLK' in db.shortval[ttyp]: + bits = get_shortval_fuses(db, ttyp, fin_attrs, "HCLK") + tile = tilemap[row, col] + for r, c in bits: + tile[r][c] = 1 + + if side in "TB": + if side == 'T': + row = 0 + else: + row = db.rows - 1 + for col in range(db.cols): + set_fuse() + else: + if side == 'R': + col = 0 + else: + col = db.col - 1 + for row in range(db.rows): + set_fuse() + _iologic_default_attrs = { 'DUMMY': {}, 'IOLOGIC': {}, @@ -2431,6 +2466,14 @@ def place(db, tilemap, bels, cst, args): cfg_tile = tilemap[(0, 37)] for r, c in bits: cfg_tile[r][c] = 1 + elif typ == "DHCEN": + if 'DHCEN_SIDE' not in attrs: + continue + # DHCEN as such is just a control wire and does not have a fuse + # itself, but HCLK has fuses that allow this control. Here we look + # for the corresponding HCLK and set its fuses. + _, wire, side = db.extra_func[row - 1, col -1]['dhcen'][int(num)]['hclk'] + hclk_attrs = find_and_set_dhcen_hclk_fuses(db, wire, side) else: print("unknown type", typ)