From 223e0a2d4e10323ce9c3fe14b62eb84ce9cf8382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 3 Nov 2025 12:48:44 +0100 Subject: [PATCH] timeest: Add top ports launching/sampling --- passes/cmds/timeest.cc | 55 +++++++++++++++++++++++++++++----------- tests/various/timeest.ys | 12 +++++++++ 2 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 tests/various/timeest.ys diff --git a/passes/cmds/timeest.cc b/passes/cmds/timeest.cc index ce9c628f4..c82bc901d 100644 --- a/passes/cmds/timeest.cc +++ b/passes/cmds/timeest.cc @@ -39,7 +39,8 @@ const arrivalint INF_PAST = std::numeric_limits::min(); struct EstimateSta { SigMap sigmap; Module *m; - SigBit clk; + std::optional clk; + bool top_port_endpoints = false; dict>, Aig> aigs; dict cell_aigs; @@ -73,15 +74,18 @@ struct EstimateSta { } // TODO: ignores clock polarity - EstimateSta(Module *m, SigBit clk) - : sigmap(m), m(m), clk(clk) + EstimateSta(Module *m, std::optional clk, bool top_port_endpoints) + : sigmap(m), m(m), clk(clk), top_port_endpoints(top_port_endpoints) { - sigmap.apply(clk); + if (clk.has_value()) + sigmap.apply(*clk); } void run() { - log("Domain %s\n", log_signal(clk)); + log("\nModule %s\n", log_id(m)); + if (clk.has_value()) + log("Domain %s\n", log_signal(*clk)); // first, we collect launch and sample points and convert the combinational logic to AIG std::vector combinational; @@ -151,6 +155,22 @@ struct EstimateSta { } } + // add top module port launching/sampling, if requested + if (top_port_endpoints) { + SigSpec all_inputs, all_outputs; + for (auto port_id : m->ports) { + Wire *port = m->wire(port_id); + if (port->port_input && !port->port_output) { + all_inputs.append(port); + } else if (port->port_output && !port->port_input) { + all_outputs.append(port); + } else if (port->port_output && port->port_input) { + log_warning("Ignoring bi-directional port %s\n", log_id(port)); + } + } + add_seq(nullptr, all_inputs, all_outputs); + } + // now we toposort the combinational logic // each toposort node is either a SigBit or a pair of Cell * / AigNode * @@ -360,7 +380,7 @@ struct TimeestPass : Pass { log("\n"); log(" timeest [-clk ] [options] [selection]\n"); log("\n"); - log("Estimate the critical path in clock domain by counting AIG nodes.\n"); + log("Estimate the critical path by counting AIG nodes.\n"); log("\n"); log(" -all_paths\n"); log(" Print or select nodes from all critical paths instead of focusing on\n"); @@ -374,7 +394,8 @@ struct TimeestPass : Pass { { log_header(d, "Executing TIMEEST pass. (estimate timing)\n"); - std::string clk; + std::string clk_name; + bool clk_domain_specified = false; bool all_paths = false; bool select = false; size_t argidx; @@ -388,26 +409,30 @@ struct TimeestPass : Pass { continue; } if (args[argidx] == "-clk" && argidx + 1 < args.size()) { - clk = args[++argidx]; + clk_domain_specified = true; + clk_name = args[++argidx]; continue; } break; } extra_args(args, argidx, d); - if (clk.empty()) - log_cmd_error("No -clk argument provided\n"); - if (select && d->selected_modules().size() > 1) log_cmd_error("The -select option operates on a single selected module\n"); for (auto m : d->selected_modules()) { - if (!m->wire(RTLIL::escape_id(clk))) { - log_warning("No domain '%s' in module %s\n", clk, log_id(m)); - continue; + std::optional clk; + + if (clk_domain_specified) { + if (!m->wire(RTLIL::escape_id(clk_name))) { + log_warning("No domain '%s' in module %s\n", clk, log_id(m)); + continue; + } + + clk = SigBit(m->wire(RTLIL::escape_id(clk_name)), 0); } - EstimateSta sta(m, SigBit(m->wire(RTLIL::escape_id(clk)), 0)); + EstimateSta sta(m, clk, /*top_port_endpoints=*/ !clk_domain_specified); sta.all_paths = all_paths; sta.select = select; sta.run(); diff --git a/tests/various/timeest.ys b/tests/various/timeest.ys new file mode 100644 index 000000000..3859de5e1 --- /dev/null +++ b/tests/various/timeest.ys @@ -0,0 +1,12 @@ +read_verilog <