Merge remote-tracking branch 'origin/master' into xc7dsp
[yosys.git] / passes / pmgen / xilinx_dsp_cascade.pmg
1 pattern xilinx_dsp_cascadeP
2
3 udata <std::function<SigSpec(const SigSpec&)>> unextend
4 state <SigSpec> sigC
5
6 code
7 unextend = [](const SigSpec &sig) {
8 int i;
9 for (i = GetSize(sig)-1; i > 0; i--)
10 if (sig[i] != sig[i-1])
11 break;
12 // Do not remove non-const sign bit
13 if (sig[i].wire)
14 ++i;
15 return sig.extract(0, i);
16 };
17 endcode
18
19 match dsp_pcin
20 select dsp_pcin->type.in(\DSP48E1)
21 select !param(dsp_pcin, \CREG, State::S1).as_bool()
22 select port(dsp_pcin, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011")
23 select nusers(port(dsp_pcin, \C, SigSpec())) > 1
24 select nusers(port(dsp_pcin, \PCIN, SigSpec())) == 0
25 endmatch
26
27 code sigC
28 sigC = unextend(port(dsp_pcin, \C));
29 endcode
30
31 match dsp_pcout
32 select dsp_pcout->type.in(\DSP48E1)
33 select nusers(port(dsp_pcout, \P, SigSpec())) > 1
34 select nusers(port(dsp_pcout, \PCOUT, SigSpec())) <= 1
35
36 index <SigBit> port(dsp_pcout, \P)[0] === sigC[0]
37 filter GetSize(port(dsp_pcin, \P)) >= GetSize(sigC)
38 filter port(dsp_pcout, \P).extract(0, GetSize(sigC)) == sigC
39
40 optional
41 endmatch
42
43 match dsp_pcout_shift17
44 if !dsp_pcout
45 select dsp_pcout_shift17->type.in(\DSP48E1)
46 select nusers(port(dsp_pcout_shift17, \P, SigSpec())) > 1
47 select nusers(port(dsp_pcout_shift17, \PCOUT, SigSpec())) <= 1
48
49 index <SigBit> port(dsp_pcout_shift17, \P)[17] === sigC[0]
50 filter GetSize(port(dsp_pcout_shift17, \P)) >= GetSize(sigC)+17
51 filter port(dsp_pcout_shift17, \P).extract(17, GetSize(sigC)) == sigC
52 endmatch
53
54 code
55 Cell *dsp;
56 if (dsp_pcout)
57 dsp = dsp_pcout;
58 else if (dsp_pcout_shift17)
59 dsp = dsp_pcout_shift17;
60 else log_abort();
61
62 dsp_pcin->setPort(ID(C), Const(0, 48));
63
64 Wire *cascade = module->addWire(NEW_ID, 48);
65 dsp_pcin->setPort(ID(PCIN), cascade);
66 dsp->setPort(ID(PCOUT), cascade);
67 add_siguser(cascade, dsp_pcin);
68 add_siguser(cascade, dsp);
69
70 SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7));
71 if (dsp_pcout)
72 opmode[6] = State::S0;
73 else if (dsp_pcout_shift17)
74 opmode[6] = State::S1;
75 else log_abort();
76
77 opmode[5] = State::S0;
78 opmode[4] = State::S1;
79 dsp_pcin->setPort(\OPMODE, opmode);
80
81 log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
82
83 if (nusers(port(dsp_pcin, \PCOUT, SigSpec())) > 1) {
84 log_debug(" Saturated PCIN/PCOUT on %s\n", log_id(dsp_pcin));
85 blacklist(dsp_pcin);
86 }
87 if (nusers(port(dsp, \PCIN, SigSpec())) > 1) {
88 log_debug(" Saturated PCIN/PCOUT on %s\n", log_id(dsp));
89 blacklist(dsp_pcout);
90 }
91
92 did_something = true;
93 accept;
94 endcode
95
96 // ##########
97
98 pattern xilinx_dsp_cascadeAB
99
100 udata <std::function<SigSpec(const SigSpec&)>> unextend
101 state <SigBit> clock
102 state <SigSpec> sigA sigB
103
104 state <bool> ffA1cepol ffA2cepol ffB1cepol ffB2cepol
105 state <bool> ffArstpol ffBrstpol
106
107 state <Cell*> ffA1 ffA1cemux ffA1rstmux ffA2 ffA2cemux ffA2rstmux
108 state <Cell*> ffB1 ffB1cemux ffB1rstmux ffB2 ffB2cemux ffB2rstmux
109
110 // subpattern
111 state <SigSpec> argQ argD
112 state <bool> ffcepol ffrstpol
113 state <int> ffoffset
114 udata <SigSpec> dffD dffQ
115 udata <SigBit> dffclock
116 udata <Cell*> dff dffcemux dffrstmux
117 udata <bool> dffcepol dffrstpol
118
119 code
120 unextend = [](const SigSpec &sig) {
121 int i;
122 for (i = GetSize(sig)-1; i > 0; i--)
123 if (sig[i] != sig[i-1])
124 break;
125 // Do not remove non-const sign bit
126 if (sig[i].wire)
127 ++i;
128 return sig.extract(0, i);
129 };
130 endcode
131
132 match dspD
133 select dspD->type.in(\DSP48E1)
134 select (param(dspD, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && nusers(port(dspD, \A, SigSpec())) > 1 && nusers(port(dspD, \ACIN, SigSpec())) == 0) || (param(dspD, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && nusers(port(dspD, \B, SigSpec())) > 1 && nusers(port(dspD, \BCIN, SigSpec())) == 0)
135 endmatch
136
137 code sigA sigB
138 if (param(dspD, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT")
139 sigA = unextend(port(dspD, \A));
140 if (param(dspD, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT")
141 sigB = unextend(port(dspD, \B));
142 endcode
143
144 code argQ ffA2 ffA2cemux ffA2rstmux ffA2cepol ffArstpol ffA1 ffA1cemux ffA1rstmux ffA1cepol sigA clock
145 if (!sigA.empty()) {
146 argQ = sigA;
147 subpattern(in_dffe);
148 if (dff) {
149 ffA2 = dff;
150 clock = dffclock;
151 if (dffrstmux) {
152 ffA2rstmux = dffrstmux;
153 ffArstpol = dffrstpol;
154 }
155 if (dffcemux) {
156 ffA2cemux = dffcemux;
157 ffA2cepol = dffcepol;
158 }
159 sigA = dffD;
160
161 // Now attempt to match A1
162 argQ = sigA;
163 subpattern(in_dffe);
164 if (dff) {
165 if ((ffA2rstmux != nullptr) ^ (dffrstmux != nullptr))
166 goto reject_ffA1;
167 if (dffrstmux) {
168 if (ffArstpol != dffrstpol)
169 goto reject_ffA1;
170 if (port(ffA2rstmux, \S) != port(dffrstmux, \S))
171 goto reject_ffA1;
172 ffA1rstmux = dffrstmux;
173 }
174
175 ffA1 = dff;
176 clock = dffclock;
177
178 if (dffcemux) {
179 ffA1cemux = dffcemux;
180 ffA1cepol = dffcepol;
181 }
182 sigA = dffD;
183
184 reject_ffA1: ;
185 }
186 }
187 }
188 endcode
189
190 match dspQA2
191 if ffA1
192 select dspQA2->type.in(\DSP48E1)
193 select param(dspQA2, \A_REG, 2).as_int() == 2
194 select nusers(port(dspQA2, \A, SigSpec())) > 1
195 select nusers(port(dspQA2, \ACOUT, SigSpec())) == 0
196 slice offset GetSize(port(dspQA2, \A))
197 index <SigBit> port(dspQA2, \A)[offset] === sigA[0]
198 index <SigBit> port(dspQA2, \CLK, State::S0) === port(dspD, \CLK, State::S0)
199
200 // Check that the rest of sigA is present
201 filter GetSize(port(dspQA2, \A)) >= offset + GetSize(sigA)
202 filter port(dspQA2, \A).extract(offset, GetSize(sigA)) == sigA
203
204 optional
205 endmatch
206
207 code
208 if (dspQA2) {
209 // Check CE and RST are compatible
210 if ((ffA1cemux != nullptr) == port(dspQA2, \CEA1, State::S1).is_fully_const())
211 reject;
212 if ((ffA2cemux != nullptr) == port(dspQA2, \CEA2, State::S1).is_fully_const())
213 reject;
214 if ((ffA1rstmux != nullptr) == port(dspQA2, \RSTA, State::S0).is_fully_const())
215 reject;
216 if ((ffA2rstmux != nullptr) == port(dspQA2, \RSTA, State::S0).is_fully_const())
217 reject;
218
219 if (ffA1cemux) {
220 if (port(dspQA2, \CEA1) != port(ffA1cemux, \S))
221 reject;
222 // TODO: Support inversions
223 if (!ffA1cepol)
224 reject;
225 }
226 if (ffA2cemux) {
227 if (port(dspQA2, \CEA2) != port(ffA2cemux, \S))
228 reject;
229 // TODO: Support inversions
230 if (!ffA2cepol)
231 reject;
232 }
233 if (ffA1rstmux) {
234 if (port(dspQA2, \RSTA) != port(ffA1rstmux, \S))
235 reject;
236 // TODO: Support inversions
237 if (!ffArstpol)
238 reject;
239 }
240 if (ffA2rstmux) {
241 if (port(dspQA2, \RSTA) != port(ffA2rstmux, \S))
242 reject;
243 // TODO: Support inversions
244 if (!ffArstpol)
245 reject;
246 }
247 }
248 endcode
249
250 match dspQA1
251 if !dspQA1 && !ffA1
252 if ffA2
253 select dspQA1->type.in(\DSP48E1)
254 select param(dspQA1, \A_REG, 2).as_int() == 1
255 select nusers(port(dspQA1, \A, SigSpec())) > 1
256 select nusers(port(dspQA1, \ACOUT, SigSpec())) == 0
257 slice offset GetSize(port(dspQA1, \A))
258 index <SigBit> port(dspQA1, \A)[offset] === sigA[0]
259 index <SigBit> port(dspQA1, \CLK, State::S0) === port(dspD, \CLK, State::S0)
260
261 // Check that the rest of sigA is present
262 filter GetSize(port(dspQA1, \A)) >= offset + GetSize(sigA)
263 filter port(dspQA1, \A).extract(offset, GetSize(sigA)) == sigA
264
265 optional
266 endmatch
267
268 code
269 if (dspQA1) {
270 // Check CE and RST are compatible
271 if ((ffA2cemux != NULL) == port(dspQA1, \CEA2, State::S1).is_fully_const())
272 reject;
273 if ((ffA2rstmux != NULL) == port(dspQA1, \RSTA, State::S0).is_fully_const())
274 reject;
275
276 if (!ffA2cepol || !ffArstpol)
277 reject;
278
279 if (ffA2cemux) {
280 if (port(dspQA1, \CEA2) != port(ffA2cemux, \S))
281 reject;
282 // TODO: Support inversions
283 if (!ffA2cepol)
284 reject;
285 }
286 if (ffA2rstmux) {
287 if (port(dspQA1, \RSTA) != port(ffA2rstmux, \S))
288 reject;
289 // TODO: Support inversions
290 if (!ffArstpol)
291 reject;
292 }
293 }
294 endcode
295
296 code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol ffB1 ffB1cemux ffB1rstmux ffB1cepol sigB clock
297 if (!sigB.empty()) {
298 argQ = sigB;
299 subpattern(in_dffe);
300 if (dff) {
301 ffB2 = dff;
302 clock = dffclock;
303 if (dffrstmux) {
304 ffB2rstmux = dffrstmux;
305 ffBrstpol = dffrstpol;
306 }
307 if (dffcemux) {
308 ffB2cemux = dffcemux;
309 ffB2cepol = dffcepol;
310 }
311 sigB = dffD;
312
313 // Now attempt to match B1
314 argQ = sigB;
315 subpattern(in_dffe);
316 if (dff) {
317 if ((ffB2rstmux != nullptr) ^ (dffrstmux != nullptr))
318 goto reject_ffB1;
319 if (dffrstmux) {
320 if (ffBrstpol != dffrstpol)
321 goto reject_ffB1;
322 if (port(ffB2rstmux, \S) != port(dffrstmux, \S))
323 goto reject_ffB1;
324 ffB1rstmux = dffrstmux;
325 }
326
327 ffB1 = dff;
328 clock = dffclock;
329
330 if (dffcemux) {
331 ffB1cemux = dffcemux;
332 ffB1cepol = dffcepol;
333 }
334 sigB = dffD;
335
336 reject_ffB1: ;
337 }
338 }
339 }
340 endcode
341
342 match dspQB2
343 if ffB1
344 select dspQB2->type.in(\DSP48E1)
345 select param(dspQB2, \B_REG, 2).as_int() == 2
346 select nusers(port(dspQB2, \B, SigSpec())) > 1
347 select nusers(port(dspQB2, \BCOUT, SigSpec())) == 0
348 slice offset GetSize(port(dspQB2, \B))
349 index <SigBit> port(dspQB2, \B)[offset] === sigB[0]
350 index <SigBit> port(dspQB2, \CLK, State::S0) === port(dspD, \CLK, State::S0)
351
352 // Check that the rest of sigB is present
353 filter GetSize(port(dspQB2, \B)) >= offset + GetSize(sigB)
354 filter port(dspQB2, \B).extract(offset, GetSize(sigB)) == sigB
355
356 optional
357 endmatch
358
359 code
360 if (dspQB2) {
361 // Check CE and RST are compatible
362 if ((ffB1cemux != nullptr) == port(dspQB2, \CEB1, State::S1).is_fully_const())
363 reject;
364 if ((ffB2cemux != NULL) == port(dspQB2, \CEB2, State::S1).is_fully_const())
365 reject;
366 if ((ffB1rstmux != NULL) == port(dspQB2, \RSTB, State::S0).is_fully_const())
367 reject;
368 if ((ffB2rstmux != NULL) == port(dspQB2, \RSTB, State::S0).is_fully_const())
369 reject;
370
371 if (ffB1cemux) {
372 if (port(dspQB2, \CEB1) != port(ffB1cemux, \S))
373 reject;
374 // TODO: Support inversions
375 if (!ffB1cepol)
376 reject;
377 }
378 if (ffB2cemux) {
379 if (port(dspQB2, \CEB2) != port(ffB2cemux, \S))
380 reject;
381 // TODO: Support inversions
382 if (!ffB2cepol)
383 reject;
384 }
385 if (ffB2rstmux) {
386 if (port(dspQB2, \RSTB) != port(ffB2rstmux, \S))
387 reject;
388 // TODO: Support inversions
389 if (!ffBrstpol)
390 reject;
391 }
392 }
393 endcode
394
395 match dspQB1
396 if !dspQB1 && !ffB1
397 if ffB2
398 select dspQB1->type.in(\DSP48E1)
399 select param(dspQB1, \B_REG, 2).as_int() >= 1
400 select nusers(port(dspQB1, \B, SigSpec())) > 1
401 select nusers(port(dspQB1, \BCOUT, SigSpec())) == 0
402 slice offset GetSize(port(dspQB1, \B))
403 index <SigBit> port(dspQB1, \B)[offset] === sigB[0]
404 index <SigBit> port(dspQB1, \CLK, State::S0) === port(dspD, \CLK, State::S0)
405
406 // Check that the rest of sigB is present
407 filter GetSize(port(dspQB1, \B)) >= offset + GetSize(sigB)
408 filter port(dspQB1, \B).extract(offset, GetSize(sigB)) == sigB
409
410 optional
411 endmatch
412
413 code
414 if (dspQB1) {
415 // Check CE and RST are compatible
416 if ((ffB2cemux != NULL) != port(dspQB1, \CEB2, State::S1).is_fully_const())
417 reject;
418 if ((ffB2rstmux != NULL) != port(dspQB1, \RSTB, State::S0).is_fully_const())
419 reject;
420
421 if (!ffA2cepol || !ffArstpol)
422 reject;
423
424 if (ffA2cemux) {
425 if (port(dspQB1, \CEB2) != port(ffB2cemux, \S))
426 reject;
427 // TODO: Support inversions
428 if (!ffA2cepol)
429 reject;
430 }
431 if (ffA2rstmux) {
432 if (port(dspQB1, \RSTB) != port(ffB2rstmux, \S))
433 reject;
434 // TODO: Support inversions
435 if (!ffArstpol)
436 reject;
437 }
438 }
439 endcode
440
441 code
442 if (dspQA1 || dspQA2) {
443 dspD->setParam(\A_INPUT, Const("CASCADE"));
444 dspD->setPort(\A, Const(0, 30));
445
446 Wire *cascade = module->addWire(NEW_ID, 30);
447 if (dspQA1) {
448 dspQA1->setParam(\ACASCREG, 1);
449 dspQA1->setPort(\ACOUT, cascade);
450 log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dspQA1), log_id(dspD));
451 }
452 else if (dspQA2) {
453 dspQA2->setParam(\ACASCREG, 2);
454 dspQA2->setPort(\ACOUT, cascade);
455 log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dspQA2), log_id(dspD));
456 }
457 else
458 log_abort();
459
460 dspD->setPort(\ACIN, cascade);
461 did_something = true;
462 }
463 if (dspQB1 || dspQB2) {
464 dspD->setParam(\B_INPUT, Const("CASCADE"));
465 dspD->setPort(\B, Const(0, 18));
466
467 Wire *cascade = module->addWire(NEW_ID, 18);
468 if (dspQB1) {
469 dspQB1->setParam(\BCASCREG, 1);
470 dspQB1->setPort(\BCOUT, cascade);
471 log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dspQB1), log_id(dspD));
472 }
473 else if (dspQB2) {
474 dspQB2->setParam(\BCASCREG, 2);
475 dspQB2->setPort(\BCOUT, cascade);
476 log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dspQB2), log_id(dspD));
477 }
478 else
479 log_abort();
480
481 dspD->setPort(\BCIN, cascade);
482 did_something = true;
483 }
484
485 accept;
486 endcode
487
488
489 // #######################
490
491 subpattern in_dffe
492 arg argD argQ clock
493
494 code
495 dff = nullptr;
496 for (auto c : argQ.chunks()) {
497 if (!c.wire)
498 reject;
499 if (c.wire->get_bool_attribute(\keep))
500 reject;
501 }
502 endcode
503
504 match ff
505 select ff->type.in($dff)
506 // DSP48E1 does not support clock inversion
507 select param(ff, \CLK_POLARITY).as_bool()
508
509 slice offset GetSize(port(ff, \D))
510 index <SigBit> port(ff, \Q)[offset] === argQ[0]
511
512 // Check that the rest of argQ is present
513 filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
514 filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
515
516 set ffoffset offset
517 endmatch
518
519 code argQ argD
520 {
521 if (clock != SigBit() && port(ff, \CLK) != clock)
522 reject;
523
524 SigSpec Q = port(ff, \Q);
525 dff = ff;
526 dffclock = port(ff, \CLK);
527 dffD = argQ;
528 argD = port(ff, \D);
529 argQ = Q;
530 dffD.replace(argQ, argD);
531 // Only search for ffrstmux if dffD only
532 // has two (ff, ffrstmux) users
533 if (nusers(dffD) > 2)
534 argD = SigSpec();
535 }
536 endcode
537
538 match ffrstmux
539 if !argD.empty()
540 select ffrstmux->type.in($mux)
541 index <SigSpec> port(ffrstmux, \Y) === argD
542
543 choice <IdString> BA {\B, \A}
544 // DSP48E1 only supports reset to zero
545 select port(ffrstmux, BA).is_fully_zero()
546
547 define <bool> pol (BA == \B)
548 set ffrstpol pol
549 semioptional
550 endmatch
551
552 code argD
553 if (ffrstmux) {
554 dffrstmux = ffrstmux;
555 dffrstpol = ffrstpol;
556 argD = port(ffrstmux, ffrstpol ? \A : \B);
557 dffD.replace(port(ffrstmux, \Y), argD);
558
559 // Only search for ffcemux if argQ has at
560 // least 3 users (ff, <upstream>, ffrstmux) and
561 // dffD only has two (ff, ffrstmux)
562 if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
563 argD = SigSpec();
564 }
565 else
566 dffrstmux = nullptr;
567 endcode
568
569 match ffcemux
570 if !argD.empty()
571 select ffcemux->type.in($mux)
572 index <SigSpec> port(ffcemux, \Y) === argD
573 choice <IdString> AB {\A, \B}
574 index <SigSpec> port(ffcemux, AB) === argQ
575 define <bool> pol (AB == \A)
576 set ffcepol pol
577 semioptional
578 endmatch
579
580 code argD
581 if (ffcemux) {
582 dffcemux = ffcemux;
583 dffcepol = ffcepol;
584 argD = port(ffcemux, ffcepol ? \B : \A);
585 dffD.replace(port(ffcemux, \Y), argD);
586 }
587 else
588 dffcemux = nullptr;
589 endcode