Merge branch 'router_improve' of http://github.com/YosysHQ/nextpnr into xc7-router_improve

This commit is contained in:
Eddie Hung 2018-11-13 09:05:35 -08:00
commit 97f1901fcf
11 changed files with 440 additions and 250 deletions

View File

@ -53,6 +53,30 @@ void IdString::initialize_add(const BaseCtx *ctx, const char *s, int idx)
ctx->idstring_idx_to_str->push_back(&insert_rc.first->first);
}
const char *BaseCtx::nameOfBel(BelId bel) const
{
const Context *ctx = getCtx();
return ctx->getBelName(bel).c_str(ctx);
}
const char *BaseCtx::nameOfWire(WireId wire) const
{
const Context *ctx = getCtx();
return ctx->getWireName(wire).c_str(ctx);
}
const char *BaseCtx::nameOfPip(PipId pip) const
{
const Context *ctx = getCtx();
return ctx->getPipName(pip).c_str(ctx);
}
const char *BaseCtx::nameOfGroup(GroupId group) const
{
const Context *ctx = getCtx();
return ctx->getGroupName(group).c_str(ctx);
}
WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const
{
if (net_info->driver.cell == nullptr)

View File

@ -487,13 +487,23 @@ struct BaseCtx
const Context *getCtx() const { return reinterpret_cast<const Context *>(this); }
template <typename T> const char *nameOf(const T *obj)
const char *nameOf(IdString name) const
{
return name.c_str(this);
}
template <typename T> const char *nameOf(const T *obj) const
{
if (obj == nullptr)
return "";
return obj->name.c_str(getCtx());
return obj->name.c_str(this);
}
const char *nameOfBel(BelId bel) const;
const char *nameOfWire(WireId wire) const;
const char *nameOfPip(PipId pip) const;
const char *nameOfGroup(GroupId group) const;
// --------------------------------------------------------------
bool allUiReload = true;

View File

@ -33,15 +33,14 @@ struct arc_key
NetInfo *net_info;
int user_idx;
bool operator==(const arc_key &other) const {
return (net_info == other.net_info) && (user_idx == other.user_idx);
}
bool operator==(const arc_key &other) const { return (net_info == other.net_info) && (user_idx == other.user_idx); }
bool operator<(const arc_key &other) const { return net_info == other.net_info ? user_idx < other.user_idx : net_info->name < other.net_info->name; }
struct Hash
{
std::size_t operator()(const arc_key &arg) const noexcept
{
std::size_t seed = std::hash<NetInfo*>()(arg.net_info);
std::size_t seed = std::hash<NetInfo *>()(arg.net_info);
seed ^= std::hash<int>()(arg.user_idx) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
@ -52,12 +51,15 @@ struct arc_entry
{
arc_key arc;
delay_t pri;
int randtag = 0;
struct Greater
struct Less
{
bool operator()(const arc_entry &lhs, const arc_entry &rhs) const noexcept
{
return lhs.pri > rhs.pri;
if (lhs.pri != rhs.pri)
return lhs.pri < rhs.pri;
return lhs.randtag < rhs.randtag;
}
};
};
@ -90,7 +92,7 @@ struct Router1
Context *ctx;
const Router1Cfg &cfg;
std::priority_queue<arc_entry, std::vector<arc_entry>, arc_entry::Greater> arc_queue;
std::priority_queue<arc_entry, std::vector<arc_entry>, arc_entry::Less> arc_queue;
std::unordered_map<WireId, std::unordered_set<arc_key, arc_key::Hash>> wire_to_arcs;
std::unordered_map<arc_key, std::unordered_set<WireId>, arc_key::Hash> arc_to_wires;
std::unordered_set<arc_key, arc_key::Hash> queued_arcs;
@ -99,13 +101,13 @@ struct Router1
std::priority_queue<QueuedWire, std::vector<QueuedWire>, QueuedWire::Greater> queue;
std::unordered_map<WireId, int> wireScores;
std::unordered_map<NetInfo*, int> netScores;
std::unordered_map<NetInfo *, int> netScores;
int arcs_with_ripup = 0;
int arcs_without_ripup = 0;
bool ripup_flag;
Router1(Context *ctx, const Router1Cfg &cfg) : ctx(ctx), cfg(cfg) { }
Router1(Context *ctx, const Router1Cfg &cfg) : ctx(ctx), cfg(cfg) {}
void arc_queue_insert(const arc_key &arc, WireId src_wire, WireId dst_wire)
{
@ -117,6 +119,13 @@ struct Router1
arc_entry entry;
entry.arc = arc;
entry.pri = pri;
entry.randtag = ctx->rng();
#if 0
if (ctx->debug)
log("[arc_queue_insert] %s (%d) %s %s [%d %d]\n", ctx->nameOf(entry.arc.net_info), entry.arc.user_idx,
ctx->nameOfWire(src_wire), ctx->nameOfWire(dst_wire), (int)entry.pri, entry.randtag);
#endif
arc_queue.push(entry);
queued_arcs.insert(arc);
@ -139,6 +148,13 @@ struct Router1
arc_key arc_queue_pop()
{
arc_entry entry = arc_queue.top();
#if 0
if (ctx->debug)
log("[arc_queue_pop] %s (%d) [%d %d]\n", ctx->nameOf(entry.arc.net_info), entry.arc.user_idx,
(int)entry.pri, entry.randtag);
#endif
arc_queue.pop();
queued_arcs.erase(entry.arc);
return entry.arc;
@ -147,22 +163,31 @@ struct Router1
void ripup_net(NetInfo *net)
{
if (ctx->debug)
log(" ripup net %s\n", net->name.c_str(ctx));
log(" ripup net %s\n", ctx->nameOf(net));
netScores[net]++;
auto net_wires_copy = net->wires;
for (auto &it : net_wires_copy) {
WireId w = it.first;
std::vector<WireId> wires;
for (auto &it : net->wires)
wires.push_back(it.first);
ctx->sorted_shuffle(wires);
for (WireId w : wires) {
std::vector<arc_key> arcs;
for (auto &it : wire_to_arcs[w]) {
arc_to_wires[it].erase(w);
arc_queue_insert(it);
arcs.push_back(it);
}
wire_to_arcs[w].clear();
ctx->sorted_shuffle(arcs);
for (auto &it : arcs)
arc_queue_insert(it);
if (ctx->debug)
log(" unbind wire %s\n", ctx->getWireName(w).c_str(ctx));
log(" unbind wire %s\n", ctx->nameOfWire(w));
ctx->unbindWire(w);
wireScores[w]++;
@ -174,7 +199,7 @@ struct Router1
void ripup_wire(WireId wire, int extra_indent = 0)
{
if (ctx->debug)
log(" ripup wire %s\n", ctx->getWireName(wire).c_str(ctx));
log(" ripup wire %s\n", ctx->nameOfWire(wire));
WireId w = ctx->getConflictingWireWire(wire);
@ -183,14 +208,20 @@ struct Router1
if (n != nullptr)
ripup_net(n);
} else {
std::vector<arc_key> arcs;
for (auto &it : wire_to_arcs[w]) {
arc_to_wires[it].erase(w);
arc_queue_insert(it);
arcs.push_back(it);
}
wire_to_arcs[w].clear();
ctx->sorted_shuffle(arcs);
for (auto &it : arcs)
arc_queue_insert(it);
if (ctx->debug)
log(" unbind wire %s\n", ctx->getWireName(w).c_str(ctx));
log(" unbind wire %s\n", ctx->nameOfWire(w));
ctx->unbindWire(w);
wireScores[w]++;
@ -202,7 +233,7 @@ struct Router1
void ripup_pip(PipId pip)
{
if (ctx->debug)
log(" ripup pip %s\n", ctx->getPipName(pip).c_str(ctx));
log(" ripup pip %s\n", ctx->nameOfPip(pip));
WireId w = ctx->getConflictingPipWire(pip);
@ -211,14 +242,20 @@ struct Router1
if (n != nullptr)
ripup_net(n);
} else {
std::vector<arc_key> arcs;
for (auto &it : wire_to_arcs[w]) {
arc_to_wires[it].erase(w);
arc_queue_insert(it);
arcs.push_back(it);
}
wire_to_arcs[w].clear();
ctx->sorted_shuffle(arcs);
for (auto &it : arcs)
arc_queue_insert(it);
if (ctx->debug)
log(" unbind wire %s\n", ctx->getWireName(w).c_str(ctx));
log(" unbind wire %s\n", ctx->nameOfWire(w));
ctx->unbindWire(w);
wireScores[w]++;
@ -245,15 +282,17 @@ struct Router1
{
std::unordered_set<arc_key, arc_key::Hash> valid_arcs;
for (auto &net_it : ctx->nets)
{
for (auto &net_it : ctx->nets) {
NetInfo *net_info = net_it.second.get();
std::unordered_set<WireId> valid_wires_for_net;
if (skip_net(net_info))
continue;
// log("[check] net: %s\n", net_info->name.c_str(ctx));
#if 0
if (ctx->debug)
log("[check] net: %s\n", ctx->nameOf(net_info));
#endif
auto src_wire = ctx->getNetinfoSourceWire(net_info);
log_assert(src_wire != WireId());
@ -267,10 +306,16 @@ struct Router1
arc.user_idx = user_idx;
valid_arcs.insert(arc);
// log("[check] arc: %s %s\n", ctx->getWireName(src_wire).c_str(ctx), ctx->getWireName(dst_wire).c_str(ctx));
#if 0
if (ctx->debug)
log("[check] arc: %s %s\n", ctx->nameOfWire(src_wire), ctx->nameOfWire(dst_wire));
#endif
for (WireId wire : arc_to_wires[arc]) {
// log("[check] wire: %s\n", ctx->getWireName(wire).c_str(ctx));
#if 0
if (ctx->debug)
log("[check] wire: %s\n", ctx->nameOfWire(wire));
#endif
valid_wires_for_net.insert(wire);
log_assert(wire_to_arcs[wire].count(arc));
log_assert(net_info->wires.count(wire));
@ -295,12 +340,17 @@ struct Router1
void setup()
{
std::unordered_map<WireId, NetInfo*> src_to_net;
std::unordered_map<WireId, NetInfo *> src_to_net;
std::unordered_map<WireId, arc_key> dst_to_arc;
std::vector<IdString> net_names;
for (auto &net_it : ctx->nets)
{
NetInfo *net_info = net_it.second.get();
net_names.push_back(net_it.first);
ctx->sorted_shuffle(net_names);
for (IdString net_name : net_names) {
NetInfo *net_info = ctx->nets.at(net_name).get();
if (skip_net(net_info))
continue;
@ -308,34 +358,38 @@ struct Router1
auto src_wire = ctx->getNetinfoSourceWire(net_info);
if (src_wire == WireId())
log_error("No wire found for port %s on source cell %s.\n", net_info->driver.port.c_str(ctx),
net_info->driver.cell->name.c_str(ctx));
log_error("No wire found for port %s on source cell %s.\n", ctx->nameOf(net_info->driver.port),
ctx->nameOf(net_info->driver.cell));
if (src_to_net.count(src_wire))
log_error("Found two nets with same source wire %s: %s vs %s\n", ctx->getWireName(src_wire).c_str(ctx),
log_error("Found two nets with same source wire %s: %s vs %s\n", ctx->nameOfWire(src_wire),
ctx->nameOf(net_info), ctx->nameOf(src_to_net.at(src_wire)));
if (dst_to_arc.count(src_wire))
log_error("Wire %s is used as source and sink in different nets: %s vs %s (%d)\n", ctx->getWireName(src_wire).c_str(ctx),
ctx->nameOf(net_info), ctx->nameOf(dst_to_arc.at(src_wire).net_info), dst_to_arc.at(src_wire).user_idx);
log_error("Wire %s is used as source and sink in different nets: %s vs %s (%d)\n",
ctx->nameOfWire(src_wire), ctx->nameOf(net_info),
ctx->nameOf(dst_to_arc.at(src_wire).net_info), dst_to_arc.at(src_wire).user_idx);
for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) {
auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]);
if (dst_wire == WireId())
log_error("No wire found for port %s on destination cell %s.\n", net_info->users[user_idx].port.c_str(ctx),
net_info->users[user_idx].cell->name.c_str(ctx));
log_error("No wire found for port %s on destination cell %s.\n",
ctx->nameOf(net_info->users[user_idx].port),
ctx->nameOf(net_info->users[user_idx].cell));
if (dst_to_arc.count(dst_wire)) {
if (dst_to_arc.at(dst_wire).net_info == net_info)
continue;
log_error("Found two arcs with same sink wire %s: %s (%d) vs %s (%d)\n", ctx->getWireName(dst_wire).c_str(ctx),
ctx->nameOf(net_info), user_idx, ctx->nameOf(dst_to_arc.at(dst_wire).net_info), dst_to_arc.at(dst_wire).user_idx);
log_error("Found two arcs with same sink wire %s: %s (%d) vs %s (%d)\n",
ctx->nameOfWire(dst_wire), ctx->nameOf(net_info), user_idx,
ctx->nameOf(dst_to_arc.at(dst_wire).net_info), dst_to_arc.at(dst_wire).user_idx);
}
if (src_to_net.count(dst_wire))
log_error("Wire %s is used as source and sink in different nets: %s vs %s (%d)\n", ctx->getWireName(dst_wire).c_str(ctx),
ctx->nameOf(src_to_net.at(dst_wire)), ctx->nameOf(net_info), user_idx);
log_error("Wire %s is used as source and sink in different nets: %s vs %s (%d)\n",
ctx->nameOfWire(dst_wire), ctx->nameOf(src_to_net.at(dst_wire)),
ctx->nameOf(net_info), user_idx);
arc_key arc;
arc.net_info = net_info;
@ -390,9 +444,10 @@ struct Router1
ripup_flag = false;
if (ctx->debug) {
log("Routing arc %d on net %s (%d arcs total):\n", user_idx, net_info->name.c_str(ctx), int(net_info->users.size()));
log(" source ... %s\n", ctx->getWireName(src_wire).c_str(ctx));
log(" sink ..... %s\n", ctx->getWireName(dst_wire).c_str(ctx));
log("Routing arc %d on net %s (%d arcs total):\n", user_idx, ctx->nameOf(net_info),
int(net_info->users.size()));
log(" source ... %s\n", ctx->nameOfWire(src_wire));
log(" sink ..... %s\n", ctx->nameOfWire(dst_wire));
}
// unbind wires that are currently used exclusively by this arc
@ -406,7 +461,7 @@ struct Router1
arc_wires.erase(arc);
if (arc_wires.empty()) {
if (ctx->debug)
log(" unbind %s\n", ctx->getWireName(wire).c_str(ctx));
log(" unbind %s\n", ctx->nameOfWire(wire));
ctx->unbindWire(wire);
}
}
@ -443,8 +498,7 @@ struct Router1
visited[qw.wire] = qw;
}
while (visitCnt++ < maxVisitCnt && !queue.empty())
{
while (visitCnt++ < maxVisitCnt && !queue.empty()) {
QueuedWire qw = queue.top();
queue.pop();
@ -484,10 +538,12 @@ struct Router1
}
}
if (conflictWireNet != nullptr && conflictPipWire != WireId() && conflictWireNet->wires.count(conflictPipWire))
if (conflictWireNet != nullptr && conflictPipWire != WireId() &&
conflictWireNet->wires.count(conflictPipWire))
conflictPipWire = WireId();
if (conflictPipNet != nullptr && conflictWireWire != WireId() && conflictPipNet->wires.count(conflictWireWire))
if (conflictPipNet != nullptr && conflictWireWire != WireId() &&
conflictPipNet->wires.count(conflictWireWire))
conflictWireWire = WireId();
if (conflictWireWire == conflictPipWire)
@ -545,7 +601,7 @@ struct Router1
#if 0
if (ctx->debug)
log("Found better route to %s. Old vs new delay estimate: %.3f (%.3f) %.3f (%.3f)\n",
ctx->getWireName(next_wire).c_str(ctx),
ctx->nameOfWire(next_wire),
ctx->getDelayNS(old_score),
ctx->getDelayNS(old_visited_it->second.delay),
ctx->getDelayNS(next_score),
@ -562,7 +618,7 @@ struct Router1
if (cfg.useEstimate) {
next_qw.togo = ctx->estimateDelay(next_wire, dst_wire);
delay_t this_est = next_qw.delay + next_qw.togo;
if (this_est/2 - cfg.estimatePrecision > best_est)
if (this_est / 2 - cfg.estimatePrecision > best_est)
continue;
if (best_est > this_est)
best_est = this_est;
@ -572,8 +628,8 @@ struct Router1
#if 0
if (ctx->debug)
log("%s -> %s: %.3f (%.3f)\n",
ctx->getWireName(qw.wire).c_str(ctx),
ctx->getWireName(next_wire).c_str(ctx),
ctx->nameOfWire(qw.wire),
ctx->nameOfWire(next_wire),
ctx->getDelayNS(next_score),
ctx->getDelayNS(next_delay));
#endif
@ -582,8 +638,7 @@ struct Router1
queue.push(next_qw);
if (next_wire == dst_wire) {
if (maxVisitCnt == INT_MAX)
maxVisitCnt = 2*visitCnt;
maxVisitCnt = std::min(maxVisitCnt, 2 * visitCnt + (next_qw.penalty > 0 ? 100 : 0));
best_score = next_score - next_bonus;
}
}
@ -602,6 +657,7 @@ struct Router1
log(" final route delay: %8.2f\n", ctx->getDelayNS(visited[dst_wire].delay));
log(" final route penalty: %8.2f\n", ctx->getDelayNS(visited[dst_wire].penalty));
log(" final route bonus: %8.2f\n", ctx->getDelayNS(visited[dst_wire].bonus));
log(" arc budget: %12.2f\n", ctx->getDelayNS(net_info->users[user_idx].budget));
}
// bind resulting route (and maybe unroute other nets)
@ -609,14 +665,26 @@ struct Router1
std::unordered_set<WireId> unassign_wires = arc_to_wires[arc];
WireId cursor = dst_wire;
delay_t accumulated_path_delay = 0;
delay_t last_path_delay_delta = 0;
while (1) {
auto pip = visited[cursor].pip;
if (ctx->debug)
log(" node %s\n", ctx->getWireName(cursor).c_str(ctx));
if (ctx->debug) {
delay_t path_delay_delta = ctx->estimateDelay(cursor, dst_wire) - accumulated_path_delay;
if (pip == PipId())
NPNR_ASSERT(cursor == src_wire);
log(" node %s (%+.2f %+.2f)\n", ctx->nameOfWire(cursor), ctx->getDelayNS(path_delay_delta),
ctx->getDelayNS(path_delay_delta - last_path_delay_delta));
last_path_delay_delta = path_delay_delta;
if (pip != PipId())
accumulated_path_delay += ctx->getPipDelay(pip).maxDelay();
accumulated_path_delay += ctx->getWireDelay(cursor).maxDelay();
}
if (pip == PipId())
NPNR_ASSERT(cursor == src_wire);
if (!net_info->wires.count(cursor) || net_info->wires.at(cursor).pip != pip) {
if (!ctx->checkWireAvail(cursor)) {
@ -631,11 +699,11 @@ struct Router1
if (pip == PipId()) {
if (ctx->debug)
log(" bind wire %s\n", ctx->getWireName(cursor).c_str(ctx));
log(" bind wire %s\n", ctx->nameOfWire(cursor));
ctx->bindWire(cursor, net_info, STRENGTH_WEAK);
} else {
if (ctx->debug)
log(" bind pip %s\n", ctx->getPipName(pip).c_str(ctx));
log(" bind pip %s\n", ctx->nameOfPip(pip));
ctx->bindPip(pip, net_info, STRENGTH_WEAK);
}
}
@ -670,8 +738,8 @@ Router1Cfg::Router1Cfg(Context *ctx) : Settings(ctx)
useEstimate = get<bool>("router1/useEstimate", true);
wireRipupPenalty = ctx->getRipupDelayPenalty();
netRipupPenalty = 10*ctx->getRipupDelayPenalty();
reuseBonus = wireRipupPenalty/2;
netRipupPenalty = 10 * ctx->getRipupDelayPenalty();
reuseBonus = wireRipupPenalty / 2;
estimatePrecision = 100 * ctx->getRipupDelayPenalty();
}
@ -702,10 +770,9 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
while (!router.arc_queue.empty()) {
if (++iter_cnt % 1000 == 0) {
log_info("%10d | %8d %10d | %4d %5d | %9d\n",
iter_cnt, router.arcs_with_ripup, router.arcs_without_ripup,
router.arcs_with_ripup - last_arcs_with_ripup,
router.arcs_without_ripup - last_arcs_without_ripup, int(router.arc_queue.size()));
log_info("%10d | %8d %10d | %4d %5d | %9d\n", iter_cnt, router.arcs_with_ripup,
router.arcs_without_ripup, router.arcs_with_ripup - last_arcs_with_ripup,
router.arcs_without_ripup - last_arcs_without_ripup, int(router.arc_queue.size()));
last_arcs_with_ripup = router.arcs_with_ripup;
last_arcs_without_ripup = router.arcs_without_ripup;
#ifndef NDEBUG
@ -719,8 +786,7 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
arc_key arc = router.arc_queue_pop();
if (!router.route_arc(arc, true)) {
log_warning("Failed to find a route for arc %d of net %s.\n",
arc.user_idx, arc.net_info->name.c_str(ctx));
log_warning("Failed to find a route for arc %d of net %s.\n", arc.user_idx, ctx->nameOf(arc.net_info));
#ifndef NDEBUG
router.check();
ctx->check();
@ -730,10 +796,9 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
}
}
log_info("%10d | %8d %10d | %4d %5d | %9d\n",
iter_cnt, router.arcs_with_ripup, router.arcs_without_ripup,
router.arcs_with_ripup - last_arcs_with_ripup,
router.arcs_without_ripup - last_arcs_without_ripup, int(router.arc_queue.size()));
log_info("%10d | %8d %10d | %4d %5d | %9d\n", iter_cnt, router.arcs_with_ripup, router.arcs_without_ripup,
router.arcs_with_ripup - last_arcs_with_ripup, router.arcs_without_ripup - last_arcs_without_ripup,
int(router.arc_queue.size()));
log_info("Routing complete.\n");
#ifndef NDEBUG
@ -758,165 +823,171 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
bool Context::checkRoutedDesign() const
{
const Context *ctx = getCtx();
const Context *ctx = getCtx();
for (auto &net_it : ctx->nets) {
NetInfo *net_info = net_it.second.get();
for (auto &net_it : ctx->nets) {
NetInfo *net_info = net_it.second.get();
#ifdef ARCH_ECP5
if (net_info->is_global)
continue;
#endif
if (ctx->debug)
log("checking net %s\n", ctx->nameOf(net_info));
if (net_info->users.empty()) {
if (ctx->debug)
log("checking net %s\n", net_info->name.c_str(ctx));
if (net_info->users.empty()) {
if (ctx->debug)
log(" net without sinks\n");
log_assert(net_info->wires.empty());
continue;
}
bool found_unrouted = false;
bool found_loop = false;
bool found_stub = false;
struct ExtraWireInfo {
int order_num = 0;
std::unordered_set<WireId> children;
};
std::unordered_map<WireId, ExtraWireInfo> db;
for (auto &it : net_info->wires) {
WireId w = it.first;
PipId p = it.second.pip;
if (p != PipId()) {
log_assert(ctx->getPipDstWire(p) == w);
db[ctx->getPipSrcWire(p)].children.insert(w);
}
}
auto src_wire = ctx->getNetinfoSourceWire(net_info);
log_assert(src_wire != WireId());
if (net_info->wires.count(src_wire) == 0) {
if (ctx->debug)
log(" source (%s) not bound to net\n", ctx->getWireName(src_wire).c_str(ctx));
found_unrouted = true;
}
std::unordered_map<WireId, int> dest_wires;
for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) {
auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]);
log_assert(dst_wire != WireId());
dest_wires[dst_wire] = user_idx;
if (net_info->wires.count(dst_wire) == 0) {
if (ctx->debug)
log(" sink %d (%s) not bound to net\n", user_idx, ctx->getWireName(dst_wire).c_str(ctx));
found_unrouted = true;
}
}
std::function<void(WireId, int)> setOrderNum;
std::unordered_set<WireId> logged_wires;
setOrderNum = [&](WireId w, int num) {
auto &db_entry = db[w];
if (db_entry.order_num != 0) {
found_loop = true;
log(" %*s=> loop\n", 2*num, "");
return;
}
db_entry.order_num = num;
for (WireId child : db_entry.children) {
if (ctx->debug) {
log(" %*s-> %s\n", 2*num, "", ctx->getWireName(child).c_str(ctx));
logged_wires.insert(child);
}
setOrderNum(child, num+1);
}
if (db_entry.children.empty()) {
if (dest_wires.count(w) != 0) {
if (ctx->debug)
log(" %*s=> sink %d\n", 2*num, "", dest_wires.at(w));
} else {
if (ctx->debug)
log(" %*s=> stub\n", 2*num, "");
found_stub = true;
}
}
};
if (ctx->debug) {
log(" driver: %s\n", ctx->getWireName(src_wire).c_str(ctx));
logged_wires.insert(src_wire);
}
setOrderNum(src_wire, 1);
std::unordered_set<WireId> dangling_wires;
for (auto &it : db) {
auto &db_entry = it.second;
if (db_entry.order_num == 0)
dangling_wires.insert(it.first);
}
if (ctx->debug) {
if (dangling_wires.empty()) {
log(" no dangling wires.\n");
} else {
std::unordered_set<WireId> root_wires = dangling_wires;
for (WireId w : dangling_wires) {
for (WireId c : db[w].children)
root_wires.erase(c);
}
for (WireId w : root_wires) {
log(" dangling wire: %s\n", ctx->getWireName(w).c_str(ctx));
logged_wires.insert(w);
setOrderNum(w, 1);
}
for (WireId w : dangling_wires) {
if (logged_wires.count(w) == 0)
log(" loop: %s -> %s\n",
ctx->getWireName(ctx->getPipSrcWire(net_info->wires.at(w).pip)).c_str(ctx),
ctx->getWireName(w).c_str(ctx));
}
}
}
bool fail = false;
if (found_unrouted) {
if (ctx->debug)
log("check failed: found unrouted arcs\n");
fail = true;
}
if (found_loop) {
if (ctx->debug)
log("check failed: found loops\n");
fail = true;
}
if (found_stub) {
if (ctx->debug)
log("check failed: found stubs\n");
fail = true;
}
if (!dangling_wires.empty()) {
if (ctx->debug)
log("check failed: found dangling wires\n");
fail = true;
}
if (fail)
return false;
log(" net without sinks\n");
log_assert(net_info->wires.empty());
continue;
}
return true;
bool found_unrouted = false;
bool found_loop = false;
bool found_stub = false;
struct ExtraWireInfo
{
int order_num = 0;
std::unordered_set<WireId> children;
};
std::unordered_map<WireId, ExtraWireInfo> db;
for (auto &it : net_info->wires) {
WireId w = it.first;
PipId p = it.second.pip;
if (p != PipId()) {
log_assert(ctx->getPipDstWire(p) == w);
db[ctx->getPipSrcWire(p)].children.insert(w);
}
}
auto src_wire = ctx->getNetinfoSourceWire(net_info);
log_assert(src_wire != WireId());
if (net_info->wires.count(src_wire) == 0) {
if (ctx->debug)
log(" source (%s) not bound to net\n", ctx->nameOfWire(src_wire));
found_unrouted = true;
}
std::unordered_map<WireId, int> dest_wires;
for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) {
auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]);
log_assert(dst_wire != WireId());
dest_wires[dst_wire] = user_idx;
if (net_info->wires.count(dst_wire) == 0) {
if (ctx->debug)
log(" sink %d (%s) not bound to net\n", user_idx, ctx->nameOfWire(dst_wire));
found_unrouted = true;
}
}
std::function<void(WireId, int)> setOrderNum;
std::unordered_set<WireId> logged_wires;
setOrderNum = [&](WireId w, int num) {
auto &db_entry = db[w];
if (db_entry.order_num != 0) {
found_loop = true;
log(" %*s=> loop\n", 2 * num, "");
return;
}
db_entry.order_num = num;
for (WireId child : db_entry.children) {
if (ctx->debug) {
log(" %*s-> %s\n", 2 * num, "", ctx->nameOfWire(child));
logged_wires.insert(child);
}
setOrderNum(child, num + 1);
}
if (db_entry.children.empty()) {
if (dest_wires.count(w) != 0) {
if (ctx->debug)
log(" %*s=> sink %d\n", 2 * num, "", dest_wires.at(w));
} else {
if (ctx->debug)
log(" %*s=> stub\n", 2 * num, "");
found_stub = true;
}
}
};
if (ctx->debug) {
log(" driver: %s\n", ctx->nameOfWire(src_wire));
logged_wires.insert(src_wire);
}
setOrderNum(src_wire, 1);
std::unordered_set<WireId> dangling_wires;
for (auto &it : db) {
auto &db_entry = it.second;
if (db_entry.order_num == 0)
dangling_wires.insert(it.first);
}
if (ctx->debug) {
if (dangling_wires.empty()) {
log(" no dangling wires.\n");
} else {
std::unordered_set<WireId> root_wires = dangling_wires;
for (WireId w : dangling_wires) {
for (WireId c : db[w].children)
root_wires.erase(c);
}
for (WireId w : root_wires) {
log(" dangling wire: %s\n", ctx->nameOfWire(w));
logged_wires.insert(w);
setOrderNum(w, 1);
}
for (WireId w : dangling_wires) {
if (logged_wires.count(w) == 0)
log(" loop: %s -> %s\n",
ctx->nameOfWire(ctx->getPipSrcWire(net_info->wires.at(w).pip)),
ctx->nameOfWire(w));
}
}
}
bool fail = false;
if (found_unrouted) {
if (ctx->debug)
log("check failed: found unrouted arcs\n");
fail = true;
}
if (found_loop) {
if (ctx->debug)
log("check failed: found loops\n");
fail = true;
}
if (found_stub) {
if (ctx->debug)
log("check failed: found stubs\n");
fail = true;
}
if (!dangling_wires.empty()) {
if (ctx->debug)
log("check failed: found dangling wires\n");
fail = true;
}
if (fail)
return false;
}
return true;
}
bool Context::getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay,

View File

@ -30,15 +30,15 @@ delay_t maxDelay() const { return delay; }
### BelId
A type representing a bel name. `BelId()` must construct a unique null-value. Must provide `==` and `!=` operators and a specialization for `std::hash<BelId>`.
A type representing a bel name. `BelId()` must construct a unique null-value. Must provide `==`, `!=`, and `<` operators and a specialization for `std::hash<BelId>`.
### WireId
A type representing a wire name. `WireId()` must construct a unique null-value. Must provide `==` and `!=` operators and a specialization for `std::hash<WireId>`.
A type representing a wire name. `WireId()` must construct a unique null-value. Must provide `==`, `!=`, and `<` operators and a specialization for `std::hash<WireId>`.
### PipId
A type representing a pip name. `PipId()` must construct a unique null-value. Must provide `==` and `!=` operators and a specialization for `std::hash<PipId>`.
A type representing a pip name. `PipId()` must construct a unique null-value. Must provide `==`, `!=`, and `<` operators and a specialization for `std::hash<PipId>`.
### GroupId

View File

@ -38,7 +38,94 @@ For nextpnr we are using the following terminology.
Adding new architectures to nextpnr
-----------------------------------
TBD
### Implementing new architectures
Each nextpnr architecture must implement the *nextpnr architecture API*.
See [archapi.md](archapi.md) for a complete reference of the architecture API.
### Delay Estimates
Each architecture must implement a `estimateDelay()` method that estimates the expected delay for a path from given `src` to `dst` wires.
*It is very important that this method slightly overestimates the expected delay.* Furthermore, it should overestimate the expected delay
by a slightly larger margin for longer paths than for shorter paths. Otherwise there will be performance issues with the router.
The delays estimates returned by that method should also be as fine-grain as possible. It definitely pays off to spend some time improving the `estimateDelay()`
for your architecture once implementing small designs work.
### Ripup Information
The `getConflictingWireWire()`, `getConflictingWireNet()`, `getConflictingPipWire()`, and `getConflictingPipNet()` methods are used by the router
to determine which resources to rip up in order to make a given routing resource (wire or pip) available.
The architecture must guanrantee that the following invariants hold.
**Invariant 1:**
```
if (!ctx->checkWireAvail(wire)) {
WireId w = getConflictingWireWire(wire);
if (w != WireId()) {
ctx->unbindWire(w);
assert(ctx->checkWireAvail(wire));
}
}
```
**Invariant 2:**
```
if (!ctx->checkWireAvail(wire)) {
NetInfo *n = getConflictingWireNet(wire);
if (n != nullptr) {
for (auto &it : n->wires)
ctx->unbindWire(it.first);
assert(ctx->checkWireAvail(wire));
}
}
```
**Invariant 3:**
```
if (!ctx->checkPipAvail(pip)) {
WireId w = getConflictingPipWire(pip);
if (w != WireId()) {
ctx->unbindWire(w);
assert(ctx->checkPipAvail(pip));
}
}
```
**Invariant 4:**
```
if (!ctx->checkPipAvail(pip)) {
NetInfo *n = getConflictingPipNet(pip);
if (n != nullptr) {
for (auto &it : n->wires)
ctx->unbindWire(it.first);
assert(ctx->checkPipAvail(pip));
}
}
```
**Invariant 5:**
```
if (ctx->checkWireAvail(wire)) {
// bind is guaranteed to succeed
ctx->bindWire(wire, net, strength);
}
```
**Invariant 6:**
```
if (ctx->checkPipAvail(pip) && ctx->checkWireAvail(ctx->getPipDstWire(pip))) {
// bind is guaranteed to succeed
ctx->bindPip(pip, net, strength);
}
```
Nextpnr and other tools
-----------------------

View File

@ -400,7 +400,7 @@ BelId Arch::getBelByLocation(Loc loc) const
delay_t Arch::estimateDelay(WireId src, WireId dst) const
{
return 100 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y));
return 170 * (abs(src.location.x - dst.location.x) + abs(src.location.y - dst.location.y));
}
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
@ -409,7 +409,7 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
auto driver_loc = getBelLocation(driver.cell->bel);
auto sink_loc = getBelLocation(sink.cell->bel);
return 100 * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y));
return 170 * (abs(driver_loc.x - sink_loc.x) + abs(driver_loc.y - sink_loc.y));
}
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }

View File

@ -619,10 +619,7 @@ struct Arch : BaseCtx
return wire_to_net.at(wire);
}
WireId getConflictingWireWire(WireId wire) const
{
return wire;
}
WireId getConflictingWireWire(WireId wire) const { return wire; }
NetInfo *getConflictingWireNet(WireId wire) const
{
@ -729,10 +726,7 @@ struct Arch : BaseCtx
return pip_to_net.at(pip);
}
WireId getConflictingPipWire(PipId pip) const
{
return WireId();
}
WireId getConflictingPipWire(PipId pip) const { return WireId(); }
NetInfo *getConflictingPipNet(PipId pip) const
{

View File

@ -75,6 +75,7 @@ struct Location
bool operator==(const Location &other) const { return x == other.x && y == other.y; }
bool operator!=(const Location &other) const { return x != other.x || y != other.y; }
bool operator<(const Location &other) const { return y == other.y ? x < other.x : y < other.y; }
};
inline Location operator+(const Location &a, const Location &b) { return Location(a.x + b.x, a.y + b.y); }
@ -86,6 +87,7 @@ struct BelId
bool operator==(const BelId &other) const { return index == other.index && location == other.location; }
bool operator!=(const BelId &other) const { return index != other.index || location != other.location; }
bool operator<(const BelId &other) const { return location == other.location ? index < other.index : location < other.location; }
};
struct WireId
@ -95,6 +97,7 @@ struct WireId
bool operator==(const WireId &other) const { return index == other.index && location == other.location; }
bool operator!=(const WireId &other) const { return index != other.index || location != other.location; }
bool operator<(const WireId &other) const { return location == other.location ? index < other.index : location < other.location; }
};
struct PipId
@ -104,6 +107,7 @@ struct PipId
bool operator==(const PipId &other) const { return index == other.index && location == other.location; }
bool operator!=(const PipId &other) const { return index != other.index || location != other.location; }
bool operator<(const PipId &other) const { return location == other.location ? index < other.index : location < other.location; }
};
struct GroupId

View File

@ -453,8 +453,8 @@ void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelecti
addProperty(topItem, QVariant::String, "Type", ctx->getWireType(wire).c_str(ctx));
addProperty(topItem, QVariant::Bool, "Available", ctx->checkWireAvail(wire));
addProperty(topItem, QVariant::String, "Bound Net", ctx->nameOf(ctx->getBoundWireNet(wire)), ElementType::NET);
addProperty(topItem, QVariant::String, "Conflicting Wire", ctx->getWireName(ctx->getConflictingWireWire(wire)).c_str(ctx),
ElementType::WIRE);
addProperty(topItem, QVariant::String, "Conflicting Wire",
ctx->getWireName(ctx->getConflictingWireWire(wire)).c_str(ctx), ElementType::WIRE);
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->nameOf(ctx->getConflictingWireNet(wire)),
ElementType::NET);
@ -515,8 +515,8 @@ void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelecti
addProperty(topItem, QVariant::String, "Type", ctx->getPipType(pip).c_str(ctx));
addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
addProperty(topItem, QVariant::String, "Bound Net", ctx->nameOf(ctx->getBoundPipNet(pip)), ElementType::NET);
addProperty(topItem, QVariant::String, "Conflicting Wire", ctx->getWireName(ctx->getConflictingPipWire(pip)).c_str(ctx),
ElementType::WIRE);
addProperty(topItem, QVariant::String, "Conflicting Wire",
ctx->getWireName(ctx->getConflictingPipWire(pip)).c_str(ctx), ElementType::WIRE);
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->nameOf(ctx->getConflictingPipNet(pip)),
ElementType::NET);
addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx),

View File

@ -566,10 +566,7 @@ struct Arch : BaseCtx
return wire_to_net[wire.index];
}
WireId getConflictingWireWire(WireId wire) const
{
return wire;
}
WireId getConflictingWireWire(WireId wire) const { return wire; }
NetInfo *getConflictingWireNet(WireId wire) const
{

View File

@ -66,6 +66,7 @@ struct BelId
bool operator==(const BelId &other) const { return index == other.index; }
bool operator!=(const BelId &other) const { return index != other.index; }
bool operator<(const BelId &other) const { return index < other.index; }
};
struct WireId
@ -74,6 +75,7 @@ struct WireId
bool operator==(const WireId &other) const { return index == other.index; }
bool operator!=(const WireId &other) const { return index != other.index; }
bool operator<(const WireId &other) const { return index < other.index; }
};
struct PipId
@ -82,6 +84,7 @@ struct PipId
bool operator==(const PipId &other) const { return index == other.index; }
bool operator!=(const PipId &other) const { return index != other.index; }
bool operator<(const PipId &other) const { return index < other.index; }
};
struct GroupId