Add flag to restart place-and-route on failed target frequency

* Add --restart-on-failed-target-frequency flag and logic to restart the
  place-and-route process if the target frequency is not achieved.

* This flag is intended to be used with the --randomize-seed option to
  generate a new random seed for every run. This can significantly
  improve the chances of achieving a higher clock frequency compared to
  using the default seed.

* Move print_net_source out of log_crit_paths() to remove the 'static'
  keyword, which could otherwise cause a segmentation fault if the
  context is changed.
This commit is contained in:
Jonas Thörnblad 2024-09-26 15:08:43 +02:00
parent 437fb70ed3
commit a0356aa0fc
No known key found for this signature in database
GPG Key ID: 2D318AD00A326F95
4 changed files with 71 additions and 29 deletions

View File

@ -345,7 +345,9 @@ po::options_description CommandHandler::getGeneralOptions()
general.add_options()("top", po::value<std::string>(), "name of top module"); general.add_options()("top", po::value<std::string>(), "name of top module");
general.add_options()("seed", po::value<uint64_t>(), "seed value for random number generator"); general.add_options()("seed", po::value<uint64_t>(), "seed value for random number generator");
general.add_options()("randomize-seed,r", "randomize seed value for random number generator"); general.add_options()("randomize-seed,r", "randomize seed value for random number generator");
general.add_options()("restart-on-failed-target-frequency",
"restart place and route if target frequency is not achieved (use together with "
"--randomize-seed option)");
general.add_options()( general.add_options()(
"placer", po::value<std::string>(), "placer", po::value<std::string>(),
std::string("placer algorithm to use; available: " + boost::algorithm::join(Arch::availablePlacers, ", ") + std::string("placer algorithm to use; available: " + boost::algorithm::join(Arch::availablePlacers, ", ") +
@ -673,6 +675,17 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
ctx->debug = true; ctx->debug = true;
if (!ctx->place() && !ctx->force) if (!ctx->place() && !ctx->force)
log_error("Placing design failed.\n"); log_error("Placing design failed.\n");
if (vm.count("restart-on-failed-target-frequency")) {
if (!ctx->target_frequency_achieved) {
log_break();
log_info("Target frequency not achieved, restarting...\n");
log_break();
#ifndef NO_PYTHON
deinit_python();
#endif
return 2;
}
}
ctx->debug = saved_debug; ctx->debug = saved_debug;
ctx->check(); ctx->check();
if (vm.count("placed-svg")) if (vm.count("placed-svg"))
@ -686,6 +699,17 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
ctx->debug = true; ctx->debug = true;
if (!ctx->route() && !ctx->force) if (!ctx->route() && !ctx->force)
log_error("Routing design failed.\n"); log_error("Routing design failed.\n");
if (vm.count("restart-on-failed-target-frequency")) {
if (!ctx->target_frequency_achieved) {
log_break();
log_info("Target frequency not achieved, restarting...\n");
log_break();
#ifndef NO_PYTHON
deinit_python();
#endif
return 2;
}
}
ctx->debug = saved_debug; ctx->debug = saved_debug;
run_script_hook("post-route"); run_script_hook("post-route");
if (vm.count("routed-svg")) if (vm.count("routed-svg"))
@ -753,10 +777,15 @@ int CommandHandler::exec()
return 0; return 0;
dict<std::string, Property> values; dict<std::string, Property> values;
restart:
std::unique_ptr<Context> ctx = createContext(values); std::unique_ptr<Context> ctx = createContext(values);
setupContext(ctx.get()); setupContext(ctx.get());
setupArchContext(ctx.get()); setupArchContext(ctx.get());
int rc = executeMain(std::move(ctx)); int rc = executeMain(std::move(ctx));
if (rc == 2) {
ctx.reset();
goto restart;
}
printFooter(); printFooter();
log_break(); log_break();
log_info("Program finished normally.\n"); log_info("Program finished normally.\n");

View File

@ -38,6 +38,8 @@ struct Context : Arch, DeterministicRNG
bool disable_critical_path_source_print = false; bool disable_critical_path_source_print = false;
// True when detailed per-net timing is to be stored / reported // True when detailed per-net timing is to be stored / reported
bool detailed_timing_report = false; bool detailed_timing_report = false;
// Default to true, will update when timing analysis is run
bool target_frequency_achieved = true;
ArchArgs arch_args; ArchArgs arch_args;

View File

@ -1339,6 +1339,16 @@ void timing_analysis(Context *ctx, bool print_slack_histogram, bool print_fmax,
if (update_results) if (update_results)
ctx->timing_result = result; ctx->timing_result = result;
ctx->target_frequency_achieved = true;
for (auto &clock : result.clock_paths) {
float fmax = result.clock_fmax[clock.first].achieved;
float target = result.clock_fmax[clock.first].constraint;
bool passed = target < fmax;
if (!passed) {
ctx->target_frequency_achieved = false;
}
}
} }
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -38,35 +38,36 @@ static std::string clock_event_name(const Context *ctx, const ClockEvent &e, int
return value; return value;
}; };
static void print_net_source(const Context *ctx, const NetInfo *net)
{
// Check if this net is annotated with a source list
auto sources = net->attrs.find(ctx->id("src"));
if (sources == net->attrs.end()) {
// No sources for this net, can't print anything
return;
}
// Sources are separated by pipe characters.
// There is no guaranteed ordering on sources, so we just print all
auto sourcelist = sources->second.as_string();
std::vector<std::string> source_entries;
size_t current = 0, prev = 0;
while ((current = sourcelist.find("|", prev)) != std::string::npos) {
source_entries.emplace_back(sourcelist.substr(prev, current - prev));
prev = current + 1;
}
// Ensure we emplace the final entry
source_entries.emplace_back(sourcelist.substr(prev, current - prev));
// Iterate and print our source list at the correct indentation level
log_info(" Defined in:\n");
for (auto entry : source_entries) {
log_info(" %s\n", entry.c_str());
}
}
static void log_crit_paths(const Context *ctx, TimingResult &result) static void log_crit_paths(const Context *ctx, TimingResult &result)
{ {
static auto print_net_source = [ctx](const NetInfo *net) {
// Check if this net is annotated with a source list
auto sources = net->attrs.find(ctx->id("src"));
if (sources == net->attrs.end()) {
// No sources for this net, can't print anything
return;
}
// Sources are separated by pipe characters.
// There is no guaranteed ordering on sources, so we just print all
auto sourcelist = sources->second.as_string();
std::vector<std::string> source_entries;
size_t current = 0, prev = 0;
while ((current = sourcelist.find("|", prev)) != std::string::npos) {
source_entries.emplace_back(sourcelist.substr(prev, current - prev));
prev = current + 1;
}
// Ensure we emplace the final entry
source_entries.emplace_back(sourcelist.substr(prev, current - prev));
// Iterate and print our source list at the correct indentation level
log_info(" Defined in:\n");
for (auto entry : source_entries) {
log_info(" %s\n", entry.c_str());
}
};
// A helper function for reporting one critical path // A helper function for reporting one critical path
auto print_path_report = [ctx](const CriticalPath &path) { auto print_path_report = [ctx](const CriticalPath &path) {
delay_t total(0), logic_total(0), route_total(0); delay_t total(0), logic_total(0), route_total(0);
@ -137,7 +138,7 @@ static void log_crit_paths(const Context *ctx, TimingResult &result)
} }
if (!ctx->disable_critical_path_source_print) { if (!ctx->disable_critical_path_source_print) {
print_net_source(net); print_net_source(ctx, net);
} }
} }
} }