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