From 5db1765bee29f7f55fadba52ee86cf8f59583b3a Mon Sep 17 00:00:00 2001 From: "N. Engelhardt" Date: Thu, 17 Apr 2025 18:17:42 +0200 Subject: [PATCH 1/8] add flatten -nocleanup option --- passes/techmap/flatten.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/passes/techmap/flatten.cc b/passes/techmap/flatten.cc index 3425509b1..6363b3432 100644 --- a/passes/techmap/flatten.cc +++ b/passes/techmap/flatten.cc @@ -349,6 +349,10 @@ struct FlattenPass : public Pass { log(" -separator \n"); log(" Use this separator char instead of '.' when concatenating design levels.\n"); log("\n"); + log(" -nocleanup\n"); + log(" Don't remove unused submodules, leave a flattened version of each\n"); + log(" submodule in the design.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { @@ -360,6 +364,8 @@ struct FlattenPass : public Pass { if (design->scratchpad.count("flatten.separator")) worker.separator = design->scratchpad_get_string("flatten.separator"); + bool cleanup = true; + size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-wb") { @@ -378,6 +384,10 @@ struct FlattenPass : public Pass { worker.separator = args[++argidx]; continue; } + if (args[argidx] == "-nocleanup") { + cleanup = false; + continue; + } break; } extra_args(args, argidx, design); @@ -414,7 +424,7 @@ struct FlattenPass : public Pass { for (auto module : topo_modules.sorted) worker.flatten_module(design, module, used_modules, worker.separator); - if (top != nullptr) + if (cleanup && top != nullptr) for (auto module : design->modules().to_vector()) if (!used_modules[module] && !module->get_blackbox_attribute(worker.ignore_wb)) { log("Deleting now unused module %s.\n", log_id(module)); From 386b33d192902210144b8a5a7c4cf9fe543c7458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 31 May 2024 18:51:18 +0200 Subject: [PATCH 2/8] timeest: Add command for critical path estimation --- passes/cmds/Makefile.inc | 1 + passes/cmds/timeest.cc | 361 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 362 insertions(+) create mode 100644 passes/cmds/timeest.cc diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index af7e1bca6..4ecaea7dd 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -55,3 +55,4 @@ OBJS += passes/cmds/wrapcell.o OBJS += passes/cmds/setenv.o OBJS += passes/cmds/abstract.o OBJS += passes/cmds/test_select.o +OBJS += passes/cmds/timeest.o diff --git a/passes/cmds/timeest.cc b/passes/cmds/timeest.cc new file mode 100644 index 000000000..1a54360cc --- /dev/null +++ b/passes/cmds/timeest.cc @@ -0,0 +1,361 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Martin PoviĊĦer + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/sigtools.h" +#include "kernel/register.h" +#include "kernel/cellaigs.h" +#include "kernel/utils.h" +#include "kernel/ff.h" +#include "kernel/mem.h" + +#include + +USING_YOSYS_NAMESPACE +template<> struct hash_ops : hash_ptr_ops {}; + +PRIVATE_NAMESPACE_BEGIN + +typedef long int arrivalint; +const arrivalint INF_PAST = std::numeric_limits::min(); + +// each clock domain must have its own EstimateSta structure +struct EstimateSta { + SigMap sigmap; + Module *m; + SigBit clk; + + dict>, Aig> aigs; + dict cell_aigs; + + std::vector> launchers; + std::vector> samplers; + bool all_paths = false; + + void add_seq(Cell *cell, SigSpec launch, SigSpec sample) + { + sigmap.apply(launch); + sigmap.apply(sample); + launch.sort_and_unify(); + sample.sort_and_unify(); + for (auto bit : launch) + launchers.push_back(std::make_pair(cell, bit)); + for (auto bit : sample) + samplers.push_back(std::make_pair(cell, bit)); + } + + int cell_type_factor(IdString type) + { + if (type.in(ID($gt), ID($ge), ID($lt), ID($le), ID($add), ID($sub), + ID($logic_not), ID($reduce_and), ID($reduce_or), ID($eq))) + return 1; + else + return 2; + } + + // TODO: ignores clock polarity + EstimateSta(Module *m, SigBit clk) + : sigmap(m), m(m), clk(clk) + { + sigmap.apply(clk); + } + + void run() + { + log("Domain %s\n", log_signal(clk)); + + std::vector combinational; + + for (auto cell : m->cells()) { + SigSpec launch, sample; + if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + FfData ff(nullptr, cell); + if (!ff.has_clk) { + log_warning("Ignoring unsupported storage element '%s' (%s)\n", + log_id(cell), log_id(cell->type)); + continue; + } + if (ff.sig_clk != clk) + continue; + launch.append(ff.sig_q); + sample.append(ff.sig_d); + if (ff.has_ce) + sample.append(ff.sig_ce); + if (ff.has_srst) + sample.append(ff.sig_srst); + add_seq(cell, launch, sample); + } else if (cell->is_mem_cell()) { + // memories handled separately + continue; + } else if (cell->type == ID($scopeinfo)) { + continue; + } else { + auto fingerprint = std::make_pair(cell->type, cell->parameters); + if (!aigs.count(fingerprint)) { + aigs.emplace(fingerprint, Aig(cell)); + if (aigs.at(fingerprint).name.empty()) { + log_error("Unsupported cell '%s' in module '%s'", + log_id(cell->type), log_id(m)); + } + } + + combinational.push_back(cell); + continue; + } + } + + for (auto cell : combinational) { + auto fingerprint = std::make_pair(cell->type, cell->parameters); + cell_aigs.emplace(cell, &aigs.at(fingerprint)); + } + + for (auto &mem : Mem::get_all_memories(m)) { + for (auto &rd : mem.rd_ports) { + if (!rd.clk_enable) { + log_error("Unsupported async memory port '%s'\n", log_id(rd.cell)); + continue; + } + if (sigmap(rd.clk) != clk) + continue; + add_seq(rd.cell, rd.data, {rd.addr, rd.srst, rd.en}); + } + for (auto &wr : mem.wr_ports) { + if (sigmap(wr.clk) != clk) + continue; + add_seq(wr.cell, {}, {wr.en, wr.addr, wr.data}); + } + } + + TopoSort> topo; + + auto desc_aig = [&](Cell *cell, AigNode &node) { + return std::make_tuple(RTLIL::S0, cell, &node); + }; + auto desc_sig = [&](SigBit bit) { + return std::make_tuple(sigmap(bit), (Cell *) NULL, (AigNode *) NULL); + }; + + for (auto cell : combinational) { + assert(cell_aigs.count(cell)); + Aig &aig = *cell_aigs.at(cell); + for (auto &node : aig.nodes) { + if (!node.portname.empty()) { + topo.edge( + desc_sig(cell->getPort(node.portname)[node.portbit]), + desc_aig(cell, node) + ); + } else if (node.left_parent < 0 && node.right_parent < 0) { + // constant, nothing to do + } else { + topo.edge( + desc_aig(cell, aig.nodes[node.left_parent]), + desc_aig(cell, node) + ); + topo.edge( + desc_aig(cell, aig.nodes[node.right_parent]), + desc_aig(cell, node) + ); + } + + for (auto &oport : node.outports) { + topo.edge( + desc_aig(cell, node), + desc_sig(cell->getPort(oport.first)[oport.second]) + ); + } + } + } + + if (!topo.sort()) + log_error("Module '%s' contains combinational loops", log_id(m)); + + dict, arrivalint> levels; + + for (auto node : topo.sorted) + levels[node] = INF_PAST; + + for (auto pair : launchers) + levels[desc_sig(pair.second)] = 0; + + for (auto node : topo.sorted) { + AigNode *aig_node = std::get<2>(node); + if (aig_node) { + Cell *cell = std::get<1>(node); + Aig &aig = *cell_aigs.at(cell); + if (!aig_node->portname.empty()) { + SigBit bit = cell->getPort(aig_node->portname)[aig_node->portbit]; + levels[node] = levels[desc_sig(bit)]; + } else if (aig_node->left_parent < 0 && aig_node->right_parent < 0) { + // constant, nothing to do + } else { + int left = levels[desc_aig(cell, aig.nodes[aig_node->left_parent])]; + int right = levels[desc_aig(cell, aig.nodes[aig_node->right_parent])]; + levels[node] = (std::max(left, right) + cell_type_factor(cell->type)); + } + + for (auto &oport : aig_node->outports) { + levels[desc_sig(cell->getPort(oport.first)[oport.second])] = levels[node]; + } + } + } + + arrivalint crit = INF_PAST; + for (auto pair : samplers) + if (levels[desc_sig(pair.second)] > crit) + crit = levels[desc_sig(pair.second)]; + + if (crit < 0) { + log("No paths found\n"); + return; + } + + log("Critical path is %ld nodes long:\n\n", crit); + + // we use dict instead of pool because dict gives us + // some compile-time errors related to hashing + dict, bool> critical; + + for (auto pair : samplers) { + if (levels[desc_sig(pair.second)] == crit) { + critical[desc_sig(pair.second)] = true; + if (!all_paths) + break; + } + } + + for (auto it = topo.sorted.rbegin(); it != topo.sorted.rend(); it++) { + auto node = *it; + AigNode *aig_node = std::get<2>(node); + if (aig_node) { + Cell *cell = std::get<1>(node); + Aig &aig = *cell_aigs.at(cell); + + for (auto &oport : aig_node->outports) { + //levels[desc_sig(cell->getPort(oport.first)[oport.second])] = levels[node]; + if (critical.count(desc_sig(cell->getPort(oport.first)[oport.second]))) + critical[node] = true; + } + + if (!aig_node->portname.empty()) { + SigBit bit = cell->getPort(aig_node->portname)[aig_node->portbit]; + //levels[node] = levels[desc_sig(bit)]; + if (critical.count(node)) + critical[desc_sig(bit)] = true; + } else if (aig_node->left_parent < 0 && aig_node->right_parent < 0) { + // constant, nothing to do + } else { + auto left = desc_aig(cell, aig.nodes[aig_node->left_parent]); + auto right = desc_aig(cell, aig.nodes[aig_node->right_parent]); + //levels[node] = (std::max(left, right) + 1); + int crit_input_lvl = levels[node] - cell_type_factor(cell->type); + if (critical.count(node)) { + bool left_critical = (levels[left] == crit_input_lvl); + bool right_critical = (levels[right] == crit_input_lvl); + if (all_paths) { + if (left_critical) + critical[left] = true; + if (right_critical) + critical[right] = true; + } else { + if (left_critical) + critical[left] = true; + else if (right_critical) + critical[right] = true; + } + } + } + } + } + + pool printed; + for (auto node : topo.sorted) { + if (!critical.count(node)) + continue; + AigNode *aig_node = std::get<2>(node); + if (aig_node) { + Cell *cell = std::get<1>(node); + if (!printed.count(cell)) { + std::string cell_src; + if (cell->has_attribute(ID::src)) { + std::string src_attr = cell->get_src_attribute(); + cell_src = stringf(" source: %s", src_attr.c_str()); + } + log(" cell %s (%s)%s\n", log_id(cell), log_id(cell->type), cell_src.c_str()); + printed.insert(cell); + } + } else { + SigBit bit = std::get<0>(node); + std::string wire_src; + if (bit.wire && bit.wire->has_attribute(ID::src)) { + std::string src_attr = bit.wire->get_src_attribute(); + wire_src = stringf(" source: %s", src_attr.c_str()); + } + log(" wire %s%s (level %ld)\n", log_signal(bit), wire_src.c_str(), levels[node]); + } + } + } +}; + +struct TimeestPass : Pass { + TimeestPass() : Pass("timeest", "estimate timing") {} + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" timeest [-clk ] [selection]\n"); + log("\n"); + log("Estimate the critical path in clock domain by counting AIG nodes.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *d) override + { + log_header(d, "Executing TIMEEST pass. (estimate timing)\n"); + + std::string clk; + bool all_paths = false; + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-all_paths") { + all_paths = true; + continue; + } + if (args[argidx] == "-clk" && argidx + 1 < args.size()) { + clk = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, d); + + if (clk.empty()) + log_cmd_error("No -clk argument provided\n"); + + for (auto m : d->selected_modules()) { + if (!m->wire(RTLIL::escape_id(clk))) { + log_warning("No domain '%s' in module %s\n", clk.c_str(), log_id(m)); + continue; + } + + EstimateSta sta(m, SigBit(m->wire(RTLIL::escape_id(clk)), 0)); + sta.all_paths = all_paths; + sta.run(); + } + } +} TimeestPass; + +PRIVATE_NAMESPACE_END From 4323d56b9e286f2b0cf4c7742f0ed27c8513af2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 31 May 2024 18:55:29 +0200 Subject: [PATCH 3/8] timeest: Fill missing header --- passes/cmds/timeest.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/passes/cmds/timeest.cc b/passes/cmds/timeest.cc index 1a54360cc..dec7f5237 100644 --- a/passes/cmds/timeest.cc +++ b/passes/cmds/timeest.cc @@ -25,6 +25,7 @@ #include "kernel/mem.h" #include +#include USING_YOSYS_NAMESPACE template<> struct hash_ops : hash_ptr_ops {}; From c5e154e941094924846e1ed53a4861c5419f6f60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 31 May 2024 19:07:10 +0200 Subject: [PATCH 4/8] timeest: Fix templating --- passes/cmds/timeest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/timeest.cc b/passes/cmds/timeest.cc index dec7f5237..e815e32f7 100644 --- a/passes/cmds/timeest.cc +++ b/passes/cmds/timeest.cc @@ -28,7 +28,7 @@ #include USING_YOSYS_NAMESPACE -template<> struct hash_ops : hash_ptr_ops {}; +template<> struct ::Yosys::hashlib::hash_ops : Yosys::hashlib::hash_ptr_ops {}; PRIVATE_NAMESPACE_BEGIN From e8196b1dda5b454d1c117fbfd98ececaf2c22f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 22 Aug 2024 10:21:10 +0200 Subject: [PATCH 5/8] timeest: Update help --- passes/cmds/timeest.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/passes/cmds/timeest.cc b/passes/cmds/timeest.cc index e815e32f7..82867d84c 100644 --- a/passes/cmds/timeest.cc +++ b/passes/cmds/timeest.cc @@ -318,10 +318,14 @@ struct TimeestPass : Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" timeest [-clk ] [selection]\n"); + log(" timeest [-clk ] [options] [selection]\n"); log("\n"); log("Estimate the critical path in clock domain 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"); + log(" a single illustratory path.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *d) override { From 28c7f202caa22041f3a0fe002b089e42a264facd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 22 Aug 2024 10:48:42 +0200 Subject: [PATCH 6/8] timeest: Add `-select` --- passes/cmds/timeest.cc | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/passes/cmds/timeest.cc b/passes/cmds/timeest.cc index 82867d84c..020e4347c 100644 --- a/passes/cmds/timeest.cc +++ b/passes/cmds/timeest.cc @@ -47,6 +47,7 @@ struct EstimateSta { std::vector> launchers; std::vector> samplers; bool all_paths = false; + bool select = false; void add_seq(Cell *cell, SigSpec launch, SigSpec sample) { @@ -283,6 +284,9 @@ struct EstimateSta { } } + SigPool bits_to_select; + pool to_select; + pool printed; for (auto node : topo.sorted) { if (!critical.count(node)) @@ -291,6 +295,7 @@ struct EstimateSta { if (aig_node) { Cell *cell = std::get<1>(node); if (!printed.count(cell)) { + to_select.insert(cell->name); std::string cell_src; if (cell->has_attribute(ID::src)) { std::string src_attr = cell->get_src_attribute(); @@ -301,6 +306,7 @@ struct EstimateSta { } } else { SigBit bit = std::get<0>(node); + bits_to_select.add(bit); std::string wire_src; if (bit.wire && bit.wire->has_attribute(ID::src)) { std::string src_attr = bit.wire->get_src_attribute(); @@ -309,6 +315,19 @@ struct EstimateSta { log(" wire %s%s (level %ld)\n", log_signal(bit), wire_src.c_str(), levels[node]); } } + + for (auto wire : m->wires()) { + if (bits_to_select.check_any(sigmap(wire))) + to_select.insert(wire->name); + } + + if (select) { + RTLIL::Selection sel(false); + for (auto member : to_select) + sel.selected_members[m->name].insert(member); + m->design->selection_stack.back() = sel; + m->design->selection_stack.back().optimize(m->design); + } } }; @@ -326,6 +345,9 @@ struct TimeestPass : Pass { log(" Print or select nodes from all critical paths instead of focusing on\n"); log(" a single illustratory path.\n"); log("\n"); + log(" -select\n"); + log(" Select the nodes of a critical path\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *d) override { @@ -333,12 +355,17 @@ struct TimeestPass : Pass { std::string clk; bool all_paths = false; + bool select = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-all_paths") { all_paths = true; continue; } + if (args[argidx] == "-select") { + select = true; + continue; + } if (args[argidx] == "-clk" && argidx + 1 < args.size()) { clk = args[++argidx]; continue; @@ -350,6 +377,9 @@ struct TimeestPass : Pass { 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.c_str(), log_id(m)); @@ -358,6 +388,7 @@ struct TimeestPass : Pass { EstimateSta sta(m, SigBit(m->wire(RTLIL::escape_id(clk)), 0)); sta.all_paths = all_paths; + sta.select = select; sta.run(); } } From 9c9a0e3e45a856aa40a98b8b81b24e14592920ec Mon Sep 17 00:00:00 2001 From: Emily Schmidt Date: Tue, 22 Apr 2025 12:21:32 +0100 Subject: [PATCH 7/8] add some comments to timeest --- passes/cmds/timeest.cc | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/passes/cmds/timeest.cc b/passes/cmds/timeest.cc index 020e4347c..2987d6927 100644 --- a/passes/cmds/timeest.cc +++ b/passes/cmds/timeest.cc @@ -61,6 +61,8 @@ struct EstimateSta { samplers.push_back(std::make_pair(cell, bit)); } + // we include a discount factor for cells that can be implemented using carry chain logic + // and to account for the AIG model not being balanced int cell_type_factor(IdString type) { if (type.in(ID($gt), ID($ge), ID($lt), ID($le), ID($add), ID($sub), @@ -81,11 +83,13 @@ struct EstimateSta { { log("Domain %s\n", log_signal(clk)); + // first, we collect launch and sample points and convert the combinational logic to AIG std::vector combinational; for (auto cell : m->cells()) { SigSpec launch, sample; if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + // collect launch and sample points for FF cell FfData ff(nullptr, cell); if (!ff.has_clk) { log_warning("Ignoring unsupported storage element '%s' (%s)\n", @@ -107,6 +111,7 @@ struct EstimateSta { } else if (cell->type == ID($scopeinfo)) { continue; } else { + // find or build AIG model of combinational cell auto fingerprint = std::make_pair(cell->type, cell->parameters); if (!aigs.count(fingerprint)) { aigs.emplace(fingerprint, Aig(cell)); @@ -121,11 +126,14 @@ struct EstimateSta { } } + // since we're now taking reference into `aigs`, we can no longer modify it + // and thus have to fill `cell_aigs` in a separate loop for (auto cell : combinational) { auto fingerprint = std::make_pair(cell->type, cell->parameters); cell_aigs.emplace(cell, &aigs.at(fingerprint)); } + // collect launch and sample points for memory cells for (auto &mem : Mem::get_all_memories(m)) { for (auto &rd : mem.rd_ports) { if (!rd.clk_enable) { @@ -143,6 +151,9 @@ struct EstimateSta { } } + // now we toposort the combinational logic + + // each toposort node is either a SigBit or a pair of Cell * / AigNode * TopoSort> topo; auto desc_aig = [&](Cell *cell, AigNode &node) { @@ -152,6 +163,7 @@ struct EstimateSta { return std::make_tuple(sigmap(bit), (Cell *) NULL, (AigNode *) NULL); }; + // collect edges of the AIG graph for (auto cell : combinational) { assert(cell_aigs.count(cell)); Aig &aig = *cell_aigs.at(cell); @@ -185,12 +197,16 @@ struct EstimateSta { if (!topo.sort()) log_error("Module '%s' contains combinational loops", log_id(m)); - + + // now we determine how long it takes for signals to stabilize + + // `levels` records the time after a clock edge after which a signal is stable dict, arrivalint> levels; for (auto node : topo.sorted) levels[node] = INF_PAST; + // launch points are at 0 by definition for (auto pair : launchers) levels[desc_sig(pair.second)] = 0; @@ -200,22 +216,26 @@ struct EstimateSta { Cell *cell = std::get<1>(node); Aig &aig = *cell_aigs.at(cell); if (!aig_node->portname.empty()) { + // for a cell port, copy `levels` value from port bit SigBit bit = cell->getPort(aig_node->portname)[aig_node->portbit]; levels[node] = levels[desc_sig(bit)]; } else if (aig_node->left_parent < 0 && aig_node->right_parent < 0) { // constant, nothing to do } else { + // for each AIG node, find maximum of parents and add a cell-specific delay int left = levels[desc_aig(cell, aig.nodes[aig_node->left_parent])]; int right = levels[desc_aig(cell, aig.nodes[aig_node->right_parent])]; levels[node] = (std::max(left, right) + cell_type_factor(cell->type)); } + // copy `levels` value to any output ports for (auto &oport : aig_node->outports) { levels[desc_sig(cell->getPort(oport.first)[oport.second])] = levels[node]; } } } + // now find the length of the critical path (slowest path in the design) arrivalint crit = INF_PAST; for (auto pair : samplers) if (levels[desc_sig(pair.second)] > crit) @@ -232,6 +252,7 @@ struct EstimateSta { // some compile-time errors related to hashing dict, bool> critical; + // actually find one critical path, or all such paths if requested for (auto pair : samplers) { if (levels[desc_sig(pair.second)] == crit) { critical[desc_sig(pair.second)] = true; @@ -240,6 +261,7 @@ struct EstimateSta { } } + // walk backwards through toposorted nodes and set critical flag on nodes in critical path for (auto it = topo.sorted.rbegin(); it != topo.sorted.rend(); it++) { auto node = *it; AigNode *aig_node = std::get<2>(node); @@ -248,22 +270,20 @@ struct EstimateSta { Aig &aig = *cell_aigs.at(cell); for (auto &oport : aig_node->outports) { - //levels[desc_sig(cell->getPort(oport.first)[oport.second])] = levels[node]; if (critical.count(desc_sig(cell->getPort(oport.first)[oport.second]))) critical[node] = true; } if (!aig_node->portname.empty()) { SigBit bit = cell->getPort(aig_node->portname)[aig_node->portbit]; - //levels[node] = levels[desc_sig(bit)]; if (critical.count(node)) critical[desc_sig(bit)] = true; } else if (aig_node->left_parent < 0 && aig_node->right_parent < 0) { // constant, nothing to do } else { + // figure out which parent is on the critical path auto left = desc_aig(cell, aig.nodes[aig_node->left_parent]); auto right = desc_aig(cell, aig.nodes[aig_node->right_parent]); - //levels[node] = (std::max(left, right) + 1); int crit_input_lvl = levels[node] - cell_type_factor(cell->type); if (critical.count(node)) { bool left_critical = (levels[left] == crit_input_lvl); @@ -284,6 +304,7 @@ struct EstimateSta { } } + // finally print the path we found SigPool bits_to_select; pool to_select; From 4b4cdf75b829fafb0db665ad82a860c768366be1 Mon Sep 17 00:00:00 2001 From: Emily Schmidt Date: Tue, 22 Apr 2025 13:19:28 +0100 Subject: [PATCH 8/8] timeest: gcc refuses to parse "struct ::Yosys:..." --- passes/cmds/timeest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/timeest.cc b/passes/cmds/timeest.cc index 2987d6927..05dd2a4b3 100644 --- a/passes/cmds/timeest.cc +++ b/passes/cmds/timeest.cc @@ -28,7 +28,7 @@ #include USING_YOSYS_NAMESPACE -template<> struct ::Yosys::hashlib::hash_ops : Yosys::hashlib::hash_ptr_ops {}; +template<> struct Yosys::hashlib::hash_ops : Yosys::hashlib::hash_ptr_ops {}; PRIVATE_NAMESPACE_BEGIN