aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gowin/arch.cc123
-rw-r--r--gowin/arch.h4
-rw-r--r--gowin/cells.h2
-rw-r--r--gowin/constids.inc3
-rw-r--r--gowin/pack.cc11
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 <iostream>
#include <math.h>
#include <regex>
+#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<IdString> &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<IdString> 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<IdString, std::string> attrs;
CellInfo *bound_cell;
dict<IdString, PinInfo> pins;
+ std::vector<IdString> pin_cfgs;
DecalXY decalxy_active, decalxy_inactive;
int x, y, z;
bool gb;
@@ -474,8 +475,11 @@ struct Arch : BaseArch<ArchRanges>
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