3868bbb8948b0acc12eaa4732c1f9623aaa24d09
[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_first(const Cell* /*first_cell*/, const SigMap &/*sigmap*/) { return true; }
32 virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0;
33 virtual Cell* fixup(Cell *cell, const vector<int> &taps, const vector<SigBit> &qbits) = 0;
34 };
35
36 struct ShregmapOptions
37 {
38 int minlen, maxlen;
39 int keep_before, keep_after;
40 bool zinit, init, params, ffe;
41 dict<IdString, pair<IdString, IdString>> ffcells;
42 ShregmapTech *tech;
43
44 ShregmapOptions()
45 {
46 minlen = 2;
47 maxlen = 0;
48 keep_before = 0;
49 keep_after = 0;
50 zinit = false;
51 init = false;
52 params = false;
53 ffe = false;
54 tech = nullptr;
55 }
56 };
57
58 struct ShregmapTechGreenpak4 : ShregmapTech
59 {
60 virtual bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/) override
61 {
62 if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
63 taps.clear();
64 return true;
65 }
66
67 if (GetSize(taps) > 2)
68 return false;
69
70 if (taps.back() > 16) return false;
71
72 return true;
73 }
74
75 virtual Cell* fixup(Cell *cell, const vector<int> &taps, const vector<SigBit> &qbits) override
76 {
77 auto D = cell->getPort("\\D");
78 auto C = cell->getPort("\\C");
79
80 auto newcell = cell->module->addCell(NEW_ID, "\\GP_SHREG");
81 newcell->setPort("\\nRST", State::S1);
82 newcell->setPort("\\CLK", C);
83 newcell->setPort("\\IN", D);
84
85 int i = 0;
86 for (auto tap : taps) {
87 newcell->setPort(i ? "\\OUTB" : "\\OUTA", qbits[tap]);
88 newcell->setParam(i ? "\\OUTB_TAP" : "\\OUTA_TAP", tap + 1);
89 i++;
90 }
91
92 cell->setParam("\\OUTA_INVERT", 0);
93 return newcell;
94 }
95 };
96
97 struct ShregmapTechXilinx7Static : ShregmapTech
98 {
99 dict<SigBit, Cell*> sigbit_to_cell;
100 const ShregmapOptions &opts;
101
102 virtual void init(const Module* module, const SigMap &sigmap) override
103 {
104 for (const auto &i : module->cells_) {
105 auto cell = i.second;
106 if (!cell->type.in("\\FDRE", "\\FDRE_1","\\FDSE", "\\FDSE_1",
107 "\\FDCE", "\\FDCE_1", "\\FDPE", "\\FDPE_1"))
108 continue;
109
110 sigbit_to_cell[sigmap(cell->getPort("\\Q"))] = cell;
111 }
112 }
113
114 ShregmapTechXilinx7Static(const ShregmapOptions &opts) : opts(opts) {}
115
116 virtual bool analyze_first(const Cell* first_cell, const SigMap &sigmap) override
117 {
118 if (first_cell->type.in("\\FDRE", "\\FDRE_1")) {
119 bool is_R_inverted = false;
120 if (first_cell->hasParam("\\IS_R_INVERTED"))
121 is_R_inverted = first_cell->getParam("\\IS_R_INVERTED").as_bool();
122 SigBit R = sigmap(first_cell->getPort("\\R"));
123 if (R != RTLIL::S0 && R != RTLIL::S1)
124 return false;
125 if ((!is_R_inverted && R != RTLIL::S0) || (is_R_inverted && R != RTLIL::S1))
126 return false;
127 return true;
128 }
129 if (first_cell->type.in("\\FDSE", "\\FDSE_1")) {
130 bool is_S_inverted = false;
131 if (first_cell->hasParam("\\IS_S_INVERTED"))
132 is_S_inverted = first_cell->getParam("\\IS_S_INVERTED").as_bool();
133 SigBit S = sigmap(first_cell->getPort("\\S"));
134 if (S != RTLIL::S0 && S != RTLIL::S1)
135 return false;
136 if ((!is_S_inverted && S != RTLIL::S0) || (is_S_inverted && S != RTLIL::S1))
137 return false;
138 return true;
139 }
140 if (first_cell->type.in("\\FDCE", "\\FDCE_1")) {
141 bool is_CLR_inverted = false;
142 if (first_cell->hasParam("\\IS_CLR_INVERTED"))
143 is_CLR_inverted = first_cell->getParam("\\IS_CLR_INVERTED").as_bool();
144 SigBit CLR = sigmap(first_cell->getPort("\\CLR"));
145 if (CLR != RTLIL::S0 && CLR != RTLIL::S1)
146 return false;
147 if ((!is_CLR_inverted && CLR != RTLIL::S0) || (is_CLR_inverted && CLR != RTLIL::S1))
148 return false;
149 return true;
150 }
151 if (first_cell->type.in("\\FDPE", "\\FDPE_1")) {
152 bool is_PRE_inverted = false;
153 if (first_cell->hasParam("\\IS_PRE_INVERTED"))
154 is_PRE_inverted = first_cell->getParam("\\IS_PRE_INVERTED").as_bool();
155 SigBit PRE = sigmap(first_cell->getPort("\\PRE"));
156 if (PRE != RTLIL::S0 && PRE != RTLIL::S1)
157 return false;
158 if ((!is_PRE_inverted && PRE != RTLIL::S0) || (is_PRE_inverted && PRE != RTLIL::S1))
159 return false;
160 return true;
161 }
162 return true;
163 }
164
165 virtual bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/) override
166 {
167 return GetSize(taps) == 1 && taps[0] >= opts.minlen-1;
168 }
169
170 virtual Cell* fixup(Cell *cell, const vector<int> &/*taps*/, const vector<SigBit> &qbits) override
171 {
172 auto newcell = cell->module->addCell(NEW_ID, "$__SHREG_");
173 newcell->set_src_attribute(cell->get_src_attribute());
174 newcell->setParam("\\DEPTH", cell->getParam("\\DEPTH"));
175
176 if (cell->type.in("$__SHREG_DFF_N_", "$__SHREG_DFF_P_",
177 "$__SHREG_DFFE_NN_", "$__SHREG_DFFE_NP_", "$__SHREG_DFFE_PN_", "$__SHREG_DFFE_PP_")) {
178 int param_clkpol = -1;
179 int param_enpol = 2;
180 if (cell->type == "$__SHREG_DFF_N_") param_clkpol = 0;
181 else if (cell->type == "$__SHREG_DFF_P_") param_clkpol = 1;
182 else if (cell->type == "$__SHREG_DFFE_NN_") param_clkpol = 0, param_enpol = 0;
183 else if (cell->type == "$__SHREG_DFFE_NP_") param_clkpol = 0, param_enpol = 1;
184 else if (cell->type == "$__SHREG_DFFE_PN_") param_clkpol = 1, param_enpol = 0;
185 else if (cell->type == "$__SHREG_DFFE_PP_") param_clkpol = 1, param_enpol = 1;
186 else log_abort();
187
188 log_assert(param_clkpol >= 0);
189 newcell->setParam("\\CLKPOL", param_clkpol);
190 newcell->setParam("\\ENPOL", param_enpol);
191 newcell->setParam("\\INIT", cell->getParam("\\INIT"));
192
193 if (cell->hasPort("\\E"))
194 newcell->setPort("\\E", cell->getPort("\\E"));
195 }
196 else if (cell->type.in("$__SHREG_FDRE", "$__SHREG_FDRE_1","$__SHREG_FDSE", "$__SHREG_FDSE_1",
197 "$__SHREG_FDCE", "$__SHREG_FDCE_1", "$__SHREG_FDPE", "$__SHREG_FDPE_1")) {
198 int param_clkpol = 1;
199 if (cell->hasParam("\\IS_C_INVERTED") && cell->getParam("\\IS_C_INVERTED").as_bool())
200 param_clkpol = 0;
201 newcell->setParam("\\CLKPOL", param_clkpol);
202 newcell->setParam("\\ENPOL", 1);
203 log_assert(cell->getParam("\\INIT").is_fully_undef());
204 SigSpec INIT;
205 for (auto q : qbits) {
206 Cell* reg = sigbit_to_cell.at(q);
207 INIT.append(SigBit(reg->getParam("\\INIT").as_bool()));
208 }
209
210 newcell->setPort("\\E", cell->getPort("\\CE"));
211 }
212 else log_abort();
213
214 newcell->setParam("\\ENPOL", 1);
215
216 newcell->setPort("\\C", cell->getPort("\\C"));
217 newcell->setPort("\\D", cell->getPort("\\D"));
218 newcell->setPort("\\Q", cell->getPort("\\Q"));
219
220 return newcell;
221 }
222 };
223
224 struct ShregmapTechXilinx7Dynamic : ShregmapTechXilinx7Static
225 {
226 dict<SigBit, std::tuple<Cell*,int,int>> sigbit_to_shiftx_offset;
227
228 ShregmapTechXilinx7Dynamic(const ShregmapOptions &opts) : ShregmapTechXilinx7Static(opts) {}
229
230 virtual void init(const Module* module, const SigMap &sigmap) override
231 {
232 for (const auto &i : module->cells_) {
233 auto cell = i.second;
234 if (cell->type == "$shiftx") {
235 if (cell->getParam("\\Y_WIDTH") != 1) continue;
236 int j = 0;
237 for (auto bit : sigmap(cell->getPort("\\A")))
238 sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0);
239 log_assert(j == cell->getParam("\\A_WIDTH").as_int());
240 }
241 else if (cell->type == "$mux") {
242 int j = 0;
243 for (auto bit : sigmap(cell->getPort("\\A")))
244 sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++);
245 j = 0;
246 for (auto bit : sigmap(cell->getPort("\\B")))
247 sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 1, j++);
248 }
249 }
250 }
251
252 virtual void non_chain_user(const SigBit &bit, const Cell *cell, IdString port) override
253 {
254 auto it = sigbit_to_shiftx_offset.find(bit);
255 if (it == sigbit_to_shiftx_offset.end())
256 return;
257 if (cell) {
258 if (cell->type == "$shiftx" && port == "\\A")
259 return;
260 if (cell->type == "$mux" && (port == "\\A" || port == "\\B"))
261 return;
262 }
263 sigbit_to_shiftx_offset.erase(it);
264 }
265
266 virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) override
267 {
268 if (GetSize(taps) == 1)
269 return taps[0] >= opts.minlen-1 && sigbit_to_shiftx_offset.count(qbits[0]);
270
271 if (taps.back() < opts.minlen-1)
272 return false;
273
274 Cell *shiftx = nullptr;
275 int group = 0;
276 for (int i = 0; i < GetSize(taps); ++i) {
277 // Check taps are sequential
278 if (i != taps[i])
279 return false;
280
281 auto it = sigbit_to_shiftx_offset.find(qbits[i]);
282 if (it == sigbit_to_shiftx_offset.end())
283 return false;
284
285 // Check taps are not connected to a shift register,
286 // or sequential to the same shift register
287 if (i == 0) {
288 int offset;
289 std::tie(shiftx,offset,group) = it->second;
290 if (offset != i)
291 return false;
292 }
293 else {
294 Cell *shiftx_ = std::get<0>(it->second);
295 if (shiftx_ != shiftx)
296 return false;
297 int offset = std::get<1>(it->second);
298 if (offset != i)
299 return false;
300 int group_ = std::get<2>(it->second);
301 if (group_ != group)
302 return false;
303 }
304 }
305 log_assert(shiftx);
306
307 // Only map if $shiftx exclusively covers the shift register
308 if (shiftx->type == "$shiftx") {
309 if (GetSize(taps) > shiftx->getParam("\\A_WIDTH").as_int())
310 return false;
311 // Due to padding the most significant bits of A may be 1'bx,
312 // and if so, discount them
313 if (GetSize(taps) < shiftx->getParam("\\A_WIDTH").as_int()) {
314 const SigSpec A = shiftx->getPort("\\A");
315 const int A_width = shiftx->getParam("\\A_WIDTH").as_int();
316 for (int i = GetSize(taps); i < A_width; ++i)
317 if (A[i] != RTLIL::Sx) return false;
318 }
319 else if (GetSize(taps) != shiftx->getParam("\\A_WIDTH").as_int())
320 return false;
321 }
322 else if (shiftx->type == "$mux") {
323 if (GetSize(taps) != 2)
324 return false;
325 }
326 else log_abort();
327
328 return true;
329 }
330
331 virtual Cell* fixup(Cell *cell, const vector<int> &taps, const vector<SigBit> &qbits) override
332 {
333 auto bit = qbits[taps.front()];
334
335 auto it = sigbit_to_shiftx_offset.find(bit);
336 log_assert(it != sigbit_to_shiftx_offset.end());
337
338 Cell* newcell = ShregmapTechXilinx7Static::fixup(cell, taps, qbits);
339 log_assert(newcell);
340 log_assert(newcell->type == "$__SHREG_");
341 newcell->type = "$__XILINX_SHREG_";
342
343 Cell* shiftx = std::get<0>(it->second);
344 RTLIL::SigSpec l_wire;
345 if (shiftx->type == "$shiftx")
346 l_wire = shiftx->getPort("\\B");
347 else if (shiftx->type == "$mux")
348 l_wire = shiftx->getPort("\\S");
349 else log_abort();
350
351 newcell->setPort("\\L", l_wire);
352 newcell->setPort("\\Q", shiftx->getPort("\\Y"));
353 shiftx->setPort("\\Y", cell->module->addWire(NEW_ID));
354
355 return newcell;
356 }
357 };
358
359
360 struct ShregmapWorker
361 {
362 Module *module;
363 SigMap sigmap;
364
365 const ShregmapOptions &opts;
366 int dff_count, shreg_count;
367
368 pool<Cell*> remove_cells;
369 pool<SigBit> remove_init;
370
371 dict<SigBit, bool> sigbit_init;
372 dict<SigBit, Cell*> sigbit_chain_next;
373 dict<SigBit, Cell*> sigbit_chain_prev;
374 pool<SigBit> sigbit_with_non_chain_users;
375 pool<Cell*> chain_start_cells;
376
377 void make_sigbit_chain_next_prev()
378 {
379 for (auto wire : module->wires())
380 {
381 if (wire->port_output || wire->get_bool_attribute("\\keep")) {
382 for (auto bit : sigmap(wire)) {
383 sigbit_with_non_chain_users.insert(bit);
384 if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {});
385 }
386 }
387
388 if (wire->attributes.count("\\init")) {
389 SigSpec initsig = sigmap(wire);
390 Const initval = wire->attributes.at("\\init");
391 for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
392 if (initval[i] == State::S0 && !opts.zinit)
393 sigbit_init[initsig[i]] = false;
394 else if (initval[i] == State::S1)
395 sigbit_init[initsig[i]] = true;
396 }
397 }
398
399 for (auto cell : module->cells())
400 {
401 if (opts.ffcells.count(cell->type) && !cell->get_bool_attribute("\\keep"))
402 {
403 IdString d_port = opts.ffcells.at(cell->type).first;
404 IdString q_port = opts.ffcells.at(cell->type).second;
405
406 SigBit d_bit = sigmap(cell->getPort(d_port).as_bit());
407 SigBit q_bit = sigmap(cell->getPort(q_port).as_bit());
408
409 if (opts.init || sigbit_init.count(q_bit) == 0)
410 {
411 if (sigbit_chain_next.count(d_bit)) {
412 sigbit_with_non_chain_users.insert(d_bit);
413 } else
414 sigbit_chain_next[d_bit] = cell;
415
416 sigbit_chain_prev[q_bit] = cell;
417 continue;
418 }
419 }
420
421 for (auto conn : cell->connections())
422 if (cell->input(conn.first))
423 for (auto bit : sigmap(conn.second)) {
424 sigbit_with_non_chain_users.insert(bit);
425 if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first);
426 }
427 }
428 }
429
430 void find_chain_start_cells()
431 {
432 for (auto it : sigbit_chain_next)
433 {
434 if (opts.tech == nullptr && sigbit_with_non_chain_users.count(it.first))
435 goto start_cell;
436
437 if (sigbit_chain_prev.count(it.first) != 0)
438 {
439 Cell *c1 = sigbit_chain_prev.at(it.first);
440 Cell *c2 = it.second;
441
442 if (c1->type != c2->type)
443 goto start_cell;
444
445 if (c1->parameters != c2->parameters)
446 goto start_cell;
447
448 IdString d_port = opts.ffcells.at(c1->type).first;
449 IdString q_port = opts.ffcells.at(c1->type).second;
450
451 auto c1_conn = c1->connections();
452 auto c2_conn = c1->connections();
453
454 c1_conn.erase(d_port);
455 c1_conn.erase(q_port);
456
457 c2_conn.erase(d_port);
458 c2_conn.erase(q_port);
459
460 if (c1_conn != c2_conn)
461 goto start_cell;
462
463 continue;
464 }
465
466 start_cell:
467 chain_start_cells.insert(it.second);
468 }
469 }
470
471 vector<Cell*> create_chain(Cell *start_cell)
472 {
473 vector<Cell*> chain;
474
475 Cell *c = start_cell;
476 while (c != nullptr)
477 {
478 chain.push_back(c);
479
480 IdString q_port = opts.ffcells.at(c->type).second;
481 SigBit q_bit = sigmap(c->getPort(q_port).as_bit());
482
483 if (sigbit_chain_next.count(q_bit) == 0)
484 break;
485
486 c = sigbit_chain_next.at(q_bit);
487 if (chain_start_cells.count(c) != 0)
488 break;
489 }
490
491 return chain;
492 }
493
494 void process_chain(vector<Cell*> &chain)
495 {
496 if (GetSize(chain) < opts.keep_before + opts.minlen + opts.keep_after)
497 return;
498
499 int cursor = opts.keep_before;
500 while (cursor < GetSize(chain) - opts.keep_after)
501 {
502 int depth = GetSize(chain) - opts.keep_after - cursor;
503
504 if (opts.maxlen > 0)
505 depth = std::min(opts.maxlen, depth);
506
507 Cell *first_cell = chain[cursor];
508 IdString q_port = opts.ffcells.at(first_cell->type).second;
509 vector<SigBit> qbits;
510 vector<int> taps;
511
512 if (opts.tech)
513 {
514 if (!opts.tech->analyze_first(first_cell, sigmap)) {
515 cursor += depth;
516 continue;
517 }
518
519 for (int i = 0; i < depth; i++)
520 {
521 Cell *cell = chain[cursor+i];
522 auto qbit = sigmap(cell->getPort(q_port));
523 qbits.push_back(qbit);
524
525 if (sigbit_with_non_chain_users.count(qbit))
526 taps.push_back(i);
527 }
528
529 while (depth > 0)
530 {
531 if (taps.empty() || taps.back() < depth-1)
532 taps.push_back(depth-1);
533
534 if (opts.tech->analyze(taps, qbits))
535 break;
536
537 taps.pop_back();
538 depth--;
539 }
540
541 depth = 0;
542 for (auto tap : taps) {
543 log_assert(depth < tap+1);
544 depth = tap+1;
545 }
546 }
547
548 if (depth < 2) {
549 cursor++;
550 continue;
551 }
552
553 Cell *last_cell = chain[cursor+depth-1];
554
555 log("Converting %s.%s ... %s.%s to a shift register with depth %d.\n",
556 log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), depth);
557
558 dff_count += depth;
559 shreg_count += 1;
560
561 string shreg_cell_type_str = "$__SHREG";
562 if (opts.params) {
563 shreg_cell_type_str += "_";
564 } else {
565 if (first_cell->type[1] != '_')
566 shreg_cell_type_str += "_";
567 shreg_cell_type_str += first_cell->type.substr(1);
568 }
569
570 if (opts.init) {
571 vector<State> initval;
572 for (int i = depth-1; i >= 0; i--) {
573 SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
574 if (sigbit_init.count(bit) == 0)
575 initval.push_back(State::Sx);
576 else if (sigbit_init.at(bit))
577 initval.push_back(State::S1);
578 else
579 initval.push_back(State::S0);
580 remove_init.insert(bit);
581 }
582 first_cell->setParam("\\INIT", initval);
583 }
584
585 if (opts.zinit)
586 for (int i = depth-1; i >= 0; i--) {
587 SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
588 remove_init.insert(bit);
589 }
590
591 if (opts.params)
592 {
593 int param_clkpol = -1;
594 int param_enpol = 2;
595
596 if (first_cell->type == "$_DFF_N_") param_clkpol = 0;
597 if (first_cell->type == "$_DFF_P_") param_clkpol = 1;
598
599 if (first_cell->type == "$_DFFE_NN_") param_clkpol = 0, param_enpol = 0;
600 if (first_cell->type == "$_DFFE_NP_") param_clkpol = 0, param_enpol = 1;
601 if (first_cell->type == "$_DFFE_PN_") param_clkpol = 1, param_enpol = 0;
602 if (first_cell->type == "$_DFFE_PP_") param_clkpol = 1, param_enpol = 1;
603
604 log_assert(param_clkpol >= 0);
605 first_cell->setParam("\\CLKPOL", param_clkpol);
606 if (opts.ffe) first_cell->setParam("\\ENPOL", param_enpol);
607 }
608
609 first_cell->type = shreg_cell_type_str;
610 first_cell->setPort(q_port, last_cell->getPort(q_port));
611 first_cell->setParam("\\DEPTH", depth);
612
613 if (opts.tech != nullptr && opts.tech->fixup(first_cell, taps, qbits))
614 remove_cells.insert(first_cell);
615
616 for (int i = 1; i < depth; i++)
617 remove_cells.insert(chain[cursor+i]);
618 cursor += depth;
619 }
620 }
621
622 void cleanup()
623 {
624 for (auto cell : remove_cells)
625 module->remove(cell);
626
627 for (auto wire : module->wires())
628 {
629 if (wire->attributes.count("\\init") == 0)
630 continue;
631
632 SigSpec initsig = sigmap(wire);
633 Const &initval = wire->attributes.at("\\init");
634
635 for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
636 if (remove_init.count(initsig[i]))
637 initval[i] = State::Sx;
638
639 if (SigSpec(initval).is_fully_undef())
640 wire->attributes.erase("\\init");
641 }
642
643 remove_cells.clear();
644 sigbit_chain_next.clear();
645 sigbit_chain_prev.clear();
646 chain_start_cells.clear();
647 }
648
649 ShregmapWorker(Module *module, const ShregmapOptions &opts) :
650 module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
651 {
652 if (opts.tech)
653 opts.tech->init(module, sigmap);
654
655 make_sigbit_chain_next_prev();
656 find_chain_start_cells();
657
658 for (auto c : chain_start_cells) {
659 vector<Cell*> chain = create_chain(c);
660 process_chain(chain);
661 }
662
663 cleanup();
664 }
665 };
666
667 struct ShregmapPass : public Pass {
668 ShregmapPass() : Pass("shregmap", "map shift registers") { }
669 void help() YS_OVERRIDE
670 {
671 // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
672 log("\n");
673 log(" shregmap [options] [selection]\n");
674 log("\n");
675 log("This pass converts chains of $_DFF_[NP]_ gates to target specific shift register\n");
676 log("primitives. The generated shift register will be of type $__SHREG_DFF_[NP]_ and\n");
677 log("will use the same interface as the original $_DFF_*_ cells. The cell parameter\n");
678 log("'DEPTH' will contain the depth of the shift register. Use a target-specific\n");
679 log("'techmap' map file to convert those cells to the actual target cells.\n");
680 log("\n");
681 log(" -minlen N\n");
682 log(" minimum length of shift register (default = 2)\n");
683 log(" (this is the length after -keep_before and -keep_after)\n");
684 log("\n");
685 log(" -maxlen N\n");
686 log(" maximum length of shift register (default = no limit)\n");
687 log(" larger chains will be mapped to multiple shift register instances\n");
688 log("\n");
689 log(" -keep_before N\n");
690 log(" number of DFFs to keep before the shift register (default = 0)\n");
691 log("\n");
692 log(" -keep_after N\n");
693 log(" number of DFFs to keep after the shift register (default = 0)\n");
694 log("\n");
695 log(" -clkpol pos|neg|any\n");
696 log(" limit match to only positive or negative edge clocks. (default = any)\n");
697 log("\n");
698 log(" -enpol pos|neg|none|any_or_none|any\n");
699 log(" limit match to FFs with the specified enable polarity. (default = none)\n");
700 log("\n");
701 log(" -match <cell_type>[:<d_port_name>:<q_port_name>]\n");
702 log(" match the specified cells instead of $_DFF_N_ and $_DFF_P_. If\n");
703 log(" ':<d_port_name>:<q_port_name>' is omitted then 'D' and 'Q' is used\n");
704 log(" by default. E.g. the option '-clkpol pos' is just an alias for\n");
705 log(" '-match $_DFF_P_', which is an alias for '-match $_DFF_P_:D:Q'.\n");
706 log("\n");
707 log(" -params\n");
708 log(" instead of encoding the clock and enable polarity in the cell name by\n");
709 log(" deriving from the original cell name, simply name all generated cells\n");
710 log(" $__SHREG_ and use CLKPOL and ENPOL parameters. An ENPOL value of 2 is\n");
711 log(" used to denote cells without enable input. The ENPOL parameter is\n");
712 log(" omitted when '-enpol none' (or no -enpol option) is passed.\n");
713 log("\n");
714 log(" -zinit\n");
715 log(" assume the shift register is automatically zero-initialized, so it\n");
716 log(" becomes legal to merge zero initialized FFs into the shift register.\n");
717 log("\n");
718 log(" -init\n");
719 log(" map initialized registers to the shift reg, add an INIT parameter to\n");
720 log(" generated cells with the initialization value. (first bit to shift out\n");
721 log(" in LSB position)\n");
722 log("\n");
723 log(" -tech greenpak4\n");
724 log(" map to greenpak4 shift registers.\n");
725 log("\n");
726 }
727 void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
728 {
729 ShregmapOptions opts;
730 string clkpol, enpol;
731
732 log_header(design, "Executing SHREGMAP pass (map shift registers).\n");
733
734 size_t argidx;
735 for (argidx = 1; argidx < args.size(); argidx++)
736 {
737 if (args[argidx] == "-clkpol" && argidx+1 < args.size()) {
738 clkpol = args[++argidx];
739 continue;
740 }
741 if (args[argidx] == "-enpol" && argidx+1 < args.size()) {
742 enpol = args[++argidx];
743 continue;
744 }
745 if (args[argidx] == "-match" && argidx+1 < args.size()) {
746 vector<string> match_args = split_tokens(args[++argidx], ":");
747 if (GetSize(match_args) < 2)
748 match_args.push_back("D");
749 if (GetSize(match_args) < 3)
750 match_args.push_back("Q");
751 IdString id_cell_type(RTLIL::escape_id(match_args[0]));
752 IdString id_d_port_name(RTLIL::escape_id(match_args[1]));
753 IdString id_q_port_name(RTLIL::escape_id(match_args[2]));
754 opts.ffcells[id_cell_type] = make_pair(id_d_port_name, id_q_port_name);
755 continue;
756 }
757 if (args[argidx] == "-minlen" && argidx+1 < args.size()) {
758 opts.minlen = atoi(args[++argidx].c_str());
759 continue;
760 }
761 if (args[argidx] == "-maxlen" && argidx+1 < args.size()) {
762 opts.maxlen = atoi(args[++argidx].c_str());
763 continue;
764 }
765 if (args[argidx] == "-keep_before" && argidx+1 < args.size()) {
766 opts.keep_before = atoi(args[++argidx].c_str());
767 continue;
768 }
769 if (args[argidx] == "-keep_after" && argidx+1 < args.size()) {
770 opts.keep_after = atoi(args[++argidx].c_str());
771 continue;
772 }
773 if (args[argidx] == "-tech" && argidx+1 < args.size() && opts.tech == nullptr) {
774 string tech = args[++argidx];
775 if (tech == "greenpak4") {
776 clkpol = "pos";
777 opts.zinit = true;
778 opts.tech = new ShregmapTechGreenpak4;
779 }
780 else if (tech == "xilinx_static" || tech == "xilinx_dynamic") {
781 opts.init = true;
782 opts.ffcells["$_DFF_P_"] = make_pair(IdString("\\D"), IdString("\\Q"));
783 opts.ffcells["$_DFF_N_"] = make_pair(IdString("\\D"), IdString("\\Q"));
784 opts.ffcells["$_DFFE_PP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
785 opts.ffcells["$_DFFE_PN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
786 opts.ffcells["$_DFFE_NP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
787 opts.ffcells["$_DFFE_NN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
788 opts.ffcells["\\FDRE"] = make_pair(IdString("\\D"), IdString("\\Q"));
789 opts.ffcells["\\FDRE_1"] = make_pair(IdString("\\D"), IdString("\\Q"));
790 opts.ffcells["\\FDSE"] = make_pair(IdString("\\D"), IdString("\\Q"));
791 opts.ffcells["\\FDSE_1"] = make_pair(IdString("\\D"), IdString("\\Q"));
792 opts.ffcells["\\FDCE"] = make_pair(IdString("\\D"), IdString("\\Q"));
793 opts.ffcells["\\FDCE_1"] = make_pair(IdString("\\D"), IdString("\\Q"));
794 opts.ffcells["\\FDPE"] = make_pair(IdString("\\D"), IdString("\\Q"));
795 opts.ffcells["\\FDPE_1"] = make_pair(IdString("\\D"), IdString("\\Q"));
796 if (tech == "xilinx_static")
797 opts.tech = new ShregmapTechXilinx7Static(opts);
798 else if (tech == "xilinx_dynamic")
799 opts.tech = new ShregmapTechXilinx7Dynamic(opts);
800 } else {
801 argidx--;
802 break;
803 }
804 continue;
805 }
806 if (args[argidx] == "-zinit") {
807 opts.zinit = true;
808 continue;
809 }
810 if (args[argidx] == "-init") {
811 opts.init = true;
812 continue;
813 }
814 if (args[argidx] == "-params") {
815 opts.params = true;
816 continue;
817 }
818 break;
819 }
820 extra_args(args, argidx, design);
821
822 if (opts.zinit && opts.init)
823 log_cmd_error("Options -zinit and -init are exclusive!\n");
824
825 if (opts.ffcells.empty())
826 {
827 bool clk_pos = clkpol == "" || clkpol == "pos" || clkpol == "any";
828 bool clk_neg = clkpol == "" || clkpol == "neg" || clkpol == "any";
829
830 bool en_none = enpol == "" || enpol == "none" || enpol == "any_or_none";
831 bool en_pos = enpol == "pos" || enpol == "any" || enpol == "any_or_none";
832 bool en_neg = enpol == "neg" || enpol == "any" || enpol == "any_or_none";
833
834 if (clk_pos && en_none)
835 opts.ffcells["$_DFF_P_"] = make_pair(IdString("\\D"), IdString("\\Q"));
836 if (clk_neg && en_none)
837 opts.ffcells["$_DFF_N_"] = make_pair(IdString("\\D"), IdString("\\Q"));
838
839 if (clk_pos && en_pos)
840 opts.ffcells["$_DFFE_PP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
841 if (clk_pos && en_neg)
842 opts.ffcells["$_DFFE_PN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
843
844 if (clk_neg && en_pos)
845 opts.ffcells["$_DFFE_NP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
846 if (clk_neg && en_neg)
847 opts.ffcells["$_DFFE_NN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
848
849 if (en_pos || en_neg)
850 opts.ffe = true;
851 }
852 else
853 {
854 if (!clkpol.empty())
855 log_cmd_error("Options -clkpol and -match are exclusive!\n");
856 if (!enpol.empty())
857 log_cmd_error("Options -enpol and -match are exclusive!\n");
858 if (opts.params)
859 log_cmd_error("Options -params and -match are exclusive!\n");
860 }
861
862 int dff_count = 0;
863 int shreg_count = 0;
864
865 for (auto module : design->selected_modules()) {
866 ShregmapWorker worker(module, opts);
867 dff_count += worker.dff_count;
868 shreg_count += worker.shreg_count;
869 }
870
871 log("Converted %d dff cells into %d shift registers.\n", dff_count, shreg_count);
872
873 if (opts.tech != nullptr) {
874 delete opts.tech;
875 opts.tech = nullptr;
876 }
877 }
878 } ShregmapPass;
879
880 PRIVATE_NAMESPACE_END