mirror of
https://github.com/YosysHQ/yosys
synced 2025-05-09 16:55:49 +00:00
Merge 4bd91fbb11
into 6378ba10eb
This commit is contained in:
commit
d4ba45aea2
7 changed files with 488 additions and 0 deletions
1
Makefile
1
Makefile
|
@ -894,6 +894,7 @@ SH_TEST_DIRS += tests/bram
|
|||
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
|
||||
|
|
|
@ -32,6 +32,7 @@ PEEPOPT_PATTERN = passes/opt/peepopt_shiftmul_right.pmg
|
|||
PEEPOPT_PATTERN += passes/opt/peepopt_shiftmul_left.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_shiftadd.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_muldiv.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_muldiv_c.pmg
|
||||
PEEPOPT_PATTERN += passes/opt/peepopt_formal_clockgateff.pmg
|
||||
|
||||
passes/opt/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
|
||||
|
|
|
@ -29,6 +29,14 @@ bool did_something;
|
|||
// scratchpad configurations for pmgen
|
||||
int shiftadd_max_ratio;
|
||||
|
||||
// Helper function, removes LSB 0s
|
||||
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);
|
||||
}
|
||||
|
||||
#include "passes/opt/peepopt_pm.h"
|
||||
|
||||
struct PeepoptPass : public Pass {
|
||||
|
@ -45,6 +53,8 @@ struct PeepoptPass : public Pass {
|
|||
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");
|
||||
log("\n");
|
||||
log(" * shiftmul - Replace A>>(B*C) with A'>>(B<<K) where C and K are constants\n");
|
||||
log(" and A' is derived from A by appropriately inserting padding\n");
|
||||
log(" into the signal. (right variant)\n");
|
||||
|
@ -106,6 +116,7 @@ struct PeepoptPass : public Pass {
|
|||
pm.run_shiftmul_right();
|
||||
pm.run_shiftmul_left();
|
||||
pm.run_muldiv();
|
||||
pm.run_muldiv_c();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
125
passes/opt/peepopt_muldiv_c.pmg
Normal file
125
passes/opt/peepopt_muldiv_c.pmg
Normal file
|
@ -0,0 +1,125 @@
|
|||
pattern muldiv_c
|
||||
//
|
||||
// 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
|
||||
|
||||
match mul
|
||||
// Select multiplier
|
||||
select mul->type == $mul
|
||||
endmatch
|
||||
|
||||
code a b_const mul_y
|
||||
// Get multiplier signals
|
||||
a = port(mul, \A);
|
||||
b_const = port(mul, \B);
|
||||
mul_y = port(mul, \Y);
|
||||
|
||||
// Fanout of each multiplier Y bit should be 1 (no bit-split)
|
||||
if (nusers(mul_y) != 2)
|
||||
reject;
|
||||
|
||||
// A and B can be interchanged
|
||||
branch;
|
||||
std::swap(a, b_const);
|
||||
endcode
|
||||
|
||||
match div
|
||||
// Select div of form (a * b_const) / c_const
|
||||
select div->type == $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
|
||||
// Get div signals
|
||||
SigSpec div_a = port(div, \A);
|
||||
SigSpec c_const = port(div, \B);
|
||||
SigSpec div_y = port(div, \Y);
|
||||
|
||||
// Get offset of multiplier result chunk in divider
|
||||
int offset = GetSize(div_a) - GetSize(mul_y);
|
||||
|
||||
// Get properties and values of b_const and c_const
|
||||
// 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;
|
||||
|
||||
// Check that b is divisible by c
|
||||
if (b_const_int_shifted % c_const_int != 0)
|
||||
reject;
|
||||
|
||||
// Rewire to only keep multiplier
|
||||
mul->setPort(\A, a);
|
||||
mul->setPort(\B, const_ratio);
|
||||
mul->setPort(\Y, div_y);
|
||||
|
||||
// Remove divider
|
||||
autoremove(div);
|
||||
|
||||
// Log, fixup, accept
|
||||
log("muldiv_const pattern in %s: mul=%s, div=%s\n", log_id(module), log_id(mul), log_id(div));
|
||||
mul->fixup_parameters();
|
||||
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
|
6
tests/peepopt/run-test.sh
Normal file
6
tests/peepopt/run-test.sh
Normal 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