1 // This file describes the main pattern matcher setup (of three total) that
2 // forms the `xilinx_dsp` pass described in xilinx_dsp.cc - version for
3 // DSP48A/DSP48A1 (Spartan 3A DSP, Spartan 6).
4 // At a high level, it works as follows:
5 // ( 1) Starting from a DSP48A/DSP48A1 cell
6 // ( 2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
7 // (attached to at most two $mux cells that implement clock-enable or
8 // reset functionality, using a subpattern discussed below)
9 // If B1REG matched, treat 'B' input as input of B1REG
10 // ( 3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
12 // ( 4) Match 'B' input for B0REG
13 // ( 5) Match 'A' input for A1REG
14 // If A1REG, then match 'A' input for A0REG
15 // ( 6) Match 'D' input for DREG
16 // ( 7) Match 'P' output that exclusively drives an MREG
17 // ( 8) Match 'P' output that exclusively drives one of two inputs to an $add
19 // The other input to the adder is assumed to come in from the 'C' input
20 // (note: 'P' -> 'C' connections that exist for accumulators are
21 // recognised in xilinx_dsp.cc).
22 // ( 9) Match 'P' output that exclusively drives a PREG
23 // (10) If post-adder and PREG both present, match for a $mux cell driving
24 // the 'C' input, where one of the $mux's inputs is the PREG output.
25 // This indicates an accumulator situation, and one where a $mux exists
26 // to override the accumulated value:
27 // +--------------------------------+
32 // | /-------\ +----+ |
33 // +----+ +-| post- |___|PREG|---+ 'P'
34 // |MREG|------ | adder | +----+
36 // Notes: see the notes in xilinx_dsp.pmg
38 pattern xilinx_dsp48a_pack
41 state <SigSpec> sigA sigB sigC sigD sigM sigP
42 state <IdString> postAddAB postAddMuxAB
43 state <bool> ffAcepol ffBcepol ffDcepol ffMcepol ffPcepol
44 state <bool> ffArstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol
45 state <Cell*> ffA0 ffA0cemux ffA0rstmux ffA1 ffA1cemux ffA1rstmux
46 state <Cell*> ffB0 ffB0cemux ffB0rstmux ffB1 ffB1cemux ffB1rstmux
47 state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux
49 // Variables used for subpatterns
50 state <SigSpec> argQ argD
51 state <bool> ffcepol ffrstpol
53 udata <SigSpec> dffD dffQ
54 udata <SigBit> dffclock
55 udata <Cell*> dff dffcemux dffrstmux
56 udata <bool> dffcepol dffrstpol
58 // (1) Starting from a DSP48A/DSP48A1 cell
60 select dsp->type.in(\DSP48A, \DSP48A1)
63 code sigA sigB sigC sigD sigM clock
64 auto unextend = [](const SigSpec &sig) {
66 for (i = GetSize(sig)-1; i > 0; i--)
67 if (sig[i] != sig[i-1])
69 // Do not remove non-const sign bit
72 return sig.extract(0, i);
74 sigA = unextend(port(dsp, \A));
75 sigB = unextend(port(dsp, \B));
77 sigC = port(dsp, \C, SigSpec());
78 sigD = port(dsp, \D, SigSpec());
80 SigSpec P = port(dsp, \P);
81 // Only care about those bits that are used
83 for (i = GetSize(P)-1; i >= 0; i--)
87 log_assert(nusers(P.extract_end(i)) <= 1);
88 // This sigM could have no users if downstream sinks (e.g. $add) is
89 // narrower than $mul result, for example
92 sigM = P.extract(0, i);
94 clock = port(dsp, \CLK, SigBit());
97 // (2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
98 // (attached to at most two $mux cells that implement clock-enable or
99 // reset functionality, using a subpattern discussed above)
100 // If matched, treat 'B' input as input of B1REG
101 code argQ ffB1 ffB1cemux ffB1rstmux ffBcepol ffBrstpol sigB clock
102 if (param(dsp, \B1REG).as_int() == 0 && param(dsp, \B0REG).as_int() == 0 && port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()) {
109 ffB1rstmux = dffrstmux;
110 ffBrstpol = dffrstpol;
113 ffB1cemux = dffcemux;
121 // (3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
124 if sigD.empty() || sigD.is_fully_zero()
125 if param(dsp, \B0REG).as_int() == 0
126 // Ensure that preAdder not already used
127 if port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()
129 select preAdd->type.in($add, $sub)
130 // Output has to be 18 bits or less
131 select GetSize(port(preAdd, \Y)) <= 18
132 select nusers(port(preAdd, \Y)) == 2
133 // D port has to be 18 bits or less
134 select GetSize(port(preAdd, \A)) <= 18
135 // B port has to be 18 bits or less
136 select GetSize(port(preAdd, \B)) <= 18
137 index <SigSpec> port(preAdd, \Y) === sigB
144 sigD = port(preAdd, \A);
145 sigB = port(preAdd, \B);
149 // (4) Match 'B' input for B0REG
150 code argQ ffB0 ffB0cemux ffB0rstmux ffBcepol ffBrstpol sigB clock
151 if (param(dsp, \B0REG).as_int() == 0) {
156 if ((ffB1rstmux != nullptr) ^ (dffrstmux != nullptr))
158 if ((ffB1cemux != nullptr) ^ (dffcemux != nullptr))
161 if (ffBrstpol != dffrstpol)
163 if (port(ffB1rstmux, \S) != port(dffrstmux, \S))
165 ffB0rstmux = dffrstmux;
168 if (ffBcepol != dffcepol)
170 if (port(ffB1cemux, \S) != port(dffcemux, \S))
172 ffB0cemux = dffcemux;
178 ffB0rstmux = dffrstmux;
179 ffBrstpol = dffrstpol;
182 ffB0cemux = dffcemux;
191 // (5) Match 'A' input for A1REG
192 // If A1REG, then match 'A' input for A0REG
193 code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux ffA0rstmux
194 if (param(dsp, \A0REG).as_int() == 0 && param(dsp, \A1REG).as_int() == 0) {
201 ffA1rstmux = dffrstmux;
202 ffArstpol = dffrstpol;
205 ffA1cemux = dffcemux;
210 // Now attempt to match A0
215 if ((ffA1rstmux != nullptr) ^ (dffrstmux != nullptr))
217 if ((ffA1cemux != nullptr) ^ (dffcemux != nullptr))
220 if (ffArstpol != dffrstpol)
222 if (port(ffA1rstmux, \S) != port(dffrstmux, \S))
224 ffA0rstmux = dffrstmux;
227 if (ffAcepol != dffcepol)
229 if (port(ffA1cemux, \S) != port(dffcemux, \S))
231 ffA0cemux = dffcemux;
238 ffA0cemux = dffcemux;
251 // (6) Match 'D' input for DREG
252 code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
253 if (param(dsp, \DREG).as_int() == 0) {
260 ffDrstmux = dffrstmux;
261 ffDrstpol = dffrstpol;
272 // (7) Match 'P' output that exclusively drives an MREG
273 code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
274 if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) {
276 subpattern(out_dffe);
281 ffMrstmux = dffrstmux;
282 ffMrstpol = dffrstpol;
294 // (8) Match 'P' output that exclusively drives one of two inputs to an $add
295 // cell (post-adder).
296 // The other input to the adder is assumed to come in from the 'C' input
297 // (note: 'P' -> 'C' connections that exist for accumulators are
298 // recognised in xilinx_dsp.cc).
300 // Ensure that Z mux is not already used
301 if port(dsp, \OPMODE, SigSpec()).extract(2,2).is_fully_zero()
303 select postAdd->type.in($add)
304 select GetSize(port(postAdd, \Y)) <= 48
305 choice <IdString> AB {\A, \B}
306 select nusers(port(postAdd, AB)) <= 3
307 filter ffMcemux || nusers(port(postAdd, AB)) == 2
308 filter !ffMcemux || nusers(port(postAdd, AB)) == 3
310 index <SigBit> port(postAdd, AB)[0] === sigP[0]
311 filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
312 filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP
313 // Check that remainder of AB is a sign- or zero-extension
314 filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP))
322 sigC = port(postAdd, postAddAB == \A ? \B : \A);
323 sigP = port(postAdd, \Y);
327 // (9) Match 'P' output that exclusively drives a PREG
328 code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
329 if (param(dsp, \PREG).as_int() == 0) {
331 // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
332 if (ffMcemux && !postAdd) users++;
333 if (nusers(sigP) == users) {
335 subpattern(out_dffe);
340 ffPrstmux = dffrstmux;
341 ffPrstpol = dffrstpol;
353 // (10) If post-adder and PREG both present, match for a $mux cell driving
354 // the 'C' input, where one of the $mux's inputs is the PREG output.
355 // This indicates an accumulator situation, and one where a $mux exists
356 // to override the accumulated value:
357 // +--------------------------------+
362 // | /-------\ +----+ |
363 // +----+ +-| post- |___|PREG|---+ 'P'
364 // |MREG|------ | adder | +----+
369 select postAddMux->type.in($mux)
370 select nusers(port(postAddMux, \Y)) == 2
371 choice <IdString> AB {\A, \B}
372 index <SigSpec> port(postAddMux, AB) === sigP
373 index <SigSpec> port(postAddMux, \Y) === sigC
380 sigC = port(postAddMux, postAddMuxAB == \A ? \B : \A);
387 // #######################
389 // Subpattern for matching against input registers, based on knowledge of the
390 // 'Q' input. Typically, identifying registers with clock-enable and reset
391 // capability would be a task would be handled by other Yosys passes such as
392 // dff2dffe, but since DSP inference happens much before this, these patterns
393 // have to be manually identified.
395 // (1) Starting from a $dff cell that (partially or fully) drives the given
397 // (2) Match for a $mux cell implementing synchronous reset semantics ---
398 // one that exclusively drives the 'D' input of the $dff, with one of its
399 // $mux inputs being fully zero
400 // (3) Match for a $mux cell implement clock enable semantics --- one that
401 // exclusively drives the 'D' input of the $dff (or the other input of
402 // the reset $mux) and where one of this $mux's inputs is connected to
403 // the 'Q' output of the $dff
409 if (GetSize(argQ) == 0)
411 for (const auto &c : argQ.chunks()) {
412 // Abandon matches when 'Q' is a constant
415 // Abandon matches when 'Q' has the keep attribute set
416 if (c.wire->get_bool_attribute(\keep))
418 // Abandon matches when 'Q' has a non-zero init attribute set
419 // (not supported by DSP48E1)
420 Const init = c.wire->attributes.at(\init, Const());
422 for (auto b : init.extract(c.offset, c.width))
423 if (b != State::Sx && b != State::S0)
428 // (1) Starting from a $dff cell that (partially or fully) drives the given
431 select ff->type.in($dff)
432 // DSP48E1 does not support clock inversion
433 select param(ff, \CLK_POLARITY).as_bool()
435 slice offset GetSize(port(ff, \D))
436 index <SigBit> port(ff, \Q)[offset] === argQ[0]
438 // Check that the rest of argQ is present
439 filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
440 filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
442 filter clock == SigBit() || port(ff, \CLK) == clock
448 SigSpec Q = port(ff, \Q);
450 dffclock = port(ff, \CLK);
454 dffD.replace(argQ, argD);
455 // Only search for ffrstmux if dffD only
456 // has two (ff, ffrstmux) users
457 if (nusers(dffD) > 2)
461 // (2) Match for a $mux cell implementing synchronous reset semantics ---
462 // exclusively drives the 'D' input of the $dff, with one of the $mux
463 // inputs being fully zero
466 select ffrstmux->type.in($mux)
467 index <SigSpec> port(ffrstmux, \Y) === argD
469 choice <IdString> BA {\B, \A}
470 // DSP48E1 only supports reset to zero
471 select port(ffrstmux, BA).is_fully_zero()
473 define <bool> pol (BA == \B)
480 dffrstmux = ffrstmux;
481 dffrstpol = ffrstpol;
482 argD = port(ffrstmux, ffrstpol ? \A : \B);
483 dffD.replace(port(ffrstmux, \Y), argD);
485 // Only search for ffcemux if argQ has at
486 // least 3 users (ff, <upstream>, ffrstmux) and
487 // dffD only has two (ff, ffrstmux)
488 if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
495 // (3) Match for a $mux cell implement clock enable semantics --- one that
496 // exclusively drives the 'D' input of the $dff (or the other input of
497 // the reset $mux) and where one of this $mux's inputs is connected to
498 // the 'Q' output of the $dff
501 select ffcemux->type.in($mux)
502 index <SigSpec> port(ffcemux, \Y) === argD
503 choice <IdString> AB {\A, \B}
504 index <SigSpec> port(ffcemux, AB) === argQ
505 define <bool> pol (AB == \A)
514 argD = port(ffcemux, ffcepol ? \B : \A);
515 dffD.replace(port(ffcemux, \Y), argD);
521 // #######################
523 // Subpattern for matching against output registers, based on knowledge of the
526 // (1) Starting from an optional $mux cell that implements clock enable
527 // semantics --- one where the given 'D' argument (partially or fully)
528 // drives one of its two inputs
529 // (2) Starting from, or continuing onto, another optional $mux cell that
530 // implements synchronous reset semantics --- one where the given 'D'
531 // argument (or the clock enable $mux output) drives one of its two inputs
532 // and where the other input is fully zero
533 // (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
534 // output of the previous clock enable or reset $mux cells)
540 for (auto c : argD.chunks())
541 // Abandon matches when 'D' has the keep attribute set
542 if (c.wire->get_bool_attribute(\keep))
546 // (1) Starting from an optional $mux cell that implements clock enable
547 // semantics --- one where the given 'D' argument (partially or fully)
548 // drives one of its two inputs
550 select ffcemux->type.in($mux)
551 // ffcemux output must have two users: ffcemux and ff.D
552 select nusers(port(ffcemux, \Y)) == 2
554 choice <IdString> AB {\A, \B}
555 // keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s)
556 select nusers(port(ffcemux, AB)) >= 3
558 slice offset GetSize(port(ffcemux, \Y))
559 define <IdString> BA (AB == \A ? \B : \A)
560 index <SigBit> port(ffcemux, BA)[offset] === argD[0]
562 // Check that the rest of argD is present
563 filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD)
564 filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
567 define <bool> pol (AB == \A)
576 SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
577 SigSpec Y = port(ffcemux, \Y);
580 argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B));
587 // (2) Starting from, or continuing onto, another optional $mux cell that
588 // implements synchronous reset semantics --- one where the given 'D'
589 // argument (or the clock enable $mux output) drives one of its two inputs
590 // and where the other input is fully zero
592 select ffrstmux->type.in($mux)
593 // ffrstmux output must have two users: ffrstmux and ff.D
594 select nusers(port(ffrstmux, \Y)) == 2
596 choice <IdString> BA {\B, \A}
597 // DSP48E1 only supports reset to zero
598 select port(ffrstmux, BA).is_fully_zero()
600 slice offset GetSize(port(ffrstmux, \Y))
601 define <IdString> AB (BA == \B ? \A : \B)
602 index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
604 // Check that offset is consistent
605 filter !ffcemux || ffoffset == offset
606 // Check that the rest of argD is present
607 filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
608 filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
611 define <bool> pol (AB == \A)
618 dffrstmux = ffrstmux;
620 SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
621 SigSpec Y = port(ffrstmux, \Y);
624 dffrstmux = ffrstmux;
625 dffrstpol = ffrstpol;
629 // (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
630 // output of the previous clock enable or reset $mux cells)
632 select ff->type.in($dff)
633 // DSP48E1 does not support clock inversion
634 select param(ff, \CLK_POLARITY).as_bool()
636 slice offset GetSize(port(ff, \D))
637 index <SigBit> port(ff, \D)[offset] === argD[0]
639 // Check that offset is consistent
640 filter (!ffcemux && !ffrstmux) || ffoffset == offset
641 // Check that the rest of argD is present
642 filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
643 filter port(ff, \D).extract(offset, GetSize(argD)) == argD
644 // Check that FF.Q is connected to CE-mux
645 filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
647 filter clock == SigBit() || port(ff, \CLK) == clock
653 SigSpec D = port(ff, \D);
654 SigSpec Q = port(ff, \Q);
660 // Abandon matches when 'Q' has a non-zero init attribute set
661 // (not supported by DSP48E1)
662 for (auto c : argQ.chunks()) {
663 Const init = c.wire->attributes.at(\init, Const());
665 for (auto b : init.extract(c.offset, c.width))
666 if (b != State::Sx && b != State::S0)
672 dffclock = port(ff, \CLK);