summaryrefslogtreecommitdiff
path: root/passes/opt/opt_expr.cc
diff options
context:
space:
mode:
Diffstat (limited to 'passes/opt/opt_expr.cc')
-rw-r--r--passes/opt/opt_expr.cc315
1 files changed, 304 insertions, 11 deletions
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index b62eae28..0ba233c6 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -259,6 +259,29 @@ bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative)
return last_bit_one;
}
+// if the signal has only one bit set, return the index of that bit.
+// otherwise return -1
+int get_onehot_bit_index(RTLIL::SigSpec signal)
+{
+ int bit_index = -1;
+
+ for (int i = 0; i < GetSize(signal); i++)
+ {
+ if (signal[i] == RTLIL::State::S0)
+ continue;
+
+ if (signal[i] != RTLIL::State::S1)
+ return -1;
+
+ if (bit_index != -1)
+ return -1;
+
+ bit_index = i;
+ }
+
+ return bit_index;
+}
+
void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool clkinv)
{
if (!design->selected(module))
@@ -348,19 +371,20 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (cell->type.in("$reduce_and", "$_AND_"))
detect_const_and = true;
- if (cell->type.in("$and", "$logic_and") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1)
+ if (cell->type.in("$and", "$logic_and") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1 && !cell->getParam("\\A_SIGNED").as_bool())
detect_const_and = true;
if (cell->type.in("$reduce_or", "$reduce_bool", "$_OR_"))
detect_const_or = true;
- if (cell->type.in("$or", "$logic_or") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1)
+ if (cell->type.in("$or", "$logic_or") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1 && !cell->getParam("\\A_SIGNED").as_bool())
detect_const_or = true;
if (detect_const_and || detect_const_or)
{
pool<SigBit> input_bits = assign_map(cell->getPort("\\A")).to_sigbit_pool();
- bool found_zero = false, found_one = false, found_inv = false;
+ bool found_zero = false, found_one = false, found_undef = false, found_inv = false, many_conconst = false;
+ SigBit non_const_input = State::Sm;
if (cell->hasPort("\\B")) {
vector<SigBit> more_bits = assign_map(cell->getPort("\\B")).to_sigbit_vector();
@@ -368,12 +392,20 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
for (auto bit : input_bits) {
- if (bit == State::S0)
- found_zero = true;
- if (bit == State::S1)
- found_one = true;
- if (invert_map.count(bit) && input_bits.count(invert_map.at(bit)))
- found_inv = true;
+ if (bit.wire) {
+ if (invert_map.count(bit) && input_bits.count(invert_map.at(bit)))
+ found_inv = true;
+ if (non_const_input != State::Sm)
+ many_conconst = true;
+ non_const_input = many_conconst ? State::Sm : bit;
+ } else {
+ if (bit == State::S0)
+ found_zero = true;
+ else if (bit == State::S1)
+ found_one = true;
+ else
+ found_undef = true;
+ }
}
if (detect_const_and && (found_zero || found_inv)) {
@@ -387,6 +419,12 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
replace_cell(assign_map, module, cell, "const_or", "\\Y", RTLIL::State::S1);
goto next_cell;
}
+
+ if (non_const_input != State::Sm && !found_undef) {
+ cover("opt.opt_expr.and_or_buffer");
+ replace_cell(assign_map, module, cell, "and_or_buffer", "\\Y", non_const_input);
+ goto next_cell;
+ }
}
if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor", "$neg") &&
@@ -411,6 +449,53 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (group_cell_inputs(module, cell, true, assign_map))
goto next_cell;
+ if (cell->type == "$logic_not" || cell->type == "$logic_and" || cell->type == "$logic_or" ||
+ cell->type == "$reduce_or" || cell->type == "$reduce_and" || cell->type == "$reduce_bool")
+ {
+ SigBit neutral_bit = cell->type == "$reduce_and" ? State::S1 : State::S0;
+
+ RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
+ RTLIL::SigSpec new_sig_a;
+
+ for (auto bit : sig_a)
+ if (bit != neutral_bit) new_sig_a.append(bit);
+
+ if (GetSize(new_sig_a) == 0)
+ new_sig_a.append(neutral_bit);
+
+ if (GetSize(new_sig_a) < GetSize(sig_a)) {
+ cover_list("opt.opt_expr.fine.neutral_A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_and", "$reduce_bool", cell->type.str());
+ log("Replacing port A of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
+ cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_sig_a));
+ cell->setPort("\\A", new_sig_a);
+ cell->parameters.at("\\A_WIDTH") = GetSize(new_sig_a);
+ did_something = true;
+ }
+ }
+
+ if (cell->type == "$logic_and" || cell->type == "$logic_or")
+ {
+ SigBit neutral_bit = State::S0;
+
+ RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B"));
+ RTLIL::SigSpec new_sig_b;
+
+ for (auto bit : sig_b)
+ if (bit != neutral_bit) new_sig_b.append(bit);
+
+ if (GetSize(new_sig_b) == 0)
+ new_sig_b.append(neutral_bit);
+
+ if (GetSize(new_sig_b) < GetSize(sig_b)) {
+ cover_list("opt.opt_expr.fine.neutral_B", "$logic_and", "$logic_or", cell->type.str());
+ log("Replacing port B of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
+ cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_sig_b));
+ cell->setPort("\\B", new_sig_b);
+ cell->parameters.at("\\B_WIDTH") = GetSize(new_sig_b);
+ did_something = true;
+ }
+ }
+
if (cell->type == "$reduce_and")
{
RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A"));
@@ -633,6 +718,23 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
+ if (cell->type == "$_TBUF_" || cell->type == "$tribuf") {
+ RTLIL::SigSpec input = cell->getPort(cell->type == "$_TBUF_" ? "\\E" : "\\EN");
+ RTLIL::SigSpec a = cell->getPort("\\A");
+ assign_map.apply(input);
+ assign_map.apply(a);
+ if (input == State::S1)
+ ACTION_DO("\\Y", cell->getPort("\\A"));
+ if (input == State::S0 && !a.is_fully_undef()) {
+ cover("opt.opt_expr.action_" S__LINE__);
+ log("Replacing data input of %s cell `%s' in module `%s' with constant undef.\n",
+ cell->type.c_str(), cell->name.c_str(), module->name.c_str());
+ cell->setPort("\\A", SigSpec(State::Sx, GetSize(a)));
+ did_something = true;
+ goto next_cell;
+ }
+ }
+
if (cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex")
{
RTLIL::SigSpec a = cell->getPort("\\A");
@@ -1167,6 +1269,197 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
+ // remove redundant pairs of bits in ==, ===, !=, and !==
+ // replace cell with const driver if inputs can't be equal
+ if (do_fine && cell->type.in("$eq", "$ne", "$eqx", "$nex"))
+ {
+ pool<pair<SigBit, SigBit>> redundant_cache;
+ mfp<SigBit> contradiction_cache;
+
+ contradiction_cache.promote(State::S0);
+ contradiction_cache.promote(State::S1);
+
+ int a_width = cell->getParam("\\A_WIDTH").as_int();
+ int b_width = cell->getParam("\\B_WIDTH").as_int();
+
+ bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
+ int width = is_signed ? std::min(a_width, b_width) : std::max(a_width, b_width);
+
+ SigSpec sig_a = cell->getPort("\\A");
+ SigSpec sig_b = cell->getPort("\\B");
+
+ int redundant_bits = 0;
+
+ for (int i = width-1; i >= 0; i--)
+ {
+ SigBit bit_a = i < a_width ? assign_map(sig_a[i]) : State::S0;
+ SigBit bit_b = i < b_width ? assign_map(sig_b[i]) : State::S0;
+
+ if (bit_a != State::Sx && bit_a != State::Sz &&
+ bit_b != State::Sx && bit_b != State::Sz)
+ contradiction_cache.merge(bit_a, bit_b);
+
+ if (bit_b < bit_a)
+ std::swap(bit_a, bit_b);
+
+ pair<SigBit, SigBit> key(bit_a, bit_b);
+
+ if (redundant_cache.count(key)) {
+ if (i < a_width) sig_a.remove(i);
+ if (i < b_width) sig_b.remove(i);
+ redundant_bits++;
+ continue;
+ }
+
+ redundant_cache.insert(key);
+ }
+
+ if (contradiction_cache.find(State::S0) == contradiction_cache.find(State::S1))
+ {
+ SigSpec y_sig = cell->getPort("\\Y");
+ Const y_value(cell->type.in("$eq", "$eqx") ? 0 : 1, GetSize(y_sig));
+
+ log("Replacing cell `%s' in module `%s' with constant driver %s.\n",
+ log_id(cell), log_id(module), log_signal(y_value));
+
+ module->connect(y_sig, y_value);
+ module->remove(cell);
+
+ did_something = true;
+ goto next_cell;
+ }
+
+ if (redundant_bits)
+ {
+ log("Removed %d redundant input bits from %s cell `%s' in module `%s'.\n",
+ redundant_bits, log_id(cell->type), log_id(cell), log_id(module));
+
+ cell->setPort("\\A", sig_a);
+ cell->setPort("\\B", sig_b);
+ cell->setParam("\\A_WIDTH", GetSize(sig_a));
+ cell->setParam("\\B_WIDTH", GetSize(sig_b));
+
+ did_something = true;
+ goto next_cell;
+ }
+ }
+
+ // replace a<0 or a>=0 with the top bit of a
+ 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
+ bool is_lt = false;
+
+ //references the variable signal in the comparison
+ 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());
+ a_prime[0] = sigVar[width - 1];
+ if (is_lt) {
+ log("Replacing %s cell `%s' (implementing X<0) with X[%d]: %s\n",
+ log_id(cell->type), log_id(cell), width-1, log_signal(a_prime));
+ module->connect(cell->getPort("\\Y"), a_prime);
+ module->remove(cell);
+ } else {
+ log("Replacing %s cell `%s' (implementing X>=0) with ~X[%d]: %s\n",
+ log_id(cell->type), log_id(cell), width-1, log_signal(a_prime));
+ module->addNot(NEW_ID, a_prime, cell->getPort("\\Y"));
+ module->remove(cell);
+ }
+ did_something = true;
+ goto next_cell;
+ } else
+ if (sigConst.is_fully_const() && sigConst.is_fully_def() && var_signed == false)
+ {
+ if (sigConst.is_fully_zero()) {
+ RTLIL::SigSpec a_prime(RTLIL::State::S0, 1);
+ if (is_lt) {
+ log("Replacing %s cell `%s' (implementing unsigned X<0) with constant false.\n",
+ log_id(cell->type), log_id(cell));
+ a_prime[0] = RTLIL::State::S0;
+ } else {
+ log("Replacing %s cell `%s' (implementing unsigned X>=0) with constant true.\n",
+ log_id(cell->type), log_id(cell));
+ a_prime[0] = RTLIL::State::S1;
+ }
+ 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 (const_bit_set >= 0 && const_bit_set < width) {
+ int bit_set = const_bit_set;
+ RTLIL::SigSpec a_prime(RTLIL::State::S0, width - bit_set);
+ for (int i = bit_set; i < width; i++) {
+ a_prime[i - bit_set] = sigVar[i];
+ }
+ 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);
+ did_something = true;
+ 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;
+
+ }
+ }
+ }
+
next_cell:;
#undef ACTION_DO
#undef ACTION_DO_Y
@@ -1177,7 +1470,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
struct OptExprPass : public Pass {
OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1211,7 +1504,7 @@ struct OptExprPass : public Pass {
log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool mux_undef = false;
bool mux_bool = false;