timing: Fix domain init when loops are present

Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
gatecat 2021-04-13 09:23:08 +01:00
parent 5cd2a7f9c2
commit ece10c3e04
2 changed files with 73 additions and 58 deletions

View File

@ -185,6 +185,7 @@ void TimingAnalyser::topo_sort()
} }
} }
} }
have_loops = !no_loops;
std::swap(topological_order, topo.sorted); std::swap(topological_order, topo.sorted);
} }
@ -195,66 +196,77 @@ void TimingAnalyser::setup_port_domains()
d.endpoints.clear(); d.endpoints.clear();
} }
// Go forward through the topological order (domains from the PoV of arrival time) // Go forward through the topological order (domains from the PoV of arrival time)
for (auto port : topological_order) { bool first_iter = true;
auto &pd = ports.at(port); do {
auto &pi = port_info(port); updated_domains = false;
if (pi.type == PORT_OUT) { for (auto port : topological_order) {
for (auto &fanin : pd.cell_arcs) { auto &pd = ports.at(port);
if (fanin.type != CellArc::CLK_TO_Q) auto &pi = port_info(port);
continue; if (pi.type == PORT_OUT) {
// registered outputs are startpoints if (first_iter) {
auto dom = domain_id(port.cell, fanin.other_port, fanin.edge); for (auto &fanin : pd.cell_arcs) {
// create per-domain data if (fanin.type != CellArc::CLK_TO_Q)
pd.arrival[dom]; continue;
domains.at(dom).startpoints.emplace_back(port, fanin.other_port); // registered outputs are startpoints
} auto dom = domain_id(port.cell, fanin.other_port, fanin.edge);
// copy domains across routing // create per-domain data
if (pi.net != nullptr) pd.arrival[dom];
for (auto &usr : pi.net->users) domains.at(dom).startpoints.emplace_back(port, fanin.other_port);
copy_domains(port, CellPortKey(usr), false); }
} else { }
// copy domains from input to output // copy domains across routing
for (auto &fanout : pd.cell_arcs) { if (pi.net != nullptr)
if (fanout.type != CellArc::COMBINATIONAL) for (auto &usr : pi.net->users)
continue; copy_domains(port, CellPortKey(usr), false);
copy_domains(port, CellPortKey(port.cell, fanout.other_port), 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)
// Go backward through the topological order (domains from the PoV of required time) for (auto port : reversed_range(topological_order)) {
for (auto port : reversed_range(topological_order)) { auto &pd = ports.at(port);
auto &pd = ports.at(port); auto &pi = port_info(port);
auto &pi = port_info(port); if (pi.type == PORT_OUT) {
if (pi.type == PORT_OUT) { // copy domains from output to input
// copy domains from output to input for (auto &fanin : pd.cell_arcs) {
for (auto &fanin : pd.cell_arcs) { if (fanin.type != CellArc::COMBINATIONAL)
if (fanin.type != CellArc::COMBINATIONAL) continue;
continue; copy_domains(port, CellPortKey(port.cell, fanin.other_port), true);
copy_domains(port, CellPortKey(port.cell, fanin.other_port), true); }
} else {
if (first_iter) {
for (auto &fanout : pd.cell_arcs) {
if (fanout.type != CellArc::SETUP)
continue;
// registered inputs are endpoints
auto dom = domain_id(port.cell, fanout.other_port, fanout.edge);
// create per-domain data
pd.required[dom];
domains.at(dom).endpoints.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);
} }
} 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.required[dom];
domains.at(dom).endpoints.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);
} }
} // Iterate over ports and find domain paris
// Iterate over ports and find domain paris for (auto port : topological_order) {
for (auto port : topological_order) { auto &pd = ports.at(port);
auto &pd = ports.at(port); for (auto &arr : pd.arrival)
for (auto &arr : pd.arrival) for (auto &req : pd.required) {
for (auto &req : pd.required) { pd.domain_pairs[domain_pair_id(arr.first, req.first)];
pd.domain_pairs[domain_pair_id(arr.first, req.first)]; }
} }
} first_iter = false;
// If there are loops, repeat the process until a fixed point is reached, as there might be unusual ways to
// visit points, which would result in a missing domain key and therefore crash later on
} while (have_loops && updated_domains);
} }
void TimingAnalyser::reset_times() void TimingAnalyser::reset_times()
@ -561,8 +573,9 @@ domain_id_t TimingAnalyser::domain_pair_id(domain_id_t launch, domain_id_t captu
void TimingAnalyser::copy_domains(const CellPortKey &from, const CellPortKey &to, bool backward) void TimingAnalyser::copy_domains(const CellPortKey &from, const CellPortKey &to, bool backward)
{ {
auto &f = ports.at(from), &t = ports.at(to); auto &f = ports.at(from), &t = ports.at(to);
for (auto &dom : (backward ? f.required : f.arrival)) for (auto &dom : (backward ? f.required : f.arrival)) {
(backward ? t.required : t.arrival)[dom.first]; updated_domains |= (backward ? t.required : t.arrival).emplace(dom.first, ArrivReqTime{}).second;
}
} }
CellInfo *TimingAnalyser::cell_info(const CellPortKey &key) { return ctx->cells.at(key.cell).get(); } CellInfo *TimingAnalyser::cell_info(const CellPortKey &key) { return ctx->cells.at(key.cell).get(); }

View File

@ -142,6 +142,8 @@ struct TimingAnalyser
bool setup_only = false; bool setup_only = false;
bool verbose_mode = false; bool verbose_mode = false;
bool have_loops = false;
bool updated_domains = false;
private: private:
void init_ports(); void init_ports();