diff --git a/common/timing.cc b/common/timing.cc index f642153f..4daa19b0 100644 --- a/common/timing.cc +++ b/common/timing.cc @@ -35,6 +35,7 @@ void TimingAnalyser::setup() init_ports(); get_cell_delays(); topo_sort(); + setup_port_domains(); } void TimingAnalyser::init_ports() @@ -80,6 +81,8 @@ void TimingAnalyser::get_cell_delays() if (cls == TMG_REGISTER_INPUT) { for (int i = 0; i < clkInfoCount; i++) { auto info = ctx->getPortClockingInfo(ci, name, i); + if (!ci->ports.count(info.clock_port) || ci->ports.at(info.clock_port).net == nullptr) + continue; pd.cell_arcs.emplace_back(CellArc::SETUP, info.clock_port, DelayQuad(info.setup, info.setup), info.edge); pd.cell_arcs.emplace_back(CellArc::HOLD, info.clock_port, DelayQuad(info.hold, info.hold), @@ -102,6 +105,8 @@ void TimingAnalyser::get_cell_delays() if (cls == TMG_REGISTER_OUTPUT) { for (int i = 0; i < clkInfoCount; i++) { auto info = ctx->getPortClockingInfo(ci, name, i); + if (!ci->ports.count(info.clock_port) || ci->ports.at(info.clock_port).net == nullptr) + continue; pd.cell_arcs.emplace_back(CellArc::CLK_TO_Q, info.clock_port, info.clockToQ, info.edge); } } @@ -158,6 +163,89 @@ void TimingAnalyser::topo_sort() std::swap(topological_order, topo.sorted); } +void TimingAnalyser::setup_port_domains() +{ + for (auto &d : domains) { + d.startpoints.clear(); + d.endpoints.clear(); + } + // Go forward through the topological order (domains from the PoV of arrival time) + for (auto port : topological_order) { + auto &pd = ports.at(port); + auto &pi = port_info(port); + if (pi.type == PORT_OUT) { + for (auto &fanin : pd.cell_arcs) { + if (fanin.type != CellArc::CLK_TO_Q) + continue; + // registered outputs are startpoints + auto dom = domain_id(port.cell, fanin.other_port, fanin.edge); + // create per-domain data + pd.domains[dom].has_arrival = true; + domains.at(dom).startpoints.emplace_back(port, fanin.other_port); + } + // copy domains across routing + if (pi.net != nullptr) + for (auto &usr : pi.net->users) + copy_domains(port, CellPortKey(usr), false); + } else { + // copy domains from input to output + for (auto &fanout : pd.cell_arcs) { + if (fanout.type != CellArc::COMBINATIONAL) + continue; + copy_domains(port, CellPortKey(port.cell, fanout.other_port), false); + } + } + } + // Go backward through the topological order (domains from the PoV of required time) + for (auto port : reversed_range(topological_order)) { + auto &pd = ports.at(port); + auto &pi = port_info(port); + if (pi.type == PORT_OUT) { + // copy domains from output to input + for (auto &fanin : pd.cell_arcs) { + if (fanin.type != CellArc::COMBINATIONAL) + continue; + copy_domains(port, CellPortKey(port.cell, fanin.other_port), true); + } + } else { + for (auto &fanout : pd.cell_arcs) { + if (fanout.type != CellArc::SETUP) + continue; + // registered inputs are startpoints + auto dom = domain_id(port.cell, fanout.other_port, fanout.edge); + // create per-domain data + pd.domains[dom].has_required = true; + domains.at(dom).startpoints.emplace_back(port, fanout.other_port); + } + // copy port to driver + if (pi.net != nullptr && pi.net->driver.cell != nullptr) + copy_domains(port, CellPortKey(pi.net->driver), true); + } + } +} + +TimingAnalyser::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); +} +TimingAnalyser::domain_id_t TimingAnalyser::domain_id(const NetInfo *net, ClockEdge edge) +{ + NPNR_ASSERT(net != nullptr); + ClockDomainKey key{net->name, edge}; + auto inserted = domain_to_id.emplace(key, domain_to_id.size()); + if (inserted.second) { + domains.emplace_back(key); + } + return inserted.first->second; +} +void TimingAnalyser::copy_domains(const CellPortKey &from, const CellPortKey &to, bool backward) +{ + auto &f = ports.at(from), &t = ports.at(to); + for (auto &dom : f.domains) + if (backward ? dom.second.has_required : dom.second.has_arrival) + (backward ? t.domains[dom.first].has_required : t.domains[dom.first].has_arrival) = true; +} + CellInfo *TimingAnalyser::cell_info(const CellPortKey &key) { return ctx->cells.at(key.cell).get(); } PortInfo &TimingAnalyser::port_info(const CellPortKey &key) { return ctx->cells.at(key.cell)->ports.at(key.port); } diff --git a/common/timing.h b/common/timing.h index e07dc105..9aa15e59 100644 --- a/common/timing.h +++ b/common/timing.h @@ -84,6 +84,7 @@ struct ClockDomainKey { IdString clock; ClockEdge edge; + ClockDomainKey(IdString clock_net, ClockEdge edge) : clock(clock_net), edge(edge){}; // probably also need something here to deal with constraints inline bool is_async() const { return clock == IdString(); } @@ -109,6 +110,7 @@ struct TimingAnalyser void init_ports(); void get_cell_delays(); void topo_sort(); + void setup_port_domains(); // To avoid storing the domain tag structure (which could get large when considering more complex constrained tag // cases), assign each domain an ID and use that instead typedef int domain_id_t; @@ -123,6 +125,7 @@ struct TimingAnalyser // Data per port-domain tuple struct PortDomainData { + bool has_arrival = false, has_required = false; ArrivReqTime arrival, required; delay_t setup_slack = std::numeric_limits::max(), hold_slack = std::numeric_limits::max(); delay_t budget = std::numeric_limits::max(); @@ -167,12 +170,25 @@ struct TimingAnalyser DelayPair route_delay; }; + struct PerDomain + { + PerDomain(ClockDomainKey key) : key(key){}; + ClockDomainKey key; + // these are pairs (signal port; clock port) + std::vector> startpoints, endpoints; + }; + CellInfo *cell_info(const CellPortKey &key); PortInfo &port_info(const CellPortKey &key); + domain_id_t domain_id(IdString cell, IdString clock_port, ClockEdge edge); + domain_id_t domain_id(const NetInfo *net, ClockEdge edge); + + void copy_domains(const CellPortKey &from, const CellPortKey &to, bool backwards); + std::unordered_map ports; std::unordered_map domain_to_id; - std::vector id_to_domain; + std::vector domains; std::vector topological_order; diff --git a/common/util.h b/common/util.h index ce2b4da1..540646c7 100644 --- a/common/util.h +++ b/common/util.h @@ -263,6 +263,16 @@ template > struct TopoSort } }; +template struct reversed_range_t +{ + T &obj; + explicit reversed_range_t(T &obj) : obj(obj){}; + auto begin() { return obj.rbegin(); } + auto end() { return obj.rend(); } +}; + +template reversed_range_t reversed_range(T &obj) { return reversed_range_t(obj); } + NEXTPNR_NAMESPACE_END #endif