mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-13 04:28:18 +00:00
Merge pull request #770 from whitequark/opt_expr_cmp
opt_expr: refactor and improve simplification of comparisons
This commit is contained in:
commit
0fc6e2bfcf
|
@ -259,6 +259,22 @@ bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative)
|
||||||
return last_bit_one;
|
return last_bit_one;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int get_highest_hot_index(RTLIL::SigSpec signal)
|
||||||
|
{
|
||||||
|
for (int i = GetSize(signal) - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (signal[i] == RTLIL::State::S0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (signal[i] == RTLIL::State::S1)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// if the signal has only one bit set, return the index of that bit.
|
// if the signal has only one bit set, return the index of that bit.
|
||||||
// otherwise return -1
|
// otherwise return -1
|
||||||
int get_onehot_bit_index(RTLIL::SigSpec signal)
|
int get_onehot_bit_index(RTLIL::SigSpec signal)
|
||||||
|
@ -1344,119 +1360,140 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace a<0 or a>=0 with the top bit of a
|
// simplify comparisons
|
||||||
if (do_fine && (cell->type == "$lt" || cell->type == "$ge" || cell->type == "$gt" || cell->type == "$le"))
|
if (do_fine && (cell->type == "$lt" || cell->type == "$ge" || cell->type == "$gt" || cell->type == "$le"))
|
||||||
{
|
{
|
||||||
//used to decide whether the signal needs to be negated
|
IdString cmp_type = cell->type;
|
||||||
bool is_lt = false;
|
SigSpec var_sig = cell->getPort("\\A");
|
||||||
|
SigSpec const_sig = cell->getPort("\\B");
|
||||||
|
int var_width = cell->parameters["\\A_WIDTH"].as_int();
|
||||||
|
int const_width = cell->parameters["\\B_WIDTH"].as_int();
|
||||||
|
bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
|
||||||
|
|
||||||
//references the variable signal in the comparison
|
if (!const_sig.is_fully_const())
|
||||||
RTLIL::SigSpec sigVar;
|
|
||||||
|
|
||||||
//references the constant signal in the comparison
|
|
||||||
RTLIL::SigSpec sigConst;
|
|
||||||
|
|
||||||
// note that this signal must be constant for the optimization
|
|
||||||
// to take place, but it is not checked beforehand.
|
|
||||||
// If new passes are added, this signal must be checked for const-ness
|
|
||||||
|
|
||||||
//width of the variable port
|
|
||||||
int width;
|
|
||||||
int const_width;
|
|
||||||
|
|
||||||
bool var_signed;
|
|
||||||
|
|
||||||
if (cell->type == "$lt" || cell->type == "$ge") {
|
|
||||||
is_lt = cell->type == "$lt" ? 1 : 0;
|
|
||||||
sigVar = cell->getPort("\\A");
|
|
||||||
sigConst = cell->getPort("\\B");
|
|
||||||
width = cell->parameters["\\A_WIDTH"].as_int();
|
|
||||||
const_width = cell->parameters["\\B_WIDTH"].as_int();
|
|
||||||
var_signed = cell->parameters["\\A_SIGNED"].as_bool();
|
|
||||||
} else
|
|
||||||
if (cell->type == "$gt" || cell->type == "$le") {
|
|
||||||
is_lt = cell->type == "$gt" ? 1 : 0;
|
|
||||||
sigVar = cell->getPort("\\B");
|
|
||||||
sigConst = cell->getPort("\\A");
|
|
||||||
width = cell->parameters["\\B_WIDTH"].as_int();
|
|
||||||
const_width = cell->parameters["\\A_WIDTH"].as_int();
|
|
||||||
var_signed = cell->parameters["\\B_SIGNED"].as_bool();
|
|
||||||
} else
|
|
||||||
log_abort();
|
|
||||||
|
|
||||||
// replace a(signed) < 0 with the high bit of a
|
|
||||||
if (sigConst.is_fully_const() && sigConst.is_fully_zero() && var_signed == true)
|
|
||||||
{
|
{
|
||||||
RTLIL::SigSpec a_prime(RTLIL::State::S0, cell->parameters["\\Y_WIDTH"].as_int());
|
std::swap(var_sig, const_sig);
|
||||||
a_prime[0] = sigVar[width - 1];
|
std::swap(var_width, const_width);
|
||||||
if (is_lt) {
|
if (cmp_type == "$gt")
|
||||||
log("Replacing %s cell `%s' (implementing X<0) with X[%d]: %s\n",
|
cmp_type = "$lt";
|
||||||
log_id(cell->type), log_id(cell), width-1, log_signal(a_prime));
|
else if (cmp_type == "$lt")
|
||||||
module->connect(cell->getPort("\\Y"), a_prime);
|
cmp_type = "$gt";
|
||||||
module->remove(cell);
|
else if (cmp_type == "$ge")
|
||||||
} else {
|
cmp_type = "$le";
|
||||||
log("Replacing %s cell `%s' (implementing X>=0) with ~X[%d]: %s\n",
|
else if (cmp_type == "$le")
|
||||||
log_id(cell->type), log_id(cell), width-1, log_signal(a_prime));
|
cmp_type = "$ge";
|
||||||
module->addNot(NEW_ID, a_prime, cell->getPort("\\Y"));
|
}
|
||||||
module->remove(cell);
|
|
||||||
}
|
if (const_sig.is_fully_def() && const_sig.is_fully_const())
|
||||||
did_something = true;
|
|
||||||
goto next_cell;
|
|
||||||
} else
|
|
||||||
if (sigConst.is_fully_const() && sigConst.is_fully_def() && var_signed == false)
|
|
||||||
{
|
{
|
||||||
if (sigConst.is_fully_zero()) {
|
std::string condition, replacement;
|
||||||
RTLIL::SigSpec a_prime(RTLIL::State::S0, GetSize(cell->getPort("\\Y")));
|
SigSpec replace_sig(State::S0, GetSize(cell->getPort("\\Y")));
|
||||||
if (is_lt) {
|
bool replace = false;
|
||||||
log("Replacing %s cell `%s' (implementing unsigned X<0) with constant false.\n",
|
bool remove = false;
|
||||||
log_id(cell->type), log_id(cell));
|
|
||||||
a_prime[0] = RTLIL::State::S0;
|
if (!is_signed)
|
||||||
} else {
|
{ /* unsigned */
|
||||||
log("Replacing %s cell `%s' (implementing unsigned X>=0) with constant true.\n",
|
if (const_sig.is_fully_zero() && cmp_type == "$lt") {
|
||||||
log_id(cell->type), log_id(cell));
|
condition = "unsigned X<0";
|
||||||
a_prime[0] = RTLIL::State::S1;
|
replacement = "constant 0";
|
||||||
|
replace_sig[0] = State::S0;
|
||||||
|
replace = true;
|
||||||
|
}
|
||||||
|
if (const_sig.is_fully_zero() && cmp_type == "$ge") {
|
||||||
|
condition = "unsigned X>=0";
|
||||||
|
replacement = "constant 1";
|
||||||
|
replace_sig[0] = State::S1;
|
||||||
|
replace = true;
|
||||||
|
}
|
||||||
|
if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$gt") {
|
||||||
|
condition = "unsigned X>~0";
|
||||||
|
replacement = "constant 0";
|
||||||
|
replace_sig[0] = State::S0;
|
||||||
|
replace = true;
|
||||||
|
}
|
||||||
|
if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$le") {
|
||||||
|
condition = "unsigned X<=~0";
|
||||||
|
replacement = "constant 1";
|
||||||
|
replace_sig[0] = State::S1;
|
||||||
|
replace = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int const_bit_hot = get_onehot_bit_index(const_sig);
|
||||||
|
if (const_bit_hot >= 0 && const_bit_hot < var_width)
|
||||||
|
{
|
||||||
|
RTLIL::SigSpec var_high_sig(RTLIL::State::S0, var_width - const_bit_hot);
|
||||||
|
for (int i = const_bit_hot; i < var_width; i++) {
|
||||||
|
var_high_sig[i - const_bit_hot] = var_sig[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmp_type == "$lt")
|
||||||
|
{
|
||||||
|
condition = stringf("unsigned X<%s", log_signal(const_sig));
|
||||||
|
replacement = stringf("!X[%d:%d]", var_width - 1, const_bit_hot);
|
||||||
|
module->addLogicNot(NEW_ID, var_high_sig, cell->getPort("\\Y"));
|
||||||
|
remove = true;
|
||||||
|
}
|
||||||
|
if (cmp_type == "$ge")
|
||||||
|
{
|
||||||
|
condition = stringf("unsigned X>=%s", log_signal(const_sig));
|
||||||
|
replacement = stringf("|X[%d:%d]", var_width - 1, const_bit_hot);
|
||||||
|
module->addReduceOr(NEW_ID, var_high_sig, cell->getPort("\\Y"));
|
||||||
|
remove = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int const_bit_set = get_highest_hot_index(const_sig);
|
||||||
|
if(const_bit_set >= var_width)
|
||||||
|
{
|
||||||
|
string cmp_name;
|
||||||
|
if (cmp_type == "$lt" || cmp_type == "$le")
|
||||||
|
{
|
||||||
|
if (cmp_type == "$lt") cmp_name = "<";
|
||||||
|
if (cmp_type == "$le") cmp_name = "<=";
|
||||||
|
condition = stringf("unsigned X[%d:0]%s%s", var_width - 1, cmp_name.c_str(), log_signal(const_sig));
|
||||||
|
replacement = "constant 1";
|
||||||
|
replace_sig[0] = State::S1;
|
||||||
|
replace = true;
|
||||||
|
}
|
||||||
|
if (cmp_type == "$gt" || cmp_type == "$ge")
|
||||||
|
{
|
||||||
|
if (cmp_type == "$gt") cmp_name = ">";
|
||||||
|
if (cmp_type == "$ge") cmp_name = ">=";
|
||||||
|
condition = stringf("unsigned X[%d:0]%s%s", var_width - 1, cmp_name.c_str(), log_signal(const_sig));
|
||||||
|
replacement = "constant 0";
|
||||||
|
replace_sig[0] = State::S0;
|
||||||
|
replace = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ /* signed */
|
||||||
|
if (const_sig.is_fully_zero() && cmp_type == "$lt")
|
||||||
|
{
|
||||||
|
condition = "signed X<0";
|
||||||
|
replacement = stringf("X[%d]", var_width - 1);
|
||||||
|
replace_sig[0] = var_sig[var_width - 1];
|
||||||
|
replace = true;
|
||||||
|
}
|
||||||
|
if (const_sig.is_fully_zero() && cmp_type == "$ge")
|
||||||
|
{
|
||||||
|
condition = "signed X>=0";
|
||||||
|
replacement = stringf("X[%d]", var_width - 1);
|
||||||
|
module->addNot(NEW_ID, var_sig[var_width - 1], cell->getPort("\\Y"));
|
||||||
|
remove = true;
|
||||||
}
|
}
|
||||||
module->connect(cell->getPort("\\Y"), a_prime);
|
|
||||||
module->remove(cell);
|
|
||||||
did_something = true;
|
|
||||||
goto next_cell;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int const_bit_set = get_onehot_bit_index(sigConst);
|
if (replace || remove)
|
||||||
if (const_bit_set >= 0 && const_bit_set < width) {
|
{
|
||||||
int bit_set = const_bit_set;
|
log("Replacing %s cell `%s' (implementing %s) with %s.\n",
|
||||||
RTLIL::SigSpec a_prime(RTLIL::State::S0, width - bit_set);
|
log_id(cell->type), log_id(cell), condition.c_str(), replacement.c_str());
|
||||||
for (int i = bit_set; i < width; i++) {
|
if (replace)
|
||||||
a_prime[i - bit_set] = sigVar[i];
|
module->connect(cell->getPort("\\Y"), replace_sig);
|
||||||
}
|
|
||||||
if (is_lt) {
|
|
||||||
log("Replacing %s cell `%s' (implementing unsigned X<%s) with !X[%d:%d]: %s.\n",
|
|
||||||
log_id(cell->type), log_id(cell), log_signal(sigConst), width - 1, bit_set, log_signal(a_prime));
|
|
||||||
module->addLogicNot(NEW_ID, a_prime, cell->getPort("\\Y"));
|
|
||||||
} else {
|
|
||||||
log("Replacing %s cell `%s' (implementing unsigned X>=%s) with |X[%d:%d]: %s.\n",
|
|
||||||
log_id(cell->type), log_id(cell), log_signal(sigConst), width - 1, bit_set, log_signal(a_prime));
|
|
||||||
module->addReduceOr(NEW_ID, a_prime, cell->getPort("\\Y"));
|
|
||||||
}
|
|
||||||
module->remove(cell);
|
module->remove(cell);
|
||||||
did_something = true;
|
did_something = true;
|
||||||
goto next_cell;
|
goto next_cell;
|
||||||
}
|
}
|
||||||
else if(const_bit_set >= width && const_bit_set >= 0){
|
|
||||||
RTLIL::SigSpec a_prime(RTLIL::State::S0, 1);
|
|
||||||
if(is_lt){
|
|
||||||
a_prime[0] = RTLIL::State::S1;
|
|
||||||
log("Replacing %s cell `%s' (implementing unsigned X[%d:0] < %s[%d:0]) with constant 0.\n", log_id(cell->type), log_id(cell), width-1, log_signal(sigConst),const_width-1);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
log("Replacing %s cell `%s' (implementing unsigned X[%d:0]>= %s[%d:0]) with constant 1.\n", log_id(cell->type), log_id(cell), width-1, log_signal(sigConst),const_width-1);
|
|
||||||
}
|
|
||||||
module->connect(cell->getPort("\\Y"), a_prime);
|
|
||||||
module->remove(cell);
|
|
||||||
did_something = true;
|
|
||||||
goto next_cell;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
40
tests/opt/opt_expr_cmp.v
Normal file
40
tests/opt/opt_expr_cmp.v
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
module top(...);
|
||||||
|
input [3:0] a;
|
||||||
|
|
||||||
|
output o1_1 = 4'b0000 > a;
|
||||||
|
output o1_2 = 4'b0000 <= a;
|
||||||
|
output o1_3 = 4'b1111 < a;
|
||||||
|
output o1_4 = 4'b1111 >= a;
|
||||||
|
output o1_5 = a < 4'b0000;
|
||||||
|
output o1_6 = a >= 4'b0000;
|
||||||
|
output o1_7 = a > 4'b1111;
|
||||||
|
output o1_8 = a <= 4'b1111;
|
||||||
|
|
||||||
|
output o2_1 = 4'sb0000 > $signed(a);
|
||||||
|
output o2_2 = 4'sb0000 <= $signed(a);
|
||||||
|
output o2_3 = $signed(a) < 4'sb0000;
|
||||||
|
output o2_4 = $signed(a) >= 4'sb0000;
|
||||||
|
|
||||||
|
output o3_1 = 4'b0100 > a;
|
||||||
|
output o3_2 = 4'b0100 <= a;
|
||||||
|
output o3_3 = a < 4'b0100;
|
||||||
|
output o3_4 = a >= 4'b0100;
|
||||||
|
|
||||||
|
output o4_1 = 5'b10000 > a;
|
||||||
|
output o4_2 = 5'b10000 >= a;
|
||||||
|
output o4_3 = 5'b10000 < a;
|
||||||
|
output o4_4 = 5'b10000 <= a;
|
||||||
|
output o4_5 = a < 5'b10000;
|
||||||
|
output o4_6 = a <= 5'b10000;
|
||||||
|
output o4_7 = a > 5'b10000;
|
||||||
|
output o4_8 = a >= 5'b10000;
|
||||||
|
|
||||||
|
output o5_1 = 5'b10100 > a;
|
||||||
|
output o5_2 = 5'b10100 >= a;
|
||||||
|
output o5_3 = 5'b10100 < a;
|
||||||
|
output o5_4 = 5'b10100 <= a;
|
||||||
|
output o5_5 = a < 5'b10100;
|
||||||
|
output o5_6 = a <= 5'b10100;
|
||||||
|
output o5_7 = a > 5'b10100;
|
||||||
|
output o5_8 = a >= 5'b10100;
|
||||||
|
endmodule
|
4
tests/opt/opt_expr_cmp.ys
Normal file
4
tests/opt/opt_expr_cmp.ys
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
read_verilog opt_expr_cmp.v
|
||||||
|
equiv_opt -assert opt_expr -fine
|
||||||
|
design -load postopt
|
||||||
|
select -assert-count 0 t:$gt t:$ge t:$lt t:$le
|
Loading…
Reference in a new issue