1 // This file describes the second of three pattern matcher setups that
2 // forms the `xilinx_dsp` pass described in xilinx_dsp.cc
3 // At a high level, it works as follows:
4 // (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already,
5 // and (b) uses the 'C' port
6 // (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
7 // (attached to at most two $mux cells that implement clock-enable or
8 // reset functionality, using a subpattern discussed below)
10 // - Separating out CREG packing is necessary since there is no guarantee
11 // that the cell ordering corresponds to the "expected" case (i.e. the order
12 // in which they appear in the source) thus the possiblity existed that a
13 // register got packed as a CREG into a downstream DSP that should have
14 // otherwise been a PREG of an upstream DSP that had not been visited yet
15 // - The reason this is separated out from the xilinx_dsp.pmg file is
16 // for efficiency --- each *.pmg file creates a class of the same basename,
17 // which when constructed, creates a custom database tailored to the
18 // pattern(s) contained within. Since the pattern in this file must be
19 // executed after the pattern contained in xilinx_dsp.pmg, it is necessary
20 // to reconstruct this database. Separating the two patterns into
21 // independent files causes two smaller, more specific, databases.
23 pattern xilinx_dsp_packC
25 udata <std::function<SigSpec(const SigSpec&)>> unextend
27 state <SigSpec> sigC sigP
28 state <bool> ffCcepol ffCrstpol
29 state <Cell*> ffC ffCcemux ffCrstmux
32 state <SigSpec> argQ argD
33 state <bool> ffcepol ffrstpol
35 udata <SigSpec> dffD dffQ
36 udata <SigBit> dffclock
37 udata <Cell*> dff dffcemux dffrstmux
38 udata <bool> dffcepol dffrstpol
40 // (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already,
41 // and (b) uses the 'C' port
43 select dsp->type.in(\DSP48E1)
44 select param(dsp, \CREG, 1).as_int() == 0
45 select nusers(port(dsp, \C, SigSpec())) > 1
49 unextend = [](const SigSpec &sig) {
51 for (i = GetSize(sig)-1; i > 0; i--)
52 if (sig[i] != sig[i-1])
54 // Do not remove non-const sign bit
57 return sig.extract(0, i);
59 sigC = unextend(port(dsp, \C, SigSpec()));
61 SigSpec P = port(dsp, \P);
62 if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
63 // Only care about those bits that are used
65 for (i = 0; i < GetSize(P); i++) {
66 if (nusers(P[i]) <= 1)
70 log_assert(nusers(P.extract_end(i)) <= 1);
75 clock = port(dsp, \CLK, SigBit());
78 // (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
79 // (attached to at most two $mux cells that implement clock-enable or
80 // reset functionality, using the in_dffe subpattern)
81 code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC clock
88 ffCrstmux = dffrstmux;
89 ffCrstpol = dffrstpol;
104 // #######################
106 // Subpattern for matching against input registers, based on knowledge of the
109 // (1) Starting from a $dff cell that (partially or fully) drives the given
111 // (2) Match for a $mux cell implementing synchronous reset semantics ---
112 // one that exclusively drives the 'D' input of the $dff, with one of its
113 // $mux inputs being fully zero
114 // (3) Match for a $mux cell implement clock enable semantics --- one that
115 // exclusively drives the 'D' input of the $dff (or the other input of
116 // the reset $mux) and where one of this $mux's inputs is connected to
117 // the 'Q' output of the $dff
123 for (const auto &c : argQ.chunks()) {
124 // Abandon matches when 'Q' is a constant
127 // Abandon matches when 'Q' has the keep attribute set
128 if (c.wire->get_bool_attribute(\keep))
130 // Abandon matches when 'Q' has a non-zero init attribute set
131 // (not supported by DSP48E1)
132 Const init = c.wire->attributes.at(\init, Const());
133 for (auto b : init.extract(c.offset, c.width))
134 if (b != State::Sx && b != State::S0)
139 // (1) Starting from a $dff cell that (partially or fully) drives the given
142 select ff->type.in($dff)
143 // DSP48E1 does not support clock inversion
144 select param(ff, \CLK_POLARITY).as_bool()
146 slice offset GetSize(port(ff, \D))
147 index <SigBit> port(ff, \Q)[offset] === argQ[0]
149 // Check that the rest of argQ is present
150 filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
151 filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
153 filter clock == SigBit() || port(ff, \CLK) == clock
159 SigSpec Q = port(ff, \Q);
161 dffclock = port(ff, \CLK);
165 dffD.replace(argQ, argD);
166 // Only search for ffrstmux if dffD only
167 // has two (ff, ffrstmux) users
168 if (nusers(dffD) > 2)
172 // (2) Match for a $mux cell implementing synchronous reset semantics ---
173 // exclusively drives the 'D' input of the $dff, with one of the $mux
174 // inputs being fully zero
177 select ffrstmux->type.in($mux)
178 index <SigSpec> port(ffrstmux, \Y) === argD
180 choice <IdString> BA {\B, \A}
181 // DSP48E1 only supports reset to zero
182 select port(ffrstmux, BA).is_fully_zero()
184 define <bool> pol (BA == \B)
191 dffrstmux = ffrstmux;
192 dffrstpol = ffrstpol;
193 argD = port(ffrstmux, ffrstpol ? \A : \B);
194 dffD.replace(port(ffrstmux, \Y), argD);
196 // Only search for ffcemux if argQ has at
197 // least 3 users (ff, <upstream>, ffrstmux) and
198 // dffD only has two (ff, ffrstmux)
199 if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
206 // (3) Match for a $mux cell implement clock enable semantics --- one that
207 // exclusively drives the 'D' input of the $dff (or the other input of
208 // the reset $mux) and where one of this $mux's inputs is connected to
209 // the 'Q' output of the $dff
212 select ffcemux->type.in($mux)
213 index <SigSpec> port(ffcemux, \Y) === argD
214 choice <IdString> AB {\A, \B}
215 index <SigSpec> port(ffcemux, AB) === argQ
216 define <bool> pol (AB == \A)
225 argD = port(ffcemux, ffcepol ? \B : \A);
226 dffD.replace(port(ffcemux, \Y), argD);