Added the --ignore-rel-clk option to control timing checks for cross-domain paths, formatted code
Signed-off-by: Maciej Kurc <mkurc@antmicro.com>
This commit is contained in:
parent
1f1bae3e23
commit
9000c41c4b
@ -172,6 +172,7 @@ po::options_description CommandHandler::getGeneralOptions()
|
|||||||
general.add_options()("no-pack", "process design without packing");
|
general.add_options()("no-pack", "process design without packing");
|
||||||
|
|
||||||
general.add_options()("ignore-loops", "ignore combinational loops in timing analysis");
|
general.add_options()("ignore-loops", "ignore combinational loops in timing analysis");
|
||||||
|
general.add_options()("ignore-rel-clk", "ignore clock-to-clock relations in timing checks");
|
||||||
|
|
||||||
general.add_options()("version,V", "show version");
|
general.add_options()("version,V", "show version");
|
||||||
general.add_options()("test", "check architecture database integrity");
|
general.add_options()("test", "check architecture database integrity");
|
||||||
@ -270,6 +271,10 @@ void CommandHandler::setupContext(Context *ctx)
|
|||||||
ctx->settings[ctx->id("timing/ignoreLoops")] = true;
|
ctx->settings[ctx->id("timing/ignoreLoops")] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vm.count("ignore-rel-clk")) {
|
||||||
|
ctx->settings[ctx->id("timing/ignoreRelClk")] = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (vm.count("timing-allow-fail")) {
|
if (vm.count("timing-allow-fail")) {
|
||||||
ctx->settings[ctx->id("timing/allowFail")] = true;
|
ctx->settings[ctx->id("timing/allowFail")] = true;
|
||||||
}
|
}
|
||||||
|
@ -285,81 +285,81 @@ void TimingAnalyser::setup_port_domains()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimingAnalyser::identify_related_domains() {
|
void TimingAnalyser::identify_related_domains()
|
||||||
|
{
|
||||||
|
|
||||||
// Identify clock nets
|
// Identify clock nets
|
||||||
pool<IdString> clock_nets;
|
pool<IdString> clock_nets;
|
||||||
for (const auto& domain : domains) {
|
for (const auto &domain : domains) {
|
||||||
clock_nets.insert(domain.key.clock);
|
clock_nets.insert(domain.key.clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each clock net identify all nets that can possibly drive it. Compute
|
// For each clock net identify all nets that can possibly drive it. Compute
|
||||||
// cumulative delays to each of them.
|
// cumulative delays to each of them.
|
||||||
std::function<void(const NetInfo*, dict<IdString, delay_t>&, delay_t)> find_net_drivers =
|
std::function<void(const NetInfo *, dict<IdString, delay_t> &, delay_t)> find_net_drivers =
|
||||||
[&] (const NetInfo* ni, dict<IdString, delay_t>& drivers, delay_t delay_acc)
|
[&](const NetInfo *ni, dict<IdString, delay_t> &drivers, delay_t delay_acc) {
|
||||||
{
|
// Get driving cell and port
|
||||||
// Get driving cell and port
|
const CellInfo *cell = ni->driver.cell;
|
||||||
const CellInfo* cell = ni->driver.cell;
|
const IdString port = ni->driver.port;
|
||||||
const IdString port = ni->driver.port;
|
|
||||||
|
|
||||||
bool didGoUpstream = false;
|
bool didGoUpstream = false;
|
||||||
|
|
||||||
// The cell has only one port
|
// The cell has only one port
|
||||||
if (cell->ports.size() == 1) {
|
if (cell->ports.size() == 1) {
|
||||||
drivers[ni->name] = delay_acc;
|
drivers[ni->name] = delay_acc;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the driver timing class
|
// Get the driver timing class
|
||||||
int info_count = 0;
|
int info_count = 0;
|
||||||
auto timing_class = ctx->getPortTimingClass(cell, port, info_count);
|
auto timing_class = ctx->getPortTimingClass(cell, port, info_count);
|
||||||
|
|
||||||
// The driver must be a combinational output
|
// The driver must be a combinational output
|
||||||
if (timing_class != TMG_COMB_OUTPUT) {
|
if (timing_class != TMG_COMB_OUTPUT) {
|
||||||
drivers[ni->name] = delay_acc;
|
drivers[ni->name] = delay_acc;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recurse upstream through all input ports that have combinational
|
// Recurse upstream through all input ports that have combinational
|
||||||
// paths to this driver
|
// paths to this driver
|
||||||
for (const auto& it : cell->ports) {
|
for (const auto &it : cell->ports) {
|
||||||
const auto& pi = it.second;
|
const auto &pi = it.second;
|
||||||
|
|
||||||
// Only connected inputs
|
// Only connected inputs
|
||||||
if (pi.type != PORT_IN) {
|
if (pi.type != PORT_IN) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (pi.net == nullptr) {
|
if (pi.net == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The input must be a combinational input
|
// The input must be a combinational input
|
||||||
timing_class = ctx->getPortTimingClass(cell, pi.name, info_count);
|
timing_class = ctx->getPortTimingClass(cell, pi.name, info_count);
|
||||||
if (timing_class != TMG_COMB_INPUT) {
|
if (timing_class != TMG_COMB_INPUT) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// There must be a combinational arc
|
// There must be a combinational arc
|
||||||
DelayQuad delay;
|
DelayQuad delay;
|
||||||
if (!ctx->getCellDelay(cell, pi.name, port, delay)) {
|
if (!ctx->getCellDelay(cell, pi.name, port, delay)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recurse
|
// Recurse
|
||||||
find_net_drivers(pi.net, drivers, delay_acc + delay.maxDelay());
|
find_net_drivers(pi.net, drivers, delay_acc + delay.maxDelay());
|
||||||
didGoUpstream = true;
|
didGoUpstream = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Did not propagate upstream through the cell, mark the net as driver
|
// Did not propagate upstream through the cell, mark the net as driver
|
||||||
if (!didGoUpstream) {
|
if (!didGoUpstream) {
|
||||||
drivers[ni->name] = delay_acc;
|
drivers[ni->name] = delay_acc;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Identify possible drivers for each clock domain
|
// Identify possible drivers for each clock domain
|
||||||
dict<IdString, dict<IdString, delay_t>> clock_drivers;
|
dict<IdString, dict<IdString, delay_t>> clock_drivers;
|
||||||
for (const auto& domain : domains) {
|
for (const auto &domain : domains) {
|
||||||
|
|
||||||
const NetInfo* ni = ctx->nets.at(domain.key.clock).get();
|
const NetInfo *ni = ctx->nets.at(domain.key.clock).get();
|
||||||
dict<IdString, delay_t> drivers;
|
dict<IdString, delay_t> drivers;
|
||||||
find_net_drivers(ni, drivers, 0);
|
find_net_drivers(ni, drivers, 0);
|
||||||
|
|
||||||
@ -367,21 +367,18 @@ void TimingAnalyser::identify_related_domains() {
|
|||||||
|
|
||||||
if (ctx->debug) {
|
if (ctx->debug) {
|
||||||
log("Clock '%s' can be driven by:\n", domain.key.clock.str(ctx).c_str());
|
log("Clock '%s' can be driven by:\n", domain.key.clock.str(ctx).c_str());
|
||||||
for (const auto& it : drivers) {
|
for (const auto &it : drivers) {
|
||||||
const NetInfo* net = ctx->nets.at(it.first).get();
|
const NetInfo *net = ctx->nets.at(it.first).get();
|
||||||
log(" %s.%s delay %.3fns\n",
|
log(" %s.%s delay %.3fns\n", net->driver.cell->name.str(ctx).c_str(), net->driver.port.str(ctx).c_str(),
|
||||||
net->driver.cell->name.str(ctx).c_str(),
|
ctx->getDelayNS(it.second));
|
||||||
net->driver.port.str(ctx).c_str(),
|
|
||||||
ctx->getDelayNS(it.second)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identify related clocks. For simplicity do it both for A->B and B->A
|
// Identify related clocks. For simplicity do it both for A->B and B->A
|
||||||
// cases.
|
// cases.
|
||||||
for (const auto& c1 : clock_drivers) {
|
for (const auto &c1 : clock_drivers) {
|
||||||
for (const auto& c2 : clock_drivers) {
|
for (const auto &c2 : clock_drivers) {
|
||||||
|
|
||||||
if (c1 == c2) {
|
if (c1 == c2) {
|
||||||
continue;
|
continue;
|
||||||
@ -389,14 +386,14 @@ void TimingAnalyser::identify_related_domains() {
|
|||||||
|
|
||||||
// Make an intersection of the two drivers sets
|
// Make an intersection of the two drivers sets
|
||||||
pool<IdString> common_drivers;
|
pool<IdString> common_drivers;
|
||||||
for (const auto& it : c1.second) {
|
for (const auto &it : c1.second) {
|
||||||
common_drivers.insert(it.first);
|
common_drivers.insert(it.first);
|
||||||
}
|
}
|
||||||
for (const auto& it : c2.second) {
|
for (const auto &it : c2.second) {
|
||||||
common_drivers.insert(it.first);
|
common_drivers.insert(it.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it=common_drivers.begin(); it!=common_drivers.end();) {
|
for (auto it = common_drivers.begin(); it != common_drivers.end();) {
|
||||||
if (!c1.second.count(*it) || !c2.second.count(*it)) {
|
if (!c1.second.count(*it) || !c2.second.count(*it)) {
|
||||||
it = common_drivers.erase(it);
|
it = common_drivers.erase(it);
|
||||||
} else {
|
} else {
|
||||||
@ -406,21 +403,17 @@ void TimingAnalyser::identify_related_domains() {
|
|||||||
|
|
||||||
if (ctx->debug) {
|
if (ctx->debug) {
|
||||||
|
|
||||||
log("Possible common driver(s) for clocks '%s' and '%s'\n",
|
log("Possible common driver(s) for clocks '%s' and '%s'\n", c1.first.str(ctx).c_str(),
|
||||||
c1.first.str(ctx).c_str(), c2.first.str(ctx).c_str());
|
c2.first.str(ctx).c_str());
|
||||||
|
|
||||||
for (const auto& it : common_drivers) {
|
for (const auto &it : common_drivers) {
|
||||||
|
|
||||||
const NetInfo* ni = ctx->nets.at(it).get();
|
const NetInfo *ni = ctx->nets.at(it).get();
|
||||||
const CellInfo* cell = ni->driver.cell;
|
const CellInfo *cell = ni->driver.cell;
|
||||||
const IdString port = ni->driver.port;
|
const IdString port = ni->driver.port;
|
||||||
|
|
||||||
log(" net '%s', cell %s (%s), port %s\n",
|
log(" net '%s', cell %s (%s), port %s\n", it.str(ctx).c_str(), cell->name.str(ctx).c_str(),
|
||||||
it.str(ctx).c_str(),
|
cell->type.str(ctx).c_str(), port.str(ctx).c_str());
|
||||||
cell->name.str(ctx).c_str(),
|
|
||||||
cell->type.str(ctx).c_str(),
|
|
||||||
port.str(ctx).c_str()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,7 +425,7 @@ void TimingAnalyser::identify_related_domains() {
|
|||||||
|
|
||||||
// Compute delay from c1 to c2 and store it
|
// Compute delay from c1 to c2 and store it
|
||||||
auto driver = *common_drivers.begin();
|
auto driver = *common_drivers.begin();
|
||||||
auto delay = c2.second.at(driver) - c1.second.at(driver);
|
auto delay = c2.second.at(driver) - c1.second.at(driver);
|
||||||
clock_delays[std::make_pair(c1.first, c2.first)] = delay;
|
clock_delays[std::make_pair(c1.first, c2.first)] = delay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -626,8 +619,8 @@ void TimingAnalyser::compute_slack()
|
|||||||
auto &dp = domain_pairs.at(pdp.first);
|
auto &dp = domain_pairs.at(pdp.first);
|
||||||
|
|
||||||
// Get clock names
|
// Get clock names
|
||||||
const auto& launch_clock = domains.at(dp.key.launch).key.clock;
|
const auto &launch_clock = domains.at(dp.key.launch).key.clock;
|
||||||
const auto& capture_clock = domains.at(dp.key.capture).key.clock;
|
const auto &capture_clock = domains.at(dp.key.capture).key.clock;
|
||||||
|
|
||||||
// Get clock-to-clock delay if any
|
// Get clock-to-clock delay if any
|
||||||
delay_t clock_to_clock = 0;
|
delay_t clock_to_clock = 0;
|
||||||
@ -727,12 +720,9 @@ void TimingAnalyser::print_report()
|
|||||||
|
|
||||||
print_fmax();
|
print_fmax();
|
||||||
|
|
||||||
for (const auto& it : clock_delays) {
|
for (const auto &it : clock_delays) {
|
||||||
log_info("Clock-to-clock %s -> %s: %0.02f ns\n",
|
log_info("Clock-to-clock %s -> %s: %0.02f ns\n", it.first.first.str(ctx).c_str(),
|
||||||
it.first.first.str(ctx).c_str(),
|
it.first.second.str(ctx).c_str(), ctx->getDelayNS(it.second));
|
||||||
it.first.second.str(ctx).c_str(),
|
|
||||||
ctx->getDelayNS(it.second)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1393,7 +1383,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
|||||||
timing.walk_paths();
|
timing.walk_paths();
|
||||||
|
|
||||||
// Use TimingAnalyser to determine clock-to-clock relations
|
// Use TimingAnalyser to determine clock-to-clock relations
|
||||||
TimingAnalyser timingAnalyser (ctx);
|
TimingAnalyser timingAnalyser(ctx);
|
||||||
timingAnalyser.setup();
|
timingAnalyser.setup();
|
||||||
|
|
||||||
bool report_critical_paths = print_path || print_fmax || update_results;
|
bool report_critical_paths = print_path || print_fmax || update_results;
|
||||||
@ -1628,13 +1618,13 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
|||||||
log_break();
|
log_break();
|
||||||
|
|
||||||
// All clock to clock delays
|
// All clock to clock delays
|
||||||
const auto& clock_delays = timingAnalyser.get_clock_delays();
|
const auto &clock_delays = timingAnalyser.get_clock_delays();
|
||||||
|
|
||||||
// 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 : xclock_reports) {
|
for (auto &report : xclock_reports) {
|
||||||
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 (clock_delays.count(key)) {
|
if (clock_delays.count(key)) {
|
||||||
@ -1646,14 +1636,14 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
|||||||
unsigned max_width_xcb = 0;
|
unsigned max_width_xcb = 0;
|
||||||
for (auto &report : xclock_reports) {
|
for (auto &report : xclock_reports) {
|
||||||
max_width_xca = std::max((unsigned)format_event(report.clock_pair.start).length(), max_width_xca);
|
max_width_xca = std::max((unsigned)format_event(report.clock_pair.start).length(), max_width_xca);
|
||||||
max_width_xcb = std::max((unsigned)format_event(report.clock_pair.end).length(), max_width_xcb);
|
max_width_xcb = std::max((unsigned)format_event(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 (!xclock_reports.empty()) {
|
if (!xclock_reports.empty()) {
|
||||||
for (auto &report : xclock_reports) {
|
for (auto &report : xclock_reports) {
|
||||||
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 (!clock_delays.count(key)) {
|
if (!clock_delays.count(key)) {
|
||||||
@ -1685,28 +1675,27 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
|||||||
float target;
|
float target;
|
||||||
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)) {
|
|
||||||
target = clock_fmax.at(clock_b).constraint;
|
target = clock_fmax.at(clock_b).constraint;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
target = std::min(clock_fmax.at(clock_a).constraint, clock_fmax.at(clock_b).constraint);
|
target = std::min(clock_fmax.at(clock_a).constraint, clock_fmax.at(clock_b).constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool passed = target < fmax;
|
bool passed = target < fmax;
|
||||||
|
|
||||||
auto ev_a = format_event(report.clock_pair.start, max_width_xca);
|
auto ev_a = format_event(report.clock_pair.start, max_width_xca);
|
||||||
auto ev_b = format_event(report.clock_pair.end, max_width_xcb);
|
auto ev_b = format_event(report.clock_pair.end, max_width_xcb);
|
||||||
|
|
||||||
if (!warn_on_failure || passed)
|
if (!warn_on_failure || passed)
|
||||||
log_info("Max frequency for %s -> %s: %.02f MHz (%s at %.02f MHz)\n",
|
log_info("Max frequency for %s -> %s: %.02f MHz (%s at %.02f MHz)\n", ev_a.c_str(), ev_b.c_str(),
|
||||||
ev_a.c_str(), ev_b.c_str(), fmax, passed ? "PASS" : "FAIL", target);
|
fmax, passed ? "PASS" : "FAIL", target);
|
||||||
else if (bool_or_default(ctx->settings, ctx->id("timing/allowFail"), false))
|
else if (bool_or_default(ctx->settings, ctx->id("timing/allowFail"), false) ||
|
||||||
log_warning("Max frequency for %s -> %s: %.02f MHz (%s at %.02f MHz)\n",
|
bool_or_default(ctx->settings, ctx->id("timing/ignoreRelClk"), false))
|
||||||
ev_a.c_str(), ev_b.c_str(), fmax, passed ? "PASS" : "FAIL", target);
|
log_warning("Max frequency for %s -> %s: %.02f MHz (%s at %.02f MHz)\n", ev_a.c_str(),
|
||||||
|
ev_b.c_str(), fmax, passed ? "PASS" : "FAIL", target);
|
||||||
else
|
else
|
||||||
log_nonfatal_error("Max frequency for %s -> %s: %.02f MHz (%s at %.02f MHz)\n",
|
log_nonfatal_error("Max frequency for %s -> %s: %.02f MHz (%s at %.02f MHz)\n", ev_a.c_str(),
|
||||||
ev_a.c_str(), ev_b.c_str(), fmax, passed ? "PASS" : "FAIL", target);
|
ev_b.c_str(), fmax, passed ? "PASS" : "FAIL", target);
|
||||||
}
|
}
|
||||||
log_break();
|
log_break();
|
||||||
}
|
}
|
||||||
@ -1722,7 +1711,8 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
|
|||||||
delay /= 2;
|
delay /= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("Clock to clock delay %s -> %s: %0.02f ns\n", ev_a.c_str(), ev_b.c_str(), ctx->getDelayNS(delay));
|
log_info("Clock to clock delay %s -> %s: %0.02f ns\n", ev_a.c_str(), ev_b.c_str(),
|
||||||
|
ctx->getDelayNS(delay));
|
||||||
}
|
}
|
||||||
|
|
||||||
log_break();
|
log_break();
|
||||||
|
@ -92,9 +92,7 @@ struct TimingAnalyser
|
|||||||
return slack;
|
return slack;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto get_clock_delays () const {
|
auto get_clock_delays() const { return clock_delays; }
|
||||||
return clock_delays;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setup_only = false;
|
bool setup_only = false;
|
||||||
bool verbose_mode = false;
|
bool verbose_mode = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user