From 0253911ee28f12dd13686adc8317f3b4c6ac2f44 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 11 Aug 2026 17:23:57 +0200 Subject: [PATCH] opt_clean: move unused_bits analysis to kernel/unused_bits.h --- backends/edif/edif.cc | 1 - docs/source/using_yosys/synthesis/fsm.rst | 4 +- docs/source/using_yosys/synthesis/opt.rst | 2 - kernel/CMakeLists.txt | 2 + kernel/constids.inc | 1 - kernel/unused_bits.h | 54 +++++++++++++++++++ passes/cmds/wrapcell.cc | 16 ++---- passes/fsm/fsm_expand.cc | 11 ++-- passes/fsm/fsm_opt.cc | 31 ++++------- passes/fsm/fsmdata.h | 3 +- passes/opt/opt_clean/wires.cc | 64 +++-------------------- passes/opt/wreduce.cc | 1 - 12 files changed, 86 insertions(+), 104 deletions(-) create mode 100644 kernel/unused_bits.h diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index 9d3392e99..51abc98bd 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -406,7 +406,6 @@ struct EdifBackend : public Backend { auto count_nontrivial_attr = [](Wire *w) { int count = w->attributes.size(); count -= w->attributes.count(ID::src); - count -= w->attributes.count(ID::unused_bits); return count; }; diff --git a/docs/source/using_yosys/synthesis/fsm.rst b/docs/source/using_yosys/synthesis/fsm.rst index 2da0b1896..fab25933f 100644 --- a/docs/source/using_yosys/synthesis/fsm.rst +++ b/docs/source/using_yosys/synthesis/fsm.rst @@ -108,9 +108,7 @@ FSM optimization The `fsm_opt` pass performs basic optimizations on `$fsm` cells (not including state recoding). The following optimizations are performed (in this order): -- Unused control outputs are removed from the `$fsm` cell. The attribute - ``unused_bits`` (that is usually set by the `opt_clean` pass) is used to - determine which control outputs are unused. +- Unused control outputs are removed from the `$fsm` cell. - Control inputs that are connected to the same driver are merged. diff --git a/docs/source/using_yosys/synthesis/opt.rst b/docs/source/using_yosys/synthesis/opt.rst index 56330bc37..9075016a9 100644 --- a/docs/source/using_yosys/synthesis/opt.rst +++ b/docs/source/using_yosys/synthesis/opt.rst @@ -203,8 +203,6 @@ Removing unused cells and wires - `opt_clean` pass ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This pass identifies unused signals and cells and removes them from the design. -It also creates an ``unused_bits`` attribute on wires with unused bits. This -attribute can be used for debugging or by other optimization passes. When to use `opt` or `clean` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 4cd76b4ff..e6ca435be 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -80,6 +80,7 @@ yosys_core(kernel threading.h timinginfo.h topo_scc.h + unused_bits.h utils.h version.cc yosys.cc @@ -145,6 +146,7 @@ yosys_core(kernel sigtools.h threading.h timinginfo.h + unused_bits.h utils.h yosys.h yosys_common.h diff --git a/kernel/constids.inc b/kernel/constids.inc index ecfdc8d39..673a50e7c 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -1029,7 +1029,6 @@ X(unsigned_a_i) X(unsigned_b) X(unsigned_b_i) X(unsupported_sva) -X(unused_bits) X(use_dsp) X(value) X(via_celltype) diff --git a/kernel/unused_bits.h b/kernel/unused_bits.h new file mode 100644 index 000000000..5762ed4f7 --- /dev/null +++ b/kernel/unused_bits.h @@ -0,0 +1,54 @@ +#ifndef UNUSED_BITS_H +#define UNUSED_BITS_H + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/newcelltypes.h" + +YOSYS_NAMESPACE_BEGIN + +struct UnusedBits +{ + SigMap sigmap; + pool used; + + UnusedBits(RTLIL::Module *module) : sigmap(module) + { + NewCellTypes ct(module->design); + collect_used(module, ct); + } + + UnusedBits(RTLIL::Module *module, const NewCellTypes &ct) : sigmap(module) + { + collect_used(module, ct); + } + + void collect_used(RTLIL::Module *module, const NewCellTypes &ct) + { + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) + if (!ct.cell_output(cell->type, conn.first)) + add_used(conn.second); + + for (auto wire : module->wires()) + if (wire->port_output) + add_used(wire); + } + + void add_used(const RTLIL::SigSpec &sig) + { + for (auto bit : sigmap(sig)) + if (bit.wire != nullptr) + used.insert(bit); + } + + bool check(RTLIL::SigBit bit) const + { + RTLIL::SigBit mapped = sigmap(bit); + return mapped.wire != nullptr && !used.count(mapped); + } +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/passes/cmds/wrapcell.cc b/passes/cmds/wrapcell.cc index 9d73a63c0..d31710984 100644 --- a/passes/cmds/wrapcell.cc +++ b/passes/cmds/wrapcell.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" +#include "kernel/unused_bits.h" #include "backends/rtlil/rtlil_backend.h" USING_YOSYS_NAMESPACE @@ -190,19 +191,10 @@ struct WrapcellPass : Pass { bool tracking_unused = has_fmt_field(name_fmt, "%unused"); - for (auto module : d->selected_modules()) { - SigPool unused; + NewCellTypes ct_all(d); - for (auto wire : module->wires()) - if (wire->has_attribute(ID::unused_bits)) { - std::string str = wire->get_string_attribute(ID::unused_bits); - for (auto it = str.begin(); it != str.end();) { - auto sep = it; - for (; sep != str.end() && *sep != ' '; sep++); - unused.add(SigBit(wire, std::stoi(std::string(it, sep)))); - for (it = sep; it != str.end() && *it == ' '; it++); - } - } + for (auto module : d->selected_modules()) { + UnusedBits unused(module, ct_all); for (auto cell : module->selected_cells()) { Module *subm; diff --git a/passes/fsm/fsm_expand.cc b/passes/fsm/fsm_expand.cc index 40c1d9904..9b05ca688 100644 --- a/passes/fsm/fsm_expand.cc +++ b/passes/fsm/fsm_expand.cc @@ -22,6 +22,7 @@ #include "kernel/sigtools.h" #include "kernel/consteval.h" #include "kernel/celltypes.h" +#include "kernel/unused_bits.h" #include "fsmdata.h" #include @@ -32,6 +33,7 @@ struct FsmExpand { RTLIL::Module *module; RTLIL::Cell *fsm_cell; + UnusedBits& unused_bits; bool full_mode; SigMap assign_map; @@ -127,7 +129,7 @@ struct FsmExpand if (trans_num > limit_transitions) { log(" grown transition table to %d entries -> optimize.\n", trans_num); - FsmData::optimize_fsm(fsm_cell, module); + FsmData::optimize_fsm(fsm_cell, module, unused_bits); already_optimized = true; trans_num = fsm_cell->parameters[ID::TRANS_NUM].as_int(); @@ -218,7 +220,7 @@ struct FsmExpand fsm_data.copy_to_cell(fsm_cell); } - FsmExpand(RTLIL::Cell *cell, RTLIL::Design *design, RTLIL::Module *mod, bool full) + FsmExpand(RTLIL::Cell *cell, RTLIL::Design *design, RTLIL::Module *mod, UnusedBits& unused_bits, bool full) : unused_bits(unused_bits) { module = mod; fsm_cell = cell; @@ -257,7 +259,7 @@ struct FsmExpand module->remove(c); if (merged_set.size() > 0 && !already_optimized) - FsmData::optimize_fsm(fsm_cell, module); + FsmData::optimize_fsm(fsm_cell, module, unused_bits); log(" merged %d cells into FSM.\n", GetSize(merged_set)); } @@ -296,12 +298,13 @@ struct FsmExpandPass : public Pass { extra_args(args, argidx, design); for (auto mod : design->selected_modules()) { + UnusedBits unused_bits(mod); std::vector fsm_cells; for (auto cell : mod->selected_cells()) if (cell->type == ID($fsm)) fsm_cells.push_back(cell); for (auto c : fsm_cells) { - FsmExpand fsm_expand(c, design, mod, full_mode); + FsmExpand fsm_expand(c, design, mod, unused_bits, full_mode); fsm_expand.execute(); } } diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc index b61dec890..eae91488f 100644 --- a/passes/fsm/fsm_opt.cc +++ b/passes/fsm/fsm_opt.cc @@ -22,8 +22,8 @@ #include "kernel/sigtools.h" #include "kernel/consteval.h" #include "kernel/celltypes.h" +#include "kernel/unused_bits.h" #include "fsmdata.h" -#include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -33,6 +33,7 @@ struct FsmOpt FsmData fsm_data; RTLIL::Cell *cell; RTLIL::Module *module; + UnusedBits& unused_bits; void opt_unreachable_states() { @@ -79,21 +80,7 @@ struct FsmOpt bool signal_is_unused(RTLIL::SigSpec sig) { - RTLIL::SigBit bit = sig.as_bit(); - - if (bit.wire == NULL || bit.wire->attributes.count(ID::unused_bits) == 0) - return false; - - char *str = strdup(bit.wire->attributes[ID::unused_bits].decode_string().c_str()); - for (char *tok = strtok(str, " "); tok != NULL; tok = strtok(NULL, " ")) { - if (tok[0] && bit.offset == atoi(tok)) { - free(str); - return true; - } - } - free(str); - - return false; + return unused_bits.check(sig.as_bit()); } void opt_const_and_unused_inputs() @@ -294,7 +281,7 @@ struct FsmOpt } } - FsmOpt(RTLIL::Cell *cell, RTLIL::Module *module) + FsmOpt(RTLIL::Cell *cell, RTLIL::Module *module, UnusedBits& unused_bits) : unused_bits(unused_bits) { log("Optimizing FSM `%s' from module `%s'.\n", cell->name, module->name); @@ -318,9 +305,9 @@ struct FsmOpt PRIVATE_NAMESPACE_END -void YOSYS_NAMESPACE_PREFIX FsmData::optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module) +void YOSYS_NAMESPACE_PREFIX FsmData::optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module, UnusedBits& unused_bits) { - FsmOpt fsmopt(cell, module); + FsmOpt fsmopt(cell, module, unused_bits); } PRIVATE_NAMESPACE_BEGIN @@ -343,10 +330,12 @@ struct FsmOptPass : public Pass { log_header(design, "Executing FSM_OPT pass (simple optimizations of FSMs).\n"); extra_args(args, 1, design); - for (auto mod : design->selected_modules()) + for (auto mod : design->selected_modules()) { + UnusedBits unused_bits(mod); for (auto cell : mod->selected_cells()) if (cell->type == ID($fsm)) - FsmData::optimize_fsm(cell, mod); + FsmData::optimize_fsm(cell, mod, unused_bits); + } } } FsmOptPass; diff --git a/passes/fsm/fsmdata.h b/passes/fsm/fsmdata.h index 4d824e136..b0317a1db 100644 --- a/passes/fsm/fsmdata.h +++ b/passes/fsm/fsmdata.h @@ -21,6 +21,7 @@ #define FSMDATA_H #include "kernel/yosys.h" +#include "kernel/unused_bits.h" YOSYS_NAMESPACE_BEGIN @@ -155,7 +156,7 @@ struct FsmData } // implemented in fsm_opt.cc - static void optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module); + static void optimize_fsm(RTLIL::Cell *cell, RTLIL::Module *module, UnusedBits& unused_bits); }; YOSYS_NAMESPACE_END diff --git a/passes/opt/opt_clean/wires.cc b/passes/opt/opt_clean/wires.cc index 28c792936..e250bdadd 100644 --- a/passes/opt/opt_clean/wires.cc +++ b/passes/opt/opt_clean/wires.cc @@ -86,7 +86,6 @@ int count_nontrivial_wire_attrs(RTLIL::Wire *w) count -= w->attributes.count(ID::src); count -= w->attributes.count(ID::hdlname); count -= w->attributes.count(ID::scopename); - count -= w->attributes.count(ID::unused_bits); return count; } @@ -168,13 +167,6 @@ bool check_any(const ShardedSigPool &sigs, const RTLIL::SigSpec &spec) { return false; } -bool check_all(const ShardedSigPool &sigs, const RTLIL::SigSpec &spec) { - for (SigBit b : spec) - if (sigs.find({b, b.hash_top().yield()}) == nullptr) - return false; - return true; -} - struct UpdateConnection { RTLIL::Cell *cell; RTLIL::IdString port; @@ -328,33 +320,28 @@ struct DeferredUpdates { }; struct UsedSignals { // here, "connected" means "driven or driving something" - // meanwhile, "used" means "driving something" // sigmapped ShardedSigPool connected; // pre-sigmapped ShardedSigPool raw_connected; - // sigmapped - ShardedSigPool used; void clear(ParallelDispatchThreadPool::Subpool &subpool) { subpool.run([this](const ParallelDispatchThreadPool::RunCtx &ctx) { connected.clear(ctx); raw_connected.clear(ctx); - used.clear(ctx); }); } }; -DeferredUpdates analyse_connectivity(UsedSignals& used, SigConnKinds& sig_analysis, const AnalysisContext& actx, CleanRunContext &clean_ctx) { +DeferredUpdates analyse_connectivity(UsedSignals& used, SigConnKinds& sig_analysis, const AnalysisContext& actx) { DeferredUpdates deferred(actx.subpool); ShardedSigPool::Builder conn_builder(actx.subpool); ShardedSigPool::Builder raw_conn_builder(actx.subpool); - ShardedSigPool::Builder used_builder(actx.subpool); // gather the usage information for cells and update cell connections with the altered sigmap // also gather the usage information for ports, wires with `keep` // also gather init bits - actx.subpool.run([&deferred, &conn_builder, &raw_conn_builder, &used_builder, &sig_analysis, &actx, &clean_ctx](const ParallelDispatchThreadPool::RunCtx &ctx) { + actx.subpool.run([&deferred, &conn_builder, &raw_conn_builder, &sig_analysis, &actx](const ParallelDispatchThreadPool::RunCtx &ctx) { // Parallel destruction of these sharded structures sig_analysis.clear(ctx); @@ -366,8 +353,6 @@ DeferredUpdates analyse_connectivity(UsedSignals& used, SigConnKinds& sig_analys deferred.update_connections.insert(ctx, {cell, port, spec}); add_spec(raw_conn_builder, ctx, spec); add_spec(conn_builder, ctx, spec); - if (!clean_ctx.ct_all.cell_output(cell->type, port)) - add_spec(used_builder, ctx, spec); } } for (int i : ctx.item_range(actx.mod->wires_size())) { @@ -377,8 +362,6 @@ DeferredUpdates analyse_connectivity(UsedSignals& used, SigConnKinds& sig_analys add_spec(raw_conn_builder, ctx, sig); actx.assign_map.apply(sig); add_spec(conn_builder, ctx, sig); - if (!wire->port_input) - add_spec(used_builder, ctx, sig); } if (wire->get_bool_attribute(ID::keep)) { RTLIL::SigSpec sig = RTLIL::SigSpec(wire); @@ -390,12 +373,11 @@ DeferredUpdates analyse_connectivity(UsedSignals& used, SigConnKinds& sig_analys deferred.initialized_wires.insert(ctx, wire); } }); - actx.subpool.run([&conn_builder, &raw_conn_builder, &used_builder](const ParallelDispatchThreadPool::RunCtx &ctx) { + actx.subpool.run([&conn_builder, &raw_conn_builder](const ParallelDispatchThreadPool::RunCtx &ctx) { conn_builder.process(ctx); raw_conn_builder.process(ctx); - used_builder.process(ctx); }); - used = {conn_builder, raw_conn_builder, used_builder}; + used = {conn_builder, raw_conn_builder}; return deferred; } @@ -404,14 +386,10 @@ struct WireDeleter { ShardedVector remove_init; ShardedVector> set_init; ShardedVector new_connections; - ShardedVector remove_unused_bits; - ShardedVector> set_unused_bits; WireDeleter(UsedSignals& used_sig_analysis, bool purge_mode, const AnalysisContext& actx) : remove_init(actx.subpool), set_init(actx.subpool), - new_connections(actx.subpool), - remove_unused_bits(actx.subpool), - set_unused_bits(actx.subpool) { + new_connections(actx.subpool) { ShardedVector del_wires(actx.subpool); actx.subpool.run([&actx, purge_mode, &del_wires, &used_sig_analysis, this](const ParallelDispatchThreadPool::RunCtx &ctx) { for (int i : ctx.item_range(actx.mod->wires_size())) { @@ -471,32 +449,6 @@ struct WireDeleter { } else if (init_changed) set_init.insert(ctx, {wire, std::move(initval)}); - - std::string unused_bits; - if (!check_all(used_sig_analysis.used, s2)) { - for (int i = 0; i < GetSize(s2); i++) { - if (s2[i].wire == NULL) - continue; - SigBit b = s2[i]; - if (used_sig_analysis.used.find({b, b.hash_top().yield()}) == nullptr) { - if (!unused_bits.empty()) - unused_bits += " "; - unused_bits += stringf("%d", i); - } - } - } - if (unused_bits.empty() || wire->port_id != 0) { - if (wire->attributes.count(ID::unused_bits)) - remove_unused_bits.insert(ctx, wire); - } else { - RTLIL::Const unused_bits_const(std::move(unused_bits)); - if (wire->attributes.count(ID::unused_bits)) { - RTLIL::Const &unused_bits_attr = wire->attributes.at(ID::unused_bits); - if (unused_bits_attr != unused_bits_const) - set_unused_bits.insert(ctx, {wire, std::move(unused_bits_const)}); - } else - set_unused_bits.insert(ctx, {wire, std::move(unused_bits_const)}); - } } } }); @@ -511,10 +463,6 @@ struct WireDeleter { p.first->attributes[ID::init] = std::move(p.second); for (auto &conn : new_connections) mod->connect(std::move(conn)); - for (RTLIL::Wire *wire : remove_unused_bits) - wire->attributes.erase(ID::unused_bits); - for (auto &p : set_unused_bits) - p.first->attributes[ID::unused_bits] = std::move(p.second); } int delete_wires(RTLIL::Module* mod, bool verbose) { int deleted_and_unreported = 0; @@ -557,7 +505,7 @@ bool rmunused_module_signals(RTLIL::Module *module, ParallelDispatchThreadPool:: module->connections_.clear(); UsedSignals used; - DeferredUpdates deferred = analyse_connectivity(used, conn_kinds, actx, clean_ctx); + DeferredUpdates deferred = analyse_connectivity(used, conn_kinds, actx); fixup_cell_ports(deferred.update_connections); // Rip up and re-apply init attributes onto representative wires with x-bits // in place of unset init bits diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 2ba01d26b..1f8c11ba6 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -453,7 +453,6 @@ struct WreduceWorker { int count = w->attributes.size(); count -= w->attributes.count(ID::src); - count -= w->attributes.count(ID::unused_bits); return count; }