diff options
author | gatecat <gatecat@ds0.me> | 2021-03-02 10:39:26 +0000 |
---|---|---|
committer | gatecat <gatecat@ds0.me> | 2021-03-04 10:29:36 +0000 |
commit | 16e7bba87b8944914abf5a5d18561393f8a5c39b (patch) | |
tree | 7f2e14d0f22bb89a95cfb937f89b6d1b40d8c178 | |
parent | 0528ceead1276171fff4ecaa3aafe6b0e9c8ffa5 (diff) | |
download | nextpnr-16e7bba87b8944914abf5a5d18561393f8a5c39b.tar.gz nextpnr-16e7bba87b8944914abf5a5d18561393f8a5c39b.tar.bz2 nextpnr-16e7bba87b8944914abf5a5d18561393f8a5c39b.zip |
timing: Add backwards path walking
Signed-off-by: gatecat <gatecat@ds0.me>
-rw-r--r-- | common/nextpnr.h | 5 | ||||
-rw-r--r-- | common/timing.cc | 66 | ||||
-rw-r--r-- | common/timing.h | 1 |
3 files changed, 71 insertions, 1 deletions
diff --git a/common/nextpnr.h b/common/nextpnr.h index ab2f8dca..404900c4 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -552,6 +552,10 @@ struct DelayPair { return {min_delay + other.min_delay, max_delay + other.max_delay}; } + DelayPair operator-(const DelayPair &other) const + { + return {min_delay - other.min_delay, max_delay - other.max_delay}; + } }; // four-quadrant, min and max rise and fall delay @@ -575,6 +579,7 @@ struct DelayQuad DelayPair delayPair() const { return DelayPair(minDelay(), maxDelay()); }; DelayQuad operator+(const DelayQuad &other) const { return {rise + other.rise, fall + other.fall}; } + DelayQuad operator-(const DelayQuad &other) const { return {rise - other.rise, fall - other.fall}; } }; struct ClockConstraint; 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); diff --git a/common/timing.h b/common/timing.h index afc39aee..b050e671 100644 --- a/common/timing.h +++ b/common/timing.h @@ -138,6 +138,7 @@ struct TimingAnalyser void reset_times(); void walk_forward(); + void walk_backward(); const DelayPair init_delay{std::numeric_limits<delay_t>::max(), std::numeric_limits<delay_t>::lowest()}; |