pattern xilinx_dsp state > unextend state clock state sigA sigffAmuxY sigB sigffBmuxY sigC sigffCmuxY sigD sigffDmuxY sigM sigP state postAddAB postAddMuxAB state ffAcepol ffADcepol ffBcepol ffCcepol ffDcepol ffMcepol ffPcepol ffPrstpol state ffPoffset state ffAD ffADmux ffA ffAmux ffB ffBmux ffC ffCmux ffD ffDmux ffM ffMmux ffP ffPcemux ffPrstmux // subpattern state argQ argD state ffcepol ffrstpol udata dffD dffQ udata dffclock udata dff dffcemux dffrstmux udata 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 AB {\A, \B} // A port has to be 30 bits or less select GetSize(port(preAdd, AB)) <= 30 define BA (AB == \A ? \B : \A) // D port has to be 25 bits or less select GetSize(port(preAdd, BA)) <= 25 index 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 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 AB {\A, \B} index port(postAddMux, AB) === sigP index 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, , 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 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 AB {\A, \B} filter offset+GetSize(argQ) <= GetSize(port(ffcemux, \Y)) filter port(ffcemux, AB).extract(offset, GetSize(argQ)) == argQ define 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 BA {\B, \A} // new-value net must have exactly two users: (upstream) and ffcemux select nusers(port(ffcemux, BA)) == 2 define 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 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 BA {\B, \A} // DSP48E1 only supports reset to zero select port(ffrstmux, BA).is_fully_zero() define AB (BA == \B ? \A : \B) // keep-last-value net must have exactly 2 users: ffrstmux, ffcemux/ 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 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 port(ff_enable, \D) === argD index 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 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