mirror of
https://github.com/YosysHQ/yosys
synced 2026-06-01 14:47:53 +00:00
Merge 2eb1051cca into dc77140275
This commit is contained in:
commit
b728c178c5
4 changed files with 307 additions and 0 deletions
|
|
@ -24,6 +24,7 @@ OBJS += passes/opt/pmux2shiftx.o
|
||||||
OBJS += passes/opt/muxpack.o
|
OBJS += passes/opt/muxpack.o
|
||||||
OBJS += passes/opt/opt_balance_tree.o
|
OBJS += passes/opt/opt_balance_tree.o
|
||||||
|
|
||||||
|
OBJS += passes/opt/splitlarge.o
|
||||||
OBJS += passes/opt/peepopt.o
|
OBJS += passes/opt/peepopt.o
|
||||||
GENFILES += passes/opt/peepopt_pm.h
|
GENFILES += passes/opt/peepopt_pm.h
|
||||||
passes/opt/peepopt.o: passes/opt/peepopt_pm.h
|
passes/opt/peepopt.o: passes/opt/peepopt_pm.h
|
||||||
|
|
|
||||||
201
passes/opt/splitlarge.cc
Normal file
201
passes/opt/splitlarge.cc
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
||||||
|
* 2025 Silimate Inc. <akash@silimate.com>
|
||||||
|
*
|
||||||
|
* 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 <queue>
|
||||||
|
|
||||||
|
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<Cell *>& 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<Cell *> 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<std::string> 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
|
||||||
71
tests/opt/splitlarge_wide_op.tcl
Normal file
71
tests/opt/splitlarge_wide_op.tcl
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
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 splitlarge_wide_op.v
|
||||||
|
hierarchy -top wide_op
|
||||||
|
chparam -set width 1024 -set op $i wide_op
|
||||||
|
yosys proc
|
||||||
|
simplemap
|
||||||
|
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
|
||||||
|
log -pop
|
||||||
|
}
|
||||||
|
log -pop
|
||||||
34
tests/opt/splitlarge_wide_op.v
Normal file
34
tests/opt/splitlarge_wide_op.v
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue