ecp5: Separate global promotion and routing
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
c782f07b1b
commit
24a2feda30
@ -491,7 +491,7 @@ struct Arch : BaseCtx
|
|||||||
BelId getBelByLocation(Loc loc) const;
|
BelId getBelByLocation(Loc loc) const;
|
||||||
BelRange getBelsByTile(int x, int y) const;
|
BelRange getBelsByTile(int x, int y) const;
|
||||||
|
|
||||||
bool getBelGlobalBuf(BelId bel) const { return false; }
|
bool getBelGlobalBuf(BelId bel) const { return getBelType(bel) == id_DCCA; }
|
||||||
|
|
||||||
bool checkBelAvail(BelId bel) const
|
bool checkBelAvail(BelId bel) const
|
||||||
{
|
{
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
#include "place_common.h"
|
#include "place_common.h"
|
||||||
|
#include "util.h"
|
||||||
#define fmt_str(x) (static_cast<const std::ostringstream &>(std::ostringstream() << x).str())
|
#define fmt_str(x) (static_cast<const std::ostringstream &>(std::ostringstream() << x).str())
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
@ -257,6 +258,45 @@ class Ecp5GlobalRouter
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get DCC wirelength based on source
|
||||||
|
wirelen_t get_dcc_wirelen(CellInfo *dcc)
|
||||||
|
{
|
||||||
|
NetInfo *clki = dcc->ports.at(id_CLKI).net;
|
||||||
|
BelId drv_bel;
|
||||||
|
const PortRef &drv = clki->driver;
|
||||||
|
if (drv.cell == nullptr) {
|
||||||
|
return 0;
|
||||||
|
} else if (drv.cell->attrs.count(ctx->id("BEL"))) {
|
||||||
|
drv_bel = ctx->getBelByName(ctx->id(drv.cell->attrs.at(ctx->id("BEL"))));
|
||||||
|
} else {
|
||||||
|
// Check if driver is a singleton
|
||||||
|
BelId last_bel;
|
||||||
|
bool singleton = true;
|
||||||
|
for (auto bel : ctx->getBels()) {
|
||||||
|
if (ctx->getBelType(bel) == drv.cell->type) {
|
||||||
|
if (last_bel != BelId()) {
|
||||||
|
singleton = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
last_bel = bel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (singleton && last_bel != BelId()) {
|
||||||
|
drv_bel = last_bel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (drv_bel == BelId()) {
|
||||||
|
// Driver is not locked. Use standard metric
|
||||||
|
float tns;
|
||||||
|
return get_net_metric(ctx, clki, MetricType::WIRELENGTH, tns);
|
||||||
|
} else {
|
||||||
|
// Driver is locked
|
||||||
|
Loc dcc_loc = ctx->getBelLocation(dcc->bel);
|
||||||
|
Loc drv_loc = ctx->getBelLocation(drv_bel);
|
||||||
|
return std::abs(dcc_loc.x - drv_loc.x) + std::abs(dcc_loc.y - drv_loc.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Attempt to place a DCC
|
// Attempt to place a DCC
|
||||||
void place_dcc(CellInfo *dcc)
|
void place_dcc(CellInfo *dcc)
|
||||||
{
|
{
|
||||||
@ -266,8 +306,7 @@ class Ecp5GlobalRouter
|
|||||||
if (ctx->getBelType(bel) == id_DCCA && ctx->checkBelAvail(bel)) {
|
if (ctx->getBelType(bel) == id_DCCA && ctx->checkBelAvail(bel)) {
|
||||||
if (ctx->isValidBelForCell(dcc, bel)) {
|
if (ctx->isValidBelForCell(dcc, bel)) {
|
||||||
ctx->bindBel(bel, dcc, STRENGTH_LOCKED);
|
ctx->bindBel(bel, dcc, STRENGTH_LOCKED);
|
||||||
float tns;
|
wirelen_t wirelen = get_dcc_wirelen(dcc);
|
||||||
wirelen_t wirelen = get_net_metric(ctx, dcc->ports.at(id_CLKI).net, MetricType::WIRELENGTH, tns);
|
|
||||||
if (wirelen < best_wirelen) {
|
if (wirelen < best_wirelen) {
|
||||||
best_bel = bel;
|
best_bel = bel;
|
||||||
best_wirelen = wirelen;
|
best_wirelen = wirelen;
|
||||||
@ -322,19 +361,33 @@ class Ecp5GlobalRouter
|
|||||||
Context *ctx;
|
Context *ctx;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void promote_and_route_globals()
|
void promote_globals()
|
||||||
{
|
{
|
||||||
log_info("Promoting and routing globals...\n");
|
log_info("Promoting globals...\n");
|
||||||
auto clocks = get_clocks();
|
auto clocks = get_clocks();
|
||||||
|
for (auto clock : clocks) {
|
||||||
|
log_info(" promoting clock net %s to global network\n", clock->name.c_str(ctx));
|
||||||
|
insert_dcc(clock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void route_globals()
|
||||||
|
{
|
||||||
|
log_info("Routing globals...\n");
|
||||||
std::set<int> all_globals, fab_globals;
|
std::set<int> all_globals, fab_globals;
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
all_globals.insert(i);
|
all_globals.insert(i);
|
||||||
if (i < 8)
|
if (i < 8)
|
||||||
fab_globals.insert(i);
|
fab_globals.insert(i);
|
||||||
}
|
}
|
||||||
for (auto clock : clocks) {
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (ci->type == id_DCCA) {
|
||||||
|
NetInfo *clock = ci->ports.at(id_CLKO).net;
|
||||||
|
NPNR_ASSERT(clock != nullptr);
|
||||||
bool drives_fabric = std::any_of(clock->users.begin(), clock->users.end(),
|
bool drives_fabric = std::any_of(clock->users.begin(), clock->users.end(),
|
||||||
[this](const PortRef &port) { return !is_clock_port(port); });
|
[this](const PortRef &port) { return !is_clock_port(port); });
|
||||||
|
|
||||||
int glbid;
|
int glbid;
|
||||||
if (drives_fabric) {
|
if (drives_fabric) {
|
||||||
if (fab_globals.empty())
|
if (fab_globals.empty())
|
||||||
@ -343,26 +396,24 @@ class Ecp5GlobalRouter
|
|||||||
} else {
|
} else {
|
||||||
glbid = *(all_globals.begin());
|
glbid = *(all_globals.begin());
|
||||||
}
|
}
|
||||||
all_globals.erase(glbid);
|
|
||||||
fab_globals.erase(glbid);
|
log_info(" routing clock net %s using global %d\n", clock->name.c_str(ctx), glbid);
|
||||||
log_info(" promoting clock net %s to global %d\n", clock->name.c_str(ctx), glbid);
|
bool routed = route_onto_global(clock, glbid);
|
||||||
auto old_users = clock->users;
|
|
||||||
NetInfo *global = insert_dcc(clock);
|
|
||||||
bool routed = route_onto_global(global, glbid);
|
|
||||||
NPNR_ASSERT(routed);
|
NPNR_ASSERT(routed);
|
||||||
|
|
||||||
// WCK must have routing priority
|
// WCK must have routing priority
|
||||||
auto sorted_users = global->users;
|
auto sorted_users = clock->users;
|
||||||
std::sort(sorted_users.begin(), sorted_users.end(), [this](const PortRef &a, const PortRef &b) {
|
std::sort(sorted_users.begin(), sorted_users.end(), [this](const PortRef &a, const PortRef &b) {
|
||||||
return global_route_priority(a) < global_route_priority(b);
|
return global_route_priority(a) < global_route_priority(b);
|
||||||
});
|
});
|
||||||
for (const auto &user : sorted_users) {
|
for (const auto &user : sorted_users) {
|
||||||
route_logic_tile_global(global, glbid, user);
|
route_logic_tile_global(clock, glbid, user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
void promote_ecp5_globals(Context *ctx) { Ecp5GlobalRouter(ctx).promote_globals(); }
|
||||||
void route_ecp5_globals(Context *ctx) { Ecp5GlobalRouter(ctx).promote_and_route_globals(); }
|
void route_ecp5_globals(Context *ctx) { Ecp5GlobalRouter(ctx).route_globals(); }
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
void promote_ecp5_globals(Context *ctx);
|
||||||
void route_ecp5_globals(Context *ctx);
|
void route_ecp5_globals(Context *ctx);
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
@ -24,6 +24,7 @@
|
|||||||
#include "cells.h"
|
#include "cells.h"
|
||||||
#include "chain_utils.h"
|
#include "chain_utils.h"
|
||||||
#include "design_utils.h"
|
#include "design_utils.h"
|
||||||
|
#include "globals.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
@ -1047,6 +1048,7 @@ class Ecp5Packer
|
|||||||
pack_lut_pairs();
|
pack_lut_pairs();
|
||||||
pack_remaining_luts();
|
pack_remaining_luts();
|
||||||
pack_remaining_ffs();
|
pack_remaining_ffs();
|
||||||
|
promote_ecp5_globals(ctx);
|
||||||
ctx->check();
|
ctx->check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user