aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYRabbit <rabbit@yrabbit.cyou>2022-07-04 10:32:39 +1000
committerYRabbit <rabbit@yrabbit.cyou>2022-07-04 10:32:39 +1000
commit3172a38daeb4588d3aaa686816c09d64ccf08587 (patch)
tree7dd071befb4f13acf4a135984856867fed881531
parent6d85de43ee1f9585e3c3a170f52513755ed924b5 (diff)
downloadnextpnr-3172a38daeb4588d3aaa686816c09d64ccf08587.tar.gz
nextpnr-3172a38daeb4588d3aaa686816c09d64ccf08587.tar.bz2
nextpnr-3172a38daeb4588d3aaa686816c09d64ccf08587.zip
gowin: Let the placer know about global networks
Refactor in order to detect networks that will be routed in a special mode earlier. This makes it possible to mark the source of such networks as a global buffer, thereby removing their influence on element placement. In addition, timing classes are set for some cells. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
-rw-r--r--gowin/arch.cc56
-rw-r--r--gowin/arch.h14
-rw-r--r--gowin/globals.cc486
-rw-r--r--gowin/globals.h66
-rw-r--r--gowin/pack.cc4
5 files changed, 367 insertions, 259 deletions
diff --git a/gowin/arch.cc b/gowin/arch.cc
index 7b9097c9..a968c112 100644
--- a/gowin/arch.cc
+++ b/gowin/arch.cc
@@ -25,7 +25,6 @@
#include <regex>
#include "embed.h"
#include "gfx.h"
-#include "globals.h"
#include "nextpnr.h"
#include "placer1.h"
#include "placer_heap.h"
@@ -257,6 +256,8 @@ bool Arch::allocate_longwire(NetInfo *ni, int lw_idx)
return true;
}
+void Arch::auto_longwires() {}
+
void Arch::fix_longwire_bels()
{
// After routing, it is clear which wires and in which bus SS00 and SS40 are used and
@@ -552,6 +553,28 @@ void Arch::setDelayScaling(double scale, double offset)
args.delayOffset = offset;
}
+void Arch::addCellTimingCombIn(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_COMB_INPUT; }
+
+void Arch::addCellTimingCombOut(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_COMB_OUTPUT; }
+
+void Arch::addCellTimingRegIn(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_REGISTER_INPUT; }
+
+void Arch::addCellTimingRegOut(IdString cell, IdString port)
+{
+ cellTiming[cell].portClasses[port] = TMG_REGISTER_OUTPUT;
+}
+
+void Arch::addCellTimingIO(IdString cell, IdString port)
+{
+ if (port == id_I) {
+ cellTiming[cell].portClasses[port] = TMG_ENDPOINT;
+ } else {
+ if (port == id_O) {
+ cellTiming[cell].portClasses[port] = TMG_STARTPOINT;
+ }
+ }
+}
+
void Arch::addCellTimingClock(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_CLOCK_INPUT; }
void Arch::addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayQuad delay)
@@ -1989,6 +2012,8 @@ void Arch::assignArchInfo()
// add timing paths
addCellTimingClock(cname, id_CLK);
+ addCellTimingRegIn(cname, id_CE);
+ addCellTimingRegIn(cname, id_LSR);
IdString ports[4] = {id_A, id_B, id_C, id_D};
for (int i = 0; i < 4; i++) {
DelayPair setup =
@@ -2019,7 +2044,18 @@ void Arch::assignArchInfo()
delay = delay + delayLookup(speed->lut.timings.get(), speed->lut.num_timings, id_fx_ofx1);
addCellTimingDelay(cname, id_I0, id_OF, delay);
addCellTimingDelay(cname, id_I1, id_OF, delay);
+ addCellTimingCombIn(cname, id_SEL);
}
+ case ID_IOB:
+ /* FALLTHRU */
+ case ID_IOBS:
+ addCellTimingIO(cname, id_I);
+ addCellTimingIO(cname, id_O);
+ break;
+ case ID_BUFS:
+ addCellTimingCombIn(cname, id_I);
+ addCellTimingCombOut(cname, id_O);
+ break;
default:
break;
}
@@ -2063,4 +2099,22 @@ bool Arch::cellsCompatible(const CellInfo **cells, int count) const
return true;
}
+void Arch::route_gowin_globals(Context *ctx) { globals_router.route_globals(ctx); }
+
+void Arch::mark_gowin_globals(Context *ctx) { globals_router.mark_globals(ctx); }
+// ---------------------------------------------------------------
+void Arch::pre_pack(Context *ctx)
+{
+ if (bool_or_default(settings, id("arch.enable-auto-longwires"))) {
+ auto_longwires();
+ }
+}
+
+void Arch::post_pack(Context *ctx)
+{
+ if (bool_or_default(settings, id("arch.enable-globals"))) {
+ mark_gowin_globals(ctx);
+ }
+}
+
NEXTPNR_NAMESPACE_END
diff --git a/gowin/arch.h b/gowin/arch.h
index c59f8eb3..cd43aa7a 100644
--- a/gowin/arch.h
+++ b/gowin/arch.h
@@ -31,6 +31,8 @@
#include "nextpnr_namespaces.h"
#include "nextpnr_types.h"
+#include "globals.h"
+
NEXTPNR_NAMESPACE_BEGIN
template <typename T> struct RelPtr
@@ -337,6 +339,11 @@ struct Arch : BaseArch<ArchRanges>
void setDelayScaling(double scale, double offset);
void addCellTimingClock(IdString cell, IdString port);
+ void addCellTimingIO(IdString cell, IdString port);
+ void addCellTimingCombIn(IdString cell, IdString port);
+ void addCellTimingCombOut(IdString cell, IdString port);
+ void addCellTimingRegIn(IdString cell, IdString port);
+ void addCellTimingRegOut(IdString cell, IdString port);
void addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayQuad delay);
void addCellTimingSetupHold(IdString cell, IdString port, IdString clock, DelayPair setup, DelayPair hold);
void addCellTimingClockToOut(IdString cell, IdString port, IdString clock, DelayQuad clktoq);
@@ -462,6 +469,13 @@ struct Arch : BaseArch<ArchRanges>
bool haveBelType(int x, int y, IdString bel_type);
bool allocate_longwire(NetInfo *ni, int lw_idx = -1);
void fix_longwire_bels();
+ void pre_pack(Context *ctx);
+ void post_pack(Context *ctx);
+ void auto_longwires();
+
+ GowinGlobalRouter globals_router;
+ void mark_gowin_globals(Context *ctx);
+ void route_gowin_globals(Context *ctx);
// chip db version
unsigned int const chipdb_version = 1;
diff --git a/gowin/globals.cc b/gowin/globals.cc
index ed75a938..1794dd4c 100644
--- a/gowin/globals.cc
+++ b/gowin/globals.cc
@@ -18,7 +18,6 @@
*
*/
-#include "globals.h"
#include <algorithm>
#include <iomanip>
#include <iostream>
@@ -31,306 +30,279 @@
NEXTPNR_NAMESPACE_BEGIN
-class GowinGlobalRouter
+bool GowinGlobalRouter::is_clock_port(PortRef const &user)
{
- public:
- GowinGlobalRouter(Context *ctx) : ctx(ctx){};
-
- private:
- // wire -> clock#
- dict<WireId, int> used_wires;
-
- // ordered nets
- struct onet_t
- {
- IdString name;
- int clock_ports;
- WireId clock_io_wire; // IO wire if there is one
-
- onet_t() : name(IdString()), clock_ports(0), clock_io_wire(WireId()) {}
- onet_t(IdString _name) : name(_name), clock_ports(0), clock_io_wire(WireId()) {}
-
- // sort
- bool operator<(const onet_t &other) const
- {
- if ((clock_io_wire != WireId()) ^ (other.clock_io_wire != WireId())) {
- return !(clock_io_wire != WireId());
- }
- return clock_ports < other.clock_ports;
- }
- // search
- bool operator==(const onet_t &other) const { return name == other.name; }
- };
+ if ((user.cell->type == id_SLICE || user.cell->type == id_ODDR || user.cell->type == id_ODDRC) &&
+ user.port == id_CLK) {
+ return true;
+ }
+ return false;
+}
- bool is_clock_port(PortRef const &user)
- {
- if ((user.cell->type == id_SLICE || user.cell->type == id_ODDR || user.cell->type == id_ODDRC) && user.port == id_CLK) {
- return true;
- }
- return false;
+std::pair<WireId, BelId> GowinGlobalRouter::clock_io(Context *ctx, PortRef const &driver)
+{
+ // XXX normally all alternative functions of the pins should be passed
+ // in the chip database, but at the moment we find them from aliases/pips
+ // XXX check diff inputs too
+ if (driver.cell == nullptr || driver.cell->type != id_IOB || !driver.cell->attrs.count(id_BEL)) {
+ return std::make_pair(WireId(), BelId());
}
+ // clock IOs have pips output->SPINExx
- WireId clock_io(PortRef const &driver)
- {
- // XXX normally all alternative functions of the pins should be passed
- // in the chip database, but at the moment we find them from aliases/pips
- // XXX check diff inputs too
- if (driver.cell == nullptr || driver.cell->bel == BelId()) {
- return WireId();
- }
- // clock IOs have pips output->SPINExx
- BelInfo &bel = ctx->bel_info(driver.cell->bel);
- if (bel.type != id_IOB) {
- return WireId();
+ BelInfo &bel = ctx->bel_info(ctx->id(driver.cell->attrs[id_BEL].as_string()));
+ WireId wire = bel.pins[id_O].wire;
+ for (auto const pip : ctx->getPipsDownhill(wire)) {
+ if (ctx->wire_info(ctx->getPipDstWire(pip)).type.str(ctx).rfind("SPINE", 0) == 0) {
+ return std::make_pair(wire, bel.name);
}
- WireId wire = bel.pins[id_O].wire;
- for (auto const pip : ctx->getPipsDownhill(wire)) {
- if (ctx->wire_info(ctx->getPipDstWire(pip)).type.str(ctx).rfind("SPINE", 0) == 0) {
- return wire;
- }
- }
- return WireId();
}
+ return std::make_pair(WireId(), BelId());
+}
- // gather the clock nets
- void gather_clock_nets(std::vector<onet_t> &clock_nets)
- {
- for (auto const &net : ctx->nets) {
- NetInfo const *ni = net.second.get();
- auto new_clock = clock_nets.end();
- WireId clock_wire = clock_io(ni->driver);
- if (clock_wire != WireId()) {
- clock_nets.emplace_back(net.first);
- new_clock = --clock_nets.end();
- new_clock->clock_io_wire = clock_wire;
- }
- for (auto const &user : ni->users) {
- if (is_clock_port(user)) {
- if (new_clock == clock_nets.end()) {
- clock_nets.emplace_back(net.first);
- new_clock = --clock_nets.end();
- }
- ++(new_clock->clock_ports);
+// gather the clock nets
+void GowinGlobalRouter::gather_clock_nets(Context *ctx, std::vector<globalnet_t> &clock_nets)
+{
+ for (auto const &net : ctx->nets) {
+ NetInfo const *ni = net.second.get();
+ auto new_clock = clock_nets.end();
+ auto clock_wire_bel = clock_io(ctx, ni->driver);
+ if (clock_wire_bel.first != WireId()) {
+ clock_nets.emplace_back(net.first);
+ new_clock = --clock_nets.end();
+ new_clock->clock_io_wire = clock_wire_bel.first;
+ new_clock->clock_io_bel = clock_wire_bel.second;
+ }
+ for (auto const &user : ni->users) {
+ if (is_clock_port(user)) {
+ if (new_clock == clock_nets.end()) {
+ clock_nets.emplace_back(net.first);
+ new_clock = --clock_nets.end();
}
+ ++(new_clock->clock_ports);
}
}
- // need to prioritize the nets
- std::sort(clock_nets.begin(), clock_nets.end());
+ }
+ // need to prioritize the nets
+ std::sort(clock_nets.begin(), clock_nets.end());
- if (ctx->verbose) {
- for (auto const &net : clock_nets) {
- log_info(" Net:%s, ports:%d, io:%s\n", net.name.c_str(ctx), net.clock_ports,
- net.clock_io_wire == WireId() ? "No" : net.clock_io_wire.c_str(ctx));
- }
+ if (ctx->verbose) {
+ for (auto const &net : clock_nets) {
+ log_info(" Net:%s, ports:%d, io:%s\n", net.name.c_str(ctx), net.clock_ports,
+ net.clock_io_wire == WireId() ? "No" : net.clock_io_wire.c_str(ctx));
}
}
+}
- // non clock port
- // returns GB pip
- IdString route_to_non_clock_port(WireId const dstWire, int clock, pool<IdString> &used_pips,
- pool<IdString> &undo_wires)
- {
- static std::vector<IdString> one_hop = {id_S111, id_S121, id_N111, id_N121, id_W111, id_W121, id_E111, id_E121};
- char buf[40];
- // uphill pips
- for (auto const uphill : ctx->getPipsUphill(dstWire)) {
- WireId srcWire = ctx->getPipSrcWire(uphill);
- if (find(one_hop.begin(), one_hop.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) !=
- one_hop.end()) {
- // found one hop pip
- if (used_wires.count(srcWire)) {
- if (used_wires[srcWire] != clock) {
- continue;
- }
+// non clock port
+// returns GB pip
+IdString GowinGlobalRouter::route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock,
+ pool<IdString> &used_pips, pool<IdString> &undo_wires)
+{
+ static std::vector<IdString> one_hop = {id_S111, id_S121, id_N111, id_N121, id_W111, id_W121, id_E111, id_E121};
+ char buf[40];
+ // uphill pips
+ for (auto const uphill : ctx->getPipsUphill(dstWire)) {
+ WireId srcWire = ctx->getPipSrcWire(uphill);
+ if (find(one_hop.begin(), one_hop.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) != one_hop.end()) {
+ // found one hop pip
+ if (used_wires.count(srcWire)) {
+ if (used_wires[srcWire] != clock) {
+ continue;
+ }
+ }
+ WireInfo wi = ctx->wire_info(srcWire);
+ std::string wire_alias = srcWire.str(ctx).substr(srcWire.str(ctx).rfind("_") + 1);
+ snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock, wire_alias.c_str());
+ IdString gb = ctx->id(buf);
+ auto up_pips = ctx->getPipsUphill(srcWire);
+ if (find(up_pips.begin(), up_pips.end(), gb) != up_pips.end()) {
+ if (!used_wires.count(srcWire)) {
+ used_wires.insert(std::make_pair(srcWire, clock));
+ undo_wires.insert(srcWire);
}
- WireInfo wi = ctx->wire_info(srcWire);
- std::string wire_alias = srcWire.str(ctx).substr(srcWire.str(ctx).rfind("_") + 1);
- snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock, wire_alias.c_str());
- IdString gb = ctx->id(buf);
- auto up_pips = ctx->getPipsUphill(srcWire);
- if (find(up_pips.begin(), up_pips.end(), gb) != up_pips.end()) {
- if (!used_wires.count(srcWire)) {
- used_wires.insert(std::make_pair(srcWire, clock));
- undo_wires.insert(srcWire);
- }
- used_pips.insert(uphill);
- if (ctx->verbose) {
- log_info(" 1-hop Pip:%s\n", uphill.c_str(ctx));
- }
- return gb;
+ used_pips.insert(uphill);
+ if (ctx->verbose) {
+ log_info(" 1-hop Pip:%s\n", uphill.c_str(ctx));
}
+ return gb;
}
}
- return IdString();
}
+ return IdString();
+}
- // route one net
- void route_net(onet_t const &net, int clock)
- {
- // For failed routing undo
- pool<IdString> used_pips;
- pool<IdString> undo_wires;
-
- log_info(" Route net %s, use clock #%d.\n", net.name.c_str(ctx), clock);
- for (auto const &user : ctx->net_info(net.name).users) {
- // >>> port <- GB<clock>0
- WireId dstWire = ctx->getNetinfoSinkWire(&ctx->net_info(net.name), user, 0);
- if (ctx->verbose) {
- log_info(" Cell:%s, port:%s, wire:%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx),
- dstWire.c_str(ctx));
- }
-
- char buf[30];
- PipId gb_pip_id;
- if (user.port == id_CLK) {
- WireInfo const wi = ctx->wire_info(dstWire);
- snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock,
- ctx->wire_info(dstWire).type.c_str(ctx));
- gb_pip_id = ctx->id(buf);
- // sanity
- NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gb_pip_id) !=
- ctx->getPipsUphill(dstWire).end());
- } else {
- // Non clock port
- gb_pip_id = route_to_non_clock_port(dstWire, clock, used_pips, undo_wires);
- if (gb_pip_id == IdString()) {
- if (ctx->verbose) {
- log_info(" Can't find route to %s, net %s will be routed in a standard way.\n",
- dstWire.c_str(ctx), net.name.c_str(ctx));
- }
- for (IdString const undo : undo_wires) {
- used_wires.erase(undo);
- }
- return;
- }
- }
- if (ctx->verbose) {
- log_info(" GB Pip:%s\n", gb_pip_id.c_str(ctx));
- }
+// route one net
+void GowinGlobalRouter::route_net(Context *ctx, globalnet_t const &net)
+{
+ // For failed routing undo
+ pool<IdString> used_pips;
+ pool<IdString> undo_wires;
- if (used_pips.count(gb_pip_id)) {
- if (ctx->verbose) {
- log_info(" ^routed already^\n");
- }
- continue;
- }
- used_pips.insert(gb_pip_id);
+ log_info(" Route net %s, use clock #%d.\n", net.name.c_str(ctx), net.clock);
+ for (auto const &user : ctx->net_info(net.name).users) {
+ // >>> port <- GB<clock>0
+ WireId dstWire = ctx->getNetinfoSinkWire(&ctx->net_info(net.name), user, 0);
+ if (ctx->verbose) {
+ log_info(" Cell:%s, port:%s, wire:%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx),
+ dstWire.c_str(ctx));
+ }
- // >>> GBOx <- GTx0
- dstWire = ctx->getPipSrcWire(gb_pip_id);
- WireInfo dstWireInfo = ctx->wire_info(dstWire);
- int branch_tap_idx = clock > 3 ? 1 : 0;
- snprintf(buf, sizeof(buf), "R%dC%d_GT%d0_GBO%d", dstWireInfo.y + 1, dstWireInfo.x + 1, branch_tap_idx,
- branch_tap_idx);
- PipId gt_pip_id = ctx->id(buf);
- if (ctx->verbose) {
- log_info(" GT Pip:%s\n", buf);
- }
+ char buf[30];
+ PipId gb_pip_id;
+ if (user.port == id_CLK) {
+ WireInfo const wi = ctx->wire_info(dstWire);
+ snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, net.clock,
+ ctx->wire_info(dstWire).type.c_str(ctx));
+ gb_pip_id = ctx->id(buf);
// sanity
- NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gt_pip_id) !=
+ NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gb_pip_id) !=
ctx->getPipsUphill(dstWire).end());
- // if already routed
- if (used_pips.count(gt_pip_id)) {
+ } else {
+ // Non clock port
+ gb_pip_id = route_to_non_clock_port(ctx, dstWire, net.clock, used_pips, undo_wires);
+ if (gb_pip_id == IdString()) {
if (ctx->verbose) {
- log_info(" ^routed already^\n");
+ log_info(" Can't find route to %s, net %s will be routed in a standard way.\n", dstWire.c_str(ctx),
+ net.name.c_str(ctx));
}
- continue;
- }
- used_pips.insert(gt_pip_id);
-
- // >>> GTx0 <- SPINExx
- // XXX no optimization here, we need to store
- // the SPINE <-> clock# correspondence in the database. In the
- // meantime, we define in run-time in a completely suboptimal way.
- std::vector<std::string> clock_spine;
- dstWire = ctx->getPipSrcWire(gt_pip_id);
- for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) {
- std::string name = ctx->wire_info(ctx->getPipSrcWire(uphill_pip)).type.str(ctx);
- if (name.rfind("SPINE", 0) == 0) {
- clock_spine.push_back(name);
+ for (IdString const undo : undo_wires) {
+ used_wires.erase(undo);
}
+ return;
}
- sort(clock_spine.begin(), clock_spine.end(), [](const std::string &a, const std::string &b) -> bool {
- return (a.size() < b.size()) || (a.size() == b.size() && a < b);
- });
- dstWireInfo = ctx->wire_info(dstWire);
- snprintf(buf, sizeof(buf), "R%dC%d_%s_GT%d0", dstWireInfo.y + 1, dstWireInfo.x + 1,
- clock_spine[clock - branch_tap_idx * 4].c_str(), branch_tap_idx);
- PipId spine_pip_id = ctx->id(buf);
+ }
+ if (ctx->verbose) {
+ log_info(" GB Pip:%s\n", gb_pip_id.c_str(ctx));
+ }
+
+ if (used_pips.count(gb_pip_id)) {
if (ctx->verbose) {
- log_info(" Spine Pip:%s\n", buf);
+ log_info(" ^routed already^\n");
}
- // sanity
- NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), spine_pip_id) !=
- ctx->getPipsUphill(dstWire).end());
- // if already routed
- if (used_pips.count(spine_pip_id)) {
- if (ctx->verbose) {
- log_info(" ^routed already^\n");
- }
- continue;
+ continue;
+ }
+ used_pips.insert(gb_pip_id);
+
+ // >>> GBOx <- GTx0
+ dstWire = ctx->getPipSrcWire(gb_pip_id);
+ WireInfo dstWireInfo = ctx->wire_info(dstWire);
+ int branch_tap_idx = net.clock > 3 ? 1 : 0;
+ snprintf(buf, sizeof(buf), "R%dC%d_GT%d0_GBO%d", dstWireInfo.y + 1, dstWireInfo.x + 1, branch_tap_idx,
+ branch_tap_idx);
+ PipId gt_pip_id = ctx->id(buf);
+ if (ctx->verbose) {
+ log_info(" GT Pip:%s\n", buf);
+ }
+ // sanity
+ NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gt_pip_id) !=
+ ctx->getPipsUphill(dstWire).end());
+ // if already routed
+ if (used_pips.count(gt_pip_id)) {
+ if (ctx->verbose) {
+ log_info(" ^routed already^\n");
}
- used_pips.insert(spine_pip_id);
+ continue;
+ }
+ used_pips.insert(gt_pip_id);
- // >>> SPINExx <- IO
- dstWire = ctx->getPipSrcWire(spine_pip_id);
- dstWireInfo = ctx->wire_info(dstWire);
- PipId io_pip_id = PipId();
- for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) {
- if (ctx->getPipSrcWire(uphill_pip) == net.clock_io_wire) {
- io_pip_id = uphill_pip;
- }
+ // >>> GTx0 <- SPINExx
+ // XXX no optimization here, we need to store
+ // the SPINE <-> clock# correspondence in the database. In the
+ // meantime, we define in run-time in a completely suboptimal way.
+ std::vector<std::string> clock_spine;
+ dstWire = ctx->getPipSrcWire(gt_pip_id);
+ for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) {
+ std::string name = ctx->wire_info(ctx->getPipSrcWire(uphill_pip)).type.str(ctx);
+ if (name.rfind("SPINE", 0) == 0) {
+ clock_spine.push_back(name);
}
- NPNR_ASSERT(io_pip_id != PipId());
+ }
+ sort(clock_spine.begin(), clock_spine.end(), [](const std::string &a, const std::string &b) -> bool {
+ return (a.size() < b.size()) || (a.size() == b.size() && a < b);
+ });
+ dstWireInfo = ctx->wire_info(dstWire);
+ snprintf(buf, sizeof(buf), "R%dC%d_%s_GT%d0", dstWireInfo.y + 1, dstWireInfo.x + 1,
+ clock_spine[net.clock - branch_tap_idx * 4].c_str(), branch_tap_idx);
+ PipId spine_pip_id = ctx->id(buf);
+ if (ctx->verbose) {
+ log_info(" Spine Pip:%s\n", buf);
+ }
+ // sanity
+ NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), spine_pip_id) !=
+ ctx->getPipsUphill(dstWire).end());
+ // if already routed
+ if (used_pips.count(spine_pip_id)) {
if (ctx->verbose) {
- log_info(" IO Pip:%s\n", io_pip_id.c_str(ctx));
+ log_info(" ^routed already^\n");
}
- // if already routed
- if (used_pips.count(io_pip_id)) {
- if (ctx->verbose) {
- log_info(" ^routed already^\n");
- }
- continue;
+ continue;
+ }
+ used_pips.insert(spine_pip_id);
+
+ // >>> SPINExx <- IO
+ dstWire = ctx->getPipSrcWire(spine_pip_id);
+ dstWireInfo = ctx->wire_info(dstWire);
+ PipId io_pip_id = PipId();
+ for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) {
+ if (ctx->getPipSrcWire(uphill_pip) == net.clock_io_wire) {
+ io_pip_id = uphill_pip;
}
- used_pips.insert(io_pip_id);
}
- log_info(" Net %s is routed.\n", net.name.c_str(ctx));
- for (auto const pip : used_pips) {
- ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED);
+ NPNR_ASSERT(io_pip_id != PipId());
+ if (ctx->verbose) {
+ log_info(" IO Pip:%s\n", io_pip_id.c_str(ctx));
+ }
+ // if already routed
+ if (used_pips.count(io_pip_id)) {
+ if (ctx->verbose) {
+ log_info(" ^routed already^\n");
+ }
+ continue;
}
- ctx->bindWire(net.clock_io_wire, &ctx->net_info(net.name), STRENGTH_LOCKED);
+ used_pips.insert(io_pip_id);
}
+ log_info(" Net %s is routed.\n", net.name.c_str(ctx));
+ for (auto const pip : used_pips) {
+ ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED);
+ }
+ ctx->bindWire(net.clock_io_wire, &ctx->net_info(net.name), STRENGTH_LOCKED);
+}
- public:
- Context *ctx;
- void route_globals()
- {
- log_info("Routing globals...\n");
+void GowinGlobalRouter::route_globals(Context *ctx)
+{
+ log_info("Routing globals...\n");
- std::vector<onet_t> clock_nets;
- gather_clock_nets(clock_nets);
- // XXX we need to use the list of indexes of clocks from the database
- // use 6 clocks (XXX 3 for GW1NZ-1)
- int max_clock = 3, cur_clock = -1;
- for (auto const &net : clock_nets) {
- // XXX only IO clock for now
- if (net.clock_io_wire == WireId()) {
- log_info(" Non IO clock, skip %s.\n", net.name.c_str(ctx));
- continue;
- }
- if (++cur_clock >= max_clock) {
- log_info(" No more clock wires left, skip the remaining nets.\n");
- break;
- }
- route_net(net, cur_clock);
- }
+ for (auto const &net : nets) {
+ route_net(ctx, net);
}
-};
+}
-void route_gowin_globals(Context *ctx)
+// Allocate networks that will be routed through the global system.
+// Mark their driver cells as global buffers to exclude them from the analysis.
+void GowinGlobalRouter::mark_globals(Context *ctx)
{
- GowinGlobalRouter router(ctx);
- router.route_globals();
+ log_info("Find global nets...\n");
+
+ std::vector<globalnet_t> clock_nets;
+ gather_clock_nets(ctx, clock_nets);
+ // XXX we need to use the list of indexes of clocks from the database
+ // use 6 clocks (XXX 3 for GW1NZ-1)
+ int max_clock = 3, cur_clock = -1;
+ for (auto &net : clock_nets) {
+ // XXX only IO clock for now
+ if (net.clock_io_wire == WireId()) {
+ log_info(" Non IO clock, skip %s.\n", net.name.c_str(ctx));
+ continue;
+ }
+ if (++cur_clock >= max_clock) {
+ log_info(" No more clock wires left, skip the remaining nets.\n");
+ break;
+ }
+ net.clock = cur_clock;
+ BelInfo &bi = ctx->bel_info(net.clock_io_bel);
+ bi.gb = true;
+ nets.emplace_back(net);
+ }
}
NEXTPNR_NAMESPACE_END
diff --git a/gowin/globals.h b/gowin/globals.h
index 41a8727a..69232d7c 100644
--- a/gowin/globals.h
+++ b/gowin/globals.h
@@ -18,10 +18,74 @@
*
*/
+#ifndef GOWIN_GLOBALS_H
+#define GOWIN_GLOBALS_H
+
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
-void route_gowin_globals(Context *ctx);
+class GowinGlobalRouter
+{
+ public:
+ GowinGlobalRouter() {}
+
+ private:
+ // wire -> clock#
+ dict<WireId, int> used_wires;
+
+ // ordered nets
+ struct globalnet_t
+ {
+ IdString name;
+ int clock_ports;
+ BelId clock_io_bel;
+ WireId clock_io_wire; // IO wire if there is one
+ int clock; // clock #
+
+ globalnet_t()
+ {
+ name = IdString();
+ clock_ports = 0;
+ clock_io_bel = BelId();
+ clock_io_wire = WireId();
+ clock = -1;
+ }
+ globalnet_t(IdString _name)
+ {
+ name = _name;
+ clock_ports = 0;
+ clock_io_bel = BelId();
+ clock_io_wire = WireId();
+ clock = -1;
+ }
+
+ // sort
+ bool operator<(const globalnet_t &other) const
+ {
+ if ((clock_io_wire != WireId()) ^ (other.clock_io_wire != WireId())) {
+ return !(clock_io_wire != WireId());
+ }
+ return clock_ports < other.clock_ports;
+ }
+ // search
+ bool operator==(const globalnet_t &other) const { return name == other.name; }
+ };
+
+ // discovered nets
+ std::vector<globalnet_t> nets;
+
+ bool is_clock_port(PortRef const &user);
+ std::pair<WireId, BelId> clock_io(Context *ctx, PortRef const &driver);
+ void gather_clock_nets(Context *ctx, std::vector<globalnet_t> &clock_nets);
+ IdString route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock, pool<IdString> &used_pips,
+ pool<IdString> &undo_wires);
+ void route_net(Context *ctx, globalnet_t const &net);
+
+ public:
+ void mark_globals(Context *ctx);
+ void route_globals(Context *ctx);
+};
NEXTPNR_NAMESPACE_END
+#endif // GOWIN_GLOBALS_H
diff --git a/gowin/pack.cc b/gowin/pack.cc
index 83820142..5b304f10 100644
--- a/gowin/pack.cc
+++ b/gowin/pack.cc
@@ -26,6 +26,8 @@
#include "log.h"
#include "util.h"
+#include "globals.h"
+
NEXTPNR_NAMESPACE_BEGIN
static void make_dummy_alu(Context *ctx, int alu_idx, CellInfo *ci, CellInfo *packed_head,
@@ -1009,6 +1011,7 @@ bool Arch::pack()
Context *ctx = getCtx();
try {
log_break();
+ pre_pack(ctx);
pack_constants(ctx);
pack_gsr(ctx);
pack_io(ctx);
@@ -1018,6 +1021,7 @@ bool Arch::pack()
pack_alus(ctx);
pack_lut_lutffs(ctx);
pack_nonlut_ffs(ctx);
+ post_pack(ctx);
ctx->settings[id_pack] = 1;
ctx->assignArchInfo();
log_info("Checksum: 0x%08x\n", ctx->checksum());