From a180a0003f8a8ff07f30fbdc20e9880cb4c56711 Mon Sep 17 00:00:00 2001 From: nella Date: Wed, 11 Mar 2026 11:31:40 +0100 Subject: [PATCH 01/15] impl csa tree. --- passes/opt/Makefile.inc | 1 + passes/opt/csa_tree.cc | 357 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 358 insertions(+) create mode 100644 passes/opt/csa_tree.cc diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 5dee824ff..42c17d6c0 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -24,6 +24,7 @@ OBJS += passes/opt/opt_ffinv.o OBJS += passes/opt/pmux2shiftx.o OBJS += passes/opt/muxpack.o OBJS += passes/opt/opt_balance_tree.o +OBJS += passes/opt/csa_tree.o OBJS += passes/opt/peepopt.o GENFILES += passes/opt/peepopt_pm.h diff --git a/passes/opt/csa_tree.cc b/passes/opt/csa_tree.cc new file mode 100644 index 000000000..2328af3d8 --- /dev/null +++ b/passes/opt/csa_tree.cc @@ -0,0 +1,357 @@ +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct CsaTreeWorker +{ + RTLIL::Module *module; + SigMap sigmap; + int min_operands; + + dict sig_to_driver; + dict cell_fanout; + pool consumed; + + int stat_trees = 0; + int stat_fa_cells = 0; + int stat_removed_cells = 0; + + CsaTreeWorker(RTLIL::Module *module, int min_operands) : + module(module), sigmap(module), min_operands(min_operands) {} + + void build_maps() + { + dict sig_consumers; + + for (auto cell : module->cells()) + { + if (cell->type.in(ID($add), ID($sub))) + { + RTLIL::SigSpec y = sigmap(cell->getPort(ID::Y)); + for (auto bit : y) + if (bit.wire != nullptr) + sig_to_driver[bit] = cell; + } + + for (auto &conn : cell->connections()) + { + if (cell->input(conn.first)) + { + for (auto bit : sigmap(conn.second)) + if (bit.wire != nullptr) + sig_consumers[bit]++; + } + } + } + + for (auto wire : module->wires()) + if (wire->port_output) + for (auto bit : sigmap(wire)) + if (bit.wire != nullptr) + sig_consumers[bit]++; + + for (auto cell : module->cells()) + { + if (!cell->type.in(ID($add), ID($sub))) + continue; + int fo = 0; + for (auto bit : sigmap(cell->getPort(ID::Y))) + if (bit.wire != nullptr) + fo = std::max(fo, sig_consumers.count(bit) ? sig_consumers.at(bit) : 0); + cell_fanout[cell] = fo; + } + } + + struct Operand { + RTLIL::SigSpec sig; + bool is_signed; + bool do_subtract; + }; + + bool can_absorb(RTLIL::Cell *cell) + { + if (cell == nullptr) + return false; + if (!cell->type.in(ID($add), ID($sub))) + return false; + if (consumed.count(cell)) + return false; + if (cell_fanout.count(cell) ? cell_fanout.at(cell) != 1 : true) + return false; + return true; + } + + RTLIL::Cell *get_driver(RTLIL::SigSpec sig) + { + sig = sigmap(sig); + if (sig.empty()) + return nullptr; + + RTLIL::Cell *driver = nullptr; + for (auto bit : sig) + { + if (bit.wire == nullptr) + continue; + auto it = sig_to_driver.find(bit); + if (it == sig_to_driver.end()) + return nullptr; + if (driver == nullptr) + driver = it->second; + else if (driver != it->second) + return nullptr; // mixed + } + return driver; + } + + void collect_operands( + RTLIL::Cell *cell, + bool negate, + std::vector &operands, + std::vector &tree_cells + ) { + tree_cells.push_back(cell); + consumed.insert(cell); + + bool a_signed = cell->getParam(ID::A_SIGNED).as_bool(); + bool b_signed = cell->getParam(ID::B_SIGNED).as_bool(); + bool is_sub = (cell->type == ID($sub)); + + RTLIL::SigSpec sig_a = cell->getPort(ID::A); + RTLIL::SigSpec sig_b = cell->getPort(ID::B); + + RTLIL::Cell *driver_a = get_driver(sig_a); + if (can_absorb(driver_a)) { + collect_operands(driver_a, negate, operands, tree_cells); + } else { + operands.push_back({sig_a, a_signed, negate}); + } + + bool b_negate = negate ^ is_sub; + RTLIL::Cell *driver_b = get_driver(sig_b); + if (can_absorb(driver_b)) { + collect_operands(driver_b, b_negate, operands, tree_cells); + } else { + operands.push_back({sig_b, b_signed, b_negate}); + } + } + + void create_fa( + RTLIL::SigSpec a, + RTLIL::SigSpec b, + RTLIL::SigSpec c, + int width, + RTLIL::SigSpec &sum_out, + RTLIL::SigSpec &carry_out + ) { + RTLIL::Wire *w_sum = module->addWire(NEW_ID, width); + RTLIL::Wire *w_carry = module->addWire(NEW_ID, width); + + RTLIL::Cell *fa = module->addCell(NEW_ID, ID($fa)); + fa->setParam(ID::WIDTH, width); + fa->setPort(ID::A, a); + fa->setPort(ID::B, b); + fa->setPort(ID::C, c); + fa->setPort(ID::Y, w_sum); + fa->setPort(ID::X, w_carry); + + sum_out = w_sum; + carry_out = w_carry; + stat_fa_cells++; + } + + RTLIL::SigSpec extend_to(RTLIL::SigSpec sig, bool is_signed, int target_width) + { + if (GetSize(sig) >= target_width) + return sig.extract(0, target_width); + + RTLIL::SigSpec result = sig; + RTLIL::SigBit pad = is_signed ? sig[GetSize(sig) - 1] : RTLIL::S0; + while (GetSize(result) < target_width) + result.append(pad); + return result; + } + + RTLIL::SigSpec build_csa_tree(std::vector &operands, int output_width) + { + int width = output_width; + std::vector summands; + int sub_count = 0; + + for (auto &op : operands) + { + RTLIL::SigSpec sig = extend_to(op.sig, op.is_signed, width); + + if (op.do_subtract) { + sig = module->Not(NEW_ID, sig); + sub_count++; + } + + summands.push_back(sig); + } + + if (sub_count > 0) { + RTLIL::Const correction(sub_count, width); + summands.push_back(RTLIL::SigSpec(correction)); + } + + if (summands.empty()) + return RTLIL::SigSpec(0, width); + + if (summands.size() == 1) + return summands[0]; + + if (summands.size() == 2) { + RTLIL::Wire *result = module->addWire(NEW_ID, width); + module->addAdd(NEW_ID, summands[0], summands[1], result); + return result; + } + + while (summands.size() > 2) + { + std::vector next; + int i = 0; + + while (i + 2 < (int)summands.size()) + { + RTLIL::SigSpec a = summands[i]; + RTLIL::SigSpec b = summands[i + 1]; + RTLIL::SigSpec c = summands[i + 2]; + + RTLIL::SigSpec sum, carry; + create_fa(a, b, c, width, sum, carry); + + RTLIL::SigSpec carry_shifted; + carry_shifted.append(RTLIL::S0); + carry_shifted.append(carry.extract(0, width - 1)); + + next.push_back(sum); + next.push_back(carry_shifted); + i += 3; + } + + while (i < (int)summands.size()) + next.push_back(summands[i++]); + + summands.swap(next); + } + + RTLIL::Wire *result = module->addWire(NEW_ID, width); + module->addAdd(NEW_ID, summands[0], summands[1], result); + return result; + } + + void run() + { + build_maps(); + + std::vector roots; + for (auto cell : module->selected_cells()) + if (cell->type.in(ID($add), ID($sub))) + roots.push_back(cell); + + std::sort(roots.begin(), roots.end(), + [](RTLIL::Cell *a, RTLIL::Cell *b) { + return a->name < b->name; + }); + + std::sort(roots.begin(), roots.end(), + [&](RTLIL::Cell *a, RTLIL::Cell *b) { + return (cell_fanout.count(a) ? cell_fanout.at(a) : 0) > + (cell_fanout.count(b) ? cell_fanout.at(b) : 0); + }); + + for (auto root : roots) + { + if (consumed.count(root)) + continue; + + std::vector operands; + std::vector tree_cells; + + collect_operands(root, false, operands, tree_cells); + + if ((int)operands.size() < min_operands) { + for (auto c : tree_cells) + consumed.erase(c); + continue; + } + + int output_width = root->getParam(ID::Y_WIDTH).as_int(); + + log(" Found adder tree rooted at %s with %d operands (depth %d cells)\n", + log_id(root), (int)operands.size(), (int)tree_cells.size()); + + RTLIL::SigSpec new_output = build_csa_tree(operands, output_width); + RTLIL::SigSpec old_output = root->getPort(ID::Y); + module->connect(old_output, new_output); + + for (auto c : tree_cells) { + module->remove(c); + stat_removed_cells++; + } + + stat_trees++; + } + } +}; + +struct CsaTreePass : public Pass +{ + CsaTreePass() : Pass("csa_tree", + "convert adder chains to carry-save adder trees") {} + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" csa_tree [options] [selection]\n"); + log("\n"); + log("This pass converts chains of $add/$sub cells into carry-save adder trees using\n"); + log("$fa (full adder / 3:2 compressor) cells to reduce the critical path depth of\n"); + log("multi-operand addition.\n"); + log("\n"); + log("For N operands of width W, the critical path is reduced from\n"); + log("O(N * log W) to O(log_1.5(N) + log W).\n"); + log("\n"); + log(" -min_operands N\n"); + log(" Minimum number of operands to trigger CSA tree construction.\n"); + log(" Default: 3. Values below 3 are clamped to 3.\n"); + log("\n"); + } + + void execute(std::vector args, RTLIL::Design *design) override + { + int min_operands = 3; + + log_header(design, "Executing CSA_TREE pass (carry-save adder tree optimization).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-min_operands" && argidx + 1 < args.size()) { + min_operands = std::max(3, atoi(args[++argidx].c_str())); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + log("Processing module %s...\n", log_id(module)); + + CsaTreeWorker worker(module, min_operands); + worker.run(); + + if (worker.stat_trees > 0) + log(" Converted %d adder tree(s): created %d $fa cells, " + "removed %d $add/$sub cells.\n", + worker.stat_trees, worker.stat_fa_cells, + worker.stat_removed_cells); + } + } +} CsaTreePass; + +PRIVATE_NAMESPACE_END From 728403d1eb230d2be366c5cc7a893c166f4a2b21 Mon Sep 17 00:00:00 2001 From: nella Date: Fri, 13 Mar 2026 11:06:48 +0100 Subject: [PATCH 02/15] better balancing. --- passes/opt/csa_tree.cc | 497 ++++++++++++++++++++--------------------- 1 file changed, 242 insertions(+), 255 deletions(-) diff --git a/passes/opt/csa_tree.cc b/passes/opt/csa_tree.cc index 2328af3d8..92f744a9f 100644 --- a/passes/opt/csa_tree.cc +++ b/passes/opt/csa_tree.cc @@ -1,355 +1,342 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include + USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct CsaTreeWorker { - RTLIL::Module *module; + Module *module; SigMap sigmap; - int min_operands; - dict sig_to_driver; - dict cell_fanout; - pool consumed; + dict> bit_consumers; + dict fanout; + pool all_adds; - int stat_trees = 0; - int stat_fa_cells = 0; - int stat_removed_cells = 0; + CsaTreeWorker(Module *module) : module(module), sigmap(module) {} - CsaTreeWorker(RTLIL::Module *module, int min_operands) : - module(module), sigmap(module), min_operands(min_operands) {} + struct DepthSig { + SigSpec sig; + int depth; + }; - void build_maps() + void find_adds() { - dict sig_consumers; - for (auto cell : module->cells()) - { - if (cell->type.in(ID($add), ID($sub))) - { - RTLIL::SigSpec y = sigmap(cell->getPort(ID::Y)); - for (auto bit : y) - if (bit.wire != nullptr) - sig_to_driver[bit] = cell; - } + if (cell->type == ID($add)) + all_adds.insert(cell); + } + void build_fanout_map() + { + for (auto cell : module->cells()) for (auto &conn : cell->connections()) - { if (cell->input(conn.first)) - { for (auto bit : sigmap(conn.second)) - if (bit.wire != nullptr) - sig_consumers[bit]++; - } - } - } + bit_consumers[bit].insert(cell); + + for (auto &pair : bit_consumers) + fanout[pair.first] = pair.second.size(); for (auto wire : module->wires()) if (wire->port_output) - for (auto bit : sigmap(wire)) - if (bit.wire != nullptr) - sig_consumers[bit]++; + for (auto bit : sigmap(SigSpec(wire))) + fanout[bit]++; + } - for (auto cell : module->cells()) - { - if (!cell->type.in(ID($add), ID($sub))) - continue; - int fo = 0; - for (auto bit : sigmap(cell->getPort(ID::Y))) - if (bit.wire != nullptr) - fo = std::max(fo, sig_consumers.count(bit) ? sig_consumers.at(bit) : 0); - cell_fanout[cell] = fo; + Cell*single_add_consumer(SigSpec sig) + { + Cell*consumer = nullptr; + + for (auto bit : sig) { + if (!fanout.count(bit) || fanout[bit] != 1) + return nullptr; + if (!bit_consumers.count(bit) || bit_consumers[bit].size() != 1) + return nullptr; + + Cell* c = *bit_consumers[bit].begin(); + if (!all_adds.count(c)) + return nullptr; + + if (consumer == nullptr) + consumer = c; + else if (consumer != c) + return nullptr; } + + return consumer; + } + + dict find_add_parents() + { + dict parent_of; + + for (auto cell : all_adds) { + SigSpec y = sigmap(cell->getPort(ID::Y)); + Cell* consumer = single_add_consumer(y); + if (consumer != nullptr && consumer != cell) + parent_of[cell] = consumer; + } + + return parent_of; + } + + pool collect_chain(Cell* root, const dict> &children_of) + { + pool chain; + std::queue worklist; + worklist.push(root); + + while (!worklist.empty()) { + Cell* cur = worklist.front(); + worklist.pop(); + + if (chain.count(cur)) + continue; + chain.insert(cur); + + if (children_of.count(cur)) + for (auto child : children_of.at(cur)) + worklist.push(child); + } + + return chain; + } + + bool is_chain_internal(SigSpec sig, const pool &chain_y_bits) + { + for (auto bit : sig) + if (chain_y_bits.count(bit)) + return true; + return false; + } + + pool collect_chain_outputs(const pool &chain) + { + pool bits; + for (auto cell : chain) + for (auto bit : sigmap(cell->getPort(ID::Y))) + bits.insert(bit); + return bits; } struct Operand { - RTLIL::SigSpec sig; + SigSpec sig; bool is_signed; - bool do_subtract; }; - bool can_absorb(RTLIL::Cell *cell) + std::vector collect_leaf_operands(const pool &chain, const pool &chain_y_bits) { - if (cell == nullptr) - return false; - if (!cell->type.in(ID($add), ID($sub))) - return false; - if (consumed.count(cell)) - return false; - if (cell_fanout.count(cell) ? cell_fanout.at(cell) != 1 : true) - return false; - return true; + std::vector operands; + + for (auto cell : chain) { + SigSpec a = sigmap(cell->getPort(ID::A)); + SigSpec b = sigmap(cell->getPort(ID::B)); + bool a_signed = cell->getParam(ID::A_SIGNED).as_bool(); + bool b_signed = cell->getParam(ID::B_SIGNED).as_bool(); + + if (!is_chain_internal(a, chain_y_bits)) + operands.push_back({a, a_signed}); + if (!is_chain_internal(b, chain_y_bits)) + operands.push_back({b, b_signed}); + } + + return operands; } - RTLIL::Cell *get_driver(RTLIL::SigSpec sig) + SigSpec extend_to(SigSpec sig, bool is_signed, int width) { - sig = sigmap(sig); - if (sig.empty()) - return nullptr; - - RTLIL::Cell *driver = nullptr; - for (auto bit : sig) - { - if (bit.wire == nullptr) - continue; - auto it = sig_to_driver.find(bit); - if (it == sig_to_driver.end()) - return nullptr; - if (driver == nullptr) - driver = it->second; - else if (driver != it->second) - return nullptr; // mixed + if (GetSize(sig) < width) { + SigBit pad = (is_signed && GetSize(sig) > 0) ? sig[GetSize(sig) - 1] : State::S0; + sig.append(SigSpec(pad, width - GetSize(sig))); } - return driver; + if (GetSize(sig) > width) + sig = sig.extract(0, width); + return sig; } - void collect_operands( - RTLIL::Cell *cell, - bool negate, - std::vector &operands, - std::vector &tree_cells - ) { - tree_cells.push_back(cell); - consumed.insert(cell); + std::pair emit_fa(SigSpec a, SigSpec b, SigSpec c, int width) + { + SigSpec sum = module->addWire(NEW_ID, width); + SigSpec cout = module->addWire(NEW_ID, width); - bool a_signed = cell->getParam(ID::A_SIGNED).as_bool(); - bool b_signed = cell->getParam(ID::B_SIGNED).as_bool(); - bool is_sub = (cell->type == ID($sub)); - - RTLIL::SigSpec sig_a = cell->getPort(ID::A); - RTLIL::SigSpec sig_b = cell->getPort(ID::B); - - RTLIL::Cell *driver_a = get_driver(sig_a); - if (can_absorb(driver_a)) { - collect_operands(driver_a, negate, operands, tree_cells); - } else { - operands.push_back({sig_a, a_signed, negate}); - } - - bool b_negate = negate ^ is_sub; - RTLIL::Cell *driver_b = get_driver(sig_b); - if (can_absorb(driver_b)) { - collect_operands(driver_b, b_negate, operands, tree_cells); - } else { - operands.push_back({sig_b, b_signed, b_negate}); - } - } - - void create_fa( - RTLIL::SigSpec a, - RTLIL::SigSpec b, - RTLIL::SigSpec c, - int width, - RTLIL::SigSpec &sum_out, - RTLIL::SigSpec &carry_out - ) { - RTLIL::Wire *w_sum = module->addWire(NEW_ID, width); - RTLIL::Wire *w_carry = module->addWire(NEW_ID, width); - - RTLIL::Cell *fa = module->addCell(NEW_ID, ID($fa)); + Cell* fa = module->addCell(NEW_ID, ID($fa)); fa->setParam(ID::WIDTH, width); fa->setPort(ID::A, a); fa->setPort(ID::B, b); fa->setPort(ID::C, c); - fa->setPort(ID::Y, w_sum); - fa->setPort(ID::X, w_carry); + fa->setPort(ID::X, cout); + fa->setPort(ID::Y, sum); - sum_out = w_sum; - carry_out = w_carry; - stat_fa_cells++; + SigSpec carry_shifted; + carry_shifted.append(State::S0); + carry_shifted.append(cout.extract(0, width - 1)); + + return {sum, carry_shifted}; } - RTLIL::SigSpec extend_to(RTLIL::SigSpec sig, bool is_signed, int target_width) + std::pair build_wallace_tree(std::vector &operands, int width, int &fa_count) { - if (GetSize(sig) >= target_width) - return sig.extract(0, target_width); + std::vector ops; + for (auto &s : operands) + ops.push_back({s, 0}); - RTLIL::SigSpec result = sig; - RTLIL::SigBit pad = is_signed ? sig[GetSize(sig) - 1] : RTLIL::S0; - while (GetSize(result) < target_width) - result.append(pad); - return result; - } + fa_count = 0; + int level = 0; - RTLIL::SigSpec build_csa_tree(std::vector &operands, int output_width) - { - int width = output_width; - std::vector summands; - int sub_count = 0; - - for (auto &op : operands) + while (ops.size() > 2) { - RTLIL::SigSpec sig = extend_to(op.sig, op.is_signed, width); - - if (op.do_subtract) { - sig = module->Not(NEW_ID, sig); - sub_count++; + std::vector ready, waiting; + for (auto &op : ops) { + if (op.depth <= level) + ready.push_back(op); + else + waiting.push_back(op); } - summands.push_back(sig); - } + if (ready.size() < 3) { + level++; + log_assert(level <= 100); + continue; + } - if (sub_count > 0) { - RTLIL::Const correction(sub_count, width); - summands.push_back(RTLIL::SigSpec(correction)); - } - - if (summands.empty()) - return RTLIL::SigSpec(0, width); - - if (summands.size() == 1) - return summands[0]; - - if (summands.size() == 2) { - RTLIL::Wire *result = module->addWire(NEW_ID, width); - module->addAdd(NEW_ID, summands[0], summands[1], result); - return result; - } - - while (summands.size() > 2) - { - std::vector next; - int i = 0; - - while (i + 2 < (int)summands.size()) - { - RTLIL::SigSpec a = summands[i]; - RTLIL::SigSpec b = summands[i + 1]; - RTLIL::SigSpec c = summands[i + 2]; - - RTLIL::SigSpec sum, carry; - create_fa(a, b, c, width, sum, carry); - - RTLIL::SigSpec carry_shifted; - carry_shifted.append(RTLIL::S0); - carry_shifted.append(carry.extract(0, width - 1)); - - next.push_back(sum); - next.push_back(carry_shifted); + std::vector next; + size_t i = 0; + while (i + 2 < ready.size()) { + auto [sum, carry] = emit_fa(ready[i].sig, ready[i+1].sig, ready[i+2].sig, width); + int d = std::max({ready[i].depth, ready[i+1].depth, ready[i+2].depth}) + 1; + next.push_back({sum, d}); + next.push_back({carry, d}); + fa_count++; i += 3; } - while (i < (int)summands.size()) - next.push_back(summands[i++]); + for (; i < ready.size(); i++) + next.push_back(ready[i]); - summands.swap(next); + for (auto &op : waiting) + next.push_back(op); + + ops = std::move(next); + level++; + log_assert(level <= 100); } - RTLIL::Wire *result = module->addWire(NEW_ID, width); - module->addAdd(NEW_ID, summands[0], summands[1], result); - return result; + log_assert(ops.size() == 2); + + int max_depth = std::max(ops[0].depth, ops[1].depth); + log(" Tree depth: %d FA levels + 1 final add\n", max_depth); + + return {ops[0].sig, ops[1].sig}; + } + + void emit_final_add(SigSpec a, SigSpec b, SigSpec y, int width) + { + Cell* add = module->addCell(NEW_ID, ID($add)); + add->setParam(ID::A_SIGNED, false); + add->setParam(ID::B_SIGNED, false); + add->setParam(ID::A_WIDTH, width); + add->setParam(ID::B_WIDTH, width); + add->setParam(ID::Y_WIDTH, width); + add->setPort(ID::A, a); + add->setPort(ID::B, b); + add->setPort(ID::Y, y); } void run() { - build_maps(); + find_adds(); + if (all_adds.empty()) + return; - std::vector roots; - for (auto cell : module->selected_cells()) - if (cell->type.in(ID($add), ID($sub))) - roots.push_back(cell); + build_fanout_map(); - std::sort(roots.begin(), roots.end(), - [](RTLIL::Cell *a, RTLIL::Cell *b) { - return a->name < b->name; - }); + auto parent_of = find_add_parents(); - std::sort(roots.begin(), roots.end(), - [&](RTLIL::Cell *a, RTLIL::Cell *b) { - return (cell_fanout.count(a) ? cell_fanout.at(a) : 0) > - (cell_fanout.count(b) ? cell_fanout.at(b) : 0); - }); + pool has_parent; + dict> children_of; + for (auto &pair : parent_of) { + has_parent.insert(pair.first); + children_of[pair.second].insert(pair.first); + } - for (auto root : roots) + pool processed; + + for (auto root : all_adds) { - if (consumed.count(root)) + if (has_parent.count(root)) + continue; + if (processed.count(root)) continue; - std::vector operands; - std::vector tree_cells; - - collect_operands(root, false, operands, tree_cells); - - if ((int)operands.size() < min_operands) { - for (auto c : tree_cells) - consumed.erase(c); + pool chain = collect_chain(root, children_of); + if (chain.size() < 2) continue; - } - int output_width = root->getParam(ID::Y_WIDTH).as_int(); + for (auto c : chain) + processed.insert(c); - log(" Found adder tree rooted at %s with %d operands (depth %d cells)\n", - log_id(root), (int)operands.size(), (int)tree_cells.size()); + pool chain_y_bits = collect_chain_outputs(chain); + auto operands = collect_leaf_operands(chain, chain_y_bits); - RTLIL::SigSpec new_output = build_csa_tree(operands, output_width); - RTLIL::SigSpec old_output = root->getPort(ID::Y); - module->connect(old_output, new_output); + if (operands.size() < 3) + continue; - for (auto c : tree_cells) { - module->remove(c); - stat_removed_cells++; - } + SigSpec root_y = root->getPort(ID::Y); + int width = GetSize(root_y); - stat_trees++; + std::vector extended; + for (auto &op : operands) + extended.push_back(extend_to(op.sig, op.is_signed, width)); + + int fa_count; + auto [final_a, final_b] = build_wallace_tree(extended, width, fa_count); + + log(" Replaced chain of %d $add cells with %d $fa + 1 $add (%d operands, module %s)\n", + (int)chain.size(), fa_count, (int)operands.size(), log_id(module)); + + emit_final_add(final_a, final_b, root_y, width); + + for (auto cell : chain) + module->remove(cell); } } }; -struct CsaTreePass : public Pass -{ - CsaTreePass() : Pass("csa_tree", - "convert adder chains to carry-save adder trees") {} +struct CsaTreePass : public Pass { + CsaTreePass() : Pass("csa_tree", "convert $add chains to carry-save adder trees") {} void help() override { - // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" csa_tree [options] [selection]\n"); + log(" csa_tree [selection]\n"); log("\n"); - log("This pass converts chains of $add/$sub cells into carry-save adder trees using\n"); - log("$fa (full adder / 3:2 compressor) cells to reduce the critical path depth of\n"); - log("multi-operand addition.\n"); + log("This pass finds chains of $add cells and replaces them with\n"); + log("carry-save adder (CSA) trees built from $fa cells, followed by\n"); + log("a single final $add for the carry-propagate step.\n"); log("\n"); - log("For N operands of width W, the critical path is reduced from\n"); - log("O(N * log W) to O(log_1.5(N) + log W).\n"); - log("\n"); - log(" -min_operands N\n"); - log(" Minimum number of operands to trigger CSA tree construction.\n"); - log(" Default: 3. Values below 3 are clamped to 3.\n"); + log("The tree uses Wallace-tree scheduling for optimal depth:\n"); + log("at each level, all ready operands are grouped into triplets\n"); + log("and compressed via full adders. This gives ceil(log_1.5(N))\n"); + log("FA levels for N input operands.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { - int min_operands = 3; - - log_header(design, "Executing CSA_TREE pass (carry-save adder tree optimization).\n"); + log_header(design, "Executing CSA_TREE pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) - { - if (args[argidx] == "-min_operands" && argidx + 1 < args.size()) { - min_operands = std::max(3, atoi(args[++argidx].c_str())); - continue; - } break; - } extra_args(args, argidx, design); - for (auto module : design->selected_modules()) - { - log("Processing module %s...\n", log_id(module)); - - CsaTreeWorker worker(module, min_operands); + for (auto module : design->selected_modules()) { + CsaTreeWorker worker(module); worker.run(); - - if (worker.stat_trees > 0) - log(" Converted %d adder tree(s): created %d $fa cells, " - "removed %d $add/$sub cells.\n", - worker.stat_trees, worker.stat_fa_cells, - worker.stat_removed_cells); } } } CsaTreePass; From 4381609684c15505c0597d2453b117bf54d8427d Mon Sep 17 00:00:00 2001 From: nella Date: Fri, 13 Mar 2026 12:09:50 +0100 Subject: [PATCH 03/15] Add structural tests for csa_tree. --- Makefile | 1 + tests/csa_tree/add_1bit.v | 9 +++++++++ tests/csa_tree/add_chain_16.v | 7 +++++++ tests/csa_tree/add_chain_2_neg.v | 8 ++++++++ tests/csa_tree/add_chain_3.v | 9 +++++++++ tests/csa_tree/add_chain_5.v | 7 +++++++ tests/csa_tree/add_chain_8.v | 7 +++++++ tests/csa_tree/add_mixed_widths.v | 10 ++++++++++ tests/csa_tree/add_multi_fanout.v | 10 ++++++++++ tests/csa_tree/add_signed.v | 6 ++++++ tests/csa_tree/add_two_chains.v | 9 +++++++++ tests/csa_tree/add_wide_output.v | 6 ++++++ tests/csa_tree/add_with_const.v | 7 +++++++ tests/csa_tree/csa_tree_16input.ys | 8 ++++++++ tests/csa_tree/csa_tree_1bit.ys | 8 ++++++++ tests/csa_tree/csa_tree_2input_neg.ys | 11 +++++++++++ tests/csa_tree/csa_tree_3input.ys | 11 +++++++++++ tests/csa_tree/csa_tree_5input.ys | 11 +++++++++++ tests/csa_tree/csa_tree_8input.ys | 9 +++++++++ tests/csa_tree/csa_tree_const.ys | 9 +++++++++ tests/csa_tree/csa_tree_fir.ys | 8 ++++++++ tests/csa_tree/csa_tree_mixed_widths.ys | 9 +++++++++ tests/csa_tree/csa_tree_multi_fanout.ys | 8 ++++++++ tests/csa_tree/csa_tree_signed.ys | 9 +++++++++ tests/csa_tree/csa_tree_two_chains.ys | 9 +++++++++ tests/csa_tree/csa_tree_wide_output.ys | 9 +++++++++ tests/csa_tree/fir_4tap.v | 24 ++++++++++++++++++++++++ tests/csa_tree/run-test.sh | 7 +++++++ 28 files changed, 246 insertions(+) create mode 100644 tests/csa_tree/add_1bit.v create mode 100644 tests/csa_tree/add_chain_16.v create mode 100644 tests/csa_tree/add_chain_2_neg.v create mode 100644 tests/csa_tree/add_chain_3.v create mode 100644 tests/csa_tree/add_chain_5.v create mode 100644 tests/csa_tree/add_chain_8.v create mode 100644 tests/csa_tree/add_mixed_widths.v create mode 100644 tests/csa_tree/add_multi_fanout.v create mode 100644 tests/csa_tree/add_signed.v create mode 100644 tests/csa_tree/add_two_chains.v create mode 100644 tests/csa_tree/add_wide_output.v create mode 100644 tests/csa_tree/add_with_const.v create mode 100644 tests/csa_tree/csa_tree_16input.ys create mode 100644 tests/csa_tree/csa_tree_1bit.ys create mode 100644 tests/csa_tree/csa_tree_2input_neg.ys create mode 100644 tests/csa_tree/csa_tree_3input.ys create mode 100644 tests/csa_tree/csa_tree_5input.ys create mode 100644 tests/csa_tree/csa_tree_8input.ys create mode 100644 tests/csa_tree/csa_tree_const.ys create mode 100644 tests/csa_tree/csa_tree_fir.ys create mode 100644 tests/csa_tree/csa_tree_mixed_widths.ys create mode 100644 tests/csa_tree/csa_tree_multi_fanout.ys create mode 100644 tests/csa_tree/csa_tree_signed.ys create mode 100644 tests/csa_tree/csa_tree_two_chains.ys create mode 100644 tests/csa_tree/csa_tree_wide_output.ys create mode 100644 tests/csa_tree/fir_4tap.v create mode 100755 tests/csa_tree/run-test.sh diff --git a/Makefile b/Makefile index 33ff74fa4..a181a9fc2 100644 --- a/Makefile +++ b/Makefile @@ -953,6 +953,7 @@ MK_TEST_DIRS += tests/verilog # Tests that don't generate .mk SH_TEST_DIRS = +SH_TEST_DIRS += tests/csa_tree SH_TEST_DIRS += tests/simple SH_TEST_DIRS += tests/simple_abc9 SH_TEST_DIRS += tests/hana diff --git a/tests/csa_tree/add_1bit.v b/tests/csa_tree/add_1bit.v new file mode 100644 index 000000000..febb7afde --- /dev/null +++ b/tests/csa_tree/add_1bit.v @@ -0,0 +1,9 @@ +// edge case for carry shifting + +module add_1bit( + input a, b, c, + output [1:0] y +); + assign y = a + b + c; +endmodule + diff --git a/tests/csa_tree/add_chain_16.v b/tests/csa_tree/add_chain_16.v new file mode 100644 index 000000000..0a1f12f8c --- /dev/null +++ b/tests/csa_tree/add_chain_16.v @@ -0,0 +1,7 @@ +module add_chain_16( + input [15:0] a0, a1, a2, a3, a4, a5, a6, a7, + input [15:0] a8, a9, a10, a11, a12, a13, a14, a15, + output [15:0] y +); + assign y = a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15; +endmodule diff --git a/tests/csa_tree/add_chain_2_neg.v b/tests/csa_tree/add_chain_2_neg.v new file mode 100644 index 000000000..0e2896c78 --- /dev/null +++ b/tests/csa_tree/add_chain_2_neg.v @@ -0,0 +1,8 @@ +// Shouldnt generate csa tree + +module add_chain_2( + input [7:0] a, b, + output [7:0] y +); + assign y = a + b; +endmodule diff --git a/tests/csa_tree/add_chain_3.v b/tests/csa_tree/add_chain_3.v new file mode 100644 index 000000000..eea529ead --- /dev/null +++ b/tests/csa_tree/add_chain_3.v @@ -0,0 +1,9 @@ +// Min chain len + +module add_chain_3( + input [7:0] a, b, c, + output [7:0] y +); + assign y = a + b + c; +endmodule + diff --git a/tests/csa_tree/add_chain_5.v b/tests/csa_tree/add_chain_5.v new file mode 100644 index 000000000..25fb30e1d --- /dev/null +++ b/tests/csa_tree/add_chain_5.v @@ -0,0 +1,7 @@ +module add_chain_5( + input [11:0] a, b, c, d, e, + output [11:0] y +); + assign y = a + b + c + d + e; +endmodule + diff --git a/tests/csa_tree/add_chain_8.v b/tests/csa_tree/add_chain_8.v new file mode 100644 index 000000000..96adbc37e --- /dev/null +++ b/tests/csa_tree/add_chain_8.v @@ -0,0 +1,7 @@ +module add_chain_8( + input [15:0] a, b, c, d, e, f, g, h, + output [15:0] y +); + assign y = a + b + c + d + e + f + g + h; +endmodule + diff --git a/tests/csa_tree/add_mixed_widths.v b/tests/csa_tree/add_mixed_widths.v new file mode 100644 index 000000000..51836872a --- /dev/null +++ b/tests/csa_tree/add_mixed_widths.v @@ -0,0 +1,10 @@ +module add_mixed_widths( + input [7:0] a, + input [3:0] b, + input [15:0] c, + input [7:0] d, + output [15:0] y +); + assign y = a + b + c + d; +endmodule + diff --git a/tests/csa_tree/add_multi_fanout.v b/tests/csa_tree/add_multi_fanout.v new file mode 100644 index 000000000..4cc7ecab3 --- /dev/null +++ b/tests/csa_tree/add_multi_fanout.v @@ -0,0 +1,10 @@ +module add_multi_fanout( + input [7:0] a, b, c, + output [7:0] mid, + output [7:0] y +); + wire [7:0] ab = a + b; + assign mid = ab; + assign y = ab + c; +endmodule + diff --git a/tests/csa_tree/add_signed.v b/tests/csa_tree/add_signed.v new file mode 100644 index 000000000..42b9ca7fd --- /dev/null +++ b/tests/csa_tree/add_signed.v @@ -0,0 +1,6 @@ +module add_signed( + input signed [7:0] a, b, c, d, + output signed [9:0] y +); + assign y = a + b + c + d; +endmodule diff --git a/tests/csa_tree/add_two_chains.v b/tests/csa_tree/add_two_chains.v new file mode 100644 index 000000000..d79d9f3d6 --- /dev/null +++ b/tests/csa_tree/add_two_chains.v @@ -0,0 +1,9 @@ +module add_two_chains( + input [7:0] a, b, c, d, + input [7:0] e, f, g, h, + output [7:0] y1, + output [7:0] y2 +); + assign y1 = a + b + c + d; + assign y2 = e + f + g + h; +endmodule diff --git a/tests/csa_tree/add_wide_output.v b/tests/csa_tree/add_wide_output.v new file mode 100644 index 000000000..1f0342777 --- /dev/null +++ b/tests/csa_tree/add_wide_output.v @@ -0,0 +1,6 @@ +module add_wide_output( + input [7:0] a, b, c, d, + output [31:0] y +); + assign y = a + b + c + d; +endmodule diff --git a/tests/csa_tree/add_with_const.v b/tests/csa_tree/add_with_const.v new file mode 100644 index 000000000..570a399e4 --- /dev/null +++ b/tests/csa_tree/add_with_const.v @@ -0,0 +1,7 @@ +module add_with_const( + input [7:0] a, b, c, + output [7:0] y +); + assign y = a + b + c + 8'd42; +endmodule + diff --git a/tests/csa_tree/csa_tree_16input.ys b/tests/csa_tree/csa_tree_16input.ys new file mode 100644 index 000000000..230d5f845 --- /dev/null +++ b/tests/csa_tree/csa_tree_16input.ys @@ -0,0 +1,8 @@ +read_verilog add_chain_16.v +hierarchy -auto-top +proc; opt_clean +csa_tree +stat + +select -assert-min 5 t:$fa +select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_1bit.ys b/tests/csa_tree/csa_tree_1bit.ys new file mode 100644 index 000000000..74d19c647 --- /dev/null +++ b/tests/csa_tree/csa_tree_1bit.ys @@ -0,0 +1,8 @@ +# Test csa_tree with single-bit operands — carry shift edge case + +read_verilog add_1bit.v +hierarchy -auto-top +proc; opt_clean +equiv_opt csa_tree +design -load postopt + diff --git a/tests/csa_tree/csa_tree_2input_neg.ys b/tests/csa_tree/csa_tree_2input_neg.ys new file mode 100644 index 000000000..810d1d8d9 --- /dev/null +++ b/tests/csa_tree/csa_tree_2input_neg.ys @@ -0,0 +1,11 @@ +# Test csa_tree on 2-operand — should not trigger + +read_verilog add_chain_2_neg.v +hierarchy -auto-top +proc; opt_clean +equiv_opt csa_tree +design -load postopt + +select -assert-none t:$fa +select -assert-count 1 t:$add + diff --git a/tests/csa_tree/csa_tree_3input.ys b/tests/csa_tree/csa_tree_3input.ys new file mode 100644 index 000000000..68ff2ff2e --- /dev/null +++ b/tests/csa_tree/csa_tree_3input.ys @@ -0,0 +1,11 @@ +# Test csa_tree on 3-operand chain — minimal trigger case + +read_verilog add_chain_3.v +hierarchy -auto-top +proc; opt_clean +equiv_opt csa_tree +design -load postopt + +select -assert-count 1 t:$fa +select -assert-count 1 t:$add + diff --git a/tests/csa_tree/csa_tree_5input.ys b/tests/csa_tree/csa_tree_5input.ys new file mode 100644 index 000000000..dadea092f --- /dev/null +++ b/tests/csa_tree/csa_tree_5input.ys @@ -0,0 +1,11 @@ +# Test csa_tree with 5 operands — tree with remainders + +read_verilog add_chain_5.v +hierarchy -auto-top +proc; opt_clean +equiv_opt csa_tree +design -load postopt + +select -assert-min 2 t:$fa +select -assert-count 1 t:$add + diff --git a/tests/csa_tree/csa_tree_8input.ys b/tests/csa_tree/csa_tree_8input.ys new file mode 100644 index 000000000..23d2d698a --- /dev/null +++ b/tests/csa_tree/csa_tree_8input.ys @@ -0,0 +1,9 @@ +read_verilog add_chain_8.v +hierarchy -auto-top +proc; opt_clean +csa_tree +stat + +select -assert-min 1 t:$fa +select -assert-count 1 t:$add + diff --git a/tests/csa_tree/csa_tree_const.ys b/tests/csa_tree/csa_tree_const.ys new file mode 100644 index 000000000..75d0bc1d7 --- /dev/null +++ b/tests/csa_tree/csa_tree_const.ys @@ -0,0 +1,9 @@ +read_verilog add_with_const.v +hierarchy -auto-top +proc; opt_clean +equiv_opt csa_tree +design -load postopt + +select -assert-min 1 t:$fa +select -assert-count 1 t:$add + diff --git a/tests/csa_tree/csa_tree_fir.ys b/tests/csa_tree/csa_tree_fir.ys new file mode 100644 index 000000000..59aac98b0 --- /dev/null +++ b/tests/csa_tree/csa_tree_fir.ys @@ -0,0 +1,8 @@ +read_verilog fir_4tap.v +hierarchy -auto-top +proc; opt_clean +equiv_opt -async2sync csa_tree +design -load postopt + +select -assert-min 1 t:$fa + diff --git a/tests/csa_tree/csa_tree_mixed_widths.ys b/tests/csa_tree/csa_tree_mixed_widths.ys new file mode 100644 index 000000000..1cecae201 --- /dev/null +++ b/tests/csa_tree/csa_tree_mixed_widths.ys @@ -0,0 +1,9 @@ +read_verilog add_mixed_widths.v +hierarchy -auto-top +proc; opt_clean +equiv_opt csa_tree +design -load postopt + +select -assert-min 1 t:$fa +select -assert-count 1 t:$add + diff --git a/tests/csa_tree/csa_tree_multi_fanout.ys b/tests/csa_tree/csa_tree_multi_fanout.ys new file mode 100644 index 000000000..0bce8d6bd --- /dev/null +++ b/tests/csa_tree/csa_tree_multi_fanout.ys @@ -0,0 +1,8 @@ +read_verilog add_multi_fanout.v +hierarchy -auto-top +proc; opt_clean +equiv_opt csa_tree +design -load postopt + +select -assert-none t:$fa + diff --git a/tests/csa_tree/csa_tree_signed.ys b/tests/csa_tree/csa_tree_signed.ys new file mode 100644 index 000000000..7e29cc123 --- /dev/null +++ b/tests/csa_tree/csa_tree_signed.ys @@ -0,0 +1,9 @@ +read_verilog add_signed.v +hierarchy -auto-top +proc; opt_clean +equiv_opt csa_tree +design -load postopt + +select -assert-min 1 t:$fa +select -assert-count 1 t:$add + diff --git a/tests/csa_tree/csa_tree_two_chains.ys b/tests/csa_tree/csa_tree_two_chains.ys new file mode 100644 index 000000000..10fbe8b15 --- /dev/null +++ b/tests/csa_tree/csa_tree_two_chains.ys @@ -0,0 +1,9 @@ +read_verilog add_two_chains.v +hierarchy -auto-top +proc; opt_clean +equiv_opt csa_tree +design -load postopt + +select -assert-min 2 t:$fa +select -assert-count 2 t:$add + diff --git a/tests/csa_tree/csa_tree_wide_output.ys b/tests/csa_tree/csa_tree_wide_output.ys new file mode 100644 index 000000000..84cd13bd7 --- /dev/null +++ b/tests/csa_tree/csa_tree_wide_output.ys @@ -0,0 +1,9 @@ +read_verilog add_wide_output.v +hierarchy -auto-top +proc; opt_clean +equiv_opt csa_tree +design -load postopt + +select -assert-min 1 t:$fa +select -assert-count 1 t:$add + diff --git a/tests/csa_tree/fir_4tap.v b/tests/csa_tree/fir_4tap.v new file mode 100644 index 000000000..6c197dbd1 --- /dev/null +++ b/tests/csa_tree/fir_4tap.v @@ -0,0 +1,24 @@ +module fir_4tap( + input clk, + input [15:0] x, + input [15:0] c0, c1, c2, c3, + output reg [31:0] y +); + reg [15:0] x1, x2, x3; + always @(posedge clk) begin + x1 <= x; + x2 <= x1; + x3 <= x2; + end + + wire [31:0] p0 = x * c0; + wire [31:0] p1 = x1 * c1; + wire [31:0] p2 = x2 * c2; + wire [31:0] p3 = x3 * c3; + + wire [31:0] sum = p0 + p1 + p2 + p3; + + always @(posedge clk) + y <= sum; +endmodule + diff --git a/tests/csa_tree/run-test.sh b/tests/csa_tree/run-test.sh new file mode 100755 index 000000000..2e3f5235c --- /dev/null +++ b/tests/csa_tree/run-test.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +source ../common-env.sh +set -e +for x in *.ys; do + echo "Running $x.." + ../../yosys -ql ${x%.ys}.log $x +done From 8d0ecbcdc04d91c98b7e323b62b2ed2289b77fc3 Mon Sep 17 00:00:00 2001 From: nella Date: Fri, 13 Mar 2026 12:23:26 +0100 Subject: [PATCH 04/15] Add csa synth tests. --- tests/csa_tree/abc_bench_add8.v | 6 +++++ tests/csa_tree/csa_tree_sim.ys | 23 ++++++++++++++++ tests/csa_tree/csa_tree_synth.ys | 45 ++++++++++++++++++++++++++++++++ tests/csa_tree/sim_add4.v | 6 +++++ 4 files changed, 80 insertions(+) create mode 100644 tests/csa_tree/abc_bench_add8.v create mode 100644 tests/csa_tree/csa_tree_sim.ys create mode 100644 tests/csa_tree/csa_tree_synth.ys create mode 100644 tests/csa_tree/sim_add4.v diff --git a/tests/csa_tree/abc_bench_add8.v b/tests/csa_tree/abc_bench_add8.v new file mode 100644 index 000000000..7e61007c5 --- /dev/null +++ b/tests/csa_tree/abc_bench_add8.v @@ -0,0 +1,6 @@ +module abc_bench_add8( + input [7:0] a, b, c, d, e, f, g, h, + output [7:0] y +); + assign y = a + b + c + d + e + f + g + h; +endmodule diff --git a/tests/csa_tree/csa_tree_sim.ys b/tests/csa_tree/csa_tree_sim.ys new file mode 100644 index 000000000..5a571c268 --- /dev/null +++ b/tests/csa_tree/csa_tree_sim.ys @@ -0,0 +1,23 @@ +read_verilog sim_add4.v +hierarchy -top sim_add4 +proc; opt_clean +csa_tree +opt_clean + +# 1 + 2 + 3 + 4 = 10 +sat -set a 1 -set b 2 -set c 3 -set d 4 -prove y 10 + +# 0 + 0 + 0 + 0 = 0 +sat -set a 0 -set b 0 -set c 0 -set d 0 -prove y 0 + +# 255 + 1 + 0 + 0 = 0 +sat -set a 255 -set b 1 -set c 0 -set d 0 -prove y 0 + +# 100 + 50 + 25 + 25 = 200 +sat -set a 100 -set b 50 -set c 25 -set d 25 -prove y 200 + +# 255 + 255 + 255 + 255 = 252 +sat -set a 255 -set b 255 -set c 255 -set d 255 -prove y 252 + +log "ok" + diff --git a/tests/csa_tree/csa_tree_synth.ys b/tests/csa_tree/csa_tree_synth.ys new file mode 100644 index 000000000..e03580217 --- /dev/null +++ b/tests/csa_tree/csa_tree_synth.ys @@ -0,0 +1,45 @@ +# Baseline: no csa_tree +read_verilog abc_bench_add8.v +hierarchy -top abc_bench_add8 +proc; opt + +# Baseline synth +techmap +abc -g AND,OR,XOR +opt_clean +stat +design -save baseline + +# With csa_tree +design -reset +read_verilog abc_bench_add8.v +hierarchy -top abc_bench_add8 +proc; opt +csa_tree +techmap +abc -g AND,OR,XOR +opt_clean +stat + +select -assert-max 250 t:$_AND_ t:$_OR_ t:$_XOR_ t:$_NOT_ %u +design -save csa_result + +# Depth comparison via ABC +design -reset +read_verilog abc_bench_add8.v +hierarchy -top abc_bench_add8 +proc; opt +techmap +abc -D 1 +stat +log "baseline depth mapping complete" + +design -reset +read_verilog abc_bench_add8.v +hierarchy -top abc_bench_add8 +proc; opt +csa_tree +techmap +abc -D 1 +stat +log "CSA depth mapping complete" diff --git a/tests/csa_tree/sim_add4.v b/tests/csa_tree/sim_add4.v new file mode 100644 index 000000000..589044889 --- /dev/null +++ b/tests/csa_tree/sim_add4.v @@ -0,0 +1,6 @@ +module sim_add4( + input [7:0] a, b, c, d, + output [7:0] y +); + assign y = a + b + c + d; +endmodule From b6ea0bb4ccafa1da9a01e89319dcdf59f47e3bbf Mon Sep 17 00:00:00 2001 From: nella Date: Fri, 13 Mar 2026 12:33:26 +0100 Subject: [PATCH 05/15] Edge case tests. --- tests/csa_tree/add_1bit_wide_out.v | 6 ++ tests/csa_tree/add_multi_const.v | 6 ++ tests/csa_tree/add_partial_chain.v | 9 +++ tests/csa_tree/add_repeated.v | 6 ++ tests/csa_tree/csa_tree_1bit_wide_out.ys | 8 +++ tests/csa_tree/csa_tree_equiv.ys | 80 ++++++++++++++++++++++++ tests/csa_tree/csa_tree_multi_const.ys | 9 +++ tests/csa_tree/csa_tree_partial_chain.ys | 8 +++ tests/csa_tree/csa_tree_repeated.ys | 8 +++ tests/csa_tree/equiv_narrow.v | 59 +++++++++++++++++ 10 files changed, 199 insertions(+) create mode 100644 tests/csa_tree/add_1bit_wide_out.v create mode 100644 tests/csa_tree/add_multi_const.v create mode 100644 tests/csa_tree/add_partial_chain.v create mode 100644 tests/csa_tree/add_repeated.v create mode 100644 tests/csa_tree/csa_tree_1bit_wide_out.ys create mode 100644 tests/csa_tree/csa_tree_equiv.ys create mode 100644 tests/csa_tree/csa_tree_multi_const.ys create mode 100644 tests/csa_tree/csa_tree_partial_chain.ys create mode 100644 tests/csa_tree/csa_tree_repeated.ys create mode 100644 tests/csa_tree/equiv_narrow.v diff --git a/tests/csa_tree/add_1bit_wide_out.v b/tests/csa_tree/add_1bit_wide_out.v new file mode 100644 index 000000000..e1a6ceb51 --- /dev/null +++ b/tests/csa_tree/add_1bit_wide_out.v @@ -0,0 +1,6 @@ +module add_1bit_wide_out( + input a, b, c, d, + output [3:0] y +); + assign y = a + b + c + d; +endmodule diff --git a/tests/csa_tree/add_multi_const.v b/tests/csa_tree/add_multi_const.v new file mode 100644 index 000000000..7da78f9de --- /dev/null +++ b/tests/csa_tree/add_multi_const.v @@ -0,0 +1,6 @@ +module add_multi_const( + input [7:0] x, + output [7:0] y +); + assign y = 8'd1 + 8'd2 + 8'd3 + x; +endmodule diff --git a/tests/csa_tree/add_partial_chain.v b/tests/csa_tree/add_partial_chain.v new file mode 100644 index 000000000..a4f20cfa4 --- /dev/null +++ b/tests/csa_tree/add_partial_chain.v @@ -0,0 +1,9 @@ +module add_partial_chain( + input [7:0] a, b, c, d, e, + output [7:0] mid, + output [7:0] y +); + wire [7:0] ab = a + b; + assign mid = ab; + assign y = ab + c + d + e; +endmodule diff --git a/tests/csa_tree/add_repeated.v b/tests/csa_tree/add_repeated.v new file mode 100644 index 000000000..e3337097b --- /dev/null +++ b/tests/csa_tree/add_repeated.v @@ -0,0 +1,6 @@ +module add_repeated( + input [7:0] a, + output [7:0] y +); + assign y = a + a + a + a; +endmodule diff --git a/tests/csa_tree/csa_tree_1bit_wide_out.ys b/tests/csa_tree/csa_tree_1bit_wide_out.ys new file mode 100644 index 000000000..5e2605340 --- /dev/null +++ b/tests/csa_tree/csa_tree_1bit_wide_out.ys @@ -0,0 +1,8 @@ +read_verilog add_1bit_wide_out.v +hierarchy -auto-top +proc; opt_clean +csa_tree +stat + +select -assert-min 1 t:$fa +select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_equiv.ys b/tests/csa_tree/csa_tree_equiv.ys new file mode 100644 index 000000000..9c5b4ab44 --- /dev/null +++ b/tests/csa_tree/csa_tree_equiv.ys @@ -0,0 +1,80 @@ +# Test bit correctness +read_verilog equiv_narrow.v +hierarchy -top equiv_add3 +proc; opt_clean +equiv_opt csa_tree +design -load postopt +select -assert-count 1 t:$fa +select -assert-count 1 t:$add +design -reset +log "equiv_add3: ok" + +read_verilog equiv_narrow.v +hierarchy -top equiv_add4 +proc; opt_clean +equiv_opt csa_tree +design -load postopt +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +design -reset +log "equiv_add4: ok" + +read_verilog equiv_narrow.v +hierarchy -top equiv_add5 +proc; opt_clean +equiv_opt csa_tree +design -load postopt +select -assert-min 2 t:$fa +select -assert-count 1 t:$add +design -reset +log "equiv_add5: ok" + +read_verilog equiv_narrow.v +hierarchy -top equiv_add8 +proc; opt_clean +equiv_opt csa_tree +design -load postopt +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +design -reset +log "equiv_add8: ok" + +read_verilog equiv_narrow.v +hierarchy -top equiv_signed +proc; opt_clean +equiv_opt csa_tree +design -load postopt +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +design -reset +log "equiv_signed: ok" + +read_verilog equiv_narrow.v +hierarchy -top equiv_mixed_w +proc; opt_clean +equiv_opt csa_tree +design -load postopt +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +design -reset +log "equiv_mixed_w: ok" + +read_verilog equiv_narrow.v +hierarchy -top equiv_repeated +proc; opt_clean +equiv_opt csa_tree +design -load postopt +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +design -reset +log "equiv_repeated: ok" + +read_verilog equiv_narrow.v +hierarchy -top equiv_1bit_wide +proc; opt_clean +equiv_opt csa_tree +design -load postopt +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +design -reset +log "equiv_1bit_wide: ok" diff --git a/tests/csa_tree/csa_tree_multi_const.ys b/tests/csa_tree/csa_tree_multi_const.ys new file mode 100644 index 000000000..a68b60e33 --- /dev/null +++ b/tests/csa_tree/csa_tree_multi_const.ys @@ -0,0 +1,9 @@ +read_verilog add_multi_const.v +hierarchy -auto-top +proc; opt_clean +csa_tree +stat + +select -assert-none t:$fa +select -assert-max 1 t:$add + diff --git a/tests/csa_tree/csa_tree_partial_chain.ys b/tests/csa_tree/csa_tree_partial_chain.ys new file mode 100644 index 000000000..3852c4f9d --- /dev/null +++ b/tests/csa_tree/csa_tree_partial_chain.ys @@ -0,0 +1,8 @@ +read_verilog add_partial_chain.v +hierarchy -auto-top +proc; opt_clean +csa_tree +stat + +select -assert-min 1 t:$fa +select -assert-min 2 t:$add diff --git a/tests/csa_tree/csa_tree_repeated.ys b/tests/csa_tree/csa_tree_repeated.ys new file mode 100644 index 000000000..7e1c2ec51 --- /dev/null +++ b/tests/csa_tree/csa_tree_repeated.ys @@ -0,0 +1,8 @@ +read_verilog add_repeated.v +hierarchy -auto-top +proc; opt_clean +csa_tree +stat + +select -assert-min 1 t:$fa +select -assert-count 1 t:$add diff --git a/tests/csa_tree/equiv_narrow.v b/tests/csa_tree/equiv_narrow.v new file mode 100644 index 000000000..f4f7ef279 --- /dev/null +++ b/tests/csa_tree/equiv_narrow.v @@ -0,0 +1,59 @@ +// Narrow-width test designs for SAT equivalence (4-bit to keep SAT fast) + +module equiv_add3( + input [3:0] a, b, c, + output [3:0] y +); + assign y = a + b + c; +endmodule + +module equiv_add4( + input [3:0] a, b, c, d, + output [3:0] y +); + assign y = a + b + c + d; +endmodule + +module equiv_add5( + input [3:0] a, b, c, d, e, + output [3:0] y +); + assign y = a + b + c + d + e; +endmodule + +module equiv_add8( + input [3:0] a, b, c, d, e, f, g, h, + output [3:0] y +); + assign y = a + b + c + d + e + f + g + h; +endmodule + +module equiv_signed( + input signed [3:0] a, b, c, d, + output signed [5:0] y +); + assign y = a + b + c + d; +endmodule + +module equiv_mixed_w( + input [1:0] a, + input [3:0] b, + input [5:0] c, + output [5:0] y +); + assign y = a + b + c; +endmodule + +module equiv_repeated( + input [3:0] a, + output [3:0] y +); + assign y = a + a + a + a; +endmodule + +module equiv_1bit_wide( + input a, b, c, d, + output [3:0] y +); + assign y = a + b + c + d; +endmodule From a7fcfc18fa664abafe3cb2aa26c463510a846365 Mon Sep 17 00:00:00 2001 From: nella Date: Fri, 13 Mar 2026 12:54:58 +0100 Subject: [PATCH 06/15] Add sub chain support for csa trees. --- passes/opt/csa_tree.cc | 157 ++++++++++++++++++++++++++++++++--------- 1 file changed, 123 insertions(+), 34 deletions(-) diff --git a/passes/opt/csa_tree.cc b/passes/opt/csa_tree.cc index 92f744a9f..e1a4a9464 100644 --- a/passes/opt/csa_tree.cc +++ b/passes/opt/csa_tree.cc @@ -13,7 +13,7 @@ struct CsaTreeWorker dict> bit_consumers; dict fanout; - pool all_adds; + pool all_addsubs; CsaTreeWorker(Module *module) : module(module), sigmap(module) {} @@ -22,11 +22,11 @@ struct CsaTreeWorker int depth; }; - void find_adds() + void find_addsubs() { for (auto cell : module->cells()) - if (cell->type == ID($add)) - all_adds.insert(cell); + if (cell->type == ID($add) || cell->type == ID($sub)) + all_addsubs.insert(cell); } void build_fanout_map() @@ -46,9 +46,9 @@ struct CsaTreeWorker fanout[bit]++; } - Cell*single_add_consumer(SigSpec sig) + Cell* single_addsub_consumer(SigSpec sig) { - Cell*consumer = nullptr; + Cell* consumer = nullptr; for (auto bit : sig) { if (!fanout.count(bit) || fanout[bit] != 1) @@ -57,7 +57,7 @@ struct CsaTreeWorker return nullptr; Cell* c = *bit_consumers[bit].begin(); - if (!all_adds.count(c)) + if (!all_addsubs.count(c)) return nullptr; if (consumer == nullptr) @@ -69,13 +69,13 @@ struct CsaTreeWorker return consumer; } - dict find_add_parents() + dict find_addsub_parents() { dict parent_of; - for (auto cell : all_adds) { + for (auto cell : all_addsubs) { SigSpec y = sigmap(cell->getPort(ID::Y)); - Cell* consumer = single_add_consumer(y); + Cell* consumer = single_addsub_consumer(y); if (consumer != nullptr && consumer != cell) parent_of[cell] = consumer; } @@ -125,22 +125,78 @@ struct CsaTreeWorker struct Operand { SigSpec sig; bool is_signed; + bool negate; }; - std::vector collect_leaf_operands(const pool &chain, const pool &chain_y_bits) + bool is_subtracted_input(Cell* child, Cell* parent) { + if (parent->type != ID($sub)) + return false; + + SigSpec child_y = sigmap(child->getPort(ID::Y)); + SigSpec parent_b = sigmap(parent->getPort(ID::B)); + + for (auto bit : child_y) + for (auto pbit : parent_b) + if (bit == pbit) + return true; + + return false; + } + + std::vector collect_leaf_operands( + const pool &chain, + const pool &chain_y_bits, + Cell* root, + const dict &parent_of, + int &correction + ) { + dict negated; + negated[root] = false; + std::queue worklist; + worklist.push(root); + + while (!worklist.empty()) { + Cell* cur = worklist.front(); + worklist.pop(); + for (auto cell : chain) { + if (!parent_of.count(cell)) + continue; + if (parent_of.at(cell) != cur) + continue; + if (negated.count(cell)) + continue; + + bool sub_b = is_subtracted_input(cell, cur); + negated[cell] = negated[cur] ^ sub_b; + worklist.push(cell); + } + } + std::vector operands; + correction = 0; for (auto cell : chain) { + bool cell_neg = negated.count(cell) ? negated[cell] : false; SigSpec a = sigmap(cell->getPort(ID::A)); SigSpec b = sigmap(cell->getPort(ID::B)); bool a_signed = cell->getParam(ID::A_SIGNED).as_bool(); bool b_signed = cell->getParam(ID::B_SIGNED).as_bool(); + bool b_subtracted = (cell->type == ID($sub)); - if (!is_chain_internal(a, chain_y_bits)) - operands.push_back({a, a_signed}); - if (!is_chain_internal(b, chain_y_bits)) - operands.push_back({b, b_signed}); + if (!is_chain_internal(a, chain_y_bits)) { + bool neg_a = cell_neg; + operands.push_back({a, a_signed, neg_a}); + if (neg_a) + correction++; + } + + if (!is_chain_internal(b, chain_y_bits)) { + bool neg_b = cell_neg ^ b_subtracted; + operands.push_back({b, b_signed, neg_b}); + if (neg_b) + correction++; + } } return operands; @@ -157,6 +213,23 @@ struct CsaTreeWorker return sig; } + SigSpec emit_not(SigSpec sig, int width) + { + SigSpec out = module->addWire(NEW_ID, width); + Cell* inv = module->addCell(NEW_ID, ID($not)); + inv->setParam(ID::A_SIGNED, false); + inv->setParam(ID::A_WIDTH, width); + inv->setParam(ID::Y_WIDTH, width); + inv->setPort(ID::A, sig); + inv->setPort(ID::Y, out); + return out; + } + + SigSpec make_constant(int value, int width) + { + return SigSpec(value, width); + } + std::pair emit_fa(SigSpec a, SigSpec b, SigSpec c, int width) { SigSpec sum = module->addWire(NEW_ID, width); @@ -247,13 +320,13 @@ struct CsaTreeWorker void run() { - find_adds(); - if (all_adds.empty()) + find_addsubs(); + if (all_addsubs.empty()) return; build_fanout_map(); - auto parent_of = find_add_parents(); + auto parent_of = find_addsub_parents(); pool has_parent; dict> children_of; @@ -264,7 +337,7 @@ struct CsaTreeWorker pool processed; - for (auto root : all_adds) + for (auto root : all_addsubs) { if (has_parent.count(root)) continue; @@ -279,23 +352,40 @@ struct CsaTreeWorker processed.insert(c); pool chain_y_bits = collect_chain_outputs(chain); - auto operands = collect_leaf_operands(chain, chain_y_bits); + int correction = 0; + auto operands = collect_leaf_operands(chain, chain_y_bits, root, parent_of, correction); if (operands.size() < 3) continue; SigSpec root_y = root->getPort(ID::Y); int width = GetSize(root_y); - std::vector extended; - for (auto &op : operands) - extended.push_back(extend_to(op.sig, op.is_signed, width)); + + for (auto &op : operands) { + SigSpec s = extend_to(op.sig, op.is_signed, width); + if (op.negate) + s = emit_not(s, width); + extended.push_back(s); + } + + if (correction > 0) + extended.push_back(make_constant(correction, width)); int fa_count; auto [final_a, final_b] = build_wallace_tree(extended, width, fa_count); + int num_subs = 0; - log(" Replaced chain of %d $add cells with %d $fa + 1 $add (%d operands, module %s)\n", - (int)chain.size(), fa_count, (int)operands.size(), log_id(module)); + for (auto cell : chain) + if (cell->type == ID($sub)) + num_subs++; + + if (num_subs > 0) + log(" Replaced chain of %d $add/%d $sub cells with %d $fa + 1 $add (%d operands, module %s)\n", + (int)chain.size() - num_subs, num_subs, fa_count, (int)operands.size(), log_id(module)); + else + log(" Replaced chain of %d $add cells with %d $fa + 1 $add (%d operands, module %s)\n", + (int)chain.size(), fa_count, (int)operands.size(), log_id(module)); emit_final_add(final_a, final_b, root_y, width); @@ -306,22 +396,21 @@ struct CsaTreeWorker }; struct CsaTreePass : public Pass { - CsaTreePass() : Pass("csa_tree", "convert $add chains to carry-save adder trees") {} + CsaTreePass() : Pass("csa_tree", "convert $add/$sub chains to carry-save adder trees") {} void help() override { - // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" csa_tree [selection]\n"); log("\n"); - log("This pass finds chains of $add cells and replaces them with\n"); - log("carry-save adder (CSA) trees built from $fa cells, followed by\n"); - log("a single final $add for the carry-propagate step.\n"); + log("This pass finds chains of $add and $sub cells and replaces them with carry-save\n"); + log("adder trees built from $fa cells, followed by a single final $add for the\n"); + log("carry-propagate step.\n"); log("\n"); - log("The tree uses Wallace-tree scheduling for optimal depth:\n"); - log("at each level, all ready operands are grouped into triplets\n"); - log("and compressed via full adders. This gives ceil(log_1.5(N))\n"); - log("FA levels for N input operands.\n"); + log("The tree uses Wallace-tree scheduling for optimal depth: at each level, all ready\n"); + log("operands are grouped into triplets and compressed via full adders. This\n"); + log("gives ceil(log_1.5(N)) FA levels for N input operands.\n"); log("\n"); } From 1f097a937f297069de502c95c4401439137eb289 Mon Sep 17 00:00:00 2001 From: nella Date: Fri, 13 Mar 2026 13:14:32 +0100 Subject: [PATCH 07/15] Add chain tests and tighten synthesis assertions for csa. --- tests/csa_tree/add_1bit.v | 2 +- tests/csa_tree/csa_tree_1bit.ys | 6 ++-- tests/csa_tree/csa_tree_equiv.ys | 1 + tests/csa_tree/csa_tree_sub_2op_neg.ys | 8 +++++ tests/csa_tree/csa_tree_sub_3op.ys | 11 +++++++ tests/csa_tree/csa_tree_sub_5op.ys | 9 ++++++ tests/csa_tree/csa_tree_sub_all.ys | 9 ++++++ tests/csa_tree/csa_tree_sub_equiv.ys | 41 ++++++++++++++++++++++++++ tests/csa_tree/csa_tree_sub_mixed.ys | 11 +++++++ tests/csa_tree/csa_tree_sub_signed.ys | 9 ++++++ tests/csa_tree/csa_tree_sub_sim.ys | 38 ++++++++++++++++++++++++ tests/csa_tree/csa_tree_synth.ys | 32 ++++++++++++++------ tests/csa_tree/csa_tree_two_chains.ys | 3 +- tests/csa_tree/csa_tree_wide_output.ys | 3 +- tests/csa_tree/equiv_sub_narrow.v | 27 +++++++++++++++++ tests/csa_tree/sub_2op_neg.v | 6 ++++ tests/csa_tree/sub_3op.v | 6 ++++ tests/csa_tree/sub_5op.v | 6 ++++ tests/csa_tree/sub_all.v | 6 ++++ tests/csa_tree/sub_mixed.v | 6 ++++ tests/csa_tree/sub_signed.v | 6 ++++ 21 files changed, 230 insertions(+), 16 deletions(-) create mode 100644 tests/csa_tree/csa_tree_sub_2op_neg.ys create mode 100644 tests/csa_tree/csa_tree_sub_3op.ys create mode 100644 tests/csa_tree/csa_tree_sub_5op.ys create mode 100644 tests/csa_tree/csa_tree_sub_all.ys create mode 100644 tests/csa_tree/csa_tree_sub_equiv.ys create mode 100644 tests/csa_tree/csa_tree_sub_mixed.ys create mode 100644 tests/csa_tree/csa_tree_sub_signed.ys create mode 100644 tests/csa_tree/csa_tree_sub_sim.ys create mode 100644 tests/csa_tree/equiv_sub_narrow.v create mode 100644 tests/csa_tree/sub_2op_neg.v create mode 100644 tests/csa_tree/sub_3op.v create mode 100644 tests/csa_tree/sub_5op.v create mode 100644 tests/csa_tree/sub_all.v create mode 100644 tests/csa_tree/sub_mixed.v create mode 100644 tests/csa_tree/sub_signed.v diff --git a/tests/csa_tree/add_1bit.v b/tests/csa_tree/add_1bit.v index febb7afde..e4b93e8c0 100644 --- a/tests/csa_tree/add_1bit.v +++ b/tests/csa_tree/add_1bit.v @@ -1,4 +1,4 @@ -// edge case for carry shifting +// Edge case for carry shifting module add_1bit( input a, b, c, diff --git a/tests/csa_tree/csa_tree_1bit.ys b/tests/csa_tree/csa_tree_1bit.ys index 74d19c647..4245f3d24 100644 --- a/tests/csa_tree/csa_tree_1bit.ys +++ b/tests/csa_tree/csa_tree_1bit.ys @@ -3,6 +3,8 @@ read_verilog add_1bit.v hierarchy -auto-top proc; opt_clean -equiv_opt csa_tree -design -load postopt +csa_tree +stat +select -assert-min 1 t:$fa +select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_equiv.ys b/tests/csa_tree/csa_tree_equiv.ys index 9c5b4ab44..ac976e1b3 100644 --- a/tests/csa_tree/csa_tree_equiv.ys +++ b/tests/csa_tree/csa_tree_equiv.ys @@ -1,4 +1,5 @@ # Test bit correctness + read_verilog equiv_narrow.v hierarchy -top equiv_add3 proc; opt_clean diff --git a/tests/csa_tree/csa_tree_sub_2op_neg.ys b/tests/csa_tree/csa_tree_sub_2op_neg.ys new file mode 100644 index 000000000..e2ee89a50 --- /dev/null +++ b/tests/csa_tree/csa_tree_sub_2op_neg.ys @@ -0,0 +1,8 @@ +read_verilog sub_2op_neg.v +hierarchy -auto-top +proc; opt_clean +csa_tree +stat + +select -assert-none t:$fa +select -assert-count 1 t:$sub diff --git a/tests/csa_tree/csa_tree_sub_3op.ys b/tests/csa_tree/csa_tree_sub_3op.ys new file mode 100644 index 000000000..70801d30c --- /dev/null +++ b/tests/csa_tree/csa_tree_sub_3op.ys @@ -0,0 +1,11 @@ +# Test minimal sub chain + +read_verilog sub_3op.v +hierarchy -auto-top +proc; opt_clean +csa_tree +stat + +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +select -assert-none t:$sub diff --git a/tests/csa_tree/csa_tree_sub_5op.ys b/tests/csa_tree/csa_tree_sub_5op.ys new file mode 100644 index 000000000..62c4a80ff --- /dev/null +++ b/tests/csa_tree/csa_tree_sub_5op.ys @@ -0,0 +1,9 @@ +read_verilog sub_5op.v +hierarchy -auto-top +proc; opt_clean +csa_tree +stat + +select -assert-min 2 t:$fa +select -assert-count 1 t:$add +select -assert-none t:$sub diff --git a/tests/csa_tree/csa_tree_sub_all.ys b/tests/csa_tree/csa_tree_sub_all.ys new file mode 100644 index 000000000..8e47bb0db --- /dev/null +++ b/tests/csa_tree/csa_tree_sub_all.ys @@ -0,0 +1,9 @@ +read_verilog sub_all.v +hierarchy -auto-top +proc; opt_clean +csa_tree +stat + +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +select -assert-none t:$sub diff --git a/tests/csa_tree/csa_tree_sub_equiv.ys b/tests/csa_tree/csa_tree_sub_equiv.ys new file mode 100644 index 000000000..b1be6bebe --- /dev/null +++ b/tests/csa_tree/csa_tree_sub_equiv.ys @@ -0,0 +1,41 @@ +# Test equiv_opt on narrow sub designs + +read_verilog equiv_sub_narrow.v +hierarchy -top equiv_sub_mixed +proc; opt_clean +equiv_opt csa_tree +design -load postopt +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +design -reset +log "equiv_sub_mixed: ok" + +read_verilog equiv_sub_narrow.v +hierarchy -top equiv_sub_all +proc; opt_clean +equiv_opt csa_tree +design -load postopt +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +design -reset +log "equiv_sub_all: ok" + +read_verilog equiv_sub_narrow.v +hierarchy -top equiv_sub_3op +proc; opt_clean +equiv_opt csa_tree +design -load postopt +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +design -reset +log "equiv_sub_3op: ok" + +read_verilog equiv_sub_narrow.v +hierarchy -top equiv_sub_signed +proc; opt_clean +equiv_opt csa_tree +design -load postopt +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +design -reset +log "equiv_sub_signed: ok" diff --git a/tests/csa_tree/csa_tree_sub_mixed.ys b/tests/csa_tree/csa_tree_sub_mixed.ys new file mode 100644 index 000000000..8fea5f0d2 --- /dev/null +++ b/tests/csa_tree/csa_tree_sub_mixed.ys @@ -0,0 +1,11 @@ +# Test mixed csa chain + +read_verilog sub_mixed.v +hierarchy -auto-top +proc; opt_clean +csa_tree +stat + +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +select -assert-none t:$sub diff --git a/tests/csa_tree/csa_tree_sub_signed.ys b/tests/csa_tree/csa_tree_sub_signed.ys new file mode 100644 index 000000000..78caf5a2e --- /dev/null +++ b/tests/csa_tree/csa_tree_sub_signed.ys @@ -0,0 +1,9 @@ +read_verilog sub_signed.v +hierarchy -auto-top +proc; opt_clean +csa_tree +stat + +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +select -assert-none t:$sub diff --git a/tests/csa_tree/csa_tree_sub_sim.ys b/tests/csa_tree/csa_tree_sub_sim.ys new file mode 100644 index 000000000..73ca5f3e1 --- /dev/null +++ b/tests/csa_tree/csa_tree_sub_sim.ys @@ -0,0 +1,38 @@ +read_verilog sub_mixed.v +hierarchy -top sub_mixed +proc; opt_clean +csa_tree +opt_clean + +# 10 + 20 - 5 + 3 = 28 +sat -set a 10 -set b 20 -set c 5 -set d 3 -prove y 28 + +# 0 + 0 - 0 + 0 = 0 +sat -set a 0 -set b 0 -set c 0 -set d 0 -prove y 0 + +# 100 + 50 - 30 + 10 = 130 +sat -set a 100 -set b 50 -set c 30 -set d 10 -prove y 130 + +# 1 + 1 - 255 + 1 = 4 +sat -set a 1 -set b 1 -set c 255 -set d 1 -prove y 4 + +log "sub_mixed vectors: ok" + +design -reset + +read_verilog sub_all.v +hierarchy -top sub_all +proc; opt_clean +csa_tree +opt_clean + +# 100 - 10 - 20 - 30 = 40 +sat -set a 100 -set b 10 -set c 20 -set d 30 -prove y 40 + +# 0 - 0 - 0 - 0 = 0 +sat -set a 0 -set b 0 -set c 0 -set d 0 -prove y 0 + +# 255 - 1 - 1 - 1 = 252 +sat -set a 255 -set b 1 -set c 1 -set d 1 -prove y 252 + +log "sub_all vectors: ok" diff --git a/tests/csa_tree/csa_tree_synth.ys b/tests/csa_tree/csa_tree_synth.ys index e03580217..eb1e12a0f 100644 --- a/tests/csa_tree/csa_tree_synth.ys +++ b/tests/csa_tree/csa_tree_synth.ys @@ -1,17 +1,20 @@ +# ABC synthesis comparison: with vs without csa_tree + # Baseline: no csa_tree read_verilog abc_bench_add8.v hierarchy -top abc_bench_add8 proc; opt - -# Baseline synth techmap abc -g AND,OR,XOR opt_clean stat -design -save baseline + +# Baseline is typically 238 gates — assert it's above 235 +select -assert-min 236 t:$_AND_ t:$_OR_ t:$_XOR_ %u + +design -reset # With csa_tree -design -reset read_verilog abc_bench_add8.v hierarchy -top abc_bench_add8 proc; opt @@ -21,25 +24,36 @@ abc -g AND,OR,XOR opt_clean stat -select -assert-max 250 t:$_AND_ t:$_OR_ t:$_XOR_ t:$_NOT_ %u -design -save csa_result +# CSA was giving ~232 gates, assert rough equality +select -assert-max 235 t:$_AND_ t:$_OR_ t:$_XOR_ %u -# Depth comparison via ABC design -reset + +# Depth-optimal: baseline read_verilog abc_bench_add8.v hierarchy -top abc_bench_add8 proc; opt techmap abc -D 1 +opt_clean stat -log "baseline depth mapping complete" + +# Baseline depth-optimal is ~243 cells +select -assert-min 240 t:$_AND_ t:$_NAND_ t:$_OR_ t:$_NOR_ t:$_XOR_ t:$_XNOR_ t:$_NOT_ %u design -reset + +# Depth-optimal: with csa_tree read_verilog abc_bench_add8.v hierarchy -top abc_bench_add8 proc; opt csa_tree techmap abc -D 1 +opt_clean stat -log "CSA depth mapping complete" + +# CSA depth-optimal is ~232 cells, must be under baseline +select -assert-max 236 t:$_AND_ t:$_NAND_ t:$_OR_ t:$_NOR_ t:$_XOR_ t:$_XNOR_ t:$_NOT_ %u + +log "CSA depth and gate count: ok" diff --git a/tests/csa_tree/csa_tree_two_chains.ys b/tests/csa_tree/csa_tree_two_chains.ys index 10fbe8b15..bc852071b 100644 --- a/tests/csa_tree/csa_tree_two_chains.ys +++ b/tests/csa_tree/csa_tree_two_chains.ys @@ -5,5 +5,4 @@ equiv_opt csa_tree design -load postopt select -assert-min 2 t:$fa -select -assert-count 2 t:$add - +select -assert-count 2 t:$add \ No newline at end of file diff --git a/tests/csa_tree/csa_tree_wide_output.ys b/tests/csa_tree/csa_tree_wide_output.ys index 84cd13bd7..82003ff63 100644 --- a/tests/csa_tree/csa_tree_wide_output.ys +++ b/tests/csa_tree/csa_tree_wide_output.ys @@ -5,5 +5,4 @@ equiv_opt csa_tree design -load postopt select -assert-min 1 t:$fa -select -assert-count 1 t:$add - +select -assert-count 1 t:$add \ No newline at end of file diff --git a/tests/csa_tree/equiv_sub_narrow.v b/tests/csa_tree/equiv_sub_narrow.v new file mode 100644 index 000000000..3e40ee0bd --- /dev/null +++ b/tests/csa_tree/equiv_sub_narrow.v @@ -0,0 +1,27 @@ +module equiv_sub_mixed( + input [3:0] a, b, c, d, + output [3:0] y +); + assign y = a + b - c + d; +endmodule + +module equiv_sub_all( + input [3:0] a, b, c, d, + output [3:0] y +); + assign y = a - b - c - d; +endmodule + +module equiv_sub_3op( + input [3:0] a, b, c, + output [3:0] y +); + assign y = a - b + c; +endmodule + +module equiv_sub_signed( + input signed [3:0] a, b, c, d, + output signed [5:0] y +); + assign y = a + b - c - d; +endmodule diff --git a/tests/csa_tree/sub_2op_neg.v b/tests/csa_tree/sub_2op_neg.v new file mode 100644 index 000000000..33a320cef --- /dev/null +++ b/tests/csa_tree/sub_2op_neg.v @@ -0,0 +1,6 @@ +module sub_2op_neg( + input [7:0] a, b, + output [7:0] y +); + assign y = a - b; +endmodule diff --git a/tests/csa_tree/sub_3op.v b/tests/csa_tree/sub_3op.v new file mode 100644 index 000000000..270605e6f --- /dev/null +++ b/tests/csa_tree/sub_3op.v @@ -0,0 +1,6 @@ +module sub_3op( + input [7:0] a, b, c, + output [7:0] y +); + assign y = a - b + c; +endmodule diff --git a/tests/csa_tree/sub_5op.v b/tests/csa_tree/sub_5op.v new file mode 100644 index 000000000..1c18adab0 --- /dev/null +++ b/tests/csa_tree/sub_5op.v @@ -0,0 +1,6 @@ +module sub_5op( + input [11:0] a, b, c, d, e, + output [11:0] y +); + assign y = a - b + c - d + e; +endmodule diff --git a/tests/csa_tree/sub_all.v b/tests/csa_tree/sub_all.v new file mode 100644 index 000000000..33897159d --- /dev/null +++ b/tests/csa_tree/sub_all.v @@ -0,0 +1,6 @@ +module sub_all( + input [7:0] a, b, c, d, + output [7:0] y +); + assign y = a - b - c - d; +endmodule diff --git a/tests/csa_tree/sub_mixed.v b/tests/csa_tree/sub_mixed.v new file mode 100644 index 000000000..f711da3fc --- /dev/null +++ b/tests/csa_tree/sub_mixed.v @@ -0,0 +1,6 @@ +module sub_mixed( + input [7:0] a, b, c, d, + output [7:0] y +); + assign y = a + b - c + d; +endmodule diff --git a/tests/csa_tree/sub_signed.v b/tests/csa_tree/sub_signed.v new file mode 100644 index 000000000..6971840ab --- /dev/null +++ b/tests/csa_tree/sub_signed.v @@ -0,0 +1,6 @@ +module sub_signed( + input signed [7:0] a, b, c, d, + output signed [9:0] y +); + assign y = a + b - c - d; +endmodule From 4b037bda7aec5ed646bb688f4e33152d763b6b48 Mon Sep 17 00:00:00 2001 From: nella Date: Fri, 13 Mar 2026 13:22:24 +0100 Subject: [PATCH 08/15] Add more robsutness tests. --- tests/csa_tree/csa_tree_idempotent.ys | 17 +++++++++ tests/csa_tree/csa_tree_sub_double_neg.ys | 44 +++++++++++++++++++++++ tests/csa_tree/equiv_sub_double_neg.v | 7 ++++ tests/csa_tree/sub_double_neg.v | 7 ++++ 4 files changed, 75 insertions(+) create mode 100644 tests/csa_tree/csa_tree_idempotent.ys create mode 100644 tests/csa_tree/csa_tree_sub_double_neg.ys create mode 100644 tests/csa_tree/equiv_sub_double_neg.v create mode 100644 tests/csa_tree/sub_double_neg.v diff --git a/tests/csa_tree/csa_tree_idempotent.ys b/tests/csa_tree/csa_tree_idempotent.ys new file mode 100644 index 000000000..4bc7dc354 --- /dev/null +++ b/tests/csa_tree/csa_tree_idempotent.ys @@ -0,0 +1,17 @@ +# Running csa_tree twice, verify nothing changes on second run + +read_verilog add_chain_8.v +hierarchy -auto-top +proc; opt_clean + +csa_tree +stat +select -assert-min 1 t:$fa +select -assert-count 1 t:$add + +csa_tree +stat + +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +select -assert-none t:$sub diff --git a/tests/csa_tree/csa_tree_sub_double_neg.ys b/tests/csa_tree/csa_tree_sub_double_neg.ys new file mode 100644 index 000000000..f3911aee3 --- /dev/null +++ b/tests/csa_tree/csa_tree_sub_double_neg.ys @@ -0,0 +1,44 @@ +# Test double negation + +read_verilog sub_double_neg.v +hierarchy -auto-top +proc; opt_clean +csa_tree +stat + +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +select -assert-none t:$sub + +design -reset + +# equiv_opt on narrow version +read_verilog equiv_sub_double_neg.v +hierarchy -top equiv_sub_double_neg +proc; opt_clean +equiv_opt csa_tree +design -load postopt +select -assert-min 1 t:$fa +select -assert-count 1 t:$add +design -reset + +# sat-prove on full width +read_verilog sub_double_neg.v +hierarchy -top sub_double_neg +proc; opt_clean +csa_tree +opt_clean + +# 10 - (30 - 20) = 10 - 10 = 0 +sat -set a 30 -set b 20 -set c 10 -prove y 0 + +# 100 - (50 - 25) = 100 - 25 = 75 +sat -set a 50 -set b 25 -set c 100 -prove y 75 + +# 0 - (0 - 0) = 0 +sat -set a 0 -set b 0 -set c 0 -prove y 0 + +# 1 - (255 - 1) = 1 - 254 = 3 +sat -set a 255 -set b 1 -set c 1 -prove y 3 + +log "double negation: ok" diff --git a/tests/csa_tree/equiv_sub_double_neg.v b/tests/csa_tree/equiv_sub_double_neg.v new file mode 100644 index 000000000..8d03da23f --- /dev/null +++ b/tests/csa_tree/equiv_sub_double_neg.v @@ -0,0 +1,7 @@ +module equiv_sub_double_neg( + input [3:0] a, b, c, + output [3:0] y +); + wire [3:0] ab = a - b; + assign y = c - ab; +endmodule diff --git a/tests/csa_tree/sub_double_neg.v b/tests/csa_tree/sub_double_neg.v new file mode 100644 index 000000000..7172ccd09 --- /dev/null +++ b/tests/csa_tree/sub_double_neg.v @@ -0,0 +1,7 @@ +module sub_double_neg( + input [7:0] a, b, c, + output [7:0] y +); + wire [7:0] ab = a - b; + assign y = c - ab; +endmodule From 18c7cb094e6e73d2eb40ff38ff5e3c75c054fbf0 Mon Sep 17 00:00:00 2001 From: nella Date: Mon, 16 Mar 2026 15:39:54 +0100 Subject: [PATCH 09/15] Add csa to synth. --- techlibs/common/synth.cc | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index 0dbb7cbec..e781ee3ab 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -67,6 +67,10 @@ struct SynthPass : public ScriptPass { log(" -booth\n"); log(" run the booth pass to map $mul to Booth encoded multipliers\n"); log("\n"); + log(" -csa\n"); + log(" run the csa_tree pass to convert $add/$sub chains to\n"); + log(" carry-save adder trees.\n"); + log("\n"); log(" -noalumacc\n"); log(" do not run 'alumacc' pass. i.e. keep arithmetic operators in\n"); log(" their direct form ($add, $sub, etc.).\n"); @@ -108,7 +112,7 @@ struct SynthPass : public ScriptPass { } string top_module, fsm_opts, memory_opts, abc; - bool autotop, flatten, noalumacc, nofsm, noabc, noshare, flowmap, booth, hieropt, relative_share; + bool autotop, flatten, noalumacc, nofsm, noabc, noshare, flowmap, booth, csa, hieropt, relative_share; int lut; std::vector techmap_maps; @@ -127,6 +131,7 @@ struct SynthPass : public ScriptPass { noshare = false; flowmap = false; booth = false; + csa = false; hieropt = false; relative_share = false; abc = "abc"; @@ -187,7 +192,10 @@ struct SynthPass : public ScriptPass { booth = true; continue; } - + if (args[argidx] == "-csa") { + csa = true; + continue; + } if (args[argidx] == "-nordff") { memory_opts += " -nordff"; continue; @@ -287,6 +295,8 @@ struct SynthPass : public ScriptPass { run(stringf("%s -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=%d", techmap_cmd, lut)); if (booth || help_mode) run("booth", " (if -booth)"); + if (csa || help_mode) + run("csa_tree", " (if -csa)"); if (!noalumacc) run("alumacc", " (unless -noalumacc)"); if (!noshare) @@ -301,7 +311,7 @@ struct SynthPass : public ScriptPass { run("memory_map"); run("opt -full"); if (help_mode) { - run(techmap_cmd, " (unless -extra-map)"); + run(techmap_cmd, " (unless -extra-map)"); run(techmap_cmd + " -map +/techmap.v -map ", " (if -extra-map)"); } else { std::string techmap_opts; From 4ce8e7d1df4b261d133ac835b698526debf8a52c Mon Sep 17 00:00:00 2001 From: nella Date: Mon, 16 Mar 2026 16:23:42 +0100 Subject: [PATCH 10/15] Tighten csa tests. --- tests/csa_tree/csa_tree_16input.ys | 2 +- tests/csa_tree/csa_tree_1bit.ys | 4 +--- tests/csa_tree/csa_tree_1bit_wide_out.ys | 2 +- tests/csa_tree/csa_tree_3input.ys | 7 ++----- tests/csa_tree/csa_tree_5input.ys | 9 +++------ tests/csa_tree/csa_tree_8input.ys | 3 +-- tests/csa_tree/csa_tree_const.ys | 7 +++---- tests/csa_tree/csa_tree_equiv.ys | 22 +++++++++++----------- tests/csa_tree/csa_tree_fir.ys | 8 ++++---- tests/csa_tree/csa_tree_idempotent.ys | 6 ++---- tests/csa_tree/csa_tree_mixed_widths.ys | 7 +++---- tests/csa_tree/csa_tree_partial_chain.ys | 4 ++-- tests/csa_tree/csa_tree_repeated.ys | 2 +- tests/csa_tree/csa_tree_signed.ys | 7 +++---- tests/csa_tree/csa_tree_sub_3op.ys | 5 ++--- tests/csa_tree/csa_tree_sub_5op.ys | 3 ++- tests/csa_tree/csa_tree_sub_all.ys | 3 ++- tests/csa_tree/csa_tree_sub_double_neg.ys | 14 ++++++-------- tests/csa_tree/csa_tree_sub_equiv.ys | 10 ++++------ tests/csa_tree/csa_tree_sub_mixed.ys | 5 ++--- tests/csa_tree/csa_tree_sub_signed.ys | 3 ++- tests/csa_tree/csa_tree_two_chains.ys | 8 ++++---- tests/csa_tree/csa_tree_wide_output.ys | 8 ++++---- 23 files changed, 66 insertions(+), 83 deletions(-) diff --git a/tests/csa_tree/csa_tree_16input.ys b/tests/csa_tree/csa_tree_16input.ys index 230d5f845..46f51d44c 100644 --- a/tests/csa_tree/csa_tree_16input.ys +++ b/tests/csa_tree/csa_tree_16input.ys @@ -4,5 +4,5 @@ proc; opt_clean csa_tree stat -select -assert-min 5 t:$fa +select -assert-count 14 t:$fa select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_1bit.ys b/tests/csa_tree/csa_tree_1bit.ys index 4245f3d24..4c1f7745a 100644 --- a/tests/csa_tree/csa_tree_1bit.ys +++ b/tests/csa_tree/csa_tree_1bit.ys @@ -1,10 +1,8 @@ -# Test csa_tree with single-bit operands — carry shift edge case - read_verilog add_1bit.v hierarchy -auto-top proc; opt_clean csa_tree stat -select -assert-min 1 t:$fa +select -assert-count 1 t:$fa select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_1bit_wide_out.ys b/tests/csa_tree/csa_tree_1bit_wide_out.ys index 5e2605340..675fbc0a3 100644 --- a/tests/csa_tree/csa_tree_1bit_wide_out.ys +++ b/tests/csa_tree/csa_tree_1bit_wide_out.ys @@ -4,5 +4,5 @@ proc; opt_clean csa_tree stat -select -assert-min 1 t:$fa +select -assert-count 2 t:$fa select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_3input.ys b/tests/csa_tree/csa_tree_3input.ys index 68ff2ff2e..9e7c9aaf2 100644 --- a/tests/csa_tree/csa_tree_3input.ys +++ b/tests/csa_tree/csa_tree_3input.ys @@ -1,11 +1,8 @@ -# Test csa_tree on 3-operand chain — minimal trigger case - read_verilog add_chain_3.v hierarchy -auto-top proc; opt_clean -equiv_opt csa_tree -design -load postopt +csa_tree +stat select -assert-count 1 t:$fa select -assert-count 1 t:$add - diff --git a/tests/csa_tree/csa_tree_5input.ys b/tests/csa_tree/csa_tree_5input.ys index dadea092f..ef26e21ce 100644 --- a/tests/csa_tree/csa_tree_5input.ys +++ b/tests/csa_tree/csa_tree_5input.ys @@ -1,11 +1,8 @@ -# Test csa_tree with 5 operands — tree with remainders - read_verilog add_chain_5.v hierarchy -auto-top proc; opt_clean -equiv_opt csa_tree -design -load postopt +csa_tree +stat -select -assert-min 2 t:$fa +select -assert-count 3 t:$fa select -assert-count 1 t:$add - diff --git a/tests/csa_tree/csa_tree_8input.ys b/tests/csa_tree/csa_tree_8input.ys index 23d2d698a..fc5148edf 100644 --- a/tests/csa_tree/csa_tree_8input.ys +++ b/tests/csa_tree/csa_tree_8input.ys @@ -4,6 +4,5 @@ proc; opt_clean csa_tree stat -select -assert-min 1 t:$fa +select -assert-count 6 t:$fa select -assert-count 1 t:$add - diff --git a/tests/csa_tree/csa_tree_const.ys b/tests/csa_tree/csa_tree_const.ys index 75d0bc1d7..be53bba11 100644 --- a/tests/csa_tree/csa_tree_const.ys +++ b/tests/csa_tree/csa_tree_const.ys @@ -1,9 +1,8 @@ read_verilog add_with_const.v hierarchy -auto-top proc; opt_clean -equiv_opt csa_tree -design -load postopt +csa_tree +stat -select -assert-min 1 t:$fa +select -assert-count 2 t:$fa select -assert-count 1 t:$add - diff --git a/tests/csa_tree/csa_tree_equiv.ys b/tests/csa_tree/csa_tree_equiv.ys index ac976e1b3..360b9d30e 100644 --- a/tests/csa_tree/csa_tree_equiv.ys +++ b/tests/csa_tree/csa_tree_equiv.ys @@ -1,4 +1,4 @@ -# Test bit correctness +# Equivalence tests using narrow operands read_verilog equiv_narrow.v hierarchy -top equiv_add3 @@ -8,34 +8,34 @@ design -load postopt select -assert-count 1 t:$fa select -assert-count 1 t:$add design -reset -log "equiv_add3: ok" +log "equiv_add3" read_verilog equiv_narrow.v hierarchy -top equiv_add4 proc; opt_clean equiv_opt csa_tree design -load postopt -select -assert-min 1 t:$fa +select -assert-count 2 t:$fa select -assert-count 1 t:$add design -reset -log "equiv_add4: ok" +log "equiv_add4" read_verilog equiv_narrow.v hierarchy -top equiv_add5 proc; opt_clean equiv_opt csa_tree design -load postopt -select -assert-min 2 t:$fa +select -assert-count 3 t:$fa select -assert-count 1 t:$add design -reset -log "equiv_add5: ok" +log "equiv_add5" read_verilog equiv_narrow.v hierarchy -top equiv_add8 proc; opt_clean equiv_opt csa_tree design -load postopt -select -assert-min 1 t:$fa +select -assert-count 6 t:$fa select -assert-count 1 t:$add design -reset log "equiv_add8: ok" @@ -45,7 +45,7 @@ hierarchy -top equiv_signed proc; opt_clean equiv_opt csa_tree design -load postopt -select -assert-min 1 t:$fa +select -assert-count 2 t:$fa select -assert-count 1 t:$add design -reset log "equiv_signed: ok" @@ -55,7 +55,7 @@ hierarchy -top equiv_mixed_w proc; opt_clean equiv_opt csa_tree design -load postopt -select -assert-min 1 t:$fa +select -assert-count 1 t:$fa select -assert-count 1 t:$add design -reset log "equiv_mixed_w: ok" @@ -65,7 +65,7 @@ hierarchy -top equiv_repeated proc; opt_clean equiv_opt csa_tree design -load postopt -select -assert-min 1 t:$fa +select -assert-count 2 t:$fa select -assert-count 1 t:$add design -reset log "equiv_repeated: ok" @@ -75,7 +75,7 @@ hierarchy -top equiv_1bit_wide proc; opt_clean equiv_opt csa_tree design -load postopt -select -assert-min 1 t:$fa +select -assert-count 2 t:$fa select -assert-count 1 t:$add design -reset log "equiv_1bit_wide: ok" diff --git a/tests/csa_tree/csa_tree_fir.ys b/tests/csa_tree/csa_tree_fir.ys index 59aac98b0..89d60af03 100644 --- a/tests/csa_tree/csa_tree_fir.ys +++ b/tests/csa_tree/csa_tree_fir.ys @@ -1,8 +1,8 @@ read_verilog fir_4tap.v hierarchy -auto-top proc; opt_clean -equiv_opt -async2sync csa_tree -design -load postopt - -select -assert-min 1 t:$fa +csa_tree +stat +select -assert-count 2 t:$fa +select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_idempotent.ys b/tests/csa_tree/csa_tree_idempotent.ys index 4bc7dc354..138edb4ae 100644 --- a/tests/csa_tree/csa_tree_idempotent.ys +++ b/tests/csa_tree/csa_tree_idempotent.ys @@ -1,17 +1,15 @@ -# Running csa_tree twice, verify nothing changes on second run - read_verilog add_chain_8.v hierarchy -auto-top proc; opt_clean csa_tree stat -select -assert-min 1 t:$fa +select -assert-count 6 t:$fa select -assert-count 1 t:$add csa_tree stat -select -assert-min 1 t:$fa +select -assert-count 6 t:$fa select -assert-count 1 t:$add select -assert-none t:$sub diff --git a/tests/csa_tree/csa_tree_mixed_widths.ys b/tests/csa_tree/csa_tree_mixed_widths.ys index 1cecae201..e324c17ca 100644 --- a/tests/csa_tree/csa_tree_mixed_widths.ys +++ b/tests/csa_tree/csa_tree_mixed_widths.ys @@ -1,9 +1,8 @@ read_verilog add_mixed_widths.v hierarchy -auto-top proc; opt_clean -equiv_opt csa_tree -design -load postopt +csa_tree +stat -select -assert-min 1 t:$fa +select -assert-count 2 t:$fa select -assert-count 1 t:$add - diff --git a/tests/csa_tree/csa_tree_partial_chain.ys b/tests/csa_tree/csa_tree_partial_chain.ys index 3852c4f9d..ba857292e 100644 --- a/tests/csa_tree/csa_tree_partial_chain.ys +++ b/tests/csa_tree/csa_tree_partial_chain.ys @@ -4,5 +4,5 @@ proc; opt_clean csa_tree stat -select -assert-min 1 t:$fa -select -assert-min 2 t:$add +select -assert-count 2 t:$fa +select -assert-count 2 t:$add diff --git a/tests/csa_tree/csa_tree_repeated.ys b/tests/csa_tree/csa_tree_repeated.ys index 7e1c2ec51..b4581d2b4 100644 --- a/tests/csa_tree/csa_tree_repeated.ys +++ b/tests/csa_tree/csa_tree_repeated.ys @@ -4,5 +4,5 @@ proc; opt_clean csa_tree stat -select -assert-min 1 t:$fa +select -assert-count 2 t:$fa select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_signed.ys b/tests/csa_tree/csa_tree_signed.ys index 7e29cc123..1093f7330 100644 --- a/tests/csa_tree/csa_tree_signed.ys +++ b/tests/csa_tree/csa_tree_signed.ys @@ -1,9 +1,8 @@ read_verilog add_signed.v hierarchy -auto-top proc; opt_clean -equiv_opt csa_tree -design -load postopt +csa_tree +stat -select -assert-min 1 t:$fa +select -assert-count 2 t:$fa select -assert-count 1 t:$add - diff --git a/tests/csa_tree/csa_tree_sub_3op.ys b/tests/csa_tree/csa_tree_sub_3op.ys index 70801d30c..536f65bd7 100644 --- a/tests/csa_tree/csa_tree_sub_3op.ys +++ b/tests/csa_tree/csa_tree_sub_3op.ys @@ -1,11 +1,10 @@ -# Test minimal sub chain - read_verilog sub_3op.v hierarchy -auto-top proc; opt_clean csa_tree stat -select -assert-min 1 t:$fa +select -assert-count 2 t:$fa select -assert-count 1 t:$add +select -assert-count 1 t:$not select -assert-none t:$sub diff --git a/tests/csa_tree/csa_tree_sub_5op.ys b/tests/csa_tree/csa_tree_sub_5op.ys index 62c4a80ff..b40c97084 100644 --- a/tests/csa_tree/csa_tree_sub_5op.ys +++ b/tests/csa_tree/csa_tree_sub_5op.ys @@ -4,6 +4,7 @@ proc; opt_clean csa_tree stat -select -assert-min 2 t:$fa +select -assert-count 4 t:$fa select -assert-count 1 t:$add +select -assert-count 2 t:$not select -assert-none t:$sub diff --git a/tests/csa_tree/csa_tree_sub_all.ys b/tests/csa_tree/csa_tree_sub_all.ys index 8e47bb0db..ba0503462 100644 --- a/tests/csa_tree/csa_tree_sub_all.ys +++ b/tests/csa_tree/csa_tree_sub_all.ys @@ -4,6 +4,7 @@ proc; opt_clean csa_tree stat -select -assert-min 1 t:$fa +select -assert-count 3 t:$fa select -assert-count 1 t:$add +select -assert-count 3 t:$not select -assert-none t:$sub diff --git a/tests/csa_tree/csa_tree_sub_double_neg.ys b/tests/csa_tree/csa_tree_sub_double_neg.ys index f3911aee3..b3a524abb 100644 --- a/tests/csa_tree/csa_tree_sub_double_neg.ys +++ b/tests/csa_tree/csa_tree_sub_double_neg.ys @@ -1,13 +1,12 @@ -# Test double negation - read_verilog sub_double_neg.v hierarchy -auto-top proc; opt_clean csa_tree stat -select -assert-min 1 t:$fa +select -assert-count 2 t:$fa select -assert-count 1 t:$add +select -assert-count 1 t:$not select -assert-none t:$sub design -reset @@ -18,27 +17,26 @@ hierarchy -top equiv_sub_double_neg proc; opt_clean equiv_opt csa_tree design -load postopt -select -assert-min 1 t:$fa +select -assert-count 2 t:$fa select -assert-count 1 t:$add design -reset -# sat-prove on full width read_verilog sub_double_neg.v hierarchy -top sub_double_neg proc; opt_clean csa_tree opt_clean -# 10 - (30 - 20) = 10 - 10 = 0 +# 10 - (30 - 20) = 0 sat -set a 30 -set b 20 -set c 10 -prove y 0 -# 100 - (50 - 25) = 100 - 25 = 75 +# 100 - (50 - 25) = 75 sat -set a 50 -set b 25 -set c 100 -prove y 75 # 0 - (0 - 0) = 0 sat -set a 0 -set b 0 -set c 0 -prove y 0 -# 1 - (255 - 1) = 1 - 254 = 3 +# 1 - (255 - 1) = 3 sat -set a 255 -set b 1 -set c 1 -prove y 3 log "double negation: ok" diff --git a/tests/csa_tree/csa_tree_sub_equiv.ys b/tests/csa_tree/csa_tree_sub_equiv.ys index b1be6bebe..7138b010c 100644 --- a/tests/csa_tree/csa_tree_sub_equiv.ys +++ b/tests/csa_tree/csa_tree_sub_equiv.ys @@ -1,11 +1,9 @@ -# Test equiv_opt on narrow sub designs - read_verilog equiv_sub_narrow.v hierarchy -top equiv_sub_mixed proc; opt_clean equiv_opt csa_tree design -load postopt -select -assert-min 1 t:$fa +select -assert-count 3 t:$fa select -assert-count 1 t:$add design -reset log "equiv_sub_mixed: ok" @@ -15,7 +13,7 @@ hierarchy -top equiv_sub_all proc; opt_clean equiv_opt csa_tree design -load postopt -select -assert-min 1 t:$fa +select -assert-count 3 t:$fa select -assert-count 1 t:$add design -reset log "equiv_sub_all: ok" @@ -25,7 +23,7 @@ hierarchy -top equiv_sub_3op proc; opt_clean equiv_opt csa_tree design -load postopt -select -assert-min 1 t:$fa +select -assert-count 2 t:$fa select -assert-count 1 t:$add design -reset log "equiv_sub_3op: ok" @@ -35,7 +33,7 @@ hierarchy -top equiv_sub_signed proc; opt_clean equiv_opt csa_tree design -load postopt -select -assert-min 1 t:$fa +select -assert-count 3 t:$fa select -assert-count 1 t:$add design -reset log "equiv_sub_signed: ok" diff --git a/tests/csa_tree/csa_tree_sub_mixed.ys b/tests/csa_tree/csa_tree_sub_mixed.ys index 8fea5f0d2..92e0e1a0d 100644 --- a/tests/csa_tree/csa_tree_sub_mixed.ys +++ b/tests/csa_tree/csa_tree_sub_mixed.ys @@ -1,11 +1,10 @@ -# Test mixed csa chain - read_verilog sub_mixed.v hierarchy -auto-top proc; opt_clean csa_tree stat -select -assert-min 1 t:$fa +select -assert-count 3 t:$fa select -assert-count 1 t:$add +select -assert-count 1 t:$not select -assert-none t:$sub diff --git a/tests/csa_tree/csa_tree_sub_signed.ys b/tests/csa_tree/csa_tree_sub_signed.ys index 78caf5a2e..ad3b0cfb4 100644 --- a/tests/csa_tree/csa_tree_sub_signed.ys +++ b/tests/csa_tree/csa_tree_sub_signed.ys @@ -4,6 +4,7 @@ proc; opt_clean csa_tree stat -select -assert-min 1 t:$fa +select -assert-count 3 t:$fa select -assert-count 1 t:$add +select -assert-count 2 t:$not select -assert-none t:$sub diff --git a/tests/csa_tree/csa_tree_two_chains.ys b/tests/csa_tree/csa_tree_two_chains.ys index bc852071b..42d5c87b9 100644 --- a/tests/csa_tree/csa_tree_two_chains.ys +++ b/tests/csa_tree/csa_tree_two_chains.ys @@ -1,8 +1,8 @@ read_verilog add_two_chains.v hierarchy -auto-top proc; opt_clean -equiv_opt csa_tree -design -load postopt +csa_tree +stat -select -assert-min 2 t:$fa -select -assert-count 2 t:$add \ No newline at end of file +select -assert-count 4 t:$fa +select -assert-count 2 t:$add diff --git a/tests/csa_tree/csa_tree_wide_output.ys b/tests/csa_tree/csa_tree_wide_output.ys index 82003ff63..ccb160f5c 100644 --- a/tests/csa_tree/csa_tree_wide_output.ys +++ b/tests/csa_tree/csa_tree_wide_output.ys @@ -1,8 +1,8 @@ read_verilog add_wide_output.v hierarchy -auto-top proc; opt_clean -equiv_opt csa_tree -design -load postopt +csa_tree +stat -select -assert-min 1 t:$fa -select -assert-count 1 t:$add \ No newline at end of file +select -assert-count 2 t:$fa +select -assert-count 1 t:$add From 1c1d782bf85ca3be75f7eca3e7a58547dcfa48e7 Mon Sep 17 00:00:00 2001 From: nella Date: Wed, 18 Mar 2026 12:36:31 +0100 Subject: [PATCH 11/15] Consolidate csa tests. --- tests/csa_tree/abc_bench_add8.v | 6 - tests/csa_tree/add_1bit.v | 9 -- tests/csa_tree/add_1bit_wide_out.v | 6 - tests/csa_tree/add_chain_16.v | 7 - tests/csa_tree/add_chain_2_neg.v | 8 -- tests/csa_tree/add_chain_3.v | 9 -- tests/csa_tree/add_chain_5.v | 7 - tests/csa_tree/add_chain_8.v | 7 - tests/csa_tree/add_mixed_widths.v | 10 -- tests/csa_tree/add_multi_const.v | 6 - tests/csa_tree/add_multi_fanout.v | 10 -- tests/csa_tree/add_partial_chain.v | 9 -- tests/csa_tree/add_repeated.v | 6 - tests/csa_tree/add_signed.v | 6 - tests/csa_tree/add_two_chains.v | 9 -- tests/csa_tree/add_wide_output.v | 6 - tests/csa_tree/add_with_const.v | 7 - tests/csa_tree/csa_tree_16input.ys | 8 -- tests/csa_tree/csa_tree_1bit.ys | 8 -- tests/csa_tree/csa_tree_1bit_wide_out.ys | 8 -- tests/csa_tree/csa_tree_2input_neg.ys | 11 -- tests/csa_tree/csa_tree_3input.ys | 8 -- tests/csa_tree/csa_tree_5input.ys | 8 -- tests/csa_tree/csa_tree_8input.ys | 8 -- tests/csa_tree/csa_tree_add_chains.ys | 61 ++++++++ tests/csa_tree/csa_tree_const.ys | 8 -- tests/csa_tree/csa_tree_edge_cases.ys | 147 +++++++++++++++++++ tests/csa_tree/csa_tree_equiv.ys | 165 +++++++++++++++++----- tests/csa_tree/csa_tree_fir.ys | 8 -- tests/csa_tree/csa_tree_idempotent.ys | 14 +- tests/csa_tree/csa_tree_mixed_widths.ys | 8 -- tests/csa_tree/csa_tree_multi_const.ys | 9 -- tests/csa_tree/csa_tree_multi_fanout.ys | 8 -- tests/csa_tree/csa_tree_negative.ys | 77 ++++++++++ tests/csa_tree/csa_tree_partial_chain.ys | 8 -- tests/csa_tree/csa_tree_repeated.ys | 8 -- tests/csa_tree/csa_tree_signed.ys | 8 -- tests/csa_tree/csa_tree_sim.ys | 77 ++++++++-- tests/csa_tree/csa_tree_sub_2op_neg.ys | 8 -- tests/csa_tree/csa_tree_sub_3op.ys | 10 -- tests/csa_tree/csa_tree_sub_5op.ys | 10 -- tests/csa_tree/csa_tree_sub_all.ys | 10 -- tests/csa_tree/csa_tree_sub_chains.ys | 102 +++++++++++++ tests/csa_tree/csa_tree_sub_double_neg.ys | 42 ------ tests/csa_tree/csa_tree_sub_equiv.ys | 39 ----- tests/csa_tree/csa_tree_sub_mixed.ys | 10 -- tests/csa_tree/csa_tree_sub_signed.ys | 10 -- tests/csa_tree/csa_tree_sub_sim.ys | 38 ----- tests/csa_tree/csa_tree_synth.ys | 87 +++++++----- tests/csa_tree/csa_tree_two_chains.ys | 8 -- tests/csa_tree/csa_tree_wide_output.ys | 8 -- tests/csa_tree/equiv_narrow.v | 59 -------- tests/csa_tree/equiv_sub_double_neg.v | 7 - tests/csa_tree/equiv_sub_narrow.v | 27 ---- tests/csa_tree/fir_4tap.v | 24 ---- tests/csa_tree/run-test.sh | 4 +- tests/csa_tree/sim_add4.v | 6 - tests/csa_tree/sub_2op_neg.v | 6 - tests/csa_tree/sub_3op.v | 6 - tests/csa_tree/sub_5op.v | 6 - tests/csa_tree/sub_all.v | 6 - tests/csa_tree/sub_double_neg.v | 7 - tests/csa_tree/sub_mixed.v | 6 - tests/csa_tree/sub_signed.v | 6 - 64 files changed, 641 insertions(+), 704 deletions(-) delete mode 100644 tests/csa_tree/abc_bench_add8.v delete mode 100644 tests/csa_tree/add_1bit.v delete mode 100644 tests/csa_tree/add_1bit_wide_out.v delete mode 100644 tests/csa_tree/add_chain_16.v delete mode 100644 tests/csa_tree/add_chain_2_neg.v delete mode 100644 tests/csa_tree/add_chain_3.v delete mode 100644 tests/csa_tree/add_chain_5.v delete mode 100644 tests/csa_tree/add_chain_8.v delete mode 100644 tests/csa_tree/add_mixed_widths.v delete mode 100644 tests/csa_tree/add_multi_const.v delete mode 100644 tests/csa_tree/add_multi_fanout.v delete mode 100644 tests/csa_tree/add_partial_chain.v delete mode 100644 tests/csa_tree/add_repeated.v delete mode 100644 tests/csa_tree/add_signed.v delete mode 100644 tests/csa_tree/add_two_chains.v delete mode 100644 tests/csa_tree/add_wide_output.v delete mode 100644 tests/csa_tree/add_with_const.v delete mode 100644 tests/csa_tree/csa_tree_16input.ys delete mode 100644 tests/csa_tree/csa_tree_1bit.ys delete mode 100644 tests/csa_tree/csa_tree_1bit_wide_out.ys delete mode 100644 tests/csa_tree/csa_tree_2input_neg.ys delete mode 100644 tests/csa_tree/csa_tree_3input.ys delete mode 100644 tests/csa_tree/csa_tree_5input.ys delete mode 100644 tests/csa_tree/csa_tree_8input.ys create mode 100644 tests/csa_tree/csa_tree_add_chains.ys delete mode 100644 tests/csa_tree/csa_tree_const.ys create mode 100644 tests/csa_tree/csa_tree_edge_cases.ys delete mode 100644 tests/csa_tree/csa_tree_fir.ys delete mode 100644 tests/csa_tree/csa_tree_mixed_widths.ys delete mode 100644 tests/csa_tree/csa_tree_multi_const.ys delete mode 100644 tests/csa_tree/csa_tree_multi_fanout.ys create mode 100644 tests/csa_tree/csa_tree_negative.ys delete mode 100644 tests/csa_tree/csa_tree_partial_chain.ys delete mode 100644 tests/csa_tree/csa_tree_repeated.ys delete mode 100644 tests/csa_tree/csa_tree_signed.ys delete mode 100644 tests/csa_tree/csa_tree_sub_2op_neg.ys delete mode 100644 tests/csa_tree/csa_tree_sub_3op.ys delete mode 100644 tests/csa_tree/csa_tree_sub_5op.ys delete mode 100644 tests/csa_tree/csa_tree_sub_all.ys create mode 100644 tests/csa_tree/csa_tree_sub_chains.ys delete mode 100644 tests/csa_tree/csa_tree_sub_double_neg.ys delete mode 100644 tests/csa_tree/csa_tree_sub_equiv.ys delete mode 100644 tests/csa_tree/csa_tree_sub_mixed.ys delete mode 100644 tests/csa_tree/csa_tree_sub_signed.ys delete mode 100644 tests/csa_tree/csa_tree_sub_sim.ys delete mode 100644 tests/csa_tree/csa_tree_two_chains.ys delete mode 100644 tests/csa_tree/csa_tree_wide_output.ys delete mode 100644 tests/csa_tree/equiv_narrow.v delete mode 100644 tests/csa_tree/equiv_sub_double_neg.v delete mode 100644 tests/csa_tree/equiv_sub_narrow.v delete mode 100644 tests/csa_tree/fir_4tap.v delete mode 100644 tests/csa_tree/sim_add4.v delete mode 100644 tests/csa_tree/sub_2op_neg.v delete mode 100644 tests/csa_tree/sub_3op.v delete mode 100644 tests/csa_tree/sub_5op.v delete mode 100644 tests/csa_tree/sub_all.v delete mode 100644 tests/csa_tree/sub_double_neg.v delete mode 100644 tests/csa_tree/sub_mixed.v delete mode 100644 tests/csa_tree/sub_signed.v diff --git a/tests/csa_tree/abc_bench_add8.v b/tests/csa_tree/abc_bench_add8.v deleted file mode 100644 index 7e61007c5..000000000 --- a/tests/csa_tree/abc_bench_add8.v +++ /dev/null @@ -1,6 +0,0 @@ -module abc_bench_add8( - input [7:0] a, b, c, d, e, f, g, h, - output [7:0] y -); - assign y = a + b + c + d + e + f + g + h; -endmodule diff --git a/tests/csa_tree/add_1bit.v b/tests/csa_tree/add_1bit.v deleted file mode 100644 index e4b93e8c0..000000000 --- a/tests/csa_tree/add_1bit.v +++ /dev/null @@ -1,9 +0,0 @@ -// Edge case for carry shifting - -module add_1bit( - input a, b, c, - output [1:0] y -); - assign y = a + b + c; -endmodule - diff --git a/tests/csa_tree/add_1bit_wide_out.v b/tests/csa_tree/add_1bit_wide_out.v deleted file mode 100644 index e1a6ceb51..000000000 --- a/tests/csa_tree/add_1bit_wide_out.v +++ /dev/null @@ -1,6 +0,0 @@ -module add_1bit_wide_out( - input a, b, c, d, - output [3:0] y -); - assign y = a + b + c + d; -endmodule diff --git a/tests/csa_tree/add_chain_16.v b/tests/csa_tree/add_chain_16.v deleted file mode 100644 index 0a1f12f8c..000000000 --- a/tests/csa_tree/add_chain_16.v +++ /dev/null @@ -1,7 +0,0 @@ -module add_chain_16( - input [15:0] a0, a1, a2, a3, a4, a5, a6, a7, - input [15:0] a8, a9, a10, a11, a12, a13, a14, a15, - output [15:0] y -); - assign y = a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15; -endmodule diff --git a/tests/csa_tree/add_chain_2_neg.v b/tests/csa_tree/add_chain_2_neg.v deleted file mode 100644 index 0e2896c78..000000000 --- a/tests/csa_tree/add_chain_2_neg.v +++ /dev/null @@ -1,8 +0,0 @@ -// Shouldnt generate csa tree - -module add_chain_2( - input [7:0] a, b, - output [7:0] y -); - assign y = a + b; -endmodule diff --git a/tests/csa_tree/add_chain_3.v b/tests/csa_tree/add_chain_3.v deleted file mode 100644 index eea529ead..000000000 --- a/tests/csa_tree/add_chain_3.v +++ /dev/null @@ -1,9 +0,0 @@ -// Min chain len - -module add_chain_3( - input [7:0] a, b, c, - output [7:0] y -); - assign y = a + b + c; -endmodule - diff --git a/tests/csa_tree/add_chain_5.v b/tests/csa_tree/add_chain_5.v deleted file mode 100644 index 25fb30e1d..000000000 --- a/tests/csa_tree/add_chain_5.v +++ /dev/null @@ -1,7 +0,0 @@ -module add_chain_5( - input [11:0] a, b, c, d, e, - output [11:0] y -); - assign y = a + b + c + d + e; -endmodule - diff --git a/tests/csa_tree/add_chain_8.v b/tests/csa_tree/add_chain_8.v deleted file mode 100644 index 96adbc37e..000000000 --- a/tests/csa_tree/add_chain_8.v +++ /dev/null @@ -1,7 +0,0 @@ -module add_chain_8( - input [15:0] a, b, c, d, e, f, g, h, - output [15:0] y -); - assign y = a + b + c + d + e + f + g + h; -endmodule - diff --git a/tests/csa_tree/add_mixed_widths.v b/tests/csa_tree/add_mixed_widths.v deleted file mode 100644 index 51836872a..000000000 --- a/tests/csa_tree/add_mixed_widths.v +++ /dev/null @@ -1,10 +0,0 @@ -module add_mixed_widths( - input [7:0] a, - input [3:0] b, - input [15:0] c, - input [7:0] d, - output [15:0] y -); - assign y = a + b + c + d; -endmodule - diff --git a/tests/csa_tree/add_multi_const.v b/tests/csa_tree/add_multi_const.v deleted file mode 100644 index 7da78f9de..000000000 --- a/tests/csa_tree/add_multi_const.v +++ /dev/null @@ -1,6 +0,0 @@ -module add_multi_const( - input [7:0] x, - output [7:0] y -); - assign y = 8'd1 + 8'd2 + 8'd3 + x; -endmodule diff --git a/tests/csa_tree/add_multi_fanout.v b/tests/csa_tree/add_multi_fanout.v deleted file mode 100644 index 4cc7ecab3..000000000 --- a/tests/csa_tree/add_multi_fanout.v +++ /dev/null @@ -1,10 +0,0 @@ -module add_multi_fanout( - input [7:0] a, b, c, - output [7:0] mid, - output [7:0] y -); - wire [7:0] ab = a + b; - assign mid = ab; - assign y = ab + c; -endmodule - diff --git a/tests/csa_tree/add_partial_chain.v b/tests/csa_tree/add_partial_chain.v deleted file mode 100644 index a4f20cfa4..000000000 --- a/tests/csa_tree/add_partial_chain.v +++ /dev/null @@ -1,9 +0,0 @@ -module add_partial_chain( - input [7:0] a, b, c, d, e, - output [7:0] mid, - output [7:0] y -); - wire [7:0] ab = a + b; - assign mid = ab; - assign y = ab + c + d + e; -endmodule diff --git a/tests/csa_tree/add_repeated.v b/tests/csa_tree/add_repeated.v deleted file mode 100644 index e3337097b..000000000 --- a/tests/csa_tree/add_repeated.v +++ /dev/null @@ -1,6 +0,0 @@ -module add_repeated( - input [7:0] a, - output [7:0] y -); - assign y = a + a + a + a; -endmodule diff --git a/tests/csa_tree/add_signed.v b/tests/csa_tree/add_signed.v deleted file mode 100644 index 42b9ca7fd..000000000 --- a/tests/csa_tree/add_signed.v +++ /dev/null @@ -1,6 +0,0 @@ -module add_signed( - input signed [7:0] a, b, c, d, - output signed [9:0] y -); - assign y = a + b + c + d; -endmodule diff --git a/tests/csa_tree/add_two_chains.v b/tests/csa_tree/add_two_chains.v deleted file mode 100644 index d79d9f3d6..000000000 --- a/tests/csa_tree/add_two_chains.v +++ /dev/null @@ -1,9 +0,0 @@ -module add_two_chains( - input [7:0] a, b, c, d, - input [7:0] e, f, g, h, - output [7:0] y1, - output [7:0] y2 -); - assign y1 = a + b + c + d; - assign y2 = e + f + g + h; -endmodule diff --git a/tests/csa_tree/add_wide_output.v b/tests/csa_tree/add_wide_output.v deleted file mode 100644 index 1f0342777..000000000 --- a/tests/csa_tree/add_wide_output.v +++ /dev/null @@ -1,6 +0,0 @@ -module add_wide_output( - input [7:0] a, b, c, d, - output [31:0] y -); - assign y = a + b + c + d; -endmodule diff --git a/tests/csa_tree/add_with_const.v b/tests/csa_tree/add_with_const.v deleted file mode 100644 index 570a399e4..000000000 --- a/tests/csa_tree/add_with_const.v +++ /dev/null @@ -1,7 +0,0 @@ -module add_with_const( - input [7:0] a, b, c, - output [7:0] y -); - assign y = a + b + c + 8'd42; -endmodule - diff --git a/tests/csa_tree/csa_tree_16input.ys b/tests/csa_tree/csa_tree_16input.ys deleted file mode 100644 index 46f51d44c..000000000 --- a/tests/csa_tree/csa_tree_16input.ys +++ /dev/null @@ -1,8 +0,0 @@ -read_verilog add_chain_16.v -hierarchy -auto-top -proc; opt_clean -csa_tree -stat - -select -assert-count 14 t:$fa -select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_1bit.ys b/tests/csa_tree/csa_tree_1bit.ys deleted file mode 100644 index 4c1f7745a..000000000 --- a/tests/csa_tree/csa_tree_1bit.ys +++ /dev/null @@ -1,8 +0,0 @@ -read_verilog add_1bit.v -hierarchy -auto-top -proc; opt_clean -csa_tree -stat - -select -assert-count 1 t:$fa -select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_1bit_wide_out.ys b/tests/csa_tree/csa_tree_1bit_wide_out.ys deleted file mode 100644 index 675fbc0a3..000000000 --- a/tests/csa_tree/csa_tree_1bit_wide_out.ys +++ /dev/null @@ -1,8 +0,0 @@ -read_verilog add_1bit_wide_out.v -hierarchy -auto-top -proc; opt_clean -csa_tree -stat - -select -assert-count 2 t:$fa -select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_2input_neg.ys b/tests/csa_tree/csa_tree_2input_neg.ys deleted file mode 100644 index 810d1d8d9..000000000 --- a/tests/csa_tree/csa_tree_2input_neg.ys +++ /dev/null @@ -1,11 +0,0 @@ -# Test csa_tree on 2-operand — should not trigger - -read_verilog add_chain_2_neg.v -hierarchy -auto-top -proc; opt_clean -equiv_opt csa_tree -design -load postopt - -select -assert-none t:$fa -select -assert-count 1 t:$add - diff --git a/tests/csa_tree/csa_tree_3input.ys b/tests/csa_tree/csa_tree_3input.ys deleted file mode 100644 index 9e7c9aaf2..000000000 --- a/tests/csa_tree/csa_tree_3input.ys +++ /dev/null @@ -1,8 +0,0 @@ -read_verilog add_chain_3.v -hierarchy -auto-top -proc; opt_clean -csa_tree -stat - -select -assert-count 1 t:$fa -select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_5input.ys b/tests/csa_tree/csa_tree_5input.ys deleted file mode 100644 index ef26e21ce..000000000 --- a/tests/csa_tree/csa_tree_5input.ys +++ /dev/null @@ -1,8 +0,0 @@ -read_verilog add_chain_5.v -hierarchy -auto-top -proc; opt_clean -csa_tree -stat - -select -assert-count 3 t:$fa -select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_8input.ys b/tests/csa_tree/csa_tree_8input.ys deleted file mode 100644 index fc5148edf..000000000 --- a/tests/csa_tree/csa_tree_8input.ys +++ /dev/null @@ -1,8 +0,0 @@ -read_verilog add_chain_8.v -hierarchy -auto-top -proc; opt_clean -csa_tree -stat - -select -assert-count 6 t:$fa -select -assert-count 1 t:$add diff --git a/tests/csa_tree/csa_tree_add_chains.ys b/tests/csa_tree/csa_tree_add_chains.ys new file mode 100644 index 000000000..46e5868a9 --- /dev/null +++ b/tests/csa_tree/csa_tree_add_chains.ys @@ -0,0 +1,61 @@ +read_verilog < Date: Thu, 19 Mar 2026 17:44:56 +0100 Subject: [PATCH 12/15] CSA add support for macc and alu cells. --- passes/opt/csa_tree.cc | 528 ++++++++++++++++++++++++----------------- 1 file changed, 306 insertions(+), 222 deletions(-) diff --git a/passes/opt/csa_tree.cc b/passes/opt/csa_tree.cc index e1a4a9464..de2e28901 100644 --- a/passes/opt/csa_tree.cc +++ b/passes/opt/csa_tree.cc @@ -1,43 +1,102 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/macc.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +struct Operand { + SigSpec sig; + bool is_signed; + bool negate; +}; + struct CsaTreeWorker { - Module *module; + Module* module; SigMap sigmap; dict> bit_consumers; dict fanout; - pool all_addsubs; - CsaTreeWorker(Module *module) : module(module), sigmap(module) {} + pool addsub_cells; + pool alu_cells; + pool macc_cells; - struct DepthSig { - SigSpec sig; - int depth; - }; + CsaTreeWorker(Module* module) : module(module), sigmap(module) {} - void find_addsubs() + static bool is_addsub(Cell* cell) { - for (auto cell : module->cells()) - if (cell->type == ID($add) || cell->type == ID($sub)) - all_addsubs.insert(cell); + return cell->type == ID($add) || cell->type == ID($sub); + } + + static bool is_alu(Cell* cell) + { + return cell->type == ID($alu); + } + + static bool is_macc(Cell* cell) + { + return cell->type == ID($macc) || cell->type == ID($macc_v2); + } + + bool alu_is_subtract(Cell* cell) + { + SigSpec bi = sigmap(cell->getPort(ID::BI)); + SigSpec ci = sigmap(cell->getPort(ID::CI)); + return GetSize(bi) == 1 && bi[0] == State::S1 && GetSize(ci) == 1 && ci[0] == State::S1; + } + + bool alu_is_add(Cell* cell) + { + SigSpec bi = sigmap(cell->getPort(ID::BI)); + SigSpec ci = sigmap(cell->getPort(ID::CI)); + return GetSize(bi) == 1 && bi[0] == State::S0 && GetSize(ci) == 1 && ci[0] == State::S0; + } + + bool alu_is_chainable(Cell* cell) + { + if (!(alu_is_add(cell) || alu_is_subtract(cell))) + return false; + + for (auto bit : sigmap(cell->getPort(ID::X))) + if (fanout.count(bit) && fanout[bit] > 0) + return false; + for (auto bit : sigmap(cell->getPort(ID::CO))) + if (fanout.count(bit) && fanout[bit] > 0) + return false; + + return true; + } + + bool is_chainable(Cell* cell) + { + return is_addsub(cell) || (is_alu(cell) && alu_is_chainable(cell)); + } + + void classify_cells() + { + for (auto cell : module->cells()) { + if (is_addsub(cell)) + addsub_cells.insert(cell); + else if (is_alu(cell)) + alu_cells.insert(cell); + else if (is_macc(cell)) + macc_cells.insert(cell); + } } void build_fanout_map() { for (auto cell : module->cells()) - for (auto &conn : cell->connections()) + for (auto& conn : cell->connections()) if (cell->input(conn.first)) for (auto bit : sigmap(conn.second)) bit_consumers[bit].insert(cell); - for (auto &pair : bit_consumers) + for (auto& pair : bit_consumers) fanout[pair.first] = pair.second.size(); for (auto wire : module->wires()) @@ -46,10 +105,9 @@ struct CsaTreeWorker fanout[bit]++; } - Cell* single_addsub_consumer(SigSpec sig) + Cell* sole_chainable_consumer(SigSpec sig, const pool& candidates) { Cell* consumer = nullptr; - for (auto bit : sig) { if (!fanout.count(bit) || fanout[bit] != 1) return nullptr; @@ -57,7 +115,7 @@ struct CsaTreeWorker return nullptr; Cell* c = *bit_consumers[bit].begin(); - if (!all_addsubs.count(c)) + if (!candidates.count(c)) return nullptr; if (consumer == nullptr) @@ -65,55 +123,39 @@ struct CsaTreeWorker else if (consumer != c) return nullptr; } - return consumer; } - dict find_addsub_parents() + dict find_parents(const pool& candidates) { dict parent_of; - - for (auto cell : all_addsubs) { - SigSpec y = sigmap(cell->getPort(ID::Y)); - Cell* consumer = single_addsub_consumer(y); - if (consumer != nullptr && consumer != cell) + for (auto cell : candidates) { + Cell* consumer = sole_chainable_consumer( + sigmap(cell->getPort(ID::Y)), candidates); + if (consumer && consumer != cell) parent_of[cell] = consumer; } - return parent_of; } - pool collect_chain(Cell* root, const dict> &children_of) + pool collect_chain(Cell* root, const dict>& children_of) { pool chain; - std::queue worklist; - worklist.push(root); - - while (!worklist.empty()) { - Cell* cur = worklist.front(); - worklist.pop(); - - if (chain.count(cur)) + std::queue q; + q.push(root); + while (!q.empty()) { + Cell* cur = q.front(); q.pop(); + if (!chain.insert(cur).second) continue; - chain.insert(cur); - - if (children_of.count(cur)) - for (auto child : children_of.at(cur)) - worklist.push(child); + auto it = children_of.find(cur); + if (it != children_of.end()) + for (auto child : it->second) + q.push(child); } - return chain; } - bool is_chain_internal(SigSpec sig, const pool &chain_y_bits) - { - for (auto bit : sig) - if (chain_y_bits.count(bit)) - return true; - return false; - } - - pool collect_chain_outputs(const pool &chain) + pool internal_bits(const pool& chain) { pool bits; for (auto cell : chain) @@ -122,54 +164,58 @@ struct CsaTreeWorker return bits; } - struct Operand { - SigSpec sig; - bool is_signed; - bool negate; - }; - - bool is_subtracted_input(Cell* child, Cell* parent) + static bool overlaps(SigSpec sig, const pool& bits) { - if (parent->type != ID($sub)) + for (auto bit : sig) + if (bits.count(bit)) + return true; + return false; + } + + bool feeds_subtracted_port(Cell* child, Cell* parent) + { + bool parent_subtracts; + if (parent->type == ID($sub)) + parent_subtracts = true; + else if (is_alu(parent)) + parent_subtracts = alu_is_subtract(parent); + else return false; - SigSpec child_y = sigmap(child->getPort(ID::Y)); - SigSpec parent_b = sigmap(parent->getPort(ID::B)); + if (!parent_subtracts) + return false; + SigSpec child_y = sigmap(child->getPort(ID::Y)); + SigSpec parent_b = sigmap(parent->getPort(ID::B)); for (auto bit : child_y) for (auto pbit : parent_b) if (bit == pbit) return true; - return false; } - std::vector collect_leaf_operands( - const pool &chain, - const pool &chain_y_bits, + std::vector extract_chain_operands( + const pool& chain, Cell* root, - const dict &parent_of, - int &correction + const dict& parent_of, + int& correction ) { + pool chain_bits = internal_bits(chain); dict negated; negated[root] = false; - std::queue worklist; - worklist.push(root); - - while (!worklist.empty()) { - Cell* cur = worklist.front(); - worklist.pop(); - for (auto cell : chain) { - if (!parent_of.count(cell)) - continue; - if (parent_of.at(cell) != cur) - continue; - if (negated.count(cell)) - continue; - - bool sub_b = is_subtracted_input(cell, cur); - negated[cell] = negated[cur] ^ sub_b; - worklist.push(cell); + { + std::queue q; + q.push(root); + while (!q.empty()) { + Cell* cur = q.front(); q.pop(); + for (auto cell : chain) { + if (!parent_of.count(cell) || parent_of.at(cell) != cur) + continue; + if (negated.count(cell)) + continue; + negated[cell] = negated[cur] ^ feeds_subtracted_port(cell, cur); + q.push(cell); + } } } @@ -177,35 +223,55 @@ struct CsaTreeWorker correction = 0; for (auto cell : chain) { - bool cell_neg = negated.count(cell) ? negated[cell] : false; + bool cell_neg; + if (negated.count(cell)) + cell_neg = negated[cell]; + else + cell_neg = false; + SigSpec a = sigmap(cell->getPort(ID::A)); SigSpec b = sigmap(cell->getPort(ID::B)); bool a_signed = cell->getParam(ID::A_SIGNED).as_bool(); bool b_signed = cell->getParam(ID::B_SIGNED).as_bool(); - bool b_subtracted = (cell->type == ID($sub)); + bool b_sub = (cell->type == ID($sub)) || (is_alu(cell) && alu_is_subtract(cell)); - if (!is_chain_internal(a, chain_y_bits)) { - bool neg_a = cell_neg; - operands.push_back({a, a_signed, neg_a}); - if (neg_a) - correction++; + if (!overlaps(a, chain_bits)) { + bool neg = cell_neg; + operands.push_back({a, a_signed, neg}); + if (neg) correction++; } - - if (!is_chain_internal(b, chain_y_bits)) { - bool neg_b = cell_neg ^ b_subtracted; - operands.push_back({b, b_signed, neg_b}); - if (neg_b) - correction++; + if (!overlaps(b, chain_bits)) { + bool neg = cell_neg ^ b_sub; + operands.push_back({b, b_signed, neg}); + if (neg) correction++; } } - return operands; } - SigSpec extend_to(SigSpec sig, bool is_signed, int width) + bool extract_macc_operands(Cell* cell, std::vector& operands, int& correction) + { + Macc macc(cell); + correction = 0; + + for (auto& term : macc.terms) { + if (GetSize(term.in_b) != 0) + return false; + operands.push_back({term.in_a, term.is_signed, term.do_subtract}); + if (term.do_subtract) + correction++; + } + return true; + } + + SigSpec extend_operand(SigSpec sig, bool is_signed, int width) { if (GetSize(sig) < width) { - SigBit pad = (is_signed && GetSize(sig) > 0) ? sig[GetSize(sig) - 1] : State::S0; + SigBit pad; + if (is_signed && GetSize(sig) > 0) + pad = sig[GetSize(sig) - 1]; + else + pad = State::S0; sig.append(SigSpec(pad, width - GetSize(sig))); } if (GetSize(sig) > width) @@ -225,14 +291,9 @@ struct CsaTreeWorker return out; } - SigSpec make_constant(int value, int width) - { - return SigSpec(value, width); - } - std::pair emit_fa(SigSpec a, SigSpec b, SigSpec c, int width) { - SigSpec sum = module->addWire(NEW_ID, width); + SigSpec sum = module->addWire(NEW_ID, width); SigSpec cout = module->addWire(NEW_ID, width); Cell* fa = module->addCell(NEW_ID, ID($fa)); @@ -243,66 +304,10 @@ struct CsaTreeWorker fa->setPort(ID::X, cout); fa->setPort(ID::Y, sum); - SigSpec carry_shifted; - carry_shifted.append(State::S0); - carry_shifted.append(cout.extract(0, width - 1)); - - return {sum, carry_shifted}; - } - - std::pair build_wallace_tree(std::vector &operands, int width, int &fa_count) - { - std::vector ops; - for (auto &s : operands) - ops.push_back({s, 0}); - - fa_count = 0; - int level = 0; - - while (ops.size() > 2) - { - std::vector ready, waiting; - for (auto &op : ops) { - if (op.depth <= level) - ready.push_back(op); - else - waiting.push_back(op); - } - - if (ready.size() < 3) { - level++; - log_assert(level <= 100); - continue; - } - - std::vector next; - size_t i = 0; - while (i + 2 < ready.size()) { - auto [sum, carry] = emit_fa(ready[i].sig, ready[i+1].sig, ready[i+2].sig, width); - int d = std::max({ready[i].depth, ready[i+1].depth, ready[i+2].depth}) + 1; - next.push_back({sum, d}); - next.push_back({carry, d}); - fa_count++; - i += 3; - } - - for (; i < ready.size(); i++) - next.push_back(ready[i]); - - for (auto &op : waiting) - next.push_back(op); - - ops = std::move(next); - level++; - log_assert(level <= 100); - } - - log_assert(ops.size() == 2); - - int max_depth = std::max(ops[0].depth, ops[1].depth); - log(" Tree depth: %d FA levels + 1 final add\n", max_depth); - - return {ops[0].sig, ops[1].sig}; + SigSpec carry; + carry.append(State::S0); + carry.append(cout.extract(0, width - 1)); + return {sum, carry}; } void emit_final_add(SigSpec a, SigSpec b, SigSpec y, int width) @@ -318,30 +323,110 @@ struct CsaTreeWorker add->setPort(ID::Y, y); } - void run() + struct DepthSig { + SigSpec sig; + int depth; + }; + + std::pair reduce_wallace(std::vector& sigs, int width, int& fa_count) { - find_addsubs(); - if (all_addsubs.empty()) + std::vector ops; + ops.reserve(sigs.size()); + for (auto& s : sigs) + ops.push_back({s, 0}); + + fa_count = 0; + + for (int level = 0; ops.size() > 2; level++) { + log_assert(level <= 100); + + std::vector ready, waiting; + for (auto& op : ops) { + if (op.depth <= level) + ready.push_back(op); + else + waiting.push_back(op); + } + + if (ready.size() < 3) continue; + + std::vector next; + size_t i = 0; + while (i + 2 < ready.size()) { + auto [sum, carry] = emit_fa(ready[i].sig, ready[i + 1].sig, ready[i + 2].sig, width); + int d = std::max({ready[i].depth, ready[i + 1].depth,ready[i + 2].depth}) + 1; + next.push_back({sum, d}); + next.push_back({carry, d}); + fa_count++; + i += 3; + } + for (; i < ready.size(); i++) + next.push_back(ready[i]); + for (auto& op : waiting) + next.push_back(op); + + ops = std::move(next); + } + + log_assert(ops.size() == 2); + log(" Tree depth: %d FA levels + 1 final add\n", + std::max(ops[0].depth, ops[1].depth)); + return {ops[0].sig, ops[1].sig}; + } + + void replace_with_csa_tree( + std::vector& operands, + SigSpec result_y, + int correction, + const char* desc + ) { + int width = GetSize(result_y); + std::vector extended; + extended.reserve(operands.size() + 1); + + for (auto& op : operands) { + SigSpec s = extend_operand(op.sig, op.is_signed, width); + if (op.negate) + s = emit_not(s, width); + extended.push_back(s); + } + + if (correction > 0) + extended.push_back(SigSpec(correction, width)); + + int fa_count; + auto [a, b] = reduce_wallace(extended, width, fa_count); + + log(" %s → %d $fa + 1 $add (%d operands, module %s)\n", + desc, fa_count, (int)operands.size(), log_id(module)); + + emit_final_add(a, b, result_y, width); + } + + void process_chains() + { + pool candidates; + for (auto cell : addsub_cells) + candidates.insert(cell); + for (auto cell : alu_cells) + if (alu_is_chainable(cell)) + candidates.insert(cell); + + if (candidates.empty()) return; - build_fanout_map(); + auto parent_of = find_parents(candidates); - auto parent_of = find_addsub_parents(); - - pool has_parent; dict> children_of; - for (auto &pair : parent_of) { - has_parent.insert(pair.first); - children_of[pair.second].insert(pair.first); + pool has_parent; + for (auto& [child, parent] : parent_of) { + children_of[parent].insert(child); + has_parent.insert(child); } pool processed; - - for (auto root : all_addsubs) - { - if (has_parent.count(root)) - continue; - if (processed.count(root)) + for (auto root : candidates) { + if (has_parent.count(root) || processed.count(root)) continue; pool chain = collect_chain(root, children_of); @@ -351,52 +436,51 @@ struct CsaTreeWorker for (auto c : chain) processed.insert(c); - pool chain_y_bits = collect_chain_outputs(chain); - int correction = 0; - auto operands = collect_leaf_operands(chain, chain_y_bits, root, parent_of, correction); - + int correction; + auto operands = extract_chain_operands( + chain, root, parent_of, correction); if (operands.size() < 3) continue; - SigSpec root_y = root->getPort(ID::Y); - int width = GetSize(root_y); - std::vector extended; - - for (auto &op : operands) { - SigSpec s = extend_to(op.sig, op.is_signed, width); - if (op.negate) - s = emit_not(s, width); - extended.push_back(s); - } - - if (correction > 0) - extended.push_back(make_constant(correction, width)); - - int fa_count; - auto [final_a, final_b] = build_wallace_tree(extended, width, fa_count); - int num_subs = 0; - - for (auto cell : chain) - if (cell->type == ID($sub)) - num_subs++; - - if (num_subs > 0) - log(" Replaced chain of %d $add/%d $sub cells with %d $fa + 1 $add (%d operands, module %s)\n", - (int)chain.size() - num_subs, num_subs, fa_count, (int)operands.size(), log_id(module)); - else - log(" Replaced chain of %d $add cells with %d $fa + 1 $add (%d operands, module %s)\n", - (int)chain.size(), fa_count, (int)operands.size(), log_id(module)); - - emit_final_add(final_a, final_b, root_y, width); - + replace_with_csa_tree(operands, root->getPort(ID::Y), + correction, "Replaced add/sub chain"); for (auto cell : chain) module->remove(cell); } } + + void process_maccs() + { + for (auto cell : macc_cells) { + std::vector operands; + int correction; + if (!extract_macc_operands(cell, operands, correction)) + continue; + if (operands.size() < 3) + continue; + + replace_with_csa_tree(operands, cell->getPort(ID::Y), + correction, "Replaced $macc"); + module->remove(cell); + } + } + + void run() + { + classify_cells(); + + if (addsub_cells.empty() && alu_cells.empty() && macc_cells.empty()) + return; + + build_fanout_map(); + process_chains(); + process_maccs(); + } }; struct CsaTreePass : public Pass { - CsaTreePass() : Pass("csa_tree", "convert $add/$sub chains to carry-save adder trees") {} + CsaTreePass() : Pass("csa_tree", + "convert add/sub/macc chains to carry-save adder trees") {} void help() override { @@ -404,17 +488,17 @@ struct CsaTreePass : public Pass { log("\n"); log(" csa_tree [selection]\n"); log("\n"); - log("This pass finds chains of $add and $sub cells and replaces them with carry-save\n"); - log("adder trees built from $fa cells, followed by a single final $add for the\n"); - log("carry-propagate step.\n"); + log("This pass replaces chains of $add/$sub cells, $alu cells (with constant\n"); + log("BI/CI), and $macc/$macc_v2 cells (without multiplications) with carry-save\n"); + log("adder trees using $fa cells and a single final $add.\n"); log("\n"); - log("The tree uses Wallace-tree scheduling for optimal depth: at each level, all ready\n"); - log("operands are grouped into triplets and compressed via full adders. This\n"); - log("gives ceil(log_1.5(N)) FA levels for N input operands.\n"); + log("The tree uses Wallace-tree scheduling: at each level, ready operands are\n"); + log("grouped into triplets and compressed via full adders, giving\n"); + log("O(log_{1.5} N) depth for N input operands.\n"); log("\n"); } - void execute(std::vector args, RTLIL::Design *design) override + void execute(std::vector args, RTLIL::Design* design) override { log_header(design, "Executing CSA_TREE pass.\n"); From 8181373ea3e3d0e0d2f0a91039bd5b1466f5e78c Mon Sep 17 00:00:00 2001 From: nella Date: Thu, 19 Mar 2026 17:45:56 +0100 Subject: [PATCH 13/15] CSA add alumacc related tests. --- tests/csa_tree/csa_tree_alu_chains.ys | 96 ++++++ .../csa_tree/csa_tree_alu_macc_edge_cases.ys | 285 ++++++++++++++++++ tests/csa_tree/csa_tree_alu_macc_equiv.ys | 179 +++++++++++ tests/csa_tree/csa_tree_alu_macc_sim.ys | 116 +++++++ tests/csa_tree/csa_tree_alu_sub_chains.ys | 79 +++++ tests/csa_tree/csa_tree_macc.ys | 141 +++++++++ 6 files changed, 896 insertions(+) create mode 100644 tests/csa_tree/csa_tree_alu_chains.ys create mode 100644 tests/csa_tree/csa_tree_alu_macc_edge_cases.ys create mode 100644 tests/csa_tree/csa_tree_alu_macc_equiv.ys create mode 100644 tests/csa_tree/csa_tree_alu_macc_sim.ys create mode 100644 tests/csa_tree/csa_tree_alu_sub_chains.ys create mode 100644 tests/csa_tree/csa_tree_macc.ys diff --git a/tests/csa_tree/csa_tree_alu_chains.ys b/tests/csa_tree/csa_tree_alu_chains.ys new file mode 100644 index 000000000..ada1368dc --- /dev/null +++ b/tests/csa_tree/csa_tree_alu_chains.ys @@ -0,0 +1,96 @@ +read_verilog < Date: Fri, 20 Mar 2026 16:47:21 +0100 Subject: [PATCH 14/15] Move csa after alumacc. --- techlibs/common/synth.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index e781ee3ab..3ab46fda1 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -68,7 +68,7 @@ struct SynthPass : public ScriptPass { log(" run the booth pass to map $mul to Booth encoded multipliers\n"); log("\n"); log(" -csa\n"); - log(" run the csa_tree pass to convert $add/$sub chains to\n"); + log(" run the csa_tree pass to convert $add/$sub chains and $macc cells to\n"); log(" carry-save adder trees.\n"); log("\n"); log(" -noalumacc\n"); @@ -295,10 +295,10 @@ struct SynthPass : public ScriptPass { run(stringf("%s -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=%d", techmap_cmd, lut)); if (booth || help_mode) run("booth", " (if -booth)"); - if (csa || help_mode) - run("csa_tree", " (if -csa)"); if (!noalumacc) run("alumacc", " (unless -noalumacc)"); + if (csa || help_mode) + run("csa_tree", " (if -csa)"); if (!noshare) run("share", " (unless -noshare)"); run("opt" + hieropt_flag); From 39d9be2df909106eac451dc61073182be25c8709 Mon Sep 17 00:00:00 2001 From: nella Date: Fri, 20 Mar 2026 16:56:07 +0100 Subject: [PATCH 15/15] rm misc comments. --- tests/csa_tree/csa_tree_macc.ys | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/csa_tree/csa_tree_macc.ys b/tests/csa_tree/csa_tree_macc.ys index 27684e0ed..4c2490e34 100644 --- a/tests/csa_tree/csa_tree_macc.ys +++ b/tests/csa_tree/csa_tree_macc.ys @@ -1,8 +1,3 @@ -# Tests for csa_tree operating on $macc cells (post-alumacc) -# After alumacc, chains of adds get merged into a single $macc cell. -# The csa_tree pass should decompose it into FA + final add. - -# 3-input add merged into $macc read_verilog <