mirror of
https://github.com/YosysHQ/yosys
synced 2025-08-09 04:31:25 +00:00
muxadd and muldiv_c peepopt
This commit is contained in:
parent
8dabfbe429
commit
31a5197a1c
9 changed files with 902 additions and 38 deletions
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue