diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/command.cc | 36 | ||||
-rw-r--r-- | common/command.h | 1 | ||||
-rw-r--r-- | common/log.cc | 123 | ||||
-rw-r--r-- | common/log.h | 20 | ||||
-rw-r--r-- | common/placer1.cc | 28 | ||||
-rw-r--r-- | common/placer1.h | 1 | ||||
-rw-r--r-- | common/pybindings.cc | 26 | ||||
-rw-r--r-- | common/pycontainers.h | 59 | ||||
-rw-r--r-- | common/router1.cc | 12 | ||||
-rw-r--r-- | common/timing.cc | 30 | ||||
-rw-r--r-- | common/timing.h | 3 |
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 |