Add support for SB_MAC16 CD and H registers
[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> ffAcepol ffBcepol ffCDcepol ffOcepol
10 state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol
11
12 state <Cell*> ffA ffAcemux ffArstmux ffB ffBcemux ffBrstmux ffCD ffCDcemux ffCDrstmux
13 state <Cell*> ffFJKG ffFJKGrstmux ffH ffHrstmux ffO ffOcemux ffOrstmux
14
15 // subpattern
16 state <SigSpec> argQ argD
17 state <bool> ffcepol ffrstpol
18 state <int> ffoffset
19 udata <SigSpec> dffD dffQ
20 udata <SigBit> dffclock
21 udata <Cell*> dff dffcemux dffrstmux
22 udata <bool> dffcepol 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 ffAcemux ffArstmux ffAcepol 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 (dffcemux) {
79 ffAcemux = dffcemux;
80 ffAcepol = dffcepol;
81 }
82 sigA = dffD;
83 }
84 }
85 endcode
86
87 code argQ ffB ffBcemux ffBrstmux ffBcepol 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 (dffcemux) {
100 ffBcemux = dffcemux;
101 ffBcepol = dffcepol;
102 }
103 sigB = dffD;
104 }
105 }
106 endcode
107
108 code argD ffFJKG ffFJKGrstmux 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 ffFJKG = dff;
116 clock = dffclock;
117 clock_pol = dffclock_pol;
118 if (dffrstmux)
119 ffFJKGrstmux = dffrstmux;
120 // F/J/K/G do not have a CE-like (hold) input
121 if (dffcemux)
122 reject;
123
124 // Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT)
125 // shared with A and B
126 if ((ffArstmux != NULL) != (ffFJKGrstmux != NULL))
127 reject;
128 if ((ffBrstmux != NULL) != (ffFJKGrstmux != NULL))
129 reject;
130 if (ffArstmux) {
131 if (port(ffArstmux, \S) != port(ffFJKGrstmux, \S))
132 reject;
133 if (ffArstpol != dffrstpol)
134 reject;
135 }
136 if (ffBrstmux) {
137 if (port(ffBrstmux, \S) != port(ffFJKGrstmux, \S))
138 reject;
139 if (ffBrstpol != dffrstpol)
140 reject;
141 }
142
143 sigH = dffQ;
144 }
145 }
146 endcode
147
148 code argD ffH ffHrstmux sigH sigO clock clock_pol
149 if (nusers(sigH) == 2 &&
150 (mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) {
151 argD = sigH;
152 subpattern(out_dffe);
153 if (dff) {
154 ffH = dff;
155 clock = dffclock;
156 clock_pol = dffclock_pol;
157 if (dffrstmux)
158 ffHrstmux = dffrstmux;
159 // H does not have a CE-like (hold) input
160 if (dffcemux)
161 reject;
162
163 // Reset signal of H (IRSTBOT) shared with B
164 if ((ffBrstmux != NULL) != (ffHrstmux != NULL))
165 reject;
166 if (ffBrstmux) {
167 if (port(ffBrstmux, \S) != port(ffHrstmux, \S))
168 reject;
169 if (ffBrstpol != dffrstpol)
170 reject;
171 }
172
173 sigH = dffQ;
174 }
175 }
176
177 sigO = sigH;
178 endcode
179
180 match add
181 if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT).as_int() == 3)
182 select add->type.in($add)
183 choice <IdString> AB {\A, \B}
184 select nusers(port(add, AB)) == 2
185 index <SigBit> port(add, AB)[0] === sigH[0]
186 filter GetSize(port(add, AB)) <= GetSize(sigH)
187 filter port(add, AB) == sigH.extract(0, GetSize(port(add, AB)))
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 index <int> 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 ffOcemux ffOrstmux ffOcepol 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 (dffcemux) {
258 ffOcemux = dffcemux;
259 ffOcepol = dffcepol;
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 ffCDcemux ffCDrstmux ffCDcepol ffCDrstpol sigCD clock clock_pol
278 if (!sigCD.empty() &&
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 ffCD = dff;
284 clock = dffclock;
285 clock_pol = dffclock_pol;
286 if (dffrstmux) {
287 ffCDrstmux = dffrstmux;
288 ffCDrstpol = dffrstpol;
289 }
290 if (dffcemux) {
291 ffCDcemux = dffcemux;
292 ffCDcepol = dffcepol;
293 }
294 sigCD = dffD;
295 }
296 }
297 endcode
298
299 code sigCD
300 sigCD.extend_u0(32, cd_signed);
301 endcode
302
303 code
304 accept;
305 endcode
306
307 // #######################
308
309 subpattern in_dffe
310 arg argD argQ clock clock_pol
311
312 code
313 dff = nullptr;
314 for (auto c : argQ.chunks()) {
315 if (!c.wire)
316 reject;
317 if (c.wire->get_bool_attribute(\keep))
318 reject;
319 }
320 endcode
321
322 match ff
323 select ff->type.in($dff)
324 // DSP48E1 does not support clock inversion
325 select param(ff, \CLK_POLARITY).as_bool()
326
327 slice offset GetSize(port(ff, \D))
328 index <SigBit> port(ff, \Q)[offset] === argQ[0]
329
330 // Check that the rest of argQ is present
331 filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
332 filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
333
334 set ffoffset offset
335 endmatch
336
337 code argQ argD
338 {
339 if (clock != SigBit()) {
340 if (port(ff, \CLK) != clock)
341 reject;
342 if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
343 reject;
344 }
345
346 SigSpec Q = port(ff, \Q);
347 dff = ff;
348 dffclock = port(ff, \CLK);
349 dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
350 dffD = argQ;
351 argD = port(ff, \D);
352 argQ = Q;
353 dffD.replace(argQ, argD);
354 // Only search for ffrstmux if dffD only
355 // has two (ff, ffrstmux) users
356 if (nusers(dffD) > 2)
357 argD = SigSpec();
358 }
359 endcode
360
361 match ffrstmux
362 if !argD.empty()
363 select ffrstmux->type.in($mux)
364 index <SigSpec> port(ffrstmux, \Y) === argD
365
366 choice <IdString> BA {\B, \A}
367 // DSP48E1 only supports reset to zero
368 select port(ffrstmux, BA).is_fully_zero()
369
370 define <bool> pol (BA == \B)
371 set ffrstpol pol
372 semioptional
373 endmatch
374
375 code argD
376 if (ffrstmux) {
377 dffrstmux = ffrstmux;
378 dffrstpol = ffrstpol;
379 argD = port(ffrstmux, ffrstpol ? \A : \B);
380 dffD.replace(port(ffrstmux, \Y), argD);
381
382 // Only search for ffcemux if argQ has at
383 // least 3 users (ff, <upstream>, ffrstmux) and
384 // dffD only has two (ff, ffrstmux)
385 if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
386 argD = SigSpec();
387 }
388 else
389 dffrstmux = nullptr;
390 endcode
391
392 match ffcemux
393 if !argD.empty()
394 select ffcemux->type.in($mux)
395 index <SigSpec> port(ffcemux, \Y) === argD
396 choice <IdString> AB {\A, \B}
397 index <SigSpec> port(ffcemux, AB) === argQ
398 define <bool> pol (AB == \A)
399 set ffcepol pol
400 semioptional
401 endmatch
402
403 code argD
404 if (ffcemux) {
405 dffcemux = ffcemux;
406 dffcepol = ffcepol;
407 argD = port(ffcemux, ffcepol ? \B : \A);
408 dffD.replace(port(ffcemux, \Y), argD);
409 }
410 else
411 dffcemux = nullptr;
412 endcode
413
414 // #######################
415
416 subpattern out_dffe
417 arg argD argQ clock clock_pol
418
419 code
420 dff = nullptr;
421 endcode
422
423 match ffcemux
424 select ffcemux->type.in($mux)
425 // ffcemux output must have two users: ffcemux and ff.D
426 select nusers(port(ffcemux, \Y)) == 2
427
428 choice <IdString> AB {\A, \B}
429 // keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s)
430 select nusers(port(ffcemux, AB)) >= 3
431
432 slice offset GetSize(port(ffcemux, \Y))
433 define <IdString> BA (AB == \A ? \B : \A)
434 index <SigBit> port(ffcemux, BA)[offset] === argD[0]
435
436 // Check that the rest of argD is present
437 filter GetSize(BA) >= offset + GetSize(argD)
438 filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
439
440 set ffoffset offset
441 define <bool> pol (BA == \B)
442 set ffcepol pol
443
444 semioptional
445 endmatch
446
447 code argD argQ
448 dffcemux = ffcemux;
449 if (ffcemux) {
450 SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
451 if (ffoffset + GetSize(argD) > GetSize(BA))
452 reject;
453 for (int i = 1; i < GetSize(argD); i++)
454 if (BA[ffoffset+i] != argD[i])
455 reject;
456
457 SigSpec Y = port(ffcemux, \Y);
458 argQ = argD;
459 argD.replace(BA, Y);
460 argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B));
461
462 dffcemux = ffcemux;
463 dffcepol = ffcepol;
464 }
465 endcode
466
467 match ffrstmux
468 select ffrstmux->type.in($mux)
469 // ffrstmux output must have two users: ffrstmux and ff.D
470 select nusers(port(ffrstmux, \Y)) == 2
471
472 choice <IdString> BA {\B, \A}
473 // DSP48E1 only supports reset to zero
474 select port(ffrstmux, BA).is_fully_zero()
475
476 slice offset GetSize(port(ffrstmux, \Y))
477 define <IdString> AB (BA == \B ? \A : \B)
478 index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
479
480 // Check that offset is consistent
481 filter !ffcemux || ffoffset == offset
482 // Check that the rest of argD is present
483 filter GetSize(AB) >= offset + GetSize(argD)
484 filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
485
486 set ffoffset offset
487 define <bool> pol (AB == \A)
488 set ffrstpol pol
489
490 semioptional
491 endmatch
492
493 code argD argQ
494 dffrstmux = ffrstmux;
495 if (ffrstmux) {
496 SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
497 SigSpec Y = port(ffrstmux, \Y);
498 argD.replace(AB, Y);
499
500 dffrstmux = ffrstmux;
501 dffrstpol = ffrstpol;
502 }
503 endcode
504
505 match ff
506 select ff->type.in($dff)
507 // DSP48E1 does not support clock inversion
508 select param(ff, \CLK_POLARITY).as_bool()
509
510 slice offset GetSize(port(ff, \D))
511 index <SigBit> port(ff, \D)[offset] === argD[0]
512
513 // Check that offset is consistent
514 filter (!ffcemux && !ffrstmux) || ffoffset == offset
515 // Check that the rest of argD is present
516 filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
517 filter port(ff, \D).extract(offset, GetSize(argD)) == argD
518 // Check that FF.Q is connected to CE-mux
519 filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
520
521 set ffoffset offset
522
523 semioptional
524 endmatch
525
526 code argQ
527 if (ff) {
528 if (clock != SigBit()) {
529 if (port(ff, \CLK) != clock)
530 reject;
531 if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
532 reject;
533 }
534
535 SigSpec D = port(ff, \D);
536 SigSpec Q = port(ff, \Q);
537 if (!ffcemux) {
538 argQ = argD;
539 argQ.replace(D, Q);
540 }
541
542 for (auto c : argQ.chunks()) {
543 if (c.wire->get_bool_attribute(\keep))
544 reject;
545 Const init = c.wire->attributes.at(\init, State::Sx);
546 if (!init.is_fully_undef() && !init.is_fully_zero())
547 reject;
548 }
549
550 dff = ff;
551 dffQ = argQ;
552 dffclock = port(ff, \CLK);
553 dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
554 }
555 // No enable/reset mux possible without flop
556 else if (dffcemux || dffrstmux)
557 reject;
558 endcode