Fix INIT for variable length SRs that have been bumped up one
[yosys.git] / passes / techmap / shregmap.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 ShregmapTech
27 {
28 virtual ~ShregmapTech() { }
29 virtual void init(const Module * /*module*/, const SigMap &/*sigmap*/) {}
30 virtual void non_chain_user(const SigBit &/*bit*/, const Cell* /*cell*/, IdString /*port*/) {}
31 virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0;
32 virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
33 };
34
35 struct ShregmapOptions
36 {
37 int minlen, maxlen;
38 int keep_before, keep_after;
39 bool zinit, init, params, ffe;
40 dict<IdString, pair<IdString, IdString>> ffcells;
41 ShregmapTech *tech;
42
43 ShregmapOptions()
44 {
45 minlen = 2;
46 maxlen = 0;
47 keep_before = 0;
48 keep_after = 0;
49 zinit = false;
50 init = false;
51 params = false;
52 ffe = false;
53 tech = nullptr;
54 }
55 };
56
57 struct ShregmapTechGreenpak4 : ShregmapTech
58 {
59 bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/)
60 {
61 if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
62 taps.clear();
63 return true;
64 }
65
66 if (GetSize(taps) > 2)
67 return false;
68
69 if (taps.back() > 16) return false;
70
71 return true;
72 }
73
74 bool fixup(Cell *cell, dict<int, SigBit> &taps)
75 {
76 auto D = cell->getPort("\\D");
77 auto C = cell->getPort("\\C");
78
79 auto newcell = cell->module->addCell(NEW_ID, "\\GP_SHREG");
80 newcell->setPort("\\nRST", State::S1);
81 newcell->setPort("\\CLK", C);
82 newcell->setPort("\\IN", D);
83
84 int i = 0;
85 for (auto tap : taps) {
86 newcell->setPort(i ? "\\OUTB" : "\\OUTA", tap.second);
87 newcell->setParam(i ? "\\OUTB_TAP" : "\\OUTA_TAP", tap.first + 1);
88 i++;
89 }
90
91 cell->setParam("\\OUTA_INVERT", 0);
92 return false;
93 }
94 };
95
96 struct ShregmapTechXilinx7 : ShregmapTech
97 {
98 dict<SigBit, std::pair<Cell*,int>> sigbit_to_shiftx_offset;
99 const ShregmapOptions &opts;
100
101 ShregmapTechXilinx7(const ShregmapOptions &opts) : opts(opts) {}
102
103 virtual void init(const Module* module, const SigMap &sigmap) override
104 {
105 for (auto i : module->cells_) {
106 auto cell = i.second;
107 if (cell->type != "$shiftx") continue;
108 if (cell->getParam("\\Y_WIDTH") != 1) continue;
109 int j = 0;
110 for (auto bit : sigmap(cell->getPort("\\A")))
111 sigbit_to_shiftx_offset[bit] = std::make_pair(cell, j++);
112 log_assert(j == cell->getParam("\\A_WIDTH").as_int());
113 }
114 }
115
116 virtual void non_chain_user(const SigBit &bit, const Cell *cell, IdString port) override
117 {
118 auto it = sigbit_to_shiftx_offset.find(bit);
119 if (it == sigbit_to_shiftx_offset.end())
120 return;
121 if (cell && cell->type == "$shiftx" && port == "\\A")
122 return;
123 sigbit_to_shiftx_offset.erase(it);
124 }
125
126 virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) override
127 {
128 if (GetSize(taps) == 1)
129 return taps[0] >= opts.minlen-1;
130
131 if (taps.back() < opts.minlen-1)
132 return false;
133
134 Cell *shiftx = nullptr;
135 for (int i = 0; i < GetSize(taps); ++i) {
136 // Check taps are sequential
137 if (i != taps[i])
138 return false;
139 // Check taps are not connected to a shift register,
140 // or sequential to the same shift register
141 auto it = sigbit_to_shiftx_offset.find(qbits[i]);
142 if (i == 0) {
143 if (it == sigbit_to_shiftx_offset.end()) {
144 return false;
145 }
146 else {
147 shiftx = it->second.first;
148 int offset = it->second.second;
149 if (offset != i)
150 return false;
151 }
152 }
153 else {
154 if (it == sigbit_to_shiftx_offset.end()) {
155 return false;
156 }
157 else {
158 if (shiftx != it->second.first)
159 return false;
160 int offset = it->second.second;
161 if (offset != i)
162 return false;
163 }
164 }
165 }
166 log_assert(shiftx);
167
168 // Cannot implement variable-length shift registers
169 // greater than 128 since Q31 cannot be output onto
170 // fabric
171 if (GetSize(taps) > 128)
172 return false;
173
174 // Only map if $shiftx exclusively covers the shift register
175 if (GetSize(taps) != shiftx->getParam("\\A_WIDTH").as_int())
176 return false;
177
178 return true;
179 }
180
181 virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) override
182 {
183 const auto &tap = *taps.begin();
184 auto bit = tap.second;
185 auto it = sigbit_to_shiftx_offset.find(bit);
186 // If fixed-length, no fixup necessary
187 if (it == sigbit_to_shiftx_offset.end())
188 return true;
189
190 auto cell_q = cell->getPort("\\Q");
191 log_assert(cell_q.is_bit());
192
193 Cell* shiftx = it->second.first;
194 // FIXME: Hack to ensure that $shiftx gets optimised away
195 // Without this, Yosys will refuse to optimise away a $shiftx
196 // where \\A 's width is not perfectly \\B_WIDTH ** 2
197 // See YosysHQ/yosys#878
198 auto shiftx_bwidth = shiftx->getParam("\\B_WIDTH").as_int();
199 shiftx->setPort("\\A", cell_q.repeat(1 << shiftx_bwidth));
200 shiftx->setParam("\\A_WIDTH", 1 << shiftx_bwidth);
201
202 cell->setPort("\\L", shiftx->getPort("\\B"));
203
204 return true;
205 }
206 };
207
208
209 struct ShregmapWorker
210 {
211 Module *module;
212 SigMap sigmap;
213
214 const ShregmapOptions &opts;
215 int dff_count, shreg_count;
216
217 pool<Cell*> remove_cells;
218 pool<SigBit> remove_init;
219
220 dict<SigBit, bool> sigbit_init;
221 dict<SigBit, Cell*> sigbit_chain_next;
222 dict<SigBit, Cell*> sigbit_chain_prev;
223 pool<SigBit> sigbit_with_non_chain_users;
224 pool<Cell*> chain_start_cells;
225
226 void make_sigbit_chain_next_prev()
227 {
228 for (auto wire : module->wires())
229 {
230 if (wire->port_output || wire->get_bool_attribute("\\keep")) {
231 for (auto bit : sigmap(wire)) {
232 sigbit_with_non_chain_users.insert(bit);
233 if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {});
234 }
235 }
236
237 if (wire->attributes.count("\\init")) {
238 SigSpec initsig = sigmap(wire);
239 Const initval = wire->attributes.at("\\init");
240 for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
241 if (initval[i] == State::S0 && !opts.zinit)
242 sigbit_init[initsig[i]] = false;
243 else if (initval[i] == State::S1)
244 sigbit_init[initsig[i]] = true;
245 }
246 }
247
248 for (auto cell : module->cells())
249 {
250 if (opts.ffcells.count(cell->type) && !cell->get_bool_attribute("\\keep"))
251 {
252 IdString d_port = opts.ffcells.at(cell->type).first;
253 IdString q_port = opts.ffcells.at(cell->type).second;
254
255 SigBit d_bit = sigmap(cell->getPort(d_port).as_bit());
256 SigBit q_bit = sigmap(cell->getPort(q_port).as_bit());
257
258 if (opts.init || sigbit_init.count(q_bit) == 0)
259 {
260 if (sigbit_chain_next.count(d_bit)) {
261 sigbit_with_non_chain_users.insert(d_bit);
262 } else
263 sigbit_chain_next[d_bit] = cell;
264
265 sigbit_chain_prev[q_bit] = cell;
266 continue;
267 }
268 }
269
270 for (auto conn : cell->connections())
271 if (cell->input(conn.first))
272 for (auto bit : sigmap(conn.second)) {
273 sigbit_with_non_chain_users.insert(bit);
274 if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first);
275 }
276 }
277 }
278
279 void find_chain_start_cells()
280 {
281 for (auto it : sigbit_chain_next)
282 {
283 if (opts.tech == nullptr && sigbit_with_non_chain_users.count(it.first))
284 goto start_cell;
285
286 if (sigbit_chain_prev.count(it.first) != 0)
287 {
288 Cell *c1 = sigbit_chain_prev.at(it.first);
289 Cell *c2 = it.second;
290
291 if (c1->type != c2->type)
292 goto start_cell;
293
294 if (c1->parameters != c2->parameters)
295 goto start_cell;
296
297 IdString d_port = opts.ffcells.at(c1->type).first;
298 IdString q_port = opts.ffcells.at(c1->type).second;
299
300 auto c1_conn = c1->connections();
301 auto c2_conn = c1->connections();
302
303 c1_conn.erase(d_port);
304 c1_conn.erase(q_port);
305
306 c2_conn.erase(d_port);
307 c2_conn.erase(q_port);
308
309 if (c1_conn != c2_conn)
310 goto start_cell;
311
312 continue;
313 }
314
315 start_cell:
316 chain_start_cells.insert(it.second);
317 }
318 }
319
320 vector<Cell*> create_chain(Cell *start_cell)
321 {
322 vector<Cell*> chain;
323
324 Cell *c = start_cell;
325 while (c != nullptr)
326 {
327 chain.push_back(c);
328
329 IdString q_port = opts.ffcells.at(c->type).second;
330 SigBit q_bit = sigmap(c->getPort(q_port).as_bit());
331
332 if (sigbit_chain_next.count(q_bit) == 0)
333 break;
334
335 c = sigbit_chain_next.at(q_bit);
336 if (chain_start_cells.count(c) != 0)
337 break;
338 }
339
340 return chain;
341 }
342
343 void process_chain(vector<Cell*> &chain)
344 {
345 if (GetSize(chain) < opts.keep_before + opts.minlen + opts.keep_after)
346 return;
347
348 int cursor = opts.keep_before;
349 while (cursor < GetSize(chain) - opts.keep_after)
350 {
351 int depth = GetSize(chain) - opts.keep_after - cursor;
352
353 if (opts.maxlen > 0)
354 depth = std::min(opts.maxlen, depth);
355
356 Cell *first_cell = chain[cursor];
357 IdString q_port = opts.ffcells.at(first_cell->type).second;
358 dict<int, SigBit> taps_dict;
359
360 if (opts.tech)
361 {
362 vector<SigBit> qbits;
363 vector<int> taps;
364
365 for (int i = 0; i < depth; i++)
366 {
367 Cell *cell = chain[cursor+i];
368 auto qbit = sigmap(cell->getPort(q_port));
369 qbits.push_back(qbit);
370
371 if (sigbit_with_non_chain_users.count(qbit))
372 taps.push_back(i);
373 }
374
375 while (depth > 0)
376 {
377 if (taps.empty() || taps.back() < depth-1)
378 taps.push_back(depth-1);
379
380 if (opts.tech->analyze(taps, qbits))
381 break;
382
383 taps.pop_back();
384 depth--;
385 }
386
387 depth = 0;
388 for (auto tap : taps) {
389 taps_dict[tap] = qbits.at(tap);
390 log_assert(depth < tap+1);
391 depth = tap+1;
392 }
393 }
394
395 if (depth < 2) {
396 cursor++;
397 continue;
398 }
399
400 Cell *last_cell = chain[cursor+depth-1];
401
402 log("Converting %s.%s ... %s.%s to a shift register with depth %d.\n",
403 log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), depth);
404
405 dff_count += depth;
406 shreg_count += 1;
407
408 string shreg_cell_type_str = "$__SHREG";
409 if (opts.params) {
410 shreg_cell_type_str += "_";
411 } else {
412 if (first_cell->type[1] != '_')
413 shreg_cell_type_str += "_";
414 shreg_cell_type_str += first_cell->type.substr(1);
415 }
416
417 if (opts.init) {
418 vector<State> initval;
419 for (int i = depth-1; i >= 0; i--) {
420 SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
421 if (sigbit_init.count(bit) == 0)
422 initval.push_back(State::Sx);
423 else if (sigbit_init.at(bit))
424 initval.push_back(State::S1);
425 else
426 initval.push_back(State::S0);
427 remove_init.insert(bit);
428 }
429 first_cell->setParam("\\INIT", initval);
430 }
431
432 if (opts.zinit)
433 for (int i = depth-1; i >= 0; i--) {
434 SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
435 remove_init.insert(bit);
436 }
437
438 if (opts.params)
439 {
440 int param_clkpol = -1;
441 int param_enpol = 2;
442
443 if (first_cell->type == "$_DFF_N_") param_clkpol = 0;
444 if (first_cell->type == "$_DFF_P_") param_clkpol = 1;
445
446 if (first_cell->type == "$_DFFE_NN_") param_clkpol = 0, param_enpol = 0;
447 if (first_cell->type == "$_DFFE_NP_") param_clkpol = 0, param_enpol = 1;
448 if (first_cell->type == "$_DFFE_PN_") param_clkpol = 1, param_enpol = 0;
449 if (first_cell->type == "$_DFFE_PP_") param_clkpol = 1, param_enpol = 1;
450
451 log_assert(param_clkpol >= 0);
452 first_cell->setParam("\\CLKPOL", param_clkpol);
453 if (opts.ffe) first_cell->setParam("\\ENPOL", param_enpol);
454 }
455
456 first_cell->type = shreg_cell_type_str;
457 first_cell->setPort(q_port, last_cell->getPort(q_port));
458 if (!first_cell->hasPort("\\L"))
459 first_cell->setPort("\\L", depth-1);
460 first_cell->setParam("\\DEPTH", depth);
461
462 if (opts.tech != nullptr && !opts.tech->fixup(first_cell, taps_dict))
463 remove_cells.insert(first_cell);
464
465 for (int i = 1; i < depth; i++)
466 remove_cells.insert(chain[cursor+i]);
467 cursor += depth;
468 }
469 }
470
471 void cleanup()
472 {
473 for (auto cell : remove_cells)
474 module->remove(cell);
475
476 for (auto wire : module->wires())
477 {
478 if (wire->attributes.count("\\init") == 0)
479 continue;
480
481 SigSpec initsig = sigmap(wire);
482 Const &initval = wire->attributes.at("\\init");
483
484 for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
485 if (remove_init.count(initsig[i]))
486 initval[i] = State::Sx;
487
488 if (SigSpec(initval).is_fully_undef())
489 wire->attributes.erase("\\init");
490 }
491
492 remove_cells.clear();
493 sigbit_chain_next.clear();
494 sigbit_chain_prev.clear();
495 chain_start_cells.clear();
496 }
497
498 ShregmapWorker(Module *module, const ShregmapOptions &opts) :
499 module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
500 {
501 if (opts.tech)
502 opts.tech->init(module, sigmap);
503
504 make_sigbit_chain_next_prev();
505 find_chain_start_cells();
506
507 for (auto c : chain_start_cells) {
508 vector<Cell*> chain = create_chain(c);
509 process_chain(chain);
510 }
511
512 cleanup();
513 }
514 };
515
516 struct ShregmapPass : public Pass {
517 ShregmapPass() : Pass("shregmap", "map shift registers") { }
518 void help() YS_OVERRIDE
519 {
520 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
521 log("\n");
522 log(" shregmap [options] [selection]\n");
523 log("\n");
524 log("This pass converts chains of $_DFF_[NP]_ gates to target specific shift register\n");
525 log("primitives. The generated shift register will be of type $__SHREG_DFF_[NP]_ and\n");
526 log("will use the same interface as the original $_DFF_*_ cells. The cell parameter\n");
527 log("'DEPTH' will contain the depth of the shift register. Use a target-specific\n");
528 log("'techmap' map file to convert those cells to the actual target cells.\n");
529 log("\n");
530 log(" -minlen N\n");
531 log(" minimum length of shift register (default = 2)\n");
532 log(" (this is the length after -keep_before and -keep_after)\n");
533 log("\n");
534 log(" -maxlen N\n");
535 log(" maximum length of shift register (default = no limit)\n");
536 log(" larger chains will be mapped to multiple shift register instances\n");
537 log("\n");
538 log(" -keep_before N\n");
539 log(" number of DFFs to keep before the shift register (default = 0)\n");
540 log("\n");
541 log(" -keep_after N\n");
542 log(" number of DFFs to keep after the shift register (default = 0)\n");
543 log("\n");
544 log(" -clkpol pos|neg|any\n");
545 log(" limit match to only positive or negative edge clocks. (default = any)\n");
546 log("\n");
547 log(" -enpol pos|neg|none|any_or_none|any\n");
548 log(" limit match to FFs with the specified enable polarity. (default = none)\n");
549 log("\n");
550 log(" -match <cell_type>[:<d_port_name>:<q_port_name>]\n");
551 log(" match the specified cells instead of $_DFF_N_ and $_DFF_P_. If\n");
552 log(" ':<d_port_name>:<q_port_name>' is omitted then 'D' and 'Q' is used\n");
553 log(" by default. E.g. the option '-clkpol pos' is just an alias for\n");
554 log(" '-match $_DFF_P_', which is an alias for '-match $_DFF_P_:D:Q'.\n");
555 log("\n");
556 log(" -params\n");
557 log(" instead of encoding the clock and enable polarity in the cell name by\n");
558 log(" deriving from the original cell name, simply name all generated cells\n");
559 log(" $__SHREG_ and use CLKPOL and ENPOL parameters. An ENPOL value of 2 is\n");
560 log(" used to denote cells without enable input. The ENPOL parameter is\n");
561 log(" omitted when '-enpol none' (or no -enpol option) is passed.\n");
562 log("\n");
563 log(" -zinit\n");
564 log(" assume the shift register is automatically zero-initialized, so it\n");
565 log(" becomes legal to merge zero initialized FFs into the shift register.\n");
566 log("\n");
567 log(" -init\n");
568 log(" map initialized registers to the shift reg, add an INIT parameter to\n");
569 log(" generated cells with the initialization value. (first bit to shift out\n");
570 log(" in LSB position)\n");
571 log("\n");
572 log(" -tech greenpak4\n");
573 log(" map to greenpak4 shift registers.\n");
574 log("\n");
575 }
576 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
577 {
578 ShregmapOptions opts;
579 string clkpol, enpol;
580
581 log_header(design, "Executing SHREGMAP pass (map shift registers).\n");
582
583 size_t argidx;
584 for (argidx = 1; argidx < args.size(); argidx++)
585 {
586 if (args[argidx] == "-clkpol" && argidx+1 < args.size()) {
587 clkpol = args[++argidx];
588 continue;
589 }
590 if (args[argidx] == "-enpol" && argidx+1 < args.size()) {
591 enpol = args[++argidx];
592 continue;
593 }
594 if (args[argidx] == "-match" && argidx+1 < args.size()) {
595 vector<string> match_args = split_tokens(args[++argidx], ":");
596 if (GetSize(match_args) < 2)
597 match_args.push_back("D");
598 if (GetSize(match_args) < 3)
599 match_args.push_back("Q");
600 IdString id_cell_type(RTLIL::escape_id(match_args[0]));
601 IdString id_d_port_name(RTLIL::escape_id(match_args[1]));
602 IdString id_q_port_name(RTLIL::escape_id(match_args[2]));
603 opts.ffcells[id_cell_type] = make_pair(id_d_port_name, id_q_port_name);
604 continue;
605 }
606 if (args[argidx] == "-minlen" && argidx+1 < args.size()) {
607 opts.minlen = atoi(args[++argidx].c_str());
608 continue;
609 }
610 if (args[argidx] == "-maxlen" && argidx+1 < args.size()) {
611 opts.maxlen = atoi(args[++argidx].c_str());
612 continue;
613 }
614 if (args[argidx] == "-keep_before" && argidx+1 < args.size()) {
615 opts.keep_before = atoi(args[++argidx].c_str());
616 continue;
617 }
618 if (args[argidx] == "-keep_after" && argidx+1 < args.size()) {
619 opts.keep_after = atoi(args[++argidx].c_str());
620 continue;
621 }
622 if (args[argidx] == "-tech" && argidx+1 < args.size() && opts.tech == nullptr) {
623 string tech = args[++argidx];
624 if (tech == "greenpak4") {
625 clkpol = "pos";
626 opts.zinit = true;
627 opts.tech = new ShregmapTechGreenpak4;
628 }
629 else if (tech == "xilinx") {
630 opts.init = true;
631 opts.params = true;
632 enpol = "any_or_none";
633 opts.tech = new ShregmapTechXilinx7(opts);
634 } else {
635 argidx--;
636 break;
637 }
638 continue;
639 }
640 if (args[argidx] == "-zinit") {
641 opts.zinit = true;
642 continue;
643 }
644 if (args[argidx] == "-init") {
645 opts.init = true;
646 continue;
647 }
648 if (args[argidx] == "-params") {
649 opts.params = true;
650 continue;
651 }
652 break;
653 }
654 extra_args(args, argidx, design);
655
656 if (opts.zinit && opts.init)
657 log_cmd_error("Options -zinit and -init are exclusive!\n");
658
659 if (opts.ffcells.empty())
660 {
661 bool clk_pos = clkpol == "" || clkpol == "pos" || clkpol == "any";
662 bool clk_neg = clkpol == "" || clkpol == "neg" || clkpol == "any";
663
664 bool en_none = enpol == "" || enpol == "none" || enpol == "any_or_none";
665 bool en_pos = enpol == "pos" || enpol == "any" || enpol == "any_or_none";
666 bool en_neg = enpol == "neg" || enpol == "any" || enpol == "any_or_none";
667
668 if (clk_pos && en_none)
669 opts.ffcells["$_DFF_P_"] = make_pair(IdString("\\D"), IdString("\\Q"));
670 if (clk_neg && en_none)
671 opts.ffcells["$_DFF_N_"] = make_pair(IdString("\\D"), IdString("\\Q"));
672
673 if (clk_pos && en_pos)
674 opts.ffcells["$_DFFE_PP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
675 if (clk_pos && en_neg)
676 opts.ffcells["$_DFFE_PN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
677
678 if (clk_neg && en_pos)
679 opts.ffcells["$_DFFE_NP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
680 if (clk_neg && en_neg)
681 opts.ffcells["$_DFFE_NN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
682
683 if (en_pos || en_neg)
684 opts.ffe = true;
685 }
686 else
687 {
688 if (!clkpol.empty())
689 log_cmd_error("Options -clkpol and -match are exclusive!\n");
690 if (!enpol.empty())
691 log_cmd_error("Options -enpol and -match are exclusive!\n");
692 if (opts.params)
693 log_cmd_error("Options -params and -match are exclusive!\n");
694 }
695
696 int dff_count = 0;
697 int shreg_count = 0;
698
699 for (auto module : design->selected_modules()) {
700 ShregmapWorker worker(module, opts);
701 dff_count += worker.dff_count;
702 shreg_count += worker.shreg_count;
703 }
704
705 log("Converted %d dff cells into %d shift registers.\n", dff_count, shreg_count);
706
707 if (opts.tech != nullptr) {
708 delete opts.tech;
709 opts.tech = nullptr;
710 }
711 }
712 } ShregmapPass;
713
714 PRIVATE_NAMESPACE_END