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