SB_MAC16 ffCD to not pack same as ffO
[yosys.git] / passes / pmgen / ice40_dsp.pmg
1 pattern ice40_dsp
2
3 state <SigBit> clock
4 state <bool> clock_pol cd_signed o_lo
5 state <SigSpec> sigA sigB sigCD sigH sigO
6 state <Cell*> add mux
7 state <IdString> addAB muxAB
8
9 state <bool> ffAholdpol ffBholdpol ffCDholdpol ffOholdpol
10 state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol
11
12 state <Cell*> ffA ffAholdmux ffArstmux ffB ffBholdmux ffBrstmux ffCD ffCDholdmux
13 state <Cell*> ffFJKG ffH ffO ffOholdmux ffOrstmux
14
15 // subpattern
16 state <SigSpec> argQ argD
17 state <bool> ffholdpol ffrstpol
18 state <int> ffoffset
19 udata <SigSpec> dffD dffQ
20 udata <SigBit> dffclock
21 udata <Cell*> dff dffholdmux dffrstmux
22 udata <bool> dffholdpol dffrstpol dffclock_pol
23
24 match mul
25 select mul->type.in($mul, \SB_MAC16)
26 select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
27 endmatch
28
29 code sigA sigB sigH
30 SigSpec O;
31 if (mul->type == $mul)
32 O = mul->getPort(\Y);
33 else if (mul->type == \SB_MAC16)
34 O = mul->getPort(\O);
35 else log_abort();
36 if (GetSize(O) <= 10)
37 reject;
38
39 sigA = port(mul, \A);
40 int i;
41 for (i = GetSize(sigA)-1; i > 0; i--)
42 if (sigA[i] != sigA[i-1])
43 break;
44 // Do not remove non-const sign bit
45 if (sigA[i].wire)
46 ++i;
47 sigA.remove(i, GetSize(sigA)-i);
48 sigB = port(mul, \B);
49 for (i = GetSize(sigB)-1; i > 0; i--)
50 if (sigB[i] != sigB[i-1])
51 break;
52 // Do not remove non-const sign bit
53 if (sigB[i].wire)
54 ++i;
55 sigB.remove(i, GetSize(sigB)-i);
56
57 // Only care about those bits that are used
58 for (i = 0; i < GetSize(O); i++) {
59 if (nusers(O[i]) <= 1)
60 break;
61 sigH.append(O[i]);
62 }
63 log_assert(nusers(O.extract_end(i)) <= 1);
64 endcode
65
66 code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
67 if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) {
68 argQ = sigA;
69 subpattern(in_dffe);
70 if (dff) {
71 ffA = dff;
72 clock = dffclock;
73 clock_pol = dffclock_pol;
74 if (dffrstmux) {
75 ffArstmux = dffrstmux;
76 ffArstpol = dffrstpol;
77 }
78 if (dffholdmux) {
79 ffAholdmux = dffholdmux;
80 ffAholdpol = dffholdpol;
81 }
82 sigA = dffD;
83 }
84 }
85 endcode
86
87 code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
88 if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) {
89 argQ = sigB;
90 subpattern(in_dffe);
91 if (dff) {
92 ffB = dff;
93 clock = dffclock;
94 clock_pol = dffclock_pol;
95 if (dffrstmux) {
96 ffBrstmux = dffrstmux;
97 ffBrstpol = dffrstpol;
98 }
99 if (dffholdmux) {
100 ffBholdmux = dffholdmux;
101 ffBholdpol = dffholdpol;
102 }
103 sigB = dffD;
104 }
105 }
106 endcode
107
108 code argD ffFJKG sigH sigO clock clock_pol
109 if (nusers(sigH) == 2 &&
110 (mul->type != \SB_MAC16 ||
111 (!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool()))) {
112 argD = sigH;
113 subpattern(out_dffe);
114 if (dff) {
115 // F/J/K/G do not have a CE-like (hold) input
116 if (dffholdmux)
117 goto reject_ffFJKG;
118
119 // Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT)
120 // shared with A and B
121 if ((ffArstmux != NULL) != (dffrstmux != NULL))
122 goto reject_ffFJKG;
123 if ((ffBrstmux != NULL) != (dffrstmux != NULL))
124 goto reject_ffFJKG;
125 if (ffArstmux) {
126 if (port(ffArstmux, \S) != port(dffrstmux, \S))
127 goto reject_ffFJKG;
128 if (ffArstpol != dffrstpol)
129 goto reject_ffFJKG;
130 }
131 if (ffBrstmux) {
132 if (port(ffBrstmux, \S) != port(dffrstmux, \S))
133 goto reject_ffFJKG;
134 if (ffBrstpol != dffrstpol)
135 goto reject_ffFJKG;
136 }
137
138 ffFJKG = dff;
139 clock = dffclock;
140 clock_pol = dffclock_pol;
141 sigH = dffQ;
142 }
143 }
144
145 if (0) {
146 reject_ffFJKG: ;
147 }
148 endcode
149
150 code argD ffH sigH sigO clock clock_pol
151 if (ffFJKG && nusers(sigH) == 2 &&
152 (mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) {
153 argD = sigH;
154 subpattern(out_dffe);
155 if (dff) {
156 // H does not have a CE-like (hold) input
157 if (dffholdmux)
158 goto reject_ffH;
159
160 // Reset signal of H (IRSTBOT) shared with B
161 if ((ffBrstmux != NULL) != (dffrstmux != NULL))
162 goto reject_ffH;
163 if (ffBrstmux) {
164 if (port(ffBrstmux, \S) != port(dffrstmux, \S))
165 goto reject_ffH;
166 if (ffBrstpol != dffrstpol)
167 goto reject_ffH;
168 }
169
170 ffH = dff;
171 clock = dffclock;
172 clock_pol = dffclock_pol;
173 sigH = dffQ;
174 }
175 }
176
177 if (0) {
178 reject_ffH: ;
179 }
180
181 sigO = sigH;
182 endcode
183
184 match add
185 if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT).as_int() == 3)
186 select add->type.in($add)
187 choice <IdString> AB {\A, \B}
188 select nusers(port(add, AB)) == 2
189 index <SigBit> port(add, AB)[0] === sigH[0]
190 filter GetSize(port(add, AB)) <= GetSize(sigH)
191 filter port(add, AB) == sigH.extract(0, GetSize(port(add, AB)))
192 filter nusers(sigH.extract_end(GetSize(port(add, AB)))) <= 1
193 set addAB AB
194 optional
195 endmatch
196
197 code sigCD sigO cd_signed
198 if (add) {
199 sigCD = port(add, addAB == \A ? \B : \A);
200 cd_signed = param(add, addAB == \A ? \B_SIGNED : \A_SIGNED).as_bool();
201
202 int natural_mul_width = GetSize(sigA) + GetSize(sigB);
203 int actual_mul_width = GetSize(sigH);
204 int actual_acc_width = GetSize(sigCD);
205
206 if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
207 reject;
208 // If accumulator, check adder width and signedness
209 if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(add, \A_SIGNED).as_bool()))
210 reject;
211
212 sigO = port(add, \Y);
213 }
214 endcode
215
216 match mux
217 select mux->type == $mux
218 choice <IdString> AB {\A, \B}
219 select nusers(port(mux, AB)) == 2
220 index <SigSpec> port(mux, AB) === sigO
221 set muxAB AB
222 optional
223 endmatch
224
225 code sigO
226 if (mux)
227 sigO = port(mux, \Y);
228 endcode
229
230 code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo
231 if (mul->type != \SB_MAC16 ||
232 // Ensure that register is not already used
233 ((mul->parameters.at(\TOPOUTPUT_SELECT, 0).as_int() != 1 && mul->parameters.at(\BOTOUTPUT_SELECT, 0).as_int() != 1) &&
234 // Ensure that OLOADTOP/OLOADBOT is unused or zero
235 (mul->connections_.at(\OLOADTOP, State::S0).is_fully_zero() && mul->connections_.at(\OLOADBOT, State::S0).is_fully_zero()))) {
236
237 dff = nullptr;
238
239 // First try entire sigO
240 if (nusers(sigO) == 2) {
241 argD = sigO;
242 subpattern(out_dffe);
243 }
244
245 // Otherwise try just its least significant 16 bits
246 if (!dff && GetSize(sigO) > 16) {
247 argD = sigO.extract(0, 16);
248 if (nusers(argD) == 2) {
249 subpattern(out_dffe);
250 o_lo = dff;
251 }
252 }
253
254 if (dff) {
255 ffO = dff;
256 clock = dffclock;
257 clock_pol = dffclock_pol;
258 if (dffrstmux) {
259 ffOrstmux = dffrstmux;
260 ffOrstpol = dffrstpol;
261 }
262 if (dffholdmux) {
263 ffOholdmux = dffholdmux;
264 ffOholdpol = dffholdpol;
265 }
266
267 sigO.replace(sigO.extract(0, GetSize(dffQ)), dffQ);
268 }
269
270 // Loading value into output register is not
271 // supported unless using accumulator
272 if (mux) {
273 if (sigCD != sigO)
274 reject;
275 sigCD = port(mux, muxAB == \B ? \A : \B);
276
277 cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
278 }
279 }
280 endcode
281
282 code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol
283 if (!sigCD.empty() && sigCD != sigO &&
284 (mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) {
285 argQ = sigCD;
286 subpattern(in_dffe);
287 if (dff) {
288 if (dffholdmux) {
289 ffCDholdmux = dffholdmux;
290 ffCDholdpol = dffholdpol;
291 }
292
293 // Reset signal of C (IRSTTOP) and D (IRSTBOT)
294 // shared with A and B
295 if ((ffArstmux != NULL) != (dffrstmux != NULL))
296 goto reject_ffCD;
297 if ((ffBrstmux != NULL) != (dffrstmux != NULL))
298 goto reject_ffCD;
299 if (ffArstmux) {
300 if (port(ffArstmux, \S) != port(dffrstmux, \S))
301 goto reject_ffCD;
302 if (ffArstpol != dffrstpol)
303 goto reject_ffCD;
304 }
305 if (ffBrstmux) {
306 if (port(ffBrstmux, \S) != port(dffrstmux, \S))
307 goto reject_ffCD;
308 if (ffBrstpol != dffrstpol)
309 goto reject_ffCD;
310 }
311
312 ffCD = dff;
313 clock = dffclock;
314 clock_pol = dffclock_pol;
315 sigCD = dffD;
316 }
317 }
318
319 if (0) {
320 reject_ffCD: ;
321 }
322 endcode
323
324 code sigCD
325 sigCD.extend_u0(32, cd_signed);
326 endcode
327
328 code
329 accept;
330 endcode
331
332 // #######################
333
334 subpattern in_dffe
335 arg argD argQ clock clock_pol
336
337 code
338 dff = nullptr;
339 for (auto c : argQ.chunks()) {
340 if (!c.wire)
341 reject;
342 if (c.wire->get_bool_attribute(\keep))
343 reject;
344 }
345 endcode
346
347 match ff
348 select ff->type.in($dff)
349 // DSP48E1 does not support clock inversion
350 select param(ff, \CLK_POLARITY).as_bool()
351
352 slice offset GetSize(port(ff, \D))
353 index <SigBit> port(ff, \Q)[offset] === argQ[0]
354
355 // Check that the rest of argQ is present
356 filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
357 filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
358
359 set ffoffset offset
360 endmatch
361
362 code argQ argD
363 {
364 if (clock != SigBit()) {
365 if (port(ff, \CLK) != clock)
366 reject;
367 if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
368 reject;
369 }
370
371 SigSpec Q = port(ff, \Q);
372 dff = ff;
373 dffclock = port(ff, \CLK);
374 dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
375 dffD = argQ;
376 argD = port(ff, \D);
377 argQ = Q;
378 dffD.replace(argQ, argD);
379 // Only search for ffrstmux if dffD only
380 // has two (ff, ffrstmux) users
381 if (nusers(dffD) > 2)
382 argD = SigSpec();
383 }
384 endcode
385
386 match ffrstmux
387 if !argD.empty()
388 select ffrstmux->type.in($mux)
389 index <SigSpec> port(ffrstmux, \Y) === argD
390
391 choice <IdString> BA {\B, \A}
392 // DSP48E1 only supports reset to zero
393 select port(ffrstmux, BA).is_fully_zero()
394
395 define <bool> pol (BA == \B)
396 set ffrstpol pol
397 semioptional
398 endmatch
399
400 code argD
401 if (ffrstmux) {
402 dffrstmux = ffrstmux;
403 dffrstpol = ffrstpol;
404 argD = port(ffrstmux, ffrstpol ? \A : \B);
405 dffD.replace(port(ffrstmux, \Y), argD);
406
407 // Only search for ffholdmux if argQ has at
408 // least 3 users (ff, <upstream>, ffrstmux) and
409 // dffD only has two (ff, ffrstmux)
410 if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
411 argD = SigSpec();
412 }
413 else
414 dffrstmux = nullptr;
415 endcode
416
417 match ffholdmux
418 if !argD.empty()
419 select ffholdmux->type.in($mux)
420 index <SigSpec> port(ffholdmux, \Y) === argD
421 choice <IdString> BA {\B, \A}
422 index <SigSpec> port(ffholdmux, BA) === argQ
423 define <bool> pol (BA == \B)
424 set ffholdpol pol
425 semioptional
426 endmatch
427
428 code argD
429 if (ffholdmux) {
430 dffholdmux = ffholdmux;
431 dffholdpol = ffholdpol;
432 argD = port(ffholdmux, ffholdpol ? \A : \B);
433 dffD.replace(port(ffholdmux, \Y), argD);
434 }
435 else
436 dffholdmux = nullptr;
437 endcode
438
439 // #######################
440
441 subpattern out_dffe
442 arg argD argQ clock clock_pol
443
444 code
445 dff = nullptr;
446 for (auto c : argD.chunks())
447 if (c.wire->get_bool_attribute(\keep))
448 reject;
449 endcode
450
451 match ffholdmux
452 select ffholdmux->type.in($mux)
453 // ffholdmux output must have two users: ffholdmux and ff.D
454 select nusers(port(ffholdmux, \Y)) == 2
455
456 choice <IdString> BA {\B, \A}
457 // keep-last-value net must have at least three users: ffholdmux, ff, downstream sink(s)
458 select nusers(port(ffholdmux, BA)) >= 3
459
460 slice offset GetSize(port(ffholdmux, \Y))
461 define <IdString> AB (BA == \B ? \A : \B)
462 index <SigBit> port(ffholdmux, AB)[offset] === argD[0]
463
464 // Check that the rest of argD is present
465 filter GetSize(port(ffholdmux, AB)) >= offset + GetSize(argD)
466 filter port(ffholdmux, AB).extract(offset, GetSize(argD)) == argD
467
468 set ffoffset offset
469 define <bool> pol (BA == \B)
470 set ffholdpol pol
471
472 semioptional
473 endmatch
474
475 code argD argQ
476 dffholdmux = ffholdmux;
477 if (ffholdmux) {
478 SigSpec AB = port(ffholdmux, ffholdpol ? \A : \B);
479 SigSpec Y = port(ffholdmux, \Y);
480 argQ = argD;
481 argD.replace(AB, Y);
482 argQ.replace(AB, port(ffholdmux, ffholdpol ? \B : \A));
483
484 dffholdmux = ffholdmux;
485 dffholdpol = ffholdpol;
486 }
487 endcode
488
489 match ffrstmux
490 select ffrstmux->type.in($mux)
491 // ffrstmux output must have two users: ffrstmux and ff.D
492 select nusers(port(ffrstmux, \Y)) == 2
493
494 choice <IdString> BA {\B, \A}
495 // DSP48E1 only supports reset to zero
496 select port(ffrstmux, BA).is_fully_zero()
497
498 slice offset GetSize(port(ffrstmux, \Y))
499 define <IdString> AB (BA == \B ? \A : \B)
500 index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
501
502 // Check that offset is consistent
503 filter !ffholdmux || ffoffset == offset
504 // Check that the rest of argD is present
505 filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
506 filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
507
508 set ffoffset offset
509 define <bool> pol (AB == \A)
510 set ffrstpol pol
511
512 semioptional
513 endmatch
514
515 code argD argQ
516 dffrstmux = ffrstmux;
517 if (ffrstmux) {
518 SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
519 SigSpec Y = port(ffrstmux, \Y);
520 argD.replace(AB, Y);
521
522 dffrstmux = ffrstmux;
523 dffrstpol = ffrstpol;
524 }
525 endcode
526
527 match ff
528 select ff->type.in($dff)
529 // DSP48E1 does not support clock inversion
530 select param(ff, \CLK_POLARITY).as_bool()
531
532 slice offset GetSize(port(ff, \D))
533 index <SigBit> port(ff, \D)[offset] === argD[0]
534
535 // Check that offset is consistent
536 filter (!ffholdmux && !ffrstmux) || ffoffset == offset
537 // Check that the rest of argD is present
538 filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
539 filter port(ff, \D).extract(offset, GetSize(argD)) == argD
540 // Check that FF.Q is connected to CE-mux
541 filter !ffholdmux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
542
543 set ffoffset offset
544 endmatch
545
546 code argQ
547 if (ff) {
548 if (clock != SigBit()) {
549 if (port(ff, \CLK) != clock)
550 reject;
551 if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
552 reject;
553 }
554 SigSpec D = port(ff, \D);
555 SigSpec Q = port(ff, \Q);
556 if (!ffholdmux) {
557 argQ = argD;
558 argQ.replace(D, Q);
559 }
560
561 for (auto c : argQ.chunks()) {
562 Const init = c.wire->attributes.at(\init, State::Sx);
563 if (!init.is_fully_undef() && !init.is_fully_zero())
564 reject;
565 }
566
567 dff = ff;
568 dffQ = argQ;
569 dffclock = port(ff, \CLK);
570 dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
571 }
572 // No enable/reset mux possible without flop
573 else if (dffholdmux || dffrstmux)
574 reject;
575 endcode