From 4a51ae1f2c6356976afd1df759aa2d0c9a01caf5 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 4 Sep 2026 10:37:35 +0200 Subject: [PATCH 1/6] abc: don't bump assign_map in extract --- passes/techmap/abc.cc | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 3c4341808..e5e0540fe 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -327,7 +327,7 @@ struct AbcModuleState { void handle_loops(AbcSigMap &assign_map, RTLIL::Module *module); void prepare_module(RTLIL::Design *design, RTLIL::Module *module, AbcSigMap &assign_map, const std::vector &cells, bool dff_mode, std::string clk_str); - void extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module); + void extract(RTLIL::Design *design, RTLIL::Module *module); void finish(); }; @@ -787,6 +787,7 @@ void AbcModuleState::handle_loops(AbcSigMap &assign_map, RTLIL::Module *module) } edges[id1].swap(edges[id3]); + // TODO connect(assign_map, module, RTLIL::SigSig(signal_bits[id3], signal_bits[id1])); dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count); } @@ -1513,7 +1514,7 @@ void emit_global_input_files(const AbcConfig &config) } } -void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL::Module *module) +void AbcModuleState::extract(RTLIL::Design *design, RTLIL::Module *module) { log_push(); log_header(design, "Executed ABC.\n"); @@ -1563,7 +1564,7 @@ void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL RTLIL::IdString name_y = remap_name(c->getPort(ID::Y).as_wire()->name); conn.first = module->wire(name_y); conn.second = RTLIL::SigSpec(c->type == ID(ZERO) ? 0 : 1, 1); - connect(assign_map, module, conn); + module->connect(conn); continue; } if (c->type == ID(BUF)) { @@ -1572,7 +1573,7 @@ void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL RTLIL::IdString name_a = remap_name(c->getPort(ID::A).as_wire()->name); conn.first = module->wire(name_y); conn.second = module->wire(name_a); - connect(assign_map, module, conn); + module->connect(conn); continue; } if (c->type == ID(NOT)) { @@ -1704,7 +1705,7 @@ void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL RTLIL::SigSig conn; conn.first = module->wire(remap_name(c->connections().begin()->second.as_wire()->name)); conn.second = RTLIL::SigSpec(c->type == ID(_const0_) ? 0 : 1, 1); - connect(assign_map, module, conn); + module->connect(conn); continue; } @@ -1749,7 +1750,7 @@ void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL if (c->type == ID($lut) && GetSize(c->getPort(ID::A)) == 1 && c->getParam(ID::LUT).as_int() == 2) { SigSpec my_a = module->wire(remap_name(c->getPort(ID::A).as_wire()->name)); SigSpec my_y = module->wire(remap_name(c->getPort(ID::Y).as_wire()->name)); - connect(assign_map, module, RTLIL::SigSig(my_a, my_y)); + module->connect(RTLIL::SigSig(my_a, my_y)); continue; } @@ -1774,7 +1775,7 @@ void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL conn.first = module->wire(remap_name(conn.first.as_wire()->name)); if (!conn.second.is_fully_const()) conn.second = module->wire(remap_name(conn.second.as_wire()->name)); - connect(assign_map, module, conn); + module->connect(conn); } cell_stats.sort(); @@ -1795,7 +1796,7 @@ void AbcModuleState::extract(AbcSigMap &assign_map, RTLIL::Design *design, RTLIL conn.second = signal_bits[si.id]; in_wires++; } - connect(assign_map, module, conn); + module->connect(conn); } log("ABC RESULTS: internal signals: %8d\n", int(run_abc.signal_list.size()) - in_wires - out_wires); log("ABC RESULTS: input signals: %8d\n", in_wires); @@ -2441,7 +2442,7 @@ struct AbcPass : public Pass { state.prepare_module(design, mod, assign_map, cells, dff_mode, clk_str); ConcurrentStack process_pool; state.run_abc.run(process_pool); - state.extract(assign_map, design, mod); + state.extract(design, mod); continue; } @@ -2636,7 +2637,7 @@ struct AbcPass : public Pass { ++work_finished_count; } while (work_finished_by_index[next_state_index_to_process] != nullptr) { - work_finished_by_index[next_state_index_to_process]->extract(assign_map, design, mod); + work_finished_by_index[next_state_index_to_process]->extract(design, mod); work_finished_by_index[next_state_index_to_process] = nullptr; ++next_state_index_to_process; } @@ -2666,7 +2667,7 @@ struct AbcPass : public Pass { ++work_finished_count; } while (next_state_index_to_process < GetSize(work_finished_by_index)) { - work_finished_by_index[next_state_index_to_process]->extract(assign_map, design, mod); + work_finished_by_index[next_state_index_to_process]->extract(design, mod); work_finished_by_index[next_state_index_to_process] = nullptr; ++next_state_index_to_process; } From 3870f810f3bef058b452d5fcaa0a0cc5961b5add Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 4 Sep 2026 11:11:55 +0200 Subject: [PATCH 2/6] kernel: avoid running ensure_at_least in parallel --- kernel/yosys.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/yosys.cc b/kernel/yosys.cc index c2574497d..f99a8229b 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -125,6 +125,7 @@ Multithreading::~Multithreading() { } void Autoidx::ensure_at_least(int v) { + log_assert(!Multithreading::active()); value = std::max(value, v); } From 74ff7c407b5cc1c7a84d1932589527edb15baf89 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 4 Sep 2026 11:12:33 +0200 Subject: [PATCH 3/6] abc: per-clock-domain autoidx mechanism for multi-threaded determinism --- passes/techmap/abc.cc | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index e5e0540fe..c9582a5fa 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -301,7 +301,7 @@ struct AbcModuleState { RunAbcState run_abc; int state_index; - int map_autoidx = 0; + int map_autoidx; std::vector signal_bits; dict signal_map; FfInitVals &initvals; @@ -315,8 +315,8 @@ struct AbcModuleState { int undef_bits_lost = 0; - AbcModuleState(const AbcConfig &config, FfInitVals &initvals, int state_index) - : run_abc(config), state_index(state_index), initvals(initvals) {} + AbcModuleState(const AbcConfig &config, FfInitVals &initvals, int state_index, int map_autoidx) + : run_abc(config), state_index(state_index), map_autoidx(map_autoidx), initvals(initvals) {} AbcModuleState(AbcModuleState&&) = delete; int map_signal(const AbcSigMap &assign_map, RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, int in2 = -1, int in3 = -1, int in4 = -1); @@ -674,6 +674,8 @@ void AbcModuleState::handle_loops(AbcSigMap &assign_map, RTLIL::Module *module) FILE *dot_f = nullptr; int dot_nr = 0; + // Avoids mutating autoidx for multi-threaded determinism + int loop_count = 0; // uncomment for troubleshooting the loop detection code // dot_f = fopen("test.dot", "w"); @@ -753,9 +755,7 @@ void AbcModuleState::handle_loops(AbcSigMap &assign_map, RTLIL::Module *module) log_assert(signal_bits[id1].wire != nullptr); - std::stringstream sstr; - sstr << "$abcloop$" << (autoidx++); - RTLIL::Wire *wire = module->addWire(sstr.str()); + RTLIL::Wire *wire = module->addWire(stringf("$abcloop$%d$%d", map_autoidx, loop_count++)); bool first_line = true; for (int id2 : edges[id1]) { @@ -935,8 +935,6 @@ struct abc_output_filter void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module, AbcSigMap &assign_map, const std::vector &cells, bool dff_mode, std::string clk_str) { - map_autoidx = autoidx++; - if (clk_str != "$") { clk_polarity = true; @@ -2438,7 +2436,7 @@ struct AbcPass : public Pass { std::vector cells = mod->selected_cells(); assign_cell_connection_ports(mod, {&cells}, assign_map); - AbcModuleState state(config, initvals, 0); + AbcModuleState state(config, initvals, 0, autoidx++); state.prepare_module(design, mod, assign_map, cells, dff_mode, clk_str); ConcurrentStack process_pool; state.run_abc.run(process_pool); @@ -2600,6 +2598,10 @@ struct AbcPass : public Pass { assign_cell_connection_ports(mod, cell_sets, assign_map); } + // Advance autoidx by the clock domain count for multi-threaded determinism + int autoidx_base = autoidx; + autoidx.ensure_at_least(autoidx_base + GetSize(assigned_cells)); + // Reserve one core for our main thread, and don't create more worker threads // than ABC runs. int max_threads = assigned_cells.size(); @@ -2641,7 +2643,8 @@ struct AbcPass : public Pass { work_finished_by_index[next_state_index_to_process] = nullptr; ++next_state_index_to_process; } - std::unique_ptr state = std::make_unique(config, initvals, state_index++); + std::unique_ptr state = std::make_unique( + config, initvals, state_index, autoidx_base + state_index); state->clk_polarity = std::get<0>(it.first); state->clk_sig = assign_map(std::get<1>(it.first)); state->en_polarity = std::get<2>(it.first); @@ -2658,6 +2661,7 @@ struct AbcPass : public Pass { state->run_abc.run(process_pool); work_finished_queue.push_back(std::move(state)); } + state_index++; } work_queue.close(); while (work_finished_count < GetSize(assigned_cells)) { From cdbf2948bbccaffb44a9989d21fdf1630e30acdb Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 4 Sep 2026 11:38:26 +0200 Subject: [PATCH 4/6] abc: defer cell removal from prepare until extract for multi-threaded determinism --- passes/techmap/abc.cc | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index c9582a5fa..9947d11fc 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -304,6 +304,8 @@ struct AbcModuleState { int map_autoidx; std::vector signal_bits; dict signal_map; + // Deletions are deferred until extract for multi-threaded determinism + std::vector cells_to_remove; FfInitVals &initvals; bool had_init = false; @@ -321,7 +323,7 @@ struct AbcModuleState { int map_signal(const AbcSigMap &assign_map, RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, int in2 = -1, int in3 = -1, int in4 = -1); void mark_port(const AbcSigMap &assign_map, RTLIL::SigSpec sig); - bool extract_cell(const AbcSigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, bool keepff); + bool prepare_cell(const AbcSigMap &assign_map, RTLIL::Cell *cell, bool keepff); std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr); void dump_loop_graph(FILE *f, int &nr, dict> &edges, pool &workpool, std::vector &in_counts); void handle_loops(AbcSigMap &assign_map, RTLIL::Module *module); @@ -379,7 +381,7 @@ void AbcModuleState::mark_port(const AbcSigMap &assign_map, RTLIL::SigSpec sig) run_abc.signal_list[signal_map[bit]].is_port = true; } -bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, bool keepff) +bool AbcModuleState::prepare_cell(const AbcSigMap &assign_map, RTLIL::Cell *cell, bool keepff) { if (cell->is_builtin_ff()) { FfData ff(&initvals, cell); @@ -463,7 +465,8 @@ bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *mo map_signal(assign_map, ff.sig_q, type, map_signal(assign_map, ff.sig_d)); - ff.remove(); + ff.remove_init(); + cells_to_remove.push_back(cell); return true; } @@ -477,7 +480,7 @@ bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *mo map_signal(assign_map, sig_y, cell->type == ID($_BUF_) ? G(BUF) : G(NOT), map_signal(assign_map, sig_a)); - module->remove(cell); + cells_to_remove.push_back(cell); return true; } @@ -513,7 +516,7 @@ bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *mo else log_abort(); - module->remove(cell); + cells_to_remove.push_back(cell); return true; } @@ -535,7 +538,7 @@ bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *mo map_signal(assign_map, sig_y, cell->type == ID($_MUX_) ? G(MUX) : G(NMUX), mapped_a, mapped_b, mapped_s); - module->remove(cell); + cells_to_remove.push_back(cell); return true; } @@ -557,7 +560,7 @@ bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *mo map_signal(assign_map, sig_y, cell->type == ID($_AOI3_) ? G(AOI3) : G(OAI3), mapped_a, mapped_b, mapped_c); - module->remove(cell); + cells_to_remove.push_back(cell); return true; } @@ -582,7 +585,7 @@ bool AbcModuleState::extract_cell(const AbcSigMap &assign_map, RTLIL::Module *mo map_signal(assign_map, sig_y, cell->type == ID($_AOI4_) ? G(AOI4) : G(OAI4), mapped_a, mapped_b, mapped_c, mapped_d); - module->remove(cell); + cells_to_remove.push_back(cell); return true; } @@ -1133,7 +1136,7 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module had_init = false; std::vector kept_cells; for (auto c : cells) - if (!extract_cell(assign_map, module, c, config.keepff)) + if (!prepare_cell(assign_map, c, config.keepff)) kept_cells.push_back(c); if (undef_bits_lost) @@ -1141,7 +1144,7 @@ void AbcModuleState::prepare_module(RTLIL::Design *design, RTLIL::Module *module // Wires with port_id > 0, ID::keep, and connections to cells outside our cell set have already // been accounted for via AbcSigVal::is_port. Now we just need to account for - // connections to cells inside our cell set that weren't removed by extract_cell(). + // connections to cells inside our cell set that weren't removed by prepare_cell(). for (auto cell : kept_cells) for (auto &port_it : cell->connections()) mark_port(assign_map, port_it.second); @@ -1514,6 +1517,10 @@ void emit_global_input_files(const AbcConfig &config) void AbcModuleState::extract(RTLIL::Design *design, RTLIL::Module *module) { + for (RTLIL::Cell *cell : cells_to_remove) + module->remove(cell); + cells_to_remove.clear(); + log_push(); log_header(design, "Executed ABC.\n"); run_abc.logs.flush(); From e74a81c6390edff7ad38c19a6e886a033e18c5ef Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 4 Sep 2026 12:01:35 +0200 Subject: [PATCH 5/6] abc: cleanup --- passes/techmap/abc.cc | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 9947d11fc..5246efc27 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -660,12 +660,6 @@ void AbcModuleState::dump_loop_graph(FILE *f, int &nr, dict> &edg fprintf(f, "}\n"); } -void connect(AbcSigMap &assign_map, RTLIL::Module *module, const RTLIL::SigSig &conn) -{ - module->connect(conn); - assign_map.add(conn.first, conn.second); -} - void AbcModuleState::handle_loops(AbcSigMap &assign_map, RTLIL::Module *module) { // http://en.wikipedia.org/wiki/Topological_sorting @@ -790,8 +784,9 @@ void AbcModuleState::handle_loops(AbcSigMap &assign_map, RTLIL::Module *module) } edges[id1].swap(edges[id3]); - // TODO - connect(assign_map, module, RTLIL::SigSig(signal_bits[id3], signal_bits[id1])); + auto conn = SigSig(signal_bits[id3], signal_bits[id1]); + module->connect(conn); + assign_map.add(conn.first, conn.second); dump_loop_graph(dot_f, dot_nr, edges, workpool, in_edges_count); } } From 3be4154275b657259ae8167d547e2838b92c4da2 Mon Sep 17 00:00:00 2001 From: nella Date: Wed, 2 Sep 2026 12:09:25 +0200 Subject: [PATCH 6/6] Sigmap single port bits in mux tree feedback search. find_muxtree_feedback_patterns sigmapped the whole A/B/S ports of every visited $pmux, which is O(port width) per visited bit and makes wide mux trees quadratic. Sigmap only the single bit that is needed, via a new port_bit() helper, and factor the repeated Sx port writes into break_feedback(). Output is unchanged. --- passes/opt/dff/simple.cc | 60 +++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/passes/opt/dff/simple.cc b/passes/opt/dff/simple.cc index 9d0a6faef..0d6905cb4 100644 --- a/passes/opt/dff/simple.cc +++ b/passes/opt/dff/simple.cc @@ -103,6 +103,18 @@ struct SimpleContext } } + RTLIL::SigBit port_bit(RTLIL::Cell *cell, RTLIL::IdString port, int offset) + { + return worker.sigmap(cell->getPort(port)[offset]); + } + + void break_feedback(RTLIL::Cell *mux, RTLIL::IdString port, int offset) + { + RTLIL::SigSpec s = mux->getPort(port); + s[offset] = RTLIL::Sx; + mux->setPort(port, s); + } + patterns_t find_muxtree_feedback_patterns(RTLIL::SigBit d, RTLIL::SigBit q, pattern_t path) { // Find feedback paths D->Q through mux tree, replacing found paths with Sx @@ -117,20 +129,17 @@ struct SimpleContext return ret; // D not driven by MUX / MUX drives multiple loads cell_int_t mbit = bit2mux.at(d); - RTLIL::SigSpec sig_a = worker.sigmap(mbit.first->getPort(ID::A)); - RTLIL::SigSpec sig_b = worker.sigmap(mbit.first->getPort(ID::B)); - RTLIL::SigSpec sig_s = worker.sigmap(mbit.first->getPort(ID::S)); - int width = GetSize(sig_a), index = mbit.second; + RTLIL::Cell *mux = mbit.first; + int width = GetSize(mux->getPort(ID::A)), index = mbit.second; + int s_width = GetSize(mux->getPort(ID::S)); // Traverse MUX tree - for (int i = 0; i < GetSize(sig_s); i++) { - if (path.count(sig_s[i]) && path.at(sig_s[i])) { - ret = find_muxtree_feedback_patterns(sig_b[i*width + index], q, path); - if (sig_b[i*width + index] == q) { - RTLIL::SigSpec s = mbit.first->getPort(ID::B); - s[i*width + index] = RTLIL::Sx; - mbit.first->setPort(ID::B, s); - } + for (int i = 0; i < s_width; i++) { + RTLIL::SigBit s_bit = port_bit(mux, ID::S, i); + if (path.count(s_bit) && path.at(s_bit)) { + ret = find_muxtree_feedback_patterns(port_bit(mux, ID::B, i*width + index), q, path); + if (port_bit(mux, ID::B, i*width + index) == q) + break_feedback(mux, ID::B, i*width + index); return ret; } @@ -138,34 +147,29 @@ struct SimpleContext // Specific path wasn't forced, explore the 0 branch pattern_t path_else = path; - for (int i = 0; i < GetSize(sig_s); i++) { - if (path.count(sig_s[i])) + for (int i = 0; i < s_width; i++) { + RTLIL::SigBit s_bit = port_bit(mux, ID::S, i); + if (path.count(s_bit)) continue; pattern_t path_this = path; - path_else[sig_s[i]] = false; // Assume S=0 for 'else' path - path_this[sig_s[i]] = true; // Assume S=1 for 'this' path + path_else[s_bit] = false; // Assume S=0 for 'else' path + path_this[s_bit] = true; // Assume S=1 for 'this' path // Selected when S=1 - for (auto &pat : find_muxtree_feedback_patterns(sig_b[i*width + index], q, path_this)) + for (auto &pat : find_muxtree_feedback_patterns(port_bit(mux, ID::B, i*width + index), q, path_this)) ret.insert(pat); - if (sig_b[i*width + index] == q) { - RTLIL::SigSpec s = mbit.first->getPort(ID::B); - s[i*width + index] = RTLIL::Sx; - mbit.first->setPort(ID::B, s); - } + if (port_bit(mux, ID::B, i*width + index) == q) + break_feedback(mux, ID::B, i*width + index); } // Selected when S=0 - for (auto &pat : find_muxtree_feedback_patterns(sig_a[index], q, path_else)) + for (auto &pat : find_muxtree_feedback_patterns(port_bit(mux, ID::A, index), q, path_else)) ret.insert(pat); - if (sig_a[index] == q) { - RTLIL::SigSpec s = mbit.first->getPort(ID::A); - s[index] = RTLIL::Sx; - mbit.first->setPort(ID::A, s); - } + if (port_bit(mux, ID::A, index) == q) + break_feedback(mux, ID::A, index); return ret; }