Merge pull request #1105 from YosysHQ/clifford/fixlogicinit
[yosys.git] / passes / opt / opt_rmdff.cc
1 /*
2 * yosys -- Yosys Open SYnthesis Suite
3 *
4 * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
5 *
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.
9 *
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.
17 *
18 */
19
20 #include "kernel/register.h"
21 #include "kernel/sigtools.h"
22 #include "kernel/log.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25
26 USING_YOSYS_NAMESPACE
27 PRIVATE_NAMESPACE_BEGIN
28
29 SigMap assign_map, dff_init_map;
30 SigSet<RTLIL::Cell*> mux_drivers;
31 dict<SigBit, pool<SigBit>> init_attributes;
32 bool keepdc;
33
34 void remove_init_attr(SigSpec sig)
35 {
36 for (auto bit : assign_map(sig))
37 if (init_attributes.count(bit))
38 for (auto wbit : init_attributes.at(bit))
39 wbit.wire->attributes.at("\\init")[wbit.offset] = State::Sx;
40 }
41
42 bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
43 {
44 SigSpec sig_set, sig_clr;
45 State pol_set, pol_clr;
46
47 if (cell->hasPort("\\S"))
48 sig_set = cell->getPort("\\S");
49
50 if (cell->hasPort("\\R"))
51 sig_clr = cell->getPort("\\R");
52
53 if (cell->hasPort("\\SET"))
54 sig_set = cell->getPort("\\SET");
55
56 if (cell->hasPort("\\CLR"))
57 sig_clr = cell->getPort("\\CLR");
58
59 log_assert(GetSize(sig_set) == GetSize(sig_clr));
60
61 if (cell->type.substr(0,8) == "$_DFFSR_") {
62 pol_set = cell->type[9] == 'P' ? State::S1 : State::S0;
63 pol_clr = cell->type[10] == 'P' ? State::S1 : State::S0;
64 } else
65 if (cell->type.substr(0,11) == "$_DLATCHSR_") {
66 pol_set = cell->type[12] == 'P' ? State::S1 : State::S0;
67 pol_clr = cell->type[13] == 'P' ? State::S1 : State::S0;
68 } else
69 if (cell->type == "$dffsr" || cell->type == "$dlatchsr") {
70 pol_set = cell->parameters["\\SET_POLARITY"].as_bool() ? State::S1 : State::S0;
71 pol_clr = cell->parameters["\\CLR_POLARITY"].as_bool() ? State::S1 : State::S0;
72 } else
73 log_abort();
74
75 State npol_set = pol_set == State::S0 ? State::S1 : State::S0;
76 State npol_clr = pol_clr == State::S0 ? State::S1 : State::S0;
77
78 SigSpec sig_d = cell->getPort("\\D");
79 SigSpec sig_q = cell->getPort("\\Q");
80
81 bool did_something = false;
82 bool proper_sr = false;
83 bool used_pol_set = false;
84 bool used_pol_clr = false;
85 bool hasreset = false;
86 Const reset_val;
87 SigSpec sig_reset;
88
89 for (int i = 0; i < GetSize(sig_set); i++)
90 {
91 SigBit s = sig_set[i], c = sig_clr[i];
92
93 if (s != npol_set || c != npol_clr)
94 hasreset = true;
95
96 if (s == pol_set || c == pol_clr)
97 {
98 log("Constantly %s Q bit %s for SR cell %s (%s) from module %s.\n",
99 s == pol_set ? "set" : "cleared", log_signal(sig_q[i]),
100 log_id(cell), log_id(cell->type), log_id(mod));
101
102 remove_init_attr(sig_q[i]);
103 mod->connect(sig_q[i], s == pol_set ? State::S1 : State::S0);
104 sig_set.remove(i);
105 sig_clr.remove(i);
106 sig_d.remove(i);
107 sig_q.remove(i--);
108 did_something = true;
109 continue;
110 }
111 if (sig_reset.empty() && s.wire != nullptr) sig_reset = s;
112 if (sig_reset.empty() && c.wire != nullptr) sig_reset = c;
113
114 if (s.wire != nullptr && s != sig_reset) proper_sr = true;
115 if (c.wire != nullptr && c != sig_reset) proper_sr = true;
116
117 if ((s.wire == nullptr) != (c.wire == nullptr)) {
118 if (s.wire != nullptr) used_pol_set = true;
119 if (c.wire != nullptr) used_pol_clr = true;
120 reset_val.bits.push_back(c.wire == nullptr ? State::S1 : State::S0);
121 } else
122 proper_sr = true;
123 }
124
125 if (!hasreset)
126 proper_sr = false;
127
128 if (GetSize(sig_set) == 0)
129 {
130 log("Removing %s (%s) from module %s.\n", log_id(cell), log_id(cell->type), log_id(mod));
131 mod->remove(cell);
132 return true;
133 }
134
135 if (cell->type == "$dffsr" || cell->type == "$dlatchsr")
136 {
137 cell->setParam("\\WIDTH", GetSize(sig_d));
138 cell->setPort("\\SET", sig_set);
139 cell->setPort("\\CLR", sig_clr);
140 cell->setPort("\\D", sig_d);
141 cell->setPort("\\Q", sig_q);
142 }
143 else
144 {
145 cell->setPort("\\S", sig_set);
146 cell->setPort("\\R", sig_clr);
147 cell->setPort("\\D", sig_d);
148 cell->setPort("\\Q", sig_q);
149 }
150
151 if (proper_sr)
152 return did_something;
153
154 if (used_pol_set && used_pol_clr && pol_set != pol_clr)
155 return did_something;
156
157 if (cell->type == "$dlatchsr")
158 return did_something;
159
160 State unified_pol = used_pol_set ? pol_set : pol_clr;
161
162 if (cell->type == "$dffsr")
163 {
164 if (hasreset)
165 {
166 log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$adff", log_id(mod));
167
168 cell->type = "$adff";
169 cell->setParam("\\ARST_POLARITY", unified_pol);
170 cell->setParam("\\ARST_VALUE", reset_val);
171 cell->setPort("\\ARST", sig_reset);
172
173 cell->unsetParam("\\SET_POLARITY");
174 cell->unsetParam("\\CLR_POLARITY");
175 cell->unsetPort("\\SET");
176 cell->unsetPort("\\CLR");
177 }
178 else
179 {
180 log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$dff", log_id(mod));
181
182 cell->type = "$dff";
183 cell->unsetParam("\\SET_POLARITY");
184 cell->unsetParam("\\CLR_POLARITY");
185 cell->unsetPort("\\SET");
186 cell->unsetPort("\\CLR");
187 }
188
189 return true;
190 }
191
192 if (!hasreset)
193 {
194 IdString new_type;
195
196 if (cell->type.substr(0,8) == "$_DFFSR_")
197 new_type = stringf("$_DFF_%c_", cell->type[8]);
198 else if (cell->type.substr(0,11) == "$_DLATCHSR_")
199 new_type = stringf("$_DLATCH_%c_", cell->type[11]);
200 else
201 log_abort();
202
203 log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), log_id(new_type), log_id(mod));
204
205 cell->type = new_type;
206 cell->unsetPort("\\S");
207 cell->unsetPort("\\R");
208
209 return true;
210 }
211
212 return did_something;
213 }
214
215 bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
216 {
217 SigSpec sig_e;
218 State on_state, off_state;
219
220 if (dlatch->type == "$dlatch") {
221 sig_e = assign_map(dlatch->getPort("\\EN"));
222 on_state = dlatch->getParam("\\EN_POLARITY").as_bool() ? State::S1 : State::S0;
223 off_state = dlatch->getParam("\\EN_POLARITY").as_bool() ? State::S0 : State::S1;
224 } else
225 if (dlatch->type == "$_DLATCH_P_") {
226 sig_e = assign_map(dlatch->getPort("\\E"));
227 on_state = State::S1;
228 off_state = State::S0;
229 } else
230 if (dlatch->type == "$_DLATCH_N_") {
231 sig_e = assign_map(dlatch->getPort("\\E"));
232 on_state = State::S0;
233 off_state = State::S1;
234 } else
235 log_abort();
236
237 if (sig_e == off_state)
238 {
239 RTLIL::Const val_init;
240 for (auto bit : dff_init_map(dlatch->getPort("\\Q")))
241 val_init.bits.push_back(bit.wire == NULL ? bit.data : State::Sx);
242 mod->connect(dlatch->getPort("\\Q"), val_init);
243 goto delete_dlatch;
244 }
245
246 if (sig_e == on_state)
247 {
248 mod->connect(dlatch->getPort("\\Q"), dlatch->getPort("\\D"));
249 goto delete_dlatch;
250 }
251
252 return false;
253
254 delete_dlatch:
255 log("Removing %s (%s) from module %s.\n", log_id(dlatch), log_id(dlatch->type), log_id(mod));
256 remove_init_attr(dlatch->getPort("\\Q"));
257 mod->remove(dlatch);
258 return true;
259 }
260
261 bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
262 {
263 RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r, sig_e;
264 RTLIL::Const val_cp, val_rp, val_rv, val_ep;
265
266 if (dff->type == "$_FF_") {
267 sig_d = dff->getPort("\\D");
268 sig_q = dff->getPort("\\Q");
269 }
270 else if (dff->type == "$_DFF_N_" || dff->type == "$_DFF_P_") {
271 sig_d = dff->getPort("\\D");
272 sig_q = dff->getPort("\\Q");
273 sig_c = dff->getPort("\\C");
274 val_cp = RTLIL::Const(dff->type == "$_DFF_P_", 1);
275 }
276 else if (dff->type.substr(0,6) == "$_DFF_" && dff->type.substr(9) == "_" &&
277 (dff->type[6] == 'N' || dff->type[6] == 'P') &&
278 (dff->type[7] == 'N' || dff->type[7] == 'P') &&
279 (dff->type[8] == '0' || dff->type[8] == '1')) {
280 sig_d = dff->getPort("\\D");
281 sig_q = dff->getPort("\\Q");
282 sig_c = dff->getPort("\\C");
283 sig_r = dff->getPort("\\R");
284 val_cp = RTLIL::Const(dff->type[6] == 'P', 1);
285 val_rp = RTLIL::Const(dff->type[7] == 'P', 1);
286 val_rv = RTLIL::Const(dff->type[8] == '1', 1);
287 }
288 else if (dff->type.substr(0,7) == "$_DFFE_" && dff->type.substr(9) == "_" &&
289 (dff->type[7] == 'N' || dff->type[7] == 'P') &&
290 (dff->type[8] == 'N' || dff->type[8] == 'P')) {
291 sig_d = dff->getPort("\\D");
292 sig_q = dff->getPort("\\Q");
293 sig_c = dff->getPort("\\C");
294 sig_e = dff->getPort("\\E");
295 val_cp = RTLIL::Const(dff->type[7] == 'P', 1);
296 val_ep = RTLIL::Const(dff->type[8] == 'P', 1);
297 }
298 else if (dff->type == "$ff") {
299 sig_d = dff->getPort("\\D");
300 sig_q = dff->getPort("\\Q");
301 }
302 else if (dff->type == "$dff") {
303 sig_d = dff->getPort("\\D");
304 sig_q = dff->getPort("\\Q");
305 sig_c = dff->getPort("\\CLK");
306 val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1);
307 }
308 else if (dff->type == "$dffe") {
309 sig_e = dff->getPort("\\EN");
310 sig_d = dff->getPort("\\D");
311 sig_q = dff->getPort("\\Q");
312 sig_c = dff->getPort("\\CLK");
313 val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1);
314 val_ep = RTLIL::Const(dff->parameters["\\EN_POLARITY"].as_bool(), 1);
315 }
316 else if (dff->type == "$adff") {
317 sig_d = dff->getPort("\\D");
318 sig_q = dff->getPort("\\Q");
319 sig_c = dff->getPort("\\CLK");
320 sig_r = dff->getPort("\\ARST");
321 val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1);
322 val_rp = RTLIL::Const(dff->parameters["\\ARST_POLARITY"].as_bool(), 1);
323 val_rv = dff->parameters["\\ARST_VALUE"];
324 }
325 else
326 log_abort();
327
328 assign_map.apply(sig_d);
329 assign_map.apply(sig_q);
330 assign_map.apply(sig_c);
331 assign_map.apply(sig_r);
332
333 bool has_init = false;
334 RTLIL::Const val_init;
335 for (auto bit : dff_init_map(sig_q).to_sigbit_vector()) {
336 if (bit.wire == NULL || keepdc)
337 has_init = true;
338 val_init.bits.push_back(bit.wire == NULL ? bit.data : RTLIL::State::Sx);
339 }
340
341 if (dff->type.in("$ff", "$dff") && mux_drivers.has(sig_d)) {
342 std::set<RTLIL::Cell*> muxes;
343 mux_drivers.find(sig_d, muxes);
344 for (auto mux : muxes) {
345 RTLIL::SigSpec sig_a = assign_map(mux->getPort("\\A"));
346 RTLIL::SigSpec sig_b = assign_map(mux->getPort("\\B"));
347 if (sig_a == sig_q && sig_b.is_fully_const() && (!has_init || val_init == sig_b.as_const())) {
348 mod->connect(sig_q, sig_b);
349 goto delete_dff;
350 }
351 if (sig_b == sig_q && sig_a.is_fully_const() && (!has_init || val_init == sig_a.as_const())) {
352 mod->connect(sig_q, sig_a);
353 goto delete_dff;
354 }
355 }
356 }
357
358 // If clock is driven by a constant and (i) no reset signal
359 // (ii) Q has no initial value
360 // (iii) initial value is same as reset value
361 if (!sig_c.empty() && sig_c.is_fully_const() && (!sig_r.size() || !has_init || val_init == val_rv)) {
362 if (val_rv.bits.size() == 0)
363 val_rv = val_init;
364 // Q is permanently reset value or initial value
365 mod->connect(sig_q, val_rv);
366 goto delete_dff;
367 }
368
369 // If D is fully undefined and reset signal present and (i) Q has no initial value
370 // (ii) initial value is same as reset value
371 if (sig_d.is_fully_undef() && sig_r.size() && (!has_init || val_init == val_rv)) {
372 // Q is permanently reset value
373 mod->connect(sig_q, val_rv);
374 goto delete_dff;
375 }
376
377 // If D is fully undefined and no reset signal and Q has an initial value
378 if (sig_d.is_fully_undef() && !sig_r.size() && has_init) {
379 // Q is permanently initial value
380 mod->connect(sig_q, val_init);
381 goto delete_dff;
382 }
383
384 // If D is fully constant and (i) no reset signal
385 // (ii) reset value is same as constant D
386 // and (a) has no initial value
387 // (b) initial value same as constant D
388 if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) {
389 // Q is permanently D
390 mod->connect(sig_q, sig_d);
391 goto delete_dff;
392 }
393
394 // If D input is same as Q output and (i) no reset signal
395 // (ii) no initial signal
396 // (iii) initial value is same as reset value
397 if (sig_d == sig_q && (sig_r.empty() || !has_init || val_init == val_rv)) {
398 // Q is permanently reset value or initial value
399 if (sig_r.size())
400 mod->connect(sig_q, val_rv);
401 else if (has_init)
402 mod->connect(sig_q, val_init);
403 goto delete_dff;
404 }
405
406 // If reset signal is present, and is fully constant
407 if (!sig_r.empty() && sig_r.is_fully_const())
408 {
409 // If reset value is permanently active or if reset is undefined
410 if (sig_r == val_rp || sig_r.is_fully_undef()) {
411 // Q is permanently reset value
412 mod->connect(sig_q, val_rv);
413 goto delete_dff;
414 }
415
416 log("Removing unused reset from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
417
418 if (dff->type == "$adff") {
419 dff->type = "$dff";
420 dff->unsetPort("\\ARST");
421 dff->unsetParam("\\ARST_POLARITY");
422 dff->unsetParam("\\ARST_VALUE");
423 return true;
424 }
425
426 log_assert(dff->type.substr(0,6) == "$_DFF_");
427 dff->type = stringf("$_DFF_%c_", + dff->type[6]);
428 dff->unsetPort("\\R");
429 }
430
431 // If enable signal is present, and is fully constant
432 if (!sig_e.empty() && sig_e.is_fully_const())
433 {
434 // If enable value is permanently inactive
435 if (sig_e != val_ep) {
436 // Q is permanently initial value
437 mod->connect(sig_q, val_init);
438 goto delete_dff;
439 }
440
441 log("Removing unused enable from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
442
443 if (dff->type == "$dffe") {
444 dff->type = "$dff";
445 dff->unsetPort("\\EN");
446 dff->unsetParam("\\EN_POLARITY");
447 return true;
448 }
449
450 log_assert(dff->type.substr(0,7) == "$_DFFE_");
451 dff->type = stringf("$_DFF_%c_", + dff->type[7]);
452 dff->unsetPort("\\E");
453 }
454
455 return false;
456
457 delete_dff:
458 log("Removing %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
459 remove_init_attr(dff->getPort("\\Q"));
460 mod->remove(dff);
461 return true;
462 }
463
464 struct OptRmdffPass : public Pass {
465 OptRmdffPass() : Pass("opt_rmdff", "remove DFFs with constant inputs") { }
466 void help() YS_OVERRIDE
467 {
468 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
469 log("\n");
470 log(" opt_rmdff [-keepdc] [selection]\n");
471 log("\n");
472 log("This pass identifies flip-flops with constant inputs and replaces them with\n");
473 log("a constant driver.\n");
474 log("\n");
475 }
476 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
477 {
478 int total_count = 0, total_initdrv = 0;
479 log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n");
480
481 keepdc = false;
482
483 size_t argidx;
484 for (argidx = 1; argidx < args.size(); argidx++) {
485 if (args[argidx] == "-keepdc") {
486 keepdc = true;
487 continue;
488 }
489 break;
490 }
491 extra_args(args, argidx, design);
492
493 for (auto module : design->selected_modules())
494 {
495 pool<SigBit> driven_bits;
496 dict<SigBit, State> init_bits;
497
498 assign_map.set(module);
499 dff_init_map.set(module);
500 mux_drivers.clear();
501 init_attributes.clear();
502
503 for (auto wire : module->wires())
504 {
505 if (wire->attributes.count("\\init") != 0) {
506 Const initval = wire->attributes.at("\\init");
507 for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++)
508 if (initval[i] == State::S0 || initval[i] == State::S1)
509 dff_init_map.add(SigBit(wire, i), initval[i]);
510 for (int i = 0; i < GetSize(wire); i++) {
511 SigBit wire_bit(wire, i), mapped_bit = assign_map(wire_bit);
512 if (mapped_bit.wire) {
513 init_attributes[mapped_bit].insert(wire_bit);
514 if (i < GetSize(initval))
515 init_bits[mapped_bit] = initval[i];
516 }
517 }
518 }
519
520 if (wire->port_input) {
521 for (auto bit : assign_map(wire))
522 driven_bits.insert(bit);
523 }
524 }
525 mux_drivers.clear();
526
527 std::vector<RTLIL::IdString> dff_list;
528 std::vector<RTLIL::IdString> dffsr_list;
529 std::vector<RTLIL::IdString> dlatch_list;
530 for (auto cell : module->cells())
531 {
532 for (auto &conn : cell->connections())
533 if (cell->output(conn.first) || !cell->known())
534 for (auto bit : assign_map(conn.second))
535 driven_bits.insert(bit);
536
537 if (cell->type == "$mux" || cell->type == "$pmux") {
538 if (cell->getPort("\\A").size() == cell->getPort("\\B").size())
539 mux_drivers.insert(assign_map(cell->getPort("\\Y")), cell);
540 continue;
541 }
542
543 if (!design->selected(module, cell))
544 continue;
545
546 if (cell->type.in("$_DFFSR_NNN_", "$_DFFSR_NNP_", "$_DFFSR_NPN_", "$_DFFSR_NPP_",
547 "$_DFFSR_PNN_", "$_DFFSR_PNP_", "$_DFFSR_PPN_", "$_DFFSR_PPP_", "$dffsr",
548 "$_DLATCHSR_NNN_", "$_DLATCHSR_NNP_", "$_DLATCHSR_NPN_", "$_DLATCHSR_NPP_",
549 "$_DLATCHSR_PNN_", "$_DLATCHSR_PNP_", "$_DLATCHSR_PPN_", "$_DLATCHSR_PPP_", "$dlatchsr"))
550 dffsr_list.push_back(cell->name);
551
552 if (cell->type.in("$_FF_", "$_DFF_N_", "$_DFF_P_",
553 "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
554 "$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_",
555 "$_DFFE_NN_", "$_DFFE_NP_", "$_DFFE_PN_", "$_DFFE_PP_",
556 "$ff", "$dff", "$dffe", "$adff"))
557 dff_list.push_back(cell->name);
558
559 if (cell->type.in("$dlatch", "$_DLATCH_P_", "$_DLATCH_N_"))
560 dlatch_list.push_back(cell->name);
561 }
562
563 for (auto &id : dffsr_list) {
564 if (module->cell(id) != nullptr &&
565 handle_dffsr(module, module->cells_[id]))
566 total_count++;
567 }
568
569 for (auto &id : dff_list) {
570 if (module->cell(id) != nullptr &&
571 handle_dff(module, module->cells_[id]))
572 total_count++;
573 }
574
575 for (auto &id : dlatch_list) {
576 if (module->cell(id) != nullptr &&
577 handle_dlatch(module, module->cells_[id]))
578 total_count++;
579 }
580
581 SigSpec const_init_sigs;
582
583 for (auto bit : init_bits)
584 if (!driven_bits.count(bit.first))
585 const_init_sigs.append(bit.first);
586
587 const_init_sigs.sort_and_unify();
588
589 for (SigSpec sig : const_init_sigs.chunks())
590 {
591 Const val;
592
593 for (auto bit : sig)
594 val.bits.push_back(init_bits.at(bit));
595
596 log("Promoting init spec %s = %s to constant driver in module %s.\n",
597 log_signal(sig), log_signal(val), log_id(module));
598
599 module->connect(sig, val);
600 remove_init_attr(sig);
601 total_initdrv++;
602 }
603 }
604
605 assign_map.clear();
606 mux_drivers.clear();
607 init_attributes.clear();
608
609 if (total_count || total_initdrv)
610 design->scratchpad_set_bool("opt.did_something", true);
611
612 if (total_initdrv)
613 log("Promoted %d init specs to constant drivers.\n", total_initdrv);
614
615 if (total_count)
616 log("Replaced %d DFF cells.\n", total_count);
617 }
618 } OptRmdffPass;
619
620 PRIVATE_NAMESPACE_END