Merge pull request #1299 from YosysHQ/eddie/cleanup2
[yosys.git] / passes / opt / pmux2shiftx.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/yosys.h"
21 #include "kernel/sigtools.h"
22
23 USING_YOSYS_NAMESPACE
24 PRIVATE_NAMESPACE_BEGIN
25
26 struct OnehotDatabase
27 {
28 Module *module;
29 const SigMap &sigmap;
30 bool verbose = false;
31 bool initialized = false;
32
33 pool<SigBit> init_ones;
34 dict<SigSpec, pool<SigSpec>> sig_sources_db;
35 dict<SigSpec, bool> sig_onehot_cache;
36 pool<SigSpec> recursion_guard;
37
38 OnehotDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap)
39 {
40 }
41
42 void initialize()
43 {
44 log_assert(!initialized);
45 initialized = true;
46
47 for (auto wire : module->wires())
48 {
49 auto it = wire->attributes.find(ID(init));
50 if (it == wire->attributes.end())
51 continue;
52
53 auto &val = it->second;
54 int width = std::max(GetSize(wire), GetSize(val));
55
56 for (int i = 0; i < width; i++)
57 if (val[i] == State::S1)
58 init_ones.insert(sigmap(SigBit(wire, i)));
59 }
60
61 for (auto cell : module->cells())
62 {
63 vector<SigSpec> inputs;
64 SigSpec output;
65
66 if (cell->type.in(ID($adff), ID($dff), ID($dffe), ID($dlatch), ID($ff)))
67 {
68 output = cell->getPort(ID(Q));
69 if (cell->type == ID($adff))
70 inputs.push_back(cell->getParam(ID(ARST_VALUE)));
71 inputs.push_back(cell->getPort(ID(D)));
72 }
73
74 if (cell->type.in(ID($mux), ID($pmux)))
75 {
76 output = cell->getPort(ID(Y));
77 inputs.push_back(cell->getPort(ID(A)));
78 SigSpec B = cell->getPort(ID(B));
79 for (int i = 0; i < GetSize(B); i += GetSize(output))
80 inputs.push_back(B.extract(i, GetSize(output)));
81 }
82
83 if (!output.empty())
84 {
85 output = sigmap(output);
86 auto &srcs = sig_sources_db[output];
87 for (auto src : inputs) {
88 while (!src.empty() && src[GetSize(src)-1] == State::S0)
89 src.remove(GetSize(src)-1);
90 srcs.insert(sigmap(src));
91 }
92 }
93 }
94 }
95
96 void query_worker(const SigSpec &sig, bool &retval, bool &cache, int indent)
97 {
98 if (verbose)
99 log("%*s %s\n", indent, "", log_signal(sig));
100 log_assert(retval);
101
102 if (recursion_guard.count(sig)) {
103 if (verbose)
104 log("%*s - recursion\n", indent, "");
105 cache = false;
106 return;
107 }
108
109 auto it = sig_onehot_cache.find(sig);
110 if (it != sig_onehot_cache.end()) {
111 if (verbose)
112 log("%*s - cached (%s)\n", indent, "", it->second ? "true" : "false");
113 if (!it->second)
114 retval = false;
115 return;
116 }
117
118 bool found_init_ones = false;
119 for (auto bit : sig) {
120 if (init_ones.count(bit)) {
121 if (found_init_ones) {
122 if (verbose)
123 log("%*s - non-onehot init value\n", indent, "");
124 retval = false;
125 break;
126 }
127 found_init_ones = true;
128 }
129 }
130
131 if (retval)
132 {
133 if (sig.is_fully_const())
134 {
135 bool found_ones = false;
136 for (auto bit : sig) {
137 if (bit == State::S1) {
138 if (found_ones) {
139 if (verbose)
140 log("%*s - non-onehot constant\n", indent, "");
141 retval = false;
142 break;
143 }
144 found_ones = true;
145 }
146 }
147 }
148 else
149 {
150 auto srcs = sig_sources_db.find(sig);
151 if (srcs == sig_sources_db.end()) {
152 if (verbose)
153 log("%*s - no sources for non-const signal\n", indent, "");
154 retval = false;
155 } else {
156 for (auto &src : srcs->second) {
157 bool child_cache = true;
158 recursion_guard.insert(sig);
159 query_worker(src, retval, child_cache, indent+4);
160 recursion_guard.erase(sig);
161 if (!child_cache)
162 cache = false;
163 if (!retval)
164 break;
165 }
166 }
167 }
168 }
169
170 // it is always safe to cache a negative result
171 if (cache || !retval)
172 sig_onehot_cache[sig] = retval;
173 }
174
175 bool query(const SigSpec &sig)
176 {
177 bool retval = true;
178 bool cache = true;
179
180 if (verbose)
181 log("** ONEHOT QUERY START (%s)\n", log_signal(sig));
182
183 if (!initialized)
184 initialize();
185
186 query_worker(sig, retval, cache, 3);
187
188 if (verbose)
189 log("** ONEHOT QUERY RESULT = %s\n", retval ? "true" : "false");
190
191 // it is always safe to cache the root result of a query
192 if (!cache)
193 sig_onehot_cache[sig] = retval;
194
195 return retval;
196 }
197 };
198
199 struct Pmux2ShiftxPass : public Pass {
200 Pmux2ShiftxPass() : Pass("pmux2shiftx", "transform $pmux cells to $shiftx cells") { }
201 void help() YS_OVERRIDE
202 {
203 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
204 log("\n");
205 log(" pmux2shiftx [options] [selection]\n");
206 log("\n");
207 log("This pass transforms $pmux cells to $shiftx cells.\n");
208 log("\n");
209 log(" -v, -vv\n");
210 log(" verbose output\n");
211 log("\n");
212 log(" -min_density <percentage>\n");
213 log(" specifies the minimum density for the shifter\n");
214 log(" default: 50\n");
215 log("\n");
216 log(" -min_choices <int>\n");
217 log(" specified the minimum number of choices for a control signal\n");
218 log(" default: 3\n");
219 log("\n");
220 log(" -onehot ignore|pmux|shiftx\n");
221 log(" select strategy for one-hot encoded control signals\n");
222 log(" default: pmux\n");
223 log("\n");
224 log(" -norange\n");
225 log(" disable $sub inference for \"range decoders\"\n");
226 log("\n");
227 }
228 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
229 {
230 int min_density = 50;
231 int min_choices = 3;
232 bool allow_onehot = false;
233 bool optimize_onehot = true;
234 bool verbose = false;
235 bool verbose_onehot = false;
236 bool norange = false;
237
238 log_header(design, "Executing PMUX2SHIFTX pass.\n");
239
240 size_t argidx;
241 for (argidx = 1; argidx < args.size(); argidx++) {
242 if (args[argidx] == "-min_density" && argidx+1 < args.size()) {
243 min_density = atoi(args[++argidx].c_str());
244 continue;
245 }
246 if (args[argidx] == "-min_choices" && argidx+1 < args.size()) {
247 min_choices = atoi(args[++argidx].c_str());
248 continue;
249 }
250 if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "ignore") {
251 argidx++;
252 allow_onehot = false;
253 optimize_onehot = false;
254 continue;
255 }
256 if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "pmux") {
257 argidx++;
258 allow_onehot = false;
259 optimize_onehot = true;
260 continue;
261 }
262 if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "shiftx") {
263 argidx++;
264 allow_onehot = true;
265 optimize_onehot = false;
266 continue;
267 }
268 if (args[argidx] == "-v") {
269 verbose = true;
270 continue;
271 }
272 if (args[argidx] == "-vv") {
273 verbose = true;
274 verbose_onehot = true;
275 continue;
276 }
277 if (args[argidx] == "-norange") {
278 norange = true;
279 continue;
280 }
281 break;
282 }
283 extra_args(args, argidx, design);
284
285 for (auto module : design->selected_modules())
286 {
287 SigMap sigmap(module);
288 OnehotDatabase onehot_db(module, sigmap);
289 onehot_db.verbose = verbose_onehot;
290
291 dict<SigBit, pair<SigSpec, Const>> eqdb;
292
293 for (auto cell : module->cells())
294 {
295 if (cell->type == ID($eq))
296 {
297 dict<SigBit, State> bits;
298
299 SigSpec A = sigmap(cell->getPort(ID(A)));
300 SigSpec B = sigmap(cell->getPort(ID(B)));
301
302 int a_width = cell->getParam(ID(A_WIDTH)).as_int();
303 int b_width = cell->getParam(ID(B_WIDTH)).as_int();
304
305 if (a_width < b_width) {
306 bool a_signed = cell->getParam(ID(A_SIGNED)).as_int();
307 A.extend_u0(b_width, a_signed);
308 }
309
310 if (b_width < a_width) {
311 bool b_signed = cell->getParam(ID(B_SIGNED)).as_int();
312 B.extend_u0(a_width, b_signed);
313 }
314
315 for (int i = 0; i < GetSize(A); i++) {
316 SigBit a_bit = A[i], b_bit = B[i];
317 if (b_bit.wire && !a_bit.wire) {
318 std::swap(a_bit, b_bit);
319 }
320 if (!a_bit.wire || b_bit.wire)
321 goto next_cell;
322 if (bits.count(a_bit))
323 goto next_cell;
324 bits[a_bit] = b_bit.data;
325 }
326
327 if (GetSize(bits) > 20)
328 goto next_cell;
329
330 bits.sort();
331 pair<SigSpec, Const> entry;
332
333 for (auto it : bits) {
334 entry.first.append_bit(it.first);
335 entry.second.bits.push_back(it.second);
336 }
337
338 eqdb[sigmap(cell->getPort(ID(Y))[0])] = entry;
339 goto next_cell;
340 }
341
342 if (cell->type == ID($logic_not))
343 {
344 dict<SigBit, State> bits;
345
346 SigSpec A = sigmap(cell->getPort(ID(A)));
347
348 for (int i = 0; i < GetSize(A); i++)
349 bits[A[i]] = State::S0;
350
351 bits.sort();
352 pair<SigSpec, Const> entry;
353
354 for (auto it : bits) {
355 entry.first.append_bit(it.first);
356 entry.second.bits.push_back(it.second);
357 }
358
359 eqdb[sigmap(cell->getPort(ID(Y))[0])] = entry;
360 goto next_cell;
361 }
362 next_cell:;
363 }
364
365 for (auto cell : module->selected_cells())
366 {
367 if (cell->type != ID($pmux))
368 continue;
369
370 string src = cell->get_src_attribute();
371 int width = cell->getParam(ID(WIDTH)).as_int();
372 int width_bits = ceil_log2(width);
373 int extwidth = width;
374
375 while (extwidth & (extwidth-1))
376 extwidth++;
377
378 dict<SigSpec, pool<int>> seldb;
379
380 SigSpec A = cell->getPort(ID(A));
381 SigSpec B = cell->getPort(ID(B));
382 SigSpec S = sigmap(cell->getPort(ID(S)));
383 for (int i = 0; i < GetSize(S); i++)
384 {
385 if (!eqdb.count(S[i]))
386 continue;
387
388 auto &entry = eqdb.at(S[i]);
389 seldb[entry.first].insert(i);
390 }
391
392 if (seldb.empty())
393 continue;
394
395 bool printed_pmux_header = false;
396
397 if (verbose) {
398 printed_pmux_header = true;
399 log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell));
400 log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits);
401 }
402
403 SigSpec updated_S = cell->getPort(ID(S));
404 SigSpec updated_B = cell->getPort(ID(B));
405
406 while (!seldb.empty())
407 {
408 // pick the largest entry in seldb
409 SigSpec sig = seldb.begin()->first;
410 for (auto &it : seldb) {
411 if (GetSize(sig) < GetSize(it.first))
412 sig = it.first;
413 else if (GetSize(seldb.at(sig)) < GetSize(it.second))
414 sig = it.first;
415 }
416
417 // find the relevant choices
418 bool is_onehot = GetSize(sig) > 2;
419 dict<Const, int> choices;
420 for (int i : seldb.at(sig)) {
421 Const val = eqdb.at(S[i]).second;
422 int onebits = 0;
423 for (auto b : val.bits)
424 if (b == State::S1)
425 onebits++;
426 if (onebits > 1)
427 is_onehot = false;
428 choices[val] = i;
429 }
430
431 bool full_pmux = GetSize(choices) == GetSize(S);
432
433 // TBD: also find choices that are using signals that are subsets of the bits in "sig"
434
435 if (!verbose)
436 {
437 if (is_onehot && !allow_onehot && !optimize_onehot) {
438 seldb.erase(sig);
439 continue;
440 }
441
442 if (GetSize(choices) < min_choices) {
443 seldb.erase(sig);
444 continue;
445 }
446 }
447
448 if (!printed_pmux_header) {
449 printed_pmux_header = true;
450 log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell));
451 log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits);
452 }
453
454 log(" checking ctrl signal %s\n", log_signal(sig));
455
456 auto print_choices = [&]() {
457 log(" table of choices:\n");
458 for (auto &it : choices)
459 log(" %3d: %s: %s\n", it.second, log_signal(it.first),
460 log_signal(B.extract(it.second*width, width)));
461 };
462
463 if (verbose)
464 {
465 if (is_onehot && !allow_onehot && !optimize_onehot) {
466 print_choices();
467 log(" ignoring one-hot encoding.\n");
468 seldb.erase(sig);
469 continue;
470 }
471
472 if (GetSize(choices) < min_choices) {
473 print_choices();
474 log(" insufficient choices.\n");
475 seldb.erase(sig);
476 continue;
477 }
478 }
479
480 if (is_onehot && optimize_onehot)
481 {
482 print_choices();
483 if (!onehot_db.query(sig))
484 {
485 log(" failed to detect onehot driver. do not optimize.\n");
486 }
487 else
488 {
489 log(" optimizing one-hot encoding.\n");
490 for (auto &it : choices)
491 {
492 const Const &val = it.first;
493 int index = -1;
494
495 for (int i = 0; i < GetSize(val); i++)
496 if (val[i] == State::S1) {
497 log_assert(index < 0);
498 index = i;
499 }
500
501 if (index < 0) {
502 log(" %3d: zero encoding.\n", it.second);
503 continue;
504 }
505
506 SigBit new_ctrl = sig[index];
507 log(" %3d: new crtl signal is %s.\n", it.second, log_signal(new_ctrl));
508 updated_S[it.second] = new_ctrl;
509 }
510 }
511 seldb.erase(sig);
512 continue;
513 }
514
515 // find the best permutation
516 vector<int> perm_new_from_old(GetSize(sig));
517 Const perm_xormask(State::S0, GetSize(sig));
518 {
519 vector<int> values(GetSize(choices));
520 vector<bool> used_src_columns(GetSize(sig));
521 vector<vector<bool>> columns(GetSize(sig), vector<bool>(GetSize(values)));
522
523 for (int i = 0; i < GetSize(choices); i++) {
524 Const val = choices.element(i)->first;
525 for (int k = 0; k < GetSize(val); k++)
526 if (val[k] == State::S1)
527 columns[k][i] = true;
528 }
529
530 for (int dst_col = GetSize(sig)-1; dst_col >= 0; dst_col--)
531 {
532 int best_src_col = -1;
533 bool best_inv = false;
534 int best_maxval = 0;
535 int best_delta = 0;
536
537 // find best src column for this dst column
538 for (int src_col = 0; src_col < GetSize(sig); src_col++)
539 {
540 if (used_src_columns[src_col])
541 continue;
542
543 int this_maxval = 0;
544 int this_minval = 1 << 30;
545
546 int this_inv_maxval = 0;
547 int this_inv_minval = 1 << 30;
548
549 for (int i = 0; i < GetSize(values); i++)
550 {
551 int val = values[i];
552 int inv_val = val;
553
554 if (columns[src_col][i])
555 val |= 1 << dst_col;
556 else
557 inv_val |= 1 << dst_col;
558
559 this_maxval = std::max(this_maxval, val);
560 this_minval = std::min(this_minval, val);
561
562 this_inv_maxval = std::max(this_inv_maxval, inv_val);
563 this_inv_minval = std::min(this_inv_minval, inv_val);
564 }
565
566 int this_delta = this_maxval - this_minval;
567 int this_inv_delta = this_maxval - this_minval;
568 bool this_inv = false;
569
570 if (!norange && this_delta != this_inv_delta)
571 this_inv = this_inv_delta < this_delta;
572 else if (this_maxval != this_inv_maxval)
573 this_inv = this_inv_maxval < this_maxval;
574
575 if (this_inv) {
576 this_delta = this_inv_delta;
577 this_maxval = this_inv_maxval;
578 this_minval = this_inv_minval;
579 }
580
581 bool this_is_better = false;
582
583 if (best_src_col < 0)
584 this_is_better = true;
585 else if (!norange && this_delta != best_delta)
586 this_is_better = this_delta < best_delta;
587 else if (this_maxval != best_maxval)
588 this_is_better = this_maxval < best_maxval;
589 else
590 this_is_better = sig[best_src_col] < sig[src_col];
591
592 if (this_is_better) {
593 best_src_col = src_col;
594 best_inv = this_inv;
595 best_maxval = this_maxval;
596 best_delta = this_delta;
597 }
598 }
599
600 used_src_columns[best_src_col] = true;
601 perm_new_from_old[dst_col] = best_src_col;
602 perm_xormask[dst_col] = best_inv ? State::S1 : State::S0;
603 }
604 }
605
606 // permutated sig
607 SigSpec perm_sig(State::S0, GetSize(sig));
608 for (int i = 0; i < GetSize(sig); i++)
609 perm_sig[i] = sig[perm_new_from_old[i]];
610
611 log(" best permutation: %s\n", log_signal(perm_sig));
612 log(" best xor mask: %s\n", log_signal(perm_xormask));
613
614 // permutated choices
615 int min_choice = 1 << 30;
616 int max_choice = -1;
617 dict<Const, int> perm_choices;
618
619 for (auto &it : choices)
620 {
621 Const &old_c = it.first;
622 Const new_c(State::S0, GetSize(old_c));
623
624 for (int i = 0; i < GetSize(old_c); i++)
625 new_c[i] = old_c[perm_new_from_old[i]];
626
627 Const new_c_before_xor = new_c;
628 new_c = const_xor(new_c, perm_xormask, false, false, GetSize(new_c));
629
630 perm_choices[new_c] = it.second;
631
632 min_choice = std::min(min_choice, new_c.as_int());
633 max_choice = std::max(max_choice, new_c.as_int());
634
635 log(" %3d: %s -> %s -> %s: %s\n", it.second, log_signal(old_c), log_signal(new_c_before_xor),
636 log_signal(new_c), log_signal(B.extract(it.second*width, width)));
637 }
638
639 int range_density = 100*GetSize(choices) / (max_choice-min_choice+1);
640 int absolute_density = 100*GetSize(choices) / (max_choice+1);
641
642 log(" choices: %d\n", GetSize(choices));
643 log(" min choice: %d\n", min_choice);
644 log(" max choice: %d\n", max_choice);
645 log(" range density: %d%%\n", range_density);
646 log(" absolute density: %d%%\n", absolute_density);
647
648 if (full_pmux) {
649 int full_density = 100*GetSize(choices) / (1 << GetSize(sig));
650 log(" full density: %d%%\n", full_density);
651 if (full_density < min_density) {
652 full_pmux = false;
653 } else {
654 min_choice = 0;
655 max_choice = (1 << GetSize(sig))-1;
656 log(" update to full case.\n");
657 log(" new min choice: %d\n", min_choice);
658 log(" new max choice: %d\n", max_choice);
659 }
660 }
661
662 bool full_case = (min_choice == 0) && (max_choice == (1 << GetSize(sig))-1) && (full_pmux || max_choice+1 == GetSize(choices));
663 log(" full case: %s\n", full_case ? "true" : "false");
664
665 // check density percentages
666 Const offset(State::S0, GetSize(sig));
667 if (!norange && absolute_density < min_density && range_density >= min_density)
668 {
669 offset = Const(min_choice, GetSize(sig));
670 log(" offset: %s\n", log_signal(offset));
671
672 min_choice -= offset.as_int();
673 max_choice -= offset.as_int();
674
675 dict<Const, int> new_perm_choices;
676 for (auto &it : perm_choices)
677 new_perm_choices[const_sub(it.first, offset, false, false, GetSize(sig))] = it.second;
678 perm_choices.swap(new_perm_choices);
679 } else
680 if (absolute_density < min_density) {
681 log(" insufficient density.\n");
682 seldb.erase(sig);
683 continue;
684 }
685
686 // creat cmp signal
687 SigSpec cmp = perm_sig;
688 if (perm_xormask.as_bool())
689 cmp = module->Xor(NEW_ID, cmp, perm_xormask, false, src);
690 if (offset.as_bool())
691 cmp = module->Sub(NEW_ID, cmp, offset, false, src);
692
693 // create enable signal
694 SigBit en = State::S1;
695 if (!full_case) {
696 Const enable_mask(State::S0, max_choice+1);
697 for (auto &it : perm_choices)
698 enable_mask[it.first.as_int()] = State::S1;
699 en = module->addWire(NEW_ID);
700 module->addShift(NEW_ID, enable_mask, cmp, en, false, src);
701 }
702
703 // create data signal
704 SigSpec data(State::Sx, (max_choice+1)*extwidth);
705 if (full_pmux) {
706 for (int i = 0; i <= max_choice; i++)
707 data.replace(i*extwidth, A);
708 }
709 for (auto &it : perm_choices) {
710 int position = it.first.as_int()*extwidth;
711 int data_index = it.second;
712 data.replace(position, B.extract(data_index*width, width));
713 updated_S[data_index] = State::S0;
714 updated_B.replace(data_index*width, SigSpec(State::Sx, width));
715 }
716
717 // create shiftx cell
718 SigSpec shifted_cmp = {cmp, SigSpec(State::S0, width_bits)};
719 SigSpec outsig = module->addWire(NEW_ID, width);
720 Cell *c = module->addShiftx(NEW_ID, data, shifted_cmp, outsig, false, src);
721 updated_S.append(en);
722 updated_B.append(outsig);
723 log(" created $shiftx cell %s.\n", log_id(c));
724
725 // remove this sig and continue with the next block
726 seldb.erase(sig);
727 }
728
729 // update $pmux cell
730 cell->setPort(ID(S), updated_S);
731 cell->setPort(ID(B), updated_B);
732 cell->setParam(ID(S_WIDTH), GetSize(updated_S));
733 }
734 }
735 }
736 } Pmux2ShiftxPass;
737
738 struct OnehotPass : public Pass {
739 OnehotPass() : Pass("onehot", "optimize $eq cells for onehot signals") { }
740 void help() YS_OVERRIDE
741 {
742 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
743 log("\n");
744 log(" onehot [options] [selection]\n");
745 log("\n");
746 log("This pass optimizes $eq cells that compare one-hot signals against constants\n");
747 log("\n");
748 log(" -v, -vv\n");
749 log(" verbose output\n");
750 log("\n");
751 }
752 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
753 {
754 bool verbose = false;
755 bool verbose_onehot = false;
756
757 log_header(design, "Executing ONEHOT pass.\n");
758
759 size_t argidx;
760 for (argidx = 1; argidx < args.size(); argidx++) {
761 if (args[argidx] == "-v") {
762 verbose = true;
763 continue;
764 }
765 if (args[argidx] == "-vv") {
766 verbose = true;
767 verbose_onehot = true;
768 continue;
769 }
770 break;
771 }
772 extra_args(args, argidx, design);
773
774 for (auto module : design->selected_modules())
775 {
776 SigMap sigmap(module);
777 OnehotDatabase onehot_db(module, sigmap);
778 onehot_db.verbose = verbose_onehot;
779
780 for (auto cell : module->selected_cells())
781 {
782 if (cell->type != ID($eq))
783 continue;
784
785 SigSpec A = sigmap(cell->getPort(ID(A)));
786 SigSpec B = sigmap(cell->getPort(ID(B)));
787
788 int a_width = cell->getParam(ID(A_WIDTH)).as_int();
789 int b_width = cell->getParam(ID(B_WIDTH)).as_int();
790
791 if (a_width < b_width) {
792 bool a_signed = cell->getParam(ID(A_SIGNED)).as_int();
793 A.extend_u0(b_width, a_signed);
794 }
795
796 if (b_width < a_width) {
797 bool b_signed = cell->getParam(ID(B_SIGNED)).as_int();
798 B.extend_u0(a_width, b_signed);
799 }
800
801 if (A.is_fully_const())
802 std::swap(A, B);
803
804 if (!B.is_fully_const())
805 continue;
806
807 if (verbose)
808 log("Checking $eq(%s, %s) cell %s/%s.\n", log_signal(A), log_signal(B), log_id(module), log_id(cell));
809
810 if (!onehot_db.query(A)) {
811 if (verbose)
812 log(" onehot driver test on %s failed.\n", log_signal(A));
813 continue;
814 }
815
816 int index = -1;
817 bool not_onehot = false;
818
819 for (int i = 0; i < GetSize(B); i++) {
820 if (B[i] != State::S1)
821 continue;
822 if (index >= 0)
823 not_onehot = true;
824 index = i;
825 }
826
827 if (index < 0) {
828 if (verbose)
829 log(" not optimizing the zero pattern.\n");
830 continue;
831 }
832
833 SigSpec Y = cell->getPort(ID(Y));
834
835 if (not_onehot)
836 {
837 if (verbose)
838 log(" replacing with constant 0 driver.\n");
839 else
840 log("Replacing one-hot $eq(%s, %s) cell %s/%s with constant 0 driver.\n", log_signal(A), log_signal(B), log_id(module), log_id(cell));
841 module->connect(Y, SigSpec(1, GetSize(Y)));
842 }
843 else
844 {
845 SigSpec sig = A[index];
846 if (verbose)
847 log(" replacing with signal %s.\n", log_signal(sig));
848 else
849 log("Replacing one-hot $eq(%s, %s) cell %s/%s with signal %s.\n",log_signal(A), log_signal(B), log_id(module), log_id(cell), log_signal(sig));
850 sig.extend_u0(GetSize(Y));
851 module->connect(Y, sig);
852 }
853
854 module->remove(cell);
855 }
856 }
857 }
858 } OnehotPass;
859
860 PRIVATE_NAMESPACE_END