Remove old timing analyser
This commit is contained in:
parent
b0820eeaaa
commit
d2a489d5e9
@ -99,11 +99,12 @@ struct Context : Arch, DeterministicRNG
|
||||
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// provided by report_json.cc
|
||||
// provided by report.cc
|
||||
void writeJsonReport(std::ostream &out) const;
|
||||
|
||||
// provided by timing_report.cc
|
||||
void log_timing_results(bool print_histogram, bool print_path, bool warn_on_failure);
|
||||
// provided by timing_log.cc
|
||||
void log_timing_results(TimingResult &result, bool print_histogram, bool print_fmax, bool print_path,
|
||||
bool warn_on_failure);
|
||||
// --------------------------------------------------------------
|
||||
|
||||
uint32_t checksum() const;
|
||||
|
@ -365,7 +365,7 @@ struct TimingResult
|
||||
// clock to clock delays
|
||||
dict<std::pair<IdString, IdString>, delay_t> clock_delays;
|
||||
|
||||
// Histogram of delays
|
||||
// Histogram of slack
|
||||
dict<int, unsigned> slack_histogram;
|
||||
};
|
||||
|
||||
|
@ -238,10 +238,7 @@ void Context::writeJsonReport(std::ostream &out) const
|
||||
}
|
||||
|
||||
Json::object jsonRoot{
|
||||
{ "utilization", util_json },
|
||||
{ "fmax", fmax_json },
|
||||
{ "critical_paths", json_report_critical_paths(this) }
|
||||
};
|
||||
{"utilization", util_json}, {"fmax", fmax_json}, {"critical_paths", json_report_critical_paths(this)}};
|
||||
|
||||
if (detailed_timing_report) {
|
||||
jsonRoot["detailed_net_timings"] = json_report_detailed_net_timings(this);
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2018 gatecat <gatecat@ds0.me>
|
||||
* 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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@ -29,38 +30,22 @@
|
||||
|
||||
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)
|
||||
{
|
||||
ClockDomainKey key{IdString(), ClockEdge::RISING_EDGE};
|
||||
domain_to_id.emplace(key, 0);
|
||||
domains.emplace_back(key);
|
||||
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,
|
||||
bool update_route_delays)
|
||||
void TimingAnalyser::setup(bool update_net_timings, bool update_histogram, bool update_crit_paths)
|
||||
{
|
||||
init_ports();
|
||||
get_cell_delays();
|
||||
topo_sort();
|
||||
setup_port_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,
|
||||
@ -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
|
||||
// timing_result which contains mixed reports
|
||||
if (update_net_timings || update_histogram || update_crit_paths) {
|
||||
ctx->timing_result = TimingResult();
|
||||
result = TimingResult();
|
||||
}
|
||||
|
||||
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> domain_delay;
|
||||
|
||||
for (auto p : topological_order) {
|
||||
auto &pd = ports.at(p);
|
||||
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 l = domains.at(launch);
|
||||
auto c = domains.at(capture);
|
||||
|
||||
delay_t delay = arr.second.value.maxDelay() - req.second.value.minDelay();
|
||||
if (!domain_delay.count(dp) || domain_delay.at(dp) < delay)
|
||||
domain_delay[dp] = delay;
|
||||
@ -728,7 +717,6 @@ void TimingAnalyser::compute_criticality()
|
||||
{
|
||||
for (auto p : topological_order) {
|
||||
auto &pd = ports.at(p);
|
||||
|
||||
for (auto &pdp : pd.domain_pairs) {
|
||||
auto &dp = domain_pairs.at(pdp.first);
|
||||
// Do not set criticality for asynchronous paths
|
||||
@ -747,7 +735,7 @@ void TimingAnalyser::compute_criticality()
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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 report;
|
||||
@ -855,7 +817,6 @@ CriticalPath TimingAnalyser::build_critical_path_report(domain_id_t domain_pair,
|
||||
std::vector<PortRef> crit_path_rev;
|
||||
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()) {
|
||||
auto cell = cell_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;
|
||||
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) {
|
||||
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()
|
||||
{
|
||||
auto &clock_reports = ctx->timing_result.clock_paths;
|
||||
auto &xclock_reports = ctx->timing_result.xclock_paths;
|
||||
auto &clock_fmax = ctx->timing_result.clock_fmax;
|
||||
auto &empty_clocks = ctx->timing_result.empty_paths;
|
||||
auto &clock_delays_ctx = ctx->timing_result.clock_delays;
|
||||
auto &clock_reports = result.clock_paths;
|
||||
auto &xclock_reports = result.xclock_paths;
|
||||
auto &clock_fmax = result.clock_fmax;
|
||||
auto &empty_clocks = result.empty_paths;
|
||||
auto &clock_delays_ctx = result.clock_delays;
|
||||
|
||||
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())
|
||||
continue;
|
||||
|
||||
auto path_delay = delay_by_domain.at(dp.key.launch);
|
||||
auto path_delay = delay_by_domain.at(i);
|
||||
|
||||
double Fmax;
|
||||
empty_clocks.erase(launch.clock);
|
||||
|
||||
if (launch.edge == capture.edge)
|
||||
Fmax = 1000 / ctx->getDelayNS(path_delay);
|
||||
@ -999,10 +956,16 @@ void TimingAnalyser::build_crit_path_reports()
|
||||
if (ctx->nets.at(launch.clock)->clkconstr)
|
||||
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].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 &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())
|
||||
continue;
|
||||
|
||||
log_info("testin testing2 %s -> %s\n", clock_event_name(ctx, launch).c_str(),
|
||||
clock_event_name(ctx, capture).c_str());
|
||||
auto worst_endpoint = get_worst_eps(i, 1);
|
||||
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));
|
||||
xclock_reports.emplace_back(build_critical_path_report(i, worst_endpoint.at(0)));
|
||||
}
|
||||
|
||||
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 &b = rb.clock_pair;
|
||||
|
||||
@ -1043,21 +1003,16 @@ void TimingAnalyser::build_crit_path_reports()
|
||||
if (a.end.edge < b.end.edge)
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
for (auto &clock : clock_reports) {
|
||||
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;
|
||||
}
|
||||
std::sort(xclock_reports.begin(), xclock_reports.end(), cmp_crit_path);
|
||||
|
||||
clock_delays_ctx = clock_delays;
|
||||
}
|
||||
|
||||
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 (auto &ep : domains.at(dom_id).endpoints) {
|
||||
@ -1069,7 +1024,7 @@ void TimingAnalyser::build_slack_histogram_report()
|
||||
auto &launch = domains.at(arr.first).key;
|
||||
|
||||
// @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())
|
||||
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); }
|
||||
|
||||
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
|
||||
|
@ -2,6 +2,7 @@
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* 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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@ -74,8 +75,10 @@ struct TimingAnalyser
|
||||
{
|
||||
public:
|
||||
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
|
||||
// model), but want to re-run STA with their own calculated delays
|
||||
@ -91,7 +94,9 @@ struct TimingAnalyser
|
||||
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 verbose_mode = false;
|
||||
@ -115,18 +120,14 @@ struct TimingAnalyser
|
||||
void compute_criticality();
|
||||
|
||||
void build_detailed_net_timing_report();
|
||||
// Build up CriticalPathData
|
||||
CriticalPath build_critical_path_report(domain_id_t domain_pair, CellPortKey endpoint);
|
||||
void build_crit_path_reports();
|
||||
void build_slack_histogram_report();
|
||||
|
||||
|
||||
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);
|
||||
// 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()};
|
||||
|
||||
@ -235,10 +236,11 @@ struct TimingAnalyser
|
||||
domain_id_t async_clock_id;
|
||||
|
||||
Context *ctx;
|
||||
|
||||
TimingResult result;
|
||||
};
|
||||
|
||||
// Perform timing analysis and print out the fmax, and optionally the
|
||||
// critical path
|
||||
// Perform timing analysis and optionaly print out slack histogram, fmax and critical paths
|
||||
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);
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* 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>
|
||||
*
|
||||
* 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;
|
||||
};
|
||||
|
||||
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) {
|
||||
// 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
|
||||
for (auto &clock : ctx->timing_result.clock_paths) {
|
||||
for (auto &clock : result.clock_paths) {
|
||||
log_break();
|
||||
std::string start =
|
||||
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
|
||||
for (auto &report : ctx->timing_result.xclock_paths) {
|
||||
for (auto &report : result.xclock_paths) {
|
||||
log_break();
|
||||
std::string start = clock_event_name(ctx, report.clock_pair.start);
|
||||
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();
|
||||
|
||||
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;
|
||||
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());
|
||||
|
||||
for (auto &clock : ctx->timing_result.clock_paths) {
|
||||
for (auto &clock : result.clock_paths) {
|
||||
const auto &clock_name = clock.first.str(ctx);
|
||||
const int width = max_width - clock_name.size();
|
||||
|
||||
float fmax = ctx->timing_result.clock_fmax[clock.first].achieved;
|
||||
float target = ctx->timing_result.clock_fmax[clock.first].constraint;
|
||||
float fmax = result.clock_fmax[clock.first].achieved;
|
||||
float target = result.clock_fmax[clock.first].constraint;
|
||||
bool passed = target < fmax;
|
||||
|
||||
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
|
||||
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 &clock2_name = report.clock_pair.end.clock;
|
||||
|
||||
const auto key = std::make_pair(clock1_name, clock2_name);
|
||||
if (ctx->timing_result.clock_delays.count(key)) {
|
||||
xclock_delays[report.clock_pair] = ctx->timing_result.clock_delays.at(key);
|
||||
if (result.clock_delays.count(key)) {
|
||||
xclock_delays[report.clock_pair] = result.clock_delays.at(key);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned max_width_xca = 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_xcb = std::max((unsigned)clock_event_name(ctx, report.clock_pair.end).length(), max_width_xcb);
|
||||
}
|
||||
|
||||
// Check and report xpath delays for related clocks
|
||||
if (!ctx->timing_result.xclock_paths.empty()) {
|
||||
for (auto &report : ctx->timing_result.xclock_paths) {
|
||||
if (!result.xclock_paths.empty()) {
|
||||
for (auto &report : result.xclock_paths) {
|
||||
const auto &clock_a = report.clock_pair.start.clock;
|
||||
const auto &clock_b = report.clock_pair.end.clock;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -224,7 +231,7 @@ static void log_fmax(Context *ctx, bool warn_on_failure)
|
||||
// Compensate path delay for clock-to-clock delay. If the
|
||||
// result is negative then only the latter matters. Otherwise
|
||||
// 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;
|
||||
|
||||
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,
|
||||
// otherwise get the worst case (min.)
|
||||
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)) {
|
||||
target = clock_fmax.at(clock_a).constraint;
|
||||
} 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
|
||||
if (!ctx->timing_result.clock_delays.empty()) {
|
||||
if (!result.clock_delays.empty()) {
|
||||
for (auto &pair : xclock_delays) {
|
||||
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);
|
||||
@ -284,19 +291,19 @@ static void log_fmax(Context *ctx, bool warn_on_failure)
|
||||
log_break();
|
||||
}
|
||||
|
||||
for (auto &eclock : ctx->timing_result.empty_paths) {
|
||||
for (auto &eclock : result.empty_paths) {
|
||||
if (eclock != IdString())
|
||||
log_info("Clock '%s' has no interior paths\n", eclock.c_str(ctx));
|
||||
}
|
||||
log_break();
|
||||
|
||||
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);
|
||||
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 &b = report.clock_pair.end;
|
||||
delay_t path_delay = 0;
|
||||
@ -309,23 +316,15 @@ static void log_fmax(Context *ctx, bool warn_on_failure)
|
||||
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 bar_width = 60;
|
||||
|
||||
int min_slack = INT_MAX;
|
||||
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)
|
||||
min_slack = i.first;
|
||||
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)));
|
||||
std::vector<unsigned> bins(num_bins);
|
||||
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);
|
||||
if (bin_idx < 0)
|
||||
bin_idx = 0;
|
||||
@ -347,6 +346,8 @@ static void log_histogram(Context *ctx)
|
||||
}
|
||||
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(" + represents [1,%d) endpoint(s)\n", max_freq / bar_width);
|
||||
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 ? '+' : ' ');
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
log_histogram(this);
|
||||
if (print_histogram && !timing_result.slack_histogram.empty())
|
||||
log_histogram(this, result);
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user