aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/archcheck.cc146
-rw-r--r--common/log.h6
-rw-r--r--common/nextpnr.cc61
-rw-r--r--common/nextpnr.h202
-rw-r--r--common/place_common.cc4
-rw-r--r--common/placer1.cc15
-rw-r--r--common/router1.cc276
7 files changed, 458 insertions, 252 deletions
diff --git a/common/archcheck.cc b/common/archcheck.cc
new file mode 100644
index 00000000..5c4ef26c
--- /dev/null
+++ b/common/archcheck.cc
@@ -0,0 +1,146 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "nextpnr.h"
+#include "log.h"
+
+#if 0
+#define dbg(...) log(__VA_ARGS__)
+#else
+#define dbg(...)
+#endif
+
+USING_NEXTPNR_NAMESPACE
+
+namespace {
+
+void archcheck_names(const Context *ctx)
+{
+ log_info("Checking entity names.\n");
+
+ log_info("Checking bel names..\n");
+ for (BelId bel : ctx->getBels()) {
+ IdString name = ctx->getBelName(bel);
+ BelId bel2 = ctx->getBelByName(name);
+ log_assert(bel == bel2);
+ }
+
+ log_info("Checking wire names..\n");
+ for (WireId wire : ctx->getWires()) {
+ IdString name = ctx->getWireName(wire);
+ WireId wire2 = ctx->getWireByName(name);
+ log_assert(wire == wire2);
+ }
+
+ log_info("Checking pip names..\n");
+ for (PipId pip : ctx->getPips()) {
+ IdString name = ctx->getPipName(pip);
+ PipId pip2 = ctx->getPipByName(name);
+ log_assert(pip == pip2);
+ }
+
+ log_break();
+}
+
+void archcheck_locs(const Context *ctx)
+{
+ log_info("Checking location data.\n");
+
+ log_info("Checking all bels..\n");
+ for (BelId bel : ctx->getBels()) {
+ log_assert(bel != BelId());
+ dbg("> %s\n", ctx->getBelName(bel).c_str(ctx));
+
+ Loc loc = ctx->getBelLocation(bel);
+ dbg(" ... %d %d %d\n", loc.x, loc.y, loc.z);
+
+ log_assert(0 <= loc.x);
+ log_assert(0 <= loc.y);
+ log_assert(0 <= loc.z);
+ log_assert(loc.x < ctx->getGridDimX());
+ log_assert(loc.y < ctx->getGridDimY());
+ log_assert(loc.z < ctx->getTileDimZ(loc.x, loc.y));
+
+ BelId bel2 = ctx->getBelByLocation(loc);
+ dbg(" ... %s\n", ctx->getBelName(bel2).c_str(ctx));
+ log_assert(bel == bel2);
+ }
+
+ log_info("Checking all locations..\n");
+ for (int x = 0; x < ctx->getGridDimX(); x++)
+ for (int y = 0; y < ctx->getGridDimY(); y++)
+ {
+ dbg("> %d %d\n", x, y);
+ std::unordered_set<int> usedz;
+
+ for (int z = 0; z < ctx->getTileDimZ(x, y); z++) {
+ BelId bel = ctx->getBelByLocation(Loc(x, y, z));
+ if (bel == BelId())
+ continue;
+ Loc loc = ctx->getBelLocation(bel);
+ dbg(" + %d %s\n", z, ctx->getBelName(bel).c_str(ctx));
+ log_assert(x == loc.x);
+ log_assert(y == loc.y);
+ log_assert(z == loc.z);
+ usedz.insert(z);
+ }
+
+ for (BelId bel : ctx->getBelsByTile(x, y)) {
+ Loc loc = ctx->getBelLocation(bel);
+ dbg(" - %d %s\n", loc.z, ctx->getBelName(bel).c_str(ctx));
+ log_assert(x == loc.x);
+ log_assert(y == loc.y);
+ log_assert(usedz.count(loc.z));
+ usedz.erase(loc.z);
+ }
+
+ log_assert(usedz.empty());
+ }
+
+ log_break();
+}
+
+void archcheck_conn(const Context *ctx)
+{
+#if 0
+ log_info("Checking connectivity data.\n");
+
+ log_info("Checking all wires..\n");
+ for (WireId wire : ctx->getWires())
+ {
+ ...
+ }
+#endif
+}
+
+} // namespace
+
+NEXTPNR_NAMESPACE_BEGIN
+
+void Context::archcheck() const
+{
+ log_info("Running architecture database integrity check.\n");
+ log_break();
+
+ archcheck_names(this);
+ archcheck_locs(this);
+ archcheck_conn(this);
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/common/log.h b/common/log.h
index c3607e39..35450311 100644
--- a/common/log.h
+++ b/common/log.h
@@ -69,17 +69,13 @@ NPNR_NORETURN void log_cmd_error(const char *format, ...) NPNR_ATTRIBUTE(format(
void log_break();
void log_flush();
-#ifndef NDEBUG
static inline void log_assert_worker(bool cond, const char *expr, const char *file, int line)
{
if (!cond)
log_error("Assert `%s' failed in %s:%d.\n", expr, file, line);
}
#define log_assert(_assert_expr_) \
- YOSYS_NAMESPACE_PREFIX log_assert_worker(_assert_expr_, #_assert_expr_, __FILE__, __LINE__)
-#else
-#define log_assert(_assert_expr_)
-#endif
+ NEXTPNR_NAMESPACE_PREFIX log_assert_worker(_assert_expr_, #_assert_expr_, __FILE__, __LINE__)
#define log_abort() log_error("Abort in %s:%d.\n", __FILE__, __LINE__)
#define log_ping() log("-- %s:%d %s --\n", __FILE__, __LINE__, __PRETTY_FUNCTION__)
diff --git a/common/nextpnr.cc b/common/nextpnr.cc
index 3861e5fe..2c50c9a1 100644
--- a/common/nextpnr.cc
+++ b/common/nextpnr.cc
@@ -51,6 +51,67 @@ void IdString::initialize_add(const BaseCtx *ctx, const char *s, int idx)
ctx->idstring_idx_to_str->push_back(&insert_rc.first->first);
}
+WireId Context::getNetinfoSourceWire(NetInfo *net_info) const
+{
+ if (net_info->driver.cell == nullptr)
+ return WireId();
+
+ auto src_bel = net_info->driver.cell->bel;
+
+ if (src_bel == BelId())
+ return WireId();
+
+ IdString driver_port = net_info->driver.port;
+
+ auto driver_port_it = net_info->driver.cell->pins.find(driver_port);
+ if (driver_port_it != net_info->driver.cell->pins.end())
+ driver_port = driver_port_it->second;
+
+ return getBelPinWire(src_bel, portPinFromId(driver_port));
+}
+
+WireId Context::getNetinfoSinkWire(NetInfo *net_info, int user_idx) const
+{
+ auto &user_info = net_info->users[user_idx];
+ auto dst_bel = user_info.cell->bel;
+
+ if (dst_bel == BelId())
+ return WireId();
+
+ IdString user_port = user_info.port;
+
+ auto user_port_it = user_info.cell->pins.find(user_port);
+
+ if (user_port_it != user_info.cell->pins.end())
+ user_port = user_port_it->second;
+
+ return getBelPinWire(dst_bel, portPinFromId(user_port));
+}
+
+delay_t Context::getNetinfoRouteDelay(NetInfo *net_info, int user_idx) const
+{
+ WireId src_wire = getNetinfoSourceWire(net_info);
+ WireId cursor = getNetinfoSinkWire(net_info, user_idx);
+ delay_t delay = 0;
+
+ while (cursor != WireId() && cursor != src_wire) {
+ auto it = net_info->wires.find(cursor);
+ if (it == net_info->wires.end())
+ break;
+ PipId pip = it->second.pip;
+ delay += getPipDelay(pip).maxDelay();
+ delay += getWireDelay(cursor).maxDelay();
+ cursor = getPipSrcWire(pip);
+ }
+
+ if (cursor == src_wire)
+ delay += getWireDelay(src_wire).maxDelay();
+ else
+ delay += estimateDelay(src_wire, cursor);
+
+ return delay;
+}
+
static uint32_t xorshift32(uint32_t x)
{
x ^= x << 13;
diff --git a/common/nextpnr.h b/common/nextpnr.h
index 40fd3d13..021772fe 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -2,6 +2,7 @@
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -19,7 +20,10 @@
#include <algorithm>
#include <assert.h>
+#include <condition_variable>
#include <memory>
+#include <mutex>
+#include <pthread.h>
#include <stdexcept>
#include <stdint.h>
#include <string>
@@ -164,6 +168,9 @@ struct Loc
{
int x = -1, y = -1, z = -1;
+ Loc() {}
+ Loc(int x, int y, int z) : x(x), y(y), z(z) {}
+
bool operator==(const Loc &other) const { return (x == other.x) && (y == other.y) && (z == other.z); }
bool operator!=(const Loc &other) const { return (x != other.x) || (y != other.y) || (z == other.z); }
};
@@ -267,19 +274,87 @@ struct CellInfo : ArchCellInfo
std::unordered_map<IdString, IdString> pins;
};
-struct BaseCtx
+struct DeterministicRNG
{
- // --------------------------------------------------------------
+ uint64_t rngstate;
- mutable std::unordered_map<std::string, int> *idstring_str_to_idx;
- mutable std::vector<const std::string *> *idstring_idx_to_str;
+ DeterministicRNG() : rngstate(0x3141592653589793) {}
- IdString id(const std::string &s) const { return IdString(this, s); }
+ uint64_t rng64()
+ {
+ // xorshift64star
+ // https://arxiv.org/abs/1402.6246
- IdString id(const char *s) const { return IdString(this, s); }
+ uint64_t retval = rngstate * 0x2545F4914F6CDD1D;
- // --------------------------------------------------------------
+ rngstate ^= rngstate >> 12;
+ rngstate ^= rngstate << 25;
+ rngstate ^= rngstate >> 27;
+
+ return retval;
+ }
+ int rng() { return rng64() & 0x3fffffff; }
+
+ int rng(int n)
+ {
+ assert(n > 0);
+
+ // round up to power of 2
+ int m = n - 1;
+ m |= (m >> 1);
+ m |= (m >> 2);
+ m |= (m >> 4);
+ m |= (m >> 8);
+ m |= (m >> 16);
+ m += 1;
+
+ while (1) {
+ int x = rng64() & (m - 1);
+ if (x < n)
+ return x;
+ }
+ }
+
+ void rngseed(uint64_t seed)
+ {
+ rngstate = seed ? seed : 0x3141592653589793;
+ for (int i = 0; i < 5; i++)
+ rng64();
+ }
+
+ template <typename T> void shuffle(std::vector<T> &a)
+ {
+ for (size_t i = 0; i != a.size(); i++) {
+ size_t j = i + rng(a.size() - i);
+ if (j > i)
+ std::swap(a[i], a[j]);
+ }
+ }
+
+ template <typename T> void sorted_shuffle(std::vector<T> &a)
+ {
+ std::sort(a.begin(), a.end());
+ shuffle(a);
+ }
+};
+
+struct BaseCtx
+{
+ // Lock to perform mutating actions on the Context.
+ std::mutex mutex;
+ pthread_t mutex_owner;
+
+ // Lock to be taken by UI when wanting to access context - the yield()
+ // method will lock/unlock it when its' released the main mutex to make
+ // sure the UI is not starved.
+ std::mutex ui_mutex;
+
+ // ID String database.
+ mutable std::unordered_map<std::string, int> *idstring_str_to_idx;
+ mutable std::vector<const std::string *> *idstring_idx_to_str;
+
+ // Placed nets and cells.
std::unordered_map<IdString, std::unique_ptr<NetInfo>> nets;
std::unordered_map<IdString, std::unique_ptr<CellInfo>> cells;
@@ -297,13 +372,57 @@ struct BaseCtx
delete idstring_idx_to_str;
}
+ // Must be called before performing any mutating changes on the Ctx/Arch.
+ void lock(void)
+ {
+ mutex.lock();
+ mutex_owner = pthread_self();
+ }
+
+ void unlock(void)
+ {
+ NPNR_ASSERT(pthread_equal(pthread_self(), mutex_owner) != 0);
+ mutex.unlock();
+ }
+
+ // Must be called by the UI before rendering data. This lock will be
+ // prioritized when processing code calls yield().
+ void lock_ui(void)
+ {
+ ui_mutex.lock();
+ mutex.lock();
+ }
+
+ void unlock_ui(void)
+ {
+ mutex.unlock();
+ ui_mutex.unlock();
+ }
+
+ // Yield to UI by unlocking the main mutex, flashing the UI mutex and
+ // relocking the main mutex. Call this when you're performing a
+ // long-standing action while holding a lock to let the UI show
+ // visualization updates.
+ // Must be called with the main lock taken.
+ void yield(void)
+ {
+ unlock();
+ ui_mutex.lock();
+ ui_mutex.unlock();
+ lock();
+ }
+
+ IdString id(const std::string &s) const { return IdString(this, s); }
+
+ IdString id(const char *s) const { return IdString(this, s); }
+
Context *getCtx() { return reinterpret_cast<Context *>(this); }
const Context *getCtx() const { return reinterpret_cast<const Context *>(this); }
// --------------------------------------------------------------
- bool allUiReload = false;
+ bool allUiReload = true;
bool frameUiReload = false;
std::unordered_set<BelId> belUiReload;
std::unordered_set<WireId> wireUiReload;
@@ -329,7 +448,7 @@ NEXTPNR_NAMESPACE_END
NEXTPNR_NAMESPACE_BEGIN
-struct Context : Arch
+struct Context : Arch, DeterministicRNG
{
bool verbose = false;
bool debug = false;
@@ -341,74 +460,19 @@ struct Context : Arch
// --------------------------------------------------------------
+ WireId getNetinfoSourceWire(NetInfo *net_info) const;
+ WireId getNetinfoSinkWire(NetInfo *net_info, int user_idx) const;
+ delay_t getNetinfoRouteDelay(NetInfo *net_info, int user_idx) const;
+
// provided by router1.cc
bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t &delay);
// --------------------------------------------------------------
- uint64_t rngstate = 0x3141592653589793;
-
- uint64_t rng64()
- {
- // xorshift64star
- // https://arxiv.org/abs/1402.6246
-
- uint64_t retval = rngstate * 0x2545F4914F6CDD1D;
-
- rngstate ^= rngstate >> 12;
- rngstate ^= rngstate << 25;
- rngstate ^= rngstate >> 27;
-
- return retval;
- }
-
- int rng() { return rng64() & 0x3fffffff; }
-
- int rng(int n)
- {
- assert(n > 0);
-
- // round up to power of 2
- int m = n - 1;
- m |= (m >> 1);
- m |= (m >> 2);
- m |= (m >> 4);
- m |= (m >> 8);
- m |= (m >> 16);
- m += 1;
-
- while (1) {
- int x = rng64() & (m - 1);
- if (x < n)
- return x;
- }
- }
-
- void rngseed(uint64_t seed)
- {
- rngstate = seed ? seed : 0x3141592653589793;
- for (int i = 0; i < 5; i++)
- rng64();
- }
-
- template <typename T> void shuffle(std::vector<T> &a)
- {
- for (size_t i = 0; i != a.size(); i++) {
- size_t j = i + rng(a.size() - i);
- if (j > i)
- std::swap(a[i], a[j]);
- }
- }
-
- template <typename T> void sorted_shuffle(std::vector<T> &a)
- {
- std::sort(a.begin(), a.end());
- shuffle(a);
- }
-
uint32_t checksum() const;
void check() const;
+ void archcheck() const;
};
NEXTPNR_NAMESPACE_END
diff --git a/common/place_common.cc b/common/place_common.cc
index 370eff23..95b7b2aa 100644
--- a/common/place_common.cc
+++ b/common/place_common.cc
@@ -36,7 +36,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type
if (driver_cell->bel == BelId())
return 0;
ctx->estimatePosition(driver_cell->bel, driver_x, driver_y, driver_gb);
- WireId drv_wire = ctx->getWireBelPin(driver_cell->bel, ctx->portPinFromId(net->driver.port));
+ WireId drv_wire = ctx->getBelPinWire(driver_cell->bel, ctx->portPinFromId(net->driver.port));
if (driver_gb)
return 0;
float worst_slack = 1000;
@@ -48,7 +48,7 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type
if (load_cell->bel == BelId())
continue;
if (ctx->timing_driven && type == MetricType::COST) {
- WireId user_wire = ctx->getWireBelPin(load_cell->bel, ctx->portPinFromId(load.port));
+ WireId user_wire = ctx->getBelPinWire(load_cell->bel, ctx->portPinFromId(load.port));
delay_t raw_wl = ctx->estimateDelay(drv_wire, user_wire);
float slack = ctx->getDelayNS(load.budget) - ctx->getDelayNS(raw_wl);
if (slack < 0)
diff --git a/common/placer1.cc b/common/placer1.cc
index 74a11040..025c7c15 100644
--- a/common/placer1.cc
+++ b/common/placer1.cc
@@ -80,6 +80,7 @@ class SAPlacer
size_t placed_cells = 0;
// Initial constraints placer
+ ctx->lock();
for (auto &cell_entry : ctx->cells) {
CellInfo *cell = cell_entry.second.get();
auto loc = cell->attrs.find(ctx->id("BEL"));
@@ -118,16 +119,19 @@ class SAPlacer
}
std::sort(autoplaced.begin(), autoplaced.end(), [](CellInfo *a, CellInfo *b) { return a->name < b->name; });
ctx->shuffle(autoplaced);
+ ctx->unlock();
// Place cells randomly initially
log_info("Creating initial placement for remaining %d cells.\n", int(autoplaced.size()));
for (auto cell : autoplaced) {
+ ctx->lock();
place_initial(cell);
placed_cells++;
if ((placed_cells - constr_placed_cells) % 500 == 0)
log_info(" initial placement placed %d/%d cells\n", int(placed_cells - constr_placed_cells),
int(autoplaced.size()));
+ ctx->unlock();
}
if ((placed_cells - constr_placed_cells) % 500 != 0)
log_info(" initial placement placed %d/%d cells\n", int(placed_cells - constr_placed_cells),
@@ -136,6 +140,7 @@ class SAPlacer
log_info("Running simulated annealing placer.\n");
// Calculate metric after initial placement
+ ctx->lock();
curr_metric = 0;
curr_tns = 0;
for (auto &net : ctx->nets) {
@@ -143,6 +148,7 @@ class SAPlacer
metrics[net.first] = wl;
curr_metric += wl;
}
+ ctx->unlock();
int n_no_progress = 0;
double avg_metric = curr_metric;
@@ -169,6 +175,7 @@ class SAPlacer
try_swap_position(cell, try_bel);
}
}
+
// Heuristic to improve placement on the 8k
if (improved)
n_no_progress = 0;
@@ -178,6 +185,7 @@ class SAPlacer
if (temp <= 1e-3 && n_no_progress >= 5) {
if (iter % 5 != 0)
log_info(" at iteration #%d: temp = %f, cost = %f\n", iter, temp, double(curr_metric));
+ ctx->unlock();
break;
}
@@ -232,8 +240,12 @@ class SAPlacer
metrics[net.first] = wl;
curr_metric += wl;
}
+
+ // Let the UI show visualization updates.
+ ctx->yield();
}
// Final post-pacement validitiy check
+ ctx->lock();
for (auto bel : ctx->getBels()) {
IdString cell = ctx->getBoundBelCell(bel);
if (!ctx->isBelLocationValid(bel)) {
@@ -251,6 +263,7 @@ class SAPlacer
}
}
}
+ ctx->unlock();
return true;
}
@@ -436,7 +449,9 @@ bool placer1(Context *ctx)
placer.place();
log_info("Checksum: 0x%08x\n", ctx->checksum());
#ifndef NDEBUG
+ ctx->lock();
ctx->check();
+ ctx->unlock();
#endif
return true;
} catch (log_execution_error_exception) {
diff --git a/common/router1.cc b/common/router1.cc
index 1ea50448..fbf3c467 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -251,7 +251,8 @@ struct Router
}
}
- Router(Context *ctx, RipupScoreboard &scores, IdString net_name, int user_idx = -1, bool reroute = false, bool ripup = false, delay_t ripup_penalty = 0)
+ Router(Context *ctx, RipupScoreboard &scores, IdString net_name, int user_idx = -1, bool reroute = false,
+ bool ripup = false, delay_t ripup_penalty = 0)
: ctx(ctx), scores(scores), net_name(net_name), ripup(ripup), ripup_penalty(ripup_penalty)
{
auto net_info = ctx->nets.at(net_name).get();
@@ -262,42 +263,26 @@ struct Router
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_bel = net_info->driver.cell->bel;
-
- if (src_bel == BelId())
- log_error("Source cell %s (%s) is not mapped to a bel.\n", net_info->driver.cell->name.c_str(ctx),
- net_info->driver.cell->type.c_str(ctx));
-
- if (ctx->debug)
- log(" Source bel: %s\n", ctx->getBelName(src_bel).c_str(ctx));
-
- IdString driver_port = net_info->driver.port;
-
- auto driver_port_it = net_info->driver.cell->pins.find(driver_port);
- if (driver_port_it != net_info->driver.cell->pins.end())
- driver_port = driver_port_it->second;
-
- auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port));
+ auto src_wire = ctx->getNetinfoSourceWire(net_info);
if (src_wire == WireId())
- log_error("No wire found for port %s (pin %s) on source cell %s "
- "(bel %s).\n",
- net_info->driver.port.c_str(ctx), driver_port.c_str(ctx), net_info->driver.cell->name.c_str(ctx),
- ctx->getBelName(src_bel).c_str(ctx));
+ 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<WireId, delay_t> src_wires;
- std::vector<PortRef> users_array;
+ std::vector<int> users_array;
if (user_idx < 0) {
// route all users
- users_array = net_info->users;
+ for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++)
+ users_array.push_back(user_idx);
ctx->shuffle(users_array);
} else {
// route only the selected user
- users_array.push_back(net_info->users[user_idx]);
+ users_array.push_back(user_idx);
}
if (reroute) {
@@ -311,32 +296,16 @@ struct Router
ctx->bindWire(src_wire, net_name, STRENGTH_WEAK);
src_wires[src_wire] = ctx->getWireDelay(src_wire).maxDelay();
- for (auto &user_it : net_info->users) {
- auto dst_bel = user_it.cell->bel;
-
- if (dst_bel == BelId())
- log_error("Destination cell %s (%s) is not mapped to a bel.\n", user_it.cell->name.c_str(ctx),
- user_it.cell->type.c_str(ctx));
-
- if (ctx->debug)
- log(" Destination bel: %s\n", ctx->getBelName(dst_bel).c_str(ctx));
-
- IdString user_port = user_it.port;
-
- auto user_port_it = user_it.cell->pins.find(user_port);
-
- if (user_port_it != user_it.cell->pins.end())
- user_port = user_port_it->second;
-
- auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
+ for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) {
+ auto dst_wire = ctx->getNetinfoSinkWire(net_info, user_idx);
if (dst_wire == WireId())
- log_error("No wire found for port %s (pin %s) on destination "
- "cell %s (bel %s).\n",
- user_it.port.c_str(ctx), user_port.c_str(ctx), user_it.cell->name.c_str(ctx),
- ctx->getBelName(dst_bel).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));
- std::function<delay_t(WireId)> register_existing_path = [ctx, net_info, &src_wires, &register_existing_path](WireId wire) -> delay_t {
+ std::function<delay_t(WireId)> register_existing_path =
+ [ctx, net_info, &src_wires, &register_existing_path](WireId wire) -> delay_t {
auto it = src_wires.find(wire);
if (it != src_wires.end())
return it->second;
@@ -345,7 +314,7 @@ struct Router
delay_t delay = register_existing_path(ctx->getPipSrcWire(pip));
delay += ctx->getPipDelay(pip).maxDelay();
delay += ctx->getWireDelay(wire).maxDelay();
- delay -= 2*ctx->getDelayEpsilon();
+ delay -= 2 * ctx->getDelayEpsilon();
src_wires[wire] = delay;
return delay;
@@ -361,7 +330,7 @@ struct Router
}
register_existing_path(dst_wire);
- check_next_user_for_existing_path:;
+ check_next_user_for_existing_path:;
}
std::vector<WireId> ripup_wires;
@@ -371,39 +340,22 @@ struct Router
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));
+ 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_it : users_array) {
+ for (int user_idx : users_array) {
if (ctx->debug)
- log(" Route to: %s.%s.\n", user_it.cell->name.c_str(ctx), user_it.port.c_str(ctx));
-
- auto dst_bel = user_it.cell->bel;
+ 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));
- if (dst_bel == BelId())
- log_error("Destination cell %s (%s) is not mapped to a bel.\n", user_it.cell->name.c_str(ctx),
- user_it.cell->type.c_str(ctx));
-
- if (ctx->debug)
- log(" Destination bel: %s\n", ctx->getBelName(dst_bel).c_str(ctx));
-
- IdString user_port = user_it.port;
-
- auto user_port_it = user_it.cell->pins.find(user_port);
-
- if (user_port_it != user_it.cell->pins.end())
- user_port = user_port_it->second;
-
- auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
+ auto dst_wire = ctx->getNetinfoSinkWire(net_info, user_idx);
if (dst_wire == WireId())
- log_error("No wire found for port %s (pin %s) on destination "
- "cell %s (bel %s).\n",
- user_it.port.c_str(ctx), user_port.c_str(ctx), user_it.cell->name.c_str(ctx),
- ctx->getBelName(dst_bel).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 (ctx->debug) {
log(" Destination wire: %s\n", ctx->getWireName(dst_wire).c_str(ctx));
@@ -499,34 +451,19 @@ struct RouteJob
};
};
-void addFullNetRouteJob(Context *ctx, IdString net_name,
- std::unordered_map<IdString, std::vector<bool>> &cache,
- std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> &queue)
+void addFullNetRouteJob(Context *ctx, IdString net_name, std::unordered_map<IdString, std::vector<bool>> &cache,
+ std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> &queue)
{
NetInfo *net_info = ctx->nets.at(net_name).get();
if (net_info->driver.cell == nullptr)
return;
- auto src_bel = net_info->driver.cell->bel;
-
- if (src_bel == BelId())
- log_error("Source cell %s (%s) is not mapped to a bel.\n", net_info->driver.cell->name.c_str(ctx),
- net_info->driver.cell->type.c_str(ctx));
-
- IdString driver_port = net_info->driver.port;
-
- auto driver_port_it = net_info->driver.cell->pins.find(driver_port);
- if (driver_port_it != net_info->driver.cell->pins.end())
- driver_port = driver_port_it->second;
-
- auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port));
+ auto src_wire = ctx->getNetinfoSourceWire(net_info);
if (src_wire == WireId())
- log_error("No wire found for port %s (pin %s) on source cell %s "
- "(bel %s).\n",
- net_info->driver.port.c_str(ctx), driver_port.c_str(ctx), net_info->driver.cell->name.c_str(ctx),
- ctx->getBelName(src_bel).c_str(ctx));
+ 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];
@@ -541,46 +478,30 @@ void addFullNetRouteJob(Context *ctx, IdString net_name,
bool got_slack = false;
- for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++)
- {
+ for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) {
if (net_cache[user_idx])
continue;
- auto &user_info = net_info->users[user_idx];
- auto dst_bel = user_info.cell->bel;
-
- if (dst_bel == BelId())
- log_error("Destination cell %s (%s) is not mapped to a bel.\n",
- user_info.cell->name.c_str(ctx), user_info.cell->type.c_str(ctx));
-
- IdString user_port = user_info.port;
-
- auto user_port_it = user_info.cell->pins.find(user_port);
-
- if (user_port_it != user_info.cell->pins.end())
- user_port = user_port_it->second;
-
- auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
+ auto dst_wire = ctx->getNetinfoSinkWire(net_info, user_idx);
if (dst_wire == WireId())
- log_error("No wire found for port %s (pin %s) on destination "
- "cell %s (bel %s).\n",
- user_info.port.c_str(ctx), user_port.c_str(ctx), user_info.cell->name.c_str(ctx),
- ctx->getBelName(dst_bel).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 (user_idx == 0)
- job.slack = user_info.budget - ctx->estimateDelay(src_wire, dst_wire);
+ job.slack = net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire);
else
- job.slack = std::min(job.slack, user_info.budget - ctx->estimateDelay(src_wire, dst_wire));
+ job.slack = std::min(job.slack, net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire));
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 = user_info.budget - ctx->estimateDelay(src_wire, dst_wire);
+ job.slack = net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire);
else
- job.slack = std::min(job.slack, user_info.budget - ctx->estimateDelay(src_wire, dst_wire));
+ job.slack = std::min(job.slack,
+ net_info->users[user_idx].budget - ctx->estimateDelay(src_wire, dst_wire));
got_slack = true;
break;
}
@@ -595,8 +516,7 @@ void addFullNetRouteJob(Context *ctx, IdString net_name,
net_cache[user_idx] = true;
}
-void addNetRouteJobs(Context *ctx, IdString net_name,
- std::unordered_map<IdString, std::vector<bool>> &cache,
+void addNetRouteJobs(Context *ctx, IdString net_name, std::unordered_map<IdString, std::vector<bool>> &cache,
std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> &queue)
{
NetInfo *net_info = ctx->nets.at(net_name).get();
@@ -604,73 +524,35 @@ void addNetRouteJobs(Context *ctx, IdString net_name,
if (net_info->driver.cell == nullptr)
return;
- auto src_bel = net_info->driver.cell->bel;
-
- if (src_bel == BelId())
- log_error("Source cell %s (%s) is not mapped to a bel.\n", net_info->driver.cell->name.c_str(ctx),
- net_info->driver.cell->type.c_str(ctx));
-
- IdString driver_port = net_info->driver.port;
-
- auto driver_port_it = net_info->driver.cell->pins.find(driver_port);
- if (driver_port_it != net_info->driver.cell->pins.end())
- driver_port = driver_port_it->second;
-
- auto src_wire = ctx->getWireBelPin(src_bel, ctx->portPinFromId(driver_port));
+ auto src_wire = ctx->getNetinfoSourceWire(net_info);
if (src_wire == WireId())
- log_error("No wire found for port %s (pin %s) on source cell %s "
- "(bel %s).\n",
- net_info->driver.port.c_str(ctx), driver_port.c_str(ctx), net_info->driver.cell->name.c_str(ctx),
- ctx->getBelName(src_bel).c_str(ctx));
+ 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++)
- {
+ for (int user_idx = 0; user_idx < int(net_info->users.size()); user_idx++) {
if (net_cache[user_idx])
continue;
- auto &user_info = net_info->users[user_idx];
- auto dst_bel = user_info.cell->bel;
-
- if (dst_bel == BelId())
- log_error("Destination cell %s (%s) is not mapped to a bel.\n",
- user_info.cell->name.c_str(ctx), user_info.cell->type.c_str(ctx));
-
- IdString user_port = user_info.port;
-
- auto user_port_it = user_info.cell->pins.find(user_port);
-
- if (user_port_it != user_info.cell->pins.end())
- user_port = user_port_it->second;
-
- auto dst_wire = ctx->getWireBelPin(dst_bel, ctx->portPinFromId(user_port));
+ auto dst_wire = ctx->getNetinfoSinkWire(net_info, user_idx);
if (dst_wire == WireId())
- log_error("No wire found for port %s (pin %s) on destination "
- "cell %s (bel %s).\n",
- user_info.port.c_str(ctx), user_port.c_str(ctx), user_info.cell->name.c_str(ctx),
- ctx->getBelName(dst_bel).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));
WireId cursor = dst_wire;
while (src_wire != cursor) {
auto it = net_info->wires.find(cursor);
if (it == net_info->wires.end()) {
- if (ctx->debug)
- log("Adding job [%s %d]: %s %s (%s) -> %s %s (%s)\n",
- net_name.c_str(ctx), user_idx,
- ctx->getBelName(src_bel).c_str(ctx), driver_port.c_str(ctx),
- ctx->getWireName(src_wire).c_str(ctx),
- ctx->getBelName(dst_bel).c_str(ctx), user_port.c_str(ctx),
- ctx->getWireName(dst_wire).c_str(ctx));
RouteJob job;
job.net = net_name;
job.user_idx = user_idx;
- job.slack = user_info.budget - ctx->estimateDelay(src_wire, dst_wire);
+ 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;
@@ -695,6 +577,7 @@ bool router1(Context *ctx)
log_break();
log_info("Routing..\n");
+ ctx->lock();
std::unordered_map<IdString, std::vector<bool>> jobCache;
std::priority_queue<RouteJob, std::vector<RouteJob>, RouteJob::Greater> jobQueue;
@@ -735,7 +618,7 @@ bool router1(Context *ctx)
bool printNets = ctx->verbose && (jobQueue.size() < 10);
while (!jobQueue.empty()) {
- if(ctx->debug)
+ if (ctx->debug)
log("Next job slack: %f\n", double(jobQueue.top().slack));
auto net_name = jobQueue.top().net;
@@ -744,8 +627,8 @@ bool router1(Context *ctx)
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));
+ 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));
}
@@ -766,15 +649,19 @@ bool router1(Context *ctx)
normalRouteNets.insert(net_name);
}
- if ((ctx->verbose || iterCnt == 1) && !printNets && (jobCnt % 100 == 0))
+ 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))
+ 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 "
@@ -828,8 +715,10 @@ bool router1(Context *ctx)
ripCnt += router.rippedNets.size();
- if ((ctx->verbose || iterCnt == 1) && !printNets && (netCnt % 100 == 0))
+ 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))
@@ -847,9 +736,8 @@ bool router1(Context *ctx)
}
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()));
-
+ 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;
@@ -857,6 +745,8 @@ bool router1(Context *ctx)
if (iterCnt == 8 || iterCnt == 16 || iterCnt == 32 || iterCnt == 64 || iterCnt == 128)
ripup_penalty += ctx->getRipupDelayPenalty();
+
+ ctx->yield();
}
log_info("routing complete after %d iterations.\n", iterCnt);
@@ -866,6 +756,38 @@ bool router1(Context *ctx)
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, 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, 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();
@@ -888,11 +810,13 @@ bool router1(Context *ctx)
log_info("Checksum: 0x%08x\n", ctx->checksum());
#ifndef NDEBUG
ctx->check();
+ ctx->unlock();
#endif
return true;
} catch (log_execution_error_exception) {
#ifndef NDEBUG
ctx->check();
+ ctx->unlock();
#endif
return false;
}