From 2e68962a025999ec85276f6362540c13ccfcd752 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Sun, 4 Dec 2022 15:06:44 +1000 Subject: [PATCH] gowin: add PLL pins processing Uses the information of the special input pins for the PLL in the current chip. If such pins are involved, no routing is performed and information about the use of implicit wires is passed to the packer. The RESET and RESET_P inputs are now also disabled if they are connected to VSS/VCC. Signed-off-by: YRabbit --- gowin/arch.cc | 123 +++++++++++++++++++++++++++++++++++++++++++-- gowin/arch.h | 4 ++ gowin/cells.h | 2 + gowin/constids.inc | 3 ++ gowin/pack.cc | 11 ++-- 5 files changed, 133 insertions(+), 10 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 756580e0..ccfe2c4b 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -23,6 +23,7 @@ #include #include #include +#include "design_utils.h" #include "embed.h" #include "gfx.h" #include "nextpnr.h" @@ -1575,6 +1576,24 @@ Arch::Arch(ArchArgs args) : args(args) } } } + + // IO pin configs + for (unsigned int i = 0; i < package->num_pins; i++) { + const PinPOD *pin = &package->pins[i]; + if (pin->num_cfgs == 0) { + continue; + } + auto b = bels.find(IdString(pin->loc_id)); + if (b == bels.end()) { + // Not all pins are transmitted, e.g. MODE, DONE etc. + continue; + } + std::vector &cfgs = b->second.pin_cfgs; + for (unsigned int j = 0; j < pin->num_cfgs; ++j) { + cfgs.push_back(IdString(pin->cfgs[j])); + } + } + // setup pips for (int i = 0; i < db->rows * db->cols; i++) { int row = i / db->cols; @@ -1958,26 +1977,122 @@ bool Arch::place() return retVal; } +static bool is_spec_iob(const Context *ctx, const CellInfo *cell, IdString pin_name) +{ + if (!is_iob(ctx, cell)) { + return false; + } + std::vector const &cfgs = ctx->bels.at(cell->bel).pin_cfgs; + bool have_pin = std::find(cfgs.begin(), cfgs.end(), pin_name) != cfgs.end(); + return have_pin; +} + +static bool is_PLL_T_IN_iob(const Context *ctx, const CellInfo *cell) +{ + return is_spec_iob(ctx, cell, ctx->id("RPLL_T_IN")); +} + +static bool is_PLL_T_FB_iob(const Context *ctx, const CellInfo *cell) +{ + return is_spec_iob(ctx, cell, ctx->id("RPLL_T_FB")); +} + +// If the PLL input can be connected using a direct wire, then do so, +// bypassing conventional routing. +void Arch::fix_pll_nets(Context *ctx) +{ + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->type != id_RPLLA) { + continue; + } + // *** CLKIN + do { + if (!port_used(ci, id_CLKIN)) { + ci->setParam(id_INSEL, Property("UNKNOWN")); + break; + } + NetInfo *net = ci->getPort(id_CLKIN); + if (net->name == id("$PACKER_VCC_NET") || net->name == id("$PACKER_GND_NET")) { + ci->setParam(id_INSEL, Property("UNKNOWN")); + break; + } + if (net_driven_by(ctx, net, is_PLL_T_IN_iob, id_O) != nullptr) { + ci->disconnectPort(id_CLKIN); + ci->setParam(id_INSEL, Property("CLKIN0")); + break; + } + // XXX do special bels (HCLK etc) + // This is general routing through CLK0 pip + ci->setParam(id_INSEL, Property("CLKIN1")); + } while (0); + + do { + // *** CLKFB + if (str_or_default(ci->params, id_CLKFB_SEL, "internal") == "internal") { + ci->setParam(id_FBSEL, Property("CLKFB3")); + continue; + } + if (!port_used(ci, id_CLKFB)) { + ci->setParam(id_FBSEL, Property("UNKNOWN")); + continue; + } + NetInfo *net = ci->getPort(id_CLKFB); + if (net->name == id("$PACKER_VCC_NET") || net->name == id("$PACKER_GND_NET")) { + ci->setParam(id_FBSEL, Property("UNKNOWN")); + continue; + } + if (net_driven_by(ctx, net, is_PLL_T_FB_iob, id_O) != nullptr) { + ci->disconnectPort(id_CLKFB); + ci->setParam(id_FBSEL, Property("CLKFB2")); + break; + } + // XXX do special bels (HCLK etc) + // This is general routing through CLK2 pip + ci->setParam(id_FBSEL, Property("CLKFB0")); + } while (0); + + // resets + Property pr_enable("ENABLE"), pr_disable("DISABLE"); + NetInfo *net = ci->getPort(id_RESET); + ci->setParam(id_RSTEN, pr_enable); + if (!port_used(ci, id_RESET) || net->name == id("$PACKER_VCC_NET") || net->name == id("$PACKER_GND_NET")) { + ci->setParam(id_RSTEN, pr_disable); + } + ci->setParam(id_PWDEN, pr_enable); + net = ci->getPort(id_RESET_P); + if (!port_used(ci, id_RESET_P) || net->name == id("$PACKER_VCC_NET") || net->name == id("$PACKER_GND_NET")) { + ci->setParam(id_PWDEN, pr_disable); + } + } +} + +void Arch::pre_route(Context *ctx) { fix_pll_nets(ctx); } + +void Arch::post_route(Context *ctx) { fix_longwire_bels(); } + bool Arch::route() { std::string router = str_or_default(settings, id_router, defaultRouter); + Context *ctx = getCtx(); + pre_route(ctx); if (bool_or_default(settings, id("arch.enable-globals"))) { - route_gowin_globals(getCtx()); + route_gowin_globals(ctx); } bool result; if (router == "router1") { - result = router1(getCtx(), Router1Cfg(getCtx())); + result = router1(ctx, Router1Cfg(ctx)); } else if (router == "router2") { - router2(getCtx(), Router2Cfg(getCtx())); + router2(ctx, Router2Cfg(ctx)); result = true; } else { log_error("Gowin architecture does not support router '%s'\n", router.c_str()); } getCtx()->settings[id_route] = 1; archInfoToAttributes(); - fix_longwire_bels(); + post_route(ctx); return result; } diff --git a/gowin/arch.h b/gowin/arch.h index 0591e41a..3e614eba 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -227,6 +227,7 @@ struct BelInfo std::map attrs; CellInfo *bound_cell; dict pins; + std::vector pin_cfgs; DecalXY decalxy_active, decalxy_inactive; int x, y, z; bool gb; @@ -474,8 +475,11 @@ struct Arch : BaseArch void fix_longwire_bels(); void pre_pack(Context *ctx); void post_pack(Context *ctx); + void pre_route(Context *ctx); + void post_route(Context *ctx); void auto_longwires(); void add_plla_ports(BelsPOD const *bel, IdString belname, int row, int col); + void fix_pll_nets(Context *ctx); GowinGlobalRouter globals_router; void mark_gowin_globals(Context *ctx); diff --git a/gowin/cells.h b/gowin/cells.h index 227206c8..ae475b77 100644 --- a/gowin/cells.h +++ b/gowin/cells.h @@ -105,6 +105,8 @@ inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type inline bool is_sram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_RAM16SDP4; } +inline bool is_iob(const Context *ctx, const CellInfo *cell) { return (cell->type.index == ID_IOB); } + // Convert a LUT primitive to (part of) an GENERIC_SLICE, swapping ports // as needed. Set no_dff if a DFF is not being used, so that the output // can be reconnected diff --git a/gowin/constids.inc b/gowin/constids.inc index e0b37b49..610755bb 100644 --- a/gowin/constids.inc +++ b/gowin/constids.inc @@ -938,6 +938,9 @@ X(CLKOUTDIV3) X(PWDEN) X(RSTEN) X(FLOCK) +X(INSEL) +X(FBSEL) +X(CLKFB_SEL) // timing X(X0) diff --git a/gowin/pack.cc b/gowin/pack.cc index 1e0380c1..c36a4757 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -820,8 +820,6 @@ static bool is_gowin_iologic(const Context *ctx, const CellInfo *cell) } } -static bool is_iob(const Context *ctx, const CellInfo *cell) { return (cell->type.index == ID_IOB); } - // Pack IO logic static void pack_iologic(Context *ctx) { @@ -1021,10 +1019,11 @@ static void pack_plls(Context *ctx) if (parm_device == "GW1N-1" || parm_device == "GW1NZ-1") { // Unused ports will be disabled during image generation. Here we add flags for such ports. Property pr_enable("ENABLE"), pr_disable("DISABLE"); - IdString ports[][2] = {{id_CLKOUTP, id_CLKOUTPS}, {id_CLKOUTD, id_CLKOUTDIV}, - {id_CLKOUTD3, id_CLKOUTDIV3}, {id_LOCK, id_FLOCK}, - {id_RESET_P, id_PWDEN}, {id_RESET, id_RSTEN}}; - for (int i = 0; i < 6; ++i) { + IdString ports[][2] = {{id_CLKOUTP, id_CLKOUTPS}, + {id_CLKOUTD, id_CLKOUTDIV}, + {id_CLKOUTD3, id_CLKOUTDIV3}, + {id_LOCK, id_FLOCK}}; + for (int i = 0; i < 4; ++i) { ci->setParam(ports[i][1], port_used(ci, ports[i][0]) ? pr_enable : pr_disable); } // B half