Small cleanup
[yosys.git] / passes / pmgen / ice40_dsp.pmg
1 pattern ice40_dsp
2
3 udata <std::function<SigSpec(const SigSpec&)>> unextend
4 state <SigBit> clock
5 state <bool> clock_pol cd_signed o_lo
6 state <SigSpec> sigA sigB sigCD sigH sigO
7 state <Cell*> add mux
8 state <IdString> addAB muxAB
9
10 state <bool> ffAholdpol ffBholdpol ffCDholdpol ffOholdpol
11 state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol
12
13 state <Cell*> ffA ffAholdmux ffArstmux ffB ffBholdmux ffBrstmux ffCD ffCDholdmux
14 state <Cell*> ffFJKG ffH ffO ffOholdmux ffOrstmux
15
16 // subpattern
17 state <SigSpec> argQ argD
18 state <bool> ffholdpol ffrstpol
19 state <int> ffoffset
20 udata <SigSpec> dffD dffQ
21 udata <SigBit> dffclock
22 udata <Cell*> dff dffholdmux dffrstmux
23 udata <bool> dffholdpol dffrstpol dffclock_pol
24
25 match mul
26 select mul->type.in($mul, \SB_MAC16)
27 select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
28 endmatch
29
30 code sigA sigB sigH
31 unextend = [](const SigSpec &sig) {
32 int i;
33 for (i = GetSize(sig)-1; i > 0; i--)
34 if (sig[i] != sig[i-1])
35 break;
36 // Do not remove non-const sign bit
37 if (sig[i].wire)
38 ++i;
39 return sig.extract(0, i);
40 };
41 sigA = unextend(port(mul, \A));
42 sigB = unextend(port(mul, \B));
43
44 SigSpec O;
45 if (mul->type == $mul)
46 O = mul->getPort(\Y);
47 else if (mul->type == \SB_MAC16)
48 O = mul->getPort(\O);
49 else log_abort();
50 if (GetSize(O) <= 10)
51 reject;
52
53 // Only care about those bits that are used
54 int i;
55 for (i = 0; i < GetSize(O); i++) {
56 if (nusers(O[i]) <= 1)
57 break;
58 sigH.append(O[i]);
59 }
60 log_assert(nusers(O.extract_end(i)) <= 1);
61 endcode
62
63 code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
64 if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) {
65 argQ = sigA;
66 subpattern(in_dffe);
67 if (dff) {
68 ffA = dff;
69 clock = dffclock;
70 clock_pol = dffclock_pol;
71 if (dffrstmux) {
72 ffArstmux = dffrstmux;
73 ffArstpol = dffrstpol;
74 }
75 if (dffholdmux) {
76 ffAholdmux = dffholdmux;
77 ffAholdpol = dffholdpol;
78 }
79 sigA = dffD;
80 }
81 }
82 endcode
83
84 code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
85 if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) {
86 argQ = sigB;
87 subpattern(in_dffe);
88 if (dff) {
89 ffB = dff;
90 clock = dffclock;
91 clock_pol = dffclock_pol;
92 if (dffrstmux) {
93 ffBrstmux = dffrstmux;
94 ffBrstpol = dffrstpol;
95 }
96 if (dffholdmux) {
97 ffBholdmux = dffholdmux;
98 ffBholdpol = dffholdpol;
99 }
100 sigB = dffD;
101 }
102 }
103 endcode
104
105 code argD ffFJKG sigH clock clock_pol
106 if (nusers(sigH) == 2 &&
107 (mul->type != \SB_MAC16 ||
108 (!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()))) {
109 argD = sigH;
110 subpattern(out_dffe);
111 if (dff) {
112 // F/J/K/G do not have a CE-like (hold) input
113 if (dffholdmux)
114 goto reject_ffFJKG;
115
116 // Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT)
117 // shared with A and B
118 if ((ffArstmux != NULL) != (dffrstmux != NULL))
119 goto reject_ffFJKG;
120 if ((ffBrstmux != NULL) != (dffrstmux != NULL))
121 goto reject_ffFJKG;
122 if (ffArstmux) {
123 if (port(ffArstmux, \S) != port(dffrstmux, \S))
124 goto reject_ffFJKG;
125 if (ffArstpol != dffrstpol)
126 goto reject_ffFJKG;
127 }
128 if (ffBrstmux) {
129 if (port(ffBrstmux, \S) != port(dffrstmux, \S))
130 goto reject_ffFJKG;
131 if (ffBrstpol != dffrstpol)
132 goto reject_ffFJKG;
133 }
134
135 ffFJKG = dff;
136 clock = dffclock;
137 clock_pol = dffclock_pol;
138 sigH = dffQ;
139 }
140 }
141
142 if (0) {
143 reject_ffFJKG: ;
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 }
173
174 if (0) {
175 reject_ffH: ;
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 ((mul->parameters.at(\TOPOUTPUT_SELECT, 0).as_int() != 1 && mul->parameters.at(\BOTOUTPUT_SELECT, 0).as_int() != 1) &&
233 // Ensure that OLOADTOP/OLOADBOT is unused or zero
234 (mul->connections_.at(\OLOADTOP, State::S0).is_fully_zero() && mul->connections_.at(\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 }
317
318 if (0) {
319 reject_ffCD: ;
320 }
321 endcode
322
323 code sigCD
324 sigCD.extend_u0(32, cd_signed);
325 endcode
326
327 code
328 accept;
329 endcode
330
331 // #######################
332
333 subpattern in_dffe
334 arg argD argQ clock clock_pol
335
336 code
337 dff = nullptr;
338 for (auto c : argQ.chunks()) {
339 if (!c.wire)
340 reject;
341 if (c.wire->get_bool_attribute(\keep))
342 reject;
343 }
344 endcode
345
346 match ff
347 select ff->type.in($dff)
348 // DSP48E1 does not support clock inversion
349 select param(ff, \CLK_POLARITY).as_bool()
350
351 slice offset GetSize(port(ff, \D))
352 index <SigBit> port(ff, \Q)[offset] === argQ[0]
353
354 // Check that the rest of argQ is present
355 filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
356 filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
357
358 set ffoffset offset
359 endmatch
360
361 code argQ argD
362 {
363 if (clock != SigBit()) {
364 if (port(ff, \CLK) != clock)
365 reject;
366 if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
367 reject;
368 }
369
370 SigSpec Q = port(ff, \Q);
371 dff = ff;
372 dffclock = port(ff, \CLK);
373 dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
374 dffD = argQ;
375 argD = port(ff, \D);
376 argQ = Q;
377 dffD.replace(argQ, argD);
378 // Only search for ffrstmux if dffD only
379 // has two (ff, ffrstmux) users
380 if (nusers(dffD) > 2)
381 argD = SigSpec();
382 }
383 endcode
384
385 match ffrstmux
386 if false /* TODO: ice40 resets are actually async */
387
388 if !argD.empty()
389 select ffrstmux->type.in($mux)
390 index <SigSpec> port(ffrstmux, \Y) === argD
391
392 choice <IdString> BA {\B, \A}
393 // DSP48E1 only supports reset to zero
394 select port(ffrstmux, BA).is_fully_zero()
395
396 define <bool> pol (BA == \B)
397 set ffrstpol pol
398 semioptional
399 endmatch
400
401 code argD
402 if (ffrstmux) {
403 dffrstmux = ffrstmux;
404 dffrstpol = ffrstpol;
405 argD = port(ffrstmux, ffrstpol ? \A : \B);
406 dffD.replace(port(ffrstmux, \Y), argD);
407
408 // Only search for ffholdmux if argQ has at
409 // least 3 users (ff, <upstream>, ffrstmux) and
410 // dffD only has two (ff, ffrstmux)
411 if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
412 argD = SigSpec();
413 }
414 else
415 dffrstmux = nullptr;
416 endcode
417
418 match ffholdmux
419 if !argD.empty()
420 select ffholdmux->type.in($mux)
421 index <SigSpec> port(ffholdmux, \Y) === argD
422 choice <IdString> BA {\B, \A}
423 index <SigSpec> port(ffholdmux, BA) === argQ
424 define <bool> pol (BA == \B)
425 set ffholdpol pol
426 semioptional
427 endmatch
428
429 code argD
430 if (ffholdmux) {
431 dffholdmux = ffholdmux;
432 dffholdpol = ffholdpol;
433 argD = port(ffholdmux, ffholdpol ? \A : \B);
434 dffD.replace(port(ffholdmux, \Y), argD);
435 }
436 else
437 dffholdmux = nullptr;
438 endcode
439
440 // #######################
441
442 subpattern out_dffe
443 arg argD argQ clock clock_pol
444
445 code
446 dff = nullptr;
447 for (auto c : argD.chunks())
448 if (c.wire->get_bool_attribute(\keep))
449 reject;
450 endcode
451
452 match ffholdmux
453 select ffholdmux->type.in($mux)
454 // ffholdmux output must have two users: ffholdmux and ff.D
455 select nusers(port(ffholdmux, \Y)) == 2
456
457 choice <IdString> BA {\B, \A}
458 // keep-last-value net must have at least three users: ffholdmux, ff, downstream sink(s)
459 select nusers(port(ffholdmux, BA)) >= 3
460
461 slice offset GetSize(port(ffholdmux, \Y))
462 define <IdString> AB (BA == \B ? \A : \B)
463 index <SigBit> port(ffholdmux, AB)[offset] === argD[0]
464
465 // Check that the rest of argD is present
466 filter GetSize(port(ffholdmux, AB)) >= offset + GetSize(argD)
467 filter port(ffholdmux, AB).extract(offset, GetSize(argD)) == argD
468
469 set ffoffset offset
470 define <bool> pol (BA == \B)
471 set ffholdpol pol
472
473 semioptional
474 endmatch
475
476 code argD argQ
477 dffholdmux = ffholdmux;
478 if (ffholdmux) {
479 SigSpec AB = port(ffholdmux, ffholdpol ? \A : \B);
480 SigSpec Y = port(ffholdmux, \Y);
481 argQ = argD;
482 argD.replace(AB, Y);
483 argQ.replace(AB, port(ffholdmux, ffholdpol ? \B : \A));
484
485 dffholdmux = ffholdmux;
486 dffholdpol = ffholdpol;
487 }
488 endcode
489
490 match ffrstmux
491 if false /* TODO: ice40 resets are actually async */
492
493 select ffrstmux->type.in($mux)
494 // ffrstmux output must have two users: ffrstmux and ff.D
495 select nusers(port(ffrstmux, \Y)) == 2
496
497 choice <IdString> BA {\B, \A}
498 // DSP48E1 only supports reset to zero
499 select port(ffrstmux, BA).is_fully_zero()
500
501 slice offset GetSize(port(ffrstmux, \Y))
502 define <IdString> AB (BA == \B ? \A : \B)
503 index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
504
505 // Check that offset is consistent
506 filter !ffholdmux || ffoffset == offset
507 // Check that the rest of argD is present
508 filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
509 filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
510
511 set ffoffset offset
512 define <bool> pol (AB == \A)
513 set ffrstpol pol
514
515 semioptional
516 endmatch
517
518 code argD argQ
519 dffrstmux = ffrstmux;
520 if (ffrstmux) {
521 SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
522 SigSpec Y = port(ffrstmux, \Y);
523 argD.replace(AB, Y);
524
525 dffrstmux = ffrstmux;
526 dffrstpol = ffrstpol;
527 }
528 endcode
529
530 match ff
531 select ff->type.in($dff)
532 // DSP48E1 does not support clock inversion
533 select param(ff, \CLK_POLARITY).as_bool()
534
535 slice offset GetSize(port(ff, \D))
536 index <SigBit> port(ff, \D)[offset] === argD[0]
537
538 // Check that offset is consistent
539 filter (!ffholdmux && !ffrstmux) || ffoffset == offset
540 // Check that the rest of argD is present
541 filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
542 filter port(ff, \D).extract(offset, GetSize(argD)) == argD
543 // Check that FF.Q is connected to CE-mux
544 filter !ffholdmux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
545
546 set ffoffset offset
547 endmatch
548
549 code argQ
550 if (ff) {
551 if (clock != SigBit()) {
552 if (port(ff, \CLK) != clock)
553 reject;
554 if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
555 reject;
556 }
557 SigSpec D = port(ff, \D);
558 SigSpec Q = port(ff, \Q);
559 if (!ffholdmux) {
560 argQ = argD;
561 argQ.replace(D, Q);
562 }
563
564 for (auto c : argQ.chunks()) {
565 Const init = c.wire->attributes.at(\init, State::Sx);
566 if (!init.is_fully_undef() && !init.is_fully_zero())
567 reject;
568 }
569
570 dff = ff;
571 dffQ = argQ;
572 dffclock = port(ff, \CLK);
573 dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
574 }
575 // No enable/reset mux possible without flop
576 else if (dffholdmux || dffrstmux)
577 reject;
578 endcode