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>
This commit is contained in:
parent
6d85de43ee
commit
3172a38dae
@ -25,7 +25,6 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include "embed.h"
|
#include "embed.h"
|
||||||
#include "gfx.h"
|
#include "gfx.h"
|
||||||
#include "globals.h"
|
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
#include "placer1.h"
|
#include "placer1.h"
|
||||||
#include "placer_heap.h"
|
#include "placer_heap.h"
|
||||||
@ -257,6 +256,8 @@ bool Arch::allocate_longwire(NetInfo *ni, int lw_idx)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Arch::auto_longwires() {}
|
||||||
|
|
||||||
void Arch::fix_longwire_bels()
|
void Arch::fix_longwire_bels()
|
||||||
{
|
{
|
||||||
// After routing, it is clear which wires and in which bus SS00 and SS40 are used and
|
// 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;
|
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::addCellTimingClock(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_CLOCK_INPUT; }
|
||||||
|
|
||||||
void Arch::addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayQuad delay)
|
void Arch::addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayQuad delay)
|
||||||
@ -1989,6 +2012,8 @@ void Arch::assignArchInfo()
|
|||||||
|
|
||||||
// add timing paths
|
// add timing paths
|
||||||
addCellTimingClock(cname, id_CLK);
|
addCellTimingClock(cname, id_CLK);
|
||||||
|
addCellTimingRegIn(cname, id_CE);
|
||||||
|
addCellTimingRegIn(cname, id_LSR);
|
||||||
IdString ports[4] = {id_A, id_B, id_C, id_D};
|
IdString ports[4] = {id_A, id_B, id_C, id_D};
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
DelayPair setup =
|
DelayPair setup =
|
||||||
@ -2019,7 +2044,18 @@ void Arch::assignArchInfo()
|
|||||||
delay = delay + delayLookup(speed->lut.timings.get(), speed->lut.num_timings, id_fx_ofx1);
|
delay = delay + delayLookup(speed->lut.timings.get(), speed->lut.num_timings, id_fx_ofx1);
|
||||||
addCellTimingDelay(cname, id_I0, id_OF, delay);
|
addCellTimingDelay(cname, id_I0, id_OF, delay);
|
||||||
addCellTimingDelay(cname, id_I1, 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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2063,4 +2099,22 @@ bool Arch::cellsCompatible(const CellInfo **cells, int count) const
|
|||||||
return true;
|
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
|
NEXTPNR_NAMESPACE_END
|
||||||
|
14
gowin/arch.h
14
gowin/arch.h
@ -31,6 +31,8 @@
|
|||||||
#include "nextpnr_namespaces.h"
|
#include "nextpnr_namespaces.h"
|
||||||
#include "nextpnr_types.h"
|
#include "nextpnr_types.h"
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
template <typename T> struct RelPtr
|
template <typename T> struct RelPtr
|
||||||
@ -337,6 +339,11 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
void setDelayScaling(double scale, double offset);
|
void setDelayScaling(double scale, double offset);
|
||||||
|
|
||||||
void addCellTimingClock(IdString cell, IdString port);
|
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 addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayQuad delay);
|
||||||
void addCellTimingSetupHold(IdString cell, IdString port, IdString clock, DelayPair setup, DelayPair hold);
|
void addCellTimingSetupHold(IdString cell, IdString port, IdString clock, DelayPair setup, DelayPair hold);
|
||||||
void addCellTimingClockToOut(IdString cell, IdString port, IdString clock, DelayQuad clktoq);
|
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 haveBelType(int x, int y, IdString bel_type);
|
||||||
bool allocate_longwire(NetInfo *ni, int lw_idx = -1);
|
bool allocate_longwire(NetInfo *ni, int lw_idx = -1);
|
||||||
void fix_longwire_bels();
|
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
|
// chip db version
|
||||||
unsigned int const chipdb_version = 1;
|
unsigned int const chipdb_version = 1;
|
||||||
|
146
gowin/globals.cc
146
gowin/globals.cc
@ -18,7 +18,6 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "globals.h"
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -31,78 +30,47 @@
|
|||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
class GowinGlobalRouter
|
bool GowinGlobalRouter::is_clock_port(PortRef const &user)
|
||||||
{
|
{
|
||||||
public:
|
if ((user.cell->type == id_SLICE || user.cell->type == id_ODDR || user.cell->type == id_ODDRC) &&
|
||||||
GowinGlobalRouter(Context *ctx) : ctx(ctx){};
|
user.port == id_CLK) {
|
||||||
|
|
||||||
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; }
|
|
||||||
};
|
|
||||||
|
|
||||||
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 true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
WireId clock_io(PortRef const &driver)
|
std::pair<WireId, BelId> GowinGlobalRouter::clock_io(Context *ctx, PortRef const &driver)
|
||||||
{
|
{
|
||||||
// XXX normally all alternative functions of the pins should be passed
|
// 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
|
// in the chip database, but at the moment we find them from aliases/pips
|
||||||
// XXX check diff inputs too
|
// XXX check diff inputs too
|
||||||
if (driver.cell == nullptr || driver.cell->bel == BelId()) {
|
if (driver.cell == nullptr || driver.cell->type != id_IOB || !driver.cell->attrs.count(id_BEL)) {
|
||||||
return WireId();
|
return std::make_pair(WireId(), BelId());
|
||||||
}
|
}
|
||||||
// clock IOs have pips output->SPINExx
|
// clock IOs have pips output->SPINExx
|
||||||
BelInfo &bel = ctx->bel_info(driver.cell->bel);
|
|
||||||
if (bel.type != id_IOB) {
|
BelInfo &bel = ctx->bel_info(ctx->id(driver.cell->attrs[id_BEL].as_string()));
|
||||||
return WireId();
|
|
||||||
}
|
|
||||||
WireId wire = bel.pins[id_O].wire;
|
WireId wire = bel.pins[id_O].wire;
|
||||||
for (auto const pip : ctx->getPipsDownhill(wire)) {
|
for (auto const pip : ctx->getPipsDownhill(wire)) {
|
||||||
if (ctx->wire_info(ctx->getPipDstWire(pip)).type.str(ctx).rfind("SPINE", 0) == 0) {
|
if (ctx->wire_info(ctx->getPipDstWire(pip)).type.str(ctx).rfind("SPINE", 0) == 0) {
|
||||||
return wire;
|
return std::make_pair(wire, bel.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return WireId();
|
return std::make_pair(WireId(), BelId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// gather the clock nets
|
// gather the clock nets
|
||||||
void gather_clock_nets(std::vector<onet_t> &clock_nets)
|
void GowinGlobalRouter::gather_clock_nets(Context *ctx, std::vector<globalnet_t> &clock_nets)
|
||||||
{
|
{
|
||||||
for (auto const &net : ctx->nets) {
|
for (auto const &net : ctx->nets) {
|
||||||
NetInfo const *ni = net.second.get();
|
NetInfo const *ni = net.second.get();
|
||||||
auto new_clock = clock_nets.end();
|
auto new_clock = clock_nets.end();
|
||||||
WireId clock_wire = clock_io(ni->driver);
|
auto clock_wire_bel = clock_io(ctx, ni->driver);
|
||||||
if (clock_wire != WireId()) {
|
if (clock_wire_bel.first != WireId()) {
|
||||||
clock_nets.emplace_back(net.first);
|
clock_nets.emplace_back(net.first);
|
||||||
new_clock = --clock_nets.end();
|
new_clock = --clock_nets.end();
|
||||||
new_clock->clock_io_wire = clock_wire;
|
new_clock->clock_io_wire = clock_wire_bel.first;
|
||||||
|
new_clock->clock_io_bel = clock_wire_bel.second;
|
||||||
}
|
}
|
||||||
for (auto const &user : ni->users) {
|
for (auto const &user : ni->users) {
|
||||||
if (is_clock_port(user)) {
|
if (is_clock_port(user)) {
|
||||||
@ -123,20 +91,19 @@ class GowinGlobalRouter
|
|||||||
net.clock_io_wire == WireId() ? "No" : net.clock_io_wire.c_str(ctx));
|
net.clock_io_wire == WireId() ? "No" : net.clock_io_wire.c_str(ctx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// non clock port
|
// non clock port
|
||||||
// returns GB pip
|
// returns GB pip
|
||||||
IdString route_to_non_clock_port(WireId const dstWire, int clock, pool<IdString> &used_pips,
|
IdString GowinGlobalRouter::route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock,
|
||||||
pool<IdString> &undo_wires)
|
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};
|
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];
|
char buf[40];
|
||||||
// uphill pips
|
// uphill pips
|
||||||
for (auto const uphill : ctx->getPipsUphill(dstWire)) {
|
for (auto const uphill : ctx->getPipsUphill(dstWire)) {
|
||||||
WireId srcWire = ctx->getPipSrcWire(uphill);
|
WireId srcWire = ctx->getPipSrcWire(uphill);
|
||||||
if (find(one_hop.begin(), one_hop.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) !=
|
if (find(one_hop.begin(), one_hop.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) != one_hop.end()) {
|
||||||
one_hop.end()) {
|
|
||||||
// found one hop pip
|
// found one hop pip
|
||||||
if (used_wires.count(srcWire)) {
|
if (used_wires.count(srcWire)) {
|
||||||
if (used_wires[srcWire] != clock) {
|
if (used_wires[srcWire] != clock) {
|
||||||
@ -162,16 +129,16 @@ class GowinGlobalRouter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return IdString();
|
return IdString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// route one net
|
// route one net
|
||||||
void route_net(onet_t const &net, int clock)
|
void GowinGlobalRouter::route_net(Context *ctx, globalnet_t const &net)
|
||||||
{
|
{
|
||||||
// For failed routing undo
|
// For failed routing undo
|
||||||
pool<IdString> used_pips;
|
pool<IdString> used_pips;
|
||||||
pool<IdString> undo_wires;
|
pool<IdString> undo_wires;
|
||||||
|
|
||||||
log_info(" Route net %s, use clock #%d.\n", net.name.c_str(ctx), clock);
|
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) {
|
for (auto const &user : ctx->net_info(net.name).users) {
|
||||||
// >>> port <- GB<clock>0
|
// >>> port <- GB<clock>0
|
||||||
WireId dstWire = ctx->getNetinfoSinkWire(&ctx->net_info(net.name), user, 0);
|
WireId dstWire = ctx->getNetinfoSinkWire(&ctx->net_info(net.name), user, 0);
|
||||||
@ -184,7 +151,7 @@ class GowinGlobalRouter
|
|||||||
PipId gb_pip_id;
|
PipId gb_pip_id;
|
||||||
if (user.port == id_CLK) {
|
if (user.port == id_CLK) {
|
||||||
WireInfo const wi = ctx->wire_info(dstWire);
|
WireInfo const wi = ctx->wire_info(dstWire);
|
||||||
snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock,
|
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));
|
ctx->wire_info(dstWire).type.c_str(ctx));
|
||||||
gb_pip_id = ctx->id(buf);
|
gb_pip_id = ctx->id(buf);
|
||||||
// sanity
|
// sanity
|
||||||
@ -192,11 +159,11 @@ class GowinGlobalRouter
|
|||||||
ctx->getPipsUphill(dstWire).end());
|
ctx->getPipsUphill(dstWire).end());
|
||||||
} else {
|
} else {
|
||||||
// Non clock port
|
// Non clock port
|
||||||
gb_pip_id = route_to_non_clock_port(dstWire, clock, used_pips, undo_wires);
|
gb_pip_id = route_to_non_clock_port(ctx, dstWire, net.clock, used_pips, undo_wires);
|
||||||
if (gb_pip_id == IdString()) {
|
if (gb_pip_id == IdString()) {
|
||||||
if (ctx->verbose) {
|
if (ctx->verbose) {
|
||||||
log_info(" Can't find route to %s, net %s will be routed in a standard way.\n",
|
log_info(" Can't find route to %s, net %s will be routed in a standard way.\n", dstWire.c_str(ctx),
|
||||||
dstWire.c_str(ctx), net.name.c_str(ctx));
|
net.name.c_str(ctx));
|
||||||
}
|
}
|
||||||
for (IdString const undo : undo_wires) {
|
for (IdString const undo : undo_wires) {
|
||||||
used_wires.erase(undo);
|
used_wires.erase(undo);
|
||||||
@ -219,7 +186,7 @@ class GowinGlobalRouter
|
|||||||
// >>> GBOx <- GTx0
|
// >>> GBOx <- GTx0
|
||||||
dstWire = ctx->getPipSrcWire(gb_pip_id);
|
dstWire = ctx->getPipSrcWire(gb_pip_id);
|
||||||
WireInfo dstWireInfo = ctx->wire_info(dstWire);
|
WireInfo dstWireInfo = ctx->wire_info(dstWire);
|
||||||
int branch_tap_idx = clock > 3 ? 1 : 0;
|
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,
|
snprintf(buf, sizeof(buf), "R%dC%d_GT%d0_GBO%d", dstWireInfo.y + 1, dstWireInfo.x + 1, branch_tap_idx,
|
||||||
branch_tap_idx);
|
branch_tap_idx);
|
||||||
PipId gt_pip_id = ctx->id(buf);
|
PipId gt_pip_id = ctx->id(buf);
|
||||||
@ -255,7 +222,7 @@ class GowinGlobalRouter
|
|||||||
});
|
});
|
||||||
dstWireInfo = ctx->wire_info(dstWire);
|
dstWireInfo = ctx->wire_info(dstWire);
|
||||||
snprintf(buf, sizeof(buf), "R%dC%d_%s_GT%d0", dstWireInfo.y + 1, dstWireInfo.x + 1,
|
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);
|
clock_spine[net.clock - branch_tap_idx * 4].c_str(), branch_tap_idx);
|
||||||
PipId spine_pip_id = ctx->id(buf);
|
PipId spine_pip_id = ctx->id(buf);
|
||||||
if (ctx->verbose) {
|
if (ctx->verbose) {
|
||||||
log_info(" Spine Pip:%s\n", buf);
|
log_info(" Spine Pip:%s\n", buf);
|
||||||
@ -299,20 +266,29 @@ class GowinGlobalRouter
|
|||||||
ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED);
|
ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED);
|
||||||
}
|
}
|
||||||
ctx->bindWire(net.clock_io_wire, &ctx->net_info(net.name), STRENGTH_LOCKED);
|
ctx->bindWire(net.clock_io_wire, &ctx->net_info(net.name), STRENGTH_LOCKED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
void GowinGlobalRouter::route_globals(Context *ctx)
|
||||||
Context *ctx;
|
{
|
||||||
void route_globals()
|
|
||||||
{
|
|
||||||
log_info("Routing globals...\n");
|
log_info("Routing globals...\n");
|
||||||
|
|
||||||
std::vector<onet_t> clock_nets;
|
for (auto const &net : nets) {
|
||||||
gather_clock_nets(clock_nets);
|
route_net(ctx, net);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
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
|
// XXX we need to use the list of indexes of clocks from the database
|
||||||
// use 6 clocks (XXX 3 for GW1NZ-1)
|
// use 6 clocks (XXX 3 for GW1NZ-1)
|
||||||
int max_clock = 3, cur_clock = -1;
|
int max_clock = 3, cur_clock = -1;
|
||||||
for (auto const &net : clock_nets) {
|
for (auto &net : clock_nets) {
|
||||||
// XXX only IO clock for now
|
// XXX only IO clock for now
|
||||||
if (net.clock_io_wire == WireId()) {
|
if (net.clock_io_wire == WireId()) {
|
||||||
log_info(" Non IO clock, skip %s.\n", net.name.c_str(ctx));
|
log_info(" Non IO clock, skip %s.\n", net.name.c_str(ctx));
|
||||||
@ -322,15 +298,11 @@ class GowinGlobalRouter
|
|||||||
log_info(" No more clock wires left, skip the remaining nets.\n");
|
log_info(" No more clock wires left, skip the remaining nets.\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
route_net(net, cur_clock);
|
net.clock = cur_clock;
|
||||||
|
BelInfo &bi = ctx->bel_info(net.clock_io_bel);
|
||||||
|
bi.gb = true;
|
||||||
|
nets.emplace_back(net);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void route_gowin_globals(Context *ctx)
|
|
||||||
{
|
|
||||||
GowinGlobalRouter router(ctx);
|
|
||||||
router.route_globals();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -18,10 +18,74 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef GOWIN_GLOBALS_H
|
||||||
|
#define GOWIN_GLOBALS_H
|
||||||
|
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
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
|
NEXTPNR_NAMESPACE_END
|
||||||
|
#endif // GOWIN_GLOBALS_H
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
static void make_dummy_alu(Context *ctx, int alu_idx, CellInfo *ci, CellInfo *packed_head,
|
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();
|
Context *ctx = getCtx();
|
||||||
try {
|
try {
|
||||||
log_break();
|
log_break();
|
||||||
|
pre_pack(ctx);
|
||||||
pack_constants(ctx);
|
pack_constants(ctx);
|
||||||
pack_gsr(ctx);
|
pack_gsr(ctx);
|
||||||
pack_io(ctx);
|
pack_io(ctx);
|
||||||
@ -1018,6 +1021,7 @@ bool Arch::pack()
|
|||||||
pack_alus(ctx);
|
pack_alus(ctx);
|
||||||
pack_lut_lutffs(ctx);
|
pack_lut_lutffs(ctx);
|
||||||
pack_nonlut_ffs(ctx);
|
pack_nonlut_ffs(ctx);
|
||||||
|
post_pack(ctx);
|
||||||
ctx->settings[id_pack] = 1;
|
ctx->settings[id_pack] = 1;
|
||||||
ctx->assignArchInfo();
|
ctx->assignArchInfo();
|
||||||
log_info("Checksum: 0x%08x\n", ctx->checksum());
|
log_info("Checksum: 0x%08x\n", ctx->checksum());
|
||||||
|
Loading…
Reference in New Issue
Block a user