mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-22 08:35:32 +00:00
Merge pull request #44 from alaindargelas/peepopts
muxadd and muldiv_c peepopt
This commit is contained in:
commit
81cced8e01
9 changed files with 902 additions and 38 deletions
1
Makefile
1
Makefile
|
@ -918,6 +918,7 @@ SH_TEST_DIRS += tests/memlib
|
|||
SH_TEST_DIRS += tests/svinterfaces
|
||||
SH_TEST_DIRS += tests/xprop
|
||||
SH_TEST_DIRS += tests/select
|
||||
SH_TEST_DIRS += tests/peepopt
|
||||
SH_TEST_DIRS += tests/proc
|
||||
SH_TEST_DIRS += tests/blif
|
||||
# SH_TEST_DIRS += tests/arch
|
||||
|
|
|
@ -59,6 +59,8 @@ PEEPOPT_PATTERN += passes/pmgen/peepopt_shiftadd.pmg
|
|||
PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv_c.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_muxadd.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv_c.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_muxadd.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_formal_clockgateff.pmg
|
||||
|
||||
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
|
||||
|
|
|
@ -28,6 +28,9 @@ bool did_something;
|
|||
// scratchpad configurations for pmgen
|
||||
int shiftadd_max_ratio;
|
||||
|
||||
// Helper function, removes LSB 0s
|
||||
SigSpec remove_bottom_padding(SigSpec sig);
|
||||
|
||||
#include "passes/pmgen/peepopt_pm.h"
|
||||
|
||||
struct PeepoptPass : public Pass {
|
||||
|
@ -42,8 +45,6 @@ struct PeepoptPass : public Pass {
|
|||
log("\n");
|
||||
log("This pass employs the following rules by default:\n");
|
||||
log("\n");
|
||||
log(" * muxadd - Replace S?(A+B):A with A+(S?B:0)\n");
|
||||
log("\n");
|
||||
log(" * muldiv - Replace (A*B)/B with A\n");
|
||||
log("\n");
|
||||
log(" * muldiv_c - Replace (A*B)/C with A*(B/C) when C is a const divisible by B.\n");
|
||||
|
@ -67,13 +68,17 @@ struct PeepoptPass : public Pass {
|
|||
log(" based pattern to prevent combinational paths from the\n");
|
||||
log(" output to the enable input after running clk2fflogic.\n");
|
||||
log("\n");
|
||||
log("If -withmuxadd is specified it adds the following rule:\n");
|
||||
log("\n");
|
||||
log(" * muxadd - Replace S?(A+B):A with A+(S?B:0)\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n");
|
||||
|
||||
bool formalclk = false;
|
||||
|
||||
bool withmuxadd = false;
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
|
@ -81,6 +86,10 @@ struct PeepoptPass : public Pass {
|
|||
formalclk = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-withmuxadd") {
|
||||
withmuxadd = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
@ -110,11 +119,21 @@ struct PeepoptPass : public Pass {
|
|||
pm.run_shiftmul_left();
|
||||
pm.run_muldiv();
|
||||
pm.run_muldiv_c();
|
||||
pm.run_muxadd();
|
||||
if (withmuxadd)
|
||||
pm.run_muxadd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} PeepoptPass;
|
||||
|
||||
|
||||
SigSpec remove_bottom_padding(SigSpec sig)
|
||||
{
|
||||
int i = 0;
|
||||
for (; i < sig.size() - 1 && sig[i] == State::S0; i++) {
|
||||
}
|
||||
return sig.extract(i, sig.size() - i);
|
||||
}
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
pattern muldiv_c
|
||||
//
|
||||
// Transforms mul->div into div->mul when b and c are divisible constants:
|
||||
// y = (a * b_const) / c_const ===> a * (b_const / c_const)
|
||||
// Authored by Akash Levy and Alain Dargelas of Silimate, Inc. under ISC license.
|
||||
// Transforms mul->div into const->mul when b and c are divisible constants:
|
||||
// y = (a * b_const) / c_const ===> a * eval(b_const / c_const)
|
||||
//
|
||||
|
||||
state <SigSpec> a b_const mul_y
|
||||
|
@ -18,9 +19,8 @@ code a b_const mul_y
|
|||
mul_y = port(mul, \Y);
|
||||
|
||||
// Fanout of each multiplier Y bit should be 1 (no bit-split)
|
||||
for (auto bit : mul_y)
|
||||
if (nusers(bit) != 2)
|
||||
reject;
|
||||
if (nusers(mul_y) != 2)
|
||||
reject;
|
||||
|
||||
// A and B can be interchanged
|
||||
branch;
|
||||
|
@ -34,6 +34,7 @@ match div
|
|||
// Check that b_const and c_const is constant
|
||||
filter b_const.is_fully_const()
|
||||
filter port(div, \B).is_fully_const()
|
||||
index <SigSpec> remove_bottom_padding(port(div, \A)) === mul_y
|
||||
endmatch
|
||||
|
||||
code
|
||||
|
@ -46,13 +47,61 @@ code
|
|||
int offset = GetSize(div_a) - GetSize(mul_y);
|
||||
|
||||
// Get properties and values of b_const and c_const
|
||||
int b_const_width = mul->getParam(ID::B_WIDTH).as_int();
|
||||
// b_const may be coming from the A port
|
||||
// But it is an RTLIL invariant that A_SIGNED equals B_SIGNED
|
||||
bool b_const_signed = mul->getParam(ID::B_SIGNED).as_bool();
|
||||
bool c_const_signed = div->getParam(ID::B_SIGNED).as_bool();
|
||||
int b_const_int = b_const.as_int(b_const_signed);
|
||||
int c_const_int = c_const.as_int(c_const_signed);
|
||||
int b_const_int_shifted = b_const_int << offset;
|
||||
|
||||
// Helper lambdas for two's complement math
|
||||
auto sign2sComplement = [](auto value, int numBits) {
|
||||
if (value & (1 << (numBits - 1))) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
auto twosComplement = [](auto value, int numBits) {
|
||||
if (value & (1 << (numBits - 1))) {
|
||||
return (~value) + 1; // invert bits before adding 1
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
// Two's complement conversion
|
||||
if (b_const_signed)
|
||||
b_const_int = sign2sComplement(b_const_int, GetSize(b_const)) * twosComplement(b_const_int, GetSize(b_const));
|
||||
if (c_const_signed)
|
||||
c_const_int = sign2sComplement(c_const_int, GetSize(c_const)) * twosComplement(c_const_int, GetSize(c_const));
|
||||
// Calculate the constant and compress the width to fit the value
|
||||
Const const_ratio;
|
||||
Const b_const_actual;
|
||||
// Avoid division by zero
|
||||
if (c_const_int == 0)
|
||||
reject;
|
||||
b_const_actual = b_const_int_shifted;
|
||||
b_const_actual.compress(b_const_signed);
|
||||
|
||||
const_ratio = b_const_int_shifted / c_const_int;
|
||||
const_ratio.compress(b_const_signed | c_const_signed);
|
||||
|
||||
// Integer values should be lesser than 32 bits
|
||||
// This is because we are using C++ types, and int is 32 bits
|
||||
// FIXME: use long long or BigInteger to make pass work with >32 bits
|
||||
if (GetSize(mul->getParam(ID::B_WIDTH)) > 32)
|
||||
reject;
|
||||
if (GetSize(b_const) > 32)
|
||||
reject;
|
||||
if (GetSize(c_const) + offset > 32)
|
||||
reject;
|
||||
|
||||
// Check for potential multiplier overflow
|
||||
if (GetSize(b_const_actual) + GetSize(a) > GetSize(mul_y))
|
||||
reject;
|
||||
|
||||
// Check that there are only zeros before offset
|
||||
if (offset < 0 || !div_a.extract(0, offset).is_fully_zero())
|
||||
reject;
|
||||
|
@ -62,7 +111,8 @@ code
|
|||
reject;
|
||||
|
||||
// Rewire to only keep multiplier
|
||||
mul->setPort(\B, Const(b_const_int_shifted / c_const_int, b_const_width));
|
||||
mul->setPort(\A, a);
|
||||
mul->setPort(\B, const_ratio);
|
||||
mul->setPort(\Y, div_y);
|
||||
|
||||
// Remove divider
|
||||
|
|
|
@ -1,59 +1,123 @@
|
|||
pattern muxadd
|
||||
//
|
||||
// Authored by Akash Levy and Alain Dargelas of Silimate, Inc. under ISC license.
|
||||
// Transforms add->mux into mux->add:
|
||||
// y = s ? (a + b) : a ===> y = a + (s ? b : 0)
|
||||
//
|
||||
// or
|
||||
// y = s ? a : (a + b) ===> y = a + (s ? 0 : b)
|
||||
|
||||
state <SigSpec> add_a add_b add_y
|
||||
state <SigSpec> add_a add_b add_y add_a_ext mux_a mux_b mux_y
|
||||
state <Const> add_a_signed
|
||||
state <IdString> add_a_id add_b_id mux_a_id mux_b_id
|
||||
|
||||
match add
|
||||
// Select adder
|
||||
select add->type == $add
|
||||
|
||||
// Set ports, allowing A and B to be swapped
|
||||
choice <IdString> A {\A, \B}
|
||||
define <IdString> B (A == \A ? \B : \A)
|
||||
set add_a port(add, A)
|
||||
set add_b port(add, B)
|
||||
set add_y port(add, \Y)
|
||||
|
||||
// Get signedness
|
||||
set add_a_signed param(add, (A == \A) ? \A_SIGNED : \B_SIGNED)
|
||||
|
||||
// Choice ids
|
||||
set add_a_id A
|
||||
set add_b_id B
|
||||
endmatch
|
||||
|
||||
code add_y add_a add_b
|
||||
code add_y add_a add_b add_a_ext
|
||||
// Get adder signals
|
||||
add_a = port(add, \A);
|
||||
add_b = port(add, \B);
|
||||
add_y = port(add, \Y);
|
||||
add_a_ext = SigSpec(port(add, add_a_id));
|
||||
add_a_ext.extend_u0(GetSize(add_y), add_a_signed.as_bool());
|
||||
|
||||
// Fanout of each adder Y bit should be 1 (no bit-split)
|
||||
for (auto bit : add_y)
|
||||
if (nusers(bit) != 2)
|
||||
reject;
|
||||
|
||||
// A and B can be interchanged
|
||||
branch;
|
||||
std::swap(add_a, add_b);
|
||||
if (nusers(add_y) != 2)
|
||||
reject;
|
||||
endcode
|
||||
|
||||
match mux
|
||||
// Select mux of form s ? (a + b) : a, allow leading 0s when A_WIDTH != Y_WIDTH
|
||||
match mux
|
||||
// Select mux of form: s ? (a + b) : a
|
||||
// Allow leading 0s when A_WIDTH != Y_WIDTH or s ? a : (a + b)
|
||||
select mux->type == $mux
|
||||
index <SigSpec> port(mux, \A) === SigSpec({Const(State::S0, GetSize(add_y)-GetSize(add_a)), add_a})
|
||||
index <SigSpec> port(mux, \B) === add_y
|
||||
choice <IdString> AB {\A, \B}
|
||||
define <IdString> BA (AB == \A ? \B : \A)
|
||||
set mux_y port(mux, \Y)
|
||||
set mux_a port(mux, AB)
|
||||
set mux_b port(mux, BA)
|
||||
set mux_a_id AB
|
||||
set mux_b_id BA
|
||||
index <SigSpec> port(mux, AB) === add_a_ext
|
||||
index <SigSpec> port(mux, BA) === add_y
|
||||
endmatch
|
||||
|
||||
code
|
||||
code add_y add_a add_b add_a_ext add_a_id add_b_id mux_y mux_a mux_b mux_a_id mux_b_id
|
||||
// Get mux signal
|
||||
SigSpec mux_y = port(mux, \Y);
|
||||
SigSpec mid;
|
||||
std::string adder_y_name;
|
||||
if (add_y.is_wire())
|
||||
adder_y_name = add_y.as_wire()->name.c_str();
|
||||
else
|
||||
adder_y_name = add_y.as_string();
|
||||
|
||||
// SILIMATE: Alias cell to mux for mid wire
|
||||
Cell *cell = mux;
|
||||
// Start by renaming the LHS of an eventual assign statement
|
||||
// where the RHS is the adder output (that is getting rewired).
|
||||
// Renaming the signal allows equiv_opt to function as it would
|
||||
// otherwise try to match the functionality which would fail
|
||||
// as the LHS signal has indeed changed function.
|
||||
|
||||
// Adder output could be assigned
|
||||
for (auto it = module->connections().begin(); it != module->connections().end(); ++it) {
|
||||
RTLIL::SigSpec rhs = it->second;
|
||||
if (rhs.is_wire()) {
|
||||
const std::string& rhs_name = rhs.as_wire()->name.c_str();
|
||||
if (rhs_name == adder_y_name) {
|
||||
RTLIL::SigSpec lhs = it->first;
|
||||
if (lhs.is_wire()) {
|
||||
const std::string& lhs_name = lhs.as_wire()->name.c_str();
|
||||
module->rename(lhs_name, module->uniquify("$" + lhs_name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Alternatively, the port name could be a wire name
|
||||
if (add_y.is_wire()) {
|
||||
if (GetSize(adder_y_name)) {
|
||||
if (adder_y_name[0] != '$') {
|
||||
module->rename(adder_y_name, module->uniquify("$" + adder_y_name));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto chunk : add_y.chunks()) {
|
||||
if (chunk.is_wire()) {
|
||||
const std::string& name = chunk.wire->name.c_str();
|
||||
if (name[0] != '$') {
|
||||
module->rename(name, module->uniquify("$" + name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create new mid wire
|
||||
SigSpec mid = module->addWire(NEW_ID2_SUFFIX("mid"), GetSize(add_b)); // SILIMATE: Improve the naming
|
||||
mid = module->addWire(NEW_ID, GetSize(add_b));
|
||||
|
||||
// Rewire
|
||||
mux->setPort(\A, Const(State::S0, GetSize(add_b)));
|
||||
mux->setPort(\B, add_b);
|
||||
// Connect ports
|
||||
add->setPort(add_b_id, mid);
|
||||
add->setPort(add_a_id, add_a);
|
||||
add->setPort(\Y, add_y);
|
||||
mux->setPort(mux_a_id, Const(State::S0, GetSize(add_b)));
|
||||
mux->setPort(mux_b_id, add_b);
|
||||
mux->setPort(\Y, mid);
|
||||
add->setPort(\B, mid);
|
||||
add->setPort(\Y, mux_y);
|
||||
module->connect(mux_y, add_y);
|
||||
|
||||
// Log, fixup, accept
|
||||
log("muxadd pattern in %s: mux=%s, add=%s\n", log_id(module), log_id(mux), log_id(add));
|
||||
add->fixup_parameters();
|
||||
mux->fixup_parameters();
|
||||
did_something = true;
|
||||
accept;
|
||||
endcode
|
||||
|
|
1
tests/peepopt/.gitignore
vendored
Normal file
1
tests/peepopt/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/*.log
|
343
tests/peepopt/muldiv_c.ys
Normal file
343
tests/peepopt/muldiv_c.ys
Normal file
|
@ -0,0 +1,343 @@
|
|||
log -header "Test simple positive case"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top (
|
||||
input wire [11:0] a,
|
||||
output wire [11:0] y
|
||||
);
|
||||
assign y = (a * 16'd5140) / (257 * 2);
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-none t:$div
|
||||
design -reset
|
||||
|
||||
|
||||
log -pop
|
||||
log -header "Test negative case where div is kept"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top (
|
||||
input wire signed [11:0] a,
|
||||
output wire signed [31:0] y,
|
||||
output wire probe
|
||||
);
|
||||
wire [28:0] tmp = (a * 16'd5140);
|
||||
assign probe = tmp[28];
|
||||
|
||||
assign y = tmp[27:0] / (257 * 2);
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-any t:$div
|
||||
design -reset
|
||||
|
||||
|
||||
log -pop
|
||||
log -header "Basic pattern transformed: (a * b) / c"
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input signed [3:0] a,
|
||||
output signed [7:0] y,
|
||||
);
|
||||
wire signed [7:0] mul;
|
||||
assign mul = a * 4'sd6;
|
||||
assign y = mul / 8'sd3;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 0 t:$div
|
||||
design -reset
|
||||
|
||||
|
||||
log -pop
|
||||
log -header "Transformed on symmetry in multiplication"
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input signed [3:0] a,
|
||||
output signed [7:0] y,
|
||||
);
|
||||
wire signed [7:0] mul;
|
||||
assign mul = 4'sd6 * a;
|
||||
assign y = mul / 8'sd3;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 0 t:$div
|
||||
design -reset
|
||||
|
||||
log -pop
|
||||
log -header "Transformed on b == c"
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input signed [3:0] a,
|
||||
output signed [7:0] y,
|
||||
);
|
||||
wire signed [7:0] mul;
|
||||
assign mul = a * 4'sd6;
|
||||
assign y = mul / 8'sd6;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 0 t:$div
|
||||
design -reset
|
||||
|
||||
|
||||
log -pop
|
||||
log -header "b negative, c positive"
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input signed [3:0] a,
|
||||
output signed [7:0] y,
|
||||
);
|
||||
wire signed [7:0] mul;
|
||||
assign mul = a * -4'sd6;
|
||||
assign y = mul / 8'sd3;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 0 t:$div
|
||||
design -reset
|
||||
|
||||
|
||||
log -pop
|
||||
log -header "b positive, c negative"
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input signed [3:0] a,
|
||||
output signed [7:0] y,
|
||||
);
|
||||
wire signed [7:0] mul;
|
||||
assign mul = a * 4'sd6;
|
||||
assign y = mul / -8'sd3;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 0 t:$div
|
||||
design -reset
|
||||
|
||||
|
||||
log -pop
|
||||
log -header "No transform when b not divisible by c"
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input signed [3:0] a,
|
||||
output signed [7:0] y,
|
||||
);
|
||||
wire signed [7:0] mul;
|
||||
assign mul = a * 4'sd3;
|
||||
assign y = mul / 8'sd2;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 1 t:$div
|
||||
design -reset
|
||||
|
||||
|
||||
log -pop
|
||||
log -header "No transform when product has a second fanout"
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input signed [3:0] a,
|
||||
output signed [7:0] y,
|
||||
output signed [7:0] z,
|
||||
);
|
||||
wire signed [7:0] mul;
|
||||
assign mul = a * 4'sd6;
|
||||
assign y = mul / 8'sd3;
|
||||
assign z = mul;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 1 t:$div
|
||||
design -reset
|
||||
|
||||
|
||||
log -pop
|
||||
log -header "No transform when divisor is 0"
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input signed [3:0] a,
|
||||
output signed [7:0] y,
|
||||
);
|
||||
wire signed [7:0] mul;
|
||||
assign mul = a * 4'sd4;
|
||||
assign y = mul / 8'sd0;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 1 t:$div
|
||||
design -reset
|
||||
|
||||
|
||||
log -pop
|
||||
log -header "No transform when (a*b) output can overflow (divider’s A input signed)"
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input signed [3:0] a,
|
||||
output signed [7:0] y,
|
||||
);
|
||||
wire signed [5:0] mul;
|
||||
assign mul = a * 4'sd6;
|
||||
assign y = mul / 8'sd3;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 1 t:$div
|
||||
design -reset
|
||||
|
||||
|
||||
log -pop
|
||||
log -header "No transform when (a*b) output can overflow (divider’s A input signed)"
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input signed [3:0] a,
|
||||
output signed [7:0] y,
|
||||
);
|
||||
wire signed [6:0] mul;
|
||||
assign mul = a * 4'sd6;
|
||||
assign y = mul / 8'sd3;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 1 t:$div
|
||||
design -reset
|
||||
|
||||
|
||||
log -pop
|
||||
log -header "No transform when (a*b) output can overflow (divider’s A input unsigned)"
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [3:0] a,
|
||||
output [7:0] y,
|
||||
);
|
||||
wire [4:0] mul;
|
||||
assign mul = a * 4'd4;
|
||||
assign y = mul / 8'd2;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 1 t:$div
|
||||
design -reset
|
||||
|
||||
log -pop
|
||||
log -header "No transform when (a*b) output can overflow (divider’s A input unsigned)"
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [3:0] a,
|
||||
output [7:0] y,
|
||||
);
|
||||
wire [6:0] mul;
|
||||
assign mul = a * 4'd8;
|
||||
assign y = mul / 8'd2;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 1 t:$div
|
||||
design -reset
|
||||
|
||||
|
||||
log -pop
|
||||
log -header "No transform when (a*b) and x/c fitting criteria but not connected (x != a*b)"
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input signed [3:0] a,
|
||||
input signed [7:0] b,
|
||||
output signed [7:0] y,
|
||||
output signed [7:0] z,
|
||||
);
|
||||
assign y = a * 4'sd6;
|
||||
assign z = b / 8'sd3;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 1 t:$div
|
||||
design -reset
|
||||
|
||||
log -pop
|
||||
log -header "No transform when b only divisible by c if b misinterpreted as unsigned"
|
||||
# b 1001 is -7 but 9 misinterpreted
|
||||
# c 11 is 3
|
||||
log -push
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input signed [3:0] a,
|
||||
output signed [7:0] y,
|
||||
);
|
||||
wire signed [7:0] mul;
|
||||
assign mul = a * 4'sb1001;
|
||||
assign y = mul / 8'sb11;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 1 t:$div
|
||||
design -reset
|
||||
|
||||
log -pop
|
||||
log -header "Transform even if (a*b) result would overflow if divider’s A input signedness is confused & (A input is unsigned)"
|
||||
log -push
|
||||
# Transform even if:
|
||||
# (a*b) result would overflow if divider’s A input signedness is confused
|
||||
# (A input is unsigned)
|
||||
read_verilog <<EOT
|
||||
module top(
|
||||
input [3:0] a,
|
||||
output [7:0] y,
|
||||
);
|
||||
wire [7:0] mul;
|
||||
assign mul = a * 4'd6;
|
||||
assign y = mul / 8'd3;
|
||||
endmodule
|
||||
EOT
|
||||
equiv_opt -assert peepopt
|
||||
design -load postopt
|
||||
select -assert-count 1 t:$mul
|
||||
select -assert-count 0 t:$div
|
||||
design -reset
|
378
tests/peepopt/muxadd.ys
Normal file
378
tests/peepopt/muxadd.ys
Normal file
|
@ -0,0 +1,378 @@
|
|||
|
||||
|
||||
log -header "Test basic s?(a+b):a pattern gets transformed (a,b module inputs)"
|
||||
log -push
|
||||
design -reset
|
||||
log -header "Test basic s?(a+b):a pattern gets transformed (a,b module inputs)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [3:0] a;
|
||||
input wire [3:0] b;
|
||||
input wire s;
|
||||
output wire [3:0] y;
|
||||
|
||||
wire [3:0] ab = a + b;
|
||||
assign y = s ? ab : a;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-any t:$add %co1 %a w:y %i # assert adder rewired
|
||||
log -pop
|
||||
|
||||
log -header "Test basic s?(a+b):a pattern gets transformed (a,b module inputs)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [3:0] a;
|
||||
input wire [3:0] b;
|
||||
input wire s;
|
||||
output wire [3:0] y;
|
||||
assign y = s ? (a + b) : a;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-any t:$add %co1 %a w:y %i # assert adder rewired
|
||||
log -pop
|
||||
|
||||
log -header "Test basic s?(a+b):a pattern with intermediate var gets transformed (a,b module inputs)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [3:0] a;
|
||||
input wire [3:0] b;
|
||||
input wire s;
|
||||
output wire [3:0] y;
|
||||
|
||||
wire [3:0] ab = a + b;
|
||||
assign y = s ? ab : a;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-any t:$add %co1 %a w:y %i # assert adder rewired
|
||||
log -pop
|
||||
|
||||
log -header "Test basic s?(a+b):a pattern gets transformed (a is driven by a cell)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a_, b, s, y);
|
||||
input wire [3:0] a_;
|
||||
wire [3:0] a = ~a_;
|
||||
input wire [3:0] b;
|
||||
input wire s;
|
||||
output wire [3:0] y;
|
||||
|
||||
wire [3:0] ab = a + b;
|
||||
assign y = s ? ab : a;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-any t:$add %co1 %a w:y %i # assert adder rewired
|
||||
log -pop
|
||||
|
||||
log -header "Test basic s?(a+b):a pattern gets transformed (b is driven by a cell, output consumed by a cell)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b_, f, s, y_);
|
||||
input wire [3:0] a;
|
||||
input wire [3:0] b_;
|
||||
input wire [3:0] f;
|
||||
wire [3:0] b = b_ ^ f;
|
||||
input wire s;
|
||||
wire [3:0] y;
|
||||
output wire [3:0] y_;
|
||||
assign y_ = ~y;
|
||||
|
||||
wire [3:0] ab = a + b;
|
||||
assign y = s ? ab : a;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-any t:$add %co1 %a w:y %i # assert adder rewired
|
||||
log -pop
|
||||
|
||||
log -header "Test no transform when a+b has more fanouts (module output)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, ab, s, y);
|
||||
input wire [2:0] a;
|
||||
input wire [2:0] b;
|
||||
output wire [2:0] ab;
|
||||
output wire [2:0] y;
|
||||
input wire s;
|
||||
assign ab = a + b;
|
||||
assign y = s ? ab : a;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-none t:$add %co1 %a w:y %i
|
||||
log -pop
|
||||
|
||||
log -header "Test no transform when a+b has more fanouts (single bit, cell)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y, z);
|
||||
input wire [2:0] a;
|
||||
input wire [2:0] b;
|
||||
output wire [2:0] y;
|
||||
input wire s;
|
||||
wire [2:0] ab = a + b;
|
||||
assign y = s ? ab : a;
|
||||
output wire [2:0] z = !ab[1];
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-none t:$add %co1 %a w:y %i
|
||||
|
||||
log -pop
|
||||
log -header "Test no transform when a+b width smaller than a's width"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [3:0] a;
|
||||
input wire [3:0] b;
|
||||
output wire [3:0] y;
|
||||
input wire s;
|
||||
wire [2:0] ab = a + b;
|
||||
assign y = s ? ab : a;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
wreduce
|
||||
opt_clean
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-none t:$add %co1 %a w:y %i
|
||||
|
||||
log -pop
|
||||
log -header "Test no transform when (a+b) wider than a, adder’s a input is unsigned, a is not padded with zeros on the muxes input"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [2:0] a;
|
||||
input wire [3:0] b;
|
||||
output wire [3:0] y;
|
||||
input wire s;
|
||||
wire [3:0] ab = a + b;
|
||||
assign y = s ? ab : {a[2], a};
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
wreduce
|
||||
opt_clean
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-none t:$add %co1 %a w:y %i
|
||||
|
||||
log -pop
|
||||
log -header "Test no transform when (a+b) wider than a, adder’s a input is signed, a is not sign-extended on the muxes input"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [2:0] a;
|
||||
input wire [3:0] b;
|
||||
output wire [3:0] y;
|
||||
input wire s;
|
||||
wire signed [3:0] ab = $signed(a) + $signed(b);
|
||||
assign y = s ? ab : {1'b0, a};
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
wreduce
|
||||
opt_clean
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-none t:$add %co1 %a w:y %i
|
||||
|
||||
log -pop
|
||||
log -header "Test no transform when adder and mux not connected together but otherwise fitting transform. criteria"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [3:0] a;
|
||||
input wire [3:0] b;
|
||||
output wire [3:0] y;
|
||||
input wire s;
|
||||
wire [3:0] ab = a + b;
|
||||
wire [3:0] ab_ = !a;
|
||||
assign y = s ? ab_ : a;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
wreduce
|
||||
opt_clean
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-none t:$add %co1 %a w:y %i
|
||||
|
||||
log -pop
|
||||
log -header "Test transform when (a+b) wider than a, adder’s a input is unsigned, a padded with zeros on the muxes input"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [2:0] a;
|
||||
input wire [3:0] b;
|
||||
output wire [3:0] y;
|
||||
input wire s;
|
||||
wire [3:0] ab = a + b;
|
||||
assign y = s ? ab : {1'b0, a};
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
wreduce
|
||||
opt_clean
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-any t:$add %co1 %a w:y %i
|
||||
|
||||
log -pop
|
||||
log -header "Test transform when (a+b) wider than a, adder’s a input is signed, a sign-extended on the muxes input"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [2:0] a;
|
||||
input wire [3:0] b;
|
||||
output wire [3:0] y;
|
||||
input wire s;
|
||||
wire signed [3:0] ab = $signed(a) + $signed(b);
|
||||
assign y = s ? ab : {a[2], a};
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
wreduce
|
||||
opt_clean
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-any t:$add %co1 %a w:y %i
|
||||
|
||||
log -pop
|
||||
log -header "Test transform when pattern is s?a:(a+b)"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [3:0] a;
|
||||
input wire [3:0] b;
|
||||
output wire [3:0] y;
|
||||
input wire s;
|
||||
wire signed [3:0] ab = a + b;
|
||||
assign y = s ? a : ab;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
wreduce
|
||||
opt_clean
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-any t:$add %co1 %a w:y %i # assert adder rewired
|
||||
|
||||
log -pop
|
||||
log -header "Test transform when pattern is a?(b+a):a"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [3:0] a;
|
||||
input wire [3:0] b;
|
||||
output wire [3:0] y;
|
||||
input wire s;
|
||||
wire signed [3:0] ab = b + a;
|
||||
assign y = s ? ab : a;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
wreduce
|
||||
opt_clean
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-any t:$add %co1 %a w:y %i # assert adder rewired
|
||||
|
||||
log -pop
|
||||
log -header "Test transform when widths b > (a+b) > a"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [2:0] a;
|
||||
input wire [4:0] b;
|
||||
output wire [3:0] y;
|
||||
input wire s;
|
||||
wire signed [3:0] ab = a + b;
|
||||
assign y = s ? ab : a;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
wreduce
|
||||
opt_clean
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-any t:$add %co1 %a w:y %i # assert adder rewired
|
||||
|
||||
log -pop
|
||||
log -header "Test transform when widths (a+b) > a > b"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [2:0] a;
|
||||
input wire [3:0] b;
|
||||
output wire [4:0] y;
|
||||
input wire s;
|
||||
wire signed [4:0] ab = a + b;
|
||||
assign y = s ? ab : a;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
wreduce
|
||||
opt_clean
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-any t:$add %co1 %a w:y %i # assert adder rewired
|
||||
|
||||
log -pop
|
||||
log -header "Test transform when widths (a+b) > b > a"
|
||||
log -push
|
||||
design -reset
|
||||
read_verilog <<EOF
|
||||
module top(a, b, s, y);
|
||||
input wire [3:0] a;
|
||||
input wire [2:0] b;
|
||||
output wire [4:0] y;
|
||||
input wire s;
|
||||
wire signed [4:0] ab = a + b;
|
||||
assign y = s ? ab : a;
|
||||
endmodule
|
||||
EOF
|
||||
check -assert
|
||||
wreduce
|
||||
opt_clean
|
||||
equiv_opt -assert peepopt -withmuxadd
|
||||
design -load postopt
|
||||
select -assert-any t:$add %co1 %a w:y %i # assert adder rewired
|
6
tests/peepopt/run-test.sh
Executable file
6
tests/peepopt/run-test.sh
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
for x in *.ys; do
|
||||
echo "Running $x.."
|
||||
../../yosys -ql ${x%.ys}.log $x
|
||||
done
|
Loading…
Add table
Add a link
Reference in a new issue