| // This file describes the second of three pattern matcher setups that |
| // forms the `xilinx_dsp` pass described in xilinx_dsp.cc |
| // At a high level, it works as follows: |
| // (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already, |
| // and (b) uses the 'C' port |
| // (2) Match the driver of the 'C' input to a possible $dff cell (CREG) |
| // (attached to at most two $mux cells that implement clock-enable or |
| // reset functionality, using a subpattern discussed below) |
| // Notes: |
| // - Running CREG packing after xilinx_dsp_pack is necessary since there is no |
| // guarantee that the cell ordering corresponds to the "expected" case (i.e. |
| // the order in which they appear in the source) thus the possiblity existed |
| // that a register got packed as a CREG into a downstream DSP that should |
| // have otherwise been a PREG of an upstream DSP that had not been visited |
| // yet |
| // - The reason this is separated out from the xilinx_dsp.pmg file is |
| // for efficiency --- each *.pmg file creates a class of the same basename, |
| // which when constructed, creates a custom database tailored to the |
| // pattern(s) contained within. Since the pattern in this file must be |
| // executed after the pattern contained in xilinx_dsp.pmg, it is necessary |
| // to reconstruct this database. Separating the two patterns into |
| // independent files causes two smaller, more specific, databases. |
| |
| pattern xilinx_dsp_packC |
| |
| udata <std::function<SigSpec(const SigSpec&)>> unextend |
| state <SigBit> clock |
| state <SigSpec> sigC sigP |
| state <bool> ffCcepol ffCrstpol |
| state <Cell*> ffC ffCcemux ffCrstmux |
| |
| // Variables used for subpatterns |
| state <SigSpec> argQ argD |
| state <bool> ffcepol ffrstpol |
| state <int> ffoffset |
| udata <SigSpec> dffD dffQ |
| udata <SigBit> dffclock |
| udata <Cell*> dff dffcemux dffrstmux |
| udata <bool> dffcepol dffrstpol |
| |
| // (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already, |
| // and (b) uses the 'C' port |
| match dsp |
| select dsp->type.in(\DSP48E1) |
| select param(dsp, \CREG, 1).as_int() == 0 |
| select nusers(port(dsp, \C, SigSpec())) > 1 |
| endmatch |
| |
| code sigC sigP clock |
| unextend = [](const SigSpec &sig) { |
| int i; |
| for (i = GetSize(sig)-1; i > 0; i--) |
| if (sig[i] != sig[i-1]) |
| break; |
| // Do not remove non-const sign bit |
| if (sig[i].wire) |
| ++i; |
| return sig.extract(0, i); |
| }; |
| sigC = unextend(port(dsp, \C, SigSpec())); |
| |
| SigSpec P = port(dsp, \P); |
| if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") { |
| // Only care about those bits that are used |
| int i; |
| for (i = GetSize(P)-1; i >= 0; i--) |
| if (nusers(P[i]) > 1) |
| break; |
| i++; |
| log_assert(nusers(P.extract_end(i)) <= 1); |
| sigP = P.extract(0, i); |
| } |
| else |
| sigP = P; |
| |
| clock = port(dsp, \CLK, SigBit()); |
| endcode |
| |
| // (2) Match the driver of the 'C' input to a possible $dff cell (CREG) |
| // (attached to at most two $mux cells that implement clock-enable or |
| // reset functionality, using the in_dffe subpattern) |
| code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC clock |
| argQ = sigC; |
| subpattern(in_dffe); |
| if (dff) { |
| ffC = dff; |
| clock = dffclock; |
| if (dffrstmux) { |
| ffCrstmux = dffrstmux; |
| ffCrstpol = dffrstpol; |
| } |
| if (dffcemux) { |
| ffCcemux = dffcemux; |
| ffCcepol = dffcepol; |
| } |
| sigC = dffD; |
| } |
| endcode |
| |
| code |
| if (ffC) |
| accept; |
| endcode |
| |
| // ####################### |
| |
| // Subpattern for matching against input registers, based on knowledge of the |
| // 'Q' input. Typically, identifying registers with clock-enable and reset |
| // capability would be a task would be handled by other Yosys passes such as |
| // dff2dffe, but since DSP inference happens much before this, these patterns |
| // have to be manually identified. |
| // At a high level: |
| // (1) Starting from a $dff cell that (partially or fully) drives the given |
| // 'Q' argument |
| // (2) Match for a $mux cell implementing synchronous reset semantics --- |
| // one that exclusively drives the 'D' input of the $dff, with one of its |
| // $mux inputs being fully zero |
| // (3) Match for a $mux cell implement clock enable semantics --- one that |
| // exclusively drives the 'D' input of the $dff (or the other input of |
| // the reset $mux) and where one of this $mux's inputs is connected to |
| // the 'Q' output of the $dff |
| subpattern in_dffe |
| arg argD argQ clock |
| |
| code |
| dff = nullptr; |
| for (const auto &c : argQ.chunks()) { |
| // Abandon matches when 'Q' is a constant |
| if (!c.wire) |
| reject; |
| // Abandon matches when 'Q' has the keep attribute set |
| if (c.wire->get_bool_attribute(\keep)) |
| reject; |
| // Abandon matches when 'Q' has a non-zero init attribute set |
| // (not supported by DSP48E1) |
| Const init = c.wire->attributes.at(\init, Const()); |
| for (auto b : init.extract(c.offset, c.width)) |
| if (b != State::Sx && b != State::S0) |
| reject; |
| } |
| endcode |
| |
| // (1) Starting from a $dff cell that (partially or fully) drives the given |
| // 'Q' argument |
| match ff |
| select ff->type.in($dff) |
| // DSP48E1 does not support clock inversion |
| select param(ff, \CLK_POLARITY).as_bool() |
| |
| slice offset GetSize(port(ff, \D)) |
| index <SigBit> port(ff, \Q)[offset] === argQ[0] |
| |
| // Check that the rest of argQ is present |
| filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) |
| filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ |
| |
| filter clock == SigBit() || port(ff, \CLK) == clock |
| |
| set ffoffset offset |
| endmatch |
| |
| code argQ argD |
| SigSpec Q = port(ff, \Q); |
| dff = ff; |
| dffclock = port(ff, \CLK); |
| dffD = argQ; |
| argD = port(ff, \D); |
| argQ = Q; |
| dffD.replace(argQ, argD); |
| // Only search for ffrstmux if dffD only |
| // has two (ff, ffrstmux) users |
| if (nusers(dffD) > 2) |
| argD = SigSpec(); |
| endcode |
| |
| // (2) Match for a $mux cell implementing synchronous reset semantics --- |
| // exclusively drives the 'D' input of the $dff, with one of the $mux |
| // inputs being fully zero |
| match ffrstmux |
| if !argD.empty() |
| select ffrstmux->type.in($mux) |
| index <SigSpec> port(ffrstmux, \Y) === argD |
| |
| choice <IdString> BA {\B, \A} |
| // DSP48E1 only supports reset to zero |
| select port(ffrstmux, BA).is_fully_zero() |
| |
| define <bool> pol (BA == \B) |
| set ffrstpol pol |
| semioptional |
| endmatch |
| |
| code argD |
| if (ffrstmux) { |
| dffrstmux = ffrstmux; |
| dffrstpol = ffrstpol; |
| argD = port(ffrstmux, ffrstpol ? \A : \B); |
| dffD.replace(port(ffrstmux, \Y), argD); |
| |
| // Only search for ffcemux if argQ has at |
| // least 3 users (ff, <upstream>, ffrstmux) and |
| // dffD only has two (ff, ffrstmux) |
| if (!(nusers(argQ) >= 3 && nusers(dffD) == 2)) |
| argD = SigSpec(); |
| } |
| else |
| dffrstmux = nullptr; |
| endcode |
| |
| // (3) Match for a $mux cell implement clock enable semantics --- one that |
| // exclusively drives the 'D' input of the $dff (or the other input of |
| // the reset $mux) and where one of this $mux's inputs is connected to |
| // the 'Q' output of the $dff |
| match ffcemux |
| if !argD.empty() |
| select ffcemux->type.in($mux) |
| index <SigSpec> port(ffcemux, \Y) === argD |
| choice <IdString> AB {\A, \B} |
| index <SigSpec> port(ffcemux, AB) === argQ |
| define <bool> pol (AB == \A) |
| set ffcepol pol |
| semioptional |
| endmatch |
| |
| code argD |
| if (ffcemux) { |
| dffcemux = ffcemux; |
| dffcepol = ffcepol; |
| argD = port(ffcemux, ffcepol ? \B : \A); |
| dffD.replace(port(ffcemux, \Y), argD); |
| } |
| else |
| dffcemux = nullptr; |
| endcode |