Analyse async paths in TimingAnalyser (#1171)

This commit is contained in:
Rowan Goemans 2023-06-09 08:01:47 +02:00 committed by GitHub
parent 119b47acf3
commit 5b958c4d80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 33 deletions

View File

@ -30,9 +30,24 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
namespace { namespace {
const char *edge_name(ClockEdge edge) { return (edge == FALLING_EDGE) ? "negedge" : "posedge"; } std::string clock_event_name(const Context *ctx, ClockDomainKey &dom) {
std::string value;
if (dom.is_async())
value = "<async>";
else
value = (dom.edge == FALLING_EDGE ? "negedge " : "posedge ") + dom.clock.str(ctx);
return value;
}
} // namespace } // namespace
TimingAnalyser::TimingAnalyser(Context *ctx) : ctx(ctx) {
ClockDomainKey key{IdString(), ClockEdge::RISING_EDGE};
domain_to_id.emplace(key, 0);
domains.emplace_back(key);
async_clock_id = 0;
};
void TimingAnalyser::setup() void TimingAnalyser::setup()
{ {
init_ports(); init_ports();
@ -69,6 +84,8 @@ void TimingAnalyser::init_ports()
void TimingAnalyser::get_cell_delays() void TimingAnalyser::get_cell_delays()
{ {
auto async_clk_key = domains.at(async_clock_id);
for (auto &port : ports) { for (auto &port : ports) {
CellInfo *ci = cell_info(port.first); CellInfo *ci = cell_info(port.first);
auto &pi = port_info(port.first); auto &pi = port_info(port.first);
@ -81,8 +98,7 @@ void TimingAnalyser::get_cell_delays()
pd.cell_arcs.clear(); pd.cell_arcs.clear();
int clkInfoCount = 0; int clkInfoCount = 0;
TimingPortClass cls = ctx->getPortTimingClass(ci, name, clkInfoCount); TimingPortClass cls = ctx->getPortTimingClass(ci, name, clkInfoCount);
if (cls == TMG_STARTPOINT || cls == TMG_ENDPOINT || cls == TMG_CLOCK_INPUT || cls == TMG_GEN_CLOCK || if (cls == TMG_CLOCK_INPUT || cls == TMG_GEN_CLOCK || cls == TMG_IGNORE)
cls == TMG_IGNORE)
continue; continue;
if (pi.type == PORT_IN) { if (pi.type == PORT_IN) {
// Input ports might have setup/hold relationships // Input ports might have setup/hold relationships
@ -98,6 +114,7 @@ void TimingAnalyser::get_cell_delays()
} }
} }
// Combinational delays through cell // Combinational delays through cell
else if (cls == TMG_COMB_INPUT) {
for (auto &other_port : ci->ports) { for (auto &other_port : ci->ports) {
auto &op = other_port.second; auto &op = other_port.second;
// ignore dangling ports and non-outputs // ignore dangling ports and non-outputs
@ -108,6 +125,11 @@ void TimingAnalyser::get_cell_delays()
if (is_path) if (is_path)
pd.cell_arcs.emplace_back(CellArc::COMBINATIONAL, other_port.first, delay); pd.cell_arcs.emplace_back(CellArc::COMBINATIONAL, other_port.first, delay);
} }
}
// asynchronous endpoint
else if (cls == TMG_ENDPOINT) {
pd.cell_arcs.emplace_back(CellArc::ENDPOINT, async_clk_key.key.clock, DelayQuad {});
}
} else if (pi.type == PORT_OUT) { } else if (pi.type == PORT_OUT) {
// Output ports might have clk-to-q relationships // Output ports might have clk-to-q relationships
if (cls == TMG_REGISTER_OUTPUT) { if (cls == TMG_REGISTER_OUTPUT) {
@ -119,6 +141,7 @@ void TimingAnalyser::get_cell_delays()
} }
} }
// Combinational delays through cell // Combinational delays through cell
else if (cls == TMG_COMB_OUTPUT) {
for (auto &other_port : ci->ports) { for (auto &other_port : ci->ports) {
auto &op = other_port.second; auto &op = other_port.second;
// ignore dangling ports and non-inputs // ignore dangling ports and non-inputs
@ -130,6 +153,11 @@ void TimingAnalyser::get_cell_delays()
pd.cell_arcs.emplace_back(CellArc::COMBINATIONAL, other_port.first, delay); pd.cell_arcs.emplace_back(CellArc::COMBINATIONAL, other_port.first, delay);
} }
} }
// Asynchronous startpoint
else if (cls == TMG_STARTPOINT) {
pd.cell_arcs.emplace_back(CellArc::STARTPOINT, async_clk_key.key.clock, DelayQuad {});
}
}
} }
} }
@ -194,9 +222,9 @@ void TimingAnalyser::setup_port_domains()
d.startpoints.clear(); d.startpoints.clear();
d.endpoints.clear(); d.endpoints.clear();
} }
// Go forward through the topological order (domains from the PoV of arrival time)
bool first_iter = true; bool first_iter = true;
do { do {
// Go forward through the topological order (domains from the PoV of arrival time)
updated_domains = false; updated_domains = false;
for (auto port : topological_order) { for (auto port : topological_order) {
auto &pd = ports.at(port); auto &pd = ports.at(port);
@ -204,10 +232,14 @@ void TimingAnalyser::setup_port_domains()
if (pi.type == PORT_OUT) { if (pi.type == PORT_OUT) {
if (first_iter) { if (first_iter) {
for (auto &fanin : pd.cell_arcs) { for (auto &fanin : pd.cell_arcs) {
if (fanin.type != CellArc::CLK_TO_Q) domain_id_t dom;
continue;
// registered outputs are startpoints // registered outputs are startpoints
auto dom = domain_id(port.cell, fanin.other_port, fanin.edge); if (fanin.type == CellArc::CLK_TO_Q)
dom = domain_id(port.cell, fanin.other_port, fanin.edge);
else if (fanin.type == CellArc::STARTPOINT)
dom = async_clock_id;
else
continue;
// create per-domain data // create per-domain data
pd.arrival[dom]; pd.arrival[dom];
domains.at(dom).startpoints.emplace_back(port, fanin.other_port); domains.at(dom).startpoints.emplace_back(port, fanin.other_port);
@ -240,10 +272,14 @@ void TimingAnalyser::setup_port_domains()
} else { } else {
if (first_iter) { if (first_iter) {
for (auto &fanout : pd.cell_arcs) { for (auto &fanout : pd.cell_arcs) {
if (fanout.type != CellArc::SETUP) domain_id_t dom;
continue;
// registered inputs are endpoints // registered inputs are endpoints
auto dom = domain_id(port.cell, fanout.other_port, fanout.edge); if (fanout.type == CellArc::SETUP)
dom = domain_id(port.cell, fanout.other_port, fanout.edge);
else if (fanout.type == CellArc::ENDPOINT)
dom = async_clock_id;
else
continue;
// create per-domain data // create per-domain data
pd.required[dom]; pd.required[dom];
domains.at(dom).endpoints.emplace_back(port, fanout.other_port); domains.at(dom).endpoints.emplace_back(port, fanout.other_port);
@ -254,7 +290,7 @@ void TimingAnalyser::setup_port_domains()
copy_domains(port, CellPortKey(pi.net->driver), true); copy_domains(port, CellPortKey(pi.net->driver), true);
} }
} }
// Iterate over ports and find domain paris // Iterate over ports and find domain pairs
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)
@ -370,6 +406,8 @@ void TimingAnalyser::identify_related_domains()
// 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) {
if (domain.key.is_async())
continue;
const NetInfo *ni = ctx->nets.at(domain.key.clock).get(); const NetInfo *ni = ctx->nets.at(domain.key.clock).get();
if (ni == nullptr) if (ni == nullptr)
@ -613,6 +651,8 @@ void TimingAnalyser::print_fmax()
auto &pd = ports.at(p); auto &pd = ports.at(p);
for (auto &req : pd.required) { for (auto &req : pd.required) {
if (pd.arrival.count(req.first)) { if (pd.arrival.count(req.first)) {
if (domains.at(req.first).key.is_async())
continue;
auto &arr = pd.arrival.at(req.first); auto &arr = pd.arrival.at(req.first);
double fmax = 1000.0 / ctx->getDelayNS(arr.value.maxDelay() - req.second.value.minDelay()); double fmax = 1000.0 / ctx->getDelayNS(arr.value.maxDelay() - req.second.value.minDelay());
if (!domain_fmax.count(req.first) || domain_fmax.at(req.first) > fmax) if (!domain_fmax.count(req.first) || domain_fmax.at(req.first) > fmax)
@ -670,6 +710,10 @@ void TimingAnalyser::compute_criticality()
auto &pd = ports.at(p); auto &pd = ports.at(p);
for (auto &pdp : pd.domain_pairs) { for (auto &pdp : pd.domain_pairs) {
auto &dp = domain_pairs.at(pdp.first); auto &dp = domain_pairs.at(pdp.first);
// Do not set criticality for asynchronous paths
if (domains.at(dp.key.launch).key.is_async() || domains.at(dp.key.capture).key.is_async())
continue;
float crit = float crit =
1.0f - (float(pdp.second.setup_slack) - float(dp.worst_setup_slack)) / float(-dp.worst_setup_slack); 1.0f - (float(pdp.second.setup_slack) - float(dp.worst_setup_slack)) / float(-dp.worst_setup_slack);
crit = std::min(crit, 1.0f); crit = std::min(crit, 1.0f);
@ -728,8 +772,9 @@ void TimingAnalyser::print_report()
auto &dp = domain_pairs.at(i); auto &dp = domain_pairs.at(i);
auto &launch = domains.at(dp.key.launch); auto &launch = domains.at(dp.key.launch);
auto &capture = domains.at(dp.key.capture); auto &capture = domains.at(dp.key.capture);
log("Worst endpoints for %s %s -> %s %s\n", edge_name(launch.key.edge), ctx->nameOf(launch.key.clock),
edge_name(capture.key.edge), ctx->nameOf(capture.key.clock)); log("Worst endpoints for %s -> %s\n", clock_event_name(ctx, launch.key).c_str(),
clock_event_name(ctx, capture.key).c_str());
auto failing_eps = get_failing_eps(i, 5); auto failing_eps = get_failing_eps(i, 5);
for (auto &ep : failing_eps) for (auto &ep : failing_eps)
print_critical_path(ep, i); print_critical_path(ep, i);

View File

@ -73,7 +73,7 @@ struct ClockDomainPairKey
struct TimingAnalyser struct TimingAnalyser
{ {
public: public:
TimingAnalyser(Context *ctx) : ctx(ctx){}; TimingAnalyser(Context *ctx);
void setup(); void setup();
void run(bool update_route_delays = true); void run(bool update_route_delays = true);
void print_report(); void print_report();
@ -157,7 +157,9 @@ struct TimingAnalyser
COMBINATIONAL, COMBINATIONAL,
SETUP, SETUP,
HOLD, HOLD,
CLK_TO_Q CLK_TO_Q,
STARTPOINT,
ENDPOINT,
} type; } type;
IdString other_port; IdString other_port;
@ -224,6 +226,8 @@ struct TimingAnalyser
std::vector<CellPortKey> topological_order; std::vector<CellPortKey> topological_order;
domain_id_t async_clock_id;
Context *ctx; Context *ctx;
}; };