4 state <bool> clock_pol cd_signed o_lo
5 state <SigSpec> sigA sigB sigCD sigH sigO
7 state <IdString> addAB muxAB
9 state <bool> ffAholdpol ffBholdpol ffCDholdpol ffOholdpol
10 state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol
12 state <Cell*> ffA ffAholdmux ffArstmux ffB ffBholdmux ffBrstmux ffCD ffCDholdmux
13 state <Cell*> ffFJKG ffH ffO ffOholdmux ffOrstmux
16 state <SigSpec> argQ argD
17 state <bool> ffholdpol ffrstpol
19 udata <SigSpec> dffD dffQ
20 udata <SigBit> dffclock
21 udata <Cell*> dff dffholdmux dffrstmux
22 udata <bool> dffholdpol dffrstpol dffclock_pol
25 select mul->type.in($mul, \SB_MAC16)
26 select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
30 auto unextend = [](const SigSpec &sig) {
32 for (i = GetSize(sig)-1; i > 0; i--)
33 if (sig[i] != sig[i-1])
35 // Do not remove non-const sign bit
38 return sig.extract(0, i);
40 sigA = unextend(port(mul, \A));
41 sigB = unextend(port(mul, \B));
44 if (mul->type == $mul)
46 else if (mul->type == \SB_MAC16)
52 // Only care about those bits that are used
54 for (i = 0; i < GetSize(O); i++) {
55 if (nusers(O[i]) <= 1)
59 log_assert(nusers(O.extract_end(i)) <= 1);
62 code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
63 if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) {
69 clock_pol = dffclock_pol;
71 ffArstmux = dffrstmux;
72 ffArstpol = dffrstpol;
75 ffAholdmux = dffholdmux;
76 ffAholdpol = dffholdpol;
83 code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
84 if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) {
90 clock_pol = dffclock_pol;
92 ffBrstmux = dffrstmux;
93 ffBrstpol = dffrstpol;
96 ffBholdmux = dffholdmux;
97 ffBholdpol = dffholdpol;
104 code argD ffFJKG sigH clock clock_pol
105 if (nusers(sigH) == 2 &&
106 (mul->type != \SB_MAC16 ||
107 (!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 subpattern(out_dffe);
111 // F/J/K/G do not have a CE-like (hold) input
115 // Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT)
116 // shared with A and B
117 if ((ffArstmux != NULL) != (dffrstmux != NULL))
119 if ((ffBrstmux != NULL) != (dffrstmux != NULL))
122 if (port(ffArstmux, \S) != port(dffrstmux, \S))
124 if (ffArstpol != dffrstpol)
128 if (port(ffBrstmux, \S) != port(dffrstmux, \S))
130 if (ffBrstpol != dffrstpol)
136 clock_pol = dffclock_pol;
144 code argD ffH sigH sigO clock clock_pol
145 if (ffFJKG && nusers(sigH) == 2 &&
146 (mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) {
148 subpattern(out_dffe);
150 // H does not have a CE-like (hold) input
154 // Reset signal of H (IRSTBOT) shared with B
155 if ((ffBrstmux != NULL) != (dffrstmux != NULL))
158 if (port(ffBrstmux, \S) != port(dffrstmux, \S))
160 if (ffBrstpol != dffrstpol)
166 clock_pol = dffclock_pol;
177 if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT).as_int() == 3)
179 select add->type.in($add)
180 choice <IdString> AB {\A, \B}
181 select nusers(port(add, AB)) == 2
183 index <SigBit> port(add, AB)[0] === sigH[0]
184 filter GetSize(port(add, AB)) <= GetSize(sigH)
185 filter port(add, AB) == sigH.extract(0, GetSize(port(add, AB)))
186 filter nusers(sigH.extract_end(GetSize(port(add, AB)))) <= 1
191 code sigCD sigO cd_signed
193 sigCD = port(add, addAB == \A ? \B : \A);
194 cd_signed = param(add, addAB == \A ? \B_SIGNED : \A_SIGNED).as_bool();
196 int natural_mul_width = GetSize(sigA) + GetSize(sigB);
197 int actual_mul_width = GetSize(sigH);
198 int actual_acc_width = GetSize(sigCD);
200 if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
202 // If accumulator, check adder width and signedness
203 if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(add, \A_SIGNED).as_bool()))
206 sigO = port(add, \Y);
211 select mux->type == $mux
212 choice <IdString> AB {\A, \B}
213 select nusers(port(mux, AB)) == 2
214 index <SigSpec> port(mux, AB) === sigO
221 sigO = port(mux, \Y);
224 code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo
225 if (mul->type != \SB_MAC16 ||
226 // Ensure that register is not already used
227 ((param(mul, \TOPOUTPUT_SELECT, 0).as_int() != 1 && param(mul, \BOTOUTPUT_SELECT, 0).as_int() != 1) &&
228 // Ensure that OLOADTOP/OLOADBOT is unused or zero
229 (port(mul, \OLOADTOP, State::S0).is_fully_zero() && port(mul, \OLOADBOT, State::S0).is_fully_zero()))) {
233 // First try entire sigO
234 if (nusers(sigO) == 2) {
236 subpattern(out_dffe);
239 // Otherwise try just its least significant 16 bits
240 if (!dff && GetSize(sigO) > 16) {
241 argD = sigO.extract(0, 16);
242 if (nusers(argD) == 2) {
243 subpattern(out_dffe);
251 clock_pol = dffclock_pol;
253 ffOrstmux = dffrstmux;
254 ffOrstpol = dffrstpol;
257 ffOholdmux = dffholdmux;
258 ffOholdpol = dffholdpol;
261 sigO.replace(sigO.extract(0, GetSize(dffQ)), dffQ);
264 // Loading value into output register is not
265 // supported unless using accumulator
269 sigCD = port(mux, muxAB == \B ? \A : \B);
271 cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
276 code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol
277 if (!sigCD.empty() && sigCD != sigO &&
278 (mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) {
283 ffCDholdmux = dffholdmux;
284 ffCDholdpol = dffholdpol;
287 // Reset signal of C (IRSTTOP) and D (IRSTBOT)
288 // shared with A and B
289 if ((ffArstmux != NULL) != (dffrstmux != NULL))
291 if ((ffBrstmux != NULL) != (dffrstmux != NULL))
294 if (port(ffArstmux, \S) != port(dffrstmux, \S))
296 if (ffArstpol != dffrstpol)
300 if (port(ffBrstmux, \S) != port(dffrstmux, \S))
302 if (ffBrstpol != dffrstpol)
308 clock_pol = dffclock_pol;
317 sigCD.extend_u0(32, cd_signed);
324 // #######################
327 arg argD argQ clock clock_pol
331 for (auto c : argQ.chunks()) {
334 if (c.wire->get_bool_attribute(\keep))
336 Const init = c.wire->attributes.at(\init, State::Sx);
337 if (!init.is_fully_undef() && !init.is_fully_zero())
343 select ff->type.in($dff)
344 // DSP48E1 does not support clock inversion
345 select param(ff, \CLK_POLARITY).as_bool()
347 slice offset GetSize(port(ff, \D))
348 index <SigBit> port(ff, \Q)[offset] === argQ[0]
350 // Check that the rest of argQ is present
351 filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
352 filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
359 if (clock != SigBit()) {
360 if (port(ff, \CLK) != clock)
362 if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
366 SigSpec Q = port(ff, \Q);
368 dffclock = port(ff, \CLK);
369 dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
373 dffD.replace(argQ, argD);
374 // Only search for ffrstmux if dffD only
375 // has two (ff, ffrstmux) users
376 if (nusers(dffD) > 2)
382 if false /* TODO: ice40 resets are actually async */
385 select ffrstmux->type.in($mux)
386 index <SigSpec> port(ffrstmux, \Y) === argD
388 choice <IdString> BA {\B, \A}
389 // DSP48E1 only supports reset to zero
390 select port(ffrstmux, BA).is_fully_zero()
392 define <bool> pol (BA == \B)
399 dffrstmux = ffrstmux;
400 dffrstpol = ffrstpol;
401 argD = port(ffrstmux, ffrstpol ? \A : \B);
402 dffD.replace(port(ffrstmux, \Y), argD);
404 // Only search for ffholdmux if argQ has at
405 // least 3 users (ff, <upstream>, ffrstmux) and
406 // dffD only has two (ff, ffrstmux)
407 if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
416 select ffholdmux->type.in($mux)
417 index <SigSpec> port(ffholdmux, \Y) === argD
418 choice <IdString> BA {\B, \A}
419 index <SigSpec> port(ffholdmux, BA) === argQ
420 define <bool> pol (BA == \B)
427 dffholdmux = ffholdmux;
428 dffholdpol = ffholdpol;
429 argD = port(ffholdmux, ffholdpol ? \A : \B);
430 dffD.replace(port(ffholdmux, \Y), argD);
433 dffholdmux = nullptr;
436 // #######################
439 arg argD argQ clock clock_pol
443 for (auto c : argD.chunks())
444 if (c.wire->get_bool_attribute(\keep))
449 select ffholdmux->type.in($mux)
450 // ffholdmux output must have two users: ffholdmux and ff.D
451 select nusers(port(ffholdmux, \Y)) == 2
453 choice <IdString> BA {\B, \A}
454 // keep-last-value net must have at least three users: ffholdmux, ff, downstream sink(s)
455 select nusers(port(ffholdmux, BA)) >= 3
457 slice offset GetSize(port(ffholdmux, \Y))
458 define <IdString> AB (BA == \B ? \A : \B)
459 index <SigBit> port(ffholdmux, AB)[offset] === argD[0]
461 // Check that the rest of argD is present
462 filter GetSize(port(ffholdmux, AB)) >= offset + GetSize(argD)
463 filter port(ffholdmux, AB).extract(offset, GetSize(argD)) == argD
466 define <bool> pol (BA == \B)
473 dffholdmux = ffholdmux;
475 SigSpec AB = port(ffholdmux, ffholdpol ? \A : \B);
476 SigSpec Y = port(ffholdmux, \Y);
479 argQ.replace(AB, port(ffholdmux, ffholdpol ? \B : \A));
481 dffholdmux = ffholdmux;
482 dffholdpol = ffholdpol;
487 if false /* TODO: ice40 resets are actually async */
489 select ffrstmux->type.in($mux)
490 // ffrstmux output must have two users: ffrstmux and ff.D
491 select nusers(port(ffrstmux, \Y)) == 2
493 choice <IdString> BA {\B, \A}
494 // DSP48E1 only supports reset to zero
495 select port(ffrstmux, BA).is_fully_zero()
497 slice offset GetSize(port(ffrstmux, \Y))
498 define <IdString> AB (BA == \B ? \A : \B)
499 index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
501 // Check that offset is consistent
502 filter !ffholdmux || ffoffset == offset
503 // Check that the rest of argD is present
504 filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
505 filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
508 define <bool> pol (AB == \A)
515 dffrstmux = ffrstmux;
517 SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
518 SigSpec Y = port(ffrstmux, \Y);
521 dffrstmux = ffrstmux;
522 dffrstpol = ffrstpol;
527 select ff->type.in($dff)
528 // DSP48E1 does not support clock inversion
529 select param(ff, \CLK_POLARITY).as_bool()
531 slice offset GetSize(port(ff, \D))
532 index <SigBit> port(ff, \D)[offset] === argD[0]
534 // Check that offset is consistent
535 filter (!ffholdmux && !ffrstmux) || ffoffset == offset
536 // Check that the rest of argD is present
537 filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
538 filter port(ff, \D).extract(offset, GetSize(argD)) == argD
539 // Check that FF.Q is connected to CE-mux
540 filter !ffholdmux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
547 if (clock != SigBit()) {
548 if (port(ff, \CLK) != clock)
550 if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
553 SigSpec D = port(ff, \D);
554 SigSpec Q = port(ff, \Q);
560 for (auto c : argQ.chunks()) {
561 Const init = c.wire->attributes.at(\init, State::Sx);
562 if (!init.is_fully_undef() && !init.is_fully_zero())
568 dffclock = port(ff, \CLK);
569 dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
571 // No enable/reset mux possible without flop
572 else if (dffholdmux || dffrstmux)