From 1742d09edbdd2ae045111eebc3e4f23b02319a92 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Sat, 20 Sep 2025 15:51:01 +1000 Subject: [PATCH] Gowin. GW5A series PLLs. (#1557) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PLLA-type PLLs are implemented, which are used in GW5A-25A chips. These are six powerful PLLs, each of which can generate seven independent frequencies. Since these devices have an unusual configuration—their fuse bits are located outside the main grid and therefore their Bels do not have specific “correct” coordinates—the extra bel functions mechanism is used to describe them. But all the complexity falls on the apicula part. Signed-off-by: YRabbit --- himbaechel/uarch/gowin/constids.inc | 11 +++++++- himbaechel/uarch/gowin/globals.cc | 4 +-- himbaechel/uarch/gowin/gowin_arch_gen.py | 30 ++++++++++---------- himbaechel/uarch/gowin/gowin_utils.cc | 5 ++-- himbaechel/uarch/gowin/pack.cc | 35 ++++++++++++++++++------ 5 files changed, 56 insertions(+), 29 deletions(-) diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index 69b7f2e6..1e8c6b50 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -1033,6 +1033,7 @@ X(OSCO) X(rPLL) X(RPLLA) X(PLLVR) +X(PLLA) // primitive attributes X(INIT) @@ -1145,7 +1146,15 @@ X(P47B) X(P47C) X(P47D) -// PLL parameters +// PLL +X(CLKOUT0) +X(CLKOUT1) +X(CLKOUT2) +X(CLKOUT3) +X(CLKOUT4) +X(CLKOUT5) +X(CLKOUT6) +X(CLKOUT7) X(CLKOUTPS) X(CLKOUTDIV) X(CLKOUTDIV3) diff --git a/himbaechel/uarch/gowin/globals.cc b/himbaechel/uarch/gowin/globals.cc index e5b89e92..fa7bb2aa 100644 --- a/himbaechel/uarch/gowin/globals.cc +++ b/himbaechel/uarch/gowin/globals.cc @@ -77,12 +77,12 @@ struct GowinGlobalRouter IdString dst_type = ctx->getWireType(dst); bool src_is_outpin = src_type.in(id_IO_O, id_PLL_O, id_HCLK, id_DLLDLY, id_OSCOUT); bool src_valid = not_dcs_pip && (src_type == id_GLOBAL_CLK || src_is_outpin); - bool dst_valid = not_dcs_pip && dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_IO_I, id_HCLK); + bool dst_valid = not_dcs_pip && dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_PLL_O, id_IO_I, id_HCLK); bool res; if (src == src_wire && (src_type == id_PLL_O || (!src_is_outpin))) { bool dst_is_spine = dst_name.str(ctx).rfind("SPINE", 0) == 0; - res = src_valid && dst_is_spine; + res = src_valid && (dst_is_spine || dst_type == id_PLL_O); } else { res = (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid); } diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index c6172355..c4fb0651 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -510,8 +510,7 @@ def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int): if not tt.has_wire(dst): tt.create_wire(dst, get_wire_type(dst)) for src in srcs.keys(): - if src not in db.wire_delay: - continue + assert src in db.wire_delay, f"No timing info for {src} wire" if not tt.has_wire(src): if src in {"VSS", "VCC"}: tt.create_wire(src, get_wire_type(src), const_value = src) @@ -770,6 +769,15 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int): if not tt.has_wire(wire): tt.create_wire(wire, "PINCFG_IN") tt.add_bel_pin(bel, port, wire, PinType.INPUT) + elif func == 'pll': + pll = tt.create_bel("PLL", "PLLA", z = PLL_Z) + pll.flags = BEL_FLAG_GLOBAL + for pin, wire in desc['outputs'].items(): + tt.create_wire(wire, "PLL_O") + tt.add_bel_pin(pll, pin, wire, PinType.OUTPUT) + for pin, wire in desc['inputs'].items(): + tt.create_wire(wire, "PLL_I") + tt.add_bel_pin(pll, pin, wire, PinType.INPUT) def set_wire_flags(tt: TileType, tdesc: TypeDesc): if tdesc.extra_func and 'clock_gates' in tdesc.extra_func: @@ -1314,13 +1322,6 @@ def create_dsp_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc return tt # PLL main tile -_pll_inputs = {'CLKFB', 'FBDSEL0', 'FBDSEL1', 'FBDSEL2', 'FBDSEL3', - 'FBDSEL4', 'FBDSEL5', 'IDSEL0', 'IDSEL1', 'IDSEL2', 'IDSEL3', - 'IDSEL4', 'IDSEL5', 'ODSEL0', 'ODSEL1', 'ODSEL2', 'ODSEL3', - 'ODSEL4', 'ODSEL5', 'RESET', 'RESET_P', 'PSDA0', 'PSDA1', - 'PSDA2', 'PSDA3', 'DUTYDA0', 'DUTYDA1', 'DUTYDA2', 'DUTYDA3', - 'FDLY0', 'FDLY1', 'FDLY2', 'FDLY3', 'CLKIN', 'VREN'} -_pll_outputs = {'CLKOUT', 'LOCK', 'CLKOUTP', 'CLKOUTD', 'CLKOUTD3'} def create_pll_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc: TypeDesc): typename = "PLL" tiletype = f"{typename}_{ttyp}" @@ -1337,8 +1338,8 @@ def create_pll_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc tt = chip.create_tile_type(tiletype) tt.extra_data = TileExtraData(chip.strs.id(typename)) - # wires + pll_outputs = {'CLKOUT', 'LOCK', 'CLKOUTP', 'CLKOUTD', 'CLKOUTD3'} if chip.name == 'GW1NS-4': pll_name = 'PLLVR' bel_type = 'PLLVR' @@ -1349,13 +1350,12 @@ def create_pll_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int, tdesc pll = tt.create_bel("PLL", bel_type, z = PLL_Z) pll.flags = BEL_FLAG_GLOBAL for pin, wire in portmap.items(): - if pin in _pll_inputs: - tt.create_wire(wire, "PLL_I") - tt.add_bel_pin(pll, pin, wire, PinType.INPUT) - else: - assert pin in _pll_outputs, f"Unknown PLL pin {pin}" + if pin in pll_outputs: tt.create_wire(wire, "PLL_O") tt.add_bel_pin(pll, pin, wire, PinType.OUTPUT) + else: + tt.create_wire(wire, "PLL_I") + tt.add_bel_pin(pll, pin, wire, PinType.INPUT) tdesc.tiletype = tiletype return tt diff --git a/himbaechel/uarch/gowin/gowin_utils.cc b/himbaechel/uarch/gowin/gowin_utils.cc index 605ae2c4..b2b41569 100644 --- a/himbaechel/uarch/gowin/gowin_utils.cc +++ b/himbaechel/uarch/gowin/gowin_utils.cc @@ -48,8 +48,9 @@ bool GowinUtils::driver_is_clksrc(const PortRef &driver) } } // PLL outputs - if (driver.cell->type.in(id_rPLL, id_PLLVR)) { - if (driver.port.in(id_CLKOUT, id_CLKOUTD, id_CLKOUTD3, id_CLKOUTP)) { + if (driver.cell->type.in(id_rPLL, id_PLLVR, id_PLLA)) { + if (driver.port.in(id_CLKOUT, id_CLKOUTD, id_CLKOUTD3, id_CLKOUTP, id_CLKOUT0, id_CLKOUT1, id_CLKOUT2, + id_CLKOUT3, id_CLKOUT4, id_CLKOUT5, id_CLKOUT6, id_CLKOUT7)) { if (ctx->debug) { if (driver.cell->bel != BelId()) { log_info("PLL out bel:%s:%s\n", ctx->nameOfBel(driver.cell->bel), driver.port.c_str(ctx)); diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index d2e3c96d..0a8d7155 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -3905,16 +3905,33 @@ struct GowinPacker for (auto &cell : ctx->cells) { auto &ci = *cell.second; - if (ci.type.in(id_rPLL, id_PLLVR)) { + if (ci.type.in(id_rPLL, id_PLLVR, id_PLLA)) { // pin renaming for compatibility - for (int i = 0; i < 6; ++i) { - ci.renamePort(ctx->idf("FBDSEL[%d]", i), ctx->idf("FBDSEL%d", i)); - ci.renamePort(ctx->idf("IDSEL[%d]", i), ctx->idf("IDSEL%d", i)); - ci.renamePort(ctx->idf("ODSEL[%d]", i), ctx->idf("ODSEL%d", i)); - if (i < 4) { - ci.renamePort(ctx->idf("PSDA[%d]", i), ctx->idf("PSDA%d", i)); - ci.renamePort(ctx->idf("DUTYDA[%d]", i), ctx->idf("DUTYDA%d", i)); - ci.renamePort(ctx->idf("FDLY[%d]", i), ctx->idf("FDLY%d", i)); + if (ci.type == id_PLLA) { + for (int i = 0; i < 8; ++i) { + ci.renamePort(ctx->idf("MDWDI[%d]", i), ctx->idf("MDWDI%d", i)); + ci.renamePort(ctx->idf("MDRDO[%d]", i), ctx->idf("MDRDO%d", i)); + if (i < 7) { + ci.renamePort(ctx->idf("SSCMDSEL[%d]", i), ctx->idf("SSCMDSEL%d", i)); + if (i < 3) { + ci.renamePort(ctx->idf("SSCMDSEL_FRAC[%d]", i), ctx->idf("SSCMDSEL_FRAC%d", i)); + ci.renamePort(ctx->idf("PSSEL[%d]", i), ctx->idf("PSSEL%d", i)); + } + if (i < 2) { + ci.renamePort(ctx->idf("MDOPC[%d]", i), ctx->idf("MDOPC%d", i)); + } + } + } + } else { + for (int i = 0; i < 6; ++i) { + ci.renamePort(ctx->idf("FBDSEL[%d]", i), ctx->idf("FBDSEL%d", i)); + ci.renamePort(ctx->idf("IDSEL[%d]", i), ctx->idf("IDSEL%d", i)); + ci.renamePort(ctx->idf("ODSEL[%d]", i), ctx->idf("ODSEL%d", i)); + if (i < 4) { + ci.renamePort(ctx->idf("PSDA[%d]", i), ctx->idf("PSDA%d", i)); + ci.renamePort(ctx->idf("DUTYDA[%d]", i), ctx->idf("DUTYDA%d", i)); + ci.renamePort(ctx->idf("FDLY[%d]", i), ctx->idf("FDLY%d", i)); + } } } // If CLKIN is connected to a special pin, then it makes sense