aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/command.cc36
-rw-r--r--common/command.h1
-rw-r--r--common/log.cc123
-rw-r--r--common/log.h20
-rw-r--r--common/placer1.cc28
-rw-r--r--common/placer1.h1
-rw-r--r--common/pybindings.cc26
-rw-r--r--common/pycontainers.h59
-rw-r--r--common/router1.cc12
-rw-r--r--common/timing.cc30
-rw-r--r--common/timing.h3
11 files changed, 205 insertions, 134 deletions
diff --git a/common/command.cc b/common/command.cc
index c5c777e8..6cc9fbe7 100644
--- a/common/command.cc
+++ b/common/command.cc
@@ -40,7 +40,7 @@
NEXTPNR_NAMESPACE_BEGIN
-CommandHandler::CommandHandler(int argc, char **argv) : argc(argc), argv(argv) { log_files.push_back(stdout); }
+CommandHandler::CommandHandler(int argc, char **argv) : argc(argc), argv(argv) { log_streams.clear(); }
bool CommandHandler::parseOptions()
{
@@ -64,14 +64,14 @@ bool CommandHandler::parseOptions()
bool CommandHandler::executeBeforeContext()
{
if (vm.count("help") || argc == 1) {
- std::cout << boost::filesystem::basename(argv[0])
+ std::cerr << boost::filesystem::basename(argv[0])
<< " -- Next Generation Place and Route (git sha1 " GIT_COMMIT_HASH_STR ")\n";
- std::cout << options << "\n";
+ std::cerr << options << "\n";
return argc != 1;
}
if (vm.count("version")) {
- std::cout << boost::filesystem::basename(argv[0])
+ std::cerr << boost::filesystem::basename(argv[0])
<< " -- Next Generation Place and Route (git sha1 " GIT_COMMIT_HASH_STR ")\n";
return true;
}
@@ -84,7 +84,9 @@ po::options_description CommandHandler::getGeneralOptions()
po::options_description general("General options");
general.add_options()("help,h", "show help");
general.add_options()("verbose,v", "verbose output");
- general.add_options()("quiet,q", "quiet mode, only errors displayed");
+ general.add_options()("quiet,q", "quiet mode, only errors and warnings displayed");
+ general.add_options()("log,l", po::value<std::string>(),
+ "log file, all log messages are written to this file regardless of -q");
general.add_options()("debug", "debug output");
general.add_options()("force,f", "keep running after errors");
#ifndef NO_GUI
@@ -102,6 +104,7 @@ po::options_description CommandHandler::getGeneralOptions()
#endif
general.add_options()("json", po::value<std::string>(), "JSON design file to ingest");
general.add_options()("seed", po::value<int>(), "seed value for random number generator");
+ general.add_options()("randomize-seed,r", "randomize seed value for random number generator");
general.add_options()("slack_redist_iter", po::value<int>(), "number of iterations between slack redistribution");
general.add_options()("cstrweight", po::value<float>(), "placer weighting for relative constraint satisfaction");
general.add_options()("pack-only", "pack design only without placement or routing");
@@ -127,7 +130,17 @@ void CommandHandler::setupContext(Context *ctx)
}
if (vm.count("quiet")) {
- log_quiet_warnings = true;
+ log_streams.push_back(std::make_pair(&std::cerr, LogLevel::WARNING));
+ } else {
+ log_streams.push_back(std::make_pair(&std::cerr, LogLevel::LOG));
+ }
+
+ if (vm.count("log")) {
+ std::string logfilename = vm["log"].as<std::string>();
+ logfile = std::ofstream(logfilename);
+ if (!logfile)
+ log_error("Failed to open log file '%s' for writing.\n", logfilename.c_str());
+ log_streams.push_back(std::make_pair(&logfile, LogLevel::LOG));
}
if (vm.count("force")) {
@@ -138,6 +151,15 @@ void CommandHandler::setupContext(Context *ctx)
ctx->rngseed(vm["seed"].as<int>());
}
+ if (vm.count("randomize-seed")) {
+ srand(time(NULL));
+ int r;
+ do {
+ r = rand();
+ } while (r == 0);
+ ctx->rngseed(r);
+ }
+
if (vm.count("slack_redist_iter")) {
ctx->slack_redist_iter = vm["slack_redist_iter"].as<int>();
if (vm.count("freq") && vm["freq"].as<double>() == 0) {
@@ -248,7 +270,7 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
deinit_python();
#endif
- return 0;
+ return had_nonfatal_error ? 1 : 0;
}
void CommandHandler::conflicting_options(const boost::program_options::variables_map &vm, const char *opt1,
diff --git a/common/command.h b/common/command.h
index 12f710f6..30e0e58b 100644
--- a/common/command.h
+++ b/common/command.h
@@ -66,6 +66,7 @@ class CommandHandler
int argc;
char **argv;
ProjectHandler project;
+ std::ofstream logfile;
};
NEXTPNR_NAMESPACE_END
diff --git a/common/log.cc b/common/log.cc
index 6b2d6065..82e09fec 100644
--- a/common/log.cc
+++ b/common/log.cc
@@ -32,19 +32,14 @@ NEXTPNR_NAMESPACE_BEGIN
NPNR_NORETURN void logv_error(const char *format, va_list ap) NPNR_ATTRIBUTE(noreturn);
-std::vector<FILE *> log_files;
-std::vector<std::ostream *> log_streams;
-FILE *log_errfile = NULL;
+std::vector<std::pair<std::ostream *, LogLevel>> log_streams;
log_write_type log_write_function = nullptr;
-bool log_error_stderr = false;
-bool log_cmd_error_throw = false;
-bool log_quiet_warnings = false;
std::string log_last_error;
void (*log_error_atexit)() = NULL;
-// static bool next_print_log = false;
static int log_newline_count = 0;
+bool had_nonfatal_error = false;
std::string stringf(const char *fmt, ...)
{
@@ -88,7 +83,7 @@ std::string vstringf(const char *fmt, va_list ap)
return string;
}
-void logv(const char *format, va_list ap)
+void logv(const char *format, va_list ap, LogLevel level = LogLevel::LOG)
{
//
// Trim newlines from the beginning
@@ -108,90 +103,50 @@ void logv(const char *format, va_list ap)
else
log_newline_count = str.size() - nnl_pos - 1;
- for (auto f : log_files)
- fputs(str.c_str(), f);
-
for (auto f : log_streams)
- *f << str;
+ if (f.second <= level)
+ *f.first << str;
if (log_write_function)
log_write_function(str);
}
-void logv_info(const char *format, va_list ap)
+void log_with_level(LogLevel level, const char *format, ...)
{
- std::string message = vstringf(format, ap);
-
- log_always("Info: %s", message.c_str());
- log_flush();
+ va_list ap;
+ va_start(ap, format);
+ logv(format, ap, level);
+ va_end(ap);
}
-void logv_warning(const char *format, va_list ap)
+void logv_prefixed(const char *prefix, const char *format, va_list ap, LogLevel level)
{
std::string message = vstringf(format, ap);
- log_always("Warning: %s", message.c_str());
+ log_with_level(level, "%s%s", prefix, message.c_str());
log_flush();
}
-void logv_warning_noprefix(const char *format, va_list ap)
-{
- std::string message = vstringf(format, ap);
-
- log_always("%s", message.c_str());
-}
-
-void logv_error(const char *format, va_list ap)
-{
-#ifdef EMSCRIPTEN
- auto backup_log_files = log_files;
-#endif
-
- if (log_errfile != NULL)
- log_files.push_back(log_errfile);
-
- if (log_error_stderr)
- for (auto &f : log_files)
- if (f == stdout)
- f = stderr;
-
- log_last_error = vstringf(format, ap);
- log_always("ERROR: %s", log_last_error.c_str());
- log_flush();
-
- if (log_error_atexit)
- log_error_atexit();
-
-#ifdef EMSCRIPTEN
- log_files = backup_log_files;
-#endif
- throw log_execution_error_exception();
-}
-
void log_always(const char *format, ...)
{
va_list ap;
va_start(ap, format);
- logv(format, ap);
+ logv(format, ap, LogLevel::ALWAYS);
va_end(ap);
}
void log(const char *format, ...)
{
- if (log_quiet_warnings)
- return;
va_list ap;
va_start(ap, format);
- logv(format, ap);
+ logv(format, ap, LogLevel::LOG);
va_end(ap);
}
void log_info(const char *format, ...)
{
- if (log_quiet_warnings)
- return;
va_list ap;
va_start(ap, format);
- logv_info(format, ap);
+ logv_prefixed("Info: ", format, ap, LogLevel::INFO);
va_end(ap);
}
@@ -199,15 +154,7 @@ void log_warning(const char *format, ...)
{
va_list ap;
va_start(ap, format);
- logv_warning(format, ap);
- va_end(ap);
-}
-
-void log_warning_noprefix(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- logv_warning_noprefix(format, ap);
+ logv_prefixed("Warning: ", format, ap, LogLevel::WARNING);
va_end(ap);
}
@@ -215,41 +162,35 @@ void log_error(const char *format, ...)
{
va_list ap;
va_start(ap, format);
- logv_error(format, ap);
-}
-
-void log_cmd_error(const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
+ logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR);
- if (log_cmd_error_throw) {
- log_last_error = vstringf(format, ap);
- log_always("ERROR: %s", log_last_error.c_str());
- log_flush();
- throw log_cmd_error_exception();
- }
+ if (log_error_atexit)
+ log_error_atexit();
- logv_error(format, ap);
+ throw log_execution_error_exception();
}
void log_break()
{
- if (log_quiet_warnings)
- return;
if (log_newline_count < 2)
- log_always("\n");
+ log("\n");
if (log_newline_count < 2)
- log_always("\n");
+ log("\n");
}
-void log_flush()
+void log_nonfatal_error(const char *format, ...)
{
- for (auto f : log_files)
- fflush(f);
+ va_list ap;
+ va_start(ap, format);
+ logv_prefixed("ERROR: ", format, ap, LogLevel::ERROR);
+ va_end(ap);
+ had_nonfatal_error = true;
+}
+void log_flush()
+{
for (auto f : log_streams)
- f->flush();
+ f.first->flush();
}
NEXTPNR_NAMESPACE_END
diff --git a/common/log.h b/common/log.h
index b5fddf53..f1727ac6 100644
--- a/common/log.h
+++ b/common/log.h
@@ -42,14 +42,21 @@ struct log_execution_error_exception
{
};
-extern std::vector<FILE *> log_files;
-extern std::vector<std::ostream *> log_streams;
-extern FILE *log_errfile;
+enum class LogLevel
+{
+ LOG,
+ INFO,
+ WARNING,
+ ERROR,
+ ALWAYS
+};
+
+extern std::vector<std::pair<std::ostream *, LogLevel>> log_streams;
extern log_write_type log_write_function;
-extern bool log_quiet_warnings;
extern std::string log_last_error;
extern void (*log_error_atexit)();
+extern bool had_nonfatal_error;
std::string stringf(const char *fmt, ...);
std::string vstringf(const char *fmt, va_list ap);
@@ -59,10 +66,8 @@ void log(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
void log_always(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
void log_info(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
void log_warning(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
-void log_warning_noprefix(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
NPNR_NORETURN void log_error(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2), noreturn);
-NPNR_NORETURN void log_cmd_error(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2), noreturn);
-
+void log_nonfatal_error(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
void log_break();
void log_flush();
@@ -75,7 +80,6 @@ static inline void log_assert_worker(bool cond, const char *expr, const char *fi
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__)
NEXTPNR_NAMESPACE_END
diff --git a/common/placer1.cc b/common/placer1.cc
index 0fd9a227..0db7ce00 100644
--- a/common/placer1.cc
+++ b/common/placer1.cc
@@ -51,15 +51,20 @@ class SAPlacer
{
int num_bel_types = 0;
for (auto bel : ctx->getBels()) {
- Loc loc = ctx->getBelLocation(bel);
IdString type = ctx->getBelType(bel);
- int type_idx;
if (bel_types.find(type) == bel_types.end()) {
- type_idx = num_bel_types++;
- bel_types[type] = type_idx;
+ bel_types[type] = std::tuple<int, int>(num_bel_types++, 1);
} else {
- type_idx = bel_types.at(type);
+ std::get<1>(bel_types.at(type))++;
}
+ }
+ for (auto bel : ctx->getBels()) {
+ Loc loc = ctx->getBelLocation(bel);
+ IdString type = ctx->getBelType(bel);
+ int type_idx = std::get<0>(bel_types.at(type));
+ int type_cnt = std::get<1>(bel_types.at(type));
+ if (type_cnt < cfg.minBelsForGridPick)
+ loc.x = loc.y = 0;
if (int(fast_bels.size()) < type_idx + 1)
fast_bels.resize(type_idx + 1);
if (int(fast_bels.at(type_idx).size()) < (loc.x + 1))
@@ -463,7 +468,10 @@ class SAPlacer
while (true) {
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);
+ int beltype_idx, beltype_cnt;
+ std::tie(beltype_idx, beltype_cnt) = bel_types.at(targetType);
+ if (beltype_cnt < cfg.minBelsForGridPick)
+ nx = ny = 0;
if (nx >= int(fast_bels.at(beltype_idx).size()))
continue;
if (ny >= int(fast_bels.at(beltype_idx).at(nx).size()))
@@ -485,7 +493,7 @@ class SAPlacer
bool improved = false;
int n_move, n_accept;
int diameter = 35, max_x = 1, max_y = 1;
- std::unordered_map<IdString, int> bel_types;
+ std::unordered_map<IdString, std::tuple<int, int>> bel_types;
std::vector<std::vector<std::vector<std::vector<BelId>>>> fast_bels;
std::unordered_set<BelId> locked_bels;
bool require_legal = true;
@@ -503,7 +511,11 @@ class SAPlacer
std::vector<decltype(NetInfo::udata)> old_udata;
};
-Placer1Cfg::Placer1Cfg(Context *ctx) : Settings(ctx) { constraintWeight = get<float>("placer1/constraintWeight", 10); }
+Placer1Cfg::Placer1Cfg(Context *ctx) : Settings(ctx)
+{
+ constraintWeight = get<float>("placer1/constraintWeight", 10);
+ minBelsForGridPick = get<int>("placer1/minBelsForGridPick", 64);
+}
bool placer1(Context *ctx, Placer1Cfg cfg)
{
diff --git a/common/placer1.h b/common/placer1.h
index 55db1fa5..7305f4b1 100644
--- a/common/placer1.h
+++ b/common/placer1.h
@@ -28,6 +28,7 @@ struct Placer1Cfg : public Settings
{
Placer1Cfg(Context *ctx);
float constraintWeight;
+ int minBelsForGridPick;
};
extern bool placer1(Context *ctx, Placer1Cfg cfg);
diff --git a/common/pybindings.cc b/common/pybindings.cc
index 061dfc47..6cae889d 100644
--- a/common/pybindings.cc
+++ b/common/pybindings.cc
@@ -68,6 +68,19 @@ void translate_assertfail(const assertion_failure &e)
PyErr_SetString(PyExc_AssertionError, e.what());
}
+namespace PythonConversion {
+template <> struct string_converter<PortRef &>
+{
+ inline PortRef from_str(Context *ctx, std::string name) { NPNR_ASSERT_FALSE("PortRef from_str not implemented"); }
+
+ inline std::string to_str(Context *ctx, const PortRef &pr)
+ {
+ return pr.cell->name.str(ctx) + "." + pr.port.str(ctx);
+ }
+};
+
+} // namespace PythonConversion
+
BOOST_PYTHON_MODULE(MODULE_NAME)
{
register_exception_translator<assertion_failure>(&translate_assertfail);
@@ -120,7 +133,7 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
readwrite_wrapper<PortInfo &, decltype(&PortInfo::type), &PortInfo::type, pass_through<PortType>,
pass_through<PortType>>::def_wrap(pi_cls, "type");
- typedef std::vector<PortRef> PortVector;
+ typedef std::vector<PortRef> PortRefVector;
typedef std::unordered_map<WireId, PipMap> WireMap;
auto ni_cls = class_<ContextualWrapper<NetInfo &>>("NetInfo", no_init);
@@ -128,7 +141,7 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
conv_from_str<IdString>>::def_wrap(ni_cls, "name");
readwrite_wrapper<NetInfo &, decltype(&NetInfo::driver), &NetInfo::driver, wrap_context<PortRef &>,
unwrap_context<PortRef &>>::def_wrap(ni_cls, "driver");
- readonly_wrapper<NetInfo &, decltype(&NetInfo::users), &NetInfo::users, wrap_context<PortVector &>>::def_wrap(
+ readonly_wrapper<NetInfo &, decltype(&NetInfo::users), &NetInfo::users, wrap_context<PortRefVector &>>::def_wrap(
ni_cls, "users");
readonly_wrapper<NetInfo &, decltype(&NetInfo::wires), &NetInfo::wires, wrap_context<WireMap &>>::def_wrap(ni_cls,
"wires");
@@ -141,12 +154,21 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
readwrite_wrapper<PortRef &, decltype(&PortRef::budget), &PortRef::budget, pass_through<delay_t>,
pass_through<delay_t>>::def_wrap(pr_cls, "budget");
+ auto pm_cls = class_<ContextualWrapper<PipMap &>>("PipMap", no_init);
+ readwrite_wrapper<PipMap &, decltype(&PipMap::pip), &PipMap::pip, conv_to_str<PipId>,
+ conv_from_str<PipId>>::def_wrap(pm_cls, "pip");
+ readwrite_wrapper<PipMap &, decltype(&PipMap::strength), &PipMap::strength, pass_through<PlaceStrength>,
+ pass_through<PlaceStrength>>::def_wrap(pm_cls, "strength");
+
def("parse_json", parse_json_shim);
def("load_design", load_design_shim, return_value_policy<manage_new_object>());
WRAP_MAP(AttrMap, pass_through<std::string>, "AttrMap");
WRAP_MAP(PortMap, wrap_context<PortInfo &>, "PortMap");
WRAP_MAP(PinMap, conv_to_str<IdString>, "PinMap");
+ WRAP_MAP(WireMap, wrap_context<PipMap &>, "WireMap");
+
+ WRAP_VECTOR(PortRefVector, wrap_context<PortRef &>);
arch_wrap_python();
}
diff --git a/common/pycontainers.h b/common/pycontainers.h
index 094706f7..70f69c51 100644
--- a/common/pycontainers.h
+++ b/common/pycontainers.h
@@ -119,9 +119,66 @@ struct range_wrapper
range_wrapper<t##Range, return_value_policy<return_by_value>, conv>().wrap(#t "Range", #t "Iterator")
/*
+A wrapper for a vector or similar structure. With support for conversion
+*/
+
+template <typename T, typename P = return_value_policy<return_by_value>,
+ typename value_conv = PythonConversion::pass_through<T>>
+struct vector_wrapper
+{
+ typedef decltype(std::declval<T>().begin()) iterator_t;
+ typedef decltype(*(std::declval<iterator_t>())) value_t;
+ typedef typename PythonConversion::ContextualWrapper<T &> wrapped_vector;
+ typedef typename PythonConversion::ContextualWrapper<std::pair<iterator_t, iterator_t>> wrapped_pair;
+ using return_t = typename value_conv::ret_type;
+ static wrapped_pair iter(wrapped_vector &range)
+ {
+ return wrapped_pair(range.ctx, std::make_pair(range.base.begin(), range.base.end()));
+ }
+
+ static std::string repr(wrapped_vector &range)
+ {
+ PythonConversion::string_converter<value_t> conv;
+ bool first = true;
+ std::stringstream ss;
+ ss << "[";
+ for (const auto &item : range.base) {
+ if (!first)
+ ss << ", ";
+ ss << "'" << conv.to_str(range.ctx, item) << "'";
+ first = false;
+ }
+ ss << "]";
+ return ss.str();
+ }
+
+ static int len(wrapped_vector &range) { return range.base.size(); }
+
+ static return_t getitem(wrapped_vector &range, int i)
+ {
+ return value_conv()(range.ctx, boost::ref(range.base.at(i)));
+ }
+
+ static void wrap(const char *range_name, const char *iter_name)
+ {
+ class_<wrapped_vector>(range_name, no_init)
+ .def("__iter__", iter)
+ .def("__repr__", repr)
+ .def("__len__", len)
+ .def("__getitem__", getitem);
+
+ iterator_wrapper<iterator_t, P, value_conv>().wrap(iter_name);
+ }
+
+ typedef iterator_wrapper<iterator_t, P, value_conv> iter_wrap;
+};
+
+#define WRAP_VECTOR(t, conv) vector_wrapper<t, return_value_policy<return_by_value>, conv>().wrap(#t, #t "Iterator")
+
+/*
Wrapper for a pair, allows accessing either using C++-style members (.first and
.second) or as a Python iterable and indexable object
- */
+*/
template <typename T1, typename T2> struct pair_wrapper
{
typedef std::pair<T1, T2> T;
diff --git a/common/router1.cc b/common/router1.cc
index 198461bc..cbc0df90 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -777,6 +777,7 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
router.arcs_without_ripup - last_arcs_without_ripup, int(router.arc_queue.size()));
last_arcs_with_ripup = router.arcs_with_ripup;
last_arcs_without_ripup = router.arcs_without_ripup;
+ ctx->yield();
#ifndef NDEBUG
router.check();
#endif
@@ -802,6 +803,7 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
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");
+ ctx->yield();
#ifndef NDEBUG
router.check();
@@ -810,7 +812,8 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
#endif
log_info("Checksum: 0x%08x\n", ctx->checksum());
- timing_analysis(ctx, true /* slack_histogram */, true /* print_fmax */, true /* print_path */);
+ timing_analysis(ctx, true /* slack_histogram */, true /* print_fmax */, true /* print_path */,
+ true /* warn_on_failure */);
ctx->unlock();
return true;
@@ -868,7 +871,12 @@ bool Context::checkRoutedDesign() const
}
auto src_wire = ctx->getNetinfoSourceWire(net_info);
- log_assert(src_wire != WireId());
+ if (src_wire == WireId()) {
+ log_assert(net_info->driver.cell == nullptr);
+ if (ctx->debug)
+ log(" undriven and unrouted\n");
+ continue;
+ }
if (net_info->wires.count(src_wire) == 0) {
if (ctx->debug)
diff --git a/common/timing.cc b/common/timing.cc
index 002ccda9..242d56d9 100644
--- a/common/timing.cc
+++ b/common/timing.cc
@@ -485,11 +485,11 @@ void assign_budget(Context *ctx, bool quiet)
for (auto &user : net.second->users) {
// Post-update check
if (!ctx->auto_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),
- ctx->getDelayNS(user.budget));
- else if (ctx->verbose)
+ log_info("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),
+ ctx->getDelayNS(user.budget));
+ else if (ctx->debug)
log_info("port %s.%s, connected to net '%s', has "
"timing budget of %fns\n",
user.cell->name.c_str(ctx), user.port.c_str(ctx), net.first.c_str(ctx),
@@ -513,7 +513,7 @@ void assign_budget(Context *ctx, bool quiet)
log_info("Checksum: 0x%08x\n", ctx->checksum());
}
-void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool print_path)
+void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool print_path, bool warn_on_failure)
{
auto format_event = [ctx](const ClockEvent &e, int field_width = 0) {
std::string value;
@@ -705,15 +705,17 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
for (auto &clock : clock_reports) {
const auto &clock_name = clock.first.str(ctx);
const int width = max_width - clock_name.size();
- if (ctx->nets.at(clock.first)->clkconstr) {
- float target = 1000 / ctx->getDelayNS(ctx->nets.at(clock.first)->clkconstr->period.minDelay());
+ float target = ctx->target_freq / 1e6;
+ if (ctx->nets.at(clock.first)->clkconstr)
+ target = 1000 / ctx->getDelayNS(ctx->nets.at(clock.first)->clkconstr->period.minDelay());
+
+ bool passed = target < clock_fmax[clock.first];
+ if (!warn_on_failure || passed)
log_info("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "",
- clock_name.c_str(), clock_fmax[clock.first],
- (target < clock_fmax[clock.first]) ? "PASS" : "FAIL", target);
- } else {
- log_info("Max frequency for clock %*s'%s': %.02f MHz\n", width, "", clock_name.c_str(),
- clock_fmax[clock.first]);
- }
+ clock_name.c_str(), clock_fmax[clock.first], passed ? "PASS" : "FAIL", target);
+ else
+ log_nonfatal_error("Max frequency for clock %*s'%s': %.02f MHz (%s at %.02f MHz)\n", width, "",
+ clock_name.c_str(), clock_fmax[clock.first], passed ? "PASS" : "FAIL", target);
}
for (auto &eclock : empty_clocks) {
if (eclock != ctx->id("$async$"))
diff --git a/common/timing.h b/common/timing.h
index 1fd76310..42f928dc 100644
--- a/common/timing.h
+++ b/common/timing.h
@@ -29,7 +29,8 @@ void assign_budget(Context *ctx, bool quiet = false);
// Perform timing analysis and print out the fmax, and optionally the
// critical path
-void timing_analysis(Context *ctx, bool slack_histogram = true, bool print_fmax = true, bool print_path = false);
+void timing_analysis(Context *ctx, bool slack_histogram = true, bool print_fmax = true, bool print_path = false,
+ bool warn_on_failure = false);
NEXTPNR_NAMESPACE_END