From cbb776c62690ddf6fea4e79f48cdc60c11eb3c22 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 18 Oct 2024 16:25:15 +0200 Subject: [PATCH 01/18] opt_merge: avoid hashing strings --- passes/opt/opt_merge.cc | 173 +++++++++++++++++++++------------------- 1 file changed, 90 insertions(+), 83 deletions(-) diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index eb3aa462e..d6ed74433 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -26,6 +26,8 @@ #include #include #include +#include +#include USING_YOSYS_NAMESPACE @@ -42,6 +44,22 @@ struct OptMergeWorker CellTypes ct; int total_count; + static vector> sorted_pmux_in(const dict &conn) + { + SigSpec sig_s = conn.at(ID::S); + SigSpec sig_b = conn.at(ID::B); + + int s_width = GetSize(sig_s); + int width = GetSize(sig_b) / s_width; + + vector> sb_pairs; + for (int i = 0; i < s_width; i++) + sb_pairs.push_back(pair(sig_s[i], sig_b.extract(i*width, width))); + + std::sort(sb_pairs.begin(), sb_pairs.end()); + return sb_pairs; + } + static void sort_pmux_conn(dict &conn) { SigSpec sig_s = conn.at(ID::S); @@ -65,90 +83,74 @@ struct OptMergeWorker } } - std::string int_to_hash_string(unsigned int v) + Hasher hash_cell_inputs(const RTLIL::Cell *cell, Hasher h) { - if (v == 0) - return "0"; - std::string str = ""; - while (v > 0) { - str += 'a' + (v & 15); - v = v >> 4; - } - return str; - } - - uint64_t hash_cell_parameters_and_connections(const RTLIL::Cell *cell) - { - vector hash_conn_strings; - std::string hash_string = cell->type.str() + "\n"; - - const dict *conn = &cell->connections(); - dict alt_conn; - + // TODO: when implemented, use celltypes to match: + // (builtin || stdcell) && (unary || binary) && symmetrical if (cell->type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul), ID($logic_and), ID($logic_or), ID($_AND_), ID($_OR_), ID($_XOR_))) { - alt_conn = *conn; - if (assign_map(alt_conn.at(ID::A)) < assign_map(alt_conn.at(ID::B))) { - alt_conn[ID::A] = conn->at(ID::B); - alt_conn[ID::B] = conn->at(ID::A); + std::array inputs = { + assign_map(cell->getPort(ID::A)), + assign_map(cell->getPort(ID::B)) + }; + std::sort(inputs.begin(), inputs.end()); + h = hash_ops>::hash_acc(inputs, h); + h = assign_map(cell->getPort(ID::Y)).hash_acc(h); + } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { + SigSpec a = assign_map(cell->getPort(ID::A)); + a.sort(); + h = a.hash_acc(h); + } else if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { + SigSpec a = assign_map(cell->getPort(ID::A)); + a.sort_and_unify(); + h = a.hash_acc(h); + } else if (cell->type == ID($pmux)) { + dict conn = cell->connections(); + assign_map.apply(conn.at(ID::A)); + assign_map.apply(conn.at(ID::B)); + assign_map.apply(conn.at(ID::S)); + for (const auto& [s_bit, b_chunk] : sorted_pmux_in(conn)) { + h = s_bit.hash_acc(h); + h = b_chunk.hash_acc(h); } - conn = &alt_conn; - } else - if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { - alt_conn = *conn; - assign_map.apply(alt_conn.at(ID::A)); - alt_conn.at(ID::A).sort(); - conn = &alt_conn; - } else - if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { - alt_conn = *conn; - assign_map.apply(alt_conn.at(ID::A)); - alt_conn.at(ID::A).sort_and_unify(); - conn = &alt_conn; - } else - if (cell->type == ID($pmux)) { - alt_conn = *conn; - assign_map.apply(alt_conn.at(ID::A)); - assign_map.apply(alt_conn.at(ID::B)); - assign_map.apply(alt_conn.at(ID::S)); - sort_pmux_conn(alt_conn); - conn = &alt_conn; - } - - for (auto &it : *conn) { - RTLIL::SigSpec sig; - if (cell->output(it.first)) { - if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) { - // For the 'Q' output of state elements, - // use its (* init *) attribute value - sig = initvals(it.second); + h = assign_map(cell->getPort(ID::A)).hash_acc(h); + } else { + std::vector> conns; + for (const auto& conn : cell->connections()) { + conns.push_back(conn); + } + std::sort(conns.begin(), conns.end()); + for (const auto& [port, sig] : conns) { + if (!cell->output(port)) { + h = port.hash_acc(h); + h = assign_map(sig).hash_acc(h); } - else - continue; } - else - sig = assign_map(it.second); - string s = "C " + it.first.str() + "="; - for (auto &chunk : sig.chunks()) { - if (chunk.wire) - s += "{" + chunk.wire->name.str() + " " + - int_to_hash_string(chunk.offset) + " " + - int_to_hash_string(chunk.width) + "}"; - else - s += RTLIL::Const(chunk.data).as_string(); - } - hash_conn_strings.push_back(s + "\n"); + + if (RTLIL::builtin_ff_cell_types().count(cell->type)) + h = initvals(cell->getPort(ID::Q)).hash_acc(h); + } + return h; + } - for (auto &it : cell->parameters) - hash_conn_strings.push_back("P " + it.first.str() + "=" + it.second.as_string() + "\n"); + static Hasher hash_cell_parameters(const RTLIL::Cell *cell, Hasher h) + { + using Paramvec = std::vector>; + Paramvec params; + for (const auto& param : cell->parameters) { + params.push_back(param); + } + std::sort(params.begin(), params.end()); + return hash_ops::hash_acc(params, h); + } - std::sort(hash_conn_strings.begin(), hash_conn_strings.end()); - - for (auto it : hash_conn_strings) - hash_string += it; - - return std::hash{}(hash_string); + Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) + { + h.eat(cell->type); + h = hash_cell_inputs(cell, h); + h = hash_cell_parameters(cell, h); + return h; } bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) @@ -255,18 +257,23 @@ struct OptMergeWorker while (did_something) { std::vector cells; - cells.reserve(module->cells_.size()); - for (auto &it : module->cells_) { - if (!design->selected(module, it.second)) + cells.reserve(module->cells().size()); + for (auto cell : module->cells()) { + if (!design->selected(module, cell)) continue; - if (mode_keepdc && has_dont_care_initval(it.second)) + if (cell->type.in(ID($meminit), ID($meminit_v2), ID($mem), ID($mem_v2))) { + // Ignore those for performance: meminit can have an excessively large port, + // mem can have an excessively large parameter holding the init data continue; - if (ct.cell_known(it.second->type) || (mode_share_all && it.second->known())) - cells.push_back(it.second); + } + if (mode_keepdc && has_dont_care_initval(cell)) + continue; + if (ct.cell_known(cell->type) || (mode_share_all && cell->known())) + cells.push_back(cell); } did_something = false; - dict sharemap; + dict sharemap; for (auto cell : cells) { if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known()) @@ -275,7 +282,7 @@ struct OptMergeWorker if (cell->type == ID($scopeinfo)) continue; - uint64_t hash = hash_cell_parameters_and_connections(cell); + Hasher::hash_t hash = hash_cell_function(cell, Hasher()).yield(); auto r = sharemap.insert(std::make_pair(hash, cell)); if (!r.second) { if (compare_cell_parameters_and_connections(cell, r.first->second)) { From ffc057a89c76f922c4bd5c13383551fc6f9233a3 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 18 Oct 2024 20:39:52 +0200 Subject: [PATCH 02/18] opt_merge: fix the many collisions case --- passes/opt/opt_merge.cc | 45 ++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index d6ed74433..025a6c977 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -254,6 +254,11 @@ struct OptMergeWorker initvals.set(&assign_map, module); bool did_something = true; + bool warned_collisions = false; + // A cell may have to go through a lot of collisions if the hash + // function is performing poorly, but it's a symptom of something bad + // beyond the user's control. + int threshold = 1000; // adjust to taste while (did_something) { std::vector cells; @@ -273,7 +278,10 @@ struct OptMergeWorker } did_something = false; - dict sharemap; + // INVARIANT: All cells associated with the same hash in sharemap + // are distinct as far as compare_cell_parameters_and_connections + // can tell. + std::unordered_multimap sharemap; for (auto cell : cells) { if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known()) @@ -283,21 +291,30 @@ struct OptMergeWorker continue; Hasher::hash_t hash = hash_cell_function(cell, Hasher()).yield(); - auto r = sharemap.insert(std::make_pair(hash, cell)); - if (!r.second) { - if (compare_cell_parameters_and_connections(cell, r.first->second)) { + // Get all cells with matching hashes + auto matching = sharemap.equal_range(hash); + int collisions = 0; + bool found = false; + for (auto it = matching.first; it != matching.second && !found; ++it) { + if (collisions > threshold && !warned_collisions) { + log_warning("High hash collision counts. opt_merge runtime may be excessive.\n" \ + "Please report to maintainers.\n"); + warned_collisions = true; + } + auto other_cell = it->second; + if (compare_cell_parameters_and_connections(cell, other_cell)) { + found = true; if (cell->has_keep_attr()) { - if (r.first->second->has_keep_attr()) + if (other_cell->has_keep_attr()) continue; - std::swap(r.first->second, cell); + std::swap(other_cell, cell); } - did_something = true; - log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), r.first->second->name.c_str()); + log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), other_cell->name.c_str()); for (auto &it : cell->connections()) { if (cell->output(it.first)) { - RTLIL::SigSpec other_sig = r.first->second->getPort(it.first); + RTLIL::SigSpec other_sig = other_cell->getPort(it.first); log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(), log_signal(it.second), log_signal(other_sig)); Const init = initvals(other_sig); @@ -311,8 +328,18 @@ struct OptMergeWorker log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); module->remove(cell); total_count++; + break; + } else { + collisions++; + log_debug(" False hash match: `%s' and `%s'.\n", cell->name.c_str(), other_cell->name.c_str()); } } + if (!did_something) { + // This cell isn't represented yet in the sharemap. + // Either it's the first of its hash, + // or falsely matches all cells in sharemap sharing its hash. + sharemap.insert(std::make_pair(hash, cell)); + } } } From 890374014714532726ceab36d7b4eedb78e119eb Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 12 Nov 2024 14:59:09 +0100 Subject: [PATCH 03/18] opt_merge: switch to unordered_set --- passes/opt/opt_merge.cc | 126 +++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 65 deletions(-) diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index 025a6c977..1992aa871 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -83,7 +83,7 @@ struct OptMergeWorker } } - Hasher hash_cell_inputs(const RTLIL::Cell *cell, Hasher h) + Hasher hash_cell_inputs(const RTLIL::Cell *cell, Hasher h) const { // TODO: when implemented, use celltypes to match: // (builtin || stdcell) && (unary || binary) && symmetrical @@ -94,26 +94,26 @@ struct OptMergeWorker assign_map(cell->getPort(ID::B)) }; std::sort(inputs.begin(), inputs.end()); - h = hash_ops>::hash_acc(inputs, h); - h = assign_map(cell->getPort(ID::Y)).hash_acc(h); + h = hash_ops>::hash_into(inputs, h); + h = assign_map(cell->getPort(ID::Y)).hash_into(h); } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { SigSpec a = assign_map(cell->getPort(ID::A)); a.sort(); - h = a.hash_acc(h); + h = a.hash_into(h); } else if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { SigSpec a = assign_map(cell->getPort(ID::A)); a.sort_and_unify(); - h = a.hash_acc(h); + h = a.hash_into(h); } else if (cell->type == ID($pmux)) { dict conn = cell->connections(); assign_map.apply(conn.at(ID::A)); assign_map.apply(conn.at(ID::B)); assign_map.apply(conn.at(ID::S)); for (const auto& [s_bit, b_chunk] : sorted_pmux_in(conn)) { - h = s_bit.hash_acc(h); - h = b_chunk.hash_acc(h); + h = s_bit.hash_into(h); + h = b_chunk.hash_into(h); } - h = assign_map(cell->getPort(ID::A)).hash_acc(h); + h = assign_map(cell->getPort(ID::A)).hash_into(h); } else { std::vector> conns; for (const auto& conn : cell->connections()) { @@ -122,13 +122,13 @@ struct OptMergeWorker std::sort(conns.begin(), conns.end()); for (const auto& [port, sig] : conns) { if (!cell->output(port)) { - h = port.hash_acc(h); - h = assign_map(sig).hash_acc(h); + h = port.hash_into(h); + h = assign_map(sig).hash_into(h); } } if (RTLIL::builtin_ff_cell_types().count(cell->type)) - h = initvals(cell->getPort(ID::Q)).hash_acc(h); + h = initvals(cell->getPort(ID::Q)).hash_into(h); } return h; @@ -142,10 +142,10 @@ struct OptMergeWorker params.push_back(param); } std::sort(params.begin(), params.end()); - return hash_ops::hash_acc(params, h); + return hash_ops::hash_into(params, h); } - Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) + Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) const { h.eat(cell->type); h = hash_cell_inputs(cell, h); @@ -153,7 +153,7 @@ struct OptMergeWorker return h; } - bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) + bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const { log_assert(cell1 != cell2); if (cell1->type != cell2->type) return false; @@ -254,11 +254,9 @@ struct OptMergeWorker initvals.set(&assign_map, module); bool did_something = true; - bool warned_collisions = false; // A cell may have to go through a lot of collisions if the hash // function is performing poorly, but it's a symptom of something bad // beyond the user's control. - int threshold = 1000; // adjust to taste while (did_something) { std::vector cells; @@ -278,10 +276,29 @@ struct OptMergeWorker } did_something = false; - // INVARIANT: All cells associated with the same hash in sharemap - // are distinct as far as compare_cell_parameters_and_connections - // can tell. - std::unordered_multimap sharemap; + + // We keep a set of known cells. They're hashed with our hash_cell_function + // and compared with our compare_cell_parameters_and_connections. + // Both need to capture OptMergeWorker to access initvals + struct CellPtrHash { + const OptMergeWorker& worker; + CellPtrHash(const OptMergeWorker& w) : worker(w) {} + std::size_t operator()(const Cell* c) const { + return (std::size_t)worker.hash_cell_function(c, Hasher()).yield(); + } + }; + struct CellPtrEqual { + const OptMergeWorker& worker; + CellPtrEqual(const OptMergeWorker& w) : worker(w) {} + bool operator()(const Cell* lhs, const Cell* rhs) const { + return worker.compare_cell_parameters_and_connections(lhs, rhs); + } + }; + std::unordered_set< + RTLIL::Cell*, + CellPtrHash, + CellPtrEqual> known_cells (0, CellPtrHash(*this), CellPtrEqual(*this)); + for (auto cell : cells) { if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known()) @@ -290,55 +307,34 @@ struct OptMergeWorker if (cell->type == ID($scopeinfo)) continue; - Hasher::hash_t hash = hash_cell_function(cell, Hasher()).yield(); - // Get all cells with matching hashes - auto matching = sharemap.equal_range(hash); - int collisions = 0; - bool found = false; - for (auto it = matching.first; it != matching.second && !found; ++it) { - if (collisions > threshold && !warned_collisions) { - log_warning("High hash collision counts. opt_merge runtime may be excessive.\n" \ - "Please report to maintainers.\n"); - warned_collisions = true; + auto [cell_in_map, inserted] = known_cells.insert(cell); + if (!inserted) { + // We've failed to insert since we already have an equivalent cell + Cell* other_cell = *cell_in_map; + if (cell->has_keep_attr()) { + if (other_cell->has_keep_attr()) + continue; + std::swap(other_cell, cell); } - auto other_cell = it->second; - if (compare_cell_parameters_and_connections(cell, other_cell)) { - found = true; - if (cell->has_keep_attr()) { - if (other_cell->has_keep_attr()) - continue; - std::swap(other_cell, cell); - } - did_something = true; - log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), other_cell->name.c_str()); - for (auto &it : cell->connections()) { - if (cell->output(it.first)) { - RTLIL::SigSpec other_sig = other_cell->getPort(it.first); - log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(), - log_signal(it.second), log_signal(other_sig)); - Const init = initvals(other_sig); - initvals.remove_init(it.second); - initvals.remove_init(other_sig); - module->connect(RTLIL::SigSig(it.second, other_sig)); - assign_map.add(it.second, other_sig); - initvals.set_init(other_sig, init); - } + did_something = true; + log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), other_cell->name.c_str()); + for (auto &it : cell->connections()) { + if (cell->output(it.first)) { + RTLIL::SigSpec other_sig = other_cell->getPort(it.first); + log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(), + log_signal(it.second), log_signal(other_sig)); + Const init = initvals(other_sig); + initvals.remove_init(it.second); + initvals.remove_init(other_sig); + module->connect(RTLIL::SigSig(it.second, other_sig)); + assign_map.add(it.second, other_sig); + initvals.set_init(other_sig, init); } - log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); - module->remove(cell); - total_count++; - break; - } else { - collisions++; - log_debug(" False hash match: `%s' and `%s'.\n", cell->name.c_str(), other_cell->name.c_str()); } - } - if (!did_something) { - // This cell isn't represented yet in the sharemap. - // Either it's the first of its hash, - // or falsely matches all cells in sharemap sharing its hash. - sharemap.insert(std::make_pair(hash, cell)); + log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); + module->remove(cell); + total_count++; } } } From 176faae7c94b0dea13052b17d4f210cb19b2d7ba Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Mar 2025 11:43:13 +0100 Subject: [PATCH 04/18] opt_merge: fix trivial binary regression --- passes/opt/opt_merge.cc | 1 - tests/opt/opt_merge_basic.ys | 13 +++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/opt/opt_merge_basic.ys diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index 1992aa871..745b27e87 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -95,7 +95,6 @@ struct OptMergeWorker }; std::sort(inputs.begin(), inputs.end()); h = hash_ops>::hash_into(inputs, h); - h = assign_map(cell->getPort(ID::Y)).hash_into(h); } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { SigSpec a = assign_map(cell->getPort(ID::A)); a.sort(); diff --git a/tests/opt/opt_merge_basic.ys b/tests/opt/opt_merge_basic.ys new file mode 100644 index 000000000..e6336f648 --- /dev/null +++ b/tests/opt/opt_merge_basic.ys @@ -0,0 +1,13 @@ +read_verilog -icells < Date: Mon, 10 Mar 2025 12:30:00 +0100 Subject: [PATCH 05/18] opt_merge: test some unary cells --- tests/opt/opt_merge_basic.ys | 69 +++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/tests/opt/opt_merge_basic.ys b/tests/opt/opt_merge_basic.ys index e6336f648..082fdb0b8 100644 --- a/tests/opt/opt_merge_basic.ys +++ b/tests/opt/opt_merge_basic.ys @@ -1,13 +1,74 @@ -read_verilog -icells < Date: Mon, 10 Mar 2025 12:56:21 +0100 Subject: [PATCH 06/18] opt_merge: test more kinds of cells --- tests/opt/opt_merge_basic.ys | 104 +++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 6 deletions(-) diff --git a/tests/opt/opt_merge_basic.ys b/tests/opt/opt_merge_basic.ys index 082fdb0b8..9de320aaa 100644 --- a/tests/opt/opt_merge_basic.ys +++ b/tests/opt/opt_merge_basic.ys @@ -19,7 +19,7 @@ module top(A, B, C, X, Y); input [7:0] A, B, C; output [7:0] X, Y; assign X = A + B; -assign Y = A + C; +assign Y = A + C; // <- look here endmodule EOT # Reject on a different input @@ -45,10 +45,9 @@ select -assert-count 1 t:$reduce_xor design -reset read_verilog -icells < B; +assign Y = A > B; +endmodule +EOT +# Exercise the general case in hash_cell_inputs - accept +opt_expr +select -assert-count 2 t:$gt +equiv_opt -assert opt_merge +design -load postopt +select -assert-count 1 t:$gt + +design -reset +read_verilog < B; +assign Y = A > C; // <- look here +endmodule +EOT +# Exercise the general case in hash_cell_inputs - reject +opt_expr +select -assert-count 2 t:$gt +opt_merge +select -assert-count 2 t:$gt From 1d773b50a4bc44cd9b431387adf053d1998a4590 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 10 Mar 2025 13:02:49 +0100 Subject: [PATCH 07/18] opt_merge: fix dangling pointers in known_cells when keep attribute is used --- passes/opt/opt_merge.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index 745b27e87..b519d33d6 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -154,7 +154,7 @@ struct OptMergeWorker bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const { - log_assert(cell1 != cell2); + if (cell1 == cell2) return true; if (cell1->type != cell2->type) return false; if (cell1->parameters != cell2->parameters) @@ -313,6 +313,8 @@ struct OptMergeWorker if (cell->has_keep_attr()) { if (other_cell->has_keep_attr()) continue; + known_cells.erase(other_cell); + known_cells.insert(cell); std::swap(other_cell, cell); } From 8877817fa915897e168f604f4c4d40c16d570063 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Sat, 15 Mar 2025 12:14:09 +1300 Subject: [PATCH 08/18] Fix Docs issues for offline pdf builds #4777 Drop svg badges. Even though they were only appearing in the html version, the latexpdf target was still trying to download them, leading to warnings-as-errors in offline builds. --- docs/source/getting_started/installation.rst | 27 +++++-------------- .../extending_yosys/test_suites.rst | 18 +++---------- 2 files changed, 11 insertions(+), 34 deletions(-) diff --git a/docs/source/getting_started/installation.rst b/docs/source/getting_started/installation.rst index 4f31af966..fe96b2314 100644 --- a/docs/source/getting_started/installation.rst +++ b/docs/source/getting_started/installation.rst @@ -34,31 +34,18 @@ Targeted architectures The `OSS CAD Suite`_ releases `nightly builds`_ for the following architectures: -.. only:: html +- **linux-x64** - Most personal Linux based computers +- **darwin-x64** - macOS 12 or later with Intel CPU +- **darwin-arm64** - macOS 12 or later with M1/M2 CPU +- **windows-x64** - Targeted for Windows 10 and 11 +- **linux-arm64** - Devices such as Raspberry Pi with 64bit OS - - linux-x64 |linux-x64| - - Most personal Linux based computers - - - darwin-x64 |darwin-x64| - - macOS 12 or later with Intel CPU - - - darwin-arm64 |darwin-arm64| - - macOS 12 or later with M1/M2 CPU - - - windows-x64 |windows-x64| - - Targeted for Windows 10 and 11 - - - linux-arm64 |linux-arm64| +For more information about the targeted architectures, and the current build +status, check the `OSS CAD Suite`_ git repository. .. _OSS CAD Suite: https://github.com/YosysHQ/oss-cad-suite-build .. _nightly builds: https://github.com/YosysHQ/oss-cad-suite-build/releases/latest -.. |linux-x64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/linux-x64.yml/badge.svg -.. |darwin-x64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/darwin-x64.yml/badge.svg -.. |darwin-arm64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/darwin-arm64.yml/badge.svg -.. |windows-x64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/windows-x64.yml/badge.svg -.. |linux-arm64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/linux-arm64.yml/badge.svg - Building from source ~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/source/yosys_internals/extending_yosys/test_suites.rst b/docs/source/yosys_internals/extending_yosys/test_suites.rst index 264b97ce1..3e5f45b94 100644 --- a/docs/source/yosys_internals/extending_yosys/test_suites.rst +++ b/docs/source/yosys_internals/extending_yosys/test_suites.rst @@ -6,22 +6,12 @@ Testing Yosys Automatic testing ----------------- -.. only:: html - - The `Yosys Git repo`_ has automatic testing of builds and running of the - included test suite on the following platforms: - - - Ubuntu |test-linux| - - macOS |test-macos| +The `Yosys Git repo`_ has automatic testing of builds and running of the +included test suite on both Ubuntu and macOS, as well as across range of +compiler versions. For up to date information, including OS versions, refer to +`the git actions page`_. .. _Yosys Git repo: https://github.com/YosysHQ/yosys - -.. |test-linux| image:: https://github.com/YosysHQ/yosys/actions/workflows/test-linux.yml/badge.svg?branch=main -.. |test-macos| image:: https://github.com/YosysHQ/yosys/actions/workflows/test-macos.yml/badge.svg?branch=main - -For up to date information, including OS versions, refer to `the git actions -page`_. - .. _the git actions page: https://github.com/YosysHQ/yosys/actions .. todo:: are unit tests currently working From 39a1623ac01f5c3d73d26fd1967d00d8ebe23b8a Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Tue, 18 Mar 2025 03:57:17 +1300 Subject: [PATCH 09/18] Docs: Don't READTHEDOCS on local builds --- docs/source/conf.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 607e088da..d97fb99a7 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -16,15 +16,17 @@ html_theme_options: dict[str] = { "source_branch": "main", "source_directory": "docs/source/", } +html_context: dict[str] = {} # try to fix the readthedocs detection -html_context: dict[str] = { - "READTHEDOCS": True, - "display_github": True, - "github_user": "YosysHQ", - "github_repo": "yosys", - "slug": "yosys", -} +if os.getenv("READTHEDOCS"): + html_context.update({ + "READTHEDOCS": True, + "display_github": True, + "github_user": "YosysHQ", + "github_repo": "yosys", + "slug": "yosys", + }) # override source_branch if not main git_slug = os.getenv("READTHEDOCS_VERSION_NAME") From 013206de391d548f1889670e35fc9da6890d6469 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 20 Mar 2025 14:23:37 +1300 Subject: [PATCH 10/18] Fix #3898 (again) --- libs/fst/00_PATCH_i386_endian.patch | 14 ++++++++++++++ libs/fst/00_UPDATE.sh | 1 + libs/fst/fstapi.cc | 5 ++++- 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 libs/fst/00_PATCH_i386_endian.patch diff --git a/libs/fst/00_PATCH_i386_endian.patch b/libs/fst/00_PATCH_i386_endian.patch new file mode 100644 index 000000000..3857f1fdf --- /dev/null +++ b/libs/fst/00_PATCH_i386_endian.patch @@ -0,0 +1,14 @@ +--- fstapi.cc ++++ fstapi.cc +@@ -4723,7 +4723,10 @@ if(gzread_pass_status) + hdr_incomplete = (xc->start_time == 0) && (xc->end_time == 0); + + fstFread(&dcheck, 8, 1, xc->f); +- xc->double_endian_match = (dcheck == FST_DOUBLE_ENDTEST); ++ /* ++ * Yosys patch: Fix double endian check for i386 targets built in modern gcc ++ */ ++ xc->double_endian_match = (dcheck == (double)FST_DOUBLE_ENDTEST); + if(!xc->double_endian_match) + { + union { diff --git a/libs/fst/00_UPDATE.sh b/libs/fst/00_UPDATE.sh index 66a0fd8df..302e187f2 100755 --- a/libs/fst/00_UPDATE.sh +++ b/libs/fst/00_UPDATE.sh @@ -19,3 +19,4 @@ patch -p0 < 00_PATCH_win_zlib.patch patch -p0 < 00_PATCH_win_io.patch patch -p1 < 00_PATCH_strict_alignment.patch patch -p0 < 00_PATCH_wx_len_overread.patch +patch -p0 < 00_PATCH_i386_endian.patch diff --git a/libs/fst/fstapi.cc b/libs/fst/fstapi.cc index ab3c54469..2463e6f15 100644 --- a/libs/fst/fstapi.cc +++ b/libs/fst/fstapi.cc @@ -4723,7 +4723,10 @@ if(gzread_pass_status) hdr_incomplete = (xc->start_time == 0) && (xc->end_time == 0); fstFread(&dcheck, 8, 1, xc->f); - xc->double_endian_match = (dcheck == FST_DOUBLE_ENDTEST); + /* + * Yosys patch: Fix double endian check for i386 targets built in modern gcc + */ + xc->double_endian_match = (dcheck == (double)FST_DOUBLE_ENDTEST); if(!xc->double_endian_match) { union { From 452dd1b74b5cee91e618043c6211745569adc339 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:19:22 +1300 Subject: [PATCH 11/18] abstract: Assign default to value Fix `-Wmaybe-uninitialized` on line 43 and 44. --- passes/cmds/abstract.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 1e67dcd88..513cd8ecc 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -45,7 +45,7 @@ struct Slice { } static int parse_index(const char *begin, const char *end, const std::string &slice) { - int value; + int value = 0; auto result = std::from_chars(begin, end, value, 10); if (result.ptr != end || result.ptr == begin) syntax_error(slice); From d704ca8019455799b2244fa31178439eff02a529 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:20:36 +1300 Subject: [PATCH 12/18] abstract: Fix indentation --- passes/cmds/abstract.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 513cd8ecc..d4eb22ca5 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -47,7 +47,7 @@ struct Slice { static int parse_index(const char *begin, const char *end, const std::string &slice) { int value = 0; auto result = std::from_chars(begin, end, value, 10); - if (result.ptr != end || result.ptr == begin) + if (result.ptr != end || result.ptr == begin) syntax_error(slice); return value; } From 2b67ad78bf533eec1134973aef4489823b7738b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber?= Date: Fri, 14 Mar 2025 21:23:33 +0100 Subject: [PATCH 13/18] mk: Fall back to cp if rsync is not available --- Makefile | 10 ++++++++-- docs/source/_images/Makefile | 8 +++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 89ffaef66..adaf57d0f 100644 --- a/Makefile +++ b/Makefile @@ -114,6 +114,12 @@ BISON ?= bison STRIP ?= strip AWK ?= awk +ifneq ($(shell :; command -v rsync),) +RSYNC_CP ?= rsync -rc +else +RSYNC_CP ?= cp -ru +endif + ifeq ($(OS), Darwin) PLUGIN_LINKFLAGS += -undefined dynamic_lookup LINKFLAGS += -rdynamic @@ -1005,13 +1011,13 @@ docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) $(Q) mkdir -p docs/source/cmd $(Q) mkdir -p temp/docs/source/cmd $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' - $(Q) rsync -rc temp/docs/source/cmd docs/source + $(Q) $(RSYNC_CP) temp/docs/source/cmd docs/source $(Q) rm -rf temp docs/source/cell/word_add.rst: $(TARGETS) $(EXTRA_TARGETS) $(Q) mkdir -p docs/source/cell $(Q) mkdir -p temp/docs/source/cell $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-cells-manual' - $(Q) rsync -rc temp/docs/source/cell docs/source + $(Q) $(RSYNC_CP) temp/docs/source/cell docs/source $(Q) rm -rf temp docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) diff --git a/docs/source/_images/Makefile b/docs/source/_images/Makefile index cc00050f1..71f52c578 100644 --- a/docs/source/_images/Makefile +++ b/docs/source/_images/Makefile @@ -3,6 +3,12 @@ all: examples all_tex # set a fake time in pdf generation to prevent unnecessary differences in output FAKETIME := TZ='Z' faketime -f '2022-01-01 00:00:00 x0,001' +ifneq ($(shell :; command -v rsync),) +RSYNC_CP ?= rsync -t +else +RSYNC_CP ?= cp -a +endif + # find all code example makefiles .PHONY: examples CODE_EXAMPLES := ../code_examples/*/Makefile @@ -19,7 +25,7 @@ FORCE: ../%/Makefile: FORCE @make -C $(@D) dots @mkdir -p $* - @find $(@D) -name *.dot -exec rsync -t {} $* \; + @find $(@D) -name *.dot -exec $(RSYNC_CP) {} $* \; # find and build all tex files .PHONY: all_tex From 59602740eea8662e30e245b267b59e47c158fa48 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 00:23:05 +0000 Subject: [PATCH 14/18] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index eeb0bf424..f9f09d501 100644 --- a/Makefile +++ b/Makefile @@ -159,7 +159,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.51+46 +YOSYS_VER := 0.51+79 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) From 04bbd4e7e2833060d6eb13d7749a391ccc799c5d Mon Sep 17 00:00:00 2001 From: Scott Ashcroft Date: Tue, 25 Mar 2025 13:08:49 +0000 Subject: [PATCH 15/18] Make all vector-size related integer params in $print sim model signed This fixes iverilog crashes on 32-bit, similar to 95944eb for $mem. --- techlibs/common/simlib.v | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index 028e4e5a9..6e39aa60a 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -2224,10 +2224,10 @@ module \$print (EN, TRG, ARGS); parameter PRIORITY = 0; parameter FORMAT = ""; -parameter ARGS_WIDTH = 0; +parameter signed ARGS_WIDTH = 0; parameter TRG_ENABLE = 1; -parameter TRG_WIDTH = 0; +parameter signed TRG_WIDTH = 0; parameter TRG_POLARITY = 0; input EN; From 518986d45c507f303c0d7b2230e5cf54ab6be293 Mon Sep 17 00:00:00 2001 From: Scott Ashcroft Date: Tue, 25 Mar 2025 13:12:04 +0000 Subject: [PATCH 16/18] Make cxxrtl tests work on 32-bit by using __builtin_clzll when needed --- tests/cxxrtl/test_value_fuzz.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/cxxrtl/test_value_fuzz.cc b/tests/cxxrtl/test_value_fuzz.cc index 7e8fbb0a9..a77120136 100644 --- a/tests/cxxrtl/test_value_fuzz.cc +++ b/tests/cxxrtl/test_value_fuzz.cc @@ -241,7 +241,10 @@ struct CtlzTest { if (a == 0) return bits; - return __builtin_clzl(a) - (64 - bits); + if (sizeof(long) == 4) + return __builtin_clzll(a) - (64 - bits); + else + return __builtin_clzl(a) - (64 - bits); } template From 4991ed9d4bd610fed7e1a969c3957ea087bb62ba Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Tue, 25 Mar 2025 18:10:47 +0100 Subject: [PATCH 17/18] cutpoint: fix typo --- passes/sat/cutpoint.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index bca6a5ec6..88f995dda 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -34,8 +34,8 @@ struct CutpointPass : public Pass { log("This command adds formal cut points to the design.\n"); log("\n"); log(" -undef\n"); - log(" set cupoint nets to undef (x). the default behavior is to create a\n"); - log(" $anyseq cell and drive the cutpoint net from that\n"); + log(" set cutpoint nets to undef (x). the default behavior is to create\n"); + log(" an $anyseq cell and drive the cutpoint net from that\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override From d3aec12fe9d17935170a739a7ac3c18207c5bcc6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 00:22:20 +0000 Subject: [PATCH 18/18] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f9f09d501..5745d1d0c 100644 --- a/Makefile +++ b/Makefile @@ -159,7 +159,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.51+79 +YOSYS_VER := 0.51+85 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2)