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 for (auto cell
: module
->cells())
43 for (auto &conn
: cell
->connections()) {
44 if (!ct
.cell_known(cell
->type
) || ct
.cell_output(cell
->type
, conn
.first
))
45 driven_signals
.add(sigmap(conn
.second
));
46 if (!ct
.cell_known(cell
->type
) || ct
.cell_input(cell
->type
, conn
.first
))
47 used_signals
.add(sigmap(conn
.second
));
50 for (auto wire
: module
->wires()) {
52 driven_signals
.add(sigmap(wire
));
53 if (wire
->port_output
)
54 used_signals
.add(sigmap(wire
));
55 all_signals
.add(sigmap(wire
));
58 all_signals
.del(driven_signals
);
59 RTLIL::SigSpec undriven_signals
= all_signals
.export_all();
61 for (auto &c
: undriven_signals
.chunks())
63 RTLIL::SigSpec sig
= c
;
65 if (c
.wire
->name
[0] == '$')
66 sig
= used_signals
.extract(sig
);
70 log("Setting undriven signal in %s to undef: %s\n", RTLIL::id2cstr(module
->name
), log_signal(c
));
71 module
->connect(RTLIL::SigSig(c
, RTLIL::SigSpec(RTLIL::State::Sx
, c
.width
)));
76 void replace_cell(SigMap
&assign_map
, RTLIL::Module
*module
, RTLIL::Cell
*cell
, std::string info
, std::string out_port
, RTLIL::SigSpec out_val
)
78 RTLIL::SigSpec Y
= cell
->getPort(out_port
);
79 out_val
.extend_u0(Y
.size(), false);
81 log("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n",
82 cell
->type
.c_str(), cell
->name
.c_str(), info
.c_str(),
83 module
->name
.c_str(), log_signal(Y
), log_signal(out_val
));
85 assign_map
.add(Y
, out_val
);
86 module
->connect(Y
, out_val
);
91 bool group_cell_inputs(RTLIL::Module
*module
, RTLIL::Cell
*cell
, bool commutative
, SigMap
&sigmap
)
93 std::string b_name
= cell
->hasPort("\\B") ? "\\B" : "\\A";
95 bool a_signed
= cell
->parameters
.at("\\A_SIGNED").as_bool();
96 bool b_signed
= cell
->parameters
.at(b_name
+ "_SIGNED").as_bool();
98 RTLIL::SigSpec sig_a
= sigmap(cell
->getPort("\\A"));
99 RTLIL::SigSpec sig_b
= sigmap(cell
->getPort(b_name
));
100 RTLIL::SigSpec sig_y
= sigmap(cell
->getPort("\\Y"));
102 sig_a
.extend_u0(sig_y
.size(), a_signed
);
103 sig_b
.extend_u0(sig_y
.size(), b_signed
);
105 std::vector
<RTLIL::SigBit
> bits_a
= sig_a
, bits_b
= sig_b
, bits_y
= sig_y
;
107 enum { GRP_DYN
, GRP_CONST_A
, GRP_CONST_B
, GRP_CONST_AB
, GRP_N
};
108 std::map
<std::pair
<RTLIL::SigBit
, RTLIL::SigBit
>, std::set
<RTLIL::SigBit
>> grouped_bits
[GRP_N
];
110 for (int i
= 0; i
< GetSize(bits_y
); i
++)
112 int group_idx
= GRP_DYN
;
113 RTLIL::SigBit bit_a
= bits_a
[i
], bit_b
= bits_b
[i
];
115 if (cell
->type
== "$or" && (bit_a
== RTLIL::State::S1
|| bit_b
== RTLIL::State::S1
))
116 bit_a
= bit_b
= RTLIL::State::S1
;
118 if (cell
->type
== "$and" && (bit_a
== RTLIL::State::S0
|| bit_b
== RTLIL::State::S0
))
119 bit_a
= bit_b
= RTLIL::State::S0
;
121 if (bit_a
.wire
== NULL
&& bit_b
.wire
== NULL
)
122 group_idx
= GRP_CONST_AB
;
123 else if (bit_a
.wire
== NULL
)
124 group_idx
= GRP_CONST_A
;
125 else if (bit_b
.wire
== NULL
&& commutative
)
126 group_idx
= GRP_CONST_A
, std::swap(bit_a
, bit_b
);
127 else if (bit_b
.wire
== NULL
)
128 group_idx
= GRP_CONST_B
;
130 grouped_bits
[group_idx
][std::pair
<RTLIL::SigBit
, RTLIL::SigBit
>(bit_a
, bit_b
)].insert(bits_y
[i
]);
133 for (int i
= 0; i
< GRP_N
; i
++)
134 if (GetSize(grouped_bits
[i
]) == GetSize(bits_y
))
137 log("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
138 log_id(cell
->type
), log_id(cell
), log_id(module
));
140 for (int i
= 0; i
< GRP_N
; i
++)
142 if (grouped_bits
[i
].empty())
145 RTLIL::Wire
*new_y
= module
->addWire(NEW_ID
, GetSize(grouped_bits
[i
]));
146 RTLIL::SigSpec new_a
, new_b
;
147 RTLIL::SigSig new_conn
;
149 for (auto &it
: grouped_bits
[i
]) {
150 for (auto &bit
: it
.second
) {
151 new_conn
.first
.append_bit(bit
);
152 new_conn
.second
.append_bit(RTLIL::SigBit(new_y
, new_a
.size()));
154 new_a
.append_bit(it
.first
.first
);
155 new_b
.append_bit(it
.first
.second
);
158 RTLIL::Cell
*c
= module
->addCell(NEW_ID
, cell
->type
);
160 c
->setPort("\\A", new_a
);
161 c
->parameters
["\\A_WIDTH"] = new_a
.size();
162 c
->parameters
["\\A_SIGNED"] = false;
164 if (b_name
== "\\B") {
165 c
->setPort("\\B", new_b
);
166 c
->parameters
["\\B_WIDTH"] = new_b
.size();
167 c
->parameters
["\\B_SIGNED"] = false;
170 c
->setPort("\\Y", new_y
);
171 c
->parameters
["\\Y_WIDTH"] = new_y
->width
;
174 module
->connect(new_conn
);
176 log(" New cell `%s': A=%s", log_id(c
), log_signal(new_a
));
178 log(", B=%s", log_signal(new_b
));
182 cover_list("opt.opt_const.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell
->type
.str());
184 module
->remove(cell
);
185 did_something
= true;
189 void replace_const_cells(RTLIL::Design
*design
, RTLIL::Module
*module
, bool consume_x
, bool mux_undef
, bool mux_bool
, bool do_fine
, bool keepdc
)
191 if (!design
->selected(module
))
194 CellTypes ct_combinational
;
195 ct_combinational
.setup_internals();
196 ct_combinational
.setup_stdcells();
198 SigMap
assign_map(module
);
199 std::map
<RTLIL::SigSpec
, RTLIL::SigSpec
> invert_map
;
201 TopoSort
<RTLIL::Cell
*, RTLIL::IdString::compare_ptr_by_name
<RTLIL::Cell
>> cells
;
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)
209 invert_map
[assign_map(cell
->getPort("\\Y"))] = assign_map(cell
->getPort("\\A"));
210 if (ct_combinational
.cell_known(cell
->type
))
211 for (auto &conn
: cell
->connections()) {
212 RTLIL::SigSpec sig
= assign_map(conn
.second
);
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")
239 if (group_cell_inputs(module
, cell
, true, assign_map
))
242 if (cell
->type
== "$reduce_and")
244 RTLIL::SigSpec sig_a
= assign_map(cell
->getPort("\\A"));
246 RTLIL::State new_a
= RTLIL::State::S1
;
247 for (auto &bit
: sig_a
.to_sigbit_vector())
248 if (bit
== RTLIL::State::Sx
) {
249 if (new_a
== RTLIL::State::S1
)
250 new_a
= RTLIL::State::Sx
;
251 } else if (bit
== RTLIL::State::S0
) {
252 new_a
= RTLIL::State::S0
;
254 } else if (bit
.wire
!= NULL
) {
255 new_a
= RTLIL::State::Sm
;
258 if (new_a
!= RTLIL::State::Sm
&& RTLIL::SigSpec(new_a
) != sig_a
) {
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",
261 cell
->type
.c_str(), cell
->name
.c_str(), module
->name
.c_str(), log_signal(sig_a
), log_signal(new_a
));
262 cell
->setPort("\\A", sig_a
= new_a
);
263 cell
->parameters
.at("\\A_WIDTH") = 1;
264 did_something
= true;
268 if (cell
->type
== "$logic_not" || cell
->type
== "$logic_and" || cell
->type
== "$logic_or" || cell
->type
== "$reduce_or" || cell
->type
== "$reduce_bool")
270 RTLIL::SigSpec sig_a
= assign_map(cell
->getPort("\\A"));
272 RTLIL::State new_a
= RTLIL::State::S0
;
273 for (auto &bit
: sig_a
.to_sigbit_vector())
274 if (bit
== RTLIL::State::Sx
) {
275 if (new_a
== RTLIL::State::S0
)
276 new_a
= RTLIL::State::Sx
;
277 } else if (bit
== RTLIL::State::S1
) {
278 new_a
= RTLIL::State::S1
;
280 } else if (bit
.wire
!= NULL
) {
281 new_a
= RTLIL::State::Sm
;
284 if (new_a
!= RTLIL::State::Sm
&& RTLIL::SigSpec(new_a
) != sig_a
) {
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",
287 cell
->type
.c_str(), cell
->name
.c_str(), module
->name
.c_str(), log_signal(sig_a
), log_signal(new_a
));
288 cell
->setPort("\\A", sig_a
= new_a
);
289 cell
->parameters
.at("\\A_WIDTH") = 1;
290 did_something
= true;
294 if (cell
->type
== "$logic_and" || cell
->type
== "$logic_or")
296 RTLIL::SigSpec sig_b
= assign_map(cell
->getPort("\\B"));
298 RTLIL::State new_b
= RTLIL::State::S0
;
299 for (auto &bit
: sig_b
.to_sigbit_vector())
300 if (bit
== RTLIL::State::Sx
) {
301 if (new_b
== RTLIL::State::S0
)
302 new_b
= RTLIL::State::Sx
;
303 } else if (bit
== RTLIL::State::S1
) {
304 new_b
= RTLIL::State::S1
;
306 } else if (bit
.wire
!= NULL
) {
307 new_b
= RTLIL::State::Sm
;
310 if (new_b
!= RTLIL::State::Sm
&& RTLIL::SigSpec(new_b
) != sig_b
) {
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",
313 cell
->type
.c_str(), cell
->name
.c_str(), module
->name
.c_str(), log_signal(sig_b
), log_signal(new_b
));
314 cell
->setPort("\\B", sig_b
= new_b
);
315 cell
->parameters
.at("\\B_WIDTH") = 1;
316 did_something
= true;
321 if (cell
->type
== "$logic_or" && (assign_map(cell
->getPort("\\A")) == RTLIL::State::S1
|| assign_map(cell
->getPort("\\B")) == RTLIL::State::S1
)) {
322 cover("opt.opt_const.one_high");
323 replace_cell(assign_map
, module
, cell
, "one high", "\\Y", RTLIL::State::S1
);
327 if (cell
->type
== "$logic_and" && (assign_map(cell
->getPort("\\A")) == RTLIL::State::S0
|| assign_map(cell
->getPort("\\B")) == RTLIL::State::S0
)) {
328 cover("opt.opt_const.one_low");
329 replace_cell(assign_map
, module
, cell
, "one low", "\\Y", RTLIL::State::S0
);
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")
339 RTLIL::SigSpec sig_a
= assign_map(cell
->getPort("\\A"));
340 RTLIL::SigSpec sig_b
= cell
->hasPort("\\B") ? assign_map(cell
->getPort("\\B")) : RTLIL::SigSpec();
342 if (cell
->type
== "$shl" || cell
->type
== "$shr" || cell
->type
== "$sshl" || cell
->type
== "$sshr" || cell
->type
== "$shift" || cell
->type
== "$shiftx")
343 sig_a
= RTLIL::SigSpec();
345 for (auto &bit
: sig_a
.to_sigbit_vector())
346 if (bit
== RTLIL::State::Sx
)
347 goto found_the_x_bit
;
349 for (auto &bit
: sig_b
.to_sigbit_vector())
350 if (bit
== RTLIL::State::Sx
)
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")
359 replace_cell(assign_map
, module
, cell
, "x-bit in input", "\\Y", RTLIL::State::Sx
);
361 replace_cell(assign_map
, module
, cell
, "x-bit in input", "\\Y", RTLIL::SigSpec(RTLIL::State::Sx
, cell
->getPort("\\Y").size()));
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());
369 replace_cell(assign_map
, module
, cell
, "double_invert", "\\Y", invert_map
.at(assign_map(cell
->getPort("\\A"))));
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
));
376 RTLIL::SigSpec tmp
= cell
->getPort("\\A");
377 cell
->setPort("\\A", cell
->getPort("\\B"));
378 cell
->setPort("\\B", tmp
);
379 cell
->setPort("\\S", invert_map
.at(assign_map(cell
->getPort("\\S"))));
380 did_something
= true;
384 if (cell
->type
== "$_NOT_") {
385 RTLIL::SigSpec input
= cell
->getPort("\\A");
386 assign_map
.apply(input
);
387 if (input
.match("1")) ACTION_DO_Y(0);
388 if (input
.match("0")) ACTION_DO_Y(1);
389 if (input
.match("*")) ACTION_DO_Y(x
);
392 if (cell
->type
== "$_AND_") {
393 RTLIL::SigSpec input
;
394 input
.append(cell
->getPort("\\B"));
395 input
.append(cell
->getPort("\\A"));
396 assign_map
.apply(input
);
397 if (input
.match(" 0")) ACTION_DO_Y(0);
398 if (input
.match("0 ")) ACTION_DO_Y(0);
399 if (input
.match("11")) ACTION_DO_Y(1);
400 if (input
.match("**")) ACTION_DO_Y(x
);
401 if (input
.match("1*")) ACTION_DO_Y(x
);
402 if (input
.match("*1")) ACTION_DO_Y(x
);
404 if (input
.match(" *")) ACTION_DO_Y(0);
405 if (input
.match("* ")) ACTION_DO_Y(0);
407 if (input
.match(" 1")) ACTION_DO("\\Y", input
.extract(1, 1));
408 if (input
.match("1 ")) ACTION_DO("\\Y", input
.extract(0, 1));
411 if (cell
->type
== "$_OR_") {
412 RTLIL::SigSpec input
;
413 input
.append(cell
->getPort("\\B"));
414 input
.append(cell
->getPort("\\A"));
415 assign_map
.apply(input
);
416 if (input
.match(" 1")) ACTION_DO_Y(1);
417 if (input
.match("1 ")) ACTION_DO_Y(1);
418 if (input
.match("00")) ACTION_DO_Y(0);
419 if (input
.match("**")) ACTION_DO_Y(x
);
420 if (input
.match("0*")) ACTION_DO_Y(x
);
421 if (input
.match("*0")) ACTION_DO_Y(x
);
423 if (input
.match(" *")) ACTION_DO_Y(1);
424 if (input
.match("* ")) ACTION_DO_Y(1);
426 if (input
.match(" 0")) ACTION_DO("\\Y", input
.extract(1, 1));
427 if (input
.match("0 ")) ACTION_DO("\\Y", input
.extract(0, 1));
430 if (cell
->type
== "$_XOR_") {
431 RTLIL::SigSpec input
;
432 input
.append(cell
->getPort("\\B"));
433 input
.append(cell
->getPort("\\A"));
434 assign_map
.apply(input
);
435 if (input
.match("00")) ACTION_DO_Y(0);
436 if (input
.match("01")) ACTION_DO_Y(1);
437 if (input
.match("10")) ACTION_DO_Y(1);
438 if (input
.match("11")) ACTION_DO_Y(0);
439 if (input
.match(" *")) ACTION_DO_Y(x
);
440 if (input
.match("* ")) ACTION_DO_Y(x
);
441 if (input
.match(" 0")) ACTION_DO("\\Y", input
.extract(1, 1));
442 if (input
.match("0 ")) ACTION_DO("\\Y", input
.extract(0, 1));
445 if (cell
->type
== "$_MUX_") {
446 RTLIL::SigSpec input
;
447 input
.append(cell
->getPort("\\S"));
448 input
.append(cell
->getPort("\\B"));
449 input
.append(cell
->getPort("\\A"));
450 assign_map
.apply(input
);
451 if (input
.extract(2, 1) == input
.extract(1, 1))
452 ACTION_DO("\\Y", input
.extract(2, 1));
453 if (input
.match(" 0")) ACTION_DO("\\Y", input
.extract(2, 1));
454 if (input
.match(" 1")) ACTION_DO("\\Y", input
.extract(1, 1));
455 if (input
.match("01 ")) ACTION_DO("\\Y", input
.extract(0, 1));
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");
464 if (input
.match("11 ")) ACTION_DO_Y(1);
465 if (input
.match("00 ")) ACTION_DO_Y(0);
466 if (input
.match("** ")) ACTION_DO_Y(x
);
467 if (input
.match("01*")) ACTION_DO_Y(x
);
468 if (input
.match("10*")) ACTION_DO_Y(x
);
470 if (input
.match("* ")) ACTION_DO("\\Y", input
.extract(1, 1));
471 if (input
.match(" * ")) ACTION_DO("\\Y", input
.extract(2, 1));
472 if (input
.match(" *")) ACTION_DO("\\Y", input
.extract(2, 1));
476 if (cell
->type
== "$eq" || cell
->type
== "$ne" || cell
->type
== "$eqx" || cell
->type
== "$nex")
478 RTLIL::SigSpec a
= cell
->getPort("\\A");
479 RTLIL::SigSpec b
= cell
->getPort("\\B");
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());
487 RTLIL::SigSpec new_a
, new_b
;
489 log_assert(GetSize(a
) == GetSize(b
));
490 for (int i
= 0; i
< GetSize(a
); i
++) {
491 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
) {
492 cover_list("opt.opt_const.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell
->type
.str());
493 RTLIL::SigSpec new_y
= RTLIL::SigSpec((cell
->type
== "$eq" || cell
->type
== "$eqx") ? RTLIL::State::S0
: RTLIL::State::S1
);
494 new_y
.extend_u0(cell
->parameters
["\\Y_WIDTH"].as_int(), false);
495 replace_cell(assign_map
, module
, cell
, "isneq", "\\Y", new_y
);
504 if (new_a
.size() == 0) {
505 cover_list("opt.opt_const.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell
->type
.str());
506 RTLIL::SigSpec new_y
= RTLIL::SigSpec((cell
->type
== "$eq" || cell
->type
== "$eqx") ? RTLIL::State::S1
: RTLIL::State::S0
);
507 new_y
.extend_u0(cell
->parameters
["\\Y_WIDTH"].as_int(), false);
508 replace_cell(assign_map
, module
, cell
, "empty", "\\Y", new_y
);
512 if (new_a
.size() < a
.size() || new_b
.size() < b
.size()) {
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)
524 RTLIL::SigSpec a
= assign_map(cell
->getPort("\\A"));
525 RTLIL::SigSpec b
= assign_map(cell
->getPort("\\B"));
527 if (a
.is_fully_const()) {
528 cover_list("opt.opt_const.eqneq.swapconst", "$eq", "$ne", cell
->type
.str());
529 RTLIL::SigSpec tmp
= cell
->getPort("\\A");
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")) {
536 RTLIL::SigSpec input
= b
;
537 ACTION_DO("\\Y", cell
->getPort("\\A"));
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");
545 did_something
= true;
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"))
559 RTLIL::SigSpec sig_a
= assign_map(cell
->getPort("\\A"));
560 RTLIL::SigSpec
sig_y(cell
->type
== "$shiftx" ? RTLIL::State::Sx
: RTLIL::State::S0
, cell
->getParam("\\Y_WIDTH").as_int());
562 if (GetSize(sig_a
) < GetSize(sig_y
))
563 sig_a
.extend_u0(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",
576 log_id(cell
->type
), log_id(cell
), log_signal(assign_map(cell
->getPort("\\B"))), shift_bits
, log_id(module
), log_signal(sig_y
));
578 module
->connect(cell
->getPort("\\Y"), sig_y
);
579 module
->remove(cell
);
581 did_something
= true;
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")
592 RTLIL::SigSpec a
= assign_map(cell
->getPort("\\A"));
593 RTLIL::SigSpec b
= assign_map(cell
->getPort("\\B"));
595 if (cell
->type
!= "$sub" && a
.is_fully_const() && a
.as_bool() == false)
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")
604 RTLIL::SigSpec b
= assign_map(cell
->getPort("\\B"));
606 if (b
.is_fully_const() && b
.as_bool() == false)
607 identity_wrt_a
= true;
610 if (cell
->type
== "$mul")
612 RTLIL::SigSpec a
= assign_map(cell
->getPort("\\A"));
613 RTLIL::SigSpec b
= assign_map(cell
->getPort("\\B"));
615 if (a
.is_fully_const() && a
.size() <= 32 && a
.as_int() == 1)
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")
624 RTLIL::SigSpec b
= assign_map(cell
->getPort("\\B"));
626 if (b
.is_fully_const() && b
.size() <= 32 && b
.as_int() == 1)
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");
652 did_something
= true;
657 if (mux_bool
&& (cell
->type
== "$mux" || cell
->type
== "$_MUX_") &&
658 cell
->getPort("\\A") == RTLIL::SigSpec(0, 1) && cell
->getPort("\\B") == RTLIL::SigSpec(1, 1)) {
659 cover_list("opt.opt_const.mux_bool", "$mux", "$_MUX_", cell
->type
.str());
660 replace_cell(assign_map
, module
, cell
, "mux_bool", "\\Y", cell
->getPort("\\S"));
664 if (mux_bool
&& (cell
->type
== "$mux" || cell
->type
== "$_MUX_") &&
665 cell
->getPort("\\A") == RTLIL::SigSpec(1, 1) && cell
->getPort("\\B") == RTLIL::SigSpec(0, 1)) {
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_";
679 did_something
= true;
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_";
698 did_something
= true;
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_";
717 did_something
= true;
721 if (mux_undef
&& (cell
->type
== "$mux" || cell
->type
== "$pmux")) {
722 RTLIL::SigSpec new_a
, new_b
, new_s
;
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());
727 replace_cell(assign_map
, module
, cell
, "mux_undef", "\\Y", cell
->getPort("\\A"));
730 for (int i
= 0; i
< cell
->getPort("\\S").size(); i
++) {
731 RTLIL::SigSpec old_b
= cell
->getPort("\\B").extract(i
*width
, width
);
732 RTLIL::SigSpec old_s
= cell
->getPort("\\S").extract(i
, 1);
733 if (old_b
.is_fully_undef() || old_s
.is_fully_undef())
738 new_a
= cell
->getPort("\\A");
739 if (new_a
.is_fully_undef() && new_s
.size() > 0) {
740 new_a
= new_b
.extract((new_s
.size()-1)*width
, width
);
741 new_b
= new_b
.extract(0, (new_s
.size()-1)*width
);
742 new_s
= new_s
.extract(0, new_s
.size()-1);
744 if (new_s
.size() == 0) {
745 cover_list("opt.opt_const.mux_empty", "$mux", "$pmux", cell
->type
.str());
746 replace_cell(assign_map
, module
, cell
, "mux_empty", "\\Y", new_a
);
749 if (new_a
== RTLIL::SigSpec(RTLIL::State::S0
) && new_b
== RTLIL::SigSpec(RTLIL::State::S1
)) {
750 cover_list("opt.opt_const.mux_sel01", "$mux", "$pmux", cell
->type
.str());
751 replace_cell(assign_map
, module
, cell
, "mux_sel01", "\\Y", new_s
);
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",
757 GetSize(cell
->getPort("\\S")) - GetSize(new_s
), log_id(cell
->type
), log_id(cell
), log_id(module
));
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");
768 did_something
= true;
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); \
808 FOLD_1ARG_CELL(reduce_and
)
809 FOLD_1ARG_CELL(reduce_or
)
810 FOLD_1ARG_CELL(reduce_xor
)
811 FOLD_1ARG_CELL(reduce_xnor
)
812 FOLD_1ARG_CELL(reduce_bool
)
814 FOLD_1ARG_CELL(logic_not
)
815 FOLD_2ARG_CELL(logic_and
)
816 FOLD_2ARG_CELL(logic_or
)
822 FOLD_2ARG_CELL(shift
)
823 FOLD_2ARG_CELL(shiftx
)
842 // be very conservative with optimizing $mux cells as we do not want to break mux trees
843 if (cell
->type
== "$mux") {
844 RTLIL::SigSpec input
= assign_map(cell
->getPort("\\S"));
845 RTLIL::SigSpec inA
= assign_map(cell
->getPort("\\A"));
846 RTLIL::SigSpec inB
= assign_map(cell
->getPort("\\B"));
847 if (input
.is_fully_const())
848 ACTION_DO("\\Y", input
.as_bool() ? cell
->getPort("\\B") : cell
->getPort("\\A"));
850 ACTION_DO("\\Y", 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;
859 RTLIL::SigSpec sig_a
= assign_map(cell
->getPort("\\A"));
860 RTLIL::SigSpec sig_b
= assign_map(cell
->getPort("\\B"));
861 RTLIL::SigSpec sig_y
= assign_map(cell
->getPort("\\Y"));
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;
866 if (sig_a
.is_fully_def() && sig_a
.size() <= 32)
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());
877 module
->connect(RTLIL::SigSig(sig_y
, RTLIL::SigSpec(0, sig_y
.size())));
878 module
->remove(cell
);
880 did_something
= true;
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"];
901 std::vector
<RTLIL::SigBit
> new_b
= RTLIL::SigSpec(i
, 6);
903 while (GetSize(new_b
) > 1 && new_b
.back() == RTLIL::State::S0
)
907 cell
->parameters
["\\B_WIDTH"] = GetSize(new_b
);
908 cell
->parameters
["\\B_SIGNED"] = false;
909 cell
->setPort("\\B", new_b
);
912 did_something
= true;
921 #undef FOLD_1ARG_CELL
922 #undef FOLD_2ARG_CELL
926 struct OptConstPass
: public Pass
{
927 OptConstPass() : Pass("opt_const", "perform const folding") { }
930 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
932 log(" opt_const [options] [selection]\n");
934 log("This pass performs const folding on internal cell types with constant inputs.\n");
936 log(" -mux_undef\n");
937 log(" remove 'undef' inputs from $mux, $pmux and $_MUX_ cells\n");
940 log(" replace $mux cells with inverters or buffers when possible\n");
943 log(" replace undriven nets with undef (x) constants\n");
946 log(" perform fine-grain optimizations\n");
949 log(" alias for -mux_undef -mux_bool -undriven -fine\n");
952 log(" some optimizations change the behavior of the circuit with respect to\n");
953 log(" don't-care bits. for example in 'a+0' a single x-bit in 'a' will cause\n");
954 log(" all result bits to be set to x. this behavior changes when 'a+0' is\n");
955 log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n");
958 virtual void execute(std::vector
<std::string
> args
, RTLIL::Design
*design
)
960 bool mux_undef
= false;
961 bool mux_bool
= false;
962 bool undriven
= false;
963 bool do_fine
= false;
966 log_header("Executing OPT_CONST pass (perform const folding).\n");
970 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
971 if (args
[argidx
] == "-mux_undef") {
975 if (args
[argidx
] == "-mux_bool") {
979 if (args
[argidx
] == "-undriven") {
983 if (args
[argidx
] == "-fine") {
987 if (args
[argidx
] == "-full") {
994 if (args
[argidx
] == "-keepdc") {
1000 extra_args(args
, argidx
, design
);
1002 for (auto module
: design
->modules())
1005 replace_undriven(design
, module
);
1009 did_something
= false;
1010 replace_const_cells(design
, module
, false, mux_undef
, mux_bool
, do_fine
, keepdc
);
1012 design
->scratchpad_set_bool("opt.did_something", true);
1013 } while (did_something
);
1014 replace_const_cells(design
, module
, true, mux_undef
, mux_bool
, do_fine
, keepdc
);
1015 } while (did_something
);
1022 PRIVATE_NAMESPACE_END