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