aboutsummaryrefslogtreecommitdiffstats
path: root/common/timing.cc
diff options
context:
space:
mode:
authorgatecat <gatecat@ds0.me>2021-03-02 10:39:26 +0000
committergatecat <gatecat@ds0.me>2021-03-04 10:29:36 +0000
commit16e7bba87b8944914abf5a5d18561393f8a5c39b (patch)
tree7f2e14d0f22bb89a95cfb937f89b6d1b40d8c178 /common/timing.cc
parent0528ceead1276171fff4ecaa3aafe6b0e9c8ffa5 (diff)
downloadnextpnr-16e7bba87b8944914abf5a5d18561393f8a5c39b.tar.gz
nextpnr-16e7bba87b8944914abf5a5d18561393f8a5c39b.tar.bz2
nextpnr-16e7bba87b8944914abf5a5d18561393f8a5c39b.zip
timing: Add backwards path walking
Signed-off-by: gatecat <gatecat@ds0.me>
Diffstat (limited to 'common/timing.cc')
-rw-r--r--common/timing.cc66
1 files changed, 65 insertions, 1 deletions
diff --git a/common/timing.cc b/common/timing.cc
index d492f574..d39aced8 100644
--- a/common/timing.cc
+++ b/common/timing.cc
@@ -38,6 +38,7 @@ void TimingAnalyser::setup()
setup_port_domains();
reset_times();
walk_forward();
+ walk_backward();
}
void TimingAnalyser::init_ports()
@@ -217,7 +218,7 @@ void TimingAnalyser::setup_port_domains()
auto dom = domain_id(port.cell, fanout.other_port, fanout.edge);
// create per-domain data
pd.required[dom];
- domains.at(dom).startpoints.emplace_back(port, fanout.other_port);
+ domains.at(dom).endpoints.emplace_back(port, fanout.other_port);
}
// copy port to driver
if (pi.net != nullptr && pi.net->driver.cell != nullptr)
@@ -272,6 +273,21 @@ void TimingAnalyser::set_arrival_time(CellPortKey target, domain_id_t domain, De
arr.path_length = std::max(arr.path_length, path_length);
}
+void TimingAnalyser::set_required_time(CellPortKey target, domain_id_t domain, DelayPair required, int path_length,
+ CellPortKey prev)
+{
+ auto &req = ports.at(target).required.at(domain);
+ if (required.min_delay < req.value.min_delay) {
+ req.value.min_delay = required.min_delay;
+ req.bwd_min = prev;
+ }
+ if (!setup_only && (required.max_delay > req.value.max_delay)) {
+ req.value.max_delay = required.max_delay;
+ req.bwd_max = prev;
+ }
+ req.path_length = std::max(req.path_length, path_length);
+}
+
void TimingAnalyser::walk_forward()
{
// Assign initial arrival time to domain startpoints
@@ -322,6 +338,54 @@ void TimingAnalyser::walk_forward()
}
}
+void TimingAnalyser::walk_backward()
+{
+ // Assign initial required time to domain endpoints
+ // Note that clock frequency will be considered later in the analysis for, for now all required times are normalised
+ // to 0ns
+ for (domain_id_t dom_id = 0; dom_id < domain_id_t(domains.size()); ++dom_id) {
+ auto &dom = domains.at(dom_id);
+ for (auto &ep : dom.endpoints) {
+ auto &pd = ports.at(ep.first);
+ DelayPair init_setuphold(0);
+ CellPortKey clock_key;
+ // TODO: clock routing delay, if analysis of that is enabled
+ if (ep.second != IdString()) {
+ // Add setup/hold time, if this endpoint is clocked
+ for (auto &fanin : pd.cell_arcs) {
+ if (fanin.type == CellArc::SETUP && fanin.other_port == ep.second)
+ init_setuphold.min_delay -= fanin.value.maxDelay();
+ if (fanin.type == CellArc::HOLD && fanin.other_port == ep.second)
+ init_setuphold.max_delay -= fanin.value.maxDelay();
+ }
+ clock_key = CellPortKey(ep.first.cell, ep.second);
+ }
+ set_required_time(ep.first, dom_id, init_setuphold, 1, clock_key);
+ }
+ }
+ // Walk backwards in topological order
+ for (auto p : reversed_range(topological_order)) {
+ auto &pd = ports.at(p);
+ for (auto &req : pd.required) {
+ if (pd.type == PORT_IN) {
+ // Input port: propagate delay back through net, subtracting route delay
+ NetInfo *net = port_info(p).net;
+ if (net != nullptr && net->driver.cell != nullptr)
+ set_required_time(CellPortKey(net->driver), req.first, req.second.value - pd.route_delay,
+ req.second.path_length, p);
+ } else if (pd.type == PORT_OUT) {
+ // Output port : propagate delay back through cell, subtracting combinational delay
+ for (auto &fanin : pd.cell_arcs) {
+ if (fanin.type != CellArc::COMBINATIONAL)
+ continue;
+ set_required_time(CellPortKey(p.cell, fanin.other_port), req.first,
+ req.second.value - fanin.value.delayPair(), req.second.path_length + 1, p);
+ }
+ }
+ }
+ }
+}
+
domain_id_t TimingAnalyser::domain_id(IdString cell, IdString clock_port, ClockEdge edge)
{
return domain_id(ctx->cells.at(cell)->ports.at(clock_port).net, edge);