2 * yosys -- Yosys Open SYnthesis Suite
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include "kernel/register.h"
21 #include "kernel/sigtools.h"
22 #include "kernel/celltypes.h"
23 #include "kernel/utils.h"
24 #include "kernel/log.h"
30 PRIVATE_NAMESPACE_BEGIN
34 void replace_undriven(RTLIL::Design
*design
, RTLIL::Module
*module
)
37 SigMap
sigmap(module
);
38 SigPool driven_signals
;
42 dict
<SigBit
, pair
<Wire
*, State
>> initbits
;
43 pool
<Wire
*> revisit_initwires
;
45 for (auto cell
: module
->cells())
46 for (auto &conn
: cell
->connections()) {
47 if (!ct
.cell_known(cell
->type
) || ct
.cell_output(cell
->type
, conn
.first
))
48 driven_signals
.add(sigmap(conn
.second
));
49 if (!ct
.cell_known(cell
->type
) || ct
.cell_input(cell
->type
, conn
.first
))
50 used_signals
.add(sigmap(conn
.second
));
53 for (auto wire
: module
->wires()) {
54 if (wire
->attributes
.count(ID(init
))) {
55 SigSpec sig
= sigmap(wire
);
56 Const initval
= wire
->attributes
.at(ID(init
));
57 for (int i
= 0; i
< GetSize(initval
) && i
< GetSize(wire
); i
++) {
58 if (initval
[i
] == State::S0
|| initval
[i
] == State::S1
)
59 initbits
[sig
[i
]] = make_pair(wire
, initval
[i
]);
63 driven_signals
.add(sigmap(wire
));
64 if (wire
->port_output
|| wire
->get_bool_attribute(ID::keep
))
65 used_signals
.add(sigmap(wire
));
66 all_signals
.add(sigmap(wire
));
69 all_signals
.del(driven_signals
);
70 RTLIL::SigSpec undriven_signals
= all_signals
.export_all();
72 for (auto &c
: undriven_signals
.chunks())
74 RTLIL::SigSpec sig
= c
;
76 if (c
.wire
->name
[0] == '$')
77 sig
= used_signals
.extract(sig
);
81 Const
val(RTLIL::State::Sx
, GetSize(sig
));
82 for (int i
= 0; i
< GetSize(sig
); i
++) {
83 SigBit bit
= sigmap(sig
[i
]);
84 auto cursor
= initbits
.find(bit
);
85 if (cursor
!= initbits
.end()) {
86 revisit_initwires
.insert(cursor
->second
.first
);
87 val
[i
] = cursor
->second
.second
;
91 log_debug("Setting undriven signal in %s to constant: %s = %s\n", log_id(module
), log_signal(sig
), log_signal(val
));
92 module
->connect(sig
, val
);
96 if (!revisit_initwires
.empty())
100 for (auto wire
: revisit_initwires
) {
101 SigSpec sig
= sm2(wire
);
102 Const initval
= wire
->attributes
.at(ID(init
));
103 for (int i
= 0; i
< GetSize(initval
) && i
< GetSize(wire
); i
++) {
104 if (SigBit(initval
[i
]) == sig
[i
])
105 initval
[i
] = State::Sx
;
107 if (initval
.is_fully_undef()) {
108 log_debug("Removing init attribute from %s/%s.\n", log_id(module
), log_id(wire
));
109 wire
->attributes
.erase(ID(init
));
110 did_something
= true;
111 } else if (initval
!= wire
->attributes
.at(ID(init
))) {
112 log_debug("Updating init attribute on %s/%s: %s\n", log_id(module
), log_id(wire
), log_signal(initval
));
113 wire
->attributes
[ID(init
)] = initval
;
114 did_something
= true;
120 void replace_cell(SigMap
&assign_map
, RTLIL::Module
*module
, RTLIL::Cell
*cell
,
121 const std::string
&info
YS_ATTRIBUTE(unused
), IdString out_port
, RTLIL::SigSpec out_val
)
123 RTLIL::SigSpec Y
= cell
->getPort(out_port
);
124 out_val
.extend_u0(Y
.size(), false);
126 log_debug("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n",
127 cell
->type
.c_str(), cell
->name
.c_str(), info
.c_str(),
128 module
->name
.c_str(), log_signal(Y
), log_signal(out_val
));
130 assign_map
.add(Y
, out_val
);
131 module
->connect(Y
, out_val
);
132 module
->remove(cell
);
133 did_something
= true;
136 bool group_cell_inputs(RTLIL::Module
*module
, RTLIL::Cell
*cell
, bool commutative
, SigMap
&sigmap
)
138 IdString b_name
= cell
->hasPort(ID::B
) ? ID::B
: ID::A
;
140 bool a_signed
= cell
->parameters
.at(ID(A_SIGNED
)).as_bool();
141 bool b_signed
= cell
->parameters
.at(b_name
.str() + "_SIGNED").as_bool();
143 RTLIL::SigSpec sig_a
= sigmap(cell
->getPort(ID::A
));
144 RTLIL::SigSpec sig_b
= sigmap(cell
->getPort(b_name
));
145 RTLIL::SigSpec sig_y
= sigmap(cell
->getPort(ID::Y
));
147 sig_a
.extend_u0(sig_y
.size(), a_signed
);
148 sig_b
.extend_u0(sig_y
.size(), b_signed
);
150 std::vector
<RTLIL::SigBit
> bits_a
= sig_a
, bits_b
= sig_b
, bits_y
= sig_y
;
152 enum { GRP_DYN
, GRP_CONST_A
, GRP_CONST_B
, GRP_CONST_AB
, GRP_N
};
153 std::map
<std::pair
<RTLIL::SigBit
, RTLIL::SigBit
>, std::set
<RTLIL::SigBit
>> grouped_bits
[GRP_N
];
155 for (int i
= 0; i
< GetSize(bits_y
); i
++)
157 int group_idx
= GRP_DYN
;
158 RTLIL::SigBit bit_a
= bits_a
[i
], bit_b
= bits_b
[i
];
160 if (cell
->type
== ID($
or) && (bit_a
== RTLIL::State::S1
|| bit_b
== RTLIL::State::S1
))
161 bit_a
= bit_b
= RTLIL::State::S1
;
163 if (cell
->type
== ID($
and) && (bit_a
== RTLIL::State::S0
|| bit_b
== RTLIL::State::S0
))
164 bit_a
= bit_b
= RTLIL::State::S0
;
166 if (bit_a
.wire
== NULL
&& bit_b
.wire
== NULL
)
167 group_idx
= GRP_CONST_AB
;
168 else if (bit_a
.wire
== NULL
)
169 group_idx
= GRP_CONST_A
;
170 else if (bit_b
.wire
== NULL
&& commutative
)
171 group_idx
= GRP_CONST_A
, std::swap(bit_a
, bit_b
);
172 else if (bit_b
.wire
== NULL
)
173 group_idx
= GRP_CONST_B
;
175 grouped_bits
[group_idx
][std::pair
<RTLIL::SigBit
, RTLIL::SigBit
>(bit_a
, bit_b
)].insert(bits_y
[i
]);
178 for (int i
= 0; i
< GRP_N
; i
++)
179 if (GetSize(grouped_bits
[i
]) == GetSize(bits_y
))
182 log_debug("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
183 log_id(cell
->type
), log_id(cell
), log_id(module
));
185 for (int i
= 0; i
< GRP_N
; i
++)
187 if (grouped_bits
[i
].empty())
190 RTLIL::Wire
*new_y
= module
->addWire(NEW_ID
, GetSize(grouped_bits
[i
]));
191 RTLIL::SigSpec new_a
, new_b
;
192 RTLIL::SigSig new_conn
;
194 for (auto &it
: grouped_bits
[i
]) {
195 for (auto &bit
: it
.second
) {
196 new_conn
.first
.append_bit(bit
);
197 new_conn
.second
.append_bit(RTLIL::SigBit(new_y
, new_a
.size()));
199 new_a
.append_bit(it
.first
.first
);
200 new_b
.append_bit(it
.first
.second
);
203 if (cell
->type
.in(ID($
and), ID($
or)) && i
== GRP_CONST_A
) {
204 log_debug(" Direct Connection: %s (%s with %s)\n", log_signal(new_b
), log_id(cell
->type
), log_signal(new_a
));
205 module
->connect(new_y
, new_b
);
206 module
->connect(new_conn
);
210 RTLIL::Cell
*c
= module
->addCell(NEW_ID
, cell
->type
);
212 c
->setPort(ID::A
, new_a
);
213 c
->parameters
[ID(A_WIDTH
)] = new_a
.size();
214 c
->parameters
[ID(A_SIGNED
)] = false;
216 if (b_name
== ID::B
) {
217 c
->setPort(ID::B
, new_b
);
218 c
->parameters
[ID(B_WIDTH
)] = new_b
.size();
219 c
->parameters
[ID(B_SIGNED
)] = false;
222 c
->setPort(ID::Y
, new_y
);
223 c
->parameters
[ID(Y_WIDTH
)] = new_y
->width
;
226 module
->connect(new_conn
);
228 log_debug(" New cell `%s': A=%s", log_id(c
), log_signal(new_a
));
230 log_debug(", B=%s", log_signal(new_b
));
234 cover_list("opt.opt_expr.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell
->type
.str());
236 module
->remove(cell
);
237 did_something
= true;
241 void handle_polarity_inv(Cell
*cell
, IdString port
, IdString param
, const SigMap
&assign_map
, const dict
<RTLIL::SigSpec
, RTLIL::SigSpec
> &invert_map
)
243 SigSpec sig
= assign_map(cell
->getPort(port
));
244 if (invert_map
.count(sig
)) {
245 log_debug("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
246 log_id(port
), log_id(cell
->type
), log_id(cell
), log_id(cell
->module
),
247 log_signal(sig
), log_signal(invert_map
.at(sig
)));
248 cell
->setPort(port
, (invert_map
.at(sig
)));
249 cell
->setParam(param
, !cell
->getParam(param
).as_bool());
253 void handle_clkpol_celltype_swap(Cell
*cell
, string type1
, string type2
, IdString port
, const SigMap
&assign_map
, const dict
<RTLIL::SigSpec
, RTLIL::SigSpec
> &invert_map
)
255 log_assert(GetSize(type1
) == GetSize(type2
));
256 string cell_type
= cell
->type
.str();
258 if (GetSize(type1
) != GetSize(cell_type
))
261 for (int i
= 0; i
< GetSize(type1
); i
++) {
262 log_assert((type1
[i
] == '?') == (type2
[i
] == '?'));
263 if (type1
[i
] == '?') {
264 if (cell_type
[i
] != '0' && cell_type
[i
] != '1' && cell_type
[i
] != 'N' && cell_type
[i
] != 'P')
266 type1
[i
] = cell_type
[i
];
267 type2
[i
] = cell_type
[i
];
271 if (cell
->type
.in(type1
, type2
)) {
272 SigSpec sig
= assign_map(cell
->getPort(port
));
273 if (invert_map
.count(sig
)) {
274 log_debug("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
275 log_id(port
), log_id(cell
->type
), log_id(cell
), log_id(cell
->module
),
276 log_signal(sig
), log_signal(invert_map
.at(sig
)));
277 cell
->setPort(port
, (invert_map
.at(sig
)));
278 cell
->type
= cell
->type
== type1
? type2
: type1
;
283 bool is_one_or_minus_one(const Const
&value
, bool is_signed
, bool &is_negative
)
285 bool all_bits_one
= true;
286 bool last_bit_one
= true;
288 if (GetSize(value
.bits
) < 1)
291 if (GetSize(value
.bits
) == 1) {
292 if (value
.bits
[0] != State::S1
)
299 for (int i
= 0; i
< GetSize(value
.bits
); i
++) {
300 if (value
.bits
[i
] != State::S1
)
301 all_bits_one
= false;
302 if (value
.bits
[i
] != (i
? State::S0
: State::S1
))
303 last_bit_one
= false;
306 if (all_bits_one
&& is_signed
) {
314 int get_highest_hot_index(RTLIL::SigSpec signal
)
316 for (int i
= GetSize(signal
) - 1; i
>= 0; i
--)
318 if (signal
[i
] == RTLIL::State::S0
)
321 if (signal
[i
] == RTLIL::State::S1
)
330 // if the signal has only one bit set, return the index of that bit.
331 // otherwise return -1
332 int get_onehot_bit_index(RTLIL::SigSpec signal
)
336 for (int i
= 0; i
< GetSize(signal
); i
++)
338 if (signal
[i
] == RTLIL::State::S0
)
341 if (signal
[i
] != RTLIL::State::S1
)
353 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
)
355 if (!design
->selected(module
))
358 CellTypes ct_combinational
;
359 ct_combinational
.setup_internals();
360 ct_combinational
.setup_stdcells();
362 SigMap
assign_map(module
);
363 dict
<RTLIL::SigSpec
, RTLIL::SigSpec
> invert_map
;
365 TopoSort
<RTLIL::Cell
*, RTLIL::IdString::compare_ptr_by_name
<RTLIL::Cell
>> cells
;
366 dict
<RTLIL::Cell
*, std::set
<RTLIL::SigBit
>> cell_to_inbit
;
367 dict
<RTLIL::SigBit
, std::set
<RTLIL::Cell
*>> outbit_to_cell
;
369 for (auto cell
: module
->cells())
370 if (design
->selected(module
, cell
) && cell
->type
[0] == '$') {
371 if (cell
->type
.in(ID($_NOT_
), ID($
not), ID($logic_not
)) &&
372 cell
->getPort(ID::A
).size() == 1 && cell
->getPort(ID::Y
).size() == 1)
373 invert_map
[assign_map(cell
->getPort(ID::Y
))] = assign_map(cell
->getPort(ID::A
));
374 if (cell
->type
.in(ID($mux
), ID($_MUX_
)) &&
375 cell
->getPort(ID::A
) == SigSpec(State::S1
) && cell
->getPort(ID::B
) == SigSpec(State::S0
))
376 invert_map
[assign_map(cell
->getPort(ID::Y
))] = assign_map(cell
->getPort(ID(S
)));
377 if (ct_combinational
.cell_known(cell
->type
))
378 for (auto &conn
: cell
->connections()) {
379 RTLIL::SigSpec sig
= assign_map(conn
.second
);
381 if (ct_combinational
.cell_input(cell
->type
, conn
.first
))
382 cell_to_inbit
[cell
].insert(sig
.begin(), sig
.end());
383 if (ct_combinational
.cell_output(cell
->type
, conn
.first
))
384 for (auto &bit
: sig
)
385 outbit_to_cell
[bit
].insert(cell
);
390 for (auto &it_right
: cell_to_inbit
)
391 for (auto &it_sigbit
: it_right
.second
)
392 for (auto &it_left
: outbit_to_cell
[it_sigbit
])
393 cells
.edge(it_left
, it_right
.first
);
397 for (auto cell
: cells
.sorted
)
399 #define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0)
400 #define ACTION_DO_Y(_v_) ACTION_DO(ID::Y, RTLIL::SigSpec(RTLIL::State::S ## _v_))
404 if (cell
->type
.in(ID($dff
), ID($dffe
), ID($dffsr
), ID($adff
), ID($fsm
), ID($memrd
), ID($memwr
)))
405 handle_polarity_inv(cell
, ID(CLK
), ID(CLK_POLARITY
), assign_map
, invert_map
);
407 if (cell
->type
.in(ID($sr
), ID($dffsr
), ID($dlatchsr
))) {
408 handle_polarity_inv(cell
, ID(SET
), ID(SET_POLARITY
), assign_map
, invert_map
);
409 handle_polarity_inv(cell
, ID(CLR
), ID(CLR_POLARITY
), assign_map
, invert_map
);
412 if (cell
->type
.in(ID($dffe
), ID($dlatch
), ID($dlatchsr
)))
413 handle_polarity_inv(cell
, ID(EN
), ID(EN_POLARITY
), assign_map
, invert_map
);
415 handle_clkpol_celltype_swap(cell
, "$_SR_N?_", "$_SR_P?_", ID(S
), assign_map
, invert_map
);
416 handle_clkpol_celltype_swap(cell
, "$_SR_?N_", "$_SR_?P_", ID(R
), assign_map
, invert_map
);
418 handle_clkpol_celltype_swap(cell
, "$_DFF_N_", "$_DFF_P_", ID(C
), assign_map
, invert_map
);
420 handle_clkpol_celltype_swap(cell
, "$_DFFE_N?_", "$_DFFE_P?_", ID(C
), assign_map
, invert_map
);
421 handle_clkpol_celltype_swap(cell
, "$_DFFE_?N_", "$_DFFE_?P_", ID(E
), assign_map
, invert_map
);
423 handle_clkpol_celltype_swap(cell
, "$_DFF_N??_", "$_DFF_P??_", ID(C
), assign_map
, invert_map
);
424 handle_clkpol_celltype_swap(cell
, "$_DFF_?N?_", "$_DFF_?P?_", ID(R
), assign_map
, invert_map
);
426 handle_clkpol_celltype_swap(cell
, "$_DFFSR_N??_", "$_DFFSR_P??_", ID(C
), assign_map
, invert_map
);
427 handle_clkpol_celltype_swap(cell
, "$_DFFSR_?N?_", "$_DFFSR_?P?_", ID(S
), assign_map
, invert_map
);
428 handle_clkpol_celltype_swap(cell
, "$_DFFSR_??N_", "$_DFFSR_??P_", ID(R
), assign_map
, invert_map
);
430 handle_clkpol_celltype_swap(cell
, "$_DLATCH_N_", "$_DLATCH_P_", ID(E
), assign_map
, invert_map
);
432 handle_clkpol_celltype_swap(cell
, "$_DLATCHSR_N??_", "$_DLATCHSR_P??_", ID(E
), assign_map
, invert_map
);
433 handle_clkpol_celltype_swap(cell
, "$_DLATCHSR_?N?_", "$_DLATCHSR_?P?_", ID(S
), assign_map
, invert_map
);
434 handle_clkpol_celltype_swap(cell
, "$_DLATCHSR_??N_", "$_DLATCHSR_??P_", ID(R
), assign_map
, invert_map
);
437 bool detect_const_and
= false;
438 bool detect_const_or
= false;
440 if (cell
->type
.in(ID($reduce_and
), ID($_AND_
)))
441 detect_const_and
= true;
443 if (cell
->type
.in(ID($
and), ID($logic_and
)) && GetSize(cell
->getPort(ID::A
)) == 1 && GetSize(cell
->getPort(ID::B
)) == 1 && !cell
->getParam(ID(A_SIGNED
)).as_bool())
444 detect_const_and
= true;
446 if (cell
->type
.in(ID($reduce_or
), ID($reduce_bool
), ID($_OR_
)))
447 detect_const_or
= true;
449 if (cell
->type
.in(ID($
or), ID($logic_or
)) && GetSize(cell
->getPort(ID::A
)) == 1 && GetSize(cell
->getPort(ID::B
)) == 1 && !cell
->getParam(ID(A_SIGNED
)).as_bool())
450 detect_const_or
= true;
452 if (detect_const_and
|| detect_const_or
)
454 pool
<SigBit
> input_bits
= assign_map(cell
->getPort(ID::A
)).to_sigbit_pool();
455 bool found_zero
= false, found_one
= false, found_undef
= false, found_inv
= false, many_conconst
= false;
456 SigBit non_const_input
= State::Sm
;
458 if (cell
->hasPort(ID::B
)) {
459 vector
<SigBit
> more_bits
= assign_map(cell
->getPort(ID::B
)).to_sigbit_vector();
460 input_bits
.insert(more_bits
.begin(), more_bits
.end());
463 for (auto bit
: input_bits
) {
465 if (invert_map
.count(bit
) && input_bits
.count(invert_map
.at(bit
)))
467 if (non_const_input
!= State::Sm
)
468 many_conconst
= true;
469 non_const_input
= many_conconst
? State::Sm
: bit
;
471 if (bit
== State::S0
)
473 else if (bit
== State::S1
)
480 if (detect_const_and
&& (found_zero
|| found_inv
)) {
481 cover("opt.opt_expr.const_and");
482 replace_cell(assign_map
, module
, cell
, "const_and", ID::Y
, RTLIL::State::S0
);
486 if (detect_const_or
&& (found_one
|| found_inv
)) {
487 cover("opt.opt_expr.const_or");
488 replace_cell(assign_map
, module
, cell
, "const_or", ID::Y
, RTLIL::State::S1
);
492 if (non_const_input
!= State::Sm
&& !found_undef
) {
493 cover("opt.opt_expr.and_or_buffer");
494 replace_cell(assign_map
, module
, cell
, "and_or_buffer", ID::Y
, non_const_input
);
499 if (cell
->type
.in(ID($reduce_and
), ID($reduce_or
), ID($reduce_bool
), ID($reduce_xor
), ID($reduce_xnor
), ID($neg
)) &&
500 GetSize(cell
->getPort(ID::A
)) == 1 && GetSize(cell
->getPort(ID::Y
)) == 1)
502 if (cell
->type
== ID($reduce_xnor
)) {
503 cover("opt.opt_expr.reduce_xnor_not");
504 log_debug("Replacing %s cell `%s' in module `%s' with $not cell.\n",
505 log_id(cell
->type
), log_id(cell
->name
), log_id(module
));
506 cell
->type
= ID($
not);
507 did_something
= true;
509 cover("opt.opt_expr.unary_buffer");
510 replace_cell(assign_map
, module
, cell
, "unary_buffer", ID::Y
, cell
->getPort(ID::A
));
517 if (cell
->type
.in(ID($
not), ID($pos
), ID($
and), ID($
or), ID($
xor), ID($xnor
)))
518 if (group_cell_inputs(module
, cell
, true, assign_map
))
521 if (cell
->type
.in(ID($logic_not
), ID($logic_and
), ID($logic_or
), ID($reduce_or
), ID($reduce_and
), ID($reduce_bool
)))
523 SigBit neutral_bit
= cell
->type
== ID($reduce_and
) ? State::S1
: State::S0
;
525 RTLIL::SigSpec sig_a
= assign_map(cell
->getPort(ID::A
));
526 RTLIL::SigSpec new_sig_a
;
528 for (auto bit
: sig_a
)
529 if (bit
!= neutral_bit
) new_sig_a
.append(bit
);
531 if (GetSize(new_sig_a
) == 0)
532 new_sig_a
.append(neutral_bit
);
534 if (GetSize(new_sig_a
) < GetSize(sig_a
)) {
535 cover_list("opt.opt_expr.fine.neutral_A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_and", "$reduce_bool", cell
->type
.str());
536 log_debug("Replacing port A of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
537 cell
->type
.c_str(), cell
->name
.c_str(), module
->name
.c_str(), log_signal(sig_a
), log_signal(new_sig_a
));
538 cell
->setPort(ID::A
, new_sig_a
);
539 cell
->parameters
.at(ID(A_WIDTH
)) = GetSize(new_sig_a
);
540 did_something
= true;
544 if (cell
->type
.in(ID($logic_and
), ID($logic_or
)))
546 SigBit neutral_bit
= State::S0
;
548 RTLIL::SigSpec sig_b
= assign_map(cell
->getPort(ID::B
));
549 RTLIL::SigSpec new_sig_b
;
551 for (auto bit
: sig_b
)
552 if (bit
!= neutral_bit
) new_sig_b
.append(bit
);
554 if (GetSize(new_sig_b
) == 0)
555 new_sig_b
.append(neutral_bit
);
557 if (GetSize(new_sig_b
) < GetSize(sig_b
)) {
558 cover_list("opt.opt_expr.fine.neutral_B", "$logic_and", "$logic_or", cell
->type
.str());
559 log_debug("Replacing port B of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
560 cell
->type
.c_str(), cell
->name
.c_str(), module
->name
.c_str(), log_signal(sig_b
), log_signal(new_sig_b
));
561 cell
->setPort(ID::B
, new_sig_b
);
562 cell
->parameters
.at(ID(B_WIDTH
)) = GetSize(new_sig_b
);
563 did_something
= true;
567 if (cell
->type
== ID($reduce_and
))
569 RTLIL::SigSpec sig_a
= assign_map(cell
->getPort(ID::A
));
571 RTLIL::State new_a
= RTLIL::State::S1
;
572 for (auto &bit
: sig_a
.to_sigbit_vector())
573 if (bit
== RTLIL::State::Sx
) {
574 if (new_a
== RTLIL::State::S1
)
575 new_a
= RTLIL::State::Sx
;
576 } else if (bit
== RTLIL::State::S0
) {
577 new_a
= RTLIL::State::S0
;
579 } else if (bit
.wire
!= NULL
) {
580 new_a
= RTLIL::State::Sm
;
583 if (new_a
!= RTLIL::State::Sm
&& RTLIL::SigSpec(new_a
) != sig_a
) {
584 cover("opt.opt_expr.fine.$reduce_and");
585 log_debug("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
586 cell
->type
.c_str(), cell
->name
.c_str(), module
->name
.c_str(), log_signal(sig_a
), log_signal(new_a
));
587 cell
->setPort(ID::A
, sig_a
= new_a
);
588 cell
->parameters
.at(ID(A_WIDTH
)) = 1;
589 did_something
= true;
593 if (cell
->type
.in(ID($logic_not
), ID($logic_and
), ID($logic_or
), ID($reduce_or
), ID($reduce_bool
)))
595 RTLIL::SigSpec sig_a
= assign_map(cell
->getPort(ID::A
));
597 RTLIL::State new_a
= RTLIL::State::S0
;
598 for (auto &bit
: sig_a
.to_sigbit_vector())
599 if (bit
== RTLIL::State::Sx
) {
600 if (new_a
== RTLIL::State::S0
)
601 new_a
= RTLIL::State::Sx
;
602 } else if (bit
== RTLIL::State::S1
) {
603 new_a
= RTLIL::State::S1
;
605 } else if (bit
.wire
!= NULL
) {
606 new_a
= RTLIL::State::Sm
;
609 if (new_a
!= RTLIL::State::Sm
&& RTLIL::SigSpec(new_a
) != sig_a
) {
610 cover_list("opt.opt_expr.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell
->type
.str());
611 log_debug("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
612 cell
->type
.c_str(), cell
->name
.c_str(), module
->name
.c_str(), log_signal(sig_a
), log_signal(new_a
));
613 cell
->setPort(ID::A
, sig_a
= new_a
);
614 cell
->parameters
.at(ID(A_WIDTH
)) = 1;
615 did_something
= true;
619 if (cell
->type
.in(ID($logic_and
), ID($logic_or
)))
621 RTLIL::SigSpec sig_b
= assign_map(cell
->getPort(ID::B
));
623 RTLIL::State new_b
= RTLIL::State::S0
;
624 for (auto &bit
: sig_b
.to_sigbit_vector())
625 if (bit
== RTLIL::State::Sx
) {
626 if (new_b
== RTLIL::State::S0
)
627 new_b
= RTLIL::State::Sx
;
628 } else if (bit
== RTLIL::State::S1
) {
629 new_b
= RTLIL::State::S1
;
631 } else if (bit
.wire
!= NULL
) {
632 new_b
= RTLIL::State::Sm
;
635 if (new_b
!= RTLIL::State::Sm
&& RTLIL::SigSpec(new_b
) != sig_b
) {
636 cover_list("opt.opt_expr.fine.B", "$logic_and", "$logic_or", cell
->type
.str());
637 log_debug("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
638 cell
->type
.c_str(), cell
->name
.c_str(), module
->name
.c_str(), log_signal(sig_b
), log_signal(new_b
));
639 cell
->setPort(ID::B
, sig_b
= new_b
);
640 cell
->parameters
.at(ID(B_WIDTH
)) = 1;
641 did_something
= true;
645 if (cell
->type
.in(ID($add
), ID($sub
)))
647 RTLIL::SigSpec sig_a
= assign_map(cell
->getPort(ID::A
));
648 RTLIL::SigSpec sig_b
= assign_map(cell
->getPort(ID::B
));
649 RTLIL::SigSpec sig_y
= cell
->getPort(ID::Y
);
650 bool sub
= cell
->type
== ID($sub
);
653 for (i
= 0; i
< GetSize(sig_y
); i
++) {
654 if (sig_b
.at(i
, State::Sx
) == State::S0
&& sig_a
.at(i
, State::Sx
) != State::Sx
)
655 module
->connect(sig_y
[i
], sig_a
[i
]);
656 else if (!sub
&& sig_a
.at(i
, State::Sx
) == State::S0
&& sig_b
.at(i
, State::Sx
) != State::Sx
)
657 module
->connect(sig_y
[i
], sig_b
[i
]);
662 cover_list("opt.opt_expr.fine", "$add", "$sub", cell
->type
.str());
663 cell
->setPort(ID::A
, sig_a
.extract_end(i
));
664 cell
->setPort(ID::B
, sig_b
.extract_end(i
));
665 cell
->setPort(ID::Y
, sig_y
.extract_end(i
));
666 cell
->fixup_parameters();
667 did_something
= true;
671 if (cell
->type
== "$alu")
673 RTLIL::SigSpec sig_a
= assign_map(cell
->getPort(ID::A
));
674 RTLIL::SigSpec sig_b
= assign_map(cell
->getPort(ID::B
));
675 RTLIL::SigBit sig_ci
= assign_map(cell
->getPort(ID(CI
)));
676 RTLIL::SigBit sig_bi
= assign_map(cell
->getPort(ID(BI
)));
677 RTLIL::SigSpec sig_x
= cell
->getPort(ID(X
));
678 RTLIL::SigSpec sig_y
= cell
->getPort(ID::Y
);
679 RTLIL::SigSpec sig_co
= cell
->getPort(ID(CO
));
681 if (sig_ci
.wire
|| sig_bi
.wire
)
684 bool sub
= (sig_ci
== State::S1
&& sig_bi
== State::S1
);
686 // If not a subtraction, yet there is a carry or B is inverted
687 // then no optimisation is possible as carry will not be constant
688 if (!sub
&& (sig_ci
!= State::S0
|| sig_bi
!= State::S0
))
692 for (i
= 0; i
< GetSize(sig_y
); i
++) {
693 if (sig_b
.at(i
, State::Sx
) == State::S0
&& sig_a
.at(i
, State::Sx
) != State::Sx
) {
694 module
->connect(sig_x
[i
], sub
? module
->Not(NEW_ID
, sig_a
[i
]).as_bit() : sig_a
[i
]);
695 module
->connect(sig_y
[i
], sig_a
[i
]);
696 module
->connect(sig_co
[i
], sub
? State::S1
: State::S0
);
698 else if (!sub
&& sig_a
.at(i
, State::Sx
) == State::S0
&& sig_b
.at(i
, State::Sx
) != State::Sx
) {
699 module
->connect(sig_x
[i
], sig_b
[i
]);
700 module
->connect(sig_y
[i
], sig_b
[i
]);
701 module
->connect(sig_co
[i
], State::S0
);
707 cover("opt.opt_expr.fine.$alu");
708 cell
->setPort(ID::A
, sig_a
.extract_end(i
));
709 cell
->setPort(ID::B
, sig_b
.extract_end(i
));
710 cell
->setPort(ID(X
), sig_x
.extract_end(i
));
711 cell
->setPort(ID::Y
, sig_y
.extract_end(i
));
712 cell
->setPort(ID(CO
), sig_co
.extract_end(i
));
713 cell
->fixup_parameters();
714 did_something
= true;
719 if (cell
->type
.in(ID($reduce_xor
), ID($reduce_xnor
), ID($shift
), ID($shiftx
), ID($shl
), ID($shr
), ID($sshl
), ID($sshr
),
720 ID($lt
), ID($le
), ID($ge
), ID($gt
), ID($neg
), ID($add
), ID($sub
), ID($mul
), ID($div
), ID($mod
), ID($pow
)))
722 RTLIL::SigSpec sig_a
= assign_map(cell
->getPort(ID::A
));
723 RTLIL::SigSpec sig_b
= cell
->hasPort(ID::B
) ? assign_map(cell
->getPort(ID::B
)) : RTLIL::SigSpec();
725 if (cell
->type
.in(ID($shl
), ID($shr
), ID($sshl
), ID($sshr
), ID($shift
), ID($shiftx
)))
726 sig_a
= RTLIL::SigSpec();
728 for (auto &bit
: sig_a
.to_sigbit_vector())
729 if (bit
== RTLIL::State::Sx
)
730 goto found_the_x_bit
;
732 for (auto &bit
: sig_b
.to_sigbit_vector())
733 if (bit
== RTLIL::State::Sx
)
734 goto found_the_x_bit
;
738 cover_list("opt.opt_expr.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
739 "$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$pow", cell
->type
.str());
740 if (cell
->type
.in(ID($reduce_xor
), ID($reduce_xnor
), ID($lt
), ID($le
), ID($ge
), ID($gt
)))
741 replace_cell(assign_map
, module
, cell
, "x-bit in input", ID::Y
, RTLIL::State::Sx
);
743 replace_cell(assign_map
, module
, cell
, "x-bit in input", ID::Y
, RTLIL::SigSpec(RTLIL::State::Sx
, cell
->getPort(ID::Y
).size()));
748 if (cell
->type
.in(ID($_NOT_
), ID($
not), ID($logic_not
)) && cell
->getPort(ID::Y
).size() == 1 &&
749 invert_map
.count(assign_map(cell
->getPort(ID::A
))) != 0) {
750 cover_list("opt.opt_expr.invert.double", "$_NOT_", "$not", "$logic_not", cell
->type
.str());
751 replace_cell(assign_map
, module
, cell
, "double_invert", ID::Y
, invert_map
.at(assign_map(cell
->getPort(ID::A
))));
755 if (cell
->type
.in(ID($_MUX_
), ID($mux
)) && invert_map
.count(assign_map(cell
->getPort(ID(S
)))) != 0) {
756 cover_list("opt.opt_expr.invert.muxsel", "$_MUX_", "$mux", cell
->type
.str());
757 log_debug("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell
->type
), log_id(cell
), log_id(module
));
758 RTLIL::SigSpec tmp
= cell
->getPort(ID::A
);
759 cell
->setPort(ID::A
, cell
->getPort(ID::B
));
760 cell
->setPort(ID::B
, tmp
);
761 cell
->setPort(ID(S
), invert_map
.at(assign_map(cell
->getPort(ID(S
)))));
762 did_something
= true;
766 if (cell
->type
== ID($_NOT_
)) {
767 RTLIL::SigSpec input
= cell
->getPort(ID::A
);
768 assign_map
.apply(input
);
769 if (input
.match("1")) ACTION_DO_Y(0);
770 if (input
.match("0")) ACTION_DO_Y(1);
771 if (input
.match("*")) ACTION_DO_Y(x
);
774 if (cell
->type
== ID($_AND_
)) {
775 RTLIL::SigSpec input
;
776 input
.append(cell
->getPort(ID::B
));
777 input
.append(cell
->getPort(ID::A
));
778 assign_map
.apply(input
);
779 if (input
.match(" 0")) ACTION_DO_Y(0);
780 if (input
.match("0 ")) ACTION_DO_Y(0);
781 if (input
.match("11")) ACTION_DO_Y(1);
782 if (input
.match("**")) ACTION_DO_Y(x
);
783 if (input
.match("1*")) ACTION_DO_Y(x
);
784 if (input
.match("*1")) ACTION_DO_Y(x
);
786 if (input
.match(" *")) ACTION_DO_Y(0);
787 if (input
.match("* ")) ACTION_DO_Y(0);
789 if (input
.match(" 1")) ACTION_DO(ID::Y
, input
.extract(1, 1));
790 if (input
.match("1 ")) ACTION_DO(ID::Y
, input
.extract(0, 1));
793 if (cell
->type
== ID($_OR_
)) {
794 RTLIL::SigSpec input
;
795 input
.append(cell
->getPort(ID::B
));
796 input
.append(cell
->getPort(ID::A
));
797 assign_map
.apply(input
);
798 if (input
.match(" 1")) ACTION_DO_Y(1);
799 if (input
.match("1 ")) ACTION_DO_Y(1);
800 if (input
.match("00")) ACTION_DO_Y(0);
801 if (input
.match("**")) ACTION_DO_Y(x
);
802 if (input
.match("0*")) ACTION_DO_Y(x
);
803 if (input
.match("*0")) ACTION_DO_Y(x
);
805 if (input
.match(" *")) ACTION_DO_Y(1);
806 if (input
.match("* ")) ACTION_DO_Y(1);
808 if (input
.match(" 0")) ACTION_DO(ID::Y
, input
.extract(1, 1));
809 if (input
.match("0 ")) ACTION_DO(ID::Y
, input
.extract(0, 1));
812 if (cell
->type
== ID($_XOR_
)) {
813 RTLIL::SigSpec input
;
814 input
.append(cell
->getPort(ID::B
));
815 input
.append(cell
->getPort(ID::A
));
816 assign_map
.apply(input
);
817 if (input
.match("00")) ACTION_DO_Y(0);
818 if (input
.match("01")) ACTION_DO_Y(1);
819 if (input
.match("10")) ACTION_DO_Y(1);
820 if (input
.match("11")) ACTION_DO_Y(0);
821 if (input
.match(" *")) ACTION_DO_Y(x
);
822 if (input
.match("* ")) ACTION_DO_Y(x
);
823 if (input
.match(" 0")) ACTION_DO(ID::Y
, input
.extract(1, 1));
824 if (input
.match("0 ")) ACTION_DO(ID::Y
, input
.extract(0, 1));
827 if (cell
->type
== ID($_MUX_
)) {
828 RTLIL::SigSpec input
;
829 input
.append(cell
->getPort(ID(S
)));
830 input
.append(cell
->getPort(ID::B
));
831 input
.append(cell
->getPort(ID::A
));
832 assign_map
.apply(input
);
833 if (input
.extract(2, 1) == input
.extract(1, 1))
834 ACTION_DO(ID::Y
, input
.extract(2, 1));
835 if (input
.match(" 0")) ACTION_DO(ID::Y
, input
.extract(2, 1));
836 if (input
.match(" 1")) ACTION_DO(ID::Y
, input
.extract(1, 1));
837 if (input
.match("01 ")) ACTION_DO(ID::Y
, input
.extract(0, 1));
838 if (input
.match("10 ")) {
839 cover("opt.opt_expr.mux_to_inv");
840 cell
->type
= ID($_NOT_
);
841 cell
->setPort(ID::A
, input
.extract(0, 1));
842 cell
->unsetPort(ID::B
);
843 cell
->unsetPort(ID(S
));
846 if (input
.match("11 ")) ACTION_DO_Y(1);
847 if (input
.match("00 ")) ACTION_DO_Y(0);
848 if (input
.match("** ")) ACTION_DO_Y(x
);
849 if (input
.match("01*")) ACTION_DO_Y(x
);
850 if (input
.match("10*")) ACTION_DO_Y(x
);
852 if (input
.match("* ")) ACTION_DO(ID::Y
, input
.extract(1, 1));
853 if (input
.match(" * ")) ACTION_DO(ID::Y
, input
.extract(2, 1));
854 if (input
.match(" *")) ACTION_DO(ID::Y
, input
.extract(2, 1));
858 if (cell
->type
.in(ID($_TBUF_
), ID($tribuf
))) {
859 RTLIL::SigSpec input
= cell
->getPort(cell
->type
== ID($_TBUF_
) ? ID(E
) : ID(EN
));
860 RTLIL::SigSpec a
= cell
->getPort(ID::A
);
861 assign_map
.apply(input
);
863 if (input
== State::S1
)
864 ACTION_DO(ID::Y
, cell
->getPort(ID::A
));
865 if (input
== State::S0
&& !a
.is_fully_undef()) {
866 cover("opt.opt_expr.action_" S__LINE__
);
867 log_debug("Replacing data input of %s cell `%s' in module `%s' with constant undef.\n",
868 cell
->type
.c_str(), cell
->name
.c_str(), module
->name
.c_str());
869 cell
->setPort(ID::A
, SigSpec(State::Sx
, GetSize(a
)));
870 did_something
= true;
875 if (cell
->type
.in(ID($eq
), ID($ne
), ID($eqx
), ID($nex
)))
877 RTLIL::SigSpec a
= cell
->getPort(ID::A
);
878 RTLIL::SigSpec b
= cell
->getPort(ID::B
);
880 if (cell
->parameters
[ID(A_WIDTH
)].as_int() != cell
->parameters
[ID(B_WIDTH
)].as_int()) {
881 int width
= max(cell
->parameters
[ID(A_WIDTH
)].as_int(), cell
->parameters
[ID(B_WIDTH
)].as_int());
882 a
.extend_u0(width
, cell
->parameters
[ID(A_SIGNED
)].as_bool() && cell
->parameters
[ID(B_SIGNED
)].as_bool());
883 b
.extend_u0(width
, cell
->parameters
[ID(A_SIGNED
)].as_bool() && cell
->parameters
[ID(B_SIGNED
)].as_bool());
886 RTLIL::SigSpec new_a
, new_b
;
888 log_assert(GetSize(a
) == GetSize(b
));
889 for (int i
= 0; i
< GetSize(a
); i
++) {
890 if (a
[i
].wire
== NULL
&& b
[i
].wire
== NULL
&& a
[i
] != b
[i
] && a
[i
].data
<= RTLIL::State::S1
&& b
[i
].data
<= RTLIL::State::S1
) {
891 cover_list("opt.opt_expr.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell
->type
.str());
892 RTLIL::SigSpec new_y
= RTLIL::SigSpec(cell
->type
.in(ID($eq
), ID($eqx
)) ? RTLIL::State::S0
: RTLIL::State::S1
);
893 new_y
.extend_u0(cell
->parameters
[ID(Y_WIDTH
)].as_int(), false);
894 replace_cell(assign_map
, module
, cell
, "isneq", ID::Y
, new_y
);
903 if (new_a
.size() == 0) {
904 cover_list("opt.opt_expr.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell
->type
.str());
905 RTLIL::SigSpec new_y
= RTLIL::SigSpec(cell
->type
.in(ID($eq
), ID($eqx
)) ? RTLIL::State::S1
: RTLIL::State::S0
);
906 new_y
.extend_u0(cell
->parameters
[ID(Y_WIDTH
)].as_int(), false);
907 replace_cell(assign_map
, module
, cell
, "empty", ID::Y
, new_y
);
911 if (new_a
.size() < a
.size() || new_b
.size() < b
.size()) {
912 cover_list("opt.opt_expr.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell
->type
.str());
913 cell
->setPort(ID::A
, new_a
);
914 cell
->setPort(ID::B
, new_b
);
915 cell
->parameters
[ID(A_WIDTH
)] = new_a
.size();
916 cell
->parameters
[ID(B_WIDTH
)] = new_b
.size();
920 if (cell
->type
.in(ID($eq
), ID($ne
)) && cell
->parameters
[ID(Y_WIDTH
)].as_int() == 1 &&
921 cell
->parameters
[ID(A_WIDTH
)].as_int() == 1 && cell
->parameters
[ID(B_WIDTH
)].as_int() == 1)
923 RTLIL::SigSpec a
= assign_map(cell
->getPort(ID::A
));
924 RTLIL::SigSpec b
= assign_map(cell
->getPort(ID::B
));
926 if (a
.is_fully_const() && !b
.is_fully_const()) {
927 cover_list("opt.opt_expr.eqneq.swapconst", "$eq", "$ne", cell
->type
.str());
928 cell
->setPort(ID::A
, b
);
929 cell
->setPort(ID::B
, a
);
933 if (b
.is_fully_const()) {
934 if (b
.as_bool() == (cell
->type
== ID($eq
))) {
935 RTLIL::SigSpec input
= b
;
936 ACTION_DO(ID::Y
, cell
->getPort(ID::A
));
938 cover_list("opt.opt_expr.eqneq.isnot", "$eq", "$ne", cell
->type
.str());
939 log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell
->type
), log_id(cell
), log_id(module
));
940 cell
->type
= ID($
not);
941 cell
->parameters
.erase(ID(B_WIDTH
));
942 cell
->parameters
.erase(ID(B_SIGNED
));
943 cell
->unsetPort(ID::B
);
944 did_something
= true;
950 if (cell
->type
.in(ID($eq
), ID($ne
)) &&
951 (assign_map(cell
->getPort(ID::A
)).is_fully_zero() || assign_map(cell
->getPort(ID::B
)).is_fully_zero()))
953 cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell
->type
.str());
954 log_debug("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell
->type
), log_id(cell
),
955 log_id(module
), "$eq" ? "$logic_not" : "$reduce_bool");
956 cell
->type
= cell
->type
== ID($eq
) ? ID($logic_not
) : ID($reduce_bool
);
957 if (assign_map(cell
->getPort(ID::A
)).is_fully_zero()) {
958 cell
->setPort(ID::A
, cell
->getPort(ID::B
));
959 cell
->setParam(ID(A_SIGNED
), cell
->getParam(ID(B_SIGNED
)));
960 cell
->setParam(ID(A_WIDTH
), cell
->getParam(ID(B_WIDTH
)));
962 cell
->unsetPort(ID::B
);
963 cell
->unsetParam(ID(B_SIGNED
));
964 cell
->unsetParam(ID(B_WIDTH
));
965 did_something
= true;
969 if (cell
->type
.in(ID($shl
), ID($shr
), ID($sshl
), ID($sshr
), ID($shift
), ID($shiftx
)) && assign_map(cell
->getPort(ID::B
)).is_fully_const())
971 bool sign_ext
= cell
->type
== ID($sshr
) && cell
->getParam(ID(A_SIGNED
)).as_bool();
972 int shift_bits
= assign_map(cell
->getPort(ID::B
)).as_int(cell
->type
.in(ID($shift
), ID($shiftx
)) && cell
->getParam(ID(B_SIGNED
)).as_bool());
974 if (cell
->type
.in(ID($shl
), ID($sshl
)))
977 RTLIL::SigSpec sig_a
= assign_map(cell
->getPort(ID::A
));
978 RTLIL::SigSpec
sig_y(cell
->type
== ID($shiftx
) ? RTLIL::State::Sx
: RTLIL::State::S0
, cell
->getParam(ID(Y_WIDTH
)).as_int());
980 if (GetSize(sig_a
) < GetSize(sig_y
))
981 sig_a
.extend_u0(GetSize(sig_y
), cell
->getParam(ID(A_SIGNED
)).as_bool());
983 for (int i
= 0; i
< GetSize(sig_y
); i
++) {
984 int idx
= i
+ shift_bits
;
985 if (0 <= idx
&& idx
< GetSize(sig_a
))
986 sig_y
[i
] = sig_a
[idx
];
987 else if (GetSize(sig_a
) <= idx
&& sign_ext
)
988 sig_y
[i
] = sig_a
[GetSize(sig_a
)-1];
991 cover_list("opt.opt_expr.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell
->type
.str());
993 log_debug("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
994 log_id(cell
->type
), log_id(cell
), log_signal(assign_map(cell
->getPort(ID::B
))), shift_bits
, log_id(module
), log_signal(sig_y
));
996 module
->connect(cell
->getPort(ID::Y
), sig_y
);
997 module
->remove(cell
);
999 did_something
= true;
1005 bool identity_wrt_a
= false;
1006 bool identity_wrt_b
= false;
1007 bool arith_inverse
= false;
1009 if (cell
->type
.in(ID($add
), ID($sub
), ID($
or), ID($
xor)))
1011 RTLIL::SigSpec a
= assign_map(cell
->getPort(ID::A
));
1012 RTLIL::SigSpec b
= assign_map(cell
->getPort(ID::B
));
1014 if (cell
->type
!= ID($sub
) && a
.is_fully_const() && a
.as_bool() == false)
1015 identity_wrt_b
= true;
1017 if (b
.is_fully_const() && b
.as_bool() == false)
1018 identity_wrt_a
= true;
1021 if (cell
->type
.in(ID($shl
), ID($shr
), ID($sshl
), ID($sshr
), ID($shift
), ID($shiftx
)))
1023 RTLIL::SigSpec b
= assign_map(cell
->getPort(ID::B
));
1025 if (b
.is_fully_const() && b
.as_bool() == false)
1026 identity_wrt_a
= true;
1029 if (cell
->type
== ID($mul
))
1031 RTLIL::SigSpec a
= assign_map(cell
->getPort(ID::A
));
1032 RTLIL::SigSpec b
= assign_map(cell
->getPort(ID::B
));
1034 if (a
.is_fully_const() && is_one_or_minus_one(a
.as_const(), cell
->getParam(ID(A_SIGNED
)).as_bool(), arith_inverse
))
1035 identity_wrt_b
= true;
1037 if (b
.is_fully_const() && is_one_or_minus_one(b
.as_const(), cell
->getParam(ID(B_SIGNED
)).as_bool(), arith_inverse
))
1038 identity_wrt_a
= true;
1041 if (cell
->type
== ID($div
))
1043 RTLIL::SigSpec b
= assign_map(cell
->getPort(ID::B
));
1045 if (b
.is_fully_const() && b
.size() <= 32 && b
.as_int() == 1)
1046 identity_wrt_a
= true;
1049 if (identity_wrt_a
|| identity_wrt_b
)
1052 cover_list("opt.opt_expr.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell
->type
.str());
1054 cover_list("opt.opt_expr.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell
->type
.str());
1056 log_debug("Replacing %s cell `%s' in module `%s' with identity for port %c.\n",
1057 cell
->type
.c_str(), cell
->name
.c_str(), module
->name
.c_str(), identity_wrt_a
? 'A' : 'B');
1059 if (!identity_wrt_a
) {
1060 cell
->setPort(ID::A
, cell
->getPort(ID::B
));
1061 cell
->parameters
.at(ID(A_WIDTH
)) = cell
->parameters
.at(ID(B_WIDTH
));
1062 cell
->parameters
.at(ID(A_SIGNED
)) = cell
->parameters
.at(ID(B_SIGNED
));
1065 cell
->type
= arith_inverse
? ID($neg
) : ID($pos
);
1066 cell
->unsetPort(ID::B
);
1067 cell
->parameters
.erase(ID(B_WIDTH
));
1068 cell
->parameters
.erase(ID(B_SIGNED
));
1071 did_something
= true;
1076 if (mux_bool
&& cell
->type
.in(ID($mux
), ID($_MUX_
)) &&
1077 cell
->getPort(ID::A
) == State::S0
&& cell
->getPort(ID::B
) == State::S1
) {
1078 cover_list("opt.opt_expr.mux_bool", "$mux", "$_MUX_", cell
->type
.str());
1079 replace_cell(assign_map
, module
, cell
, "mux_bool", ID::Y
, cell
->getPort(ID(S
)));
1083 if (mux_bool
&& cell
->type
.in(ID($mux
), ID($_MUX_
)) &&
1084 cell
->getPort(ID::A
) == State::S1
&& cell
->getPort(ID::B
) == State::S0
) {
1085 cover_list("opt.opt_expr.mux_invert", "$mux", "$_MUX_", cell
->type
.str());
1086 log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell
->type
), log_id(cell
), log_id(module
));
1087 cell
->setPort(ID::A
, cell
->getPort(ID(S
)));
1088 cell
->unsetPort(ID::B
);
1089 cell
->unsetPort(ID(S
));
1090 if (cell
->type
== ID($mux
)) {
1091 Const width
= cell
->parameters
[ID(WIDTH
)];
1092 cell
->parameters
[ID(A_WIDTH
)] = width
;
1093 cell
->parameters
[ID(Y_WIDTH
)] = width
;
1094 cell
->parameters
[ID(A_SIGNED
)] = 0;
1095 cell
->parameters
.erase(ID(WIDTH
));
1096 cell
->type
= ID($
not);
1098 cell
->type
= ID($_NOT_
);
1099 did_something
= true;
1103 if (consume_x
&& mux_bool
&& cell
->type
.in(ID($mux
), ID($_MUX_
)) && cell
->getPort(ID::A
) == State::S0
) {
1104 cover_list("opt.opt_expr.mux_and", "$mux", "$_MUX_", cell
->type
.str());
1105 log_debug("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell
->type
), log_id(cell
), log_id(module
));
1106 cell
->setPort(ID::A
, cell
->getPort(ID(S
)));
1107 cell
->unsetPort(ID(S
));
1108 if (cell
->type
== ID($mux
)) {
1109 Const width
= cell
->parameters
[ID(WIDTH
)];
1110 cell
->parameters
[ID(A_WIDTH
)] = width
;
1111 cell
->parameters
[ID(B_WIDTH
)] = width
;
1112 cell
->parameters
[ID(Y_WIDTH
)] = width
;
1113 cell
->parameters
[ID(A_SIGNED
)] = 0;
1114 cell
->parameters
[ID(B_SIGNED
)] = 0;
1115 cell
->parameters
.erase(ID(WIDTH
));
1116 cell
->type
= ID($
and);
1118 cell
->type
= ID($_AND_
);
1119 did_something
= true;
1123 if (consume_x
&& mux_bool
&& cell
->type
.in(ID($mux
), ID($_MUX_
)) && cell
->getPort(ID::B
) == State::S1
) {
1124 cover_list("opt.opt_expr.mux_or", "$mux", "$_MUX_", cell
->type
.str());
1125 log_debug("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell
->type
), log_id(cell
), log_id(module
));
1126 cell
->setPort(ID::B
, cell
->getPort(ID(S
)));
1127 cell
->unsetPort(ID(S
));
1128 if (cell
->type
== ID($mux
)) {
1129 Const width
= cell
->parameters
[ID(WIDTH
)];
1130 cell
->parameters
[ID(A_WIDTH
)] = width
;
1131 cell
->parameters
[ID(B_WIDTH
)] = width
;
1132 cell
->parameters
[ID(Y_WIDTH
)] = width
;
1133 cell
->parameters
[ID(A_SIGNED
)] = 0;
1134 cell
->parameters
[ID(B_SIGNED
)] = 0;
1135 cell
->parameters
.erase(ID(WIDTH
));
1136 cell
->type
= ID($
or);
1138 cell
->type
= ID($_OR_
);
1139 did_something
= true;
1143 if (mux_undef
&& cell
->type
.in(ID($mux
), ID($pmux
))) {
1144 RTLIL::SigSpec new_a
, new_b
, new_s
;
1145 int width
= cell
->getPort(ID::A
).size();
1146 if ((cell
->getPort(ID::A
).is_fully_undef() && cell
->getPort(ID::B
).is_fully_undef()) ||
1147 cell
->getPort(ID(S
)).is_fully_undef()) {
1148 cover_list("opt.opt_expr.mux_undef", "$mux", "$pmux", cell
->type
.str());
1149 replace_cell(assign_map
, module
, cell
, "mux_undef", ID::Y
, cell
->getPort(ID::A
));
1152 for (int i
= 0; i
< cell
->getPort(ID(S
)).size(); i
++) {
1153 RTLIL::SigSpec old_b
= cell
->getPort(ID::B
).extract(i
*width
, width
);
1154 RTLIL::SigSpec old_s
= cell
->getPort(ID(S
)).extract(i
, 1);
1155 if (old_b
.is_fully_undef() || old_s
.is_fully_undef())
1157 new_b
.append(old_b
);
1158 new_s
.append(old_s
);
1160 new_a
= cell
->getPort(ID::A
);
1161 if (new_a
.is_fully_undef() && new_s
.size() > 0) {
1162 new_a
= new_b
.extract((new_s
.size()-1)*width
, width
);
1163 new_b
= new_b
.extract(0, (new_s
.size()-1)*width
);
1164 new_s
= new_s
.extract(0, new_s
.size()-1);
1166 if (new_s
.size() == 0) {
1167 cover_list("opt.opt_expr.mux_empty", "$mux", "$pmux", cell
->type
.str());
1168 replace_cell(assign_map
, module
, cell
, "mux_empty", ID::Y
, new_a
);
1171 if (new_a
== RTLIL::SigSpec(RTLIL::State::S0
) && new_b
== RTLIL::SigSpec(RTLIL::State::S1
)) {
1172 cover_list("opt.opt_expr.mux_sel01", "$mux", "$pmux", cell
->type
.str());
1173 replace_cell(assign_map
, module
, cell
, "mux_sel01", ID::Y
, new_s
);
1176 if (cell
->getPort(ID(S
)).size() != new_s
.size()) {
1177 cover_list("opt.opt_expr.mux_reduce", "$mux", "$pmux", cell
->type
.str());
1178 log_debug("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
1179 GetSize(cell
->getPort(ID(S
))) - GetSize(new_s
), log_id(cell
->type
), log_id(cell
), log_id(module
));
1180 cell
->setPort(ID::A
, new_a
);
1181 cell
->setPort(ID::B
, new_b
);
1182 cell
->setPort(ID(S
), new_s
);
1183 if (new_s
.size() > 1) {
1184 cell
->type
= ID($pmux
);
1185 cell
->parameters
[ID(S_WIDTH
)] = new_s
.size();
1187 cell
->type
= ID($mux
);
1188 cell
->parameters
.erase(ID(S_WIDTH
));
1190 did_something
= true;
1194 #define FOLD_1ARG_CELL(_t) \
1195 if (cell->type == "$" #_t) { \
1196 RTLIL::SigSpec a = cell->getPort(ID::A); \
1197 assign_map.apply(a); \
1198 if (a.is_fully_const()) { \
1199 RTLIL::Const dummy_arg(RTLIL::State::S0, 1); \
1200 RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), dummy_arg, \
1201 cell->parameters[ID(A_SIGNED)].as_bool(), false, \
1202 cell->parameters[ID(Y_WIDTH)].as_int())); \
1203 cover("opt.opt_expr.const.$" #_t); \
1204 replace_cell(assign_map, module, cell, stringf("%s", log_signal(a)), ID::Y, y); \
1208 #define FOLD_2ARG_CELL(_t) \
1209 if (cell->type == "$" #_t) { \
1210 RTLIL::SigSpec a = cell->getPort(ID::A); \
1211 RTLIL::SigSpec b = cell->getPort(ID::B); \
1212 assign_map.apply(a), assign_map.apply(b); \
1213 if (a.is_fully_const() && b.is_fully_const()) { \
1214 RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), b.as_const(), \
1215 cell->parameters[ID(A_SIGNED)].as_bool(), \
1216 cell->parameters[ID(B_SIGNED)].as_bool(), \
1217 cell->parameters[ID(Y_WIDTH)].as_int())); \
1218 cover("opt.opt_expr.const.$" #_t); \
1219 replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), ID::Y, y); \
1228 FOLD_2ARG_CELL(xnor
)
1230 FOLD_1ARG_CELL(reduce_and
)
1231 FOLD_1ARG_CELL(reduce_or
)
1232 FOLD_1ARG_CELL(reduce_xor
)
1233 FOLD_1ARG_CELL(reduce_xnor
)
1234 FOLD_1ARG_CELL(reduce_bool
)
1236 FOLD_1ARG_CELL(logic_not
)
1237 FOLD_2ARG_CELL(logic_and
)
1238 FOLD_2ARG_CELL(logic_or
)
1242 FOLD_2ARG_CELL(sshl
)
1243 FOLD_2ARG_CELL(sshr
)
1244 FOLD_2ARG_CELL(shift
)
1245 FOLD_2ARG_CELL(shiftx
)
1264 // be very conservative with optimizing $mux cells as we do not want to break mux trees
1265 if (cell
->type
== ID($mux
)) {
1266 RTLIL::SigSpec input
= assign_map(cell
->getPort(ID(S
)));
1267 RTLIL::SigSpec inA
= assign_map(cell
->getPort(ID::A
));
1268 RTLIL::SigSpec inB
= assign_map(cell
->getPort(ID::B
));
1269 if (input
.is_fully_const())
1270 ACTION_DO(ID::Y
, input
.as_bool() ? cell
->getPort(ID::B
) : cell
->getPort(ID::A
));
1271 else if (inA
== inB
)
1272 ACTION_DO(ID::Y
, cell
->getPort(ID::A
));
1275 if (!keepdc
&& cell
->type
== ID($mul
))
1277 bool a_signed
= cell
->parameters
[ID(A_SIGNED
)].as_bool();
1278 bool b_signed
= cell
->parameters
[ID(B_SIGNED
)].as_bool();
1279 bool swapped_ab
= false;
1281 RTLIL::SigSpec sig_a
= assign_map(cell
->getPort(ID::A
));
1282 RTLIL::SigSpec sig_b
= assign_map(cell
->getPort(ID::B
));
1283 RTLIL::SigSpec sig_y
= assign_map(cell
->getPort(ID::Y
));
1285 if (sig_b
.is_fully_const() && sig_b
.size() <= 32)
1286 std::swap(sig_a
, sig_b
), std::swap(a_signed
, b_signed
), swapped_ab
= true;
1288 if (sig_a
.is_fully_def() && sig_a
.size() <= 32)
1290 int a_val
= sig_a
.as_int();
1294 cover("opt.opt_expr.mul_shift.zero");
1296 log_debug("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
1297 cell
->name
.c_str(), module
->name
.c_str());
1299 module
->connect(RTLIL::SigSig(sig_y
, RTLIL::SigSpec(0, sig_y
.size())));
1300 module
->remove(cell
);
1302 did_something
= true;
1306 for (int i
= 1; i
< (a_signed
? sig_a
.size()-1 : sig_a
.size()); i
++)
1307 if (a_val
== (1 << i
))
1310 cover("opt.opt_expr.mul_shift.swapped");
1312 cover("opt.opt_expr.mul_shift.unswapped");
1314 log_debug("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
1315 a_val
, cell
->name
.c_str(), module
->name
.c_str(), i
);
1318 cell
->setPort(ID::A
, cell
->getPort(ID::B
));
1319 cell
->parameters
.at(ID(A_WIDTH
)) = cell
->parameters
.at(ID(B_WIDTH
));
1320 cell
->parameters
.at(ID(A_SIGNED
)) = cell
->parameters
.at(ID(B_SIGNED
));
1323 std::vector
<RTLIL::SigBit
> new_b
= RTLIL::SigSpec(i
, 6);
1325 while (GetSize(new_b
) > 1 && new_b
.back() == RTLIL::State::S0
)
1328 cell
->type
= ID($shl
);
1329 cell
->parameters
[ID(B_WIDTH
)] = GetSize(new_b
);
1330 cell
->parameters
[ID(B_SIGNED
)] = false;
1331 cell
->setPort(ID::B
, new_b
);
1334 did_something
= true;
1340 if (!keepdc
&& cell
->type
.in(ID($div
), ID($mod
)))
1342 bool b_signed
= cell
->parameters
[ID(B_SIGNED
)].as_bool();
1343 SigSpec sig_b
= assign_map(cell
->getPort(ID::B
));
1344 SigSpec sig_y
= assign_map(cell
->getPort(ID::Y
));
1346 if (sig_b
.is_fully_def() && sig_b
.size() <= 32)
1348 int b_val
= sig_b
.as_int();
1352 cover("opt.opt_expr.divmod_zero");
1354 log_debug("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
1355 cell
->name
.c_str(), module
->name
.c_str());
1357 module
->connect(RTLIL::SigSig(sig_y
, RTLIL::SigSpec(State::Sx
, sig_y
.size())));
1358 module
->remove(cell
);
1360 did_something
= true;
1364 for (int i
= 1; i
< (b_signed
? sig_b
.size()-1 : sig_b
.size()); i
++)
1365 if (b_val
== (1 << i
))
1367 if (cell
->type
== ID($div
))
1369 cover("opt.opt_expr.div_shift");
1371 log_debug("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
1372 b_val
, cell
->name
.c_str(), module
->name
.c_str(), i
);
1374 std::vector
<RTLIL::SigBit
> new_b
= RTLIL::SigSpec(i
, 6);
1376 while (GetSize(new_b
) > 1 && new_b
.back() == RTLIL::State::S0
)
1379 cell
->type
= ID($shr
);
1380 cell
->parameters
[ID(B_WIDTH
)] = GetSize(new_b
);
1381 cell
->parameters
[ID(B_SIGNED
)] = false;
1382 cell
->setPort(ID::B
, new_b
);
1387 cover("opt.opt_expr.mod_mask");
1389 log_debug("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
1390 b_val
, cell
->name
.c_str(), module
->name
.c_str());
1392 std::vector
<RTLIL::SigBit
> new_b
= RTLIL::SigSpec(State::S1
, i
);
1395 new_b
.push_back(State::S0
);
1397 cell
->type
= ID($
and);
1398 cell
->parameters
[ID(B_WIDTH
)] = GetSize(new_b
);
1399 cell
->setPort(ID::B
, new_b
);
1403 did_something
= true;
1409 // remove redundant pairs of bits in ==, ===, !=, and !==
1410 // replace cell with const driver if inputs can't be equal
1411 if (do_fine
&& cell
->type
.in(ID($eq
), ID($ne
), ID($eqx
), ID($nex
)))
1413 pool
<pair
<SigBit
, SigBit
>> redundant_cache
;
1414 mfp
<SigBit
> contradiction_cache
;
1416 contradiction_cache
.promote(State::S0
);
1417 contradiction_cache
.promote(State::S1
);
1419 int a_width
= cell
->getParam(ID(A_WIDTH
)).as_int();
1420 int b_width
= cell
->getParam(ID(B_WIDTH
)).as_int();
1422 bool is_signed
= cell
->getParam(ID(A_SIGNED
)).as_bool();
1423 int width
= is_signed
? std::min(a_width
, b_width
) : std::max(a_width
, b_width
);
1425 SigSpec sig_a
= cell
->getPort(ID::A
);
1426 SigSpec sig_b
= cell
->getPort(ID::B
);
1428 int redundant_bits
= 0;
1430 for (int i
= width
-1; i
>= 0; i
--)
1432 SigBit bit_a
= i
< a_width
? assign_map(sig_a
[i
]) : State::S0
;
1433 SigBit bit_b
= i
< b_width
? assign_map(sig_b
[i
]) : State::S0
;
1435 if (bit_a
!= State::Sx
&& bit_a
!= State::Sz
&&
1436 bit_b
!= State::Sx
&& bit_b
!= State::Sz
)
1437 contradiction_cache
.merge(bit_a
, bit_b
);
1440 std::swap(bit_a
, bit_b
);
1442 pair
<SigBit
, SigBit
> key(bit_a
, bit_b
);
1444 if (redundant_cache
.count(key
)) {
1445 if (i
< a_width
) sig_a
.remove(i
);
1446 if (i
< b_width
) sig_b
.remove(i
);
1451 redundant_cache
.insert(key
);
1454 if (contradiction_cache
.find(State::S0
) == contradiction_cache
.find(State::S1
))
1456 SigSpec y_sig
= cell
->getPort(ID::Y
);
1457 Const
y_value(cell
->type
.in(ID($eq
), ID($eqx
)) ? 0 : 1, GetSize(y_sig
));
1459 log_debug("Replacing cell `%s' in module `%s' with constant driver %s.\n",
1460 log_id(cell
), log_id(module
), log_signal(y_value
));
1462 module
->connect(y_sig
, y_value
);
1463 module
->remove(cell
);
1465 did_something
= true;
1471 log_debug("Removed %d redundant input bits from %s cell `%s' in module `%s'.\n",
1472 redundant_bits
, log_id(cell
->type
), log_id(cell
), log_id(module
));
1474 cell
->setPort(ID::A
, sig_a
);
1475 cell
->setPort(ID::B
, sig_b
);
1476 cell
->setParam(ID(A_WIDTH
), GetSize(sig_a
));
1477 cell
->setParam(ID(B_WIDTH
), GetSize(sig_b
));
1479 did_something
= true;
1484 // simplify comparisons
1485 if (do_fine
&& cell
->type
.in(ID($lt
), ID($ge
), ID($gt
), ID($le
)))
1487 IdString cmp_type
= cell
->type
;
1488 SigSpec var_sig
= cell
->getPort(ID::A
);
1489 SigSpec const_sig
= cell
->getPort(ID::B
);
1490 int var_width
= cell
->parameters
[ID(A_WIDTH
)].as_int();
1491 int const_width
= cell
->parameters
[ID(B_WIDTH
)].as_int();
1492 bool is_signed
= cell
->getParam(ID(A_SIGNED
)).as_bool();
1494 if (!const_sig
.is_fully_const())
1496 std::swap(var_sig
, const_sig
);
1497 std::swap(var_width
, const_width
);
1498 if (cmp_type
== ID($gt
))
1500 else if (cmp_type
== ID($lt
))
1502 else if (cmp_type
== ID($ge
))
1504 else if (cmp_type
== ID($le
))
1508 if (const_sig
.is_fully_def() && const_sig
.is_fully_const())
1510 std::string condition
, replacement
;
1511 SigSpec
replace_sig(State::S0
, GetSize(cell
->getPort(ID::Y
)));
1512 bool replace
= false;
1513 bool remove
= false;
1517 if (const_sig
.is_fully_zero() && cmp_type
== ID($lt
)) {
1518 condition
= "unsigned X<0";
1519 replacement
= "constant 0";
1520 replace_sig
[0] = State::S0
;
1523 if (const_sig
.is_fully_zero() && cmp_type
== ID($ge
)) {
1524 condition
= "unsigned X>=0";
1525 replacement
= "constant 1";
1526 replace_sig
[0] = State::S1
;
1529 if (const_width
== var_width
&& const_sig
.is_fully_ones() && cmp_type
== ID($gt
)) {
1530 condition
= "unsigned X>~0";
1531 replacement
= "constant 0";
1532 replace_sig
[0] = State::S0
;
1535 if (const_width
== var_width
&& const_sig
.is_fully_ones() && cmp_type
== ID($le
)) {
1536 condition
= "unsigned X<=~0";
1537 replacement
= "constant 1";
1538 replace_sig
[0] = State::S1
;
1542 int const_bit_hot
= get_onehot_bit_index(const_sig
);
1543 if (const_bit_hot
>= 0 && const_bit_hot
< var_width
)
1545 RTLIL::SigSpec
var_high_sig(RTLIL::State::S0
, var_width
- const_bit_hot
);
1546 for (int i
= const_bit_hot
; i
< var_width
; i
++) {
1547 var_high_sig
[i
- const_bit_hot
] = var_sig
[i
];
1550 if (cmp_type
== ID($lt
))
1552 condition
= stringf("unsigned X<%s", log_signal(const_sig
));
1553 replacement
= stringf("!X[%d:%d]", var_width
- 1, const_bit_hot
);
1554 module
->addLogicNot(NEW_ID
, var_high_sig
, cell
->getPort(ID::Y
));
1557 if (cmp_type
== ID($ge
))
1559 condition
= stringf("unsigned X>=%s", log_signal(const_sig
));
1560 replacement
= stringf("|X[%d:%d]", var_width
- 1, const_bit_hot
);
1561 module
->addReduceOr(NEW_ID
, var_high_sig
, cell
->getPort(ID::Y
));
1566 int const_bit_set
= get_highest_hot_index(const_sig
);
1567 if(const_bit_set
>= var_width
)
1570 if (cmp_type
== ID($lt
) || cmp_type
== ID($le
))
1572 if (cmp_type
== ID($lt
)) cmp_name
= "<";
1573 if (cmp_type
== ID($le
)) cmp_name
= "<=";
1574 condition
= stringf("unsigned X[%d:0]%s%s", var_width
- 1, cmp_name
.c_str(), log_signal(const_sig
));
1575 replacement
= "constant 1";
1576 replace_sig
[0] = State::S1
;
1579 if (cmp_type
== ID($gt
) || cmp_type
== ID($ge
))
1581 if (cmp_type
== ID($gt
)) cmp_name
= ">";
1582 if (cmp_type
== ID($ge
)) cmp_name
= ">=";
1583 condition
= stringf("unsigned X[%d:0]%s%s", var_width
- 1, cmp_name
.c_str(), log_signal(const_sig
));
1584 replacement
= "constant 0";
1585 replace_sig
[0] = State::S0
;
1592 if (const_sig
.is_fully_zero() && cmp_type
== ID($lt
))
1594 condition
= "signed X<0";
1595 replacement
= stringf("X[%d]", var_width
- 1);
1596 replace_sig
[0] = var_sig
[var_width
- 1];
1599 if (const_sig
.is_fully_zero() && cmp_type
== ID($ge
))
1601 condition
= "signed X>=0";
1602 replacement
= stringf("X[%d]", var_width
- 1);
1603 module
->addNot(NEW_ID
, var_sig
[var_width
- 1], cell
->getPort(ID::Y
));
1608 if (replace
|| remove
)
1610 log_debug("Replacing %s cell `%s' (implementing %s) with %s.\n",
1611 log_id(cell
->type
), log_id(cell
), condition
.c_str(), replacement
.c_str());
1613 module
->connect(cell
->getPort(ID::Y
), replace_sig
);
1614 module
->remove(cell
);
1615 did_something
= true;
1624 #undef FOLD_1ARG_CELL
1625 #undef FOLD_2ARG_CELL
1629 struct OptExprPass
: public Pass
{
1630 OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { }
1631 void help() YS_OVERRIDE
1633 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
1635 log(" opt_expr [options] [selection]\n");
1637 log("This pass performs const folding on internal cell types with constant inputs.\n");
1638 log("It also performs some simple expression rewriting.\n");
1640 log(" -mux_undef\n");
1641 log(" remove 'undef' inputs from $mux, $pmux and $_MUX_ cells\n");
1643 log(" -mux_bool\n");
1644 log(" replace $mux cells with inverters or buffers when possible\n");
1646 log(" -undriven\n");
1647 log(" replace undriven nets with undef (x) constants\n");
1650 log(" optimize clock inverters by changing FF types\n");
1653 log(" perform fine-grain optimizations\n");
1656 log(" alias for -mux_undef -mux_bool -undriven -fine\n");
1659 log(" some optimizations change the behavior of the circuit with respect to\n");
1660 log(" don't-care bits. for example in 'a+0' a single x-bit in 'a' will cause\n");
1661 log(" all result bits to be set to x. this behavior changes when 'a+0' is\n");
1662 log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n");
1665 void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
) YS_OVERRIDE
1667 bool mux_undef
= false;
1668 bool mux_bool
= false;
1669 bool undriven
= false;
1670 bool clkinv
= false;
1671 bool do_fine
= false;
1672 bool keepdc
= false;
1674 log_header(design
, "Executing OPT_EXPR pass (perform const folding).\n");
1678 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
1679 if (args
[argidx
] == "-mux_undef") {
1683 if (args
[argidx
] == "-mux_bool") {
1687 if (args
[argidx
] == "-undriven") {
1691 if (args
[argidx
] == "-clkinv") {
1695 if (args
[argidx
] == "-fine") {
1699 if (args
[argidx
] == "-full") {
1706 if (args
[argidx
] == "-keepdc") {
1712 extra_args(args
, argidx
, design
);
1714 for (auto module
: design
->selected_modules())
1716 log("Optimizing module %s.\n", log_id(module
));
1719 did_something
= false;
1720 replace_undriven(design
, module
);
1722 design
->scratchpad_set_bool("opt.did_something", true);
1727 did_something
= false;
1728 replace_const_cells(design
, module
, false, mux_undef
, mux_bool
, do_fine
, keepdc
, clkinv
);
1730 design
->scratchpad_set_bool("opt.did_something", true);
1731 } while (did_something
);
1732 replace_const_cells(design
, module
, true, mux_undef
, mux_bool
, do_fine
, keepdc
, clkinv
);
1734 design
->scratchpad_set_bool("opt.did_something", true);
1735 } while (did_something
);
1744 PRIVATE_NAMESPACE_END