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