aboutsummaryrefslogtreecommitdiffstats
path: root/passes/pmgen/xilinx_dsp.pmg
diff options
context:
space:
mode:
Diffstat (limited to 'passes/pmgen/xilinx_dsp.pmg')
-rw-r--r--passes/pmgen/xilinx_dsp.pmg467
1 files changed, 467 insertions, 0 deletions
diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg
new file mode 100644
index 000000000..7db8e95a6
--- /dev/null
+++ b/passes/pmgen/xilinx_dsp.pmg
@@ -0,0 +1,467 @@
+pattern xilinx_dsp
+
+state <std::function<SigSpec(const SigSpec&)>> unextend
+state <SigBit> clock
+state <SigSpec> sigA sigffAmuxY sigB sigffBmuxY sigC sigffCmuxY sigD sigffDmuxY sigM sigP
+state <IdString> postAddAB postAddMuxAB
+state <bool> ffAcepol ffADcepol ffBcepol ffCcepol ffDcepol ffMcepol ffPcepol ffPrstpol
+state <int> ffPoffset
+
+state <Cell*> ffAD ffADmux ffA ffAmux ffB ffBmux ffC ffCmux ffD ffDmux ffM ffMmux ffP ffPcemux ffPrstmux
+
+// subpattern
+state <SigSpec> argQ argD
+state <bool> ffcepol ffrstpol
+udata <SigSpec> dffD dffQ
+udata <SigBit> dffclock
+udata <Cell*> dff dffcemux dffrstmux
+udata <bool> dffcepol dffrstpol
+
+match dsp
+ select dsp->type.in(\DSP48E1)
+endmatch
+
+code unextend sigA sigB sigC sigD sigM
+ 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);
+ };
+ sigA = unextend(port(dsp, \A));
+ sigB = unextend(port(dsp, \B));
+
+ sigC = dsp->connections_.at(\C, SigSpec());
+ sigD = dsp->connections_.at(\D, SigSpec());
+
+ SigSpec P = port(dsp, \P);
+ if (dsp->parameters.at(\USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
+ // Only care about those bits that are used
+ int i;
+ for (i = 0; i < GetSize(P); i++) {
+ if (nusers(P[i]) <= 1)
+ break;
+ sigM.append(P[i]);
+ }
+ log_assert(nusers(P.extract_end(i)) <= 1);
+ }
+ else
+ sigM = P;
+endcode
+
+code argQ ffAD ffADmux ffADcepol sigA clock
+ if (param(dsp, \ADREG).as_int() == 0) {
+ argQ = sigA;
+ subpattern(in_dffe);
+ if (dff) {
+ ffAD = dff;
+ clock = dffclock;
+ if (dffcemux) {
+ ffADmux = dffcemux;
+ ffADcepol = dffcepol;
+ }
+ sigA = dffD;
+ }
+ }
+endcode
+
+match preAdd
+ if sigD.empty() || sigD.is_fully_zero()
+ // Ensure that preAdder not already used
+ if dsp->parameters.at(\USE_DPORT, Const("FALSE")).decode_string() == "FALSE"
+ if dsp->connections_.at(\INMODE, Const(0, 5)).is_fully_zero()
+
+ select preAdd->type.in($add)
+ // Output has to be 25 bits or less
+ select GetSize(port(preAdd, \Y)) <= 25
+ select nusers(port(preAdd, \Y)) == 2
+ choice <IdString> AB {\A, \B}
+ // A port has to be 30 bits or less
+ select GetSize(port(preAdd, AB)) <= 30
+ define <IdString> BA (AB == \A ? \B : \A)
+ // D port has to be 25 bits or less
+ select GetSize(port(preAdd, BA)) <= 25
+ index <SigSpec> port(preAdd, \Y) === sigA
+
+ optional
+endmatch
+
+code sigA sigD
+ if (preAdd) {
+ sigA = port(preAdd, \A);
+ sigD = port(preAdd, \B);
+ if (GetSize(sigA) < GetSize(sigD))
+ std::swap(sigA, sigD);
+ }
+endcode
+
+code argQ ffA ffAmux ffAcepol sigA clock ffAD ffADmux ffADcepol
+ // Only search for ffA if there was a pre-adder
+ // (otherwise ffA would have been matched as ffAD)
+ if (preAdd) {
+ if (param(dsp, \AREG).as_int() == 0) {
+ argQ = sigA;
+ subpattern(in_dffe);
+ if (dff) {
+ ffA = dff;
+ clock = dffclock;
+ if (dffcemux) {
+ ffAmux = dffcemux;
+ ffAcepol = dffcepol;
+ }
+ sigA = dffD;
+ }
+ }
+ }
+ // And if there wasn't a pre-adder,
+ // move AD register to A
+ else if (ffAD) {
+ log_assert(!ffA && !ffAmux);
+ std::swap(ffA, ffAD);
+ std::swap(ffAmux, ffADmux);
+ ffAcepol = ffADcepol;
+ }
+endcode
+
+code argQ ffB ffBmux ffBcepol sigB clock
+ if (param(dsp, \BREG).as_int() == 0) {
+ argQ = sigB;
+ subpattern(in_dffe);
+ if (dff) {
+ ffB = dff;
+ clock = dffclock;
+ if (dffcemux) {
+ ffBmux = dffcemux;
+ ffBcepol = dffcepol;
+ }
+ sigB = dffD;
+ }
+ }
+endcode
+
+code argQ ffD ffDmux ffDcepol sigD clock
+ if (param(dsp, \DREG).as_int() == 0) {
+ argQ = sigD;
+ subpattern(in_dffe);
+ if (dff) {
+ ffD = dff;
+ clock = dffclock;
+ if (dffcemux) {
+ ffDmux = dffcemux;
+ ffDcepol = dffcepol;
+ }
+ sigD = dffD;
+ }
+ }
+endcode
+
+code argD ffM ffMmux ffMcepol 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 (dffcemux) {
+ ffMmux = dffcemux;
+ ffMcepol = dffcepol;
+ }
+ sigM = dffQ;
+ }
+ }
+ sigP = sigM;
+endcode
+
+match postAdd
+ // Ensure that Z mux is not already used
+ if port(dsp, \OPMODE).extract(4,3).is_fully_zero()
+
+ select postAdd->type.in($add)
+ select GetSize(port(postAdd, \Y)) <= 48
+ select nusers(port(postAdd, \Y)) == 2
+ choice <IdString> AB {\A, \B}
+ select nusers(port(postAdd, AB)) <= 3
+ filter ffMmux || nusers(port(postAdd, AB)) == 2
+ filter !ffMmux || nusers(port(postAdd, AB)) == 3
+ filter GetSize(unextend(port(postAdd, AB))) <= GetSize(sigP)
+ filter unextend(port(postAdd, AB)) == sigP.extract(0, GetSize(unextend(port(postAdd, AB))))
+ filter nusers(sigP.extract_end(GetSize(unextend(port(postAdd, AB))))) <= 1
+ set postAddAB AB
+ optional
+endmatch
+
+code sigC sigP
+ if (postAdd) {
+ sigC = port(postAdd, postAddAB == \A ? \B : \A);
+
+ // TODO for DSP48E1, which will have sign extended inputs/outputs
+ //int natural_mul_width = GetSize(port(dsp, \A)) + GetSize(port(dsp, \B));
+ //int actual_mul_width = GetSize(sigP);
+ //int actual_acc_width = GetSize(sigC);
+
+ //if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
+ // reject;
+ //if ((actual_acc_width != actual_mul_width) && (param(dsp, \A_SIGNED).as_bool() != param(postAdd, \A_SIGNED).as_bool()))
+ // reject;
+
+ sigP = port(postAdd, \Y);
+ }
+endcode
+
+code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
+ if (param(dsp, \PREG).as_int() == 0) {
+ // If ffMmux and no postAdd new-value net must have exactly three users: ffMmux, ffM and ffPcemux
+ if ((ffMmux && !postAdd && nusers(sigP) == 3) ||
+ // Otherwise new-value net must have exactly two users: dsp and ffPcemux
+ ((!ffMmux || postAdd) && nusers(sigP) == 2)) {
+ argD = sigP;
+ subpattern(out_dffe);
+ if (dff) {
+ ffP = dff;
+ clock = dffclock;
+ if (dffcemux) {
+ ffPcemux = dffcemux;
+ ffPcepol = dffcepol;
+ ffPrstmux = dffrstmux;
+ ffPrstpol = dffrstpol;
+ }
+ sigP = dffQ;
+ }
+ }
+ }
+endcode
+
+match postAddMux
+ if postAdd
+ if ffP
+ select postAddMux->type.in($mux)
+ select nusers(port(postAddMux, \Y)) == 2
+ choice <IdString> AB {\A, \B}
+ index <SigSpec> port(postAddMux, AB) === sigP
+ index <SigSpec> port(postAddMux, \Y) === sigC
+ set postAddMuxAB AB
+ optional
+endmatch
+
+code sigC
+ if (postAddMux)
+ sigC = port(postAddMux, postAddMuxAB == \A ? \B : \A);
+endcode
+
+code argQ ffC ffCmux ffCcepol sigC clock
+ if (param(dsp, \CREG).as_int() == 0) {
+ argQ = sigC;
+ subpattern(in_dffe);
+ if (dff) {
+ ffC = dff;
+ clock = dffclock;
+ if (dffcemux) {
+ ffCmux = dffcemux;
+ ffCcepol = dffcepol;
+ }
+ sigC = dffD;
+ }
+ }
+endcode
+
+code
+ accept;
+endcode
+
+// #######################
+
+subpattern in_dffe
+arg argQ clock ffcepol
+
+match ff
+ select ff->type.in($dff)
+ // DSP48E1 does not support clock inversion
+ select param(ff, \CLK_POLARITY).as_bool()
+ filter GetSize(port(ff, \Q)) >= GetSize(argQ)
+ slice offset GetSize(port(ff, \Q))
+ filter offset+GetSize(argQ) <= GetSize(port(ff, \Q))
+ filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
+ semioptional
+endmatch
+
+code argQ
+ if (ff) {
+ for (auto c : argQ.chunks())
+ if (c.wire->get_bool_attribute(\keep))
+ reject;
+
+ if (clock != SigBit()) {
+ if (port(ff, \CLK) != clock)
+ reject;
+ }
+ dffclock = port(ff, \CLK);
+
+ dff = ff;
+ dffD = argQ;
+ dffD.replace(port(ff, \Q), port(ff, \D));
+ // Only search for ffcemux if argQ has at
+ // least 3 users (ff, <upstream>, ffcemux) and
+ // its ff.D only has two (ff, ffcemux)
+ if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
+ argQ = SigSpec();
+ }
+ else {
+ dff = nullptr;
+ argQ = SigSpec();
+ }
+endcode
+
+match ffcemux
+ if !argQ.empty()
+ select ffcemux->type.in($mux)
+ index <SigSpec> port(ffcemux, \Y) === port(ff, \D)
+ filter GetSize(port(ffcemux, \Y)) >= GetSize(dffD)
+ slice offset GetSize(port(ffcemux, \Y))
+ filter offset+GetSize(dffD) <= GetSize(port(ffcemux, \Y))
+ filter port(ffcemux, \Y).extract(offset, GetSize(dffD)) == dffD
+ choice <IdString> AB {\A, \B}
+ filter offset+GetSize(argQ) <= GetSize(port(ffcemux, \Y))
+ filter port(ffcemux, AB).extract(offset, GetSize(argQ)) == argQ
+ define <bool> pol (AB == \A)
+ set ffcepol pol
+ semioptional
+endmatch
+
+code
+ if (ffcemux) {
+ dffcemux = ffcemux;
+ dffcepol = ffcepol;
+ dffD = port(ffcemux, dffcepol ? \B : \A);
+ }
+ else
+ dffcemux = nullptr;
+endcode
+
+// #######################
+
+subpattern out_dffe
+arg argD argQ clock
+arg unextend
+
+match ffcemux
+ select ffcemux->type.in($mux)
+ // ffcemux output must have two users: ffcemux and ff.D
+ select nusers(port(ffcemux, \Y)) == 2
+ filter GetSize(port(ffcemux, \Y)) >= GetSize(argD)
+
+ choice <IdString> BA {\B, \A}
+ // new-value net must have exactly two users: (upstream) and ffcemux
+ select nusers(port(ffcemux, BA)) == 2
+
+ define <IdString> AB (BA == \B ? \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))
+ filter GetSize(unextend(port(ffcemux, BA))) <= GetSize(argD)
+ filter unextend(port(ffcemux, BA)) == argD.extract(0, GetSize(unextend(port(ffcemux, BA))))
+ // Remaining bits on argD must not have any other users
+ filter nusers(argD.extract_end(GetSize(unextend(port(ffcemux, BA))))) <= 1
+
+ define <bool> pol (AB == \A)
+ set ffcepol pol
+ semioptional
+endmatch
+
+code argD argQ
+ if (ffcemux) {
+ dffcemux = ffcemux;
+ dffcepol = ffcepol;
+ argD = port(ffcemux, \Y);
+ argQ = port(ffcemux, ffcepol ? \A : \B);
+ }
+ else
+ dffcemux = nullptr;
+endcode
+
+match ffrstmux
+ if !argQ.empty()
+ select ffrstmux->type.in($mux)
+ // ffrstmux output must have two users: ffrstmux and ff.D
+ select nusers(port(ffrstmux, \Y)) == 2
+ filter GetSize(port(ffrstmux, \Y)) >= GetSize(argD)
+
+ choice <IdString> BA {\B, \A}
+ // DSP48E1 only supports reset to zero
+ select port(ffrstmux, BA).is_fully_zero()
+
+ define <IdString> AB (BA == \B ? \A : \B)
+ // keep-last-value net must have exactly 2 users: ffrstmux, ffcemux/<upstream>
+ select nusers(port(ffrstmux, AB)) == 2
+
+ slice offset GetSize(port(ffrstmux, \Y))
+ filter GetSize(port(ffrstmux, AB)) <= GetSize(argD)
+ filter port(ffrstmux, AB) == argD.extract(0, GetSize(port(ffrstmux, AB)))
+ // Remaining bits on argD must not have any other users
+ filter nusers(argD.extract_end(GetSize(port(ffrstmux, AB)))) <= 1
+
+ define <bool> pol (AB == \A)
+ set ffrstpol pol
+ semioptional
+endmatch
+
+code argD argQ
+ if (ffrstmux) {
+ dffrstmux = ffrstmux;
+ dffrstpol = ffrstpol;
+ argD = port(ffrstmux, \Y);
+ }
+ else {
+ dffrstmux = nullptr;
+ argQ = SigSpec();
+ }
+endcode
+
+match ff_enable
+ if !argQ.empty()
+ select ff_enable->type.in($dff)
+ // DSP48E1 does not support clock inversion
+ select param(ff_enable, \CLK_POLARITY).as_bool()
+ index <SigSpec> port(ff_enable, \D) === argD
+ index <SigSpec> port(ff_enable, \Q) === argQ
+endmatch
+
+match ff
+ if !ff_enable
+ select ff->type.in($dff)
+ // DSP48E1 does not support clock inversion
+ select param(ff, \CLK_POLARITY).as_bool()
+ index <SigSpec> port(ff, \D) === argD
+ semioptional
+endmatch
+
+code
+ if (ff_enable)
+ dff = ff_enable;
+ else
+ dff = ff;
+ if (dff) {
+ dffQ = port(dff, \Q);
+
+ for (auto c : dffQ.chunks()) {
+ if (c.wire->get_bool_attribute(\keep))
+ reject;
+ Const init = c.wire->attributes.at(\init, State::Sx);
+ if (!init.is_fully_undef() && !init.is_fully_zero())
+ reject;
+ }
+
+ if (clock != SigBit()) {
+ if (port(dff, \CLK) != clock)
+ reject;
+ }
+ dffclock = port(dff, \CLK);
+ }
+ // No enable/reset mux possible without flop
+ else if (ffcemux || ffrstmux)
+ reject;
+endcode