diff --git a/common/nextpnr.cc b/common/nextpnr.cc index 4e6407b2..903ab9e4 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) @@ -51,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 59ae0323..86e781ae 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; @@ -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; @@ -541,6 +551,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 65007a05..adad37e9 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -28,24 +28,40 @@ namespace { USING_NEXTPNR_NAMESPACE -struct hash_id_wire +struct arc_key { - std::size_t operator()(const std::pair &arg) const noexcept + 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 : net_info->name < other.net_info->name; } + + struct Hash { - std::size_t seed = std::hash()(arg.first); - seed ^= std::hash()(arg.second) + 0x9e3779b9 + (seed << 6) + (seed >> 2); - return seed; - } + 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; + int randtag = 0; + + struct Less { - 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 + { + if (lhs.pri != rhs.pri) + return lhs.pri < rhs.pri; + return lhs.randtag < rhs.randtag; + } + }; }; struct QueuedWire @@ -53,637 +69,662 @@ struct QueuedWire WireId wire; PipId pip; - delay_t delay = 0, togo = 0; + delay_t delay = 0, penalty = 0, bonus = 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; + NPNR_ASSERT(l >= 0); + NPNR_ASSERT(r >= 0); + l -= lhs.bonus; + r -= rhs.bonus; return l == r ? lhs.randtag > rhs.randtag : l > r; } }; }; -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::Less> arc_queue; + std::unordered_map> wire_to_arcs; + std::unordered_map, arc_key::Hash> arc_to_wires; + 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) + std::unordered_map wireScores; + 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) {} + + 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; - visited.clear(); + delay_t pri = ctx->estimateDelay(src_wire, dst_wire) - arc.net_info->users[arc.user_idx].budget; - for (auto &it : src_wires) { - QueuedWire qw; - qw.wire = it.first; - qw.pip = PipId(); - qw.delay = it.second - (it.second / 16); - if (cfg.useEstimate) - qw.togo = ctx->estimateDelay(qw.wire, dst_wire); - qw.randtag = ctx->rng(); + arc_entry entry; + entry.arc = arc; + entry.pri = pri; + entry.randtag = ctx->rng(); - queue.push(qw); - visited[qw.wire] = qw; - } - - int thisVisitCnt = 0; - int thisVisitCntLimit = 0; - - while (!queue.empty() && (thisVisitCntLimit == 0 || thisVisitCnt < thisVisitCntLimit)) { - 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(); - WireId next_wire = ctx->getPipDstWire(pip); - bool foundRipupNet = false; - thisVisitCnt++; - - next_delay += ctx->getWireDelay(next_wire).maxDelay(); - - if (!ctx->checkWireAvail(next_wire)) { - if (!ripup) - continue; - NetInfo *ripupWireNet = ctx->getConflictingWireNet(next_wire); - if (ripupWireNet == nullptr || ripupWireNet->name == net_name) - continue; - - auto it1 = scores.wireScores.find(next_wire); - if (it1 != scores.wireScores.end()) - next_delay += (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; - - foundRipupNet = true; - } - - if (!ctx->checkPipAvail(pip)) { - if (!ripup) - continue; - NetInfo *ripupPipNet = ctx->getConflictingPipNet(pip); - if (ripupPipNet == nullptr || ripupPipNet->name == net_name) - continue; - - auto it1 = scores.pipScores.find(pip); - if (it1 != scores.pipScores.end()) - next_delay += (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; - - foundRipupNet = true; - } - - if (foundRipupNet) - next_delay += ripup_penalty; - - NPNR_ASSERT(next_delay >= 0); - - auto it = visited.find(next_wire); - if (it != visited.end()) { - if (it->second.delay <= next_delay + ctx->getDelayEpsilon()) - continue; -#if 0 // FIXME - 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), - ctx->getDelayNS(next_delay)); +#if 0 + if (ctx->debug) + log("[arc_queue_insert] %s (%d) %s %s [%d %d]\n", ctx->nameOf(entry.arc.net_info), entry.arc.user_idx, + ctx->nameOfWire(src_wire), ctx->nameOfWire(dst_wire), (int)entry.pri, entry.randtag); #endif - if (thisVisitCntLimit == 0) - revisitCnt++; - else - overtimeRevisitCnt++; - } - QueuedWire next_qw; - next_qw.wire = next_wire; - next_qw.pip = pip; - next_qw.delay = next_delay; - if (cfg.useEstimate) - next_qw.togo = ctx->estimateDelay(next_wire, dst_wire); - next_qw.randtag = ctx->rng(); - - visited[next_qw.wire] = next_qw; - queue.push(next_qw); - } - } - - visitCnt += thisVisitCnt; + arc_queue.push(entry); + queued_arcs.insert(arc); } - 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) + void arc_queue_insert(const arc_key &arc) { - std::unordered_map src_wires; - src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay(); - route(src_wires, dst_wire); - routedOkay = visited.count(dst_wire); + if (queued_arcs.count(arc)) + return; - 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)); + 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]); - 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)); + arc_queue_insert(arc, src_wire, dst_wire); + } + arc_key arc_queue_pop() + { + arc_entry entry = arc_queue.top(); + +#if 0 if (ctx->debug) - log(" Source wire: %s\n", ctx->getWireName(src_wire).c_str(ctx)); + 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 - std::unordered_map src_wires; - std::vector> users_array; + arc_queue.pop(); + queued_arcs.erase(entry.arc); + return entry.arc; + } - 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)); + void ripup_net(NetInfo *net) + { + if (ctx->debug) + log(" ripup net %s\n", ctx->nameOf(net)); + + netScores[net]++; + + 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); + arcs.push_back(it); } - std::sort(users_array.begin(), users_array.end()); - } else { - // route only the selected user - users_array.push_back(std::pair(delay_t(), user_idx)); + 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->nameOfWire(w)); + + ctx->unbindWire(w); + wireScores[w]++; } - 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(); + ripup_flag = true; + } + + void ripup_wire(WireId wire, int extra_indent = 0) + { + if (ctx->debug) + log(" ripup wire %s\n", ctx->nameOfWire(wire)); + + WireId w = ctx->getConflictingWireWire(wire); + + if (w == WireId()) { + NetInfo *n = ctx->getConflictingWireNet(wire); + if (n != nullptr) + ripup_net(n); } 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(); + std::vector arcs; + for (auto &it : wire_to_arcs[w]) { + arc_to_wires[it].erase(w); + 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->nameOfWire(w)); + + ctx->unbindWire(w); + wireScores[w]++; + } + + ripup_flag = true; + } + + void ripup_pip(PipId pip) + { + if (ctx->debug) + log(" ripup pip %s\n", ctx->nameOfPip(pip)); + + WireId w = ctx->getConflictingPipWire(pip); + + if (w == WireId()) { + NetInfo *n = ctx->getConflictingPipNet(pip); + if (n != nullptr) + ripup_net(n); + } else { + std::vector arcs; + for (auto &it : wire_to_arcs[w]) { + arc_to_wires[it].erase(w); + 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->nameOfWire(w)); + + ctx->unbindWire(w); + wireScores[w]++; + } + + 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; + +#if 0 + if (ctx->debug) + log("[check] net: %s\n", ctx->nameOf(net_info)); +#endif + + auto src_wire = ctx->getNetinfoSourceWire(net_info); + log_assert(src_wire != WireId()); + + 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); +#if 0 + if (ctx->debug) + log("[check] arc: %s %s\n", ctx->nameOfWire(src_wire), ctx->nameOfWire(dst_wire)); +#endif + + for (WireId wire : arc_to_wires[arc]) { +#if 0 + if (ctx->debug) + log("[check] wire: %s\n", ctx->nameOfWire(wire)); +#endif + valid_wires_for_net.insert(wire); + log_assert(wire_to_arcs[wire].count(arc)); + log_assert(net_info->wires.count(wire)); + } + } + + 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() + { + std::unordered_map src_to_net; + std::unordered_map dst_to_arc; + + 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; + + auto src_wire = ctx->getNetinfoSourceWire(net_info); + + if (src_wire == WireId()) + 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->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->nameOfWire(src_wire), ctx->nameOf(net_info), + ctx->nameOf(dst_to_arc.at(src_wire).net_info), dst_to_arc.at(src_wire).user_idx); for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) { auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]); if (dst_wire == WireId()) log_error("No wire found for port %s on destination cell %s.\n", - net_info->users[user_idx].port.c_str(ctx), - net_info->users[user_idx].cell->name.c_str(ctx)); + ctx->nameOf(net_info->users[user_idx].port), + ctx->nameOf(net_info->users[user_idx].cell)); - 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; + 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->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); + } - 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; + 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->nameOfWire(dst_wire), ctx->nameOf(src_to_net.at(dst_wire)), + ctx->nameOf(net_info), user_idx); - return delay; - }; + 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; + } WireId cursor = dst_wire; - while (src_wires.count(cursor) == 0) { + wire_to_arcs[cursor].insert(arc); + arc_to_wires[arc].insert(cursor); + + while (src_wire != cursor) { auto it = net_info->wires.find(cursor); - if (it == net_info->wires.end()) - goto check_next_user_for_existing_path; + 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_arcs[cursor].insert(arc); + arc_to_wires[arc].insert(cursor); } - - 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); + src_to_net[src_wire] = net_info; - 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)); + std::vector unbind_wires; + + for (auto &it : net_info->wires) + if (it.second.strength < STRENGTH_LOCKED && wire_to_arcs.count(it.first) == 0) + unbind_wires.push_back(it.first); + + for (auto it : unbind_wires) ctx->unbindWire(it); + } + } + + 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, ctx->nameOf(net_info), + int(net_info->users.size())); + log(" source ... %s\n", ctx->nameOfWire(src_wire)); + log(" sink ..... %s\n", ctx->nameOfWire(dst_wire)); + } + + // unbind wires that are currently used exclusively by this arc + + std::unordered_set old_arc_wires; + old_arc_wires.swap(arc_to_wires[arc]); + + 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->nameOfWire(wire)); + ctx->unbindWire(wire); } } - for (auto user_idx_it : users_array) { - int user_idx = user_idx_it.second; + // reset wire queue + if (!queue.empty()) { + std::priority_queue, QueuedWire::Greater> new_queue; + queue.swap(new_queue); + } + visited.clear(); + + // A* main loop + + int visitCnt = 0; + int maxVisitCnt = INT_MAX; + delay_t best_est = 0; + delay_t best_score = -1; + + { + QueuedWire qw; + qw.wire = src_wire; + 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; + } + qw.randtag = ctx->rng(); + + queue.push(qw); + visited[qw.wire] = qw; + } + + while (visitCnt++ < maxVisitCnt && !queue.empty()) { + QueuedWire qw = queue.top(); + queue.pop(); + + 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(); + + WireId conflictWireWire = WireId(), conflictPipWire = WireId(); + NetInfo *conflictWireNet = nullptr, *conflictPipNet = 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)) { + 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 (conflictPipNet != nullptr && conflictWireWire != WireId() && + conflictPipNet->wires.count(conflictWireWire)) + conflictWireWire = WireId(); + + if (conflictWireWire == conflictPipWire) + conflictWireWire = WireId(); + + if (conflictWireNet == conflictPipNet) + conflictWireNet = nullptr; + + 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); + + 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 + if (ctx->debug) + log("Found better route to %s. Old vs new delay estimate: %.3f (%.3f) %.3f (%.3f)\n", + ctx->nameOfWire(next_wire), + ctx->getDelayNS(old_score), + ctx->getDelayNS(old_visited_it->second.delay), + ctx->getDelayNS(next_score), + ctx->getDelayNS(next_delay)); +#endif + } + + QueuedWire next_qw; + next_qw.wire = next_wire; + 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 / 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->nameOfWire(qw.wire), + ctx->nameOfWire(next_wire), + ctx->getDelayNS(next_score), + ctx->getDelayNS(next_delay)); +#endif + + visited[next_qw.wire] = next_qw; + queue.push(next_qw); + + if (next_wire == dst_wire) { + maxVisitCnt = std::min(maxVisitCnt, 2 * visitCnt + (next_qw.penalty > 0 ? 100 : 0)); + best_score = next_score - next_bonus; + } + } + } + + if (ctx->debug) + 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)); + log(" no route found for this arc\n"); + return false; + } - auto dst_wire = ctx->getNetinfoSinkWire(net_info, net_info->users[user_idx]); + 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)); + log(" arc budget: %12.2f\n", ctx->getDelayNS(net_info->users[user_idx].budget)); + } - 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)); + // bind resulting route (and maybe unroute other nets) + + std::unordered_set unassign_wires = arc_to_wires[arc]; + + WireId cursor = dst_wire; + delay_t accumulated_path_delay = 0; + delay_t last_path_delay_delta = 0; + while (1) { + auto pip = visited[cursor].pip; if (ctx->debug) { - log(" Destination wire: %s\n", ctx->getWireName(dst_wire).c_str(ctx)); - log(" Path delay estimate: %.2f\n", ctx->getDelayNS(ctx->estimateDelay(src_wire, dst_wire))); + 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(); } - route(src_wires, dst_wire); + if (pip == PipId()) + NPNR_ASSERT(cursor == src_wire); - 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)]++; + 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)); } - 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)]++; + if (pip != PipId() && !ctx->checkPipAvail(pip)) { + ripup_pip(pip); + NPNR_ASSERT(ctx->checkPipAvail(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); + if (pip == PipId()) { + if (ctx->debug) + 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->nameOfPip(pip)); + ctx->bindPip(pip, net_info, STRENGTH_WEAK); + } } + + wire_to_arcs[cursor].insert(arc); + arc_to_wires[arc].insert(cursor); + + if (pip == PipId()) + break; + + cursor = ctx->getPipSrcWire(pip); } - routedOkay = true; - } -}; - -struct RouteJob -{ - IdString net; - int user_idx = -1; - delay_t slack = 0; - int randtag = 0; - - 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; - } - }; -}; - -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 @@ -695,264 +736,80 @@ Router1Cfg::Router1Cfg(Context *ctx) : Settings(ctx) cleanupReroute = get("router1/cleanupReroute", true); fullCleanupReroute = get("router1/fullCleanupReroute", true); useEstimate = get("router1/useEstimate", true); + + wireRipupPenalty = ctx->getRipupDelayPenalty(); + netRipupPenalty = 10 * ctx->getRipupDelayPenalty(); + reuseBonus = wireRipupPenalty / 2; + + 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); - - if (jobQueue.empty()) { - log_info("found no unrouted source-sink pairs. no routing necessary.\n"); - ctx->unlock(); - return true; - } - - log_info("found %d unrouted source-sink pairs. starting routing procedure.\n", int(jobQueue.size())); - - int iterCnt = 0; - - while (!jobQueue.empty()) { - if (iterCnt == cfg.maxIterCnt) { - log_warning("giving up after %d iterations.\n", iterCnt); - log_info("Checksum: 0x%08x\n", ctx->checksum()); + Router1 router(ctx, cfg); + router.setup(); #ifndef NDEBUG + router.check(); +#endif + + 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/ripup wo/ripup | w/r wo/r | arcs\n"); + + 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())); + last_arcs_with_ripup = router.arcs_with_ripup; + last_arcs_without_ripup = router.arcs_without_ripup; +#ifndef NDEBUG + router.check(); +#endif + } + + if (ctx->debug) + log("-- %d --\n", iter_cnt); + + 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, ctx->nameOf(arc.net_info)); +#ifndef NDEBUG + router.check(); 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("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); + 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 - 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; - } + router.check(); + ctx->check(); + log_assert(ctx->checkRoutedDesign()); #endif 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) { @@ -964,33 +821,180 @@ 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) +bool Context::checkRoutedDesign() const { - RipupScoreboard scores; - Router1Cfg cfg(this); - cfg.useEstimate = useEstimate; + const Context *ctx = getCtx(); - Router router(this, cfg, scores, src_wire, dst_wire); + for (auto &net_it : ctx->nets) { + NetInfo *net_info = net_it.second.get(); - if (!router.routedOkay) - return false; +#ifdef ARCH_ECP5 + if (net_info->is_global) + continue; +#endif - if (delay != nullptr) - *delay = router.visited.at(dst_wire).delay; + if (ctx->debug) + log("checking net %s\n", ctx->nameOf(net_info)); - 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); + 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->nameOfWire(src_wire)); + 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->nameOfWire(dst_wire)); + 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->nameOfWire(child)); + logged_wires.insert(child); + } + setOrderNum(child, num + 1); + } + if (db_entry.children.empty()) { + if (dest_wires.count(w) != 0) { + if (ctx->debug) + log(" %*s=> sink %d\n", 2 * num, "", dest_wires.at(w)); + } else { + if (ctx->debug) + log(" %*s=> stub\n", 2 * num, ""); + found_stub = true; + } + } + }; + + if (ctx->debug) { + log(" driver: %s\n", ctx->nameOfWire(src_wire)); + logged_wires.insert(src_wire); + } + setOrderNum(src_wire, 1); + + std::unordered_set 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->nameOfWire(w)); + logged_wires.insert(w); + setOrderNum(w, 1); + } + + for (WireId w : dangling_wires) { + if (logged_wires.count(w) == 0) + log(" loop: %s -> %s\n", + ctx->nameOfWire(ctx->getPipSrcWire(net_info->wires.at(w).pip)), + ctx->nameOfWire(w)); + } + } + } + + bool fail = false; + + if (found_unrouted) { + if (ctx->debug) + log("check failed: found unrouted arcs\n"); + fail = true; + } + + if (found_loop) { + if (ctx->debug) + log("check failed: found loops\n"); + fail = true; + } + + if (found_stub) { + if (ctx->debug) + log("check failed: found stubs\n"); + fail = true; + } + + if (!dangling_wires.empty()) { + if (ctx->debug) + log("check failed: found dangling wires\n"); + fail = true; + } + + if (fail) + return false; } return true; } +bool Context::getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay, + std::unordered_map *route, bool useEstimate) +{ + // FIXME + return false; +} + NEXTPNR_NAMESPACE_END diff --git a/common/router1.h b/common/router1.h index a184cbe7..80d7aa96 100644 --- a/common/router1.h +++ b/common/router1.h @@ -33,6 +33,10 @@ struct Router1Cfg : Settings bool cleanupReroute; bool fullCleanupReroute; bool useEstimate; + delay_t wireRipupPenalty; + delay_t netRipupPenalty; + delay_t reuseBonus; + delay_t estimatePrecision; }; extern bool router1(Context *ctx, const Router1Cfg &cfg); 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(); diff --git a/docs/archapi.md b/docs/archapi.md index 73443c15..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 @@ -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 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 single net that can be unbound to make this -wire 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,18 +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. +### WireId getConflictingPipWire(PipId pip) const + +If this returns a non-WireId(), then unbinding that wire +will make the given pip available. + ### NetInfo \*getConflictingPipNet(PipId pip) const -Return the net that needs to be unbound in order to make this -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. +If this returns a non-nullptr, then unbinding that entire net +will make the given pip available. ### const\_range\ getPips() const diff --git a/docs/faq.md b/docs/faq.md index d440bba6..7b358187 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -38,7 +38,94 @@ For nextpnr we are using the following terminology. Adding new architectures to nextpnr ----------------------------------- -TBD +### Implementing new architectures + +Each nextpnr architecture must implement the *nextpnr architecture API*. +See [archapi.md](archapi.md) for a complete reference of the architecture API. + +### Delay Estimates + +Each architecture must implement a `estimateDelay()` method that estimates the expected delay for a path from given `src` to `dst` wires. +*It is very important that this method slightly overestimates the expected delay.* Furthermore, it should overestimate the expected delay +by a slightly larger margin for longer paths than for shorter paths. Otherwise there will be performance issues with the router. + +The delays estimates returned by that method should also be as fine-grain as possible. It definitely pays off to spend some time improving the `estimateDelay()` +for your architecture once implementing small designs work. + +### Ripup Information + +The `getConflictingWireWire()`, `getConflictingWireNet()`, `getConflictingPipWire()`, and `getConflictingPipNet()` methods are used by the router +to determine which resources to rip up in order to make a given routing resource (wire or pip) available. + +The architecture must guanrantee that the following invariants hold. + +**Invariant 1:** + +``` + if (!ctx->checkWireAvail(wire)) { + WireId w = getConflictingWireWire(wire); + if (w != WireId()) { + ctx->unbindWire(w); + assert(ctx->checkWireAvail(wire)); + } + } +``` + +**Invariant 2:** + +``` + if (!ctx->checkWireAvail(wire)) { + NetInfo *n = getConflictingWireNet(wire); + if (n != nullptr) { + for (auto &it : n->wires) + ctx->unbindWire(it.first); + assert(ctx->checkWireAvail(wire)); + } + } +``` + +**Invariant 3:** + +``` + if (!ctx->checkPipAvail(pip)) { + WireId w = getConflictingPipWire(pip); + if (w != WireId()) { + ctx->unbindWire(w); + assert(ctx->checkPipAvail(pip)); + } + } +``` + +**Invariant 4:** + +``` + if (!ctx->checkPipAvail(pip)) { + NetInfo *n = getConflictingPipNet(pip); + if (n != nullptr) { + for (auto &it : n->wires) + ctx->unbindWire(it.first); + assert(ctx->checkPipAvail(pip)); + } + } +``` + +**Invariant 5:** + +``` + if (ctx->checkWireAvail(wire)) { + // bind is guaranteed to succeed + ctx->bindWire(wire, net, strength); + } +``` + +**Invariant 6:** + +``` + if (ctx->checkPipAvail(pip) && ctx->checkWireAvail(ctx->getPipDstWire(pip))) { + // bind is guaranteed to succeed + ctx->bindPip(pip, net, strength); + } +``` Nextpnr and other tools ----------------------- 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; } diff --git a/ecp5/arch.h b/ecp5/arch.h index 583d539f..9daae11d 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -619,6 +619,8 @@ 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 +726,8 @@ 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()); 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/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..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,6 +187,7 @@ struct Arch : BaseCtx void unbindPip(PipId pip); bool checkPipAvail(PipId pip) const; NetInfo *getBoundPipNet(PipId pip) const; + WireId getConflictingPipWire(PipId pip) const; NetInfo *getConflictingPipNet(PipId pip) const; const std::vector &getPips() const; Loc getPipLocation(PipId pip) const; diff --git a/gui/designwidget.cc b/gui/designwidget.cc index a45752fc..4a1d5a8f 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,6 +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 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 27d5db9f..b992d192 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); @@ -566,6 +566,8 @@ 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()); @@ -608,14 +610,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 +630,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,33 +639,39 @@ 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); } - 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] != nullptr) - 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 @@ -671,10 +680,21 @@ struct Arch : BaseCtx return pip_to_net[pip.index]; } + WireId getConflictingPipWire(PipId pip) const + { + if (ice40_pip_hard_unavail(pip)) + return WireId(); + + return switches_locked[chip_info->pip_data[pip.index].switch_index]; + } + NetInfo *getConflictingPipNet(PipId pip) const { - NPNR_ASSERT(pip != PipId()); - return switches_locked[chip_info->pip_data[pip.index].switch_index]; + 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 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 diff --git a/xc7/arch.cc b/xc7/arch.cc index e9c6d725..17785776 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()) { @@ -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) { @@ -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)); } @@ -600,20 +617,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 } // ----------------------------------------------------------------------- @@ -957,8 +980,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; } diff --git a/xc7/arch.h b/xc7/arch.h index 9d8c4251..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, @@ -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()); 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 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.sh b/xc7/blinky.sh index cccc6c24..1ba77434 100755 --- a/xc7/blinky.sh +++ b/xc7/blinky.sh @@ -1,10 +1,14 @@ #!/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 -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/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 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 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"; diff --git a/xc7/delay.cc b/xc7/delay.cc index fe14a5ce..3bef3dfc 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()) + 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); + 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 + 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 } 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 + 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 } NEXTPNR_NAMESPACE_END 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(); 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; 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