From 049be595b5159b6961e7b230e6fa33a004c08ae6 Mon Sep 17 00:00:00 2001 From: Natalia Date: Fri, 19 Dec 2025 07:00:57 -0800 Subject: [PATCH 1/4] splitlarge: split wide / operators --- passes/opt/Makefile.inc | 2 + passes/opt/splitlarge.cc | 201 +++++++++++++++++++++++++++++++ tests/opt/splitlarge_wide_op.tcl | 70 +++++++++++ tests/opt/splitlarge_wide_op.v | 34 ++++++ 4 files changed, 307 insertions(+) create mode 100644 passes/opt/splitlarge.cc create mode 100644 tests/opt/splitlarge_wide_op.tcl create mode 100644 tests/opt/splitlarge_wide_op.v diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 426d9a79a..71edd648e 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -39,3 +39,5 @@ PEEPOPT_PATTERN += passes/opt/peepopt_formal_clockgateff.pmg passes/opt/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) endif + +OBJS += passes/opt/splitlarge.o diff --git a/passes/opt/splitlarge.cc b/passes/opt/splitlarge.cc new file mode 100644 index 000000000..a08d3e3da --- /dev/null +++ b/passes/opt/splitlarge.cc @@ -0,0 +1,201 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * 2025 Silimate Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + + #include "kernel/yosys.h" + #include "kernel/sigtools.h" + #include "kernel/utils.h" + + #include + + USING_YOSYS_NAMESPACE + PRIVATE_NAMESPACE_BEGIN + + struct SplitlargeWorker + { + Module *module; + int max_width; + + SplitlargeWorker(Module *module, int max_width) : module(module), max_width(max_width) + { + } + + void split_spec(int high_width, int low_width, SigSpec &high, SigSpec &low, const SigSpec &splittable, bool sign_extend) + { + SigSpec splittable_ext = splittable; + splittable_ext.extend_u0(high_width + low_width, sign_extend); + + high = splittable_ext.extract(low_width, high_width); + low = splittable_ext.extract(0, low_width); + } + + int width_addsub(Cell *cell) + { + int a_width = cell->parameters[ID::A_WIDTH].as_int(); + int b_width = cell->parameters[ID::B_WIDTH].as_int(); + return max(a_width, b_width); + } + + void split_addsub(Cell *cell, std::queue& q) + { + auto width = width_addsub(cell); + auto width_low = width / 2; + auto width_high = width - width_low; // Handle odd widths + + bool aSigned = cell->parameters[ID::A_SIGNED].as_bool(); + bool bSigned = cell->parameters[ID::B_SIGNED].as_bool(); + SigSpec aHigh, aLow, bHigh, bLow; + + split_spec(width_high, width_low, aHigh, aLow, cell->getPort(ID::A), cell->getParam(ID::A_SIGNED).as_bool()); + split_spec(width_high, width_low, bHigh, bLow, cell->getPort(ID::B), cell->getParam(ID::B_SIGNED).as_bool()); + + std::string yHighName = cell->name.str() + "_splitres1"; + std::string yCarryName = cell->name.str() + "_splitresc"; + std::string yLowName = cell->name.str() + "_splitres0"; + std::string nameHigh = cell->name.str() + "_split1"; + std::string nameLow = cell->name.str() + "_split0"; + std::string nameCarry = cell->name.str() + "_splitc"; + + auto yCarry = module->addWire(yCarryName, width_high + 1); + auto yHigh = module->addWire(yHighName, width_high + 1); + auto yLow = module->addWire(yLowName, width_low + 1); + + auto backupResultSpec = cell->getPort(ID::Y); + + // Modify existing adder to be low + module->rename(cell, nameLow); + cell->setPort(ID::A, aLow); + cell->setPort(ID::B, bLow); + cell->setPort(ID::Y, yLow); + cell->parameters[ID::A_WIDTH] = width_low; + cell->parameters[ID::B_WIDTH] = width_low; + cell->parameters[ID::Y_WIDTH] = width_low + 1; + cell->parameters[ID::A_SIGNED] = 0; + cell->parameters[ID::B_SIGNED] = 0; + if (width_low > max_width) { + q.emplace(cell); + } + + // Create high adder + auto highCell = module->addCell(nameHigh, cell); // copy type and parameters + highCell->setPort(ID::A, aHigh); + highCell->setPort(ID::B, bHigh); + highCell->setPort(ID::Y, yHigh); + highCell->parameters[ID::A_WIDTH] = width_high; + highCell->parameters[ID::B_WIDTH] = width_high; + highCell->parameters[ID::Y_WIDTH] = width_high + 1; + highCell->parameters[ID::A_SIGNED] = aSigned; + highCell->parameters[ID::B_SIGNED] = bSigned; + if (width_high > max_width) { + q.emplace(highCell); + } + + // Create carry adder + auto carryCell = module->addCell(nameCarry, highCell); // copy type and parameters + auto carryBit = SigSpec(yLow).extract(width_low, 1); + carryBit.extend_u0(2); + + carryCell->setPort(ID::A, yHigh); + carryCell->parameters[ID::A_WIDTH] = width_high + 1; + carryCell->parameters[ID::A_SIGNED] = aSigned || bSigned; + carryCell->setPort(ID::B, carryBit); + carryCell->parameters[ID::B_WIDTH] = 2; + carryCell->parameters[ID::B_SIGNED] = aSigned || bSigned; + carryCell->setPort(ID::Y, yCarry); + if (width_high + 1 > max_width) { + q.emplace(carryCell); + } + + // Concatenate + auto ySpec = SigSpec({yCarry, SigSpec(yLow).extract(0, width_low)}); + module->connect(backupResultSpec, ySpec.extract(0, backupResultSpec.size())); + } + + void split() + { + std::queue q; + for (auto pair: module->cells_) { + auto& cell = pair.second; + if (cell->type == ID($add) && width_addsub(cell) > max_width) { + q.emplace(cell); + } else if (cell->type == ID($sub) && width_addsub(cell) > max_width) { + q.emplace(cell); + } + } + + while (!q.empty()) { + auto cell = q.front(); + q.pop(); + + if (cell->type == ID($add)) { + split_addsub(cell, q); + } + else if (cell->type == ID($sub)) { + split_addsub(cell, q); + } + } + } + + }; + + struct SplitlargePass : public Pass { + SplitlargePass() : Pass("splitlarge", "splitting large arithmetic operators") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" splitlarge [selection]\n"); + log("\n"); + log(" splits arithmetic operators (specifically $add, $sub) into a number of\n"); + log(" operators with smaller widths. should be run after techmap/simplemap\n"); + log("\n"); + log("\n"); + log(" -max_width n\n"); + log(" max tolerable width of an $add or $sub operator.\n"); + log(" * The width of $add/$sub is defined as max(a_width, b_width)\n"); + log(" * If unset, 128 is used as a default value.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + int max_width = 128; + log_header(design, "Executing SPLITLARGE pass (splitting wide $add/$sub operators).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-max_width" && argidx+1 < args.size()) { + max_width = std::stoul(args[++argidx]); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + SplitlargeWorker worker(module, max_width); + worker.split(); + } + + Pass::call(design, "clean *"); + } + } SplitlargePass; + + PRIVATE_NAMESPACE_END diff --git a/tests/opt/splitlarge_wide_op.tcl b/tests/opt/splitlarge_wide_op.tcl new file mode 100644 index 000000000..af3f4a431 --- /dev/null +++ b/tests/opt/splitlarge_wide_op.tcl @@ -0,0 +1,70 @@ +proc op_name {op_number} { + set op [expr $op_number >> 2] + set b_signed [expr {$op_number & 1 ? "signed": "unsigned"}] + set a_signed [expr {(($op_number & 2) >> 1) ? "signed": "unsigned"}] + set cell "unknown" + if { "$op" == "0" } { + set cell "\$add" + } elseif { "$op" == "1" } { + set cell "\$sub" + } + return "$cell (a $a_signed, b $b_signed)" +} + +proc predict_adder_count {in_width max_width op_number} { + set b_signed [expr {$op_number & 1}] + set a_signed [expr {($op_number & 2) >> 1}] + set a_width [expr {$in_width + $a_signed}] + set b_width [expr {$in_width + $b_signed}] + + set adder_width [expr {max($a_width, $b_width)}] + set adder_queue [list] + + if {$adder_width > $max_width} { + lappend adder_queue $adder_width + } + + set adder_count 1 + + while {[llength $adder_queue] > 0} { + set current [lindex $adder_queue end] + set adder_queue [lrange $adder_queue 0 end-1] + + incr adder_count 2 ; # one changed adder, two new adders + + set low [expr {$current / 2}] + set high [expr {$current - $low}] + set carry [expr {$high + 1}] + + if {$low > $max_width} { + lappend adder_queue $low + } + if {$high > $max_width} { + lappend adder_queue $high + } + if {$carry > $max_width} { + lappend adder_queue $carry + } + } + return $adder_count +} + +yosys -import +log -header "splitlarge" +log -push +for {set i 0} {$i < 8} {incr i} { + log -header "[op_name $i]" + log -push + design -reset + read_verilog wide_op.v + hierarchy -top wide_op + chparam -set width 1024 -set op $i wide_op + yosys proc + simplemap + equiv_opt -post -assert splitlarge -max_width 128 + yosys select -assert-none r:A_WIDTH>128 + yosys select -assert-none r:B_WIDTH>128 + yosys select -assert-count [predict_adder_count 1024 128 $i] r:A_WIDTH + log -pop +} +log -pop diff --git a/tests/opt/splitlarge_wide_op.v b/tests/opt/splitlarge_wide_op.v new file mode 100644 index 000000000..2e2c589e7 --- /dev/null +++ b/tests/opt/splitlarge_wide_op.v @@ -0,0 +1,34 @@ + + module wide_op( + a, b, y, c + ); + parameter width = 1024; + + // ADD/SUB: 0/4 + (0 unsigned;unsigned, 1 unsigned;signed, 2 signed;unsigned, 3 signed;signed) + parameter op = 0; + + localparam ywidth = width; // (op == MUL) ? width * 2 : width; + input[width-1:0] a; + input[width-1:0] b; + output [width-1:0] y; + output c; + + generate + if (op == 0) + assign {c, y} = a + b; + else if (op == 1) + assign {c, y} = a + $signed(b); + else if (op == 2) + assign {c, y} = $signed(a) + b; + else if (op == 3) + assign {c, y} = $signed(a) + $signed(b); + else if (op == 4) + assign {c, y} = a - b; + else if (op == 5) + assign {c, y} = a - $signed(b); + else if (op == 6) + assign {c, y} = $signed(a) - b; + else if (op == 7) + assign {c, y} = $signed(a) - $signed(b); + endgenerate + endmodule From 6726b8a0ca6e52831254f88e0d9b4d6be6a0a23e Mon Sep 17 00:00:00 2001 From: nataliakokoromyti <126305457+nataliakokoromyti@users.noreply.github.com> Date: Fri, 19 Dec 2025 07:09:10 -0800 Subject: [PATCH 2/4] Update Makefile.inc --- passes/opt/Makefile.inc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 71edd648e..fdfc9e20e 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -23,7 +23,7 @@ OBJS += passes/opt/opt_lut_ins.o OBJS += passes/opt/opt_ffinv.o OBJS += passes/opt/pmux2shiftx.o OBJS += passes/opt/muxpack.o - +OBJS += passes/opt/splitlarge.o OBJS += passes/opt/peepopt.o GENFILES += passes/opt/peepopt_pm.h passes/opt/peepopt.o: passes/opt/peepopt_pm.h @@ -39,5 +39,3 @@ PEEPOPT_PATTERN += passes/opt/peepopt_formal_clockgateff.pmg passes/opt/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p peepopt $(filter-out $<,$^) endif - -OBJS += passes/opt/splitlarge.o From f2ba575b15a9de6bf12930fe975678b3118a72c8 Mon Sep 17 00:00:00 2001 From: nataliakokoromyti <126305457+nataliakokoromyti@users.noreply.github.com> Date: Fri, 19 Dec 2025 07:10:17 -0800 Subject: [PATCH 3/4] Update Makefile.inc --- passes/opt/Makefile.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index fdfc9e20e..214ea2647 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -23,6 +23,7 @@ OBJS += passes/opt/opt_lut_ins.o OBJS += passes/opt/opt_ffinv.o OBJS += passes/opt/pmux2shiftx.o OBJS += passes/opt/muxpack.o + OBJS += passes/opt/splitlarge.o OBJS += passes/opt/peepopt.o GENFILES += passes/opt/peepopt_pm.h From 2eb1051cca5dc17e022364da7ab8d89ff0fa1cf8 Mon Sep 17 00:00:00 2001 From: Natalia Kokoromyti Date: Fri, 19 Dec 2025 12:35:22 -0800 Subject: [PATCH 4/4] fix splitlarge wide_op test --- tests/opt/splitlarge_wide_op.tcl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/opt/splitlarge_wide_op.tcl b/tests/opt/splitlarge_wide_op.tcl index af3f4a431..5ba73d121 100644 --- a/tests/opt/splitlarge_wide_op.tcl +++ b/tests/opt/splitlarge_wide_op.tcl @@ -56,12 +56,13 @@ for {set i 0} {$i < 8} {incr i} { log -header "[op_name $i]" log -push design -reset - read_verilog wide_op.v + read_verilog splitlarge_wide_op.v hierarchy -top wide_op chparam -set width 1024 -set op $i wide_op yosys proc simplemap - equiv_opt -post -assert splitlarge -max_width 128 +equiv_opt -assert splitlarge +yosys splitlarge yosys select -assert-none r:A_WIDTH>128 yosys select -assert-none r:B_WIDTH>128 yosys select -assert-count [predict_adder_count 1024 128 $i] r:A_WIDTH