diff options
Diffstat (limited to 'passes/pmgen/xilinx_dsp48a.pmg')
-rw-r--r-- | passes/pmgen/xilinx_dsp48a.pmg | 322 |
1 files changed, 39 insertions, 283 deletions
diff --git a/passes/pmgen/xilinx_dsp48a.pmg b/passes/pmgen/xilinx_dsp48a.pmg index 16f5e598d..dce1b61b0 100644 --- a/passes/pmgen/xilinx_dsp48a.pmg +++ b/passes/pmgen/xilinx_dsp48a.pmg @@ -4,8 +4,6 @@ // At a high level, it works as follows: // ( 1) Starting from a DSP48A/DSP48A1 cell // ( 2) Match the driver of the 'B' input to a possible $dff cell (B1REG) -// (attached to at most two $mux cells that implement clock-enable or -// reset functionality, using a subpattern discussed below) // If B1REG matched, treat 'B' input as input of B1REG // ( 3) Match the driver of the 'B' and 'D' inputs for a possible $add cell // (pre-adder) @@ -40,20 +38,15 @@ pattern xilinx_dsp48a_pack state <SigBit> clock state <SigSpec> sigA sigB sigC sigD sigM sigP state <IdString> postAddAB postAddMuxAB -state <bool> ffAcepol ffBcepol ffDcepol ffMcepol ffPcepol -state <bool> ffArstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol -state <Cell*> ffA0 ffA0cemux ffA0rstmux ffA1 ffA1cemux ffA1rstmux -state <Cell*> ffB0 ffB0cemux ffB0rstmux ffB1 ffB1cemux ffB1rstmux -state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux +state <Cell*> ffA0 ffA1 +state <Cell*> ffB0 ffB1 +state <Cell*> ffD ffM ffP // 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 +udata <Cell*> dff // (1) Starting from a DSP48A/DSP48A1 cell match dsp @@ -98,21 +91,13 @@ endcode // (attached to at most two $mux cells that implement clock-enable or // reset functionality, using a subpattern discussed above) // If matched, treat 'B' input as input of B1REG -code argQ ffB1 ffB1cemux ffB1rstmux ffBcepol ffBrstpol sigB clock +code argQ ffB1 sigB clock if (param(dsp, \B1REG).as_int() == 0 && param(dsp, \B0REG).as_int() == 0 && port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()) { argQ = sigB; subpattern(in_dffe); if (dff) { ffB1 = dff; clock = dffclock; - if (dffrstmux) { - ffB1rstmux = dffrstmux; - ffBrstpol = dffrstpol; - } - if (dffcemux) { - ffB1cemux = dffcemux; - ffBcepol = dffcepol; - } sigB = dffD; } } @@ -147,41 +132,29 @@ code sigB sigD endcode // (4) Match 'B' input for B0REG -code argQ ffB0 ffB0cemux ffB0rstmux ffBcepol ffBrstpol sigB clock +code argQ ffB0 sigB clock if (param(dsp, \B0REG).as_int() == 0) { argQ = sigB; subpattern(in_dffe); if (dff) { if (ffB1) { - if ((ffB1rstmux != nullptr) ^ (dffrstmux != nullptr)) + if (dff->type != ffB1->type) goto ffB0_end; - if ((ffB1cemux != nullptr) ^ (dffcemux != nullptr)) - goto ffB0_end; - if (dffrstmux) { - if (ffBrstpol != dffrstpol) + if (dff->type.in($sdff, $sdffe, $sdffce)) { + if (param(dff, \SRST_POLARITY) != param(ffB1, \SRST_POLARITY)) goto ffB0_end; - if (port(ffB1rstmux, \S) != port(dffrstmux, \S)) + if (port(dff, \SRST) != port(ffB1, \SRST)) goto ffB0_end; - ffB0rstmux = dffrstmux; } - if (dffcemux) { - if (ffBcepol != dffcepol) + if (dff->type.in($dffe, $sdffe, $sdffce)) { + if (param(dff, \EN_POLARITY) != param(ffB1, \EN_POLARITY)) goto ffB0_end; - if (port(ffB1cemux, \S) != port(dffcemux, \S)) + if (port(dff, \EN) != port(ffB1, \EN)) goto ffB0_end; - ffB0cemux = dffcemux; } } ffB0 = dff; clock = dffclock; - if (dffrstmux) { - ffB0rstmux = dffrstmux; - ffBrstpol = dffrstpol; - } - if (dffcemux) { - ffB0cemux = dffcemux; - ffBcepol = dffcepol; - } sigB = dffD; } } @@ -190,21 +163,13 @@ endcode // (5) Match 'A' input for A1REG // If A1REG, then match 'A' input for A0REG -code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux ffA0rstmux +code argQ ffA1 sigA clock ffA0 if (param(dsp, \A0REG).as_int() == 0 && param(dsp, \A1REG).as_int() == 0) { argQ = sigA; subpattern(in_dffe); if (dff) { ffA1 = dff; clock = dffclock; - if (dffrstmux) { - ffA1rstmux = dffrstmux; - ffArstpol = dffrstpol; - } - if (dffcemux) { - ffA1cemux = dffcemux; - ffAcepol = dffcepol; - } sigA = dffD; // Now attempt to match A0 @@ -212,32 +177,23 @@ code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux argQ = sigA; subpattern(in_dffe); if (dff) { - if ((ffA1rstmux != nullptr) ^ (dffrstmux != nullptr)) + if (dff->type != ffA1->type) goto ffA0_end; - if ((ffA1cemux != nullptr) ^ (dffcemux != nullptr)) - goto ffA0_end; - if (dffrstmux) { - if (ffArstpol != dffrstpol) + if (dff->type.in($sdff, $sdffe, $sdffce)) { + if (param(dff, \SRST_POLARITY) != param(ffA1, \SRST_POLARITY)) goto ffA0_end; - if (port(ffA1rstmux, \S) != port(dffrstmux, \S)) + if (port(dff, \SRST) != port(ffA1, \SRST)) goto ffA0_end; - ffA0rstmux = dffrstmux; } - if (dffcemux) { - if (ffAcepol != dffcepol) + if (dff->type.in($dffe, $sdffe, $sdffce)) { + if (param(dff, \EN_POLARITY) != param(ffA1, \EN_POLARITY)) goto ffA0_end; - if (port(ffA1cemux, \S) != port(dffcemux, \S)) + if (port(dff, \EN) != port(ffA1, \EN)) goto ffA0_end; - ffA0cemux = dffcemux; } ffA0 = dff; clock = dffclock; - - if (dffcemux) { - ffA0cemux = dffcemux; - ffAcepol = dffcepol; - } sigA = dffD; ffA0_end: ; @@ -249,42 +205,26 @@ ffA0_end: ; endcode // (6) Match 'D' input for DREG -code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock +code argQ ffD sigD clock if (param(dsp, \DREG).as_int() == 0) { argQ = sigD; subpattern(in_dffe); if (dff) { ffD = dff; clock = dffclock; - if (dffrstmux) { - ffDrstmux = dffrstmux; - ffDrstpol = dffrstpol; - } - if (dffcemux) { - ffDcemux = dffcemux; - ffDcepol = dffcepol; - } sigD = dffD; } } endcode // (7) Match 'P' output that exclusively drives an MREG -code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock +code argD ffM sigM sigP clock if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) { argD = sigM; subpattern(out_dffe); if (dff) { ffM = dff; clock = dffclock; - if (dffrstmux) { - ffMrstmux = dffrstmux; - ffMrstpol = dffrstpol; - } - if (dffcemux) { - ffMcemux = dffcemux; - ffMcepol = dffcepol; - } sigM = dffQ; } } @@ -303,9 +243,7 @@ match postAdd select postAdd->type.in($add) select GetSize(port(postAdd, \Y)) <= 48 choice <IdString> AB {\A, \B} - select nusers(port(postAdd, AB)) <= 3 - filter ffMcemux || nusers(port(postAdd, AB)) == 2 - filter !ffMcemux || nusers(port(postAdd, AB)) == 3 + select nusers(port(postAdd, AB)) == 2 index <SigBit> port(postAdd, AB)[0] === sigP[0] filter GetSize(port(postAdd, AB)) >= GetSize(sigP) @@ -325,25 +263,14 @@ code sigC sigP endcode // (9) Match 'P' output that exclusively drives a PREG -code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock +code argD ffP sigP clock if (param(dsp, \PREG).as_int() == 0) { - int users = 2; - // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux - if (ffMcemux && !postAdd) users++; - if (nusers(sigP) == users) { + if (nusers(sigP) == 2) { argD = sigP; subpattern(out_dffe); if (dff) { ffP = dff; clock = dffclock; - if (dffrstmux) { - ffPrstmux = dffrstmux; - ffPrstpol = dffrstpol; - } - if (dffcemux) { - ffPcemux = dffcemux; - ffPcepol = dffcepol; - } sigP = dffQ; } } @@ -387,26 +314,13 @@ 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 +// 'Q' input. subpattern in_dffe -arg argD argQ clock +arg argQ clock code dff = nullptr; - if (GetSize(argQ) == 0) + if (argQ.empty()) reject; for (const auto &c : argQ.chunks()) { // Abandon matches when 'Q' is a constant @@ -425,13 +339,14 @@ code } endcode -// (1) Starting from a $dff cell that (partially or fully) drives the given -// 'Q' argument match ff - select ff->type.in($dff) + select ff->type.in($dff, $dffe, $sdff, $sdffe) // DSP48E1 does not support clock inversion select param(ff, \CLK_POLARITY).as_bool() + // Check that reset value, if present, is fully 0. + filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero() + slice offset GetSize(port(ff, \D)) index <SigBit> port(ff, \Q)[offset] === argQ[0] @@ -440,82 +355,16 @@ match ff filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ filter clock == SigBit() || port(ff, \CLK) == clock - - set ffoffset offset endmatch -code argQ argD +code argQ SigSpec Q = port(ff, \Q); dff = ff; dffclock = port(ff, \CLK); dffD = argQ; - argD = port(ff, \D); + SigSpec D = 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; + dffD.replace(argQ, D); endcode // ####################### @@ -543,119 +392,26 @@ code reject; endcode -// (1) Starting from an optional $mux cell that implements clock enable -// semantics --- one where the given 'D' argument (partially or fully) -// drives one of its two inputs -match ffcemux - select ffcemux->type.in($mux) - // ffcemux output must have two users: ffcemux and ff.D - select nusers(port(ffcemux, \Y)) == 2 - - choice <IdString> AB {\A, \B} - // keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s) - select nusers(port(ffcemux, AB)) >= 3 - - slice offset GetSize(port(ffcemux, \Y)) - define <IdString> BA (AB == \A ? \B : \A) - index <SigBit> port(ffcemux, BA)[offset] === argD[0] - - // Check that the rest of argD is present - filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD) - filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD - - set ffoffset offset - define <bool> pol (AB == \A) - set ffcepol pol - - semioptional -endmatch - -code argD argQ - dffcemux = ffcemux; - if (ffcemux) { - SigSpec BA = port(ffcemux, ffcepol ? \B : \A); - SigSpec Y = port(ffcemux, \Y); - argQ = argD; - argD.replace(BA, Y); - argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B)); - - dffcemux = ffcemux; - dffcepol = ffcepol; - } -endcode - -// (2) Starting from, or continuing onto, another optional $mux cell that -// implements synchronous reset semantics --- one where the given 'D' -// argument (or the clock enable $mux output) drives one of its two inputs -// and where the other input is fully zero -match ffrstmux - select ffrstmux->type.in($mux) - // ffrstmux output must have two users: ffrstmux and ff.D - select nusers(port(ffrstmux, \Y)) == 2 - - choice <IdString> BA {\B, \A} - // DSP48E1 only supports reset to zero - select port(ffrstmux, BA).is_fully_zero() - - slice offset GetSize(port(ffrstmux, \Y)) - define <IdString> AB (BA == \B ? \A : \B) - index <SigBit> port(ffrstmux, AB)[offset] === argD[0] - - // Check that offset is consistent - filter !ffcemux || ffoffset == offset - // Check that the rest of argD is present - filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD) - filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD - - set ffoffset offset - define <bool> pol (AB == \A) - set ffrstpol pol - - semioptional -endmatch - -code argD argQ - dffrstmux = ffrstmux; - if (ffrstmux) { - SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B); - SigSpec Y = port(ffrstmux, \Y); - argD.replace(AB, Y); - - dffrstmux = ffrstmux; - dffrstpol = ffrstpol; - } -endcode - -// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the -// output of the previous clock enable or reset $mux cells) match ff - select ff->type.in($dff) + select ff->type.in($dff, $dffe, $sdff, $sdffe) // DSP48E1 does not support clock inversion select param(ff, \CLK_POLARITY).as_bool() slice offset GetSize(port(ff, \D)) index <SigBit> port(ff, \D)[offset] === argD[0] - // Check that offset is consistent - filter (!ffcemux && !ffrstmux) || ffoffset == offset // Check that the rest of argD is present filter GetSize(port(ff, \D)) >= offset + GetSize(argD) filter port(ff, \D).extract(offset, GetSize(argD)) == argD - // Check that FF.Q is connected to CE-mux - filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ filter clock == SigBit() || port(ff, \CLK) == clock - - set ffoffset offset endmatch code argQ SigSpec D = port(ff, \D); SigSpec Q = port(ff, \Q); - if (!ffcemux) { - argQ = argD; - argQ.replace(D, Q); - } + argQ = argD; + argQ.replace(D, Q); // Abandon matches when 'Q' has a non-zero init attribute set // (not supported by DSP48E1) |