From e91241f10d68fcaaf0a81fa77e9a91666120ccee Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Tue, 4 Sep 2018 17:55:43 +0200 Subject: [PATCH 01/44] 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/44] 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/44] 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/44] 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/44] 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 c6f66b4468d1b5f5762deae3e74e062f98f6fc18 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Fri, 9 Nov 2018 17:05:55 -0800 Subject: [PATCH 06/44] Adapt blinky for xc7 --- xc7/blinky.pcf | 2 ++ xc7/blinky.sh | 9 ++++----- xc7/blinky_tb.v | 24 ++++++++++++------------ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/xc7/blinky.pcf b/xc7/blinky.pcf index 7208a89d..1afc0f32 100644 --- a/xc7/blinky.pcf +++ b/xc7/blinky.pcf @@ -4,3 +4,5 @@ COMP "led2" LOCATE = SITE "G14" LEVEL 1; COMP "led3" LOCATE = SITE "D18" LEVEL 1; COMP "clki" LOCATE = SITE "K17" LEVEL 1; NET "clki" PERIOD = 8 nS ; +PIN "clki_pin" = BEL "clki$iob.PAD" PINNAME PAD; +PIN "clki_pin" CLOCK_DEDICATED_ROUTE = FALSE; diff --git a/xc7/blinky.sh b/xc7/blinky.sh index 9a247c62..cccc6c24 100755 --- a/xc7/blinky.sh +++ b/xc7/blinky.sh @@ -3,9 +3,8 @@ set -ex yosys blinky.ys ../nextpnr-xc7 --json blinky.json --pcf blinky.pcf --xdl blinky.xdl --freq 250 xdl -xdl2ncd blinky.xdl -trce blinky.ncd -v 10 bitgen -w blinky.ncd -g UnconstrainedPins:Allow -#icepack blinky.asc blinky.bin -#icebox_vlog blinky.asc > blinky_chip.v -#iverilog -o blinky_tb blinky_chip.v blinky_tb.v -#vvp -N ./blinky_tb +trce blinky.ncd -v 10 +netgen -ofmt verilog -w blinky.ncd blinky_chip.v -tm blinky +iverilog -o blinky_tb blinky_chip.v blinky_tb.v -y/opt/Xilinx/14.7/ISE_DS/ISE/verilog/src/simprims/ -insert_glbl true +vvp -N ./blinky_tb diff --git a/xc7/blinky_tb.v b/xc7/blinky_tb.v index f80b5e64..0378b679 100644 --- a/xc7/blinky_tb.v +++ b/xc7/blinky_tb.v @@ -2,24 +2,24 @@ module blinky_tb; reg clk; always #5 clk = (clk === 1'b0); - wire led1, led2, led3, led4, led5; + wire led0, led1, led2, led3; chip uut ( - .io_0_8_1(clk), - .io_13_12_1(led1), - .io_13_12_0(led2), - .io_13_11_1(led3), - .io_13_11_0(led4), - .io_13_9_1(led5) + .\clki$iob.PAD.PAD (clk), + .\led0$iob.OUTBUF.OUT (led0), + .\led1$iob.OUTBUF.OUT (led1), + .\led2$iob.OUTBUF.OUT (led2), + .\led3$iob.OUTBUF.OUT (led3) ); initial begin // $dumpfile("blinky_tb.vcd"); // $dumpvars(0, blinky_tb); - repeat (10) begin - repeat (900000) @(posedge clk); - $display(led1, led2, led3, led4, led5); - end - $finish; + $monitor(led0, led1, led2, led3); + //repeat (10) begin + // repeat (900000) @(posedge clk); + // $display(led0, led1, led2, led3); + //end + //$finish; end endmodule From 97070486f06c34e841ab4363c4bb148a2f75442c Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 10 Nov 2018 14:00:36 +0100 Subject: [PATCH 07/44] 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 08/44] 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 35765096849b7aacfde1bc69e68f12f525762ba2 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sat, 10 Nov 2018 12:51:56 -0800 Subject: [PATCH 09/44] Add support for PS7 blocks --- xc7/arch.cc | 13 ++++++++++++- xc7/constids.inc | 2 ++ xc7/xdl.cc | 3 ++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/xc7/arch.cc b/xc7/arch.cc index 65c8bee1..06b138ac 100644 --- a/xc7/arch.cc +++ b/xc7/arch.cc @@ -432,7 +432,8 @@ WireId Arch::getBelPinWire(BelId bel, IdString pin) const WireId ret; auto pin_name = pin.str(this); - if (getBelType(bel) == id_SLICE_LUT6) { + auto bel_type = getBelType(bel); + if (bel_type == id_SLICE_LUT6) { // For all LUT based inputs and outputs (I1-I6,O,OQ,OMUX) then change the I/O into the LUT if (pin_name[0] == 'I' || pin_name[0] == 'O') { switch (torc_info->bel_to_loc[bel.index].z) { @@ -457,6 +458,12 @@ WireId Arch::getBelPinWire(BelId bel, IdString pin) const } } } + else if (bel_type == id_PS7) { + // e.g. Convert DDRARB[0] -> DDRARB0 + boost::erase_all(pin_name, "["); + boost::erase_all(pin_name, "]"); + } + auto site_index = torc_info->bel_to_site_index[bel.index]; const auto &site = torc_info->sites.getSite(site_index); auto &tw = site.getPinTilewire(pin_name); @@ -996,6 +1003,10 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id return TMG_COMB_OUTPUT; return TMG_COMB_INPUT; } + else if (cell->type == id_PS7) { + // TODO + return TMG_IGNORE; + } log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this)); } diff --git a/xc7/constids.inc b/xc7/constids.inc index 0be998f6..bf0e7114 100644 --- a/xc7/constids.inc +++ b/xc7/constids.inc @@ -440,6 +440,7 @@ X(DFF_ENABLE) X(CARRY_ENABLE) X(NEG_CLK) +// XC7 X(I) X(LUT1) @@ -457,3 +458,4 @@ X(FDPE) X(BUFGCTRL) X(SLICE_LUT6) X(IOB33) +X(PS7) diff --git a/xc7/xdl.cc b/xc7/xdl.cc index a83297d8..97a18538 100644 --- a/xc7/xdl.cc +++ b/xc7/xdl.cc @@ -68,7 +68,7 @@ void write_xdl(const Context *ctx, std::ostream &out) const char *type; if (cell.second->type == id_SLICE_LUT6) type = "SLICEL"; - else if (cell.second->type == id_IOB33 || cell.second->type == id_BUFGCTRL) + else if (cell.second->type == id_IOB33 || cell.second->type == id_BUFGCTRL || cell.second->type == id_PS7) type = cell.second->type.c_str(ctx); else log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx)); @@ -205,6 +205,7 @@ void write_xdl(const Context *ctx, std::ostream &out) instPtr->setConfig("CE1INV", "", "CE1"); instPtr->setConfig("S1INV", "", "S1"); instPtr->setConfig("IGNORE1INV", "", "IGNORE1"); + } else if (cell.second->type == id_PS7) { } else log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx)); } From 3b86c3a3819657e84f8091a320fc07aafbeee84a Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sat, 10 Nov 2018 12:52:52 -0800 Subject: [PATCH 10/44] blinky to instantiate PS7 so that reconfiguring from Linux doesn't kill PS --- xc7/blinky.v | 1 + xc7/ps7.vh | 328 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 329 insertions(+) create mode 100644 xc7/ps7.vh diff --git a/xc7/blinky.v b/xc7/blinky.v index 7b5aae53..cab84984 100644 --- a/xc7/blinky.v +++ b/xc7/blinky.v @@ -5,6 +5,7 @@ module blinky ( output led2, output led3 ); + `include "ps7.vh" BUFGCTRL clk_gb ( .I0(clki), diff --git a/xc7/ps7.vh b/xc7/ps7.vh new file mode 100644 index 00000000..445e67aa --- /dev/null +++ b/xc7/ps7.vh @@ -0,0 +1,328 @@ +(* keep *) +PS7 ps7_stub( + .DDRARB ('b0), + .DMA0ACLK ('b0), + .DMA0DAREADY ('b0), + .DMA0DRLAST ('b0), + .DMA0DRTYPE ('b0), + .DMA0DRVALID ('b0), + .DMA1ACLK ('b0), + .DMA1DAREADY ('b0), + .DMA1DRLAST ('b0), + .DMA1DRTYPE ('b0), + .DMA1DRVALID ('b0), + .DMA2ACLK ('b0), + .DMA2DAREADY ('b0), + .DMA2DRLAST ('b0), + .DMA2DRTYPE ('b0), + .DMA2DRVALID ('b0), + .DMA3ACLK ('b0), + .DMA3DAREADY ('b0), + .DMA3DRLAST ('b0), + .DMA3DRTYPE ('b0), + .DMA3DRVALID ('b0), + .EMIOCAN0PHYRX ('b0), + .EMIOCAN1PHYRX ('b0), + .EMIOENET0EXTINTIN ('b0), + .EMIOENET0GMIICOL ('b0), + .EMIOENET0GMIICRS ('b0), + .EMIOENET0GMIIRXCLK ('b0), + .EMIOENET0GMIIRXD ('b0), + .EMIOENET0GMIIRXDV ('b0), + .EMIOENET0GMIIRXER ('b0), + .EMIOENET0GMIITXCLK ('b0), + .EMIOENET0MDIOI ('b0), + .EMIOENET1EXTINTIN ('b0), + .EMIOENET1GMIICOL ('b0), + .EMIOENET1GMIICRS ('b0), + .EMIOENET1GMIIRXCLK ('b0), + .EMIOENET1GMIIRXD ('b0), + .EMIOENET1GMIIRXDV ('b0), + .EMIOENET1GMIIRXER ('b0), + .EMIOENET1GMIITXCLK ('b0), + .EMIOENET1MDIOI ('b0), + .EMIOGPIOI ('b0), + .EMIOI2C0SCLI ('b0), + .EMIOI2C0SDAI ('b0), + .EMIOI2C1SCLI ('b0), + .EMIOI2C1SDAI ('b0), + .EMIOPJTAGTCK ('b0), + .EMIOPJTAGTDI ('b0), + .EMIOPJTAGTMS ('b0), + .EMIOSDIO0CDN ('b0), + .EMIOSDIO0CLKFB ('b0), + .EMIOSDIO0CMDI ('b0), + .EMIOSDIO0DATAI ('b0), + .EMIOSDIO0WP ('b0), + .EMIOSDIO1CDN ('b0), + .EMIOSDIO1CLKFB ('b0), + .EMIOSDIO1CMDI ('b0), + .EMIOSDIO1DATAI ('b0), + .EMIOSDIO1WP ('b0), + .EMIOSPI0MI ('b0), + .EMIOSPI0SCLKI ('b0), + .EMIOSPI0SI ('b0), + .EMIOSPI0SSIN ('b0), + .EMIOSPI1MI ('b0), + .EMIOSPI1SCLKI ('b0), + .EMIOSPI1SI ('b0), + .EMIOSPI1SSIN ('b0), + .EMIOSRAMINTIN ('b0), + .EMIOTRACECLK ('b0), + .EMIOTTC0CLKI ('b0), + .EMIOTTC1CLKI ('b0), + .EMIOUART0CTSN ('b0), + .EMIOUART0DCDN ('b0), + .EMIOUART0DSRN ('b0), + .EMIOUART0RIN ('b0), + .EMIOUART0RX ('b0), + .EMIOUART1CTSN ('b0), + .EMIOUART1DCDN ('b0), + .EMIOUART1DSRN ('b0), + .EMIOUART1RIN ('b0), + .EMIOUART1RX ('b0), + .EMIOUSB0VBUSPWRFAULT ('b0), + .EMIOUSB1VBUSPWRFAULT ('b0), + .EMIOWDTCLKI ('b0), + .EVENTEVENTI ('b0), + .FCLKCLKTRIGN ('b0), + .FPGAIDLEN ('b0), + .FTMDTRACEINATID ('b0), + .FTMDTRACEINCLOCK ('b0), + .FTMDTRACEINDATA ('b0), + .FTMDTRACEINVALID ('b0), + .FTMTF2PDEBUG ('b0), + .FTMTF2PTRIG ('b0), + .FTMTP2FTRIGACK ('b0), + .IRQF2P ('b0), + .MAXIGP0ACLK ('b0), + .MAXIGP0ARREADY ('b0), + .MAXIGP0AWREADY ('b0), + .MAXIGP0BID ('b0), + .MAXIGP0BRESP ('b0), + .MAXIGP0BVALID ('b0), + .MAXIGP0RDATA ('b0), + .MAXIGP0RID ('b0), + .MAXIGP0RLAST ('b0), + .MAXIGP0RRESP ('b0), + .MAXIGP0RVALID ('b0), + .MAXIGP0WREADY ('b0), + .MAXIGP1ACLK ('b0), + .MAXIGP1ARREADY ('b0), + .MAXIGP1AWREADY ('b0), + .MAXIGP1BID ('b0), + .MAXIGP1BRESP ('b0), + .MAXIGP1BVALID ('b0), + .MAXIGP1RDATA ('b0), + .MAXIGP1RID ('b0), + .MAXIGP1RLAST ('b0), + .MAXIGP1RRESP ('b0), + .MAXIGP1RVALID ('b0), + .MAXIGP1WREADY ('b0), + .SAXIACPACLK ('b0), + .SAXIACPARADDR ('b0), + .SAXIACPARBURST ('b0), + .SAXIACPARCACHE ('b0), + .SAXIACPARID ('b0), + .SAXIACPARLEN ('b0), + .SAXIACPARLOCK ('b0), + .SAXIACPARPROT ('b0), + .SAXIACPARQOS ('b0), + .SAXIACPARSIZE ('b0), + .SAXIACPARUSER ('b0), + .SAXIACPARVALID ('b0), + .SAXIACPAWADDR ('b0), + .SAXIACPAWBURST ('b0), + .SAXIACPAWCACHE ('b0), + .SAXIACPAWID ('b0), + .SAXIACPAWLEN ('b0), + .SAXIACPAWLOCK ('b0), + .SAXIACPAWPROT ('b0), + .SAXIACPAWQOS ('b0), + .SAXIACPAWSIZE ('b0), + .SAXIACPAWUSER ('b0), + .SAXIACPAWVALID ('b0), + .SAXIACPBREADY ('b0), + .SAXIACPRREADY ('b0), + .SAXIACPWDATA ('b0), + .SAXIACPWID ('b0), + .SAXIACPWLAST ('b0), + .SAXIACPWSTRB ('b0), + .SAXIACPWVALID ('b0), + .SAXIGP0ACLK ('b0), + .SAXIGP0ARADDR ('b0), + .SAXIGP0ARBURST ('b0), + .SAXIGP0ARCACHE ('b0), + .SAXIGP0ARID ('b0), + .SAXIGP0ARLEN ('b0), + .SAXIGP0ARLOCK ('b0), + .SAXIGP0ARPROT ('b0), + .SAXIGP0ARQOS ('b0), + .SAXIGP0ARSIZE ('b0), + .SAXIGP0ARVALID ('b0), + .SAXIGP0AWADDR ('b0), + .SAXIGP0AWBURST ('b0), + .SAXIGP0AWCACHE ('b0), + .SAXIGP0AWID ('b0), + .SAXIGP0AWLEN ('b0), + .SAXIGP0AWLOCK ('b0), + .SAXIGP0AWPROT ('b0), + .SAXIGP0AWQOS ('b0), + .SAXIGP0AWSIZE ('b0), + .SAXIGP0AWVALID ('b0), + .SAXIGP0BREADY ('b0), + .SAXIGP0RREADY ('b0), + .SAXIGP0WDATA ('b0), + .SAXIGP0WID ('b0), + .SAXIGP0WLAST ('b0), + .SAXIGP0WSTRB ('b0), + .SAXIGP0WVALID ('b0), + .SAXIGP1ACLK ('b0), + .SAXIGP1ARADDR ('b0), + .SAXIGP1ARBURST ('b0), + .SAXIGP1ARCACHE ('b0), + .SAXIGP1ARID ('b0), + .SAXIGP1ARLEN ('b0), + .SAXIGP1ARLOCK ('b0), + .SAXIGP1ARPROT ('b0), + .SAXIGP1ARQOS ('b0), + .SAXIGP1ARSIZE ('b0), + .SAXIGP1ARVALID ('b0), + .SAXIGP1AWADDR ('b0), + .SAXIGP1AWBURST ('b0), + .SAXIGP1AWCACHE ('b0), + .SAXIGP1AWID ('b0), + .SAXIGP1AWLEN ('b0), + .SAXIGP1AWLOCK ('b0), + .SAXIGP1AWPROT ('b0), + .SAXIGP1AWQOS ('b0), + .SAXIGP1AWSIZE ('b0), + .SAXIGP1AWVALID ('b0), + .SAXIGP1BREADY ('b0), + .SAXIGP1RREADY ('b0), + .SAXIGP1WDATA ('b0), + .SAXIGP1WID ('b0), + .SAXIGP1WLAST ('b0), + .SAXIGP1WSTRB ('b0), + .SAXIGP1WVALID ('b0), + .SAXIHP0ACLK ('b0), + .SAXIHP0ARADDR ('b0), + .SAXIHP0ARBURST ('b0), + .SAXIHP0ARCACHE ('b0), + .SAXIHP0ARID ('b0), + .SAXIHP0ARLEN ('b0), + .SAXIHP0ARLOCK ('b0), + .SAXIHP0ARPROT ('b0), + .SAXIHP0ARQOS ('b0), + .SAXIHP0ARSIZE ('b0), + .SAXIHP0ARVALID ('b0), + .SAXIHP0AWADDR ('b0), + .SAXIHP0AWBURST ('b0), + .SAXIHP0AWCACHE ('b0), + .SAXIHP0AWID ('b0), + .SAXIHP0AWLEN ('b0), + .SAXIHP0AWLOCK ('b0), + .SAXIHP0AWPROT ('b0), + .SAXIHP0AWQOS ('b0), + .SAXIHP0AWSIZE ('b0), + .SAXIHP0AWVALID ('b0), + .SAXIHP0BREADY ('b0), + .SAXIHP0RDISSUECAP1EN ('b0), + .SAXIHP0RREADY ('b0), + .SAXIHP0WDATA ('b0), + .SAXIHP0WID ('b0), + .SAXIHP0WLAST ('b0), + .SAXIHP0WRISSUECAP1EN ('b0), + .SAXIHP0WSTRB ('b0), + .SAXIHP0WVALID ('b0), + .SAXIHP1ACLK ('b0), + .SAXIHP1ARADDR ('b0), + .SAXIHP1ARBURST ('b0), + .SAXIHP1ARCACHE ('b0), + .SAXIHP1ARID ('b0), + .SAXIHP1ARLEN ('b0), + .SAXIHP1ARLOCK ('b0), + .SAXIHP1ARPROT ('b0), + .SAXIHP1ARQOS ('b0), + .SAXIHP1ARSIZE ('b0), + .SAXIHP1ARVALID ('b0), + .SAXIHP1AWADDR ('b0), + .SAXIHP1AWBURST ('b0), + .SAXIHP1AWCACHE ('b0), + .SAXIHP1AWID ('b0), + .SAXIHP1AWLEN ('b0), + .SAXIHP1AWLOCK ('b0), + .SAXIHP1AWPROT ('b0), + .SAXIHP1AWQOS ('b0), + .SAXIHP1AWSIZE ('b0), + .SAXIHP1AWVALID ('b0), + .SAXIHP1BREADY ('b0), + .SAXIHP1RDISSUECAP1EN ('b0), + .SAXIHP1RREADY ('b0), + .SAXIHP1WDATA ('b0), + .SAXIHP1WID ('b0), + .SAXIHP1WLAST ('b0), + .SAXIHP1WRISSUECAP1EN ('b0), + .SAXIHP1WSTRB ('b0), + .SAXIHP1WVALID ('b0), + .SAXIHP2ACLK ('b0), + .SAXIHP2ARADDR ('b0), + .SAXIHP2ARBURST ('b0), + .SAXIHP2ARCACHE ('b0), + .SAXIHP2ARID ('b0), + .SAXIHP2ARLEN ('b0), + .SAXIHP2ARLOCK ('b0), + .SAXIHP2ARPROT ('b0), + .SAXIHP2ARQOS ('b0), + .SAXIHP2ARSIZE ('b0), + .SAXIHP2ARVALID ('b0), + .SAXIHP2AWADDR ('b0), + .SAXIHP2AWBURST ('b0), + .SAXIHP2AWCACHE ('b0), + .SAXIHP2AWID ('b0), + .SAXIHP2AWLEN ('b0), + .SAXIHP2AWLOCK ('b0), + .SAXIHP2AWPROT ('b0), + .SAXIHP2AWQOS ('b0), + .SAXIHP2AWSIZE ('b0), + .SAXIHP2AWVALID ('b0), + .SAXIHP2BREADY ('b0), + .SAXIHP2RDISSUECAP1EN ('b0), + .SAXIHP2RREADY ('b0), + .SAXIHP2WDATA ('b0), + .SAXIHP2WID ('b0), + .SAXIHP2WLAST ('b0), + .SAXIHP2WRISSUECAP1EN ('b0), + .SAXIHP2WSTRB ('b0), + .SAXIHP2WVALID ('b0), + .SAXIHP3ACLK ('b0), + .SAXIHP3ARADDR ('b0), + .SAXIHP3ARBURST ('b0), + .SAXIHP3ARCACHE ('b0), + .SAXIHP3ARID ('b0), + .SAXIHP3ARLEN ('b0), + .SAXIHP3ARLOCK ('b0), + .SAXIHP3ARPROT ('b0), + .SAXIHP3ARQOS ('b0), + .SAXIHP3ARSIZE ('b0), + .SAXIHP3ARVALID ('b0), + .SAXIHP3AWADDR ('b0), + .SAXIHP3AWBURST ('b0), + .SAXIHP3AWCACHE ('b0), + .SAXIHP3AWID ('b0), + .SAXIHP3AWLEN ('b0), + .SAXIHP3AWLOCK ('b0), + .SAXIHP3AWPROT ('b0), + .SAXIHP3AWQOS ('b0), + .SAXIHP3AWSIZE ('b0), + .SAXIHP3AWVALID ('b0), + .SAXIHP3BREADY ('b0), + .SAXIHP3RDISSUECAP1EN ('b0), + .SAXIHP3RREADY ('b0), + .SAXIHP3WDATA ('b0), + .SAXIHP3WID ('b0), + .SAXIHP3WLAST ('b0), + .SAXIHP3WRISSUECAP1EN ('b0), + .SAXIHP3WSTRB ('b0), + .SAXIHP3WVALID ('b0) +); From d904a3713800409f376b78021b7d890c7c5f505f Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sat, 10 Nov 2018 23:50:08 +0100 Subject: [PATCH 11/44] 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 12/44] 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 13/44] 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 f6bdd9264088c1f6e5cd94ed5a852e0e3c9b2aac Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sat, 10 Nov 2018 16:11:38 -0800 Subject: [PATCH 14/44] Implement Arch::getPipName() and remove debug/verbose before routing --- xc7/arch.cc | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/xc7/arch.cc b/xc7/arch.cc index 06b138ac..fbfe5fdf 100644 --- a/xc7/arch.cc +++ b/xc7/arch.cc @@ -600,20 +600,26 @@ IdString Arch::getPipName(PipId pip) const { NPNR_ASSERT(pip != PipId()); -#if 1 - int x = chip_info->pip_data[pip.index].x; - int y = chip_info->pip_data[pip.index].y; + ExtendedWireInfo ewi_src(*torc_info->ddb, torc_info->pip_to_arc[pip.index].getSourceTilewire()); + ExtendedWireInfo ewi_dst(*torc_info->ddb, torc_info->pip_to_arc[pip.index].getSinkTilewire()); + std::stringstream pip_name; + pip_name << ewi_src.mTileName << "." << ewi_src.mWireName << ".->." << ewi_dst.mWireName; + return id(pip_name.str()); - std::string src_name = chip_info->wire_data[chip_info->pip_data[pip.index].src].name.get(); - std::replace(src_name.begin(), src_name.end(), '/', '.'); - - std::string dst_name = chip_info->wire_data[chip_info->pip_data[pip.index].dst].name.get(); - std::replace(dst_name.begin(), dst_name.end(), '/', '.'); - - return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + src_name + ".->." + dst_name); -#else - return id(chip_info->pip_data[pip.index].name.get()); -#endif +//#if 1 +// int x = chip_info->pip_data[pip.index].x; +// int y = chip_info->pip_data[pip.index].y; +// +// std::string src_name = chip_info->wire_data[chip_info->pip_data[pip.index].src].name.get(); +// std::replace(src_name.begin(), src_name.end(), '/', '.'); +// +// std::string dst_name = chip_info->wire_data[chip_info->pip_data[pip.index].dst].name.get(); +// std::replace(dst_name.begin(), dst_name.end(), '/', '.'); +// +// return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + src_name + ".->." + dst_name); +//#else +// return id(chip_info->pip_data[pip.index].name.get()); +//#endif } // ----------------------------------------------------------------------- @@ -774,8 +780,6 @@ bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); } bool Arch::route() { - getCtx()->debug = true; - getCtx()->verbose = true; return router1(getCtx(), Router1Cfg(getCtx())); } From 10d6db6d94873068809e26c9e3e724683f0339b1 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sat, 10 Nov 2018 18:48:46 -0800 Subject: [PATCH 15/44] Do not add "$iob" suffix --- xc7/pack.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xc7/pack.cc b/xc7/pack.cc index 7d7eb34d..d2130e72 100644 --- a/xc7/pack.cc +++ b/xc7/pack.cc @@ -421,7 +421,7 @@ static void pack_io(Context *ctx) } } else { // Create a IOBUF buffer - std::unique_ptr ice_cell = create_xc7_cell(ctx, ctx->id("IOBUF"), ci->name.str(ctx) + "$iob"); + std::unique_ptr ice_cell = create_xc7_cell(ctx, ctx->id("IOBUF"), ci->name.str(ctx)); nxio_to_sb(ctx, ci, ice_cell.get()); new_cells.push_back(std::move(ice_cell)); sb = new_cells.back().get(); From 533c73041863c580ee26b28bd1dc2f9eb98cdba4 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sat, 10 Nov 2018 18:49:58 -0800 Subject: [PATCH 16/44] Update without $iob suffix --- xc7/blinky.pcf | 2 +- xc7/blinky_tb.v | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/xc7/blinky.pcf b/xc7/blinky.pcf index 1afc0f32..6c2e5149 100644 --- a/xc7/blinky.pcf +++ b/xc7/blinky.pcf @@ -4,5 +4,5 @@ COMP "led2" LOCATE = SITE "G14" LEVEL 1; COMP "led3" LOCATE = SITE "D18" LEVEL 1; COMP "clki" LOCATE = SITE "K17" LEVEL 1; NET "clki" PERIOD = 8 nS ; -PIN "clki_pin" = BEL "clki$iob.PAD" PINNAME PAD; +PIN "clki_pin" = BEL "clki.PAD" PINNAME PAD; PIN "clki_pin" CLOCK_DEDICATED_ROUTE = FALSE; diff --git a/xc7/blinky_tb.v b/xc7/blinky_tb.v index 0378b679..069dbe9c 100644 --- a/xc7/blinky_tb.v +++ b/xc7/blinky_tb.v @@ -4,12 +4,12 @@ module blinky_tb; wire led0, led1, led2, led3; - chip uut ( - .\clki$iob.PAD.PAD (clk), - .\led0$iob.OUTBUF.OUT (led0), - .\led1$iob.OUTBUF.OUT (led1), - .\led2$iob.OUTBUF.OUT (led2), - .\led3$iob.OUTBUF.OUT (led3) + blinky uut ( + .\clki.PAD.PAD (clk), + .\led0.OUTBUF.OUT (led0), + .\led1.OUTBUF.OUT (led1), + .\led2.OUTBUF.OUT (led2), + .\led3.OUTBUF.OUT (led3) ); initial begin From a0c6c64be70eb5b874a9309d6da47bd275823947 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sat, 10 Nov 2018 18:50:43 -0800 Subject: [PATCH 17/44] blinky.v to not instantiate PS7, blink more slowly/predictably --- xc7/blinky.v | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xc7/blinky.v b/xc7/blinky.v index cab84984..ad8f1de7 100644 --- a/xc7/blinky.v +++ b/xc7/blinky.v @@ -5,7 +5,7 @@ module blinky ( output led2, output led3 ); - `include "ps7.vh" + //`include "ps7.vh" BUFGCTRL clk_gb ( .I0(clki), @@ -19,7 +19,7 @@ module blinky ( ); localparam BITS = 4; - localparam LOG2DELAY = 21; + localparam LOG2DELAY = 23; reg [BITS+LOG2DELAY-1:0] counter = 0; reg [BITS-1:0] outcnt; @@ -29,5 +29,5 @@ module blinky ( outcnt <= counter >> LOG2DELAY; end - assign {led0, led1, led2, led3} = outcnt ^ (outcnt >> 1); + assign {led0, led1, led2, led3} = outcnt /*^ (outcnt >> 1)*/; endmodule From 99f5836b0ed2d988855930085fc78476f732362f Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sat, 10 Nov 2018 19:55:22 -0800 Subject: [PATCH 18/44] Add some cell delays --- xc7/arch.cc | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/xc7/arch.cc b/xc7/arch.cc index fbfe5fdf..7600e3a1 100644 --- a/xc7/arch.cc +++ b/xc7/arch.cc @@ -963,8 +963,22 @@ std::vector Arch::getDecalGraphics(DecalId decal) const bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const { if (cell->type == id_SLICE_LUT6) { - if (fromPort.index >= id_I1.index && fromPort.index <= id_I6.index) - return toPort == id_O || toPort == id_OQ; + if (fromPort.index >= id_I1.index && fromPort.index <= id_I6.index) { + if (toPort == id_O) { + delay.delay = 124; // Tilo + return true; + } + if (toPort == id_OQ) { + delay.delay = 95; // Tas + return true; + } + } + if (fromPort == id_CLK) { + if (toPort == id_OQ) { + delay.delay = 456; // Tcko + return false; // No path CLK->OQ, but this fn is used for getting clkToQ delay + } + } } else if (cell->type == id_BUFGCTRL) { return true; } From 5cc9b9f61f3e01c05a8895640f6111fe73611fc3 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 11 Nov 2018 10:02:32 +0100 Subject: [PATCH 19/44] 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 20/44] 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 21/44] 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 22/44] 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 23/44] 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 24/44] 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 25/44] 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 83117bef66f5fbe27d98de8e30979e47d0a1ffbb Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sun, 11 Nov 2018 10:19:17 -0800 Subject: [PATCH 26/44] Add missing APIs needed for router_improve --- xc7/arch.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/xc7/arch.h b/xc7/arch.h index 9d8c4251..092cd5f6 100644 --- a/xc7/arch.h +++ b/xc7/arch.h @@ -677,6 +677,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()); @@ -773,6 +778,11 @@ struct Arch : BaseCtx return pip_to_net[pip.index]; } + WireId getConflictingPipWire(PipId pip) const + { + return WireId(); + } + NetInfo *getConflictingPipNet(PipId pip) const { NPNR_ASSERT(pip != PipId()); From 6002a0a80ad2b7a300ea6cd3427dd942012de7d2 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 11 Nov 2018 19:48:15 +0100 Subject: [PATCH 27/44] 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 dd85e11abb0f48a4e2cad17a061729e683ee613e Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sun, 11 Nov 2018 11:55:27 -0800 Subject: [PATCH 28/44] [timing] More verbosity for crit path report --- common/timing.cc | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/common/timing.cc b/common/timing.cc index 81ce7b73..10981454 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -405,7 +405,26 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_path) log_info("%4.1f %4.1f Net %s budget %f ns (%d,%d) -> (%d,%d)\n", ctx->getDelayNS(net_delay), ctx->getDelayNS(total), net->name.c_str(ctx), ctx->getDelayNS(sink->budget), driver_loc.x, driver_loc.y, sink_loc.x, sink_loc.y); - log_info(" Sink %s.%s\n", sink_cell->name.c_str(ctx), sink->port.c_str(ctx)); + log_info(" Sink %s.%s\n", sink_cell->name.c_str(ctx), sink->port.c_str(ctx)); + if (ctx->verbose) { + auto driver_wire = ctx->getNetinfoSourceWire(net); + auto sink_wire = ctx->getNetinfoSinkWire(net, *sink); + log_info(" prediction: %f ns estimate: %f ns\n", + ctx->getDelayNS(ctx->predictDelay(net, *sink)), ctx->getDelayNS(ctx->estimateDelay(driver_wire, sink_wire))); + auto cursor = sink_wire; + delay_t cursor_delay; + while (driver_wire != cursor) { + cursor_delay = ctx->getWireDelay(cursor).maxDelay(); + log_info(" %1.3f %30s\n", ctx->getDelayNS(cursor_delay), ctx->getWireName(cursor).c_str(ctx)); + auto it = net->wires.find(cursor); + assert(it != net->wires.end()); + auto pip = it->second.pip; + NPNR_ASSERT(pip != PipId()); + cursor = ctx->getPipSrcWire(pip); + } + cursor_delay = ctx->getWireDelay(cursor).maxDelay(); + log_info(" %1.3f %30s\n", ctx->getDelayNS(cursor_delay), ctx->getWireName(cursor).c_str(ctx)); + } last_port = sink->port; } log_break(); From 53f025c03f3e055d09ee643b531ff2cc243b0a08 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sun, 11 Nov 2018 12:56:52 -0800 Subject: [PATCH 29/44] Improved delay estimator --- xc7/blinky.sh | 11 ++++++++--- xc7/delay.cc | 46 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/xc7/blinky.sh b/xc7/blinky.sh index cccc6c24..b116b3ff 100755 --- a/xc7/blinky.sh +++ b/xc7/blinky.sh @@ -5,6 +5,11 @@ yosys blinky.ys xdl -xdl2ncd blinky.xdl bitgen -w blinky.ncd -g UnconstrainedPins:Allow trce blinky.ncd -v 10 -netgen -ofmt verilog -w blinky.ncd blinky_chip.v -tm blinky -iverilog -o blinky_tb blinky_chip.v blinky_tb.v -y/opt/Xilinx/14.7/ISE_DS/ISE/verilog/src/simprims/ -insert_glbl true -vvp -N ./blinky_tb + +#netgen -ofmt verilog -w blinky.ncd blinky_chip.v -tm blinky -insert_glbl true +#iverilog -o blinky_tb blinky_chip.v blinky_tb.v -y/opt/Xilinx/14.7/ISE_DS/ISE/verilog/src/simprims/ +#vvp -N ./blinky_tb + +#xdl -xdl2ncd blinky.xdl -nopips blinky_map.ncd +#par -w blinky_map.ncd blinky_par.ncd blinky.pcf +#bitgen -w blinky_par.ncd -g UnconstrainedPins:Allow diff --git a/xc7/delay.cc b/xc7/delay.cc index fe14a5ce..397bd483 100644 --- a/xc7/delay.cc +++ b/xc7/delay.cc @@ -104,8 +104,27 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const const auto &src_info = torc_info->tiles.getTileInfo(src_tw.getTileIndex()); const auto &dst_tw = torc_info->wire_to_tilewire[dst.index]; const auto &dst_info = torc_info->tiles.getTileInfo(dst_tw.getTileIndex()); - - return 100 * (abs(src_info.getCol() - dst_info.getCol()) + abs(src_info.getRow() - dst_info.getRow())); + auto abs_delta_x = abs(src_info.getCol() - dst_info.getCol()); + auto abs_delta_y = abs(src_info.getRow() - dst_info.getRow()); +#if 1 + auto div_LH = std::div(abs_delta_x, 12); + auto div_LV = std::div(abs_delta_y, 18); + auto div_LVB = std::div(div_LV.rem, 12); + auto div_H6 = std::div(div_LH.rem, 6); + auto div_V6 = std::div(div_LVB.rem, 6); + auto div_H4 = std::div(div_H6.rem, 4); + auto div_V4 = std::div(div_V6.rem, 4); + auto div_H2 = std::div(div_H4.rem, 2); + auto div_V2 = std::div(div_V4.rem, 2); + auto num_H1 = div_H2.rem; + auto num_V1 = div_V2.rem; + return div_LH.quot * 360/2 + div_LVB.quot * 300 + div_LV.quot * 350 + + (div_H6.quot + div_H4.quot) * 210/2 + (div_V6.quot + div_V4.quot) * 210 + + div_H2.quot * 170/2 + div_V2.quot * 170 + + num_H1 * 150/2 + num_V1 * 150; +#else + return std::max(150, 33 * abs_delta_x + 66 * abs_delta_y); +#endif } delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const @@ -113,8 +132,27 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const const auto &driver = net_info->driver; 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)); + auto abs_delta_x = abs(driver_loc.x - sink_loc.x); + auto abs_delta_y = abs(driver_loc.y - sink_loc.y); +#if 1 + auto div_LH = std::div(abs_delta_x, 12); + auto div_LV = std::div(abs_delta_y, 18); + auto div_LVB = std::div(div_LV.rem, 12); + auto div_H6 = std::div(div_LH.rem, 6); + auto div_V6 = std::div(div_LVB.rem, 6); + auto div_H4 = std::div(div_H6.rem, 4); + auto div_V4 = std::div(div_V6.rem, 4); + auto div_H2 = std::div(div_H4.rem, 2); + auto div_V2 = std::div(div_V4.rem, 2); + auto num_H1 = div_H2.rem; + auto num_V1 = div_V2.rem; + return div_LH.quot * 360/2 + div_LVB.quot * 300 + div_LV.quot * 350 + + (div_H6.quot + div_H4.quot) * 210/2 + (div_V6.quot + div_V4.quot) * 210 + + div_H2.quot * 170/2 + div_V2.quot * 170 + + num_H1 * 150/2 + num_V1 * 150; +#else + return std::max(150, 33 * abs_delta_x + 66 * abs_delta_y); +#endif } NEXTPNR_NAMESPACE_END From 75654a69f04bcc336a9d35e3a04ad8508a245551 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sun, 11 Nov 2018 14:15:11 -0800 Subject: [PATCH 30/44] Fix LUT input delays, speedup construct_wire_to_delay? --- xc7/arch.cc | 89 +++++++++++++++++++++++++++++++---------------------- xc7/arch.h | 2 +- 2 files changed, 54 insertions(+), 37 deletions(-) diff --git a/xc7/arch.cc b/xc7/arch.cc index 7600e3a1..13fad6e0 100644 --- a/xc7/arch.cc +++ b/xc7/arch.cc @@ -41,7 +41,7 @@ TorcInfo::TorcInfo(Arch *ctx, const std::string &inDeviceName, const std::string site_index_to_type(construct_site_index_to_type(ctx, sites)), bel_to_loc(construct_bel_to_loc(sites, tiles, num_bels, site_index_to_type)), wire_to_tilewire(construct_wire_to_tilewire(segments, tiles, segment_to_wire, trivial_to_wire)), - num_wires(wire_to_tilewire.size()), wire_to_delay(construct_wire_to_delay(wire_to_tilewire, *ddb)), + num_wires(wire_to_tilewire.size()), wire_to_delay(construct_wire_to_delay(tiles, wire_to_tilewire, *ddb)), pip_to_arc(construct_pip_to_arc(wire_to_tilewire, *ddb, wire_to_pips_uphill, wire_to_pips_downhill)), num_pips(pip_to_arc.size()) { @@ -163,7 +163,7 @@ TorcInfo::construct_wire_to_tilewire(const Segments &segments, const Tiles &tile wire_to_tilewire.shrink_to_fit(); return wire_to_tilewire; } -std::vector TorcInfo::construct_wire_to_delay(const std::vector &wire_to_tilewire, const DDB &ddb) +std::vector TorcInfo::construct_wire_to_delay(const Tiles &tiles, const std::vector &wire_to_tilewire, const DDB &ddb) { std::vector wire_to_delay; wire_to_delay.reserve(wire_to_tilewire.size()); @@ -174,45 +174,62 @@ std::vector TorcInfo::construct_wire_to_delay(const std::vector> delay_lookup; boost::cmatch what; - ExtendedWireInfo ewi(ddb); for (const auto &tw : wire_to_tilewire) { - ewi.set(tw); - DelayInfo d; - if (boost::regex_match(ewi.mWireName, what, re_124)) { - switch (what.str(2)[0]) { - case '1': - d.delay = 150; - break; - case '2': - d.delay = 170; - break; - case '4': - d.delay = 210; - break; - case '6': - d.delay = 210; - break; - default: - throw; + const TileInfo& tileInfo = tiles.getTileInfo(tw.getTileIndex()); + auto tile_type_index = tileInfo.getTypeIndex(); + + auto it = delay_lookup.find(tile_type_index); + if (it == delay_lookup.end()) { + auto wireCount = tiles.getWireCount(tile_type_index); + std::vector tile_delays(wireCount); + for (WireIndex wireIndex(0); wireIndex < wireCount; wireIndex++) { + const WireInfo& wireInfo = tiles.getWireInfo(tile_type_index, wireIndex); + auto wire_name = wireInfo.getName(); + if (boost::regex_match(wire_name, what, re_124)) { + switch (what.str(2)[0]) { + case '1': tile_delays[wireIndex] = 150; break; + case '2': tile_delays[wireIndex] = 170; break; + case '4': tile_delays[wireIndex] = 210; break; + case '6': tile_delays[wireIndex] = 210; break; + default: throw; + } + } else if (boost::regex_match(wire_name, what, re_L)) { + std::string l(what[2]); + if (l == "H") + tile_delays[wireIndex] = 360; + else if (l == "VB") + tile_delays[wireIndex] = 300; + else if (l == "V") + tile_delays[wireIndex] = 350; + else + throw; + } else if (boost::regex_match(wire_name, what, re_BYP)) { + tile_delays[wireIndex] = 190; + } else if (boost::regex_match(wire_name, what, re_BYP_B)) { + } else if (boost::regex_match(wire_name, what, re_FAN)) { + tile_delays[wireIndex] = 190; + } else if (boost::regex_match(wire_name, what, re_CLB_I1_6)) { + switch (what.str(2)[0]) { + case '1': tile_delays[wireIndex] = 280; break; + case '2': tile_delays[wireIndex] = 280; break; + case '3': tile_delays[wireIndex] = 180; break; + case '4': tile_delays[wireIndex] = 180; break; + case '5': tile_delays[wireIndex] = 80; break; + case '6': tile_delays[wireIndex] = 40; break; + default: throw; + } + } } - } else if (boost::regex_match(ewi.mWireName, what, re_L)) { - std::string l(what[2]); - if (l == "H") - d.delay = 360; - else if (l == "VB") - d.delay = 300; - else if (l == "V") - d.delay = 350; - else - throw; - } else if (boost::regex_match(ewi.mWireName, what, re_BYP)) { - d.delay = 190; - } else if (boost::regex_match(ewi.mWireName, what, re_BYP_B)) { - } else if (boost::regex_match(ewi.mWireName, what, re_FAN)) { - d.delay = 190; + it = delay_lookup.emplace(tile_type_index, std::move(tile_delays)).first; } + assert(it != delay_lookup.end()); + DelayInfo d; + d.delay = it->second[tw.getWireIndex()]; wire_to_delay.emplace_back(std::move(d)); } diff --git a/xc7/arch.h b/xc7/arch.h index 092cd5f6..24501d61 100644 --- a/xc7/arch.h +++ b/xc7/arch.h @@ -331,7 +331,7 @@ struct TorcInfo construct_wire_to_tilewire(const Segments &segments, const Tiles &tiles, std::unordered_map &segment_to_wire, std::unordered_map &trivial_to_wire); - static std::vector construct_wire_to_delay(const std::vector &wire_to_tilewire, + static std::vector construct_wire_to_delay(const Tiles &tiles, const std::vector &wire_to_tilewire, const DDB &ddb); static std::vector construct_pip_to_arc(const std::vector &wire_to_tilewire, const DDB &ddb, std::vector> &wire_to_pips_uphill, From ec96897c1d5e01456087b7150911e93abd1b72c5 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Sun, 11 Nov 2018 14:21:06 -0800 Subject: [PATCH 31/44] Ahead of LUT input swapping, assign LUT<6 from A6 downwards --- xc7/cells.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/xc7/cells.cc b/xc7/cells.cc index e1588516..f8eb1373 100644 --- a/xc7/cells.cc +++ b/xc7/cells.cc @@ -259,17 +259,17 @@ std::unique_ptr create_xc7_cell(Context *ctx, IdString type, std::stri void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff) { lc->params[ctx->id("INIT")] = lut->params[ctx->id("INIT")]; - replace_port(lut, ctx->id("I0"), lc, id_I1); + replace_port(lut, ctx->id("I0"), lc, id_I6); if (get_net_or_empty(lut, id_I1)) - replace_port(lut, id_I1, lc, id_I2); + replace_port(lut, id_I1, lc, id_I5); if (get_net_or_empty(lut, id_I2)) - replace_port(lut, id_I2, lc, id_I3); + replace_port(lut, id_I2, lc, id_I4); if (get_net_or_empty(lut, id_I3)) - replace_port(lut, id_I3, lc, id_I4); + replace_port(lut, id_I3, lc, id_I3); if (get_net_or_empty(lut, id_I4)) - replace_port(lut, id_I4, lc, id_I5); + replace_port(lut, id_I4, lc, id_I2); if (get_net_or_empty(lut, id_I5)) - replace_port(lut, id_I5, lc, id_I6); + replace_port(lut, id_I5, lc, id_I1); if (no_dff) { replace_port(lut, id_O, lc, id_O); lc->params[ctx->id("DFF_ENABLE")] = "0"; From e0fe52360621a51dc07f005dbe461db21c07f276 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 12 Nov 2018 11:23:31 +0000 Subject: [PATCH 32/44] 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 33/44] 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 959d163ba71fd2fdf08911e9be47f4f1d60514e5 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 13 Nov 2018 14:27:23 +0000 Subject: [PATCH 34/44] 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 35/44] 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 36/44] 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 37/44] 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 38/44] 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 72a16004e14d75679582bf8860247e127c2a3f48 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Tue, 13 Nov 2018 09:01:24 -0800 Subject: [PATCH 39/44] Divide columns by 2 to get better X estimate for tiles --- xc7/arch.cc | 2 +- xc7/delay.cc | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/xc7/arch.cc b/xc7/arch.cc index 13fad6e0..17785776 100644 --- a/xc7/arch.cc +++ b/xc7/arch.cc @@ -108,7 +108,7 @@ std::vector TorcInfo::construct_bel_to_loc(const Sites &sites, const Tiles for (SiteIndex i(0); i < site_index_to_type.size(); ++i) { const auto &site = sites.getSite(i); const auto &tile_info = tiles.getTileInfo(site.getTileIndex()); - const auto x = tile_info.getCol(); + const auto x = (tile_info.getCol() + 1) / 2; // Divide by 2 because XDL coordinate space counts the INT tiles between CLBs const auto y = tile_info.getRow(); if (site_index_to_type[i] == id_SLICE_LUT6) { diff --git a/xc7/delay.cc b/xc7/delay.cc index 397bd483..fbb99962 100644 --- a/xc7/delay.cc +++ b/xc7/delay.cc @@ -104,7 +104,7 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const const auto &src_info = torc_info->tiles.getTileInfo(src_tw.getTileIndex()); const auto &dst_tw = torc_info->wire_to_tilewire[dst.index]; const auto &dst_info = torc_info->tiles.getTileInfo(dst_tw.getTileIndex()); - auto abs_delta_x = abs(src_info.getCol() - dst_info.getCol()); + auto abs_delta_x = (abs(tile_info.getCol() - dst_info.getCol()) + 1)/ 2; // Divide by 2 because XDL coordinate space counts the INT tiles between CLBs auto abs_delta_y = abs(src_info.getRow() - dst_info.getRow()); #if 1 auto div_LH = std::div(abs_delta_x, 12); @@ -118,10 +118,10 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const auto div_V2 = std::div(div_V4.rem, 2); auto num_H1 = div_H2.rem; auto num_V1 = div_V2.rem; - return div_LH.quot * 360/2 + div_LVB.quot * 300 + div_LV.quot * 350 - + (div_H6.quot + div_H4.quot) * 210/2 + (div_V6.quot + div_V4.quot) * 210 - + div_H2.quot * 170/2 + div_V2.quot * 170 - + num_H1 * 150/2 + num_V1 * 150; + return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350 + + (div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210 + + (div_H2.quot + div_V2.quot) * 170 + + (num_H1 + num_V1) * 150; #else return std::max(150, 33 * abs_delta_x + 66 * abs_delta_y); #endif @@ -146,10 +146,10 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const auto div_V2 = std::div(div_V4.rem, 2); auto num_H1 = div_H2.rem; auto num_V1 = div_V2.rem; - return div_LH.quot * 360/2 + div_LVB.quot * 300 + div_LV.quot * 350 - + (div_H6.quot + div_H4.quot) * 210/2 + (div_V6.quot + div_V4.quot) * 210 - + div_H2.quot * 170/2 + div_V2.quot * 170 - + num_H1 * 150/2 + num_V1 * 150; + return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350 + + (div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210 + + (div_H2.quot + div_V2.quot) * 170 + + (num_H1 + num_V1) * 150; #else return std::max(150, 33 * abs_delta_x + 66 * abs_delta_y); #endif From d5ca744ca0eda472661e08f8435f9453b19260fd Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Tue, 13 Nov 2018 09:10:14 -0800 Subject: [PATCH 40/44] Fix typo --- xc7/delay.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xc7/delay.cc b/xc7/delay.cc index fbb99962..3bef3dfc 100644 --- a/xc7/delay.cc +++ b/xc7/delay.cc @@ -104,7 +104,7 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const const auto &src_info = torc_info->tiles.getTileInfo(src_tw.getTileIndex()); const auto &dst_tw = torc_info->wire_to_tilewire[dst.index]; const auto &dst_info = torc_info->tiles.getTileInfo(dst_tw.getTileIndex()); - auto abs_delta_x = (abs(tile_info.getCol() - dst_info.getCol()) + 1)/ 2; // Divide by 2 because XDL coordinate space counts the INT tiles between CLBs + auto abs_delta_x = (abs(src_info.getCol() - dst_info.getCol()) + 1)/ 2; // Divide by 2 because XDL coordinate space counts the INT tiles between CLBs auto abs_delta_y = abs(src_info.getRow() - dst_info.getRow()); #if 1 auto div_LH = std::div(abs_delta_x, 12); From fa3d366ddb52ee645dcf7cc38aac08f83663581f Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Tue, 13 Nov 2018 10:10:57 -0800 Subject: [PATCH 41/44] Add missing operator needed by router_improve --- xc7/archdefs.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xc7/archdefs.h b/xc7/archdefs.h index 571d76d3..1c2a752d 100644 --- a/xc7/archdefs.h +++ b/xc7/archdefs.h @@ -70,6 +70,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 @@ -78,6 +79,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 @@ -86,6 +88,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 ca94aa19153d9354caf1bc2e4e07b11d158e709c Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Tue, 13 Nov 2018 10:11:27 -0800 Subject: [PATCH 42/44] Reduce blinky.sh freq to 150MHz --- xc7/blinky.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xc7/blinky.sh b/xc7/blinky.sh index b116b3ff..ff1a03b5 100755 --- a/xc7/blinky.sh +++ b/xc7/blinky.sh @@ -1,7 +1,7 @@ #!/bin/bash set -ex yosys blinky.ys -../nextpnr-xc7 --json blinky.json --pcf blinky.pcf --xdl blinky.xdl --freq 250 +../nextpnr-xc7 --json blinky.json --pcf blinky.pcf --xdl blinky.xdl --freq 150 xdl -xdl2ncd blinky.xdl bitgen -w blinky.ncd -g UnconstrainedPins:Allow trce blinky.ncd -v 10 From bccf95296c94ffc0019611a1ec3fb4dc95433a47 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Tue, 13 Nov 2018 11:07:08 -0800 Subject: [PATCH 43/44] Add picorv32.pcf --- xc7/picorv32.pcf | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 xc7/picorv32.pcf diff --git a/xc7/picorv32.pcf b/xc7/picorv32.pcf new file mode 100644 index 00000000..499b965d --- /dev/null +++ b/xc7/picorv32.pcf @@ -0,0 +1,3 @@ +NET "clk" PERIOD = 8 nS ; +PIN "clk_pin" = BEL "clk.PAD" PINNAME PAD; +PIN "clk_pin" CLOCK_DEDICATED_ROUTE = FALSE; From 9a498c26d1a67727a4feb5dbd8b2e13a7030beb2 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Tue, 13 Nov 2018 11:07:43 -0800 Subject: [PATCH 44/44] Update picorv32.sh --- xc7/picorv32.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/xc7/picorv32.sh b/xc7/picorv32.sh index 0ccb217e..99860236 100755 --- a/xc7/picorv32.sh +++ b/xc7/picorv32.sh @@ -3,6 +3,7 @@ set -ex rm -f picorv32.v wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v yosys picorv32.ys -../nextpnr-xc7 --json picorv32.json --xdl picorv32.xdl -#../nextpnr-ice40 --hx8k --asc picorv32.asc --json picorv32.json -#icetime -d hx8k -t picorv32.asc +../nextpnr-xc7 --json picorv32.json --xdl picorv32.xdl --pcf picorv32.pcf --freq 150 +xdl -xdl2ncd picorv32.xdl +#bitgen -w blinky.ncd -g UnconstrainedPins:Allow +trce picorv32.ncd -v 10