|
|
|
@ -72,9 +72,11 @@ struct Router2
|
|
|
|
|
|
|
|
|
|
struct WireScore
|
|
|
|
|
{
|
|
|
|
|
float cost;
|
|
|
|
|
float togo_cost;
|
|
|
|
|
float total() const { return cost + togo_cost; }
|
|
|
|
|
float delay;
|
|
|
|
|
float togo;
|
|
|
|
|
float congest;
|
|
|
|
|
float criticality;
|
|
|
|
|
float total() const { return (delay * criticality) + (congest * (1.0f - criticality)) + togo; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct PerWireData
|
|
|
|
@ -232,7 +234,7 @@ struct Router2
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
explicit QueuedWire(int wire = -1, WireScore score = WireScore{}, int randtag = 0)
|
|
|
|
|
: wire(wire), score(score), randtag(randtag){};
|
|
|
|
|
: wire(wire), score(score), randtag(randtag) {};
|
|
|
|
|
|
|
|
|
|
int wire;
|
|
|
|
|
WireScore score;
|
|
|
|
@ -242,8 +244,8 @@ struct Router2
|
|
|
|
|
{
|
|
|
|
|
bool operator()(const QueuedWire &lhs, const QueuedWire &rhs) const noexcept
|
|
|
|
|
{
|
|
|
|
|
float lhs_score = lhs.score.cost + lhs.score.togo_cost,
|
|
|
|
|
rhs_score = rhs.score.cost + rhs.score.togo_cost;
|
|
|
|
|
float lhs_score = lhs.score.total(),
|
|
|
|
|
rhs_score = rhs.score.total();
|
|
|
|
|
return lhs_score == rhs_score ? lhs.randtag > rhs.randtag : lhs_score > rhs_score;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
@ -350,40 +352,26 @@ struct Router2
|
|
|
|
|
ad.routed = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float score_wire_for_arc(NetInfo *net, store_index<PortRef> user, size_t phys_pin, WireId wire, PipId pip,
|
|
|
|
|
float score_wire_for_arc_congest(NetInfo *net, store_index<PortRef> user, size_t phys_pin, WireId wire, PipId pip,
|
|
|
|
|
float crit_weight)
|
|
|
|
|
{
|
|
|
|
|
auto &wd = wire_data(wire);
|
|
|
|
|
auto &nd = nets.at(net->udata);
|
|
|
|
|
float base_cost = cfg.get_base_cost(ctx, wire, pip, crit_weight);
|
|
|
|
|
int overuse = wd.curr_cong;
|
|
|
|
|
float hist_cost = 1.0f + crit_weight * (wd.hist_cong_cost - 1.0f);
|
|
|
|
|
float bias_cost = 0;
|
|
|
|
|
int source_uses = 0;
|
|
|
|
|
if (nd.wires.count(wire)) {
|
|
|
|
|
overuse -= 1;
|
|
|
|
|
source_uses = nd.wires.at(wire).second;
|
|
|
|
|
}
|
|
|
|
|
float present_cost = 1.0f + overuse * curr_cong_weight * crit_weight;
|
|
|
|
|
if (pip != PipId()) {
|
|
|
|
|
Loc pl = ctx->getPipLocation(pip);
|
|
|
|
|
bias_cost = cfg.bias_cost_factor * (base_cost / int(net->users.entries())) *
|
|
|
|
|
((std::abs(pl.x - nd.cx) + std::abs(pl.y - nd.cy)) / float(nd.hpwl));
|
|
|
|
|
}
|
|
|
|
|
return base_cost * hist_cost * present_cost / (1 + (source_uses * crit_weight)) + bias_cost;
|
|
|
|
|
float delay = cfg.get_base_cost(ctx, wire, pip, crit_weight);
|
|
|
|
|
return (wd.hist_cong_cost + delay) * (1.0f + float(wd.curr_cong) * 5.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float score_wire_for_arc_delay(NetInfo *net, store_index<PortRef> user, size_t phys_pin, WireId wire, PipId pip,
|
|
|
|
|
float crit_weight)
|
|
|
|
|
{
|
|
|
|
|
return cfg.get_base_cost(ctx, wire, pip, crit_weight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) / (1 + source_uses * crit_weight)) + cfg.ipin_cost_adder;
|
|
|
|
|
return ctx->getDelayNS(est_delay) * cfg.estimate_weight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool check_arc_routing(NetInfo *net, store_index<PortRef> usr, size_t phys_pin)
|
|
|
|
@ -591,6 +579,365 @@ 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), "reset$TRELLIS_IO_IN") == 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;
|
|
|
|
|
|
|
|
|
|
// 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, int y0, int y1) {
|
|
|
|
|
float priority = 0;
|
|
|
|
|
auto x = x1 - x0;
|
|
|
|
|
auto y = y1 - y0;
|
|
|
|
|
// 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:
|
|
|
|
|
/*if (x % 2 == 1) {
|
|
|
|
|
// Assume an extra H2 or H1 is used here.
|
|
|
|
|
priority++;
|
|
|
|
|
}*/
|
|
|
|
|
return unsigned(0.56626506*x + 0.55421686*y);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto lowest_priority = [&](QueuedWire curr) {
|
|
|
|
|
auto &curr_data = flat_wires.at(curr.wire);
|
|
|
|
|
auto 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 p = wire_count(box.x0, box.x1, box.y0, box.y1);
|
|
|
|
|
priority = std::min(p, priority);
|
|
|
|
|
}
|
|
|
|
|
return priority;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
dict<PipId, unsigned int> prios;
|
|
|
|
|
|
|
|
|
|
// Fast maze search.
|
|
|
|
|
int nodes = 0;
|
|
|
|
|
auto fast_search = [&](QueuedWire curr){
|
|
|
|
|
if (curr.wire == dst_wire_idx) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool done = false;
|
|
|
|
|
while (!done) {
|
|
|
|
|
NPNR_ASSERT(curr.wire < flat_wires.size());
|
|
|
|
|
auto &curr_data = flat_wires.at(curr.wire);
|
|
|
|
|
|
|
|
|
|
nodes++;
|
|
|
|
|
|
|
|
|
|
// Neighbour pass 1: find priorities.
|
|
|
|
|
auto lowest = lowest_priority(curr);
|
|
|
|
|
|
|
|
|
|
// 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, box.y0, box.y1);
|
|
|
|
|
prios.insert({dh, priority});
|
|
|
|
|
if (priority - lowest == 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 (next_idx == dst_wire_idx) {
|
|
|
|
|
done = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (t.fwd_queue.empty()) {
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
curr = t.fwd_queue.top();
|
|
|
|
|
t.fwd_queue.pop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (verbose)
|
|
|
|
|
log(" explored %d nodes\n", nodes);
|
|
|
|
|
|
|
|
|
|
return done;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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 src_qw = QueuedWire(src_wire_idx, next_score, t.rng.rng());
|
|
|
|
|
t.fwd_queue.push(src_qw);
|
|
|
|
|
t.bwd_queue.push(src_qw);
|
|
|
|
|
|
|
|
|
|
bool done = false;
|
|
|
|
|
|
|
|
|
|
while (!t.fwd_queue.empty()) {
|
|
|
|
|
auto curr = t.fwd_queue.top();
|
|
|
|
|
t.fwd_queue.pop();
|
|
|
|
|
auto &curr_data = flat_wires.at(curr.wire);
|
|
|
|
|
|
|
|
|
|
nodes++;
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
|
|
|
|
|
if (net->wires.find(next) != net->wires.end()) {
|
|
|
|
|
log("Discovered existing routing wire %s\n", ctx->nameOfWire(next));
|
|
|
|
|
t.fwd_queue.push(qw);
|
|
|
|
|
t.bwd_queue.push(qw);
|
|
|
|
|
} else {
|
|
|
|
|
t.bwd_queue.push(qw);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_visited_fwd(t, next_idx, dh);
|
|
|
|
|
|
|
|
|
|
if (next_idx == dst_wire_idx)
|
|
|
|
|
done = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int fs_calls = 0;
|
|
|
|
|
int bt_calls = 0;
|
|
|
|
|
if (!done) {
|
|
|
|
|
NPNR_ASSERT(!t.bwd_queue.empty());
|
|
|
|
|
|
|
|
|
|
auto curr = t.bwd_queue.top();
|
|
|
|
|
t.bwd_queue.pop();
|
|
|
|
|
|
|
|
|
|
NPNR_ASSERT(curr.wire < flat_wires.size());
|
|
|
|
|
|
|
|
|
|
while (!done) {
|
|
|
|
|
fs_calls++;
|
|
|
|
|
if (verbose)
|
|
|
|
|
log("fast_search from %s to %s\n", ctx->nameOfWire(flat_wires.at(curr.wire).w), ctx->nameOfWire(dst_wire));
|
|
|
|
|
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 {
|
|
|
|
|
if (verbose)
|
|
|
|
|
log(" failed; backtracking\n");
|
|
|
|
|
curr = backtrack();
|
|
|
|
|
bt_calls++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int cursor_bwd = dst_wire_idx;
|
|
|
|
|
int path_prio = 0, path_len = 0;
|
|
|
|
|
while (was_visited_fwd(cursor_bwd)) {
|
|
|
|
|
PipId pip = flat_wires.at(cursor_bwd).pip_fwd;
|
|
|
|
|
if (prios.find(pip) != prios.end())
|
|
|
|
|
path_prio += prios.at(pip);
|
|
|
|
|
path_len++;
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
update_wire_by_loc(t, net, i, phys_pin, is_mt);
|
|
|
|
|
t.processed_sinks.insert(dst_wire);
|
|
|
|
|
ad.routed = true;
|
|
|
|
|
|
|
|
|
|
reset_wires(t);
|
|
|
|
|
if (verbose)
|
|
|
|
|
log("done; %d fs calls, %d bt calls\n", fs_calls, bt_calls);
|
|
|
|
|
|
|
|
|
|
auto arc_end = std::chrono::high_resolution_clock::now();
|
|
|
|
|
auto box = ctx->getRouteBoundingBox(src_wire, dst_wire);
|
|
|
|
|
//printf("%d,%d,%f,%d\n", box.x1 - box.x0, box.y1 - box.y0, crit, path_len);
|
|
|
|
|
if (verbose)
|
|
|
|
|
log("Routing arc %d of net '%s' (is_bb = %d) took %02fs; path: %.03f/%d/%d/%d nodes\n", i.idx(), ctx->nameOf(net), is_bb,
|
|
|
|
|
std::chrono::duration<float>(arc_end - arc_start).count(), float(path_prio)/float(path_len), lowest_priority(src_qw), path_len, nodes);
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
@ -647,9 +994,11 @@ struct Router2
|
|
|
|
|
// Add 'forward' direction startpoints to queue
|
|
|
|
|
auto seed_queue_fwd = [&](WireId wire, float wire_cost = 0) {
|
|
|
|
|
WireScore base_score;
|
|
|
|
|
base_score.cost = wire_cost;
|
|
|
|
|
base_score.criticality = crit;
|
|
|
|
|
base_score.congest = 0.0;
|
|
|
|
|
base_score.delay = wire_cost;
|
|
|
|
|
int wire_idx = wire_to_idx.at(wire);
|
|
|
|
|
base_score.togo_cost = get_togo_cost(net, i, wire_idx, dst_wire, false, crit_weight);
|
|
|
|
|
base_score.togo = get_togo_cost(net, i, wire_idx, dst_wire, false, crit);
|
|
|
|
|
t.fwd_queue.push(QueuedWire(wire_idx, base_score));
|
|
|
|
|
set_visited_fwd(t, wire_idx, PipId());
|
|
|
|
|
};
|
|
|
|
@ -677,9 +1026,11 @@ struct Router2
|
|
|
|
|
}
|
|
|
|
|
auto seed_queue_bwd = [&](WireId wire) {
|
|
|
|
|
WireScore base_score;
|
|
|
|
|
base_score.cost = 0;
|
|
|
|
|
base_score.criticality = crit;
|
|
|
|
|
base_score.congest = 0.0;
|
|
|
|
|
base_score.delay = 0;
|
|
|
|
|
int wire_idx = wire_to_idx.at(wire);
|
|
|
|
|
base_score.togo_cost = get_togo_cost(net, i, wire_idx, src_wire, true, crit_weight);
|
|
|
|
|
base_score.togo = get_togo_cost(net, i, wire_idx, src_wire, true, crit);
|
|
|
|
|
t.bwd_queue.push(QueuedWire(wire_idx, base_score));
|
|
|
|
|
set_visited_bwd(t, wire_idx, PipId());
|
|
|
|
|
};
|
|
|
|
@ -731,9 +1082,10 @@ struct Router2
|
|
|
|
|
if (!thread_test_wire(t, nwd))
|
|
|
|
|
continue; // thread safety issue
|
|
|
|
|
WireScore next_score;
|
|
|
|
|
next_score.cost = curr.score.cost + score_wire_for_arc(net, i, phys_pin, next, dh, crit_weight);
|
|
|
|
|
next_score.togo_cost =
|
|
|
|
|
cfg.estimate_weight * get_togo_cost(net, i, next_idx, dst_wire, false, crit_weight);
|
|
|
|
|
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);
|
|
|
|
|
set_visited_fwd(t, next_idx, dh);
|
|
|
|
|
t.fwd_queue.push(QueuedWire(next_idx, next_score, t.rng.rng()));
|
|
|
|
|
}
|
|
|
|
@ -777,9 +1129,10 @@ struct Router2
|
|
|
|
|
if (!thread_test_wire(t, nwd))
|
|
|
|
|
continue; // thread safety issue
|
|
|
|
|
WireScore next_score;
|
|
|
|
|
next_score.cost = curr.score.cost + score_wire_for_arc(net, i, phys_pin, next, uh, crit_weight);
|
|
|
|
|
next_score.togo_cost =
|
|
|
|
|
cfg.estimate_weight * get_togo_cost(net, i, next_idx, src_wire, true, crit_weight);
|
|
|
|
|
next_score.criticality = crit;
|
|
|
|
|
next_score.congest = curr.score.congest + score_wire_for_arc_congest(net, i, phys_pin, next, uh, crit_weight);
|
|
|
|
|
next_score.delay = curr.score.delay + score_wire_for_arc_delay(net, i, phys_pin, next, uh, crit_weight);
|
|
|
|
|
next_score.togo = get_togo_cost(net, i, next_idx, src_wire, true, crit_weight);
|
|
|
|
|
set_visited_bwd(t, next_idx, uh);
|
|
|
|
|
t.bwd_queue.push(QueuedWire(next_idx, next_score, t.rng.rng()));
|
|
|
|
|
}
|
|
|
|
@ -911,7 +1264,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) {
|
|
|
|
@ -922,7 +1275,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) {
|
|
|
|
@ -1422,7 +1775,7 @@ struct Router2
|
|
|
|
|
int tmgfail = 0;
|
|
|
|
|
if (timing_driven)
|
|
|
|
|
tmg.run(false);
|
|
|
|
|
if (timing_driven_ripup && iter < 500) {
|
|
|
|
|
if (timing_driven_ripup && iter < 500 && overused_wires == 0) {
|
|
|
|
|
for (size_t i = 0; i < nets_by_udata.size(); i++) {
|
|
|
|
|
NetInfo *ni = nets_by_udata.at(i);
|
|
|
|
|
for (auto usr : ni->users.enumerate()) {
|
|
|
|
@ -1433,6 +1786,12 @@ struct Router2
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (overused_wires > 0) {
|
|
|
|
|
failed_nets.clear();
|
|
|
|
|
for (size_t i = 0; i < nets_by_udata.size(); i++) {
|
|
|
|
|
failed_nets.insert(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (overused_wires == 0 && tmgfail == 0) {
|
|
|
|
|
// Try and actually bind nextpnr Arch API wires
|
|
|
|
|
bind_and_check_all();
|
|
|
|
@ -1450,6 +1809,24 @@ struct Router2
|
|
|
|
|
++iter;
|
|
|
|
|
if (curr_cong_weight < 1e9)
|
|
|
|
|
curr_cong_weight += cfg.curr_cong_mult;
|
|
|
|
|
|
|
|
|
|
if (cfg.perf_profile) {
|
|
|
|
|
std::vector<std::pair<int, IdString>> nets_by_runtime;
|
|
|
|
|
for (auto &n : nets_by_udata) {
|
|
|
|
|
nets_by_runtime.emplace_back(nets.at(n->udata).total_route_us, n->name);
|
|
|
|
|
}
|
|
|
|
|
std::sort(nets_by_runtime.begin(), nets_by_runtime.end(), std::greater<std::pair<int, IdString>>());
|
|
|
|
|
log_info("100 slowest nets by runtime:\n");
|
|
|
|
|
for (int i = 0; i < std::min(int(nets_by_runtime.size()), 100); i++) {
|
|
|
|
|
if (nets_by_runtime.at(i).first < 1000000.0)
|
|
|
|
|
break;
|
|
|
|
|
log(" %80s %6d %.1fms\n", nets_by_runtime.at(i).second.c_str(ctx),
|
|
|
|
|
int(ctx->nets.at(nets_by_runtime.at(i).second)->users.entries()),
|
|
|
|
|
nets_by_runtime.at(i).first / 1000.0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmg.print_fmax();
|
|
|
|
|
} while (!failed_nets.empty());
|
|
|
|
|
if (cfg.perf_profile) {
|
|
|
|
|
std::vector<std::pair<int, IdString>> nets_by_runtime;
|
|
|
|
@ -1495,7 +1872,7 @@ Router2Cfg::Router2Cfg(Context *ctx)
|
|
|
|
|
hist_cong_weight = ctx->setting<float>("router2/histCongWeight", 1.0f);
|
|
|
|
|
curr_cong_mult = ctx->setting<float>("router2/currCongWeightMult", 2.0f);
|
|
|
|
|
estimate_weight = ctx->setting<float>("router2/estimateWeight", 1.25f);
|
|
|
|
|
perf_profile = ctx->setting<bool>("router2/perfProfile", false);
|
|
|
|
|
perf_profile = ctx->setting<bool>("router2/perfProfile", true);
|
|
|
|
|
if (ctx->settings.count(ctx->id("router2/heatmap")))
|
|
|
|
|
heatmap = ctx->settings.at(ctx->id("router2/heatmap")).as_string();
|
|
|
|
|
else
|
|
|
|
|