Remove old timing analyser

This commit is contained in:
rowanG077 2023-06-22 15:18:57 +02:00 committed by myrtle
parent b0820eeaaa
commit d2a489d5e9
7 changed files with 103 additions and 1137 deletions

View File

@ -99,11 +99,12 @@ struct Context : Arch, DeterministicRNG
// -------------------------------------------------------------- // --------------------------------------------------------------
// provided by report_json.cc // provided by report.cc
void writeJsonReport(std::ostream &out) const; void writeJsonReport(std::ostream &out) const;
// provided by timing_report.cc // provided by timing_log.cc
void log_timing_results(bool print_histogram, bool print_path, bool warn_on_failure); void log_timing_results(TimingResult &result, bool print_histogram, bool print_fmax, bool print_path,
bool warn_on_failure);
// -------------------------------------------------------------- // --------------------------------------------------------------
uint32_t checksum() const; uint32_t checksum() const;

View File

@ -365,7 +365,7 @@ struct TimingResult
// clock to clock delays // clock to clock delays
dict<std::pair<IdString, IdString>, delay_t> clock_delays; dict<std::pair<IdString, IdString>, delay_t> clock_delays;
// Histogram of delays // Histogram of slack
dict<int, unsigned> slack_histogram; dict<int, unsigned> slack_histogram;
}; };

View File

