From e91241f10d68fcaaf0a81fa77e9a91666120ccee Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 4 Sep 2018 17:55:43 +0200 Subject: [PATCH 01/29] Dispose of far too long routes earlier (use 3x est. delay as limit) Signed-off-by: Clifford Wolf --- common/router1.cc | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 5cd4414c..e47a9ae3 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -53,14 +53,15 @@ struct QueuedWire WireId wire; PipId pip; - delay_t delay = 0, togo = 0; + delay_t delay = 0, penalty = 0, togo = 0; int randtag = 0; struct Greater { bool operator()(const QueuedWire &lhs, const QueuedWire &rhs) const noexcept { - delay_t l = lhs.delay + lhs.togo, r = rhs.delay + rhs.togo; + delay_t l = lhs.delay + lhs.penalty + lhs.togo; + delay_t r = rhs.delay + rhs.penalty + rhs.togo; return l == r ? lhs.randtag > rhs.randtag : l > r; } }; @@ -119,7 +120,7 @@ struct Router delay_t maxDelay = 0.0; WireId failedDest; - void route(const std::unordered_map &src_wires, WireId dst_wire) + void route(const std::unordered_map &src_wires, WireId dst_wire, delay_t max_delay) { std::priority_queue, QueuedWire::Greater> queue; @@ -129,7 +130,8 @@ struct Router QueuedWire qw; qw.wire = it.first; qw.pip = PipId(); - qw.delay = it.second - (it.second / 16); + qw.delay = it.second; + qw.penalty = -(it.second / 16); if (cfg.useEstimate) qw.togo = ctx->estimateDelay(qw.wire, dst_wire); qw.randtag = ctx->rng(); @@ -150,12 +152,16 @@ struct Router for (auto pip : ctx->getPipsDownhill(qw.wire)) { delay_t next_delay = qw.delay + ctx->getPipDelay(pip).maxDelay(); + delay_t next_penalty = qw.penalty; WireId next_wire = ctx->getPipDstWire(pip); bool foundRipupNet = false; thisVisitCnt++; next_delay += ctx->getWireDelay(next_wire).maxDelay(); + if (max_delay > 0 && next_delay > max_delay) + continue; + if (!ctx->checkWireAvail(next_wire)) { if (!ripup) continue; @@ -165,11 +171,11 @@ struct Router auto it1 = scores.wireScores.find(next_wire); if (it1 != scores.wireScores.end()) - next_delay += (it1->second * ripup_penalty) / 8; + next_penalty += (it1->second * ripup_penalty) / 8; auto it2 = scores.netWireScores.find(std::make_pair(ripupWireNet->name, next_wire)); if (it2 != scores.netWireScores.end()) - next_delay += it2->second * ripup_penalty; + next_penalty += it2->second * ripup_penalty; foundRipupNet = true; } @@ -183,22 +189,23 @@ struct Router auto it1 = scores.pipScores.find(pip); if (it1 != scores.pipScores.end()) - next_delay += (it1->second * ripup_penalty) / 8; + next_penalty += (it1->second * ripup_penalty) / 8; auto it2 = scores.netPipScores.find(std::make_pair(ripupPipNet->name, pip)); if (it2 != scores.netPipScores.end()) - next_delay += it2->second * ripup_penalty; + next_penalty += it2->second * ripup_penalty; foundRipupNet = true; } if (foundRipupNet) - next_delay += ripup_penalty; + next_penalty += ripup_penalty; NPNR_ASSERT(next_delay >= 0); + NPNR_ASSERT(next_delay + next_penalty >= 0); if (visited.count(next_wire)) { - if (visited.at(next_wire).delay <= next_delay + ctx->getDelayEpsilon()) + if (visited.at(next_wire).delay + visited.at(next_wire).penalty <= next_delay + next_penalty + ctx->getDelayEpsilon()) continue; #if 0 // FIXME if (ctx->debug) @@ -217,6 +224,7 @@ struct Router next_qw.wire = next_wire; next_qw.pip = pip; next_qw.delay = next_delay; + next_qw.penalty = next_penalty; if (cfg.useEstimate) next_qw.togo = ctx->estimateDelay(next_wire, dst_wire); next_qw.randtag = ctx->rng(); @@ -235,7 +243,7 @@ struct Router { std::unordered_map src_wires; src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay(); - route(src_wires, dst_wire); + route(src_wires, dst_wire, 0); routedOkay = visited.count(dst_wire); if (ctx->debug) { @@ -369,7 +377,8 @@ struct Router log(" Path delay estimate: %.2f\n", float(ctx->estimateDelay(src_wire, dst_wire))); } - route(src_wires, dst_wire); + delay_t max_delay = 3 * ctx->estimateDelay(src_wire, dst_wire); + route(src_wires, dst_wire, max_delay); if (visited.count(dst_wire) == 0) { if (ctx->debug) From aeaa0552ba0373fb1edaed263b6edb4e8e82d7ea Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 9 Nov 2018 17:00:45 +0100 Subject: [PATCH 02/29] Essentially a rewrite router1 Signed-off-by: Clifford Wolf --- common/router1.cc | 1174 ++++++++++++++------------------------------- common/router1.h | 5 + 2 files changed, 369 insertions(+), 810 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 34554711..c4d4a130 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -28,24 +28,38 @@ namespace { USING_NEXTPNR_NAMESPACE -struct hash_id_wire +struct arc_key { - std::size_t operator()(const std::pair &arg) const noexcept - { - std::size_t seed = std::hash()(arg.first); - seed ^= std::hash()(arg.second) + 0x9e3779b9 + (seed << 6) + (seed >> 2); - return seed; + NetInfo *net_info; + int user_idx; + + bool operator==(const arc_key &other) const { + return (net_info == other.net_info) && (user_idx == other.user_idx); } + + struct Hash + { + std::size_t operator()(const arc_key &arg) const noexcept + { + std::size_t seed = std::hash()(arg.net_info); + seed ^= std::hash()(arg.user_idx) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } + }; }; -struct hash_id_pip +struct arc_entry { - std::size_t operator()(const std::pair &arg) const noexcept + arc_key arc; + delay_t pri; + + struct Greater { - std::size_t seed = std::hash()(arg.first); - seed ^= std::hash()(arg.second) + 0x9e3779b9 + (seed << 6) + (seed >> 2); - return seed; - } + bool operator()(const arc_entry &lhs, const arc_entry &rhs) const noexcept + { + return lhs.pri > rhs.pri; + } + }; }; struct QueuedWire @@ -67,143 +81,294 @@ struct QueuedWire }; }; -struct RipupScoreboard -{ - std::unordered_map wireScores; - std::unordered_map pipScores; - std::unordered_map, int, hash_id_wire> netWireScores; - std::unordered_map, int, hash_id_pip> netPipScores; -}; - -void ripup_net(Context *ctx, IdString net_name) -{ - if (ctx->debug) - log("Ripping up all routing for net %s.\n", net_name.c_str(ctx)); - - auto net_info = ctx->nets.at(net_name).get(); - std::vector pips; - std::vector wires; - - pips.reserve(net_info->wires.size()); - wires.reserve(net_info->wires.size()); - - for (auto &it : net_info->wires) { - if (it.second.pip != PipId()) - pips.push_back(it.second.pip); - else - wires.push_back(it.first); - } - - for (auto pip : pips) - ctx->unbindPip(pip); - - for (auto wire : wires) - ctx->unbindWire(wire); - - NPNR_ASSERT(net_info->wires.empty()); -} - -struct Router +struct Router1 { Context *ctx; const Router1Cfg &cfg; - RipupScoreboard scores; - IdString net_name; - bool ripup; - delay_t ripup_penalty; + std::priority_queue, arc_entry::Greater> arc_queue; + std::unordered_map> wire_to_arc; + std::unordered_set queued_arcs; - std::unordered_set rippedNets; std::unordered_map visited; - int visitCnt = 0, revisitCnt = 0, overtimeRevisitCnt = 0; - bool routedOkay = false; - delay_t maxDelay = 0.0; - WireId failedDest; + std::priority_queue, QueuedWire::Greater> queue; - void route(const std::unordered_map &src_wires, WireId dst_wire, delay_t max_delay) + std::unordered_map wireScores; + std::unordered_map pipScores; + + int arcs_with_ripup = 0; + int arcs_without_ripup = 0; + bool ripup_flag; + + Router1(Context *ctx, const Router1Cfg &cfg) : ctx(ctx), cfg(cfg) { } + + void arc_queue_insert(const arc_key &arc, WireId src_wire, WireId dst_wire) { - std::priority_queue, QueuedWire::Greater> queue; + if (queued_arcs.count(arc)) + return; + delay_t pri = ctx->estimateDelay(src_wire, dst_wire) - arc.net_info->users[arc.user_idx].budget; + + arc_entry entry; + entry.arc = arc; + entry.pri = pri; + + arc_queue.push(entry); + queued_arcs.insert(arc); + } + + void arc_queue_insert(const arc_key &arc) + { + NetInfo *net_info = arc.net_info; + int user_idx = arc.user_idx; + + auto src_wire = ctx->getNetinfoSourceWire(net_info); + auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]); + + arc_queue_insert(arc, src_wire, dst_wire); + } + + void ripup_net(NetInfo *net) + { + if (ctx->debug) + log(" ripup net %s\n", net->name.c_str(ctx)); + + auto net_wires_copy = net->wires; + for (auto &it : net_wires_copy) { + if (it.second.pip == PipId()) + ripup_wire(it.first); + else + ripup_pip(it.second.pip); + } + + ripup_flag = true; + } + + void ripup_wire(WireId wire) + { + if (ctx->debug) + log(" ripup wire %s\n", ctx->getWireName(wire).c_str(ctx)); + + wireScores[wire]++; + + if (ctx->getBoundWireNet(wire)) { + for (auto &it : wire_to_arc[wire]) + arc_queue_insert(it); + wire_to_arc[wire].clear(); + ctx->unbindWire(wire); + } + + NetInfo *net = ctx->getConflictingWireNet(wire); + if (net != nullptr) { + wireScores[wire] += net->wires.size(); + ripup_net(net); + } + + ripup_flag = true; + } + + void ripup_pip(PipId pip) + { + if (ctx->debug) + log(" ripup pip %s\n", ctx->getPipName(pip).c_str(ctx)); + + WireId wire = ctx->getPipDstWire(pip); + wireScores[wire]++; + pipScores[pip]++; + + if (ctx->getBoundPipNet(pip)) { + for (auto &it : wire_to_arc[wire]) + arc_queue_insert(it); + wire_to_arc[wire].clear(); + ctx->unbindPip(pip); + } + + NetInfo *net = ctx->getConflictingPipNet(pip); + if (net != nullptr) { + wireScores[wire] += net->wires.size(); + pipScores[pip] += net->wires.size(); + ripup_net(net); + } + + ripup_flag = true; + } + + void setup() + { + for (auto &net_it : ctx->nets) + { + NetInfo *net_info = net_it.second.get(); + +#ifdef ARCH_ECP5 + // ECP5 global nets currently appear part-unrouted due to arch database limitations + // Don't touch them in the router + if (net_info->is_global) + return; +#endif + if (net_info->driver.cell == nullptr) + return; + + 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)); + + 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)); + + arc_key arc; + arc.net_info = net_info; + arc.user_idx = user_idx; + + WireId cursor = dst_wire; + wire_to_arc[cursor].insert(arc); + + while (src_wire != cursor) { + auto it = net_info->wires.find(cursor); + if (it == net_info->wires.end()) { + arc_queue_insert(arc, src_wire, dst_wire); + break; + } + + NPNR_ASSERT(it->second.pip != PipId()); + cursor = ctx->getPipSrcWire(it->second.pip); + wire_to_arc[cursor].insert(arc); + } + } + + std::vector unbind_wires; + + for (auto &it : net_info->wires) + if (it.second.strength < STRENGTH_LOCKED && wire_to_arc.count(it.first) == 0) + unbind_wires.push_back(it.first); + + for (auto it : unbind_wires) + ctx->unbindWire(it); + } + } + + arc_key arc_queue_pop() + { + arc_entry entry = arc_queue.top(); + arc_queue.pop(); + queued_arcs.erase(entry.arc); + return entry.arc; + } + + bool route_arc(const arc_key &arc, bool ripup) + { + + NetInfo *net_info = arc.net_info; + int user_idx = arc.user_idx; + + auto src_wire = ctx->getNetinfoSourceWire(net_info); + auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]); + 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)); + } + + // unbind wires that are currently used exclusively by this arc + + std::vector unbind_wires; + + for (auto &wire_it : net_info->wires) { + auto wire = wire_it.first; + auto wire_to_arc_it = wire_to_arc.find(wire); + NPNR_ASSERT(wire_to_arc_it != wire_to_arc.end()); + + wire_to_arc_it->second.erase(arc); + if (wire_to_arc_it->second.empty()) + unbind_wires.push_back(wire); + } + + for (auto it : unbind_wires) + ctx->unbindWire(it); + + // reset wire queue + + if (!queue.empty()) { + std::priority_queue, QueuedWire::Greater> new_queue; + queue.swap(new_queue); + } visited.clear(); - for (auto &it : src_wires) { + int visitCnt = 0; + int maxVisitCnt = INT_MAX; + delay_t best_est = 0; + + { QueuedWire qw; - qw.wire = it.first; + qw.wire = src_wire; qw.pip = PipId(); - qw.delay = it.second; - qw.penalty = -(it.second / 16); - if (cfg.useEstimate) + qw.delay = ctx->getWireDelay(qw.wire).maxDelay(); + qw.penalty = 0; + if (cfg.useEstimate) { qw.togo = ctx->estimateDelay(qw.wire, dst_wire); + best_est = qw.delay + qw.togo; + } qw.randtag = ctx->rng(); queue.push(qw); visited[qw.wire] = qw; } - int thisVisitCnt = 0; - int thisVisitCntLimit = 0; - - while (!queue.empty() && (thisVisitCntLimit == 0 || thisVisitCnt < thisVisitCntLimit)) { + while (visitCnt++ < maxVisitCnt && !queue.empty()) + { QueuedWire qw = queue.top(); queue.pop(); - if (thisVisitCntLimit == 0 && visited.count(dst_wire)) - thisVisitCntLimit = (thisVisitCnt * 3) / 2; - for (auto pip : ctx->getPipsDownhill(qw.wire)) { delay_t next_delay = qw.delay + ctx->getPipDelay(pip).maxDelay(); delay_t next_penalty = qw.penalty; - WireId next_wire = ctx->getPipDstWire(pip); - bool foundRipupNet = false; - thisVisitCnt++; + WireId next_wire = ctx->getPipDstWire(pip); next_delay += ctx->getWireDelay(next_wire).maxDelay(); - if (max_delay > 0 && next_delay > max_delay) - continue; - if (!ctx->checkWireAvail(next_wire)) { - if (!ripup) - continue; NetInfo *ripupWireNet = ctx->getConflictingWireNet(next_wire); - if (ripupWireNet == nullptr || ripupWireNet->name == net_name) + + if (ripupWireNet == nullptr) continue; - auto it1 = scores.wireScores.find(next_wire); - if (it1 != scores.wireScores.end()) - next_penalty += (it1->second * ripup_penalty) / 8; + if (ripupWireNet == net_info) { + next_penalty += cfg.wireReusePenalty; + } else { + if (!ripup) + continue; - auto it2 = scores.netWireScores.find(std::make_pair(ripupWireNet->name, next_wire)); - if (it2 != scores.netWireScores.end()) - next_penalty += it2->second * ripup_penalty; - - foundRipupNet = true; + auto scores_it = wireScores.find(next_wire); + if (scores_it != wireScores.end()) + next_penalty += scores_it->second * cfg.wireRipupPenalty; + } } if (!ctx->checkPipAvail(pip)) { - if (!ripup) - continue; NetInfo *ripupPipNet = ctx->getConflictingPipNet(pip); - if (ripupPipNet == nullptr || ripupPipNet->name == net_name) + + if (ripupPipNet == nullptr) continue; - auto it1 = scores.pipScores.find(pip); - if (it1 != scores.pipScores.end()) - next_penalty += (it1->second * ripup_penalty) / 8; + if (ripupPipNet == net_info) { + next_penalty += cfg.pipReusePenalty; + } else { + if (!ripup) + continue; - auto it2 = scores.netPipScores.find(std::make_pair(ripupPipNet->name, pip)); - if (it2 != scores.netPipScores.end()) - next_penalty += it2->second * ripup_penalty; - - foundRipupNet = true; + auto scores_it = pipScores.find(pip); + if (scores_it != pipScores.end()) + next_penalty += scores_it->second * cfg.pipRipupPenalty; + } } - if (foundRipupNet) - next_penalty += ripup_penalty; - - NPNR_ASSERT(next_delay >= 0); - NPNR_ASSERT(next_delay + next_penalty >= 0); - if (visited.count(next_wire)) { if (visited.at(next_wire).delay + visited.at(next_wire).penalty <= next_delay + next_penalty + ctx->getDelayEpsilon()) continue; @@ -214,10 +379,6 @@ struct Router ctx->getDelayNS(visited.at(next_wire).delay), ctx->getDelayNS(next_delay)); #endif - if (thisVisitCntLimit == 0) - revisitCnt++; - else - overtimeRevisitCnt++; } QueuedWire next_qw; @@ -225,473 +386,83 @@ struct Router next_qw.pip = pip; next_qw.delay = next_delay; next_qw.penalty = next_penalty; - if (cfg.useEstimate) + 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 > best_est + cfg.estimatePrecision) + continue; + if (best_est > this_est) + best_est = this_est; + } next_qw.randtag = ctx->rng(); visited[next_qw.wire] = next_qw; queue.push(next_qw); + + if (maxVisitCnt == INT_MAX && next_wire == dst_wire) + maxVisitCnt = 2*visitCnt; } } - visitCnt += thisVisitCnt; - } - - Router(Context *ctx, const Router1Cfg &cfg, RipupScoreboard &scores, WireId src_wire, WireId dst_wire, - bool ripup = false, delay_t ripup_penalty = 0) - : ctx(ctx), cfg(cfg), scores(scores), ripup(ripup), ripup_penalty(ripup_penalty) - { - std::unordered_map src_wires; - src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay(); - route(src_wires, dst_wire, 0); - routedOkay = visited.count(dst_wire); - - if (ctx->debug) { - log("Route (from destination to source):\n"); - - WireId cursor = dst_wire; - - while (1) { - log(" %8.3f %s\n", ctx->getDelayNS(visited[cursor].delay), ctx->getWireName(cursor).c_str(ctx)); - - if (cursor == src_wire) - break; - - cursor = ctx->getPipSrcWire(visited[cursor].pip); - } - } - } - - Router(Context *ctx, const Router1Cfg &cfg, RipupScoreboard &scores, IdString net_name, int user_idx = -1, - bool reroute = false, bool ripup = false, delay_t ripup_penalty = 0) - : ctx(ctx), cfg(cfg), scores(scores), net_name(net_name), ripup(ripup), ripup_penalty(ripup_penalty) - { - auto net_info = ctx->nets.at(net_name).get(); - if (ctx->debug) - log("Routing net %s.\n", net_name.c_str(ctx)); - - if (ctx->debug) - log(" Source: %s.%s.\n", net_info->driver.cell->name.c_str(ctx), net_info->driver.port.c_str(ctx)); - - 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)); - - if (ctx->debug) - log(" Source wire: %s\n", ctx->getWireName(src_wire).c_str(ctx)); - - std::unordered_map src_wires; - std::vector> users_array; - - if (user_idx < 0) { - // route all users, from worst to best slack - 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]); - delay_t slack = net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire); - users_array.push_back(std::pair(slack, user_idx)); - } - std::sort(users_array.begin(), users_array.end()); - } else { - // route only the selected user - users_array.push_back(std::pair(delay_t(), user_idx)); - } - - if (reroute) { - // complete ripup - ripup_net(ctx, net_name); - ctx->bindWire(src_wire, ctx->nets.at(net_name).get(), STRENGTH_WEAK); - src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay(); - } else { - // re-use existing routes as much as possible - if (net_info->wires.count(src_wire) == 0) - ctx->bindWire(src_wire, ctx->nets.at(net_name).get(), STRENGTH_WEAK); - src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay(); - - 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)); - - std::function register_existing_path = - [ctx, net_info, &src_wires, ®ister_existing_path](WireId wire) -> delay_t { - auto it = src_wires.find(wire); - if (it != src_wires.end()) - return it->second; - - PipId pip = net_info->wires.at(wire).pip; - delay_t delay = register_existing_path(ctx->getPipSrcWire(pip)); - delay += ctx->getPipDelay(pip).maxDelay(); - delay += ctx->getWireDelay(wire).maxDelay(); - src_wires[wire] = delay; - - return delay; - }; - - WireId cursor = dst_wire; - while (src_wires.count(cursor) == 0) { - auto it = net_info->wires.find(cursor); - if (it == net_info->wires.end()) - goto check_next_user_for_existing_path; - NPNR_ASSERT(it->second.pip != PipId()); - cursor = ctx->getPipSrcWire(it->second.pip); - } - - register_existing_path(dst_wire); - check_next_user_for_existing_path:; - } - - std::vector ripup_wires; - for (auto &it : net_info->wires) - if (src_wires.count(it.first) == 0) - ripup_wires.push_back(it.first); - - for (auto &it : ripup_wires) { - if (ctx->debug) - log(" Unbind dangling wire for net %s: %s\n", net_name.c_str(ctx), - ctx->getWireName(it).c_str(ctx)); - ctx->unbindWire(it); - } - } - - for (auto user_idx_it : users_array) { - int user_idx = user_idx_it.second; + log(" total number of visited nodes: %d\n", visitCnt); + if (visited.count(dst_wire) == 0) { if (ctx->debug) - log(" Route to: %s.%s.\n", net_info->users[user_idx].cell->name.c_str(ctx), - net_info->users[user_idx].port.c_str(ctx)); - - 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)); - - if (ctx->debug) { - log(" Destination wire: %s\n", ctx->getWireName(dst_wire).c_str(ctx)); - log(" Path delay estimate: %.2f\n", float(ctx->estimateDelay(src_wire, dst_wire))); - } - - delay_t max_delay = 3 * ctx->estimateDelay(src_wire, dst_wire); - route(src_wires, dst_wire, max_delay); - - if (visited.count(dst_wire) == 0) { - if (ctx->debug) - log("Failed to route %s -> %s.\n", ctx->getWireName(src_wire).c_str(ctx), - ctx->getWireName(dst_wire).c_str(ctx)); - else if (ripup) - log_info("Failed to route %s -> %s.\n", ctx->getWireName(src_wire).c_str(ctx), - ctx->getWireName(dst_wire).c_str(ctx)); - ripup_net(ctx, net_name); - failedDest = dst_wire; - return; - } - - if (ctx->debug) - log(" Final path delay: %.3f\n", ctx->getDelayNS(visited[dst_wire].delay)); - maxDelay = fmaxf(maxDelay, visited[dst_wire].delay); - - if (ctx->debug) - log(" Route (from destination to source):\n"); - - WireId cursor = dst_wire; - - while (1) { - if (ctx->debug) - log(" %8.3f %s\n", ctx->getDelayNS(visited[cursor].delay), ctx->getWireName(cursor).c_str(ctx)); - - if (src_wires.count(cursor)) - break; - - NetInfo *conflicting_wire_net = ctx->getConflictingWireNet(cursor); - - if (conflicting_wire_net != nullptr) { - NPNR_ASSERT(ripup); - NPNR_ASSERT(conflicting_wire_net->name != net_name); - - ctx->unbindWire(cursor); - if (!ctx->checkWireAvail(cursor)) - ripup_net(ctx, conflicting_wire_net->name); - - rippedNets.insert(conflicting_wire_net->name); - scores.wireScores[cursor]++; - scores.netWireScores[std::make_pair(net_name, cursor)]++; - scores.netWireScores[std::make_pair(conflicting_wire_net->name, cursor)]++; - } - - PipId pip = visited[cursor].pip; - NetInfo *conflicting_pip_net = ctx->getConflictingPipNet(pip); - - if (conflicting_pip_net != nullptr) { - NPNR_ASSERT(ripup); - NPNR_ASSERT(conflicting_pip_net->name != net_name); - - if (ctx->getBoundPipNet(pip) == conflicting_pip_net) - ctx->unbindPip(pip); - - if (!ctx->checkPipAvail(pip)) - ripup_net(ctx, conflicting_pip_net->name); - - rippedNets.insert(conflicting_pip_net->name); - scores.pipScores[visited[cursor].pip]++; - scores.netPipScores[std::make_pair(net_name, visited[cursor].pip)]++; - scores.netPipScores[std::make_pair(conflicting_pip_net->name, visited[cursor].pip)]++; - } - - ctx->bindPip(visited[cursor].pip, ctx->nets.at(net_name).get(), STRENGTH_WEAK); - src_wires[cursor] = visited[cursor].delay; - cursor = ctx->getPipSrcWire(visited[cursor].pip); - } + log(" no route found for this arc\n"); + return false; } - routedOkay = true; - } -}; + WireId cursor = dst_wire; + while (1) { + if (ctx->debug) + log(" node %s\n", ctx->getWireName(cursor).c_str(ctx)); -struct RouteJob -{ - IdString net; - int user_idx = -1; - delay_t slack = 0; - int randtag = 0; + if (!ctx->checkWireAvail(cursor)) { + NetInfo *ripupWireNet = ctx->getConflictingWireNet(cursor); + NPNR_ASSERT(ripupWireNet != nullptr); - struct Greater - { - bool operator()(const RouteJob &lhs, const RouteJob &rhs) const noexcept - { - return lhs.slack == rhs.slack ? lhs.randtag > rhs.randtag : lhs.slack > rhs.slack; + if (ripupWireNet != net_info) + ripup_wire(cursor); + } + + auto pip = visited[cursor].pip; + + if (pip == PipId()) { + NPNR_ASSERT(cursor == src_wire); + } else { + if (!ctx->checkPipAvail(pip)) { + NetInfo *ripupPipNet = ctx->getConflictingPipNet(pip); + NPNR_ASSERT(ripupPipNet != nullptr); + + if (ripupPipNet != net_info) + ripup_pip(pip); + } + } + + if (ctx->checkWireAvail(cursor)) { + if (pip == PipId()) + ctx->bindWire(cursor, net_info, STRENGTH_WEAK); + else if (ctx->checkPipAvail(pip)) + ctx->bindPip(pip, net_info, STRENGTH_WEAK); + } + + wire_to_arc[cursor].insert(arc); + + if (pip == PipId()) + break; + + cursor = ctx->getPipSrcWire(pip); } - }; -}; -void addFullNetRouteJob(Context *ctx, const Router1Cfg &cfg, IdString net_name, - std::unordered_map> &cache, - std::priority_queue, RouteJob::Greater> &queue) -{ - NetInfo *net_info = ctx->nets.at(net_name).get(); - - if (net_info->driver.cell == nullptr) - return; - - 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)); - - auto &net_cache = cache[net_name]; - - if (net_cache.empty()) - net_cache.resize(net_info->users.size()); - - RouteJob job; - job.net = net_name; - job.user_idx = -1; - job.slack = 0; - job.randtag = ctx->rng(); - - bool got_slack = false; - - for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { - if (net_cache[user_idx]) - continue; - - 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)); - - if (user_idx == 0) - job.slack = net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire); + if (ripup_flag) + arcs_with_ripup++; else - job.slack = std::min(job.slack, net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire)); + arcs_without_ripup++; - WireId cursor = dst_wire; - while (src_wire != cursor) { - auto it = net_info->wires.find(cursor); - if (it == net_info->wires.end()) { - if (!got_slack) - job.slack = net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire); - else - job.slack = std::min(job.slack, - net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire)); - got_slack = true; - break; - } - NPNR_ASSERT(it->second.pip != PipId()); - cursor = ctx->getPipSrcWire(it->second.pip); - } + return true; } - - queue.push(job); - - for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) - net_cache[user_idx] = true; -} - -void addNetRouteJobs(Context *ctx, const Router1Cfg &cfg, IdString net_name, - std::unordered_map> &cache, - std::priority_queue, RouteJob::Greater> &queue) -{ - NetInfo *net_info = ctx->nets.at(net_name).get(); - -#ifdef ARCH_ECP5 - // ECP5 global nets currently appear part-unrouted due to arch database limitations - // Don't touch them in the router - if (net_info->is_global) - return; -#endif - if (net_info->driver.cell == nullptr) - return; - - 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)); - - auto &net_cache = cache[net_name]; - - if (net_cache.empty()) - net_cache.resize(net_info->users.size()); - - for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { - if (net_cache[user_idx]) - continue; - - 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)); - - WireId cursor = dst_wire; - while (src_wire != cursor) { - auto it = net_info->wires.find(cursor); - if (it == net_info->wires.end()) { - RouteJob job; - job.net = net_name; - job.user_idx = user_idx; - job.slack = net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire); - job.randtag = ctx->rng(); - queue.push(job); - net_cache[user_idx] = true; - break; - } - NPNR_ASSERT(it->second.pip != PipId()); - cursor = ctx->getPipSrcWire(it->second.pip); - } - } -} - -void cleanupReroute(Context *ctx, const Router1Cfg &cfg, RipupScoreboard &scores, - std::unordered_set &cleanupQueue, - std::priority_queue, RouteJob::Greater> &jobQueue, - int &totalVisitCnt, int &totalRevisitCnt, int &totalOvertimeRevisitCnt) -{ - std::priority_queue, RouteJob::Greater> cleanupJobs; - std::vector allNetinfos; - - for (auto net_name : cleanupQueue) { - NetInfo *net_info = ctx->nets.at(net_name).get(); - auto src_wire = ctx->getNetinfoSourceWire(net_info); - - if (ctx->verbose) - allNetinfos.push_back(net_info); - - std::unordered_map useCounters; - std::vector candidateArcs; - - 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 == src_wire) - continue; - - auto cursor = dst_wire; - useCounters[cursor]++; - - while (cursor != src_wire) { - auto it = net_info->wires.find(cursor); - if (it == net_info->wires.end()) - break; - cursor = ctx->getPipSrcWire(it->second.pip); - useCounters[cursor]++; - } - - if (cursor != src_wire) - continue; - - candidateArcs.push_back(user_idx); - } - - for (int user_idx : candidateArcs) { - auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]); - - if (useCounters.at(dst_wire) != 1) - continue; - - RouteJob job; - job.net = net_name; - job.user_idx = user_idx; - job.slack = net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire); - job.randtag = ctx->rng(); - cleanupJobs.push(job); - } - } - - log_info("running cleanup re-route of %d nets (%d arcs).\n", int(cleanupQueue.size()), int(cleanupJobs.size())); - - cleanupQueue.clear(); - - int visitCnt = 0, revisitCnt = 0, overtimeRevisitCnt = 0; - int totalWireCountDelta = 0; - - if (ctx->verbose) { - for (auto it : allNetinfos) - totalWireCountDelta -= it->wires.size(); - } - - while (!cleanupJobs.empty()) { - RouteJob job = cleanupJobs.top(); - cleanupJobs.pop(); - - auto net_name = job.net; - auto user_idx = job.user_idx; - - NetInfo *net_info = ctx->nets.at(net_name).get(); - auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]); - - ctx->unbindWire(dst_wire); - - Router router(ctx, cfg, scores, net_name, user_idx, false, false); - - if (!router.routedOkay) - log_error("Failed to re-route arc %d of net %s.\n", user_idx, net_name.c_str(ctx)); - - visitCnt += router.visitCnt; - revisitCnt += router.revisitCnt; - overtimeRevisitCnt += router.overtimeRevisitCnt; - } - - if (ctx->verbose) { - for (auto it : allNetinfos) - totalWireCountDelta += it->wires.size(); - - log_info(" visited %d PIPs (%.2f%% revisits, %.2f%% overtime), %+d wires.\n", visitCnt, - (100.0 * revisitCnt) / visitCnt, (100.0 * overtimeRevisitCnt) / visitCnt, totalWireCountDelta); - } - - totalVisitCnt += visitCnt; - totalRevisitCnt += revisitCnt; - totalOvertimeRevisitCnt += overtimeRevisitCnt; -} +}; } // namespace @@ -703,258 +474,63 @@ Router1Cfg::Router1Cfg(Context *ctx) : Settings(ctx) cleanupReroute = get("router1/cleanupReroute", true); fullCleanupReroute = get("router1/fullCleanupReroute", true); useEstimate = get("router1/useEstimate", true); + + wireRipupPenalty = ctx->getRipupDelayPenalty(); + pipRipupPenalty = ctx->getRipupDelayPenalty(); + + wireReusePenalty = -wireRipupPenalty/8; + pipReusePenalty = -pipRipupPenalty/8; + + estimatePrecision = 100 * ctx->getRipupDelayPenalty(); } bool router1(Context *ctx, const Router1Cfg &cfg) { try { - int totalVisitCnt = 0, totalRevisitCnt = 0, totalOvertimeRevisitCnt = 0; - delay_t ripup_penalty = ctx->getRipupDelayPenalty(); - RipupScoreboard scores; - log_break(); log_info("Routing..\n"); ctx->lock(); - std::unordered_set cleanupQueue; - std::unordered_map> jobCache; - std::priority_queue, RouteJob::Greater> jobQueue; + log_info("Setting up routing queue.\n"); - for (auto &net_it : ctx->nets) - addNetRouteJobs(ctx, cfg, net_it.first, jobCache, jobQueue); + Router1 router(ctx, cfg); + router.setup(); - if (jobQueue.empty()) { - log_info("found no unrouted source-sink pairs. no routing necessary.\n"); - ctx->unlock(); - return true; - } + log_info("Added %d arcs to routing queue.\n", int(router.arc_queue.size())); - log_info("found %d unrouted source-sink pairs. starting routing procedure.\n", int(jobQueue.size())); + int iter_cnt = 0; + int last_arcs_with_ripup = 0; + int last_arcs_without_ripup = 0; - int iterCnt = 0; + while (!router.arc_queue.empty()) { + if (++iter_cnt % 1000 == 0) { + log_info("At iteration %d:\n", iter_cnt); + log_info(" routed %d (%d) arcs with rip-up.\n", router.arcs_with_ripup, router.arcs_with_ripup - last_arcs_with_ripup); + log_info(" routed %d (%d) arcs without rip-up.\n", router.arcs_without_ripup, router.arcs_without_ripup - last_arcs_without_ripup); + log_info(" %d arcs remaining in routing queue.\n", int(router.arc_queue.size())); + last_arcs_with_ripup = router.arcs_with_ripup; + last_arcs_without_ripup = router.arcs_without_ripup; + } - while (!jobQueue.empty()) { - if (iterCnt == cfg.maxIterCnt) { - log_warning("giving up after %d iterations.\n", iterCnt); - log_info("Checksum: 0x%08x\n", ctx->checksum()); + 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)); #ifndef NDEBUG ctx->check(); #endif ctx->unlock(); return false; } - - iterCnt++; - if (ctx->verbose) - log_info("-- %d --\n", iterCnt); - - int visitCnt = 0, revisitCnt = 0, overtimeRevisitCnt = 0, jobCnt = 0, failedCnt = 0; - - std::unordered_set normalRouteNets, ripupQueue; - - if (ctx->verbose || iterCnt == 1) - log_info("routing queue contains %d jobs.\n", int(jobQueue.size())); - else if (ctx->slack_redist_iter > 0 && iterCnt % ctx->slack_redist_iter == 0) - assign_budget(ctx, true /* quiet */); - - bool printNets = ctx->verbose && (jobQueue.size() < 10); - - while (!jobQueue.empty()) { - if (ctx->debug) - log("Next job slack: %f\n", double(jobQueue.top().slack)); - - auto net_name = jobQueue.top().net; - auto user_idx = jobQueue.top().user_idx; - jobQueue.pop(); - - if (cfg.fullCleanupReroute) - cleanupQueue.insert(net_name); - - if (printNets) { - if (user_idx < 0) - log_info(" routing all %d users of net %s\n", int(ctx->nets.at(net_name)->users.size()), - net_name.c_str(ctx)); - else - log_info(" routing user %d of net %s\n", user_idx, net_name.c_str(ctx)); - } - - Router router(ctx, cfg, scores, net_name, user_idx, false, false); - - jobCnt++; - visitCnt += router.visitCnt; - revisitCnt += router.revisitCnt; - overtimeRevisitCnt += router.overtimeRevisitCnt; - - if (!router.routedOkay) { - if (printNets) - log_info(" failed to route to %s.\n", ctx->getWireName(router.failedDest).c_str(ctx)); - ripupQueue.insert(net_name); - failedCnt++; - } else { - normalRouteNets.insert(net_name); - } - - if ((ctx->verbose || iterCnt == 1) && !printNets && (jobCnt % 100 == 0)) { - log_info(" processed %d jobs. (%d routed, %d failed)\n", jobCnt, jobCnt - failedCnt, failedCnt); - ctx->yield(); - } - } - - NPNR_ASSERT(jobQueue.empty()); - jobCache.clear(); - - if ((ctx->verbose || iterCnt == 1) && (jobCnt % 100 != 0)) { - log_info(" processed %d jobs. (%d routed, %d failed)\n", jobCnt, jobCnt - failedCnt, failedCnt); - ctx->yield(); - } - - if (ctx->verbose) - log_info(" visited %d PIPs (%.2f%% revisits, %.2f%% overtime revisits).\n", visitCnt, - (100.0 * revisitCnt) / visitCnt, (100.0 * overtimeRevisitCnt) / visitCnt); - - if (!ripupQueue.empty()) { - if (ctx->verbose || iterCnt == 1) - log_info("failed to route %d nets. re-routing in ripup mode.\n", int(ripupQueue.size())); - - printNets = ctx->verbose && (ripupQueue.size() < 10); - - visitCnt = 0; - revisitCnt = 0; - overtimeRevisitCnt = 0; - int netCnt = 0; - int ripCnt = 0; - - std::vector ripupArray(ripupQueue.begin(), ripupQueue.end()); - ctx->sorted_shuffle(ripupArray); - - for (auto net_name : ripupArray) { - if (cfg.cleanupReroute) - cleanupQueue.insert(net_name); - - if (printNets) - log_info(" routing net %s. (%d users)\n", net_name.c_str(ctx), - int(ctx->nets.at(net_name)->users.size())); - - Router router(ctx, cfg, scores, net_name, -1, false, true, ripup_penalty); - - netCnt++; - visitCnt += router.visitCnt; - revisitCnt += router.revisitCnt; - overtimeRevisitCnt += router.overtimeRevisitCnt; - - if (!router.routedOkay) - log_error("Net %s is impossible to route.\n", net_name.c_str(ctx)); - - for (auto it : router.rippedNets) { - addFullNetRouteJob(ctx, cfg, it, jobCache, jobQueue); - if (cfg.cleanupReroute) - cleanupQueue.insert(it); - } - - if (printNets) { - if (router.rippedNets.size() < 10) { - log_info(" ripped up %d other nets:\n", int(router.rippedNets.size())); - for (auto n : router.rippedNets) - log_info(" %s (%d users)\n", n.c_str(ctx), int(ctx->nets.at(n)->users.size())); - } else { - log_info(" ripped up %d other nets.\n", int(router.rippedNets.size())); - } - } - - ripCnt += router.rippedNets.size(); - - if ((ctx->verbose || iterCnt == 1) && !printNets && (netCnt % 100 == 0)) { - log_info(" routed %d nets, ripped %d nets.\n", netCnt, ripCnt); - ctx->yield(); - } - } - - if ((ctx->verbose || iterCnt == 1) && (netCnt % 100 != 0)) - log_info(" routed %d nets, ripped %d nets.\n", netCnt, ripCnt); - - if (ctx->verbose) - log_info(" visited %d PIPs (%.2f%% revisits, %.2f%% overtime revisits).\n", visitCnt, - (100.0 * revisitCnt) / visitCnt, (100.0 * overtimeRevisitCnt) / visitCnt); - - if (ctx->verbose && !jobQueue.empty()) - log_info(" ripped up %d previously routed nets. continue routing.\n", int(jobQueue.size())); - } - - if (!ctx->verbose) - log_info("iteration %d: routed %d nets without ripup, routed %d nets with ripup.\n", iterCnt, - int(normalRouteNets.size()), int(ripupQueue.size())); - - totalVisitCnt += visitCnt; - totalRevisitCnt += revisitCnt; - totalOvertimeRevisitCnt += overtimeRevisitCnt; - - if (iterCnt == 8 || iterCnt == 16 || iterCnt == 32 || iterCnt == 64 || iterCnt == 128) - ripup_penalty += ctx->getRipupDelayPenalty(); - - if (jobQueue.empty() || (iterCnt % 5) == 0 || (cfg.fullCleanupReroute && iterCnt == 1)) - cleanupReroute(ctx, cfg, scores, cleanupQueue, jobQueue, totalVisitCnt, totalRevisitCnt, - totalOvertimeRevisitCnt); - - ctx->yield(); } - log_info("routing complete after %d iterations.\n", iterCnt); + log_info("At iteration %d:\n", iter_cnt); + log_info(" routed %d (%d) arcs with rip-up.\n", router.arcs_with_ripup, router.arcs_with_ripup - last_arcs_with_ripup); + log_info(" routed %d (%d) arcs without rip-up.\n", router.arcs_without_ripup, router.arcs_without_ripup - last_arcs_without_ripup); + log_info(" %d arcs remaining in routing queue.\n", int(router.arc_queue.size())); - log_info("visited %d PIPs (%.2f%% revisits, %.2f%% overtime revisits).\n", totalVisitCnt, - (100.0 * totalRevisitCnt) / totalVisitCnt, (100.0 * totalOvertimeRevisitCnt) / totalVisitCnt); - - { - float tns = 0; - int tns_net_count = 0; - int tns_arc_count = 0; - for (auto &net_it : ctx->nets) { - bool got_negative_slack = false; - NetInfo *net_info = ctx->nets.at(net_it.first).get(); - for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { - delay_t arc_delay = ctx->getNetinfoRouteDelay(net_info, net_info->users[user_idx]); - delay_t arc_budget = net_info->users[user_idx].budget; - delay_t arc_slack = arc_budget - arc_delay; - if (arc_slack < 0) { - if (!got_negative_slack) { - if (ctx->verbose) - log_info("net %s has negative slack arcs:\n", net_info->name.c_str(ctx)); - tns_net_count++; - } - if (ctx->verbose) - log_info(" arc %s -> %s has %f ns slack (delay %f, budget %f)\n", - ctx->getWireName(ctx->getNetinfoSourceWire(net_info)).c_str(ctx), - ctx->getWireName(ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx])) - .c_str(ctx), - ctx->getDelayNS(arc_slack), ctx->getDelayNS(arc_delay), - ctx->getDelayNS(arc_budget)); - tns += ctx->getDelayNS(arc_slack); - tns_arc_count++; - } - } - } - log_info("final tns with respect to arc budgets: %f ns (%d nets, %d arcs)\n", tns, tns_net_count, - tns_arc_count); - } - - NPNR_ASSERT(jobQueue.empty()); - jobCache.clear(); - - for (auto &net_it : ctx->nets) - addNetRouteJobs(ctx, cfg, net_it.first, jobCache, jobQueue); - -#ifndef NDEBUG - if (!jobQueue.empty()) { - log_info("Design strangely still contains unrouted source-sink pairs:\n"); - while (!jobQueue.empty()) { - log_info(" user %d on net %s.\n", jobQueue.top().user_idx, jobQueue.top().net.c_str(ctx)); - jobQueue.pop(); - } - log_info("Checksum: 0x%08x\n", ctx->checksum()); - ctx->check(); - ctx->unlock(); - return false; - } -#endif + log_info("Routing finished after %d iterations.\n", iter_cnt); log_info("Checksum: 0x%08x\n", ctx->checksum()); #ifndef NDEBUG @@ -975,30 +551,8 @@ bool router1(Context *ctx, const Router1Cfg &cfg) bool Context::getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay, std::unordered_map *route, bool useEstimate) { - RipupScoreboard scores; - Router1Cfg cfg(this); - cfg.useEstimate = useEstimate; - - Router router(this, cfg, scores, src_wire, dst_wire); - - if (!router.routedOkay) - return false; - - if (delay != nullptr) - *delay = router.visited.at(dst_wire).delay; - - if (route != nullptr) { - WireId cursor = dst_wire; - while (1) { - PipId pip = router.visited.at(cursor).pip; - (*route)[cursor] = pip; - if (pip == PipId()) - break; - cursor = getPipSrcWire(pip); - } - } - - return true; + // FIXME + return false; } NEXTPNR_NAMESPACE_END diff --git a/common/router1.h b/common/router1.h index a184cbe7..0c7699bc 100644 --- a/common/router1.h +++ b/common/router1.h @@ -33,6 +33,11 @@ struct Router1Cfg : Settings bool cleanupReroute; bool fullCleanupReroute; bool useEstimate; + delay_t wireRipupPenalty; + delay_t pipRipupPenalty; + delay_t wireReusePenalty; + delay_t pipReusePenalty; + delay_t estimatePrecision; }; extern bool router1(Context *ctx, const Router1Cfg &cfg); From f0a3a272ca52b2235b6609b61ba6ff56d6a9af8b Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 9 Nov 2018 22:39:39 +0100 Subject: [PATCH 03/29] Fixes and improvements in new router Signed-off-by: Clifford Wolf --- common/router1.cc | 69 +++++++++++++++++++++++++++++++++++++---------- common/router1.h | 4 +-- 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index c4d4a130..1a6d452b 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -67,7 +67,7 @@ struct QueuedWire WireId wire; PipId pip; - delay_t delay = 0, penalty = 0, togo = 0; + delay_t delay = 0, penalty = 0, bonus = 0, togo = 0; int randtag = 0; struct Greater @@ -76,6 +76,10 @@ struct QueuedWire { delay_t l = lhs.delay + lhs.penalty + lhs.togo; delay_t r = rhs.delay + rhs.penalty + rhs.togo; + NPNR_ASSERT(l >= 0); + NPNR_ASSERT(r >= 0); + l -= lhs.bonus; + r -= rhs.bonus; return l == r ? lhs.randtag > rhs.randtag : l > r; } }; @@ -304,6 +308,7 @@ struct Router1 int visitCnt = 0; int maxVisitCnt = INT_MAX; delay_t best_est = 0; + delay_t best_score = -1; { QueuedWire qw; @@ -311,6 +316,7 @@ struct Router1 qw.pip = PipId(); qw.delay = ctx->getWireDelay(qw.wire).maxDelay(); qw.penalty = 0; + qw.bonus = 0; if (cfg.useEstimate) { qw.togo = ctx->estimateDelay(qw.wire, dst_wire); best_est = qw.delay + qw.togo; @@ -329,6 +335,7 @@ struct Router1 for (auto pip : ctx->getPipsDownhill(qw.wire)) { delay_t next_delay = qw.delay + ctx->getPipDelay(pip).maxDelay(); delay_t next_penalty = qw.penalty; + delay_t next_bonus = qw.bonus; WireId next_wire = ctx->getPipDstWire(pip); next_delay += ctx->getWireDelay(next_wire).maxDelay(); @@ -340,7 +347,7 @@ struct Router1 continue; if (ripupWireNet == net_info) { - next_penalty += cfg.wireReusePenalty; + next_bonus += cfg.wireReuseBonus; } else { if (!ripup) continue; @@ -348,6 +355,8 @@ struct Router1 auto scores_it = wireScores.find(next_wire); if (scores_it != wireScores.end()) next_penalty += scores_it->second * cfg.wireRipupPenalty; + else + next_penalty += cfg.wireRipupPenalty; } } @@ -358,7 +367,7 @@ struct Router1 continue; if (ripupPipNet == net_info) { - next_penalty += cfg.pipReusePenalty; + next_bonus += cfg.pipReuseBonus; } else { if (!ripup) continue; @@ -366,17 +375,36 @@ struct Router1 auto scores_it = pipScores.find(pip); if (scores_it != pipScores.end()) next_penalty += scores_it->second * cfg.pipRipupPenalty; + else + next_penalty += cfg.pipRipupPenalty; } } - if (visited.count(next_wire)) { - if (visited.at(next_wire).delay + visited.at(next_wire).penalty <= next_delay + next_penalty + ctx->getDelayEpsilon()) + delay_t next_score = next_delay + next_penalty; + NPNR_ASSERT(next_score >= 0); + + if ((best_score >= 0) && (next_score - next_bonus - cfg.estimatePrecision > best_score)) + continue; + + auto old_visited_it = visited.find(next_wire); + if (old_visited_it != visited.end()) { + delay_t old_delay = old_visited_it->second.delay; + delay_t old_score = old_delay + old_visited_it->second.penalty; + NPNR_ASSERT(old_score >= 0); + + if (next_score + ctx->getDelayEpsilon() >= old_score) continue; -#if 0 // FIXME + + if (next_delay + ctx->getDelayEpsilon() >= old_delay) + continue; + +#if 0 if (ctx->debug) - log("Found better route to %s. Old vs new delay estimate: %.3f %.3f\n", - ctx->getWireName(next_wire).c_str(), - ctx->getDelayNS(visited.at(next_wire).delay), + log("Found better route to %s. Old vs new delay estimate: %.3f (%.3f) %.3f (%.3f)\n", + ctx->getWireName(next_wire).c_str(ctx), + ctx->getDelayNS(old_score), + ctx->getDelayNS(old_visited_it->second.delay), + ctx->getDelayNS(next_score), ctx->getDelayNS(next_delay)); #endif } @@ -386,21 +414,34 @@ struct Router1 next_qw.pip = pip; next_qw.delay = next_delay; next_qw.penalty = next_penalty; + next_qw.bonus = next_bonus; 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 > best_est + cfg.estimatePrecision) + if (this_est/2 - cfg.estimatePrecision > best_est) continue; if (best_est > this_est) best_est = this_est; } next_qw.randtag = ctx->rng(); +#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->getDelayNS(next_score), + ctx->getDelayNS(next_delay)); +#endif + visited[next_qw.wire] = next_qw; queue.push(next_qw); - if (maxVisitCnt == INT_MAX && next_wire == dst_wire) - maxVisitCnt = 2*visitCnt; + if (next_wire == dst_wire) { + if (maxVisitCnt == INT_MAX) + maxVisitCnt = 2*visitCnt; + best_score = next_score - next_bonus; + } } } @@ -478,8 +519,8 @@ Router1Cfg::Router1Cfg(Context *ctx) : Settings(ctx) wireRipupPenalty = ctx->getRipupDelayPenalty(); pipRipupPenalty = ctx->getRipupDelayPenalty(); - wireReusePenalty = -wireRipupPenalty/8; - pipReusePenalty = -pipRipupPenalty/8; + wireReuseBonus = wireRipupPenalty/8; + pipReuseBonus = pipRipupPenalty/8; estimatePrecision = 100 * ctx->getRipupDelayPenalty(); } diff --git a/common/router1.h b/common/router1.h index 0c7699bc..120bf30e 100644 --- a/common/router1.h +++ b/common/router1.h @@ -35,8 +35,8 @@ struct Router1Cfg : Settings bool useEstimate; delay_t wireRipupPenalty; delay_t pipRipupPenalty; - delay_t wireReusePenalty; - delay_t pipReusePenalty; + delay_t wireReuseBonus; + delay_t pipReuseBonus; delay_t estimatePrecision; }; From e312fc79bc62fac53be5bf7569e60eaaea818b5c Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 9 Nov 2018 22:59:23 +0100 Subject: [PATCH 04/29] Improve router console output Signed-off-by: Clifford Wolf --- common/router1.cc | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 1a6d452b..21670e77 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -537,18 +537,21 @@ bool router1(Context *ctx, const Router1Cfg &cfg) Router1 router(ctx, cfg); router.setup(); - log_info("Added %d arcs to routing queue.\n", int(router.arc_queue.size())); + log_info("Routing %d arcs.\n", int(router.arc_queue.size())); int iter_cnt = 0; int last_arcs_with_ripup = 0; int last_arcs_without_ripup = 0; + log_info(" | (re-)routed arcs | delta | remaining\n"); + log_info(" IterCnt | w/riput wo/ripup | w/r wo/r | arcs\n"); + while (!router.arc_queue.empty()) { if (++iter_cnt % 1000 == 0) { - log_info("At iteration %d:\n", iter_cnt); - log_info(" routed %d (%d) arcs with rip-up.\n", router.arcs_with_ripup, router.arcs_with_ripup - last_arcs_with_ripup); - log_info(" routed %d (%d) arcs without rip-up.\n", router.arcs_without_ripup, router.arcs_without_ripup - last_arcs_without_ripup); - log_info(" %d arcs remaining in routing queue.\n", 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; } @@ -566,12 +569,12 @@ bool router1(Context *ctx, const Router1Cfg &cfg) } } - log_info("At iteration %d:\n", iter_cnt); - log_info(" routed %d (%d) arcs with rip-up.\n", router.arcs_with_ripup, router.arcs_with_ripup - last_arcs_with_ripup); - log_info(" routed %d (%d) arcs without rip-up.\n", router.arcs_without_ripup, router.arcs_without_ripup - last_arcs_without_ripup); - log_info(" %d arcs remaining in routing queue.\n", 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 finished after %d iterations.\n", iter_cnt); + log_info("Routing complete.\n"); log_info("Checksum: 0x%08x\n", ctx->checksum()); #ifndef NDEBUG From c780ce584aa862fb52c6c32884f6a245e8ce4b51 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Fri, 9 Nov 2018 23:03:14 +0100 Subject: [PATCH 05/29] Fix log msg typo Signed-off-by: Clifford Wolf --- common/router1.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/router1.cc b/common/router1.cc index 21670e77..8991ef20 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -544,7 +544,7 @@ bool router1(Context *ctx, const Router1Cfg &cfg) int last_arcs_without_ripup = 0; log_info(" | (re-)routed arcs | delta | remaining\n"); - log_info(" IterCnt | w/riput wo/ripup | w/r wo/r | arcs\n"); + log_info(" IterCnt | w/ripup wo/ripup | w/r wo/r | arcs\n"); while (!router.arc_queue.empty()) { if (++iter_cnt % 1000 == 0) { From 97070486f06c34e841ab4363c4bb148a2f75442c Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 10 Nov 2018 14:00:36 +0100 Subject: [PATCH 06/29] Fixes and cleanups in router1 Signed-off-by: Clifford Wolf --- common/router1.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 8991ef20..7fed2729 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -132,6 +132,14 @@ struct Router1 arc_queue_insert(arc, src_wire, dst_wire); } + arc_key arc_queue_pop() + { + arc_entry entry = arc_queue.top(); + arc_queue.pop(); + queued_arcs.erase(entry.arc); + return entry.arc; + } + void ripup_net(NetInfo *net) { if (ctx->debug) @@ -207,10 +215,10 @@ struct Router1 // ECP5 global nets currently appear part-unrouted due to arch database limitations // Don't touch them in the router if (net_info->is_global) - return; + continue; #endif if (net_info->driver.cell == nullptr) - return; + continue; auto src_wire = ctx->getNetinfoSourceWire(net_info); @@ -256,14 +264,6 @@ struct Router1 } } - arc_key arc_queue_pop() - { - arc_entry entry = arc_queue.top(); - arc_queue.pop(); - queued_arcs.erase(entry.arc); - return entry.arc; - } - bool route_arc(const arc_key &arc, bool ripup) { From 6b94102e5ad32d82f826b8335c2cba7d580d95b1 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 10 Nov 2018 21:14:50 +0100 Subject: [PATCH 07/29] Add checkers and assertions to router1 and other improvements Signed-off-by: Clifford Wolf --- common/nextpnr.h | 3 +- common/router1.cc | 321 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 281 insertions(+), 43 deletions(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index 59ae0323..5a0bd4b1 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -269,7 +269,7 @@ struct PipMap struct NetInfo : ArchNetInfo { IdString name; - int32_t udata; + int32_t udata = 0; PortRef driver; std::vector users; @@ -541,6 +541,7 @@ struct Context : Arch, DeterministicRNG delay_t getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &sink) const; // provided by router1.cc + bool checkRoutedDesign() const; bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay = nullptr, std::unordered_map *route = nullptr, bool useEstimate = true); diff --git a/common/router1.cc b/common/router1.cc index 7fed2729..958edf7d 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -91,7 +91,8 @@ struct Router1 const Router1Cfg &cfg; std::priority_queue, arc_entry::Greater> arc_queue; - std::unordered_map> wire_to_arc; + std::unordered_map> wire_to_arcs; + std::unordered_map, arc_key::Hash> arc_to_wires; std::unordered_set queued_arcs; std::unordered_map visited; @@ -143,30 +144,32 @@ 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", net->name.c_str(ctx)); auto net_wires_copy = net->wires; for (auto &it : net_wires_copy) { if (it.second.pip == PipId()) - ripup_wire(it.first); + ripup_wire(it.first, true); else - ripup_pip(it.second.pip); + ripup_pip(it.second.pip, true); } ripup_flag = true; } - void ripup_wire(WireId wire) + void ripup_wire(WireId wire, bool extra_indent = false) { if (ctx->debug) - log(" ripup wire %s\n", ctx->getWireName(wire).c_str(ctx)); + log(" %sripup wire %s\n", extra_indent ? " " : "", ctx->getWireName(wire).c_str(ctx)); wireScores[wire]++; if (ctx->getBoundWireNet(wire)) { - for (auto &it : wire_to_arc[wire]) + for (auto &it : wire_to_arcs[wire]) { + arc_to_wires[it].erase(wire); arc_queue_insert(it); - wire_to_arc[wire].clear(); + } + wire_to_arcs[wire].clear(); ctx->unbindWire(wire); } @@ -179,20 +182,33 @@ struct Router1 ripup_flag = true; } - void ripup_pip(PipId pip) + void ripup_pip(PipId pip, bool extra_indent = false) { - if (ctx->debug) - log(" ripup pip %s\n", ctx->getPipName(pip).c_str(ctx)); - WireId wire = ctx->getPipDstWire(pip); + + if (ctx->debug) + log(" %sripup pip %s (%s)\n", extra_indent ? " " : "", ctx->getPipName(pip).c_str(ctx), ctx->getWireName(wire).c_str(ctx)); + wireScores[wire]++; pipScores[pip]++; if (ctx->getBoundPipNet(pip)) { - for (auto &it : wire_to_arc[wire]) - arc_queue_insert(it); - wire_to_arc[wire].clear(); ctx->unbindPip(pip); + goto remove_wire_arcs; + } + + if (ctx->getBoundWireNet(wire)) { + ctx->unbindWire(wire); + goto remove_wire_arcs; + } + + if (0) { +remove_wire_arcs: + for (auto &it : wire_to_arcs[wire]) { + arc_to_wires[it].erase(wire); + arc_queue_insert(it); + } + wire_to_arcs[wire].clear(); } NetInfo *net = ctx->getConflictingPipNet(pip); @@ -205,19 +221,75 @@ struct Router1 ripup_flag = true; } + bool skip_net(NetInfo *net_info) + { +#ifdef ARCH_ECP5 + // ECP5 global nets currently appear part-unrouted due to arch database limitations + // Don't touch them in the router + if (net_info->is_global) + return true; +#endif + if (net_info->driver.cell == nullptr) + return true; + + return false; + } + + void check() + { + std::unordered_set valid_arcs; + + for (auto &net_it : ctx->nets) + { + NetInfo *net_info = net_it.second.get(); + std::unordered_set valid_wires_for_net; + + if (skip_net(net_info)) + continue; + + auto src_wire = ctx->getNetinfoSourceWire(net_info); + log_assert(src_wire != WireId()); + + 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()); + + arc_key arc; + arc.net_info = net_info; + arc.user_idx = user_idx; + + valid_arcs.insert(arc); + + for (WireId wire : arc_to_wires[arc]) { + valid_wires_for_net.insert(wire); + log_assert(wire_to_arcs[wire].count(arc)); + log_assert(net_info->wires.count(wire)); + } + } + + for (auto &it : net_info->wires) { + WireId w = it.first; + log_assert(valid_wires_for_net.count(w)); + } + } + + for (auto &it : wire_to_arcs) { + for (auto &arc : it.second) + log_assert(valid_arcs.count(arc)); + } + + for (auto &it : arc_to_wires) { + log_assert(valid_arcs.count(it.first)); + } + } + void setup() { for (auto &net_it : ctx->nets) { NetInfo *net_info = net_it.second.get(); -#ifdef ARCH_ECP5 - // ECP5 global nets currently appear part-unrouted due to arch database limitations - // Don't touch them in the router - if (net_info->is_global) - continue; -#endif - if (net_info->driver.cell == nullptr) + if (skip_net(net_info)) continue; auto src_wire = ctx->getNetinfoSourceWire(net_info); @@ -237,8 +309,14 @@ struct Router1 arc.net_info = net_info; arc.user_idx = user_idx; + if (net_info->wires.count(src_wire) == 0) { + arc_queue_insert(arc, src_wire, dst_wire); + continue; + } + WireId cursor = dst_wire; - wire_to_arc[cursor].insert(arc); + wire_to_arcs[cursor].insert(arc); + arc_to_wires[arc].insert(cursor); while (src_wire != cursor) { auto it = net_info->wires.find(cursor); @@ -249,14 +327,15 @@ struct Router1 NPNR_ASSERT(it->second.pip != PipId()); cursor = ctx->getPipSrcWire(it->second.pip); - wire_to_arc[cursor].insert(arc); + wire_to_arcs[cursor].insert(arc); + arc_to_wires[arc].insert(cursor); } } std::vector unbind_wires; for (auto &it : net_info->wires) - if (it.second.strength < STRENGTH_LOCKED && wire_to_arc.count(it.first) == 0) + if (it.second.strength < STRENGTH_LOCKED && wire_to_arcs.count(it.first) == 0) unbind_wires.push_back(it.first); for (auto it : unbind_wires) @@ -282,21 +361,20 @@ struct Router1 // unbind wires that are currently used exclusively by this arc - std::vector unbind_wires; + std::unordered_set old_arc_wires; + old_arc_wires.swap(arc_to_wires[arc]); - for (auto &wire_it : net_info->wires) { - auto wire = wire_it.first; - auto wire_to_arc_it = wire_to_arc.find(wire); - NPNR_ASSERT(wire_to_arc_it != wire_to_arc.end()); - - wire_to_arc_it->second.erase(arc); - if (wire_to_arc_it->second.empty()) - unbind_wires.push_back(wire); + for (WireId wire : old_arc_wires) { + auto &arc_wires = wire_to_arcs.at(wire); + NPNR_ASSERT(arc_wires.count(arc)); + arc_wires.erase(arc); + if (arc_wires.empty()) { + if (ctx->debug) + log(" unbind %s\n", ctx->getWireName(wire).c_str(ctx)); + ctx->unbindWire(wire); + } } - for (auto it : unbind_wires) - ctx->unbindWire(it); - // reset wire queue if (!queue.empty()) { @@ -305,6 +383,8 @@ struct Router1 } visited.clear(); + // A* main loop + int visitCnt = 0; int maxVisitCnt = INT_MAX; delay_t best_est = 0; @@ -454,6 +534,10 @@ struct Router1 return false; } + // bind resulting route (and maybe unroute other nets) + + std::unordered_set unassign_wires = arc_to_wires[arc]; + WireId cursor = dst_wire; while (1) { if (ctx->debug) @@ -463,8 +547,10 @@ struct Router1 NetInfo *ripupWireNet = ctx->getConflictingWireNet(cursor); NPNR_ASSERT(ripupWireNet != nullptr); - if (ripupWireNet != net_info) + if (ripupWireNet != net_info) { ripup_wire(cursor); + NPNR_ASSERT(ctx->checkWireAvail(cursor)); + } } auto pip = visited[cursor].pip; @@ -476,19 +562,25 @@ struct Router1 NetInfo *ripupPipNet = ctx->getConflictingPipNet(pip); NPNR_ASSERT(ripupPipNet != nullptr); - if (ripupPipNet != net_info) + if (ripupPipNet != net_info || net_info->wires.at(cursor).pip != pip) ripup_pip(pip); } } if (ctx->checkWireAvail(cursor)) { - if (pip == PipId()) + if (pip == PipId()) { + if (ctx->debug) + log(" bind %s\n", ctx->getWireName(cursor).c_str(ctx)); ctx->bindWire(cursor, net_info, STRENGTH_WEAK); - else if (ctx->checkPipAvail(pip)) + } else if (ctx->checkPipAvail(pip)) { + if (ctx->debug) + log(" bind %s\n", ctx->getPipName(pip).c_str(ctx)); ctx->bindPip(pip, net_info, STRENGTH_WEAK); + } } - wire_to_arc[cursor].insert(arc); + wire_to_arcs[cursor].insert(arc); + arc_to_wires[arc].insert(cursor); if (pip == PipId()) break; @@ -536,6 +628,9 @@ bool router1(Context *ctx, const Router1Cfg &cfg) Router1 router(ctx, cfg); router.setup(); +#ifndef NDEBUG + router.check(); +#endif log_info("Routing %d arcs.\n", int(router.arc_queue.size())); @@ -554,6 +649,7 @@ bool router1(Context *ctx, const Router1Cfg &cfg) 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; + router.check(); } arc_key arc = router.arc_queue_pop(); @@ -573,8 +669,12 @@ bool router1(Context *ctx, const Router1Cfg &cfg) 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())); +#ifndef NDEBUG + router.check(); +#endif log_info("Routing complete.\n"); + ctx->checkRoutedDesign(); log_info("Checksum: 0x%08x\n", ctx->checksum()); #ifndef NDEBUG @@ -592,6 +692,143 @@ bool router1(Context *ctx, const Router1Cfg &cfg) } } +bool Context::checkRoutedDesign() const +{ + const Context *ctx = getCtx(); + + for (auto &net_it : ctx->nets) { + NetInfo *net_info = net_it.second.get(); + + 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 children; + }; + + std::unordered_map 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 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 setOrderNum; + std::unordered_set 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) { + log(" %*s=> sink %d\n", 2*num, "", dest_wires.at(w)); + } else { + 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 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 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)); + } + } + } + + log_assert(!found_unrouted); + log_assert(!found_loop); + log_assert(!found_stub); + log_assert(dangling_wires.empty()); + } + + return true; +} + bool Context::getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay, std::unordered_map *route, bool useEstimate) { From d904a3713800409f376b78021b7d890c7c5f505f Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 10 Nov 2018 23:50:08 +0100 Subject: [PATCH 08/29] flush logs when throwing an assertion_failure Signed-off-by: Clifford Wolf --- common/nextpnr.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/nextpnr.cc b/common/nextpnr.cc index 4e6407b2..2a581cc6 100644 --- a/common/nextpnr.cc +++ b/common/nextpnr.cc @@ -18,6 +18,7 @@ */ #include "nextpnr.h" +#include "log.h" NEXTPNR_NAMESPACE_BEGIN @@ -25,6 +26,7 @@ assertion_failure::assertion_failure(std::string msg, std::string expr_str, std: : runtime_error("Assertion failure: " + msg + " (" + filename + ":" + std::to_string(line) + ")"), msg(msg), expr_str(expr_str), filename(filename), line(line) { + log_flush(); } void IdString::set(const BaseCtx *ctx, const std::string &s) From 5b8c8bb966f7d4d2e7fe97137834a75b4e141d2e Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 10 Nov 2018 23:50:49 +0100 Subject: [PATCH 09/29] Some router1 cleanups Signed-off-by: Clifford Wolf --- common/router1.cc | 51 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 958edf7d..cac60104 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -649,7 +649,9 @@ bool router1(Context *ctx, const Router1Cfg &cfg) 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 router.check(); +#endif } arc_key arc = router.arc_queue_pop(); @@ -669,18 +671,17 @@ bool router1(Context *ctx, const Router1Cfg &cfg) 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 router.check(); + ctx->check(); + log_assert(ctx->checkRoutedDesign()); #endif - log_info("Routing complete.\n"); - ctx->checkRoutedDesign(); - log_info("Checksum: 0x%08x\n", ctx->checksum()); -#ifndef NDEBUG - ctx->check(); -#endif timing_analysis(ctx, true /* slack_histogram */, true /* print_path */); + ctx->unlock(); return true; } catch (log_execution_error_exception) { @@ -772,9 +773,11 @@ bool Context::checkRoutedDesign() const } if (db_entry.children.empty()) { if (dest_wires.count(w) != 0) { - log(" %*s=> sink %d\n", 2*num, "", dest_wires.at(w)); + if (ctx->debug) + log(" %*s=> sink %d\n", 2*num, "", dest_wires.at(w)); } else { - log(" %*s=> stub\n", 2*num, ""); + if (ctx->debug) + log(" %*s=> stub\n", 2*num, ""); found_stub = true; } } @@ -820,10 +823,34 @@ bool Context::checkRoutedDesign() const } } - log_assert(!found_unrouted); - log_assert(!found_loop); - log_assert(!found_stub); - log_assert(dangling_wires.empty()); + 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; From e7ae28cafeb72b6e427431c28868516fae170216 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 11 Nov 2018 00:29:25 +0100 Subject: [PATCH 10/29] Minor improvements in router1 Signed-off-by: Clifford Wolf --- common/router1.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index cac60104..ed9ac39a 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -247,6 +247,8 @@ remove_wire_arcs: if (skip_net(net_info)) continue; + // log("[check] net: %s\n", net_info->name.c_str(ctx)); + auto src_wire = ctx->getNetinfoSourceWire(net_info); log_assert(src_wire != WireId()); @@ -259,8 +261,10 @@ remove_wire_arcs: 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)); for (WireId wire : arc_to_wires[arc]) { + // log("[check] wire: %s\n", ctx->getWireName(wire).c_str(ctx)); valid_wires_for_net.insert(wire); log_assert(wire_to_arcs[wire].count(arc)); log_assert(net_info->wires.count(wire)); @@ -567,14 +571,14 @@ remove_wire_arcs: } } - if (ctx->checkWireAvail(cursor)) { + if (net_info->wires.count(cursor) == 0 || net_info->wires.at(cursor).pip != pip) { if (pip == PipId()) { if (ctx->debug) - log(" bind %s\n", ctx->getWireName(cursor).c_str(ctx)); + log(" bind wire %s\n", ctx->getWireName(cursor).c_str(ctx)); ctx->bindWire(cursor, net_info, STRENGTH_WEAK); - } else if (ctx->checkPipAvail(pip)) { + } else { if (ctx->debug) - log(" bind %s\n", ctx->getPipName(pip).c_str(ctx)); + log(" bind pip %s\n", ctx->getPipName(pip).c_str(ctx)); ctx->bindPip(pip, net_info, STRENGTH_WEAK); } } @@ -660,6 +664,7 @@ bool router1(Context *ctx, const Router1Cfg &cfg) log_warning("Failed to find a route for arc %d of net %s.\n", arc.user_idx, arc.net_info->name.c_str(ctx)); #ifndef NDEBUG + router.check(); ctx->check(); #endif ctx->unlock(); From 200fb3f6646a20fb5b3bca6d66964417f477689c Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sat, 10 Nov 2018 20:05:36 -0800 Subject: [PATCH 11/29] [placer1] Ignore timing of TMG_IGNORE nets --- common/place_common.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/place_common.cc b/common/place_common.cc index da8ab37d..4810697c 100644 --- a/common/place_common.cc +++ b/common/place_common.cc @@ -28,19 +28,19 @@ NEXTPNR_NAMESPACE_BEGIN wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type, float &tns) { wirelen_t wirelength = 0; - Loc driver_loc; - bool driver_gb; CellInfo *driver_cell = net->driver.cell; if (!driver_cell) return 0; if (driver_cell->bel == BelId()) return 0; - driver_gb = ctx->getBelGlobalBuf(driver_cell->bel); - driver_loc = ctx->getBelLocation(driver_cell->bel); + bool driver_gb = ctx->getBelGlobalBuf(driver_cell->bel); if (driver_gb) return 0; + IdString clock_port; + bool driver_tmg_ignore = ctx->getPortTimingClass(driver_cell, net->driver.port, clock_port); delay_t negative_slack = 0; delay_t worst_slack = std::numeric_limits::max(); + Loc driver_loc = ctx->getBelLocation(driver_cell->bel); int xmin = driver_loc.x, xmax = driver_loc.x, ymin = driver_loc.y, ymax = driver_loc.y; for (auto load : net->users) { if (load.cell == nullptr) @@ -48,7 +48,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type CellInfo *load_cell = load.cell; if (load_cell->bel == BelId()) continue; - if (ctx->timing_driven && type == MetricType::COST) { + if (!driver_tmg_ignore && ctx->timing_driven && type == MetricType::COST) { delay_t net_delay = ctx->predictDelay(net, load); auto slack = load.budget - net_delay; if (slack < 0) From 78b684bcf88b52282fe06b01e0961b67984df847 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sat, 10 Nov 2018 22:30:35 -0800 Subject: [PATCH 12/29] [placer1] Actually check for TMG_IGNORE! --- common/place_common.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/place_common.cc b/common/place_common.cc index 4810697c..95343dc0 100644 --- a/common/place_common.cc +++ b/common/place_common.cc @@ -37,7 +37,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type if (driver_gb) return 0; IdString clock_port; - bool driver_tmg_ignore = ctx->getPortTimingClass(driver_cell, net->driver.port, clock_port); + bool driver_tmg_ignore = ctx->getPortTimingClass(driver_cell, net->driver.port, clock_port) == TMG_IGNORE; delay_t negative_slack = 0; delay_t worst_slack = std::numeric_limits::max(); Loc driver_loc = ctx->getBelLocation(driver_cell->bel); From 5cc9b9f61f3e01c05a8895640f6111fe73611fc3 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 11 Nov 2018 10:02:32 +0100 Subject: [PATCH 13/29] Bugfix in router1 Signed-off-by: Clifford Wolf --- common/router1.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index ed9ac39a..a4b37daf 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -544,21 +544,22 @@ remove_wire_arcs: WireId cursor = dst_wire; while (1) { + auto pip = visited[cursor].pip; + if (ctx->debug) log(" node %s\n", ctx->getWireName(cursor).c_str(ctx)); if (!ctx->checkWireAvail(cursor)) { NetInfo *ripupWireNet = ctx->getConflictingWireNet(cursor); NPNR_ASSERT(ripupWireNet != nullptr); + NPNR_ASSERT(ripupWireNet->wires.count(cursor)); - if (ripupWireNet != net_info) { + if (ripupWireNet != net_info || net_info->wires.at(cursor).pip != pip) { ripup_wire(cursor); NPNR_ASSERT(ctx->checkWireAvail(cursor)); } } - auto pip = visited[cursor].pip; - if (pip == PipId()) { NPNR_ASSERT(cursor == src_wire); } else { @@ -658,6 +659,9 @@ bool router1(Context *ctx, const Router1Cfg &cfg) #endif } + if (ctx->debug) + log("-- %d --\n", iter_cnt); + arc_key arc = router.arc_queue_pop(); if (!router.route_arc(arc, true)) { From 285bffeac5ace1679489c2d23c8c5dc4b06f0bfc Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 11 Nov 2018 10:11:55 +0100 Subject: [PATCH 14/29] Another bugfix in router1 Signed-off-by: Clifford Wolf --- common/router1.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/router1.cc b/common/router1.cc index a4b37daf..14591232 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -567,7 +567,7 @@ remove_wire_arcs: NetInfo *ripupPipNet = ctx->getConflictingPipNet(pip); NPNR_ASSERT(ripupPipNet != nullptr); - if (ripupPipNet != net_info || net_info->wires.at(cursor).pip != pip) + if (ripupPipNet != net_info || !net_info->wires.count(cursor) || net_info->wires.at(cursor).pip != pip) ripup_pip(pip); } } From d2bdb670c0be9e18722f79c170fc99d7f41768f1 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 11 Nov 2018 11:34:38 +0100 Subject: [PATCH 15/29] Add getConflictingPipWire() arch API, router1 improvements Signed-off-by: Clifford Wolf --- common/router1.cc | 62 ++++++++++++++++++++++++++++++--------------- common/router1.h | 1 + docs/archapi.md | 24 +++++++++++++----- ecp5/arch.h | 5 ++++ generic/arch.cc | 2 ++ generic/arch.h | 1 + gui/designwidget.cc | 2 ++ ice40/arch.h | 26 ++++++++++++------- 8 files changed, 87 insertions(+), 36 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 14591232..f51155e8 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -100,6 +100,7 @@ struct Router1 std::unordered_map wireScores; std::unordered_map pipScores; + std::unordered_map netScores; int arcs_with_ripup = 0; int arcs_without_ripup = 0; @@ -146,21 +147,23 @@ struct Router1 if (ctx->debug) log(" ripup net %s\n", net->name.c_str(ctx)); + netScores[net]++; + auto net_wires_copy = net->wires; for (auto &it : net_wires_copy) { if (it.second.pip == PipId()) - ripup_wire(it.first, true); + ripup_wire(it.first, 4); else - ripup_pip(it.second.pip, true); + ripup_pip(it.second.pip, 4); } ripup_flag = true; } - void ripup_wire(WireId wire, bool extra_indent = false) + void ripup_wire(WireId wire, int extra_indent = 0) { if (ctx->debug) - log(" %sripup wire %s\n", extra_indent ? " " : "", ctx->getWireName(wire).c_str(ctx)); + log(" %*sripup wire %s\n", extra_indent, "", ctx->getWireName(wire).c_str(ctx)); wireScores[wire]++; @@ -182,14 +185,13 @@ struct Router1 ripup_flag = true; } - void ripup_pip(PipId pip, bool extra_indent = false) + void ripup_pip(PipId pip, int extra_indent = 0) { WireId wire = ctx->getPipDstWire(pip); if (ctx->debug) - log(" %sripup pip %s (%s)\n", extra_indent ? " " : "", ctx->getPipName(pip).c_str(ctx), ctx->getWireName(wire).c_str(ctx)); + log(" %*sripup pip %s (%s)\n", extra_indent, "", ctx->getPipName(pip).c_str(ctx), ctx->getWireName(wire).c_str(ctx)); - wireScores[wire]++; pipScores[pip]++; if (ctx->getBoundPipNet(pip)) { @@ -204,6 +206,7 @@ struct Router1 if (0) { remove_wire_arcs: + wireScores[wire]++; for (auto &it : wire_to_arcs[wire]) { arc_to_wires[it].erase(wire); arc_queue_insert(it); @@ -213,9 +216,11 @@ remove_wire_arcs: NetInfo *net = ctx->getConflictingPipNet(pip); if (net != nullptr) { - wireScores[wire] += net->wires.size(); - pipScores[pip] += net->wires.size(); - ripup_net(net); + wire = ctx->getConflictingPipWire(pip); + if (wire != WireId()) + ripup_wire(wire, 2); + else + ripup_net(net); } ripup_flag = true; @@ -436,11 +441,11 @@ remove_wire_arcs: if (!ripup) continue; + next_penalty += cfg.wireRipupPenalty; + auto scores_it = wireScores.find(next_wire); if (scores_it != wireScores.end()) next_penalty += scores_it->second * cfg.wireRipupPenalty; - else - next_penalty += cfg.wireRipupPenalty; } } @@ -451,16 +456,29 @@ remove_wire_arcs: continue; if (ripupPipNet == net_info) { + auto net_info_wire_it = net_info->wires.find(next_wire); + if (net_info_wire_it == net_info->wires.end() || net_info_wire_it->second.pip != pip) + goto pip_self_ripup; next_bonus += cfg.pipReuseBonus; } else { +pip_self_ripup: if (!ripup) continue; - auto scores_it = pipScores.find(pip); - if (scores_it != pipScores.end()) - next_penalty += scores_it->second * cfg.pipRipupPenalty; - else - next_penalty += cfg.pipRipupPenalty; + next_penalty += cfg.pipRipupPenalty; + + auto pip_scores_it = pipScores.find(pip); + if (pip_scores_it != pipScores.end()) + next_penalty += pip_scores_it->second * cfg.pipRipupPenalty; + + if (ctx->getConflictingPipWire(pip) == WireId()) { + auto net_scores_it = netScores.find(ripupPipNet); + if (net_scores_it != netScores.end()) + next_penalty += net_scores_it->second * cfg.netRipupPenalty; + + next_penalty += ripupPipNet->wires.size() * cfg.wireRipupPenalty; + next_penalty += (ripupPipNet->wires.size()-1) * cfg.pipRipupPenalty; + } } } @@ -479,9 +497,6 @@ remove_wire_arcs: if (next_score + ctx->getDelayEpsilon() >= old_score) continue; - if (next_delay + ctx->getDelayEpsilon() >= old_delay) - continue; - #if 0 if (ctx->debug) log("Found better route to %s. Old vs new delay estimate: %.3f (%.3f) %.3f (%.3f)\n", @@ -538,6 +553,12 @@ remove_wire_arcs: return false; } + if (ctx->debug) { + 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)); + } + // bind resulting route (and maybe unroute other nets) std::unordered_set unassign_wires = arc_to_wires[arc]; @@ -615,6 +636,7 @@ Router1Cfg::Router1Cfg(Context *ctx) : Settings(ctx) wireRipupPenalty = ctx->getRipupDelayPenalty(); pipRipupPenalty = ctx->getRipupDelayPenalty(); + netRipupPenalty = ctx->getRipupDelayPenalty(); wireReuseBonus = wireRipupPenalty/8; pipReuseBonus = pipRipupPenalty/8; diff --git a/common/router1.h b/common/router1.h index 120bf30e..65975d53 100644 --- a/common/router1.h +++ b/common/router1.h @@ -35,6 +35,7 @@ struct Router1Cfg : Settings bool useEstimate; delay_t wireRipupPenalty; delay_t pipRipupPenalty; + delay_t netRipupPenalty; delay_t wireReuseBonus; delay_t pipReuseBonus; delay_t estimatePrecision; diff --git a/docs/archapi.md b/docs/archapi.md index 73443c15..1bfb7c5c 100644 --- a/docs/archapi.md +++ b/docs/archapi.md @@ -217,12 +217,12 @@ Return the net a wire is bound to. ### NetInfo \*getConflictingWireNet(WireId wire) const -If this returns a non-nullptr, then unbinding that net +If this returns a non-nullptr, then unbinding the wire from that net will make the given wire available. This returns nullptr if the wire is already available, -or if there is no single net that can be unbound to make this -wire available. +or if there is no net that can be unbound from the wire to make it +available. ### DelayInfo getWireDelay(WireId wire) const @@ -289,11 +289,21 @@ Return the net this pip is bound to. ### NetInfo \*getConflictingPipNet(PipId pip) const Return the net that needs to be unbound in order to make this -pip available. +pip available. Note that it may be neccessary to unroute that +entire net to make the pip available. -This does not need to (but may) return the conflicting wire if the conflict is -limited to the conflicting wire being bound to the destination wire for this -pip. +This returns nullptr if the pip is already available, +or if there is no single net that can be unrouted to make +the pip available. + +### WireId getConflictingPipWire(PipId pip) const + +Return the single wire that needs to be unbound in order to make this pip +available. + +This returns WireId() if the pip is already available, +or if there is no single wire that can be unbound to make +the pip available. ### const\_range\ getPips() const diff --git a/ecp5/arch.h b/ecp5/arch.h index 583d539f..c9b1bf43 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -733,6 +733,11 @@ struct Arch : BaseCtx return pip_to_net.at(pip); } + WireId getConflictingPipWire(PipId pip) const + { + return WireId(); + } + AllPipRange getPips() const { AllPipRange range; diff --git a/generic/arch.cc b/generic/arch.cc index 3e95159a..4f2e07a2 100644 --- a/generic/arch.cc +++ b/generic/arch.cc @@ -373,6 +373,8 @@ NetInfo *Arch::getBoundPipNet(PipId pip) const { return pips.at(pip).bound_net; NetInfo *Arch::getConflictingPipNet(PipId pip) const { return pips.at(pip).bound_net; } +WireId Arch::getConflictingPipWire(PipId pip) const { return pips.at(pip).bound_net ? pips.at(pip).dstWire : WireId(); } + const std::vector &Arch::getPips() const { return pip_ids; } Loc Arch::getPipLocation(PipId pip) const { return pips.at(pip).loc; } diff --git a/generic/arch.h b/generic/arch.h index 22966e2a..d64d03c3 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -187,6 +187,7 @@ struct Arch : BaseCtx bool checkPipAvail(PipId pip) const; NetInfo *getBoundPipNet(PipId pip) const; NetInfo *getConflictingPipNet(PipId pip) const; + WireId getConflictingPipWire(PipId pip) const; const std::vector &getPips() const; Loc getPipLocation(PipId pip) const; WireId getPipSrcWire(PipId pip) const; diff --git a/gui/designwidget.cc b/gui/designwidget.cc index a45752fc..8eda8a76 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -515,6 +515,8 @@ void DesignWidget::onSelectionChanged(const QItemSelection &, const QItemSelecti addProperty(topItem, QVariant::String, "Bound Net", ctx->nameOf(ctx->getBoundPipNet(pip)), ElementType::NET); addProperty(topItem, QVariant::String, "Conflicting Net", ctx->nameOf(ctx->getConflictingPipNet(pip)), ElementType::NET); + addProperty(topItem, QVariant::String, "Conflicting Wire", ctx->getWireName(ctx->getConflictingPipWire(pip)).c_str(ctx), + ElementType::WIRE); addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx), ElementType::WIRE); addProperty(topItem, QVariant::String, "Dest Wire", ctx->getWireName(ctx->getPipDstWire(pip)).c_str(ctx), diff --git a/ice40/arch.h b/ice40/arch.h index 27d5db9f..46f2b348 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -404,7 +404,7 @@ struct Arch : BaseCtx std::vector bel_to_cell; std::vector wire_to_net; std::vector pip_to_net; - std::vector switches_locked; + std::vector switches_locked; ArchArgs args; Arch(ArchArgs args); @@ -546,7 +546,7 @@ struct Arch : BaseCtx auto pip = it->second.pip; if (pip != PipId()) { pip_to_net[pip.index] = nullptr; - switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr; + switches_locked[chip_info->pip_data[pip.index].switch_index] = WireId(); } net_wires.erase(it); @@ -608,14 +608,15 @@ struct Arch : BaseCtx { NPNR_ASSERT(pip != PipId()); NPNR_ASSERT(pip_to_net[pip.index] == nullptr); - NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] == nullptr); - - pip_to_net[pip.index] = net; - switches_locked[chip_info->pip_data[pip.index].switch_index] = net; + NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] == WireId()); WireId dst; dst.index = chip_info->pip_data[pip.index].dst; NPNR_ASSERT(wire_to_net[dst.index] == nullptr); + + pip_to_net[pip.index] = net; + switches_locked[chip_info->pip_data[pip.index].switch_index] = dst; + wire_to_net[dst.index] = net; net->wires[dst].pip = pip; net->wires[dst].strength = strength; @@ -627,7 +628,7 @@ struct Arch : BaseCtx { NPNR_ASSERT(pip != PipId()); NPNR_ASSERT(pip_to_net[pip.index] != nullptr); - NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] != nullptr); + NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] != WireId()); WireId dst; dst.index = chip_info->pip_data[pip.index].dst; @@ -636,7 +637,7 @@ struct Arch : BaseCtx pip_to_net[pip.index]->wires.erase(dst); pip_to_net[pip.index] = nullptr; - switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr; + switches_locked[chip_info->pip_data[pip.index].switch_index] = WireId(); refreshUiPip(pip); refreshUiWire(dst); } @@ -647,7 +648,7 @@ struct Arch : BaseCtx auto &pi = chip_info->pip_data[pip.index]; auto &si = chip_info->bits_info->switches[pi.switch_index]; - if (switches_locked[pi.switch_index] != nullptr) + if (switches_locked[pi.switch_index] != WireId()) return false; if (pi.flags & PipInfoPOD::FLAG_ROUTETHRU) { @@ -672,6 +673,13 @@ struct Arch : BaseCtx } NetInfo *getConflictingPipNet(PipId pip) const + { + NPNR_ASSERT(pip != PipId()); + WireId wire = switches_locked[chip_info->pip_data[pip.index].switch_index]; + return wire == WireId() ? nullptr : wire_to_net[wire.index]; + } + + WireId getConflictingPipWire(PipId pip) const { NPNR_ASSERT(pip != PipId()); return switches_locked[chip_info->pip_data[pip.index].switch_index]; From dac553cab4b72a052f9738017d13ea40495f610c Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 11 Nov 2018 12:04:02 +0100 Subject: [PATCH 16/29] Add some additional checks to router1 to find issues in input netlist Signed-off-by: Clifford Wolf --- common/router1.cc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/common/router1.cc b/common/router1.cc index f51155e8..578e287b 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -294,6 +294,9 @@ remove_wire_arcs: void setup() { + std::unordered_map src_to_net; + std::unordered_map dst_to_arc; + for (auto &net_it : ctx->nets) { NetInfo *net_info = net_it.second.get(); @@ -307,6 +310,14 @@ remove_wire_arcs: 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)); + 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), + 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); + 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]); @@ -314,10 +325,20 @@ remove_wire_arcs: 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)); + if (dst_to_arc.count(dst_wire)) + 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); + + 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); + arc_key arc; arc.net_info = net_info; arc.user_idx = user_idx; + dst_to_arc[dst_wire] = arc; + if (net_info->wires.count(src_wire) == 0) { arc_queue_insert(arc, src_wire, dst_wire); continue; @@ -341,6 +362,8 @@ remove_wire_arcs: } } + src_to_net[src_wire] = net_info; + std::vector unbind_wires; for (auto &it : net_info->wires) From ee8826b6e86fdce793a4c58ee685bd6cae5d796e Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 11 Nov 2018 12:16:25 +0100 Subject: [PATCH 17/29] Ignore "duplicate" arcs in the same net in router1 Signed-off-by: Clifford Wolf --- common/router1.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/router1.cc b/common/router1.cc index 578e287b..6a888c45 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -325,9 +325,12 @@ remove_wire_arcs: 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)); - if (dst_to_arc.count(dst_wire)) + 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); + } 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), From f93129634b479ba54d8e33186eb79f412eaeb4a9 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 11 Nov 2018 17:28:41 +0100 Subject: [PATCH 18/29] Add getConflictingWireWire() arch API, streamline getConflictingXY semantic Signed-off-by: Clifford Wolf --- common/router1.cc | 229 +++++++++++++++++++++++--------------------- common/router1.h | 1 - docs/archapi.md | 36 ++++--- ecp5/arch.h | 15 ++- generic/arch.h | 3 +- gui/designwidget.cc | 6 +- ice40/arch.h | 45 ++++++--- 7 files changed, 183 insertions(+), 152 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 6a888c45..64f01c0d 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -99,7 +99,6 @@ struct Router1 std::priority_queue, QueuedWire::Greater> queue; std::unordered_map wireScores; - std::unordered_map pipScores; std::unordered_map netScores; int arcs_with_ripup = 0; @@ -125,6 +124,9 @@ struct Router1 void arc_queue_insert(const arc_key &arc) { + if (queued_arcs.count(arc)) + return; + NetInfo *net_info = arc.net_info; int user_idx = arc.user_idx; @@ -151,10 +153,19 @@ struct Router1 auto net_wires_copy = net->wires; for (auto &it : net_wires_copy) { - if (it.second.pip == PipId()) - ripup_wire(it.first, 4); - else - ripup_pip(it.second.pip, 4); + WireId w = it.first; + + for (auto &it : wire_to_arcs[w]) { + arc_to_wires[it].erase(w); + arc_queue_insert(it); + } + wire_to_arcs[w].clear(); + + if (ctx->debug) + log(" unbind wire %s\n", ctx->getWireName(w).c_str(ctx)); + + ctx->unbindWire(w); + wireScores[w]++; } ripup_flag = true; @@ -163,64 +174,54 @@ struct Router1 void ripup_wire(WireId wire, int extra_indent = 0) { if (ctx->debug) - log(" %*sripup wire %s\n", extra_indent, "", ctx->getWireName(wire).c_str(ctx)); + log(" ripup wire %s\n", ctx->getWireName(wire).c_str(ctx)); - wireScores[wire]++; + WireId w = ctx->getConflictingWireWire(wire); - if (ctx->getBoundWireNet(wire)) { - for (auto &it : wire_to_arcs[wire]) { - arc_to_wires[it].erase(wire); + if (w == WireId()) { + NetInfo *n = ctx->getConflictingWireNet(wire); + if (n != nullptr) + ripup_net(n); + } else { + for (auto &it : wire_to_arcs[w]) { + arc_to_wires[it].erase(w); arc_queue_insert(it); } - wire_to_arcs[wire].clear(); - ctx->unbindWire(wire); - } + wire_to_arcs[w].clear(); - NetInfo *net = ctx->getConflictingWireNet(wire); - if (net != nullptr) { - wireScores[wire] += net->wires.size(); - ripup_net(net); + if (ctx->debug) + log(" unbind wire %s\n", ctx->getWireName(w).c_str(ctx)); + + ctx->unbindWire(w); + wireScores[w]++; } ripup_flag = true; } - void ripup_pip(PipId pip, int extra_indent = 0) + void ripup_pip(PipId pip) { - WireId wire = ctx->getPipDstWire(pip); - if (ctx->debug) - log(" %*sripup pip %s (%s)\n", extra_indent, "", ctx->getPipName(pip).c_str(ctx), ctx->getWireName(wire).c_str(ctx)); + log(" ripup pip %s\n", ctx->getPipName(pip).c_str(ctx)); - pipScores[pip]++; + WireId w = ctx->getConflictingPipWire(pip); - if (ctx->getBoundPipNet(pip)) { - ctx->unbindPip(pip); - goto remove_wire_arcs; - } - - if (ctx->getBoundWireNet(wire)) { - ctx->unbindWire(wire); - goto remove_wire_arcs; - } - - if (0) { -remove_wire_arcs: - wireScores[wire]++; - for (auto &it : wire_to_arcs[wire]) { - arc_to_wires[it].erase(wire); + if (w == WireId()) { + NetInfo *n = ctx->getConflictingPipNet(pip); + if (n != nullptr) + ripup_net(n); + } else { + for (auto &it : wire_to_arcs[w]) { + arc_to_wires[it].erase(w); arc_queue_insert(it); } - wire_to_arcs[wire].clear(); - } + wire_to_arcs[w].clear(); - NetInfo *net = ctx->getConflictingPipNet(pip); - if (net != nullptr) { - wire = ctx->getConflictingPipWire(pip); - if (wire != WireId()) - ripup_wire(wire, 2); - else - ripup_net(net); + if (ctx->debug) + log(" unbind wire %s\n", ctx->getWireName(w).c_str(ctx)); + + ctx->unbindWire(w); + wireScores[w]++; } ripup_flag = true; @@ -455,59 +456,82 @@ remove_wire_arcs: WireId next_wire = ctx->getPipDstWire(pip); next_delay += ctx->getWireDelay(next_wire).maxDelay(); - if (!ctx->checkWireAvail(next_wire)) { - NetInfo *ripupWireNet = ctx->getConflictingWireNet(next_wire); + WireId conflictWireWire = WireId(), conflictPipWire = WireId(); + NetInfo *conflictWireNet = nullptr, *conflictPipNet = nullptr; - if (ripupWireNet == nullptr) + bool wire_reuse = net_info->wires.count(next_wire); + bool pip_reuse = wire_reuse && net_info->wires.at(next_wire).pip == pip; + + if (!ctx->checkWireAvail(next_wire) && !wire_reuse) { + if (!ripup) continue; - - if (ripupWireNet == net_info) { - next_bonus += cfg.wireReuseBonus; - } else { - if (!ripup) + conflictWireWire = ctx->getConflictingWireWire(next_wire); + if (conflictWireWire == WireId()) { + conflictWireNet = ctx->getConflictingWireNet(next_wire); + if (conflictWireNet == nullptr) continue; - - next_penalty += cfg.wireRipupPenalty; - - auto scores_it = wireScores.find(next_wire); - if (scores_it != wireScores.end()) - next_penalty += scores_it->second * cfg.wireRipupPenalty; } } - if (!ctx->checkPipAvail(pip)) { - NetInfo *ripupPipNet = ctx->getConflictingPipNet(pip); - - if (ripupPipNet == nullptr) + if (!ctx->checkPipAvail(pip) && !pip_reuse) { + if (!ripup) continue; - - if (ripupPipNet == net_info) { - auto net_info_wire_it = net_info->wires.find(next_wire); - if (net_info_wire_it == net_info->wires.end() || net_info_wire_it->second.pip != pip) - goto pip_self_ripup; - next_bonus += cfg.pipReuseBonus; - } else { -pip_self_ripup: - if (!ripup) + conflictPipWire = ctx->getConflictingPipWire(pip); + if (conflictPipWire == WireId()) { + conflictPipNet = ctx->getConflictingPipNet(pip); + if (conflictPipNet == nullptr) continue; - - next_penalty += cfg.pipRipupPenalty; - - auto pip_scores_it = pipScores.find(pip); - if (pip_scores_it != pipScores.end()) - next_penalty += pip_scores_it->second * cfg.pipRipupPenalty; - - if (ctx->getConflictingPipWire(pip) == WireId()) { - auto net_scores_it = netScores.find(ripupPipNet); - if (net_scores_it != netScores.end()) - next_penalty += net_scores_it->second * cfg.netRipupPenalty; - - next_penalty += ripupPipNet->wires.size() * cfg.wireRipupPenalty; - next_penalty += (ripupPipNet->wires.size()-1) * cfg.pipRipupPenalty; - } } } + if (conflictWireNet != nullptr && conflictPipWire != WireId() && conflictWireNet->wires.count(conflictPipWire)) + conflictPipWire = WireId(); + + if (conflictPipNet != nullptr && conflictWireWire != WireId() && conflictPipNet->wires.count(conflictWireWire)) + conflictWireWire = WireId(); + + if (conflictWireWire == conflictPipWire) + conflictWireWire = WireId(); + + if (conflictWireNet == conflictPipNet) + conflictWireNet = nullptr; + + if (wire_reuse) + next_bonus += cfg.wireReuseBonus; + + if (pip_reuse) + next_bonus += cfg.pipReuseBonus; + + if (conflictWireWire != WireId()) { + auto scores_it = wireScores.find(conflictWireWire); + if (scores_it != wireScores.end()) + next_penalty += scores_it->second * cfg.wireRipupPenalty; + next_penalty += cfg.wireRipupPenalty; + } + + if (conflictPipWire != WireId()) { + auto scores_it = wireScores.find(conflictPipWire); + if (scores_it != wireScores.end()) + next_penalty += scores_it->second * cfg.wireRipupPenalty; + next_penalty += cfg.wireRipupPenalty; + } + + if (conflictWireNet != nullptr) { + auto scores_it = netScores.find(conflictWireNet); + if (scores_it != netScores.end()) + next_penalty += scores_it->second * cfg.netRipupPenalty; + next_penalty += cfg.netRipupPenalty; + next_penalty += conflictWireNet->wires.size() * cfg.wireRipupPenalty; + } + + if (conflictPipNet != nullptr) { + auto scores_it = netScores.find(conflictPipNet); + if (scores_it != netScores.end()) + next_penalty += scores_it->second * cfg.netRipupPenalty; + next_penalty += cfg.netRipupPenalty; + next_penalty += conflictPipNet->wires.size() * cfg.wireRipupPenalty; + } + delay_t next_score = next_delay + next_penalty; NPNR_ASSERT(next_score >= 0); @@ -596,30 +620,20 @@ pip_self_ripup: if (ctx->debug) log(" node %s\n", ctx->getWireName(cursor).c_str(ctx)); - if (!ctx->checkWireAvail(cursor)) { - NetInfo *ripupWireNet = ctx->getConflictingWireNet(cursor); - NPNR_ASSERT(ripupWireNet != nullptr); - NPNR_ASSERT(ripupWireNet->wires.count(cursor)); + if (pip == PipId()) + NPNR_ASSERT(cursor == src_wire); - if (ripupWireNet != net_info || net_info->wires.at(cursor).pip != pip) { + if (!net_info->wires.count(cursor) || net_info->wires.at(cursor).pip != pip) { + if (!ctx->checkWireAvail(cursor)) { ripup_wire(cursor); NPNR_ASSERT(ctx->checkWireAvail(cursor)); } - } - if (pip == PipId()) { - NPNR_ASSERT(cursor == src_wire); - } else { - if (!ctx->checkPipAvail(pip)) { - NetInfo *ripupPipNet = ctx->getConflictingPipNet(pip); - NPNR_ASSERT(ripupPipNet != nullptr); - - if (ripupPipNet != net_info || !net_info->wires.count(cursor) || net_info->wires.at(cursor).pip != pip) - ripup_pip(pip); + if (pip != PipId() && !ctx->checkPipAvail(pip)) { + ripup_pip(pip); + NPNR_ASSERT(ctx->checkPipAvail(pip)); } - } - if (net_info->wires.count(cursor) == 0 || net_info->wires.at(cursor).pip != pip) { if (pip == PipId()) { if (ctx->debug) log(" bind wire %s\n", ctx->getWireName(cursor).c_str(ctx)); @@ -661,11 +675,10 @@ Router1Cfg::Router1Cfg(Context *ctx) : Settings(ctx) useEstimate = get("router1/useEstimate", true); wireRipupPenalty = ctx->getRipupDelayPenalty(); - pipRipupPenalty = ctx->getRipupDelayPenalty(); - netRipupPenalty = ctx->getRipupDelayPenalty(); + netRipupPenalty = 10*ctx->getRipupDelayPenalty(); wireReuseBonus = wireRipupPenalty/8; - pipReuseBonus = pipRipupPenalty/8; + pipReuseBonus = wireRipupPenalty/2; estimatePrecision = 100 * ctx->getRipupDelayPenalty(); } diff --git a/common/router1.h b/common/router1.h index 65975d53..d6113441 100644 --- a/common/router1.h +++ b/common/router1.h @@ -34,7 +34,6 @@ struct Router1Cfg : Settings bool fullCleanupReroute; bool useEstimate; delay_t wireRipupPenalty; - delay_t pipRipupPenalty; delay_t netRipupPenalty; delay_t wireReuseBonus; delay_t pipReuseBonus; diff --git a/docs/archapi.md b/docs/archapi.md index 1bfb7c5c..85bc6ccd 100644 --- a/docs/archapi.md +++ b/docs/archapi.md @@ -215,14 +215,15 @@ Return true if the wire is available, i.e. can be bound to a net. Return the net a wire is bound to. -### NetInfo \*getConflictingWireNet(WireId wire) const +### WireId getConflictingWireWire(WireId wire) const -If this returns a non-nullptr, then unbinding the wire from that net +If this returns a non-WireId(), then unbinding that wire will make the given wire available. -This returns nullptr if the wire is already available, -or if there is no net that can be unbound from the wire to make it -available. +### NetInfo \*getConflictingWireNet(WireId wire) const + +If this returns a non-nullptr, then unbinding that entire net +will make the given wire available. ### DelayInfo getWireDelay(WireId wire) const @@ -282,28 +283,23 @@ This method must also update `NetInfo::wires`. Returns true if the given pip is available to be bound to a net. +Users must also check if the pip destination wire is available +with `checkWireAvail(getPipDstWire(pip))` before binding the +pip to a net. + ### NetInfo \*getBoundPipNet(PipId pip) const Return the net this pip is bound to. -### NetInfo \*getConflictingPipNet(PipId pip) const - -Return the net that needs to be unbound in order to make this -pip available. Note that it may be neccessary to unroute that -entire net to make the pip available. - -This returns nullptr if the pip is already available, -or if there is no single net that can be unrouted to make -the pip available. - ### WireId getConflictingPipWire(PipId pip) const -Return the single wire that needs to be unbound in order to make this pip -available. +If this returns a non-WireId(), then unbinding that wire +will make the given pip available. -This returns WireId() if the pip is already available, -or if there is no single wire that can be unbound to make -the pip available. +### NetInfo \*getConflictingPipNet(PipId pip) const + +If this returns a non-nullptr, then unbinding that entire net +will make the given pip available. ### const\_range\ getPips() const diff --git a/ecp5/arch.h b/ecp5/arch.h index c9b1bf43..bd4881c2 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -619,6 +619,11 @@ struct Arch : BaseCtx return wire_to_net.at(wire); } + WireId getConflictingWireWire(WireId wire) const + { + return wire; + } + NetInfo *getConflictingWireNet(WireId wire) const { NPNR_ASSERT(wire != WireId()); @@ -724,6 +729,11 @@ struct Arch : BaseCtx return pip_to_net.at(pip); } + WireId getConflictingPipWire(PipId pip) const + { + return WireId(); + } + NetInfo *getConflictingPipNet(PipId pip) const { NPNR_ASSERT(pip != PipId()); @@ -733,11 +743,6 @@ struct Arch : BaseCtx return pip_to_net.at(pip); } - WireId getConflictingPipWire(PipId pip) const - { - return WireId(); - } - AllPipRange getPips() const { AllPipRange range; diff --git a/generic/arch.h b/generic/arch.h index d64d03c3..9311464e 100644 --- a/generic/arch.h +++ b/generic/arch.h @@ -172,6 +172,7 @@ struct Arch : BaseCtx void unbindWire(WireId wire); bool checkWireAvail(WireId wire) const; NetInfo *getBoundWireNet(WireId wire) const; + WireId getConflictingWireWire(WireId wire) const { return wire; } NetInfo *getConflictingWireNet(WireId wire) const; DelayInfo getWireDelay(WireId wire) const { return DelayInfo(); } const std::vector &getWires() const; @@ -186,8 +187,8 @@ struct Arch : BaseCtx void unbindPip(PipId pip); bool checkPipAvail(PipId pip) const; NetInfo *getBoundPipNet(PipId pip) const; - NetInfo *getConflictingPipNet(PipId pip) const; WireId getConflictingPipWire(PipId pip) const; + NetInfo *getConflictingPipNet(PipId pip) const; const std::vector &getPips() const; Loc getPipLocation(PipId pip) const; WireId getPipSrcWire(PipId pip) const; diff --git a/gui/designwidget.cc b/gui/designwidget.cc index 8eda8a76..8a134c1f 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -453,6 +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 Net", ctx->nameOf(ctx->getConflictingWireNet(wire)), ElementType::NET); @@ -513,10 +515,10 @@ 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 Net", ctx->nameOf(ctx->getConflictingPipNet(pip)), - ElementType::NET); 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), ElementType::WIRE); addProperty(topItem, QVariant::String, "Dest Wire", ctx->getWireName(ctx->getPipDstWire(pip)).c_str(ctx), diff --git a/ice40/arch.h b/ice40/arch.h index 46f2b348..2967ee1f 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -566,6 +566,11 @@ struct Arch : BaseCtx return wire_to_net[wire.index]; } + WireId getConflictingWireWire(WireId wire) const + { + return wire; + } + NetInfo *getConflictingWireNet(WireId wire) const { NPNR_ASSERT(wire != WireId()); @@ -642,28 +647,34 @@ struct Arch : BaseCtx refreshUiWire(dst); } - bool checkPipAvail(PipId pip) const + bool ice40_pip_hard_unavail(PipId pip) const { NPNR_ASSERT(pip != PipId()); auto &pi = chip_info->pip_data[pip.index]; auto &si = chip_info->bits_info->switches[pi.switch_index]; - if (switches_locked[pi.switch_index] != WireId()) - return false; - if (pi.flags & PipInfoPOD::FLAG_ROUTETHRU) { NPNR_ASSERT(si.bel >= 0); if (bel_to_cell[si.bel] != nullptr) - return false; + return true; } if (pi.flags & PipInfoPOD::FLAG_NOCARRY) { NPNR_ASSERT(si.bel >= 0); if (bel_carry[si.bel]) - return false; + return true; } - return true; + return false; + } + + bool checkPipAvail(PipId pip) const + { + if (ice40_pip_hard_unavail(pip)) + return false; + + auto &pi = chip_info->pip_data[pip.index]; + return switches_locked[pi.switch_index] == WireId(); } NetInfo *getBoundPipNet(PipId pip) const @@ -672,19 +683,23 @@ struct Arch : BaseCtx return pip_to_net[pip.index]; } - NetInfo *getConflictingPipNet(PipId pip) const - { - NPNR_ASSERT(pip != PipId()); - WireId wire = switches_locked[chip_info->pip_data[pip.index].switch_index]; - return wire == WireId() ? nullptr : wire_to_net[wire.index]; - } - WireId getConflictingPipWire(PipId pip) const { - NPNR_ASSERT(pip != PipId()); + if (ice40_pip_hard_unavail(pip)) + return WireId(); + return switches_locked[chip_info->pip_data[pip.index].switch_index]; } + NetInfo *getConflictingPipNet(PipId pip) const + { + if (ice40_pip_hard_unavail(pip)) + return nullptr; + + WireId wire = switches_locked[chip_info->pip_data[pip.index].switch_index]; + return wire == WireId() ? nullptr : wire_to_net[wire.index]; + } + AllPipRange getPips() const { AllPipRange range; From f9a512633840a88d8c846f1387681c62161ea6a7 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 11 Nov 2018 17:50:42 +0100 Subject: [PATCH 19/29] Another router1 bugfix Signed-off-by: Clifford Wolf --- common/router1.cc | 115 ++++++++++++++++++++++------------------------ common/router1.h | 3 +- 2 files changed, 55 insertions(+), 63 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 64f01c0d..ad2b6757 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -459,77 +459,72 @@ struct Router1 WireId conflictWireWire = WireId(), conflictPipWire = WireId(); NetInfo *conflictWireNet = nullptr, *conflictPipNet = nullptr; - bool wire_reuse = net_info->wires.count(next_wire); - bool pip_reuse = wire_reuse && net_info->wires.at(next_wire).pip == pip; - - if (!ctx->checkWireAvail(next_wire) && !wire_reuse) { - if (!ripup) - continue; - conflictWireWire = ctx->getConflictingWireWire(next_wire); - if (conflictWireWire == WireId()) { - conflictWireNet = ctx->getConflictingWireNet(next_wire); - if (conflictWireNet == nullptr) + if (net_info->wires.count(next_wire) && net_info->wires.at(next_wire).pip == pip) { + next_bonus += cfg.reuseBonus; + } else { + if (!ctx->checkWireAvail(next_wire)) { + if (!ripup) continue; + conflictWireWire = ctx->getConflictingWireWire(next_wire); + if (conflictWireWire == WireId()) { + conflictWireNet = ctx->getConflictingWireNet(next_wire); + if (conflictWireNet == nullptr) + continue; + } } - } - if (!ctx->checkPipAvail(pip) && !pip_reuse) { - if (!ripup) - continue; - conflictPipWire = ctx->getConflictingPipWire(pip); - if (conflictPipWire == WireId()) { - conflictPipNet = ctx->getConflictingPipNet(pip); - if (conflictPipNet == nullptr) + if (!ctx->checkPipAvail(pip)) { + if (!ripup) continue; + conflictPipWire = ctx->getConflictingPipWire(pip); + if (conflictPipWire == WireId()) { + conflictPipNet = ctx->getConflictingPipNet(pip); + if (conflictPipNet == nullptr) + continue; + } } - } - if (conflictWireNet != nullptr && conflictPipWire != WireId() && conflictWireNet->wires.count(conflictPipWire)) - conflictPipWire = WireId(); + if (conflictWireNet != nullptr && conflictPipWire != WireId() && conflictWireNet->wires.count(conflictPipWire)) + conflictPipWire = WireId(); - if (conflictPipNet != nullptr && conflictWireWire != WireId() && conflictPipNet->wires.count(conflictWireWire)) - conflictWireWire = WireId(); + if (conflictPipNet != nullptr && conflictWireWire != WireId() && conflictPipNet->wires.count(conflictWireWire)) + conflictWireWire = WireId(); - if (conflictWireWire == conflictPipWire) - conflictWireWire = WireId(); + if (conflictWireWire == conflictPipWire) + conflictWireWire = WireId(); - if (conflictWireNet == conflictPipNet) - conflictWireNet = nullptr; + if (conflictWireNet == conflictPipNet) + conflictWireNet = nullptr; - if (wire_reuse) - next_bonus += cfg.wireReuseBonus; + if (conflictWireWire != WireId()) { + auto scores_it = wireScores.find(conflictWireWire); + if (scores_it != wireScores.end()) + next_penalty += scores_it->second * cfg.wireRipupPenalty; + next_penalty += cfg.wireRipupPenalty; + } - if (pip_reuse) - next_bonus += cfg.pipReuseBonus; + if (conflictPipWire != WireId()) { + auto scores_it = wireScores.find(conflictPipWire); + if (scores_it != wireScores.end()) + next_penalty += scores_it->second * cfg.wireRipupPenalty; + next_penalty += cfg.wireRipupPenalty; + } - if (conflictWireWire != WireId()) { - auto scores_it = wireScores.find(conflictWireWire); - if (scores_it != wireScores.end()) - next_penalty += scores_it->second * cfg.wireRipupPenalty; - next_penalty += cfg.wireRipupPenalty; - } + if (conflictWireNet != nullptr) { + auto scores_it = netScores.find(conflictWireNet); + if (scores_it != netScores.end()) + next_penalty += scores_it->second * cfg.netRipupPenalty; + next_penalty += cfg.netRipupPenalty; + next_penalty += conflictWireNet->wires.size() * cfg.wireRipupPenalty; + } - if (conflictPipWire != WireId()) { - auto scores_it = wireScores.find(conflictPipWire); - if (scores_it != wireScores.end()) - next_penalty += scores_it->second * cfg.wireRipupPenalty; - next_penalty += cfg.wireRipupPenalty; - } - - if (conflictWireNet != nullptr) { - auto scores_it = netScores.find(conflictWireNet); - if (scores_it != netScores.end()) - next_penalty += scores_it->second * cfg.netRipupPenalty; - next_penalty += cfg.netRipupPenalty; - next_penalty += conflictWireNet->wires.size() * cfg.wireRipupPenalty; - } - - if (conflictPipNet != nullptr) { - auto scores_it = netScores.find(conflictPipNet); - if (scores_it != netScores.end()) - next_penalty += scores_it->second * cfg.netRipupPenalty; - next_penalty += cfg.netRipupPenalty; - next_penalty += conflictPipNet->wires.size() * cfg.wireRipupPenalty; + if (conflictPipNet != nullptr) { + auto scores_it = netScores.find(conflictPipNet); + if (scores_it != netScores.end()) + next_penalty += scores_it->second * cfg.netRipupPenalty; + next_penalty += cfg.netRipupPenalty; + next_penalty += conflictPipNet->wires.size() * cfg.wireRipupPenalty; + } } delay_t next_score = next_delay + next_penalty; @@ -676,9 +671,7 @@ Router1Cfg::Router1Cfg(Context *ctx) : Settings(ctx) wireRipupPenalty = ctx->getRipupDelayPenalty(); netRipupPenalty = 10*ctx->getRipupDelayPenalty(); - - wireReuseBonus = wireRipupPenalty/8; - pipReuseBonus = wireRipupPenalty/2; + reuseBonus = wireRipupPenalty/2; estimatePrecision = 100 * ctx->getRipupDelayPenalty(); } diff --git a/common/router1.h b/common/router1.h index d6113441..80d7aa96 100644 --- a/common/router1.h +++ b/common/router1.h @@ -35,8 +35,7 @@ struct Router1Cfg : Settings bool useEstimate; delay_t wireRipupPenalty; delay_t netRipupPenalty; - delay_t wireReuseBonus; - delay_t pipReuseBonus; + delay_t reuseBonus; delay_t estimatePrecision; }; From 6002a0a80ad2b7a300ea6cd3427dd942012de7d2 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 11 Nov 2018 19:48:15 +0100 Subject: [PATCH 20/29] clangformat Signed-off-by: Clifford Wolf --- common/router1.cc | 408 ++++++++++++++++++++++---------------------- ecp5/arch.h | 10 +- gui/designwidget.cc | 8 +- ice40/arch.h | 5 +- 4 files changed, 215 insertions(+), 216 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index ad2b6757..41818800 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -33,15 +33,13 @@ 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); } struct Hash { std::size_t operator()(const arc_key &arg) const noexcept { - std::size_t seed = std::hash()(arg.net_info); + std::size_t seed = std::hash()(arg.net_info); seed ^= std::hash()(arg.user_idx) + 0x9e3779b9 + (seed << 6) + (seed >> 2); return seed; } @@ -55,10 +53,7 @@ struct arc_entry struct Greater { - bool operator()(const arc_entry &lhs, const arc_entry &rhs) const noexcept - { - return lhs.pri > rhs.pri; - } + bool operator()(const arc_entry &lhs, const arc_entry &rhs) const noexcept { return lhs.pri > rhs.pri; } }; }; @@ -99,13 +94,13 @@ struct Router1 std::priority_queue, QueuedWire::Greater> queue; std::unordered_map wireScores; - std::unordered_map netScores; + std::unordered_map 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) { @@ -245,15 +240,17 @@ struct Router1 { std::unordered_set valid_arcs; - for (auto &net_it : ctx->nets) - { + for (auto &net_it : ctx->nets) { NetInfo *net_info = net_it.second.get(); std::unordered_set 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", net_info->name.c_str(ctx)); +#endif auto src_wire = ctx->getNetinfoSourceWire(net_info); log_assert(src_wire != WireId()); @@ -267,10 +264,17 @@ 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->getWireName(src_wire).c_str(ctx), + ctx->getWireName(dst_wire).c_str(ctx)); +#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->getWireName(wire).c_str(ctx)); +#endif valid_wires_for_net.insert(wire); log_assert(wire_to_arcs[wire].count(arc)); log_assert(net_info->wires.count(wire)); @@ -295,11 +299,10 @@ struct Router1 void setup() { - std::unordered_map src_to_net; + std::unordered_map src_to_net; std::unordered_map dst_to_arc; - for (auto &net_it : ctx->nets) - { + for (auto &net_it : ctx->nets) { NetInfo *net_info = net_it.second.get(); if (skip_net(net_info)) @@ -316,26 +319,30 @@ struct Router1 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->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); 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), + 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)); 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->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); } 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->getWireName(dst_wire).c_str(ctx), ctx->nameOf(src_to_net.at(dst_wire)), + ctx->nameOf(net_info), user_idx); arc_key arc; arc.net_info = net_info; @@ -390,7 +397,8 @@ 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("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)); } @@ -443,8 +451,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 +491,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) @@ -562,7 +571,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; @@ -583,7 +592,7 @@ struct Router1 if (next_wire == dst_wire) { if (maxVisitCnt == INT_MAX) - maxVisitCnt = 2*visitCnt; + maxVisitCnt = 2 * visitCnt; best_score = next_score - next_bonus; } } @@ -615,8 +624,8 @@ struct Router1 if (ctx->debug) log(" node %s\n", ctx->getWireName(cursor).c_str(ctx)); - if (pip == PipId()) - NPNR_ASSERT(cursor == src_wire); + 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)) { @@ -670,8 +679,8 @@ Router1Cfg::Router1Cfg(Context *ctx) : Settings(ctx) useEstimate = get("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 +711,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 +727,8 @@ 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, + arc.net_info->name.c_str(ctx)); #ifndef NDEBUG router.check(); ctx->check(); @@ -730,10 +738,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 +765,166 @@ 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(); + if (ctx->debug) + log("checking net %s\n", net_info->name.c_str(ctx)); + + 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 children; - }; - - std::unordered_map 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 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 setOrderNum; - std::unordered_set 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 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 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 children; + }; + + std::unordered_map 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 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 setOrderNum; + std::unordered_set 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 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 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; + } + + return true; } bool Context::getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay, diff --git a/ecp5/arch.h b/ecp5/arch.h index bd4881c2..9daae11d 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -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 { diff --git a/gui/designwidget.cc b/gui/designwidget.cc index 8a134c1f..4a1d5a8f 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -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), diff --git a/ice40/arch.h b/ice40/arch.h index 2967ee1f..b992d192 100644 --- a/ice40/arch.h +++ b/ice40/arch.h @@ -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 { From e0fe52360621a51dc07f005dbe461db21c07f276 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 12 Nov 2018 11:23:31 +0000 Subject: [PATCH 21/29] Fix router1 check for ECP5 Signed-off-by: David Shah --- common/router1.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/router1.cc b/common/router1.cc index 41818800..7eb02370 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -770,6 +770,11 @@ bool Context::checkRoutedDesign() const 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", net_info->name.c_str(ctx)); From 06e0e1ffeec9b06cecc213728c279b9235316df9 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 13 Nov 2018 05:03:46 +0100 Subject: [PATCH 22/29] Various router1 fixes, Add BelId/WireId/PipId::operator<() Signed-off-by: Clifford Wolf --- common/router1.cc | 75 +++++++++++++++++++++++++++++++++++++++-------- docs/archapi.md | 6 ++-- ecp5/archdefs.h | 4 +++ ice40/archdefs.h | 3 ++ 4 files changed, 72 insertions(+), 16 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 7eb02370..0814514d 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -34,6 +34,7 @@ struct arc_key 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 : net_info->name < other.net_info->name; } struct Hash { @@ -50,10 +51,16 @@ 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; } + bool operator()(const arc_entry &lhs, const arc_entry &rhs) const noexcept + { + if (lhs.pri != rhs.pri) + return lhs.pri < rhs.pri; + return lhs.randtag < rhs.randtag; + } }; }; @@ -85,7 +92,7 @@ struct Router1 Context *ctx; const Router1Cfg &cfg; - std::priority_queue, arc_entry::Greater> arc_queue; + std::priority_queue, arc_entry::Less> arc_queue; std::unordered_map> wire_to_arcs; std::unordered_map, arc_key::Hash> arc_to_wires; std::unordered_set queued_arcs; @@ -112,6 +119,14 @@ 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->getWireName(src_wire).c_str(ctx), ctx->getWireName(dst_wire).c_str(ctx), (int)entry.pri, + entry.randtag); +#endif arc_queue.push(entry); queued_arcs.insert(arc); @@ -134,6 +149,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; @@ -146,16 +168,25 @@ struct Router1 netScores[net]++; - auto net_wires_copy = net->wires; - for (auto &it : net_wires_copy) { - WireId w = it.first; + std::vector wires; + for (auto &it : net->wires) + wires.push_back(it.first); + ctx->sorted_shuffle(wires); + + for (WireId w : wires) { + std::vector 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)); @@ -178,12 +209,18 @@ struct Router1 if (n != nullptr) ripup_net(n); } else { + std::vector 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)); @@ -206,12 +243,18 @@ struct Router1 if (n != nullptr) ripup_net(n); } else { + std::vector 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)); @@ -302,8 +345,14 @@ struct Router1 std::unordered_map src_to_net; std::unordered_map dst_to_arc; - for (auto &net_it : ctx->nets) { - NetInfo *net_info = net_it.second.get(); + std::vector net_names; + for (auto &net_it : ctx->nets) + 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; @@ -591,8 +640,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; } } @@ -611,6 +659,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) diff --git a/docs/archapi.md b/docs/archapi.md index 85bc6ccd..40eabd9d 100644 --- a/docs/archapi.md +++ b/docs/archapi.md @@ -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`. +A type representing a bel name. `BelId()` 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`. +A type representing a wire name. `WireId()` 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`. +A type representing a pip name. `PipId()` must construct a unique null-value. Must provide `==`, `!=`, and `<` operators and a specialization for `std::hash`. ### GroupId diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index b85852c2..01cbad46 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -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 diff --git a/ice40/archdefs.h b/ice40/archdefs.h index c04033e7..b9614c07 100644 --- a/ice40/archdefs.h +++ b/ice40/archdefs.h @@ -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 From 710ea1b265ec20b71afb851bca2774b12e735f74 Mon Sep 17 00:00:00 2001 From: Pedro Vanzella Date: Tue, 13 Nov 2018 11:03:48 -0200 Subject: [PATCH 23/29] Mark getArchOptions as override in derived classes --- ecp5/main.cc | 2 +- generic/main.cc | 2 +- ice40/main.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ecp5/main.cc b/ecp5/main.cc index c444f96f..cc004df3 100644 --- a/ecp5/main.cc +++ b/ecp5/main.cc @@ -40,7 +40,7 @@ class ECP5CommandHandler : public CommandHandler void customBitstream(Context *ctx) override; protected: - po::options_description getArchOptions(); + po::options_description getArchOptions() override; }; ECP5CommandHandler::ECP5CommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {} diff --git a/generic/main.cc b/generic/main.cc index 412a28ac..08b0b348 100644 --- a/generic/main.cc +++ b/generic/main.cc @@ -37,7 +37,7 @@ class GenericCommandHandler : public CommandHandler void customBitstream(Context *ctx) override; protected: - po::options_description getArchOptions(); + po::options_description getArchOptions() override; }; GenericCommandHandler::GenericCommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {} diff --git a/ice40/main.cc b/ice40/main.cc index 8bab360d..fcc56d04 100644 --- a/ice40/main.cc +++ b/ice40/main.cc @@ -43,7 +43,7 @@ class Ice40CommandHandler : public CommandHandler void customBitstream(Context *ctx) override; protected: - po::options_description getArchOptions(); + po::options_description getArchOptions() override; }; Ice40CommandHandler::Ice40CommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {} From 959d163ba71fd2fdf08911e9be47f4f1d60514e5 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 13 Nov 2018 14:27:23 +0000 Subject: [PATCH 24/29] ecp5: Improve delay estimates Signed-off-by: David Shah --- ecp5/arch.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 4a0b31b5..ffd6ebcd 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -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; } From 23218b3378ec57915f321e22ae7060ea454b65aa Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 13 Nov 2018 16:08:04 +0100 Subject: [PATCH 25/29] Add some architecture API FAQ items Signed-off-by: Clifford Wolf --- docs/faq.md | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index d440bba6..63869240 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -38,7 +38,93 @@ 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.* 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 ----------------------- From e06eef375cf62ffd258a702a7275cd0da0e67382 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 13 Nov 2018 16:08:44 +0100 Subject: [PATCH 26/29] Add more nameOf() convenience methods Signed-off-by: Clifford Wolf --- common/nextpnr.cc | 24 ++++++++++++++++++++++++ common/nextpnr.h | 14 ++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/common/nextpnr.cc b/common/nextpnr.cc index 2a581cc6..903ab9e4 100644 --- a/common/nextpnr.cc +++ b/common/nextpnr.cc @@ -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) diff --git a/common/nextpnr.h b/common/nextpnr.h index 5a0bd4b1..86e781ae 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -487,13 +487,23 @@ struct BaseCtx const Context *getCtx() const { return reinterpret_cast(this); } - template const char *nameOf(const T *obj) + const char *nameOf(IdString name) const + { + return name.c_str(this); + } + + template 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; From 51b09f2407549ced10edc831ac5d0787e492713c Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 13 Nov 2018 16:29:33 +0100 Subject: [PATCH 27/29] Improve router1 debug output, switch to nameOf APIs Signed-off-by: Clifford Wolf --- common/router1.cc | 85 ++++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 0814514d..0481a95e 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -124,8 +124,7 @@ struct Router1 #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->getWireName(src_wire).c_str(ctx), ctx->getWireName(dst_wire).c_str(ctx), (int)entry.pri, - entry.randtag); + ctx->nameOfWire(src_wire), ctx->nameOfWire(dst_wire), (int)entry.pri, entry.randtag); #endif arc_queue.push(entry); @@ -164,7 +163,7 @@ 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]++; @@ -188,7 +187,7 @@ struct Router1 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]++; @@ -200,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); @@ -222,7 +221,7 @@ struct Router1 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]++; @@ -234,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); @@ -256,7 +255,7 @@ struct Router1 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]++; @@ -292,7 +291,7 @@ struct Router1 #if 0 if (ctx->debug) - log("[check] net: %s\n", net_info->name.c_str(ctx)); + log("[check] net: %s\n", ctx->nameOf(net_info)); #endif auto src_wire = ctx->getNetinfoSourceWire(net_info); @@ -309,14 +308,13 @@ struct Router1 valid_arcs.insert(arc); #if 0 if (ctx->debug) - log("[check] arc: %s %s\n", ctx->getWireName(src_wire).c_str(ctx), - ctx->getWireName(dst_wire).c_str(ctx)); + log("[check] arc: %s %s\n", ctx->nameOfWire(src_wire), ctx->nameOfWire(dst_wire)); #endif for (WireId wire : arc_to_wires[arc]) { #if 0 if (ctx->debug) - log("[check] wire: %s\n", ctx->getWireName(wire).c_str(ctx)); + log("[check] wire: %s\n", ctx->nameOfWire(wire)); #endif valid_wires_for_net.insert(wire); log_assert(wire_to_arcs[wire].count(arc)); @@ -360,16 +358,16 @@ 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->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++) { @@ -377,20 +375,20 @@ struct Router1 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)); + 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->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->nameOfWire(dst_wire), ctx->nameOf(src_to_net.at(dst_wire)), ctx->nameOf(net_info), user_idx); arc_key arc; @@ -446,10 +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), + 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->getWireName(src_wire).c_str(ctx)); - log(" sink ..... %s\n", ctx->getWireName(dst_wire).c_str(ctx)); + 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 @@ -463,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); } } @@ -603,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), @@ -630,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 @@ -667,11 +665,17 @@ struct Router1 std::unordered_set unassign_wires = arc_to_wires[arc]; WireId cursor = dst_wire; + delay_t accumulated_path_delay = 0; while (1) { auto pip = visited[cursor].pip; - if (ctx->debug) - log(" node %s\n", ctx->getWireName(cursor).c_str(ctx)); + if (ctx->debug) { + log(" node %s (%+.1f)\n", ctx->nameOfWire(cursor), + ctx->getDelayNS(ctx->estimateDelay(cursor, dst_wire)) - ctx->getDelayNS(accumulated_path_delay)); + 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); @@ -689,11 +693,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); } } @@ -776,8 +780,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(); @@ -825,7 +828,7 @@ bool Context::checkRoutedDesign() const #endif if (ctx->debug) - log("checking net %s\n", net_info->name.c_str(ctx)); + log("checking net %s\n", ctx->nameOf(net_info)); if (net_info->users.empty()) { if (ctx->debug) @@ -861,7 +864,7 @@ bool Context::checkRoutedDesign() const 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)); + log(" source (%s) not bound to net\n", ctx->nameOfWire(src_wire)); found_unrouted = true; } @@ -873,7 +876,7 @@ bool Context::checkRoutedDesign() const 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)); + log(" sink %d (%s) not bound to net\n", user_idx, ctx->nameOfWire(dst_wire)); found_unrouted = true; } } @@ -891,7 +894,7 @@ bool Context::checkRoutedDesign() const 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)); + log(" %*s-> %s\n", 2 * num, "", ctx->nameOfWire(child)); logged_wires.insert(child); } setOrderNum(child, num + 1); @@ -909,7 +912,7 @@ bool Context::checkRoutedDesign() const }; if (ctx->debug) { - log(" driver: %s\n", ctx->getWireName(src_wire).c_str(ctx)); + log(" driver: %s\n", ctx->nameOfWire(src_wire)); logged_wires.insert(src_wire); } setOrderNum(src_wire, 1); @@ -934,7 +937,7 @@ bool Context::checkRoutedDesign() const } for (WireId w : root_wires) { - log(" dangling wire: %s\n", ctx->getWireName(w).c_str(ctx)); + log(" dangling wire: %s\n", ctx->nameOfWire(w)); logged_wires.insert(w); setOrderNum(w, 1); } @@ -942,8 +945,8 @@ bool Context::checkRoutedDesign() const 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)); + ctx->nameOfWire(ctx->getPipSrcWire(net_info->wires.at(w).pip)), + ctx->nameOfWire(w)); } } } From caca485cfff7f999a19e86e2f00187550b0c92f4 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 13 Nov 2018 17:30:49 +0100 Subject: [PATCH 28/29] Minor router1 debug log improvements Signed-off-by: Clifford Wolf --- common/router1.cc | 10 ++++++++-- docs/faq.md | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/common/router1.cc b/common/router1.cc index 0481a95e..adad37e9 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -666,12 +666,18 @@ struct Router1 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 (%+.1f)\n", ctx->nameOfWire(cursor), - ctx->getDelayNS(ctx->estimateDelay(cursor, dst_wire)) - ctx->getDelayNS(accumulated_path_delay)); + delay_t path_delay_delta = ctx->estimateDelay(cursor, dst_wire) - accumulated_path_delay; + + 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(); diff --git a/docs/faq.md b/docs/faq.md index 63869240..7b358187 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -46,7 +46,8 @@ 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.* Otherwise there will be performance issues with the router. +*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. From 7402a4b95547bc802b6fa3e76844b35097d7e33d Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Tue, 13 Nov 2018 09:26:28 -0800 Subject: [PATCH 29/29] [placer1] Tidy up logic --- common/place_common.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/place_common.cc b/common/place_common.cc index 95343dc0..1c262c6f 100644 --- a/common/place_common.cc +++ b/common/place_common.cc @@ -37,7 +37,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type if (driver_gb) return 0; IdString clock_port; - bool driver_tmg_ignore = ctx->getPortTimingClass(driver_cell, net->driver.port, clock_port) == TMG_IGNORE; + bool timing_driven = ctx->timing_driven && type == MetricType::COST && ctx->getPortTimingClass(driver_cell, net->driver.port, clock_port) != TMG_IGNORE; delay_t negative_slack = 0; delay_t worst_slack = std::numeric_limits::max(); Loc driver_loc = ctx->getBelLocation(driver_cell->bel); @@ -48,7 +48,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type CellInfo *load_cell = load.cell; if (load_cell->bel == BelId()) continue; - if (!driver_tmg_ignore && ctx->timing_driven && type == MetricType::COST) { + if (timing_driven) { delay_t net_delay = ctx->predictDelay(net, load); auto slack = load.budget - net_delay; if (slack < 0) @@ -65,7 +65,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type xmax = std::max(xmax, load_loc.x); ymax = std::max(ymax, load_loc.y); } - if (ctx->timing_driven && type == MetricType::COST) { + if (timing_driven) { wirelength = wirelen_t( (((ymax - ymin) + (xmax - xmin)) * std::min(5.0, (1.0 + std::exp(-ctx->getDelayNS(worst_slack) / 5))))); } else {