aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/archcheck.cc5
-rw-r--r--common/nextpnr.h54
-rw-r--r--common/place_common.cc22
-rw-r--r--common/placer1.cc40
-rw-r--r--common/router1.cc41
-rw-r--r--common/timing.cc274
-rw-r--r--common/timing.h5
7 files changed, 233 insertions, 208 deletions
diff --git a/common/archcheck.cc b/common/archcheck.cc
index 5c4ef26c..5059066d 100644
--- a/common/archcheck.cc
+++ b/common/archcheck.cc
@@ -17,8 +17,8 @@
*
*/
-#include "nextpnr.h"
#include "log.h"
+#include "nextpnr.h"
#if 0
#define dbg(...) log(__VA_ARGS__)
@@ -84,8 +84,7 @@ void archcheck_locs(const Context *ctx)
log_info("Checking all locations..\n");
for (int x = 0; x < ctx->getGridDimX(); x++)
- for (int y = 0; y < ctx->getGridDimY(); y++)
- {
+ for (int y = 0; y < ctx->getGridDimY(); y++) {
dbg("> %d %d\n", x, y);
std::unordered_set<int> usedz;
diff --git a/common/nextpnr.h b/common/nextpnr.h
index 021772fe..c4c05ae0 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -23,10 +23,10 @@
#include <condition_variable>
#include <memory>
#include <mutex>
-#include <pthread.h>
#include <stdexcept>
#include <stdint.h>
#include <string>
+#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -79,19 +79,19 @@ class assertion_failure : public std::runtime_error
};
NPNR_NORETURN
-inline bool assert_fail_impl(const char *message, const char *expr_str, const char *filename, int line)
+inline void assert_fail_impl(const char *message, const char *expr_str, const char *filename, int line)
{
throw assertion_failure(message, expr_str, filename, line);
}
NPNR_NORETURN
-inline bool assert_fail_impl_str(std::string message, const char *expr_str, const char *filename, int line)
+inline void assert_fail_impl_str(std::string message, const char *expr_str, const char *filename, int line)
{
throw assertion_failure(message, expr_str, filename, line);
}
-#define NPNR_ASSERT(cond) ((void)((cond) || (assert_fail_impl(#cond, #cond, __FILE__, __LINE__))))
-#define NPNR_ASSERT_MSG(cond, msg) ((void)((cond) || (assert_fail_impl(msg, #cond, __FILE__, __LINE__))))
+#define NPNR_ASSERT(cond) (!(cond) ? assert_fail_impl(#cond, #cond, __FILE__, __LINE__) : (void)true)
+#define NPNR_ASSERT_MSG(cond, msg) (!(cond) ? assert_fail_impl(msg, #cond, __FILE__, __LINE__) : (void)true)
#define NPNR_ASSERT_FALSE(msg) (assert_fail_impl(msg, "false", __FILE__, __LINE__))
#define NPNR_ASSERT_FALSE_STR(msg) (assert_fail_impl_str(msg, "false", __FILE__, __LINE__))
@@ -145,20 +145,25 @@ struct GraphicElement
{
enum type_t
{
- G_NONE,
- G_LINE,
- G_BOX,
- G_CIRCLE,
- G_LABEL
- } type = G_NONE;
+ TYPE_NONE,
+ TYPE_LINE,
+ TYPE_ARROW,
+ TYPE_BOX,
+ TYPE_CIRCLE,
+ TYPE_LABEL,
+
+ TYPE_MAX
+ } type = TYPE_NONE;
enum style_t
{
- G_FRAME,
- G_HIDDEN,
- G_INACTIVE,
- G_ACTIVE,
- } style = G_FRAME;
+ STYLE_FRAME, // Static "frame". Contrast between STYLE_INACTIVE and STYLE_ACTIVE
+ STYLE_HIDDEN, // Only display when object is selected or highlighted
+ STYLE_INACTIVE, // Render using low-contrast color
+ STYLE_ACTIVE, // Render using high-contast color
+
+ STYLE_MAX
+ } style = STYLE_FRAME;
float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0;
std::string text;
@@ -272,6 +277,16 @@ struct CellInfo : ArchCellInfo
// cell_port -> bel_pin
std::unordered_map<IdString, IdString> pins;
+
+ // placement constraints
+ CellInfo *constr_parent;
+ std::vector<CellInfo *> constr_children;
+ const int UNCONSTR = INT_MIN;
+ int constr_x = UNCONSTR; // this.x - parent.x
+ int constr_y = UNCONSTR; // this.y - parent.y
+ int constr_z = UNCONSTR; // this.z - parent.z
+ bool constr_abs_z = false; // parent.z := 0
+ // parent.[xyz] := 0 when (constr_parent == nullptr)
};
struct DeterministicRNG
@@ -343,7 +358,7 @@ struct BaseCtx
{
// Lock to perform mutating actions on the Context.
std::mutex mutex;
- pthread_t mutex_owner;
+ std::thread::id 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
@@ -376,12 +391,12 @@ struct BaseCtx
void lock(void)
{
mutex.lock();
- mutex_owner = pthread_self();
+ mutex_owner = std::this_thread::get_id();
}
void unlock(void)
{
- NPNR_ASSERT(pthread_equal(pthread_self(), mutex_owner) != 0);
+ NPNR_ASSERT(std::this_thread::get_id() == mutex_owner);
mutex.unlock();
}
@@ -455,6 +470,7 @@ struct Context : Arch, DeterministicRNG
bool force = false;
bool timing_driven = true;
float target_freq = 12e6;
+ bool user_freq = false;
Context(ArchArgs args) : Arch(args) {}
diff --git a/common/place_common.cc b/common/place_common.cc
index 95b7b2aa..5673c847 100644
--- a/common/place_common.cc
+++ b/common/place_common.cc
@@ -28,19 +28,20 @@ NEXTPNR_NAMESPACE_BEGIN
wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type, float &tns)
{
wirelen_t wirelength = 0;
- int driver_x, driver_y;
+ Loc driver_loc;
bool driver_gb;
CellInfo *driver_cell = net->driver.cell;
if (!driver_cell)
return 0;
if (driver_cell->bel == BelId())
return 0;
- ctx->estimatePosition(driver_cell->bel, driver_x, driver_y, driver_gb);
+ driver_gb = ctx->getBelGlobalBuf(driver_cell->bel);
+ driver_loc = ctx->getBelLocation(driver_cell->bel);
WireId drv_wire = ctx->getBelPinWire(driver_cell->bel, ctx->portPinFromId(net->driver.port));
if (driver_gb)
return 0;
float worst_slack = 1000;
- int xmin = driver_x, xmax = driver_x, ymin = driver_y, ymax = driver_y;
+ int xmin = driver_loc.x, xmax = driver_loc.x, ymin = driver_loc.y, ymax = driver_loc.y;
for (auto load : net->users) {
if (load.cell == nullptr)
continue;
@@ -56,15 +57,14 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type
worst_slack = std::min(slack, worst_slack);
}
- int load_x, load_y;
- bool load_gb;
- ctx->estimatePosition(load_cell->bel, load_x, load_y, load_gb);
- if (load_gb)
+ if (ctx->getBelGlobalBuf(load_cell->bel))
continue;
- xmin = std::min(xmin, load_x);
- ymin = std::min(ymin, load_y);
- xmax = std::max(xmax, load_x);
- ymax = std::max(ymax, load_y);
+ Loc load_loc = ctx->getBelLocation(load_cell->bel);
+
+ xmin = std::min(xmin, load_loc.x);
+ ymin = std::min(ymin, load_loc.y);
+ xmax = std::max(xmax, load_loc.x);
+ ymax = std::max(ymax, load_loc.y);
}
if (ctx->timing_driven && type == MetricType::COST) {
wirelength = wirelen_t((((ymax - ymin) + (xmax - xmin)) * std::min(5.0, (1.0 + std::exp(-worst_slack / 5)))));
diff --git a/common/placer1.cc b/common/placer1.cc
index 461fc4e8..264f1eba 100644
--- a/common/placer1.cc
+++ b/common/placer1.cc
@@ -50,9 +50,7 @@ class SAPlacer
{
int num_bel_types = 0;
for (auto bel : ctx->getBels()) {
- int x, y;
- bool gb;
- ctx->estimatePosition(bel, x, y, gb);
+ Loc loc = ctx->getBelLocation(bel);
BelType type = ctx->getBelType(bel);
int type_idx;
if (bel_types.find(type) == bel_types.end()) {
@@ -63,13 +61,13 @@ class SAPlacer
}
if (int(fast_bels.size()) < type_idx + 1)
fast_bels.resize(type_idx + 1);
- if (int(fast_bels.at(type_idx).size()) < (x + 1))
- fast_bels.at(type_idx).resize(x + 1);
- if (int(fast_bels.at(type_idx).at(x).size()) < (y + 1))
- fast_bels.at(type_idx).at(x).resize(y + 1);
- max_x = std::max(max_x, x);
- max_y = std::max(max_y, y);
- fast_bels.at(type_idx).at(x).at(y).push_back(bel);
+ if (int(fast_bels.at(type_idx).size()) < (loc.x + 1))
+ fast_bels.at(type_idx).resize(loc.x + 1);
+ if (int(fast_bels.at(type_idx).at(loc.x).size()) < (loc.y + 1))
+ fast_bels.at(type_idx).at(loc.x).resize(loc.y + 1);
+ max_x = std::max(max_x, loc.x);
+ max_y = std::max(max_y, loc.y);
+ fast_bels.at(type_idx).at(loc.x).at(loc.y).push_back(bel);
}
diameter = std::max(max_x, max_y) + 1;
}
@@ -96,7 +94,13 @@ class SAPlacer
BelType bel_type = ctx->getBelType(bel);
if (bel_type != ctx->belTypeFromId(cell->type)) {
log_error("Bel \'%s\' of type \'%s\' does not match cell "
- "\'%s\' of type \'%s\'",
+ "\'%s\' of type \'%s\'\n",
+ loc_name.c_str(), ctx->belTypeToId(bel_type).c_str(ctx), cell->name.c_str(ctx),
+ cell->type.c_str(ctx));
+ }
+ if (!ctx->isValidBelForCell(cell, bel)) {
+ log_error("Bel \'%s\' of type \'%s\' is not valid for cell "
+ "\'%s\' of type \'%s\'\n",
loc_name.c_str(), ctx->belTypeToId(bel_type).c_str(ctx), cell->name.c_str(ctx),
cell->type.c_str(ctx));
}
@@ -235,8 +239,7 @@ class SAPlacer
diameter *= post_legalise_dia_scale;
ctx->shuffle(autoplaced);
assign_budget(ctx);
- }
- else {
+ } else {
update_budget(ctx);
}
@@ -272,6 +275,7 @@ class SAPlacer
}
}
}
+ compute_fmax(ctx, true /* print_fmax */);
ctx->unlock();
return true;
}
@@ -387,8 +391,6 @@ class SAPlacer
// SA acceptance criterea
if (delta < 0 || (temp > 1e-6 && (ctx->rng() / float(0x3fffffff)) <= std::exp(-delta / temp))) {
n_accept++;
- //if (delta < 2)
- // improved = true;
} else {
if (other != IdString())
ctx->unbindBel(oldBel);
@@ -413,12 +415,10 @@ class SAPlacer
BelId random_bel_for_cell(CellInfo *cell)
{
BelType targetType = ctx->belTypeFromId(cell->type);
- int x, y;
- bool gb;
- ctx->estimatePosition(cell->bel, x, y, gb);
+ Loc curr_loc = ctx->getBelLocation(cell->bel);
while (true) {
- int nx = ctx->rng(2 * diameter + 1) + std::max(x - diameter, 0);
- int ny = ctx->rng(2 * diameter + 1) + std::max(y - diameter, 0);
+ int nx = ctx->rng(2 * diameter + 1) + std::max(curr_loc.x - diameter, 0);
+ int ny = ctx->rng(2 * diameter + 1) + std::max(curr_loc.y - diameter, 0);
int beltype_idx = bel_types.at(targetType);
if (nx >= int(fast_bels.at(beltype_idx).size()))
continue;
diff --git a/common/router1.cc b/common/router1.cc
index 431770da..2ae54245 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -613,38 +613,10 @@ bool router1(Context *ctx)
std::unordered_set<IdString> normalRouteNets, ripupQueue;
- if (iterCnt == 1) {
- if (ctx->verbose)
- log_info("routing queue contains %d jobs.\n", int(jobQueue.size()));
- } else {
- static auto actual_delay = [](Context *ctx, WireId src, WireId dst) {
- delay_t total_delay = 0;
- WireId last = dst;
- auto net_name = ctx->getBoundWireNet(src);
- if (net_name != IdString()) {
- auto net = ctx->nets.at(net_name).get();
- while (last != src) {
- total_delay += ctx->getWireDelay(last).maxDelay();
- auto pip = net->wires.at(last).pip;
- NPNR_ASSERT(ctx->getBoundPipNet(pip) == net_name);
- total_delay += ctx->getPipDelay(pip).maxDelay();
- last = ctx->getPipSrcWire(pip);
- if (ctx->getBoundWireNet(last) != net_name) {
- log_warning("Wire %s bound to %s not %s!\n", ctx->getWireName(last).c_str(ctx), ctx->getBoundWireNet(last).c_str(ctx), net_name.c_str(ctx));
- break;
- }
- NPNR_ASSERT(ctx->getBoundWireNet(last) == net_name);
- }
- NPNR_ASSERT(last != WireId());
- }
- if (last != src)
- total_delay += ctx->estimateDelay(src, last);
- else
- total_delay += ctx->getWireDelay(last).maxDelay();
- return total_delay;
- };
- update_budget(ctx, actual_delay);
- }
+ if (ctx->verbose || iterCnt == 1)
+ log_info("routing queue contains %d jobs.\n", int(jobQueue.size()));
+
+ update_budget(ctx);
bool printNets = ctx->verbose && (jobQueue.size() < 10);
@@ -841,14 +813,15 @@ bool router1(Context *ctx)
log_info("Checksum: 0x%08x\n", ctx->checksum());
#ifndef NDEBUG
ctx->check();
- ctx->unlock();
#endif
+ compute_fmax(ctx, true /* print_fmax */, true /* print_path */);
+ ctx->unlock();
return true;
} catch (log_execution_error_exception) {
#ifndef NDEBUG
ctx->check();
- ctx->unlock();
#endif
+ ctx->unlock();
return false;
}
}
diff --git a/common/timing.cc b/common/timing.cc
index 0e84dded..afaddbdf 100644
--- a/common/timing.cc
+++ b/common/timing.cc
@@ -26,16 +26,26 @@
NEXTPNR_NAMESPACE_BEGIN
-static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, delay_t slack);
+typedef std::unordered_map<const PortInfo *, delay_t> UpdateMap;
+typedef std::list<const PortRef *> PortRefList;
+
+static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, delay_t slack, UpdateMap *updates,
+ delay_t &min_slack, PortRefList *current_path, PortRefList *crit_path);
// Follow a path, returning budget to annotate
-static delay_t follow_user_port(Context *ctx, PortRef &user, int path_length, delay_t slack)
+static delay_t follow_user_port(Context *ctx, PortRef &user, int path_length, delay_t slack, UpdateMap *updates,
+ delay_t &min_slack, PortRefList *current_path, PortRefList *crit_path)
{
delay_t value;
if (ctx->getPortClock(user.cell, user.port) != IdString()) {
// At the end of a timing path (arguably, should check setup time
// here too)
value = slack / path_length;
+ if (slack < min_slack) {
+ min_slack = slack;
+ if (crit_path)
+ *crit_path = *current_path;
+ }
} else {
// Default to the path ending here, if no further paths found
value = slack / path_length;
@@ -48,7 +58,8 @@ static delay_t follow_user_port(Context *ctx, PortRef &user, int path_length, de
if (is_path) {
NetInfo *net = port.second.net;
if (net) {
- delay_t path_budget = follow_net(ctx, net, path_length, slack - comb_delay);
+ delay_t path_budget = follow_net(ctx, net, path_length, slack - comb_delay, updates, min_slack,
+ current_path, crit_path);
value = std::min(value, path_budget);
}
}
@@ -56,50 +67,107 @@ static delay_t follow_user_port(Context *ctx, PortRef &user, int path_length, de
}
}
- if (value < user.budget) {
- user.budget = value;
+ if (updates) {
+ auto ret = updates->emplace(&user.cell->ports.at(user.port), value);
+ if (!ret.second)
+ ret.first->second = std::min(value, ret.first->second);
}
return value;
}
-static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, delay_t slack)
+static delay_t follow_net(Context *ctx, NetInfo *net, int path_length, delay_t slack, UpdateMap *updates,
+ delay_t &min_slack, PortRefList *current_path, PortRefList *crit_path)
{
delay_t net_budget = slack / (path_length + 1);
- for (auto &usr : net->users) {
- net_budget = std::min(net_budget, follow_user_port(ctx, usr, path_length + 1, slack));
+ for (unsigned i = 0; i < net->users.size(); ++i) {
+ auto &usr = net->users[i];
+ if (crit_path)
+ current_path->push_back(&usr);
+ // If budget override is less than existing budget, then do not increment path length
+ int pl = path_length + 1;
+ auto budget = ctx->getBudgetOverride(net, i, net_budget);
+ if (budget < net_budget) {
+ net_budget = budget;
+ pl = std::max(1, path_length);
+ }
+ net_budget = std::min(net_budget,
+ follow_user_port(ctx, usr, pl, slack - ctx->getNetinfoRouteDelay(net, i),
+ updates, min_slack, current_path, crit_path));
+ if (crit_path)
+ current_path->pop_back();
}
return net_budget;
}
-void assign_budget(Context *ctx)
+static delay_t compute_min_slack(Context *ctx, UpdateMap *updates, PortRefList *crit_path)
{
- log_break();
- log_info("Annotating ports with timing budgets\n");
- // Clear delays to a very high value first
delay_t default_slack = delay_t(1.0e12 / ctx->target_freq);
- for (auto &net : ctx->nets) {
- for (auto &usr : net.second->users) {
- usr.budget = default_slack;
- }
- }
- // Go through all clocked drivers and set up paths
+ delay_t min_slack = default_slack;
+
+ PortRefList current_path;
+
+ // Go through all clocked drivers and distribute the available path
+ // slack evenly into the budget of every sink on the path ---
+ // record this value into the UpdateMap
for (auto &cell : ctx->cells) {
for (auto port : cell.second->ports) {
if (port.second.type == PORT_OUT) {
IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first);
if (clock_domain != IdString()) {
- delay_t slack = delay_t(1.0e12 / ctx->target_freq); // TODO: clock constraints
+ delay_t slack = default_slack; // TODO: clock constraints
+ delay_t clkToQ;
+ if (ctx->getCellDelay(cell.second.get(), clock_domain, port.first, clkToQ))
+ slack -= clkToQ;
if (port.second.net)
- follow_net(ctx, port.second.net, 0, slack);
+ follow_net(ctx, port.second.net, 0, slack, updates, min_slack, &current_path, crit_path);
}
}
}
}
- // Post-allocation check
+ return min_slack;
+}
+
+void assign_budget(Context *ctx)
+{
+ log_break();
+ log_info("Annotating ports with timing budgets\n");
+ // Clear delays to a very high value first
+ delay_t default_slack = delay_t(1.0e12 / ctx->target_freq);
+ for (auto &net : ctx->nets) {
+ for (auto &usr : net.second->users) {
+ usr.budget = default_slack;
+ }
+ }
+
+ UpdateMap updates;
+ delay_t min_slack = compute_min_slack(ctx, &updates, nullptr);
+
+ // If user has not specified a frequency, adjust the target frequency dynamically
+ // TODO(eddieh): Tune these factors
+ if (!ctx->user_freq) {
+ if (min_slack < 0)
+ ctx->target_freq = 1e12 / (default_slack - 0.95 * min_slack);
+ else
+ ctx->target_freq = 1e12 / (default_slack - 1.2 * min_slack);
+ if (ctx->verbose)
+ log_info("minimum slack for this assign = %d, target Fmax for next update = %.2f MHz\n", min_slack,
+ ctx->target_freq / 1e6);
+ }
+
+ // Update the budgets
for (auto &net : ctx->nets) {
- for (auto user : net.second->users) {
- if (user.budget < 0)
+ for (size_t i = 0; i < net.second->users.size(); ++i) {
+ auto &user = net.second->users[i];
+ auto pi = &user.cell->ports.at(user.port);
+ auto it = updates.find(pi);
+ if (it == updates.end())
+ continue;
+ auto budget = ctx->getNetinfoRouteDelay(net.second.get(), i) - it->second;
+ user.budget = ctx->getBudgetOverride(net.second.get(), i, budget);
+
+ // Post-update check
+ if (ctx->user_freq && user.budget < 0)
log_warning("port %s.%s, connected to net '%s', has negative "
"timing budget of %fns\n",
user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
@@ -115,112 +183,25 @@ void assign_budget(Context *ctx)
log_info("Checksum: 0x%08x\n", ctx->checksum());
}
-typedef std::unordered_map<const PortInfo*, delay_t> updates_t;
-typedef std::unordered_map<const PortInfo*, delay_t> delays_t;
-
-static delay_t follow_net_update(Context *ctx, NetInfo *net, int path_length, delay_t slack, const delays_t& delays, updates_t& updates);
-
-// Follow a path, returning budget to annotate
-static delay_t follow_user_port_update(Context *ctx, PortRef &user, int path_length, delay_t slack, const delays_t& delays, updates_t& updates)
-{
- delay_t value;
- if (ctx->getPortClock(user.cell, user.port) != IdString()) {
- // At the end of a timing path (arguably, should check setup time
- // here too)
- value = slack / path_length;
- } else {
- // Default to the path ending here, if no further paths found
- value = slack / path_length;
- // Follow outputs of the user
- for (auto& port : user.cell->ports) {
- if (port.second.type == PORT_OUT) {
- delay_t comb_delay;
- // Look up delay through this path
- bool is_path = ctx->getCellDelay(user.cell, user.port, port.first, comb_delay);
- if (is_path) {
- NetInfo *net = port.second.net;
- if (net) {
- delay_t path_budget = follow_net_update(ctx, net, path_length, slack - comb_delay, delays, updates);
- value = std::min(value, path_budget);
- }
- }
- }
- }
- }
-
- auto ret = updates.emplace(&user.cell->ports.at(user.port), value);
- if (!ret.second && value < ret.first->second) {
- ret.first->second = value;
- }
- return value;
-}
-
-static delay_t follow_net_update(Context *ctx, NetInfo *net, int path_length, delay_t slack, const delays_t& delays,updates_t& updates)
-{
- delay_t net_budget = slack / (path_length + 1);
- for (auto& usr : net->users) {
- net_budget = std::min(net_budget, follow_user_port_update(ctx, usr, path_length + 1, slack - get_or_default(delays, &usr.cell->ports.at(usr.port), 0.), delays, updates));
- }
- return net_budget;
-}
-
-void update_budget(Context *ctx, std::function<delay_t(Context*,WireId,WireId)> delay_fn)
+void update_budget(Context *ctx)
{
- delays_t delays;
- updates_t updates;
-
- // Compute the delay for every pin on every net
- for (auto &n : ctx->nets) {
- auto net = n.second.get();
-
- int driver_x, driver_y;
- bool driver_gb;
- CellInfo *driver_cell = net->driver.cell;
- if (!driver_cell)
- continue;
- if (driver_cell->bel == BelId())
- continue;
- ctx->estimatePosition(driver_cell->bel, driver_x, driver_y, driver_gb);
- WireId drv_wire = ctx->getWireBelPin(driver_cell->bel, ctx->portPinFromId(net->driver.port));
- if (driver_gb)
- continue;
- for (auto& load : net->users) {
- if (load.cell == nullptr)
- continue;
- CellInfo *load_cell = load.cell;
- if (load_cell->bel == BelId())
- continue;
- WireId user_wire = ctx->getWireBelPin(load_cell->bel, ctx->portPinFromId(load.port));
- delay_t raw_wl = delay_fn(ctx, drv_wire, user_wire);
- delays.emplace(&load_cell->ports.at(load.port), raw_wl);
- }
- }
-
- // Go through all clocked drivers and distribute the available path slack evenly into every budget
- for (auto &cell : ctx->cells) {
- for (auto& port : cell.second->ports) {
- if (port.second.type == PORT_OUT) {
- IdString clock_domain = ctx->getPortClock(cell.second.get(), port.first);
- if (clock_domain != IdString()) {
- if (port.second.net)
- follow_net_update(ctx, port.second.net, 0, delay_t(1.0e12 / ctx->target_freq) - get_or_default(delays, &port.second, 0.), delays, updates);
- }
- }
- }
- }
+ UpdateMap updates;
+ delay_t min_slack = compute_min_slack(ctx, &updates, nullptr);
// Update the budgets
for (auto &net : ctx->nets) {
- for (auto& user : net.second->users) {
+ for (size_t i = 0; i < net.second->users.size(); ++i) {
+ auto &user = net.second->users[i];
auto pi = &user.cell->ports.at(user.port);
+ auto budget = ctx->getNetinfoRouteDelay(net.second.get(), i);
auto it = updates.find(pi);
- if (it == updates.end()) continue;
- auto budget = delays.at(pi) + it->second;
- user.budget = ctx->getBudgetOverride(net.second->driver, budget);
+ if (it != updates.end())
+ budget -= it->second;
+ user.budget = ctx->getBudgetOverride(net.second.get(), i, budget);
// Post-update check
if (ctx->verbose) {
- if (user.budget < 0)
+ if (ctx->user_freq && user.budget < 0)
log_warning("port %s.%s, connected to net '%s', has negative "
"timing budget of %fns\n",
user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
@@ -233,6 +214,59 @@ void update_budget(Context *ctx, std::function<delay_t(Context*,WireId,WireId)>
}
}
}
+
+ // If user has not specified a frequency, adjust the frequency dynamically:
+ if (!ctx->user_freq) {
+ delay_t default_slack = delay_t(1.0e12 / ctx->target_freq);
+ ctx->target_freq = 1e12 / (default_slack - min_slack);
+ if (ctx->verbose)
+ log_info("minimum slack for this update = %d, target Fmax for next update = %.2f MHz\n", min_slack,
+ ctx->target_freq / 1e6);
+ }
+}
+
+void compute_fmax(Context *ctx, bool print_fmax, bool print_path)
+{
+ delay_t default_slack = delay_t(1.0e12 / ctx->target_freq);
+ PortRefList crit_path;
+ delay_t min_slack = compute_min_slack(ctx, nullptr, &crit_path);
+ if (print_path) {
+ delay_t total = 0;
+ log_break();
+ log_info("Critical path report:\n");
+ log_info("curr total\n");
+ auto &front = crit_path.front();
+ auto &front_port = front->cell->ports.at(front->port);
+ auto &front_driver = front_port.net->driver;
+ auto last_port = ctx->getPortClock(front_driver.cell, front_driver.port);
+ for (auto sink : crit_path) {
+ auto sink_cell = sink->cell;
+ auto &port = sink_cell->ports.at(sink->port);
+ auto net = port.net;
+ unsigned i = 0;
+ for (auto &usr : net->users)
+ if (&usr == sink) break;
+ else ++i;
+ auto &driver = net->driver;
+ auto driver_cell = driver.cell;
+ delay_t comb_delay;
+ ctx->getCellDelay(sink_cell, last_port, driver.port, comb_delay);
+ total += comb_delay;
+ log_info("%4d %4d Source %s.%s\n", comb_delay, total, driver_cell->name.c_str(ctx),
+ driver.port.c_str(ctx));
+ delay_t net_delay = ctx->getNetinfoRouteDelay(net, i);
+ total += net_delay;
+ auto driver_loc = ctx->getBelLocation(driver_cell->bel);
+ auto sink_loc = ctx->getBelLocation(sink_cell->bel);
+ log_info("%4d %4d Net %s budget %d (%d,%d) -> (%d,%d)\n", net_delay, total, net->name.c_str(ctx),
+ 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));
+ last_port = sink->port;
+ }
+ log_break();
+ }
+ if (print_fmax)
+ log_info("estimated Fmax = %.2f MHz\n", 1e6 / (default_slack - min_slack));
}
NEXTPNR_NAMESPACE_END
diff --git a/common/timing.h b/common/timing.h
index 8c098963..a1e12ab3 100644
--- a/common/timing.h
+++ b/common/timing.h
@@ -27,7 +27,10 @@ NEXTPNR_NAMESPACE_BEGIN
// Assign "budget" values for all user ports in the design
void assign_budget(Context *ctx);
-void update_budget(Context *ctx, std::function<delay_t(Context*,WireId,WireId)> delay_fn=&Context::estimateDelay);
+// Evenly redistribute the total path slack amongst all sinks on each path
+void update_budget(Context *ctx);
+
+void compute_fmax(Context *ctx, bool print_fmax = false, bool print_path = false);
NEXTPNR_NAMESPACE_END