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/yosys.h"
21 #include "kernel/sigtools.h"
22 #include "kernel/modtools.h"
25 using namespace RTLIL
;
27 PRIVATE_NAMESPACE_BEGIN
31 pool
<IdString
> supported_cell_types
;
36 supported_cell_types
= pool
<IdString
>({
37 "$not", "$pos", "$neg",
38 "$and", "$or", "$xor", "$xnor",
39 "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
40 "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
41 "$add", "$sub", "$mul", // "$div", "$mod", "$pow",
50 WreduceConfig
*config
;
54 std::set
<Cell
*, IdString::compare_ptr_by_name
<Cell
>> work_queue_cells
;
55 std::set
<SigBit
> work_queue_bits
;
56 pool
<SigBit
> keep_bits
;
57 dict
<SigBit
, State
> init_bits
;
58 pool
<SigBit
> remove_init_bits
;
60 WreduceWorker(WreduceConfig
*config
, Module
*module
) :
61 config(config
), module(module
), mi(module
) { }
63 void run_cell_mux(Cell
*cell
)
65 // Reduce size of MUX if inputs agree on a value for a bit or a output bit is unused
67 SigSpec sig_a
= mi
.sigmap(cell
->getPort("\\A"));
68 SigSpec sig_b
= mi
.sigmap(cell
->getPort("\\B"));
69 SigSpec sig_s
= mi
.sigmap(cell
->getPort("\\S"));
70 SigSpec sig_y
= mi
.sigmap(cell
->getPort("\\Y"));
71 std::vector
<SigBit
> bits_removed
;
73 if (sig_y
.has_const())
76 for (int i
= GetSize(sig_y
)-1; i
>= 0; i
--)
78 auto info
= mi
.query(sig_y
[i
]);
79 if (!info
->is_output
&& GetSize(info
->ports
) <= 1 && !keep_bits
.count(mi
.sigmap(sig_y
[i
]))) {
80 bits_removed
.push_back(Sx
);
84 SigBit ref
= sig_a
[i
];
85 for (int k
= 0; k
< GetSize(sig_s
); k
++) {
86 if ((config
->keepdc
|| (ref
!= Sx
&& sig_b
[k
*GetSize(sig_a
) + i
] != Sx
)) && ref
!= sig_b
[k
*GetSize(sig_a
) + i
])
88 if (sig_b
[k
*GetSize(sig_a
) + i
] != Sx
)
89 ref
= sig_b
[k
*GetSize(sig_a
) + i
];
94 bits_removed
.push_back(ref
);
97 if (bits_removed
.empty())
101 for (int i
= GetSize(bits_removed
)-1; i
>= 0; i
--)
102 sig_removed
.append_bit(bits_removed
[i
]);
104 if (GetSize(bits_removed
) == GetSize(sig_y
)) {
105 log("Removed cell %s.%s (%s).\n", log_id(module
), log_id(cell
), log_id(cell
->type
));
106 module
->connect(sig_y
, sig_removed
);
107 module
->remove(cell
);
111 log("Removed top %d bits (of %d) from mux cell %s.%s (%s).\n",
112 GetSize(sig_removed
), GetSize(sig_y
), log_id(module
), log_id(cell
), log_id(cell
->type
));
114 int n_removed
= GetSize(sig_removed
);
115 int n_kept
= GetSize(sig_y
) - GetSize(sig_removed
);
117 SigSpec new_work_queue_bits
;
118 new_work_queue_bits
.append(sig_a
.extract(n_kept
, n_removed
));
119 new_work_queue_bits
.append(sig_y
.extract(n_kept
, n_removed
));
121 SigSpec new_sig_a
= sig_a
.extract(0, n_kept
);
122 SigSpec new_sig_y
= sig_y
.extract(0, n_kept
);
125 for (int k
= 0; k
< GetSize(sig_s
); k
++) {
126 new_sig_b
.append(sig_b
.extract(k
*GetSize(sig_a
), n_kept
));
127 new_work_queue_bits
.append(sig_b
.extract(k
*GetSize(sig_a
) + n_kept
, n_removed
));
130 for (auto bit
: new_work_queue_bits
)
131 work_queue_bits
.insert(bit
);
133 cell
->setPort("\\A", new_sig_a
);
134 cell
->setPort("\\B", new_sig_b
);
135 cell
->setPort("\\Y", new_sig_y
);
136 cell
->fixup_parameters();
138 module
->connect(sig_y
.extract(n_kept
, n_removed
), sig_removed
);
141 void run_cell_dff(Cell
*cell
)
143 // Reduce size of FF if inputs are just sign/zero extended or output bit is not used
145 SigSpec sig_d
= mi
.sigmap(cell
->getPort("\\D"));
146 SigSpec sig_q
= mi
.sigmap(cell
->getPort("\\Q"));
149 int width_before
= GetSize(sig_q
);
151 if (width_before
== 0)
154 bool zero_ext
= sig_d
[GetSize(sig_d
)-1] == State::S0
;
155 bool sign_ext
= !zero_ext
;
157 for (int i
= 0; i
< GetSize(sig_q
); i
++) {
158 SigBit bit
= sig_q
[i
];
159 if (init_bits
.count(bit
))
160 initval
.bits
.push_back(init_bits
.at(bit
));
162 initval
.bits
.push_back(State::Sx
);
165 for (int i
= GetSize(sig_q
)-1; i
>= 0; i
--)
167 if (zero_ext
&& sig_d
[i
] == State::S0
&& (initval
[i
] == State::S0
|| initval
[i
] == State::Sx
)) {
168 module
->connect(sig_q
[i
], State::S0
);
169 remove_init_bits
.insert(sig_q
[i
]);
175 if (sign_ext
&& i
> 0 && sig_d
[i
] == sig_d
[i
-1] && initval
[i
] == initval
[i
-1]) {
176 module
->connect(sig_q
[i
], sig_q
[i
-1]);
177 remove_init_bits
.insert(sig_q
[i
]);
183 auto info
= mi
.query(sig_q
[i
]);
186 if (!info
->is_output
&& GetSize(info
->ports
) == 1 && !keep_bits
.count(mi
.sigmap(sig_q
[i
]))) {
187 remove_init_bits
.insert(sig_q
[i
]);
198 if (width_before
== GetSize(sig_q
))
201 if (GetSize(sig_q
) == 0) {
202 log("Removed cell %s.%s (%s).\n", log_id(module
), log_id(cell
), log_id(cell
->type
));
203 module
->remove(cell
);
207 log("Removed top %d bits (of %d) from FF cell %s.%s (%s).\n", width_before
- GetSize(sig_q
), width_before
,
208 log_id(module
), log_id(cell
), log_id(cell
->type
));
210 for (auto bit
: sig_d
)
211 work_queue_bits
.insert(bit
);
213 for (auto bit
: sig_q
)
214 work_queue_bits
.insert(bit
);
216 // Narrow ARST_VALUE parameter to new size.
217 if (cell
->parameters
.count("\\ARST_VALUE")) {
218 Const arst_value
= cell
->getParam("\\ARST_VALUE");
219 arst_value
.bits
.resize(GetSize(sig_q
));
220 cell
->setParam("\\ARST_VALUE", arst_value
);
223 cell
->setPort("\\D", sig_d
);
224 cell
->setPort("\\Q", sig_q
);
225 cell
->fixup_parameters();
228 void run_reduce_inport(Cell
*cell
, char port
, int max_port_size
, bool &port_signed
, bool &did_something
)
230 port_signed
= cell
->getParam(stringf("\\%c_SIGNED", port
)).as_bool();
231 SigSpec sig
= mi
.sigmap(cell
->getPort(stringf("\\%c", port
)));
233 if (port
== 'B' && cell
->type
.in("$shl", "$shr", "$sshl", "$sshr"))
236 int bits_removed
= 0;
237 if (GetSize(sig
) > max_port_size
) {
238 bits_removed
= GetSize(sig
) - max_port_size
;
239 for (auto bit
: sig
.extract(max_port_size
, bits_removed
))
240 work_queue_bits
.insert(bit
);
241 sig
= sig
.extract(0, max_port_size
);
245 while (GetSize(sig
) > 1 && sig
[GetSize(sig
)-1] == sig
[GetSize(sig
)-2])
246 work_queue_bits
.insert(sig
[GetSize(sig
)-1]), sig
.remove(GetSize(sig
)-1), bits_removed
++;
248 while (GetSize(sig
) > 1 && sig
[GetSize(sig
)-1] == S0
)
249 work_queue_bits
.insert(sig
[GetSize(sig
)-1]), sig
.remove(GetSize(sig
)-1), bits_removed
++;
253 log("Removed top %d bits (of %d) from port %c of cell %s.%s (%s).\n",
254 bits_removed
, GetSize(sig
) + bits_removed
, port
, log_id(module
), log_id(cell
), log_id(cell
->type
));
255 cell
->setPort(stringf("\\%c", port
), sig
);
256 did_something
= true;
260 void run_cell(Cell
*cell
)
262 bool did_something
= false;
264 if (!cell
->type
.in(config
->supported_cell_types
))
267 if (cell
->type
.in("$mux", "$pmux"))
268 return run_cell_mux(cell
);
270 if (cell
->type
.in("$dff", "$adff"))
271 return run_cell_dff(cell
);
273 SigSpec sig
= mi
.sigmap(cell
->getPort("\\Y"));
279 // Reduce size of ports A and B based on constant input bits and size of output port
281 int max_port_a_size
= cell
->hasPort("\\A") ? GetSize(cell
->getPort("\\A")) : -1;
282 int max_port_b_size
= cell
->hasPort("\\B") ? GetSize(cell
->getPort("\\B")) : -1;
284 if (cell
->type
.in("$not", "$pos", "$neg", "$and", "$or", "$xor", "$add", "$sub")) {
285 max_port_a_size
= min(max_port_a_size
, GetSize(sig
));
286 max_port_b_size
= min(max_port_b_size
, GetSize(sig
));
289 bool port_a_signed
= false;
290 bool port_b_signed
= false;
292 if (max_port_a_size
>= 0 && cell
->type
!= "$shiftx")
293 run_reduce_inport(cell
, 'A', max_port_a_size
, port_a_signed
, did_something
);
295 if (max_port_b_size
>= 0)
296 run_reduce_inport(cell
, 'B', max_port_b_size
, port_b_signed
, did_something
);
298 if (cell
->hasPort("\\A") && cell
->hasPort("\\B") && port_a_signed
&& port_b_signed
) {
299 SigSpec sig_a
= mi
.sigmap(cell
->getPort("\\A")), sig_b
= mi
.sigmap(cell
->getPort("\\B"));
300 if (GetSize(sig_a
) > 0 && sig_a
[GetSize(sig_a
)-1] == State::S0
&&
301 GetSize(sig_b
) > 0 && sig_b
[GetSize(sig_b
)-1] == State::S0
) {
302 log("Converting cell %s.%s (%s) from signed to unsigned.\n",
303 log_id(module
), log_id(cell
), log_id(cell
->type
));
304 cell
->setParam("\\A_SIGNED", 0);
305 cell
->setParam("\\B_SIGNED", 0);
306 port_a_signed
= false;
307 port_b_signed
= false;
308 did_something
= true;
312 if (cell
->hasPort("\\A") && !cell
->hasPort("\\B") && port_a_signed
) {
313 SigSpec sig_a
= mi
.sigmap(cell
->getPort("\\A"));
314 if (GetSize(sig_a
) > 0 && sig_a
[GetSize(sig_a
)-1] == State::S0
) {
315 log("Converting cell %s.%s (%s) from signed to unsigned.\n",
316 log_id(module
), log_id(cell
), log_id(cell
->type
));
317 cell
->setParam("\\A_SIGNED", 0);
318 port_a_signed
= false;
319 did_something
= true;
324 // Reduce size of port Y based on sizes for A and B and unused bits in Y
326 int bits_removed
= 0;
327 if (port_a_signed
&& cell
->type
== "$shr") {
328 // do not reduce size of output on $shr cells with signed A inputs
330 while (GetSize(sig
) > 0)
332 auto bit
= sig
[GetSize(sig
)-1];
333 if (keep_bits
.count(bit
))
336 auto info
= mi
.query(bit
);
337 if (info
->is_output
|| GetSize(info
->ports
) > 1)
340 sig
.remove(GetSize(sig
)-1);
345 if (cell
->type
.in("$pos", "$add", "$mul", "$and", "$or", "$xor"))
347 bool is_signed
= cell
->getParam("\\A_SIGNED").as_bool();
349 int a_size
= 0, b_size
= 0;
350 if (cell
->hasPort("\\A")) a_size
= GetSize(cell
->getPort("\\A"));
351 if (cell
->hasPort("\\B")) b_size
= GetSize(cell
->getPort("\\B"));
353 int max_y_size
= max(a_size
, b_size
);
355 if (cell
->type
== "$add")
358 if (cell
->type
== "$mul")
359 max_y_size
= a_size
+ b_size
;
361 while (GetSize(sig
) > 1 && GetSize(sig
) > max_y_size
) {
362 module
->connect(sig
[GetSize(sig
)-1], is_signed
? sig
[GetSize(sig
)-2] : S0
);
363 sig
.remove(GetSize(sig
)-1);
368 if (GetSize(sig
) == 0) {
369 log("Removed cell %s.%s (%s).\n", log_id(module
), log_id(cell
), log_id(cell
->type
));
370 module
->remove(cell
);
375 log("Removed top %d bits (of %d) from port Y of cell %s.%s (%s).\n",
376 bits_removed
, GetSize(sig
) + bits_removed
, log_id(module
), log_id(cell
), log_id(cell
->type
));
377 cell
->setPort("\\Y", sig
);
378 did_something
= true;
382 cell
->fixup_parameters();
387 static int count_nontrivial_wire_attrs(RTLIL::Wire
*w
)
389 int count
= w
->attributes
.size();
390 count
-= w
->attributes
.count("\\src");
391 count
-= w
->attributes
.count("\\unused_bits");
397 // create a copy as mi.sigmap will be updated as we process the module
398 SigMap init_attr_sigmap
= mi
.sigmap
;
400 for (auto w
: module
->wires()) {
401 if (w
->get_bool_attribute("\\keep"))
402 for (auto bit
: mi
.sigmap(w
))
403 keep_bits
.insert(bit
);
404 if (w
->attributes
.count("\\init")) {
405 Const initval
= w
->attributes
.at("\\init");
406 SigSpec initsig
= init_attr_sigmap(w
);
407 int width
= std::min(GetSize(initval
), GetSize(initsig
));
408 for (int i
= 0; i
< width
; i
++)
409 init_bits
[initsig
[i
]] = initval
[i
];
413 for (auto c
: module
->selected_cells())
414 work_queue_cells
.insert(c
);
416 while (!work_queue_cells
.empty())
418 work_queue_bits
.clear();
419 for (auto c
: work_queue_cells
)
422 work_queue_cells
.clear();
423 for (auto bit
: work_queue_bits
)
424 for (auto port
: mi
.query_ports(bit
))
425 if (module
->selected(port
.cell
))
426 work_queue_cells
.insert(port
.cell
);
429 pool
<SigSpec
> complete_wires
;
430 for (auto w
: module
->wires())
431 complete_wires
.insert(mi
.sigmap(w
));
433 for (auto w
: module
->selected_wires())
435 int unused_top_bits
= 0;
437 if (w
->port_id
> 0 || count_nontrivial_wire_attrs(w
) > 0)
440 for (int i
= GetSize(w
)-1; i
>= 0; i
--) {
442 auto info
= mi
.query(bit
);
443 if (info
&& (info
->is_input
|| info
->is_output
|| GetSize(info
->ports
) > 0))
448 if (unused_top_bits
== 0 || unused_top_bits
== GetSize(w
))
451 if (complete_wires
[mi
.sigmap(w
).extract(0, GetSize(w
) - unused_top_bits
)])
454 log("Removed top %d bits (of %d) from wire %s.%s.\n", unused_top_bits
, GetSize(w
), log_id(module
), log_id(w
));
455 Wire
*nw
= module
->addWire(NEW_ID
, GetSize(w
) - unused_top_bits
);
456 module
->connect(nw
, SigSpec(w
).extract(0, GetSize(nw
)));
457 module
->swap_names(w
, nw
);
460 if (!remove_init_bits
.empty()) {
461 for (auto w
: module
->wires()) {
462 if (w
->attributes
.count("\\init")) {
463 Const initval
= w
->attributes
.at("\\init");
464 Const
new_initval(State::Sx
, GetSize(w
));
465 SigSpec initsig
= init_attr_sigmap(w
);
466 int width
= std::min(GetSize(initval
), GetSize(initsig
));
467 for (int i
= 0; i
< width
; i
++) {
468 if (!remove_init_bits
.count(initsig
[i
]))
469 new_initval
[i
] = initval
[i
];
471 w
->attributes
.at("\\init") = new_initval
;
478 struct WreducePass
: public Pass
{
479 WreducePass() : Pass("wreduce", "reduce the word size of operations if possible") { }
480 void help() YS_OVERRIDE
482 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
484 log(" wreduce [options] [selection]\n");
486 log("This command reduces the word size of operations. For example it will replace\n");
487 log("the 32 bit adders in the following code with adders of more appropriate widths:\n");
489 log(" module test(input [3:0] a, b, c, output [7:0] y);\n");
490 log(" assign y = a + b + c + 1;\n");
496 log(" Do not change the width of memory address ports. Use this options in\n");
497 log(" flows that use the 'memory_memx' pass.\n");
500 log(" Do not optimize explicit don't-care values.\n");
503 void execute(std::vector
<std::string
> args
, Design
*design
) YS_OVERRIDE
505 WreduceConfig config
;
506 bool opt_memx
= false;
508 log_header(design
, "Executing WREDUCE pass (reducing word size of cells).\n");
511 for (argidx
= 1; argidx
< args
.size(); argidx
++) {
512 if (args
[argidx
] == "-memx") {
516 if (args
[argidx
] == "-keepdc") {
517 config
.keepdc
= true;
522 extra_args(args
, argidx
, design
);
524 for (auto module
: design
->selected_modules())
526 if (module
->has_processes_warn())
529 for (auto c
: module
->selected_cells())
531 if (c
->type
.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool",
532 "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
533 "$logic_not", "$logic_and", "$logic_or") && GetSize(c
->getPort("\\Y")) > 1) {
534 SigSpec sig
= c
->getPort("\\Y");
535 if (!sig
.has_const()) {
536 c
->setPort("\\Y", sig
[0]);
537 c
->setParam("\\Y_WIDTH", 1);
539 module
->connect(sig
, Const(0, GetSize(sig
)));
543 if (c
->type
.in("$div", "$mod", "$pow"))
545 SigSpec A
= c
->getPort("\\A");
546 int original_a_width
= GetSize(A
);
547 if (c
->getParam("\\A_SIGNED").as_bool()) {
548 while (GetSize(A
) > 1 && A
[GetSize(A
)-1] == State::S0
&& A
[GetSize(A
)-2] == State::S0
)
549 A
.remove(GetSize(A
)-1, 1);
551 while (GetSize(A
) > 0 && A
[GetSize(A
)-1] == State::S0
)
552 A
.remove(GetSize(A
)-1, 1);
554 if (original_a_width
!= GetSize(A
)) {
555 log("Removed top %d bits (of %d) from port A of cell %s.%s (%s).\n",
556 original_a_width
-GetSize(A
), original_a_width
, log_id(module
), log_id(c
), log_id(c
->type
));
557 c
->setPort("\\A", A
);
558 c
->setParam("\\A_WIDTH", GetSize(A
));
561 SigSpec B
= c
->getPort("\\B");
562 int original_b_width
= GetSize(B
);
563 if (c
->getParam("\\B_SIGNED").as_bool()) {
564 while (GetSize(B
) > 1 && B
[GetSize(B
)-1] == State::S0
&& B
[GetSize(B
)-2] == State::S0
)
565 B
.remove(GetSize(B
)-1, 1);
567 while (GetSize(B
) > 0 && B
[GetSize(B
)-1] == State::S0
)
568 B
.remove(GetSize(B
)-1, 1);
570 if (original_b_width
!= GetSize(B
)) {
571 log("Removed top %d bits (of %d) from port B of cell %s.%s (%s).\n",
572 original_b_width
-GetSize(B
), original_b_width
, log_id(module
), log_id(c
), log_id(c
->type
));
573 c
->setPort("\\B", B
);
574 c
->setParam("\\B_WIDTH", GetSize(B
));
578 if (!opt_memx
&& c
->type
.in("$memrd", "$memwr", "$meminit")) {
579 IdString memid
= c
->getParam("\\MEMID").decode_string();
580 RTLIL::Memory
*mem
= module
->memories
.at(memid
);
581 if (mem
->start_offset
>= 0) {
582 int cur_addrbits
= c
->getParam("\\ABITS").as_int();
583 int max_addrbits
= ceil_log2(mem
->start_offset
+ mem
->size
);
584 if (cur_addrbits
> max_addrbits
) {
585 log("Removed top %d address bits (of %d) from memory %s port %s.%s (%s).\n",
586 cur_addrbits
-max_addrbits
, cur_addrbits
,
587 c
->type
== "$memrd" ? "read" : c
->type
== "$memwr" ? "write" : "init",
588 log_id(module
), log_id(c
), log_id(memid
));
589 c
->setParam("\\ABITS", max_addrbits
);
590 c
->setPort("\\ADDR", c
->getPort("\\ADDR").extract(0, max_addrbits
));
596 WreduceWorker
worker(&config
, module
);
602 PRIVATE_NAMESPACE_END