@ -238,10 +238,7 @@ void Context::writeJsonReport(std::ostream &out) const
} }
Json::object jsonRoot{ Json::object jsonRoot{
{ "utilization", util_json }, {"utilization", util_json}, {"fmax", fmax_json}, {"critical_paths", json_report_critical_paths(this)}};
{ "fmax", fmax_json },
{ "critical_paths", json_report_critical_paths(this) }
};
if (detailed_timing_report) { if (detailed_timing_report) {
jsonRoot["detailed_net_timings"] = json_report_detailed_net_timings(this); jsonRoot["detailed_net_timings"] = json_report_detailed_net_timings(this);

View File

@ -3,6 +3,7 @@
* *
* Copyright (C) 2018 gatecat <gatecat@ds0.me> * Copyright (C) 2018 gatecat <gatecat@ds0.me>
* Copyright (C) 2018 Eddie Hung <eddieh@ece.ubc.ca> * Copyright (C) 2018 Eddie Hung <eddieh@ece.ubc.ca>
* Copyright (C) 2023 rowanG077 <goemansrowan@gmail.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -29,38 +30,22 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
namespace {
std::string clock_event_name(const Context *ctx, ClockDomainKey &dom)
{
std::string value;
if (dom.is_async())
value = "<async>";
else
value = (dom.edge == FALLING_EDGE ? "negedge " : "posedge ") + dom.clock.str(ctx);
return value;
}
} // namespace
TimingAnalyser::TimingAnalyser(Context *ctx) : ctx(ctx) TimingAnalyser::TimingAnalyser(Context *ctx) : ctx(ctx)
{ {
ClockDomainKey key{IdString(), ClockEdge::RISING_EDGE}; ClockDomainKey key{IdString(), ClockEdge::RISING_EDGE};
domain_to_id.emplace(key, 0); domain_to_id.emplace(key, 0);
domains.emplace_back(key); domains.emplace_back(key);
async_clock_id = 0; async_clock_id = 0;
domain_pair_id(async_clock_id, async_clock_id);
}; };
void TimingAnalyser::setup(bool update_net_timings, bool update_histogram, bool update_crit_paths, void TimingAnalyser::setup(bool update_net_timings, bool update_histogram, bool update_crit_paths)
bool update_route_delays)
{ {
init_ports(); init_ports();
get_cell_delays(); get_cell_delays();
topo_sort(); topo_sort();
setup_port_domains(); setup_port_domains();
identify_related_domains(); identify_related_domains();
run(update_net_timings, update_histogram, update_crit_paths, update_route_delays); run(update_net_timings, update_histogram, update_crit_paths, true);
} }
void TimingAnalyser::run(bool update_net_timings, bool update_histogram, bool update_crit_paths, void TimingAnalyser::run(bool update_net_timings, bool update_histogram, bool update_crit_paths,
@ -78,7 +63,7 @@ void TimingAnalyser::run(bool update_net_timings, bool update_histogram, bool up
// as to be updated. This is done so we ensure it's not possible to have // as to be updated. This is done so we ensure it's not possible to have
// timing_result which contains mixed reports // timing_result which contains mixed reports
if (update_net_timings || update_histogram || update_crit_paths) { if (update_net_timings || update_histogram || update_crit_paths) {
ctx->timing_result = TimingResult(); result = TimingResult();
} }
if (update_net_timings) { if (update_net_timings) {
@ -666,6 +651,7 @@ void TimingAnalyser::walk_backward()
dict<domain_id_t, delay_t> TimingAnalyser::max_delay_by_domain_pairs() dict<domain_id_t, delay_t> TimingAnalyser::max_delay_by_domain_pairs()
{ {
dict<domain_id_t, delay_t> domain_delay; dict<domain_id_t, delay_t> domain_delay;
for (auto p : topological_order) { for (auto p : topological_order) {
auto &pd = ports.at(p); auto &pd = ports.at(p);
for (auto &req : pd.required) { for (auto &req : pd.required) {
@ -675,6 +661,9 @@ dict<domain_id_t, delay_t> TimingAnalyser::max_delay_by_domain_pairs()
auto dp = domain_pair_id(launch, capture); auto dp = domain_pair_id(launch, capture);
auto l = domains.at(launch);
auto c = domains.at(capture);
delay_t delay = arr.second.value.maxDelay() - req.second.value.minDelay(); delay_t delay = arr.second.value.maxDelay() - req.second.value.minDelay();
if (!domain_delay.count(dp) || domain_delay.at(dp) < delay) if (!domain_delay.count(dp) || domain_delay.at(dp) < delay)
domain_delay[dp] = delay; domain_delay[dp] = delay;
@ -728,7 +717,6 @@ void TimingAnalyser::compute_criticality()
{ {
for (auto p : topological_order) { for (auto p : topological_order) {
auto &pd = ports.at(p); auto &pd = ports.at(p);
for (auto &pdp : pd.domain_pairs) { for (auto &pdp : pd.domain_pairs) {
auto &dp = domain_pairs.at(pdp.first); auto &dp = domain_pairs.at(pdp.first);
// Do not set criticality for asynchronous paths // Do not set criticality for asynchronous paths
@ -747,7 +735,7 @@ void TimingAnalyser::compute_criticality()
void TimingAnalyser::build_detailed_net_timing_report() void TimingAnalyser::build_detailed_net_timing_report()
{ {
auto &net_timings = ctx->timing_result.detailed_net_timings; auto &net_timings = result.detailed_net_timings;
for (domain_id_t dom_id = 0; dom_id < domain_id_t(domains.size()); ++dom_id) { for (domain_id_t dom_id = 0; dom_id < domain_id_t(domains.size()); ++dom_id) {
auto &dom = domains.at(dom_id); auto &dom = domains.at(dom_id);
@ -800,32 +788,6 @@ std::vector<CellPortKey> TimingAnalyser::get_worst_eps(domain_id_t domain_pair,
return worst_eps; return worst_eps;
} }
static std::string tgp_to_string(TimingPortClass c)
{
switch (c) {
case TMG_CLOCK_INPUT:
return "TMG_CLOCK_INPUT";
case TMG_GEN_CLOCK:
return "TMG_GEN_CLOCK";
case TMG_REGISTER_INPUT:
return "TMG_REGISTER_INPUT";
case TMG_REGISTER_OUTPUT:
return "TMG_REGISTER_OUTPUT";
case TMG_COMB_INPUT:
return "TMG_COMB_INPUT";
case TMG_COMB_OUTPUT:
return "TMG_COMB_OUTPUT";
case TMG_STARTPOINT:
return "TMG_STARTPOINT";
case TMG_ENDPOINT:
return "TMG_ENDPOINT";
case TMG_IGNORE:
return "TMG_IGNORE";
}
return "UNKNOWN";
}
CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair, CellPortKey endpoint) CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair, CellPortKey endpoint)
{ {
CriticalPath report; CriticalPath report;
@ -855,7 +817,6 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
std::vector<PortRef> crit_path_rev; std::vector<PortRef> crit_path_rev;
auto cursor = endpoint; auto cursor = endpoint;
log_info("Analyzing %s -> %s\n", clock_event_name(ctx, launch).c_str(), clock_event_name(ctx, capture).c_str());
while (cursor != CellPortKey()) { while (cursor != CellPortKey()) {
auto cell = cell_info(cursor); auto cell = cell_info(cursor);
auto &port = port_info(cursor); auto &port = port_info(cursor);
@ -863,9 +824,6 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
int port_clocks; int port_clocks;
auto portClass = ctx->getPortTimingClass(cell, port.name, port_clocks); auto portClass = ctx->getPortTimingClass(cell, port.name, port_clocks);
log_info("\tcursor at %s.%s tmg: %s, port dir: %s\n", cell->name.c_str(ctx), port.name.c_str(ctx),
tgp_to_string(portClass).c_str(), port.type == PortType::PORT_IN ? "PORT_IN" : "PORT_X");
if (portClass != TMG_CLOCK_INPUT && portClass != TMG_IGNORE && port.type == PortType::PORT_IN) { if (portClass != TMG_CLOCK_INPUT && portClass != TMG_IGNORE && port.type == PortType::PORT_IN) {
crit_path_rev.emplace_back(PortRef{cell, port.name}); crit_path_rev.emplace_back(PortRef{cell, port.name});
} }
@ -964,11 +922,11 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
void TimingAnalyser::build_crit_path_reports() void TimingAnalyser::build_crit_path_reports()
{ {
auto &clock_reports = ctx->timing_result.clock_paths; auto &clock_reports = result.clock_paths;
auto &xclock_reports = ctx->timing_result.xclock_paths; auto &xclock_reports = result.xclock_paths;
auto &clock_fmax = ctx->timing_result.clock_fmax; auto &clock_fmax = result.clock_fmax;
auto &empty_clocks = ctx->timing_result.empty_paths; auto &empty_clocks = result.empty_paths;
auto &clock_delays_ctx = ctx->timing_result.clock_delays; auto &clock_delays_ctx = result.clock_delays;
auto delay_by_domain = max_delay_by_domain_pairs(); auto delay_by_domain = max_delay_by_domain_pairs();
@ -984,10 +942,9 @@ void TimingAnalyser::build_crit_path_reports()
if (launch.clock != capture.clock || launch.is_async()) if (launch.clock != capture.clock || launch.is_async())
continue; continue;
auto path_delay = delay_by_domain.at(dp.key.launch); auto path_delay = delay_by_domain.at(i);
double Fmax; double Fmax;
empty_clocks.erase(launch.clock);
if (launch.edge == capture.edge) if (launch.edge == capture.edge)
Fmax = 1000 / ctx->getDelayNS(path_delay); Fmax = 1000 / ctx->getDelayNS(path_delay);
@ -999,10 +956,16 @@ void TimingAnalyser::build_crit_path_reports()
if (ctx->nets.at(launch.clock)->clkconstr) if (ctx->nets.at(launch.clock)->clkconstr)
target = 1000 / ctx->getDelayNS(ctx->nets.at(launch.clock)->clkconstr->period.minDelay()); target = 1000 / ctx->getDelayNS(ctx->nets.at(launch.clock)->clkconstr->period.minDelay());
auto worst_endpoint = get_worst_eps(i, 1);
if (worst_endpoint.empty())
continue;
clock_fmax[launch.clock].achieved = Fmax; clock_fmax[launch.clock].achieved = Fmax;
clock_fmax[launch.clock].constraint = target; clock_fmax[launch.clock].constraint = target;
auto worst_endpoint = get_worst_eps(i, 1).at(0);
clock_reports[launch.clock] = build_critical_path_report(i, worst_endpoint); clock_reports[launch.clock] = build_critical_path_report(i, worst_endpoint.at(0));
empty_clocks.erase(launch.clock);
} }
} }
@ -1011,20 +974,17 @@ void TimingAnalyser::build_crit_path_reports()
auto &launch = domains.at(dp.key.launch).key; auto &launch = domains.at(dp.key.launch).key;
auto &capture = domains.at(dp.key.capture).key; auto &capture = domains.at(dp.key.capture).key;
log_info("testin testing %s -> %s\n", clock_event_name(ctx, launch).c_str(),
clock_event_name(ctx, capture).c_str());
if (launch.clock == capture.clock && !launch.is_async()) if (launch.clock == capture.clock && !launch.is_async())
continue; continue;
log_info("testin testing2 %s -> %s\n", clock_event_name(ctx, launch).c_str(), auto worst_endpoint = get_worst_eps(i, 1);
clock_event_name(ctx, capture).c_str()); if (worst_endpoint.empty())
continue;
auto worst_endpoint = get_worst_eps(i, 1).at(0); xclock_reports.emplace_back(build_critical_path_report(i, worst_endpoint.at(0)));
xclock_reports.emplace_back(build_critical_path_report(i, worst_endpoint));
} }
std::sort(xclock_reports.begin(), xclock_reports.end(), [&](const CriticalPath &ra, const CriticalPath &rb) { auto cmp_crit_path = [&](const CriticalPath &ra, const CriticalPath &rb) {
const auto &a = ra.clock_pair; const auto &a = ra.clock_pair;
const auto &b = rb.clock_pair; const auto &b = rb.clock_pair;
@ -1043,21 +1003,16 @@ void TimingAnalyser::build_crit_path_reports()
if (a.end.edge < b.end.edge) if (a.end.edge < b.end.edge)
return true; return true;
return false; return false;
}); };
for (auto &clock : clock_reports) { std::sort(xclock_reports.begin(), xclock_reports.end(), cmp_crit_path);
float target = ctx->setting<float>("target_freq") / 1e6;
if (ctx->nets.at(clock.first)->clkconstr)
target = 1000 / ctx->getDelayNS(ctx->nets.at(clock.first)->clkconstr->period.minDelay());
clock_fmax[clock.first].constraint = target;
}
clock_delays_ctx = clock_delays; clock_delays_ctx = clock_delays;
} }
void TimingAnalyser::build_slack_histogram_report() void TimingAnalyser::build_slack_histogram_report()
{ {
auto &slack_histogram = ctx->timing_result.slack_histogram; auto &slack_histogram = result.slack_histogram;
for (domain_id_t dom_id = 0; dom_id < domain_id_t(domains.size()); ++dom_id) { for (domain_id_t dom_id = 0; dom_id < domain_id_t(domains.size()); ++dom_id) {
for (auto &ep : domains.at(dom_id).endpoints) { for (auto &ep : domains.at(dom_id).endpoints) {
@ -1069,7 +1024,7 @@ void TimingAnalyser::build_slack_histogram_report()
auto &launch = domains.at(arr.first).key; auto &launch = domains.at(arr.first).key;
// @gatecat: old timing analysis includes async path // @gatecat: old timing analysis includes async path
// The slack doesn' make much sense so I filter it out. // The slack doesn't make much sense so I filter it out.
if (launch.clock != capture.clock || launch.is_async()) if (launch.clock != capture.clock || launch.is_async())
continue; continue;
@ -1129,4 +1084,17 @@ CellInfo *TimingAnalyser::cell_info(const CellPortKey &key) { return ctx->cells.
PortInfo &TimingAnalyser::port_info(const CellPortKey &key) { return ctx->cells.at(key.cell)->ports.at(key.port); } PortInfo &TimingAnalyser::port_info(const CellPortKey &key) { return ctx->cells.at(key.cell)->ports.at(key.port); }
void timing_analysis(Context *ctx, bool print_slack_histogram, bool print_fmax, bool print_path, bool warn_on_failure,
bool update_results)
{
TimingAnalyser tmg(ctx);
tmg.setup(ctx->detailed_timing_report, print_slack_histogram, print_path || print_fmax);
auto &result = tmg.get_timing_result();
ctx->log_timing_results(result, print_slack_histogram, print_fmax, print_path, warn_on_failure);
if (update_results)
ctx->timing_result = result;
}
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -2,6 +2,7 @@
* nextpnr -- Next Generation Place and Route * nextpnr -- Next Generation Place and Route
* *
* Copyright (C) 2018 gatecat <gatecat@ds0.me> * Copyright (C) 2018 gatecat <gatecat@ds0.me>
* Copyright (C) 2023 rowanG077 <goemansrowan@gmail.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -74,8 +75,10 @@ struct TimingAnalyser
{ {
public: public:
TimingAnalyser(Context *ctx); TimingAnalyser(Context *ctx);
void setup(bool update_net_timings = false, bool update_histogram = false, bool update_crit_paths = false, bool update_route_delays = true);
void run(bool update_net_timings = false, bool update_histogram = false, bool update_crit_paths = false, bool update_route_delays = true); void setup(bool update_net_timings = false, bool update_histogram = false, bool update_crit_paths = false);
void run(bool update_net_timings = false, bool update_histogram = false, bool update_crit_paths = false,
bool update_route_delays = true);
// This is used when routers etc are not actually binding detailed routing (due to congestion or an abstracted // This is used when routers etc are not actually binding detailed routing (due to congestion or an abstracted
// model), but want to re-run STA with their own calculated delays // model), but want to re-run STA with their own calculated delays
@ -91,7 +94,9 @@ struct TimingAnalyser
return slack; return slack;
} }
auto get_clock_delays() const { return clock_delays; } dict<std::pair<IdString, IdString>, delay_t> get_clock_delays() const { return clock_delays; }
TimingResult &get_timing_result() { return result; }
bool setup_only = false; bool setup_only = false;
bool verbose_mode = false; bool verbose_mode = false;
@ -115,18 +120,14 @@ struct TimingAnalyser
void compute_criticality(); void compute_criticality();
void build_detailed_net_timing_report(); void build_detailed_net_timing_report();
// Build up CriticalPathData
CriticalPath build_critical_path_report(domain_id_t domain_pair, CellPortKey endpoint); CriticalPath build_critical_path_report(domain_id_t domain_pair, CellPortKey endpoint);
void build_crit_path_reports(); void build_crit_path_reports();
void build_slack_histogram_report(); void build_slack_histogram_report();
dict<domain_id_t, delay_t> max_delay_by_domain_pairs(); dict<domain_id_t, delay_t> max_delay_by_domain_pairs();
// get the N most failing endpoints for a given domain pair // get the N worst endpoints for a given domain pair
std::vector<CellPortKey> get_worst_eps(domain_id_t domain_pair, int count); std::vector<CellPortKey> get_worst_eps(domain_id_t domain_pair, int count);
// print the critical path for an endpoint and domain pair
void print_critical_path(CellPortKey endpoint, domain_id_t domain_pair);
const DelayPair init_delay{std::numeric_limits<delay_t>::max(), std::numeric_limits<delay_t>::lowest()}; const DelayPair init_delay{std::numeric_limits<delay_t>::max(), std::numeric_limits<delay_t>::lowest()};
@ -235,10 +236,11 @@ struct TimingAnalyser
domain_id_t async_clock_id; domain_id_t async_clock_id;
Context *ctx; Context *ctx;
TimingResult result;
}; };
// Perform timing analysis and print out the fmax, and optionally the // Perform timing analysis and optionaly print out slack histogram, fmax and critical paths
// 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, bool update_results = false); bool warn_on_failure = false, bool update_results = false);

View File

@ -1,6 +1,8 @@
/* /*
* nextpnr -- Next Generation Place and Route * nextpnr -- Next Generation Place and Route
* *
* Copyright (C) 2018 gatecat <gatecat@ds0.me>
* Copyright (C) 2018 Eddie Hung <eddieh@ece.ubc.ca>
* Copyright (C) 2023 rowanG077 <goemansrowan@gmail.com> * Copyright (C) 2023 rowanG077 <goemansrowan@gmail.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
@ -35,7 +37,7 @@ static std::string clock_event_name(const Context *ctx, const ClockEvent &e, int
return value; return value;
}; };
static void log_crit_paths(const Context *ctx) static void log_crit_paths(const Context *ctx, TimingResult &result)
{ {
static auto print_net_source = [ctx](const NetInfo *net) { static auto print_net_source = [ctx](const NetInfo *net) {
// Check if this net is annotated with a source list // Check if this net is annotated with a source list
@ -136,7 +138,7 @@ static void log_crit_paths(const Context *ctx)
}; };
// Single domain paths // Single domain paths
for (auto &clock : ctx->timing_result.clock_paths) { for (auto &clock : result.clock_paths) {
log_break(); log_break();
std::string start = std::string start =
clock.second.clock_pair.start.edge == FALLING_EDGE ? std::string("negedge") : std::string("posedge"); clock.second.clock_pair.start.edge == FALLING_EDGE ? std::string("negedge") : std::string("posedge");
@ -149,7 +151,7 @@ static void log_crit_paths(const Context *ctx)
} }
// Cross-domain paths // Cross-domain paths
for (auto &report : ctx->timing_result.xclock_paths) { for (auto &report : result.xclock_paths) {
log_break(); log_break();
std::string start = clock_event_name(ctx, report.clock_pair.start); std::string start = clock_event_name(ctx, report.clock_pair.start);
std::string end = clock_event_name(ctx, report.clock_pair.end); std::string end = clock_event_name(ctx, report.clock_pair.end);
@ -158,20 +160,25 @@ static void log_crit_paths(const Context *ctx)
} }
}; };
static void log_fmax(Context *ctx, bool warn_on_failure) static void log_fmax(Context *ctx, TimingResult &result, bool warn_on_failure)
{ {
log_break(); log_break();
if (result.clock_paths.empty() && result.clock_paths.empty()) {
log_info("No Fmax available; no interior timing paths found in design.\n");
return;
}
unsigned max_width = 0; unsigned max_width = 0;
for (auto &clock : ctx->timing_result.clock_paths) for (auto &clock : result.clock_paths)
max_width = std::max<unsigned>(max_width, clock.first.str(ctx).size()); max_width = std::max<unsigned>(max_width, clock.first.str(ctx).size());
for (auto &clock : ctx->timing_result.clock_paths) { for (auto &clock : result.clock_paths) {
const auto &clock_name = clock.first.str(ctx); const auto &clock_name = clock.first.str(ctx);
const int width = max_width - clock_name.size(); const int width = max_width - clock_name.size();
float fmax = ctx->timing_result.clock_fmax[clock.first].achieved; float fmax = result.clock_fmax[clock.first].achieved;
float target = ctx->timing_result.clock_fmax[clock.first].constraint; float target = result.clock_fmax[clock.first].constraint;
bool passed = target < fmax; bool passed = target < fmax;
if (!warn_on_failure || passed) if (!warn_on_failure || passed)
@ -188,31 +195,31 @@ static void log_fmax(Context *ctx, bool warn_on_failure)
// Clock to clock delays for xpaths // Clock to clock delays for xpaths
dict<ClockPair, delay_t> xclock_delays; dict<ClockPair, delay_t> xclock_delays;
for (auto &report : ctx->timing_result.xclock_paths) { for (auto &report : result.xclock_paths) {
const auto &clock1_name = report.clock_pair.start.clock; const auto &clock1_name = report.clock_pair.start.clock;
const auto &clock2_name = report.clock_pair.end.clock; const auto &clock2_name = report.clock_pair.end.clock;
const auto key = std::make_pair(clock1_name, clock2_name); const auto key = std::make_pair(clock1_name, clock2_name);
if (ctx->timing_result.clock_delays.count(key)) { if (result.clock_delays.count(key)) {
xclock_delays[report.clock_pair] = ctx->timing_result.clock_delays.at(key); xclock_delays[report.clock_pair] = result.clock_delays.at(key);
} }
} }
unsigned max_width_xca = 0; unsigned max_width_xca = 0;
unsigned max_width_xcb = 0; unsigned max_width_xcb = 0;
for (auto &report : ctx->timing_result.xclock_paths) { for (auto &report : result.xclock_paths) {
max_width_xca = std::max((unsigned)clock_event_name(ctx, report.clock_pair.start).length(), max_width_xca); max_width_xca = std::max((unsigned)clock_event_name(ctx, report.clock_pair.start).length(), max_width_xca);
max_width_xcb = std::max((unsigned)clock_event_name(ctx, report.clock_pair.end).length(), max_width_xcb); max_width_xcb = std::max((unsigned)clock_event_name(ctx, report.clock_pair.end).length(), max_width_xcb);
} }
// Check and report xpath delays for related clocks // Check and report xpath delays for related clocks
if (!ctx->timing_result.xclock_paths.empty()) { if (!result.xclock_paths.empty()) {
for (auto &report : ctx->timing_result.xclock_paths) { for (auto &report : result.xclock_paths) {
const auto &clock_a = report.clock_pair.start.clock; const auto &clock_a = report.clock_pair.start.clock;
const auto &clock_b = report.clock_pair.end.clock; const auto &clock_b = report.clock_pair.end.clock;
const auto key = std::make_pair(clock_a, clock_b); const auto key = std::make_pair(clock_a, clock_b);
if (!ctx->timing_result.clock_delays.count(key)) { if (!result.clock_delays.count(key)) {
continue; continue;
} }
@ -224,7 +231,7 @@ static void log_fmax(Context *ctx, bool warn_on_failure)
// Compensate path delay for clock-to-clock delay. If the // Compensate path delay for clock-to-clock delay. If the
// result is negative then only the latter matters. Otherwise // result is negative then only the latter matters. Otherwise
// the compensated path delay is taken. // the compensated path delay is taken.
auto clock_delay = ctx->timing_result.clock_delays.at(key); auto clock_delay = result.clock_delays.at(key);
path_delay -= clock_delay; path_delay -= clock_delay;
float fmax = std::numeric_limits<float>::infinity(); float fmax = std::numeric_limits<float>::infinity();
@ -239,7 +246,7 @@ static void log_fmax(Context *ctx, bool warn_on_failure)
// user input. In case of only one constraint preset take it, // user input. In case of only one constraint preset take it,
// otherwise get the worst case (min.) // otherwise get the worst case (min.)
float target; float target;
auto &clock_fmax = ctx->timing_result.clock_fmax; auto &clock_fmax = result.clock_fmax;
if (clock_fmax.count(clock_a) && !clock_fmax.count(clock_b)) { if (clock_fmax.count(clock_a) && !clock_fmax.count(clock_b)) {
target = clock_fmax.at(clock_a).constraint; target = clock_fmax.at(clock_a).constraint;
} else if (!clock_fmax.count(clock_a) && clock_fmax.count(clock_b)) { } else if (!clock_fmax.count(clock_a) && clock_fmax.count(clock_b)) {
@ -268,7 +275,7 @@ static void log_fmax(Context *ctx, bool warn_on_failure)
} }
// Report clock delays for xpaths // Report clock delays for xpaths
if (!ctx->timing_result.clock_delays.empty()) { if (!result.clock_delays.empty()) {
for (auto &pair : xclock_delays) { for (auto &pair : xclock_delays) {
auto ev_a = clock_event_name(ctx, pair.first.start, max_width_xca); auto ev_a = clock_event_name(ctx, pair.first.start, max_width_xca);
auto ev_b = clock_event_name(ctx, pair.first.end, max_width_xcb); auto ev_b = clock_event_name(ctx, pair.first.end, max_width_xcb);
@ -284,19 +291,19 @@ static void log_fmax(Context *ctx, bool warn_on_failure)
log_break(); log_break();
} }
for (auto &eclock : ctx->timing_result.empty_paths) { for (auto &eclock : result.empty_paths) {
if (eclock != IdString()) if (eclock != IdString())
log_info("Clock '%s' has no interior paths\n", eclock.c_str(ctx)); log_info("Clock '%s' has no interior paths\n", eclock.c_str(ctx));
} }
log_break(); log_break();
int start_field_width = 0, end_field_width = 0; int start_field_width = 0, end_field_width = 0;
for (auto &report : ctx->timing_result.xclock_paths) { for (auto &report : result.xclock_paths) {
start_field_width = std::max((int)clock_event_name(ctx, report.clock_pair.start).length(), start_field_width); start_field_width = std::max((int)clock_event_name(ctx, report.clock_pair.start).length(), start_field_width);
end_field_width = std::max((int)clock_event_name(ctx, report.clock_pair.end).length(), end_field_width); end_field_width = std::max((int)clock_event_name(ctx, report.clock_pair.end).length(), end_field_width);
} }
for (auto &report : ctx->timing_result.xclock_paths) { for (auto &report : result.xclock_paths) {
const ClockEvent &a = report.clock_pair.start; const ClockEvent &a = report.clock_pair.start;
const ClockEvent &b = report.clock_pair.end; const ClockEvent &b = report.clock_pair.end;
delay_t path_delay = 0; delay_t path_delay = 0;
@ -309,23 +316,15 @@ static void log_fmax(Context *ctx, bool warn_on_failure)
log_break(); log_break();
} }
static void log_histogram(Context *ctx) static void log_histogram(Context *ctx, TimingResult &result)
{ {
log_break();
log_info("Slack histogram:\n");
if (ctx->timing_result.slack_histogram.empty()) {
log_info(" No slack figures available\n");
return;
}
unsigned num_bins = 20; unsigned num_bins = 20;
unsigned bar_width = 60; unsigned bar_width = 60;
int min_slack = INT_MAX; int min_slack = INT_MAX;
int max_slack = INT_MIN; int max_slack = INT_MIN;
for (const auto &i : ctx->timing_result.slack_histogram) { for (const auto &i : result.slack_histogram) {
if (i.first < min_slack) if (i.first < min_slack)
min_slack = i.first; min_slack = i.first;
if (i.first > max_slack) if (i.first > max_slack)
@ -335,7 +334,7 @@ static void log_histogram(Context *ctx)
auto bin_size = std::max<unsigned>(1, ceil((max_slack - min_slack + 1) / float(num_bins))); auto bin_size = std::max<unsigned>(1, ceil((max_slack - min_slack + 1) / float(num_bins)));
std::vector<unsigned> bins(num_bins); std::vector<unsigned> bins(num_bins);
unsigned max_freq = 0; unsigned max_freq = 0;
for (const auto &i : ctx->timing_result.slack_histogram) { for (const auto &i : result.slack_histogram) {
int bin_idx = int((i.first - min_slack) / bin_size); int bin_idx = int((i.first - min_slack) / bin_size);
if (bin_idx < 0) if (bin_idx < 0)
bin_idx = 0; bin_idx = 0;
@ -347,6 +346,8 @@ static void log_histogram(Context *ctx)
} }
bar_width = std::min(bar_width, max_freq); bar_width = std::min(bar_width, max_freq);
log_break();
log_info("Slack histogram:\n");
log_info(" legend: * represents %d endpoint(s)\n", max_freq / bar_width); log_info(" legend: * represents %d endpoint(s)\n", max_freq / bar_width);
log_info(" + represents [1,%d) endpoint(s)\n", max_freq / bar_width); log_info(" + represents [1,%d) endpoint(s)\n", max_freq / bar_width);
for (unsigned i = 0; i < num_bins; ++i) for (unsigned i = 0; i < num_bins; ++i)
@ -355,15 +356,17 @@ static void log_histogram(Context *ctx)
(bins[i] * bar_width) % max_freq > 0 ? '+' : ' '); (bins[i] * bar_width) % max_freq > 0 ? '+' : ' ');
} }
void Context::log_timing_results(bool print_histogram, bool print_path, bool warn_on_failure) void Context::log_timing_results(TimingResult &result, bool print_histogram, bool print_fmax, bool print_path,
bool warn_on_failure)
{ {
if (print_path) if (print_path)
log_crit_paths(this); log_crit_paths(this, result);
log_fmax(this, warn_on_failure); if (print_fmax)
log_fmax(this, result, warn_on_failure);
if (print_histogram) if (print_histogram && !timing_result.slack_histogram.empty())
log_histogram(this); log_histogram(this, result);
} }
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

File diff suppressed because it is too large Load Diff