199 std::map<RTLIL::SigSpec, RTLIL::SigSpec> invert_map;
202 std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit;
203 std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell;
205 for (
auto cell : module->
cells())
206 if (design->
selected(module, cell) && cell->type[0] ==
'$') {
207 if ((cell->type ==
"$_NOT_" || cell->type ==
"$not" || cell->type ==
"$logic_not") &&
208 cell->getPort(
"\\A").size() == 1 && cell->getPort(
"\\Y").size() == 1)
211 for (
auto &conn : cell->connections()) {
214 if (ct_combinational.
cell_input(cell->type, conn.first))
215 cell_to_inbit[cell].insert(sig.
begin(), sig.
end());
216 if (ct_combinational.
cell_output(cell->type, conn.first))
217 for (
auto &bit : sig)
218 outbit_to_cell[bit].insert(cell);
223 for (
auto &it_right : cell_to_inbit)
224 for (
auto &it_sigbit : it_right.second)
225 for (
auto &it_left : outbit_to_cell[it_sigbit])
226 cells.
edge(it_left, it_right.first);
230 for (
auto cell : cells.
sorted)
232 #define ACTION_DO(_p_, _s_) do { cover("opt.opt_const.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0)
233 #define ACTION_DO_Y(_v_) ACTION_DO("\\Y", RTLIL::SigSpec(RTLIL::State::S ## _v_))
237 if (cell->type ==
"$not" || cell->type ==
"$pos" ||
238 cell->type ==
"$and" || cell->type ==
"$or" || cell->type ==
"$xor" || cell->type ==
"$xnor")
242 if (cell->type ==
"$reduce_and")
254 }
else if (bit.wire !=
NULL) {
259 cover(
"opt.opt_const.fine.$reduce_and");
260 log(
"Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
262 cell->setPort(
"\\A", sig_a = new_a);
263 cell->parameters.at(
"\\A_WIDTH") = 1;
268 if (cell->type ==
"$logic_not" || cell->type ==
"$logic_and" || cell->type ==
"$logic_or" || cell->type ==
"$reduce_or" || cell->type ==
"$reduce_bool")
280 }
else if (bit.wire !=
NULL) {
285 cover_list(
"opt.opt_const.fine.A",
"$logic_not",
"$logic_and",
"$logic_or",
"$reduce_or",
"$reduce_bool", cell->type.str());
286 log(
"Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
288 cell->setPort(
"\\A", sig_a = new_a);
289 cell->parameters.at(
"\\A_WIDTH") = 1;
294 if (cell->type ==
"$logic_and" || cell->type ==
"$logic_or")
306 }
else if (bit.wire !=
NULL) {
311 cover_list(
"opt.opt_const.fine.B",
"$logic_and",
"$logic_or", cell->type.str());
312 log(
"Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
314 cell->setPort(
"\\B", sig_b = new_b);
315 cell->parameters.at(
"\\B_WIDTH") = 1;
322 cover(
"opt.opt_const.one_high");
328 cover(
"opt.opt_const.one_low");
333 if (cell->type ==
"$reduce_xor" || cell->type ==
"$reduce_xnor" || cell->type ==
"$shift" || cell->type ==
"$shiftx" ||
334 cell->type ==
"$shl" || cell->type ==
"$shr" || cell->type ==
"$sshl" || cell->type ==
"$sshr" ||
335 cell->type ==
"$lt" || cell->type ==
"$le" || cell->type ==
"$ge" || cell->type ==
"$gt" ||
336 cell->type ==
"$neg" || cell->type ==
"$add" || cell->type ==
"$sub" ||
337 cell->type ==
"$mul" || cell->type ==
"$div" || cell->type ==
"$mod" || cell->type ==
"$pow")
342 if (cell->type ==
"$shl" || cell->type ==
"$shr" || cell->type ==
"$sshl" || cell->type ==
"$sshr" || cell->type ==
"$shift" || cell->type ==
"$shiftx")
347 goto found_the_x_bit;
349 for (
auto &bit : sig_b.to_sigbit_vector())
351 goto found_the_x_bit;
355 cover_list(
"opt.opt_const.xbit",
"$reduce_xor",
"$reduce_xnor",
"$shl",
"$shr",
"$sshl",
"$sshr",
"$shift",
"$shiftx",
356 "$lt",
"$le",
"$ge",
"$gt",
"$neg",
"$add",
"$sub",
"$mul",
"$div",
"$mod",
"$pow", cell->type.str());
357 if (cell->type ==
"$reduce_xor" || cell->type ==
"$reduce_xnor" ||
358 cell->type ==
"$lt" || cell->type ==
"$le" || cell->type ==
"$ge" || cell->type ==
"$gt")
366 if ((cell->type ==
"$_NOT_" || cell->type ==
"$not" || cell->type ==
"$logic_not") && cell->getPort(
"\\Y").size() == 1 &&
367 invert_map.count(
assign_map(cell->getPort(
"\\A"))) != 0) {
368 cover_list(
"opt.opt_const.invert.double",
"$_NOT_",
"$not",
"$logic_not", cell->type.str());
373 if ((cell->type ==
"$_MUX_" || cell->type ==
"$mux") && invert_map.count(
assign_map(cell->getPort(
"\\S"))) != 0) {
374 cover_list(
"opt.opt_const.invert.muxsel",
"$_MUX_",
"$mux", cell->type.str());
375 log(
"Optimizing away select inverter for %s cell `%s' in module `%s'.\n",
log_id(cell->type),
log_id(cell),
log_id(module));
377 cell->setPort(
"\\A", cell->getPort(
"\\B"));
378 cell->setPort(
"\\B", tmp);
379 cell->setPort(
"\\S", invert_map.at(
assign_map(cell->getPort(
"\\S"))));
384 if (cell->type ==
"$_NOT_") {
392 if (cell->type ==
"$_AND_") {
394 input.
append(cell->getPort(
"\\B"));
395 input.
append(cell->getPort(
"\\A"));
411 if (cell->type ==
"$_OR_") {
413 input.
append(cell->getPort(
"\\B"));
414 input.
append(cell->getPort(
"\\A"));
430 if (cell->type ==
"$_XOR_") {
432 input.
append(cell->getPort(
"\\B"));
433 input.
append(cell->getPort(
"\\A"));
445 if (cell->type ==
"$_MUX_") {
447 input.
append(cell->getPort(
"\\S"));
448 input.
append(cell->getPort(
"\\B"));
449 input.
append(cell->getPort(
"\\A"));
456 if (input.
match(
"10 ")) {
457 cover(
"opt.opt_const.mux_to_inv");
458 cell->type =
"$_NOT_";
459 cell->setPort(
"\\A", input.
extract(0, 1));
460 cell->unsetPort(
"\\B");
461 cell->unsetPort(
"\\S");
476 if (cell->type ==
"$eq" || cell->type ==
"$ne" || cell->type ==
"$eqx" || cell->type ==
"$nex")
481 if (cell->parameters[
"\\A_WIDTH"].as_int() != cell->parameters[
"\\B_WIDTH"].as_int()) {
482 int width = std::max(cell->parameters[
"\\A_WIDTH"].as_int(), cell->parameters[
"\\B_WIDTH"].as_int());
483 a.
extend_u0(width, cell->parameters[
"\\A_SIGNED"].as_bool() && cell->parameters[
"\\B_SIGNED"].as_bool());
484 b.
extend_u0(width, cell->parameters[
"\\A_SIGNED"].as_bool() && cell->parameters[
"\\B_SIGNED"].as_bool());
490 for (
int i = 0; i <
GetSize(a); i++) {
492 cover_list(
"opt.opt_const.eqneq.isneq",
"$eq",
"$ne",
"$eqx",
"$nex", cell->type.str());
494 new_y.extend(cell->parameters[
"\\Y_WIDTH"].as_int(),
false);
504 if (new_a.
size() == 0) {
505 cover_list(
"opt.opt_const.eqneq.empty",
"$eq",
"$ne",
"$eqx",
"$nex", cell->type.str());
507 new_y.extend(cell->parameters[
"\\Y_WIDTH"].as_int(),
false);
513 cover_list(
"opt.opt_const.eqneq.resize",
"$eq",
"$ne",
"$eqx",
"$nex", cell->type.str());
514 cell->setPort(
"\\A", new_a);
515 cell->setPort(
"\\B", new_b);
516 cell->parameters[
"\\A_WIDTH"] = new_a.
size();
517 cell->parameters[
"\\B_WIDTH"] = new_b.
size();
521 if ((cell->type ==
"$eq" || cell->type ==
"$ne") && cell->parameters[
"\\Y_WIDTH"].as_int() == 1 &&
522 cell->parameters[
"\\A_WIDTH"].as_int() == 1 && cell->parameters[
"\\B_WIDTH"].as_int() == 1)
528 cover_list(
"opt.opt_const.eqneq.swapconst",
"$eq",
"$ne", cell->type.str());
530 cell->setPort(
"\\A", cell->getPort(
"\\B"));
531 cell->setPort(
"\\B", tmp);
534 if (b.is_fully_const()) {
535 if (b.as_bool() == (cell->type ==
"$eq")) {
539 cover_list(
"opt.opt_const.eqneq.isnot",
"$eq",
"$ne", cell->type.str());
540 log(
"Replacing %s cell `%s' in module `%s' with inverter.\n",
log_id(cell->type),
log_id(cell),
log_id(module));
542 cell->parameters.erase(
"\\B_WIDTH");
543 cell->parameters.erase(
"\\B_SIGNED");
544 cell->unsetPort(
"\\B");
551 if (cell->type.in(
"$shl",
"$shr",
"$sshl",
"$sshr",
"$shift",
"$shiftx") &&
assign_map(cell->getPort(
"\\B")).is_fully_const())
553 bool sign_ext = cell->type ==
"$sshr" && cell->getParam(
"\\A_SIGNED").as_bool();
554 int shift_bits =
assign_map(cell->getPort(
"\\B")).as_int(cell->type.in(
"$shift",
"$shiftx") && cell->getParam(
"\\B_SIGNED").as_bool());
556 if (cell->type.in(
"$shl",
"$sshl"))
563 sig_a.
extend(
GetSize(sig_y), cell->getParam(
"\\A_SIGNED").as_bool());
565 for (
int i = 0; i <
GetSize(sig_y); i++) {
566 int idx = i + shift_bits;
567 if (0 <= idx && idx <
GetSize(sig_a))
568 sig_y[i] = sig_a[idx];
569 else if (
GetSize(sig_a) <= idx && sign_ext)
570 sig_y[i] = sig_a[
GetSize(sig_a)-1];
573 cover_list(
"opt.opt_const.constshift",
"$shl",
"$shr",
"$sshl",
"$sshr",
"$shift",
"$shiftx", cell->type.str());
575 log(
"Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
578 module->
connect(cell->getPort(
"\\Y"), sig_y);
587 bool identity_wrt_a =
false;
588 bool identity_wrt_b =
false;
590 if (cell->type ==
"$add" || cell->type ==
"$sub" || cell->type ==
"$or" || cell->type ==
"$xor")
596 identity_wrt_b =
true;
598 if (b.is_fully_const() && b.as_bool() ==
false)
599 identity_wrt_a =
true;
602 if (cell->type ==
"$shl" || cell->type ==
"$shr" || cell->type ==
"$sshl" || cell->type ==
"$sshr" || cell->type ==
"$shift" || cell->type ==
"$shiftx")
607 identity_wrt_a =
true;
610 if (cell->type ==
"$mul")
616 identity_wrt_b =
true;
618 if (b.is_fully_const() && b.size() <= 32 && b.as_int() == 1)
619 identity_wrt_a =
true;
622 if (cell->type ==
"$div")
627 identity_wrt_a =
true;
630 if (identity_wrt_a || identity_wrt_b)
633 cover_list(
"opt.opt_const.identwrt.a",
"$add",
"$sub",
"$or",
"$xor",
"$shl",
"$shr",
"$sshl",
"$sshr",
"$shift",
"$shiftx",
"$mul",
"$div", cell->type.str());
635 cover_list(
"opt.opt_const.identwrt.b",
"$add",
"$sub",
"$or",
"$xor",
"$shl",
"$shr",
"$sshl",
"$sshr",
"$shift",
"$shiftx",
"$mul",
"$div", cell->type.str());
637 log(
"Replacing %s cell `%s' in module `%s' with identity for port %c.\n",
638 cell->type.c_str(), cell->name.c_str(), module->
name.
c_str(), identity_wrt_a ?
'A' :
'B');
640 if (!identity_wrt_a) {
641 cell->setPort(
"\\A", cell->getPort(
"\\B"));
642 cell->parameters.at(
"\\A_WIDTH") = cell->parameters.at(
"\\B_WIDTH");
643 cell->parameters.at(
"\\A_SIGNED") = cell->parameters.at(
"\\B_SIGNED");
647 cell->unsetPort(
"\\B");
648 cell->parameters.erase(
"\\B_WIDTH");
649 cell->parameters.erase(
"\\B_SIGNED");
657 if (mux_bool && (cell->type ==
"$mux" || cell->type ==
"$_MUX_") &&
659 cover_list(
"opt.opt_const.mux_bool",
"$mux",
"$_MUX_", cell->type.str());
664 if (mux_bool && (cell->type ==
"$mux" || cell->type ==
"$_MUX_") &&
666 cover_list(
"opt.opt_const.mux_invert",
"$mux",
"$_MUX_", cell->type.str());
667 log(
"Replacing %s cell `%s' in module `%s' with inverter.\n",
log_id(cell->type),
log_id(cell),
log_id(module));
668 cell->setPort(
"\\A", cell->getPort(
"\\S"));
669 cell->unsetPort(
"\\B");
670 cell->unsetPort(
"\\S");
671 if (cell->type ==
"$mux") {
672 cell->parameters[
"\\A_WIDTH"] = cell->parameters[
"\\WIDTH"];
673 cell->parameters[
"\\Y_WIDTH"] = cell->parameters[
"\\WIDTH"];
674 cell->parameters[
"\\A_SIGNED"] = 0;
675 cell->parameters.erase(
"\\WIDTH");
678 cell->type =
"$_NOT_";
683 if (consume_x && mux_bool && (cell->type ==
"$mux" || cell->type ==
"$_MUX_") && cell->getPort(
"\\A") ==
RTLIL::SigSpec(0, 1)) {
684 cover_list(
"opt.opt_const.mux_and",
"$mux",
"$_MUX_", cell->type.str());
685 log(
"Replacing %s cell `%s' in module `%s' with and-gate.\n",
log_id(cell->type),
log_id(cell),
log_id(module));
686 cell->setPort(
"\\A", cell->getPort(
"\\S"));
687 cell->unsetPort(
"\\S");
688 if (cell->type ==
"$mux") {
689 cell->parameters[
"\\A_WIDTH"] = cell->parameters[
"\\WIDTH"];
690 cell->parameters[
"\\B_WIDTH"] = cell->parameters[
"\\WIDTH"];
691 cell->parameters[
"\\Y_WIDTH"] = cell->parameters[
"\\WIDTH"];
692 cell->parameters[
"\\A_SIGNED"] = 0;
693 cell->parameters[
"\\B_SIGNED"] = 0;
694 cell->parameters.erase(
"\\WIDTH");
697 cell->type =
"$_AND_";
702 if (consume_x && mux_bool && (cell->type ==
"$mux" || cell->type ==
"$_MUX_") && cell->getPort(
"\\B") ==
RTLIL::SigSpec(1, 1)) {
703 cover_list(
"opt.opt_const.mux_or",
"$mux",
"$_MUX_", cell->type.str());
704 log(
"Replacing %s cell `%s' in module `%s' with or-gate.\n",
log_id(cell->type),
log_id(cell),
log_id(module));
705 cell->setPort(
"\\B", cell->getPort(
"\\S"));
706 cell->unsetPort(
"\\S");
707 if (cell->type ==
"$mux") {
708 cell->parameters[
"\\A_WIDTH"] = cell->parameters[
"\\WIDTH"];
709 cell->parameters[
"\\B_WIDTH"] = cell->parameters[
"\\WIDTH"];
710 cell->parameters[
"\\Y_WIDTH"] = cell->parameters[
"\\WIDTH"];
711 cell->parameters[
"\\A_SIGNED"] = 0;
712 cell->parameters[
"\\B_SIGNED"] = 0;
713 cell->parameters.erase(
"\\WIDTH");
716 cell->type =
"$_OR_";
721 if (mux_undef && (cell->type ==
"$mux" || cell->type ==
"$pmux")) {
723 int width = cell->getPort(
"\\A").
size();
724 if ((cell->getPort(
"\\A").is_fully_undef() && cell->getPort(
"\\B").is_fully_undef()) ||
725 cell->getPort(
"\\S").is_fully_undef()) {
726 cover_list(
"opt.opt_const.mux_undef",
"$mux",
"$pmux", cell->type.str());
730 for (
int i = 0; i < cell->getPort(
"\\S").size(); i++) {
738 new_a = cell->getPort(
"\\A");
740 new_a = new_b.
extract((new_s.
size()-1)*width, width);
744 if (new_s.
size() == 0) {
745 cover_list(
"opt.opt_const.mux_empty",
"$mux",
"$pmux", cell->type.str());
750 cover_list(
"opt.opt_const.mux_sel01",
"$mux",
"$pmux", cell->type.str());
754 if (cell->getPort(
"\\S").size() != new_s.
size()) {
755 cover_list(
"opt.opt_const.mux_reduce",
"$mux",
"$pmux", cell->type.str());
756 log(
"Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
758 cell->setPort(
"\\A", new_a);
759 cell->setPort(
"\\B", new_b);
760 cell->setPort(
"\\S", new_s);
761 if (new_s.
size() > 1) {
762 cell->type =
"$pmux";
763 cell->parameters[
"\\S_WIDTH"] = new_s.
size();
766 cell->parameters.erase(
"\\S_WIDTH");
772 #define FOLD_1ARG_CELL(_t) \
773 if (cell->type == "$" #_t) { \
774 RTLIL::SigSpec a = cell->getPort("\\A"); \
775 assign_map.apply(a); \
776 if (a.is_fully_const()) { \
777 RTLIL::Const dummy_arg(RTLIL::State::S0, 1); \
778 RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), dummy_arg, \
779 cell->parameters["\\A_SIGNED"].as_bool(), false, \
780 cell->parameters["\\Y_WIDTH"].as_int())); \
781 cover("opt.opt_const.const.$" #_t); \
782 replace_cell(assign_map, module, cell, stringf("%s", log_signal(a)), "\\Y", y); \
786 #define FOLD_2ARG_CELL(_t) \
787 if (cell->type == "$" #_t) { \
788 RTLIL::SigSpec a = cell->getPort("\\A"); \
789 RTLIL::SigSpec b = cell->getPort("\\B"); \
790 assign_map.apply(a), assign_map.apply(b); \
791 if (a.is_fully_const() && b.is_fully_const()) { \
792 RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), b.as_const(), \
793 cell->parameters["\\A_SIGNED"].as_bool(), \
794 cell->parameters["\\B_SIGNED"].as_bool(), \
795 cell->parameters["\\Y_WIDTH"].as_int())); \
796 cover("opt.opt_const.const.$" #_t); \
797 replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \
843 if (cell->type == "$mux") {
848 ACTION_DO(
"\\Y", input.
as_bool() ? cell->getPort(
"\\B") : cell->getPort(
"\\A"));
853 if (!keepdc && cell->type ==
"$mul")
855 bool a_signed = cell->parameters[
"\\A_SIGNED"].as_bool();
856 bool b_signed = cell->parameters[
"\\B_SIGNED"].as_bool();
857 bool swapped_ab =
false;
863 if (sig_b.is_fully_const() && sig_b.size() <= 32)
864 std::swap(sig_a, sig_b), std::swap(a_signed, b_signed), swapped_ab =
true;
868 int a_val = sig_a.
as_int();
872 cover(
"opt.opt_const.mul_shift.zero");
874 log(
"Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
875 cell->name.c_str(), module->
name.
c_str());
884 for (
int i = 1; i < (a_signed ? sig_a.
size()-1 : sig_a.
size()); i++)
885 if (a_val == (1 << i))
888 cover(
"opt.opt_const.mul_shift.swapped");
890 cover(
"opt.opt_const.mul_shift.unswapped");
892 log(
"Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
893 a_val, cell->name.c_str(), module->
name.
c_str(), i);
896 cell->setPort(
"\\A", cell->getPort(
"\\B"));
897 cell->parameters[
"\\A_WIDTH"] = cell->parameters[
"\\B_WIDTH"];
898 cell->parameters[
"\\A_SIGNED"] = cell->parameters[
"\\B_SIGNED"];
907 cell->parameters[
"\\B_WIDTH"] =
GetSize(new_b);
908 cell->parameters[
"\\B_SIGNED"] =
false;
909 cell->setPort(
"\\B", new_b);
921 #undef FOLD_1ARG_CELL
922 #undef FOLD_2ARG_CELL
const char * c_str() const
bool selected(T1 *module) const
bool is_fully_def() const
bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutative, SigMap &sigmap)
#define ACTION_DO(_p_, _s_)
bool is_fully_undef() const
const char * log_signal(const RTLIL::SigSpec &sig, bool autoint)
static std::string idx(std::string str)
void extend_u0(int width, bool is_signed=false)
#define FOLD_2ARG_CELL(_t)
void apply(RTLIL::SigBit &bit) const
bool match(std::string pattern) const
USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN bool did_something
bool cell_known(RTLIL::IdString type)
void connect(const RTLIL::SigSig &conn)
bool cell_output(RTLIL::IdString type, RTLIL::IdString port)
void edge(T left, T right)
int GetSize(RTLIL::Wire *wire)
RTLIL::SigSpecIterator begin()
#define log_assert(_assert_expr_)
bool is_fully_const() const
RTLIL::ObjRange< RTLIL::Cell * > cells()
int as_int(bool is_signed=false) const
void remove(const std::set< RTLIL::Wire * > &wires)
void log(const char *format,...)
RTLIL::SigSpec extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other=NULL) const
static RTLIL::State logic_or(RTLIL::State a, RTLIL::State b)
void append(const RTLIL::SigSpec &signal)
void extend(int width, bool is_signed=false)
bool cell_input(RTLIL::IdString type, RTLIL::IdString port)
void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val)
static RTLIL::State logic_and(RTLIL::State a, RTLIL::State b)
std::pair< SigSpec, SigSpec > SigSig
const char * log_id(RTLIL::IdString str)
RTLIL::SigSpecIterator end()
std::vector< RTLIL::SigBit > to_sigbit_vector() const
#define FOLD_1ARG_CELL(_t)