router2: fcroute-based rewrite

This commit is contained in:
Lofty 2022-09-24 18:04:46 +01:00
parent a1ad114069
commit c9b3c72bff

View File

@ -368,12 +368,7 @@ struct Router2
float get_togo_cost(NetInfo *net, store_index<PortRef> user, int wire, WireId src_sink, bool bwd, float crit_weight)
{
auto &nd = nets.at(net->udata);
auto &wd = flat_wires[wire];
int source_uses = 0;
if (nd.wires.count(wd.w)) {
source_uses = nd.wires.at(wd.w).second;
}
// FIXME: timing/wirelength balance?
delay_t est_delay = ctx->estimateDelay(bwd ? src_sink : wd.w, bwd ? wd.w : src_sink);
return ctx->getDelayNS(est_delay) * cfg.estimate_weight;
@ -584,6 +579,259 @@ struct Router2
(tmg.get_setup_slack(CellPortKey(net->users.at(usr_idx))) < (2 * ctx->getDelayEpsilon()));
}
ArcRouteResult route_arc_lofty(ThreadContext &t, NetInfo *net, store_index<PortRef> i, size_t phys_pin, bool is_mt,
bool is_bb = true)
{
// Do some initial lookups and checks
auto arc_start = std::chrono::high_resolution_clock::now();
auto &nd = nets[net->udata];
auto &ad = nd.arcs.at(i.idx()).at(phys_pin);
auto &usr = net->users.at(i);
ROUTE_LOG_DBG("Routing arc %d of net '%s' (%d, %d) -> (%d, %d)\n", i.idx(), ctx->nameOf(net), ad.bb.x0,
ad.bb.y0, ad.bb.x1, ad.bb.y1);
bool verbose = false; //strcmp(ctx->nameOf(net), "_zz_132__LUT4_A_Z") == 0;
WireId src_wire = ctx->getNetinfoSourceWire(net), dst_wire = ctx->getNetinfoSinkWire(net, usr, phys_pin);
if (src_wire == WireId())
ARC_LOG_ERR("No wire found for port %s on source cell %s.\n", ctx->nameOf(net->driver.port),
ctx->nameOf(net->driver.cell));
if (dst_wire == WireId())
ARC_LOG_ERR("No wire found for port %s on destination cell %s.\n", ctx->nameOf(usr.port),
ctx->nameOf(usr.cell));
int src_wire_idx = wire_to_idx.at(src_wire);
int dst_wire_idx = wire_to_idx.at(dst_wire);
// Calculate a timing weight based on criticality
float crit = get_arc_crit(net, i);
float crit_weight = (1.0f - std::pow(crit, 2));
ROUTE_LOG_DBG(" crit=%.3f crit_weight=%.3f\n", crit, crit_weight);
// Check if arc was already done _in this iteration_
if (t.processed_sinks.count(dst_wire))
return ARC_SUCCESS;
if (verbose)
log("awoo\n");
// Impl note:
// t.fwd_queue = H (i.e. the high priority nodes)
// t.bwd_queue = G (i.e. the unexpanded nodes)
auto low_prio = std::vector<QueuedWire>{};
// Clear out the queues
if (!t.fwd_queue.empty()) {
std::priority_queue<QueuedWire, std::vector<QueuedWire>, QueuedWire::Greater> new_queue;
t.fwd_queue.swap(new_queue);
}
if (!t.bwd_queue.empty()) {
std::priority_queue<QueuedWire, std::vector<QueuedWire>, QueuedWire::Greater> new_queue;
t.bwd_queue.swap(new_queue);
}
// Unvisit any previously visited wires
reset_wires(t);
auto wire_count = [](int x0, int x1) {
unsigned int priority = 0;
auto x = x1 - x0;
// HACK: ECP5-specific.
// H6 wires:
priority += x / 6;
x -= 6 * (x / 6);
if (x % 6 == 2 || x % 6 == 4) {
// Assume an extra H6 is used here.
priority++;
x -= x % 6;
}
// H2/H1 wires:
priority += x / 2;
if (x % 2 == 1) {
// Assume an extra H2 or H1 is used here.
priority++;
}
return priority;
};
// Fast maze search.
auto fast_search = [&](QueuedWire curr){
while (curr.wire != dst_wire_idx) {
NPNR_ASSERT(curr.wire < flat_wires.size());
auto &curr_data = flat_wires.at(curr.wire);
// Neighbour pass 1: find priorities.
auto lowest_priority = std::numeric_limits<unsigned int>::max();
for (PipId dh : ctx->getPipsDownhill(curr_data.w)) {
// Skip pips outside of box in bounding-box mode
if (is_bb && !hit_test_pip(nd.bb, ctx->getPipLocation(dh)))
continue;
if (!ctx->checkPipAvailForNet(dh, net))
continue;
WireId next = ctx->getPipDstWire(dh);
int next_idx = wire_to_idx.at(next);
if (was_visited_fwd(next_idx)) {
// Don't expand the same node twice.
continue;
}
auto &nwd = flat_wires.at(next_idx);
if (nwd.unavailable)
continue;
// Reserved for another net
if (nwd.reserved_net != -1 && nwd.reserved_net != net->udata)
continue;
// Don't allow the same wire to be bound to the same net with a different driving pip
auto fnd_wire = nd.wires.find(next);
if (fnd_wire != nd.wires.end() && fnd_wire->second.first != dh)
continue;
if (!thread_test_wire(t, nwd))
continue; // thread safety issue
auto box = ctx->getRouteBoundingBox(ctx->getPipDstWire(dh), dst_wire);
unsigned int priority = wire_count(box.x0, box.x1) + wire_count(box.y0, box.y1);
lowest_priority = std::min(priority, lowest_priority);
}
// Neighbour pass 2: add high-priority nodes to the list.
for (PipId dh : ctx->getPipsDownhill(curr_data.w)) {
WireId next = ctx->getPipDstWire(dh);
int next_idx = wire_to_idx.at(next);
// Skip pips outside of box in bounding-box mode
if (is_bb && !hit_test_pip(nd.bb, ctx->getPipLocation(dh)))
continue;
if (!ctx->checkPipAvailForNet(dh, net))
continue;
if (was_visited_fwd(next_idx))
continue;
auto &nwd = flat_wires.at(next_idx);
if (nwd.unavailable)
continue;
// Reserved for another net
if (nwd.reserved_net != -1 && nwd.reserved_net != net->udata)
continue;
// Don't allow the same wire to be bound to the same net with a different driving pip
auto fnd_wire = nd.wires.find(next);
if (fnd_wire != nd.wires.end() && fnd_wire->second.first != dh)
continue;
if (!thread_test_wire(t, nwd))
continue; // thread safety issue
WireScore next_score;
next_score.criticality = crit;
next_score.congest = curr.score.congest + score_wire_for_arc_congest(net, i, phys_pin, next, dh, crit_weight);
next_score.delay = curr.score.delay + score_wire_for_arc_delay(net, i, phys_pin, next, dh, crit_weight);
next_score.togo = get_togo_cost(net, i, next_idx, dst_wire, false, crit_weight);
auto qw = QueuedWire(next_idx, next_score, t.rng.rng());
auto box = ctx->getRouteBoundingBox(ctx->getPipDstWire(dh), dst_wire);
unsigned int priority = wire_count(box.x0, box.x1) + wire_count(box.y0, box.y1);
if (priority - lowest_priority == 0) {
auto& unexplored_best = t.bwd_queue.top();
if (t.bwd_queue.empty() || next_score.total() <= unexplored_best.score.total()) {
t.fwd_queue.push(qw);
} else {
t.bwd_queue.push(qw);
}
} else {
low_prio.push_back(qw);
}
set_visited_fwd(t, next_idx, dh);
}
if (t.fwd_queue.empty()) {
return false;
} else {
curr = t.fwd_queue.top();
t.fwd_queue.pop();
}
}
return true;
};
auto backtrack = [&](){
auto& unexplored_best = t.bwd_queue.top();
auto unexplored_best_score = t.bwd_queue.empty() ? std::numeric_limits<float>::max() : unexplored_best.score.total();
if (!low_prio.empty()) {
auto p = std::partition(low_prio.begin(), low_prio.end(), [=](QueuedWire w) { return w.score.total() > unexplored_best_score; });
for (auto it = p; it != low_prio.end(); it++) {
//log("bt: wire %s -> G\n", ctx->getWireName(flat_wires.at(it->wire).w).str(ctx).c_str());
t.bwd_queue.push(*it);
}
if (t.bwd_queue.empty()) {
auto greater = [](const QueuedWire &lhs, const QueuedWire &rhs) {
float lhs_score = lhs.score.total(),
rhs_score = rhs.score.total();
return lhs_score == rhs_score ? lhs.randtag > rhs.randtag : lhs_score > rhs_score;
};
std::nth_element(low_prio.begin(), low_prio.end()-1, low_prio.end(), greater);
std::nth_element(low_prio.begin(), low_prio.end()-2, low_prio.end(), greater);
std::nth_element(low_prio.begin(), low_prio.end()-3, low_prio.end(), greater);
t.bwd_queue.push(*(low_prio.end()-1));
t.bwd_queue.push(*(low_prio.end()-2));
t.bwd_queue.push(*(low_prio.end()-3));
p = low_prio.end() - 3;
}
low_prio.erase(p, low_prio.end());
}
NPNR_ASSERT(!t.bwd_queue.empty());
auto curr = t.bwd_queue.top();
t.bwd_queue.pop();
NPNR_ASSERT(curr.wire < flat_wires.size());
return curr;
};
WireScore next_score;
next_score.criticality = crit;
next_score.congest = 0.0;
next_score.delay = 0.0;
next_score.togo = get_togo_cost(net, i, src_wire_idx, dst_wire, false, crit_weight);
auto curr = QueuedWire(src_wire_idx, next_score, t.rng.rng());
NPNR_ASSERT(curr.wire < flat_wires.size());
bool done = false;
int fs_calls = 0;
int bt_calls = 0;
while (!done) {
fs_calls++;
done = fast_search(curr);
if (!done) {
if (t.bwd_queue.empty() && low_prio.empty()) {
reset_wires(t);
//log("fail; %d fs calls, %d bt calls\n", fs_calls, bt_calls);
return ARC_RETRY_WITHOUT_BB;
} else {
curr = backtrack();
bt_calls++;
}
}
}
int cursor_bwd = dst_wire_idx;
while (was_visited_fwd(cursor_bwd)) {
PipId pip = flat_wires.at(cursor_bwd).pip_fwd;
if (pip == PipId() && cursor_bwd != src_wire_idx)
break;
bind_pip_internal(nd, i, cursor_bwd, pip);
if (ctx->debug && !is_mt) {
auto &wd = flat_wires.at(cursor_bwd);
ROUTE_LOG_DBG(" fwd wire: %s (curr %d hist %f share %d)\n", ctx->nameOfWire(wd.w),
wd.curr_cong - 1, wd.hist_cong_cost, nd.wires.at(wd.w).second);
}
if (pip == PipId()) {
break;
}
ROUTE_LOG_DBG(" fwd pip: %s (%d, %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x,
ctx->getPipLocation(pip).y);
cursor_bwd = wire_to_idx.at(ctx->getPipSrcWire(pip));
}
reset_wires(t);
//log("done; %d fs calls, %d bt calls\n", fs_calls, bt_calls);
return ARC_SUCCESS;
}
ArcRouteResult route_arc(ThreadContext &t, NetInfo *net, store_index<PortRef> i, size_t phys_pin, bool is_mt,
bool is_bb = true)
{
@ -910,7 +1158,7 @@ struct Router2
return get_arc_crit(net, a.first) > get_arc_crit(net, b.first);
});
for (auto a : t.route_arcs) {
auto res1 = route_arc(t, net, a.first, a.second, is_mt, true);
auto res1 = route_arc_lofty(t, net, a.first, a.second, is_mt, true);
if (res1 == ARC_FATAL)
return false; // Arc failed irrecoverably
else if (res1 == ARC_RETRY_WITHOUT_BB) {
@ -921,7 +1169,7 @@ struct Router2
// Attempt a re-route without the bounding box constraint
ROUTE_LOG_DBG("Rerouting arc %d.%d of net '%s' without bounding box, possible tricky routing...\n",
a.first.idx(), int(a.second), ctx->nameOf(net));
auto res2 = route_arc(t, net, a.first, a.second, is_mt, false);
auto res2 = route_arc_lofty(t, net, a.first, a.second, is_mt, false);
// If this also fails, no choice but to give up
if (res2 != ARC_SUCCESS) {
if (ctx->debug) {