pattern xilinx_dsp state > unextend state clock state sigA sigffAcemuxY sigB sigffBcemuxY sigC sigffCcemuxY sigD sigffDcemuxY sigM sigP state postAddAB postAddMuxAB state ffAcepol ffADcepol ffBcepol ffCcepol ffDcepol ffMcepol ffPcepol state ffArstpol ffADrstpol ffBrstpol ffCrstpol ffDrstpol ffMrstpol ffPrstpol state ffAD ffADcemux ffADrstmux ffA ffAcemux ffArstmux ffB ffBcemux ffBrstmux ffC ffCcemux ffCrstmux state ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux // subpattern state argQ argD state ffcepol ffrstpol state ffoffset 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 ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock if (param(dsp, \ADREG).as_int() == 0) { argQ = sigA; subpattern(in_dffe); if (dff) { ffAD = dff; clock = dffclock; if (dffcemux) { ffADcemux = dffcemux; ffADrstmux = dffrstmux; ffADcepol = dffcepol; ffADrstpol = dffrstpol; } 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 ffAcemux ffArstmux ffAcepol ffArstpol sigA clock ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol // 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) { ffAcemux = dffcemux; ffArstmux = dffrstmux; ffAcepol = dffcepol; ffArstpol = dffrstpol; } sigA = dffD; } } } // And if there wasn't a pre-adder, // move AD register to A else if (ffAD) { log_assert(!ffA && !ffAcemux && !ffArstmux); std::swap(ffA, ffAD); std::swap(ffAcemux, ffADcemux); std::swap(ffArstmux, ffADrstmux); ffAcepol = ffADcepol; ffArstpol = ffADrstpol; } endcode code argQ ffB ffBcemux ffBrstmux ffBcepol ffBrstpol sigB clock if (param(dsp, \BREG).as_int() == 0) { argQ = sigB; subpattern(in_dffe); if (dff) { ffB = dff; clock = dffclock; if (dffcemux) { ffBcemux = dffcemux; ffBrstmux = dffrstmux; ffBcepol = dffcepol; ffBrstpol = dffrstpol; } sigB = dffD; } } endcode code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock if (param(dsp, \DREG).as_int() == 0) { argQ = sigD; subpattern(in_dffe); if (dff) { ffD = dff; clock = dffclock; if (dffcemux) { ffDcemux = dffcemux; ffDrstmux = dffrstmux; ffDcepol = dffcepol; ffDrstpol = dffrstpol; } sigD = dffD; } } endcode code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol 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) { ffMcemux = dffcemux; ffMrstmux = dffrstmux; ffMcepol = dffcepol; ffMrstpol = dffrstpol; } 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 ffMcemux || nusers(port(postAdd, AB)) == 2 filter !ffMcemux || 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 ffMcemux and no postAdd new-value net must have exactly three users: ffMcemux, ffM and ffPcemux if ((ffMcemux && !postAdd && nusers(sigP) == 3) || // Otherwise new-value net must have exactly two users: dsp and ffPcemux ((!ffMcemux || 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 ffCcemux ffCrstmux ffCcepol ffCrstpol sigC clock if (param(dsp, \CREG).as_int() == 0 && sigC != sigP) { argQ = sigC; subpattern(in_dffe); if (dff) { ffC = dff; clock = dffclock; if (dffcemux) { ffCcemux = dffcemux; ffCrstmux = dffrstmux; ffCcepol = dffcepol; ffCrstpol = dffrstpol; } sigC = dffD; } } endcode code accept; endcode // ####################### subpattern in_dffe arg argD argQ clock code dff = nullptr; for (auto c : argQ.chunks()) { if (!c.wire) reject; if (c.wire->get_bool_attribute(\keep)) reject; } endcode 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 port(ff, \Q)[offset] === argQ[0] set ffoffset offset endmatch code argQ argD { if (clock != SigBit()) { if (port(ff, \CLK) != clock) reject; } SigSpec Q = port(ff, \Q); if (ffoffset + GetSize(argQ) > GetSize(Q)) reject; for (int i = 1; i < GetSize(argQ); i++) if (Q[ffoffset+i] != argQ[i]) reject; 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 match ffrstmux if !argD.empty() select ffrstmux->type.in($mux) index port(ffrstmux, \Y) === argD choice BA {\B, \A} // DSP48E1 only supports reset to zero select port(ffrstmux, BA).is_fully_zero() define 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 ffrstmux if argQ has at // least 3 users (ff, , ffrstmux) and // dffD only has two (ff, ffrstmux) if (!(nusers(argQ) >= 3 && nusers(dffD) == 2)) argD = SigSpec(); } else dffrstmux = nullptr; endcode match ffcemux if !argD.empty() select ffcemux->type.in($mux) index port(ffcemux, \Y) === argD choice AB {\A, \B} index port(ffcemux, AB) === argQ define pol (AB == \A) set ffcepol pol semioptional endmatch code argD if (ffcemux) { dffcemux = ffcemux; dffcepol = ffcepol; dffD.replace(port(ffcemux, \Y), argD); } else dffcemux = nullptr; endcode // ####################### subpattern out_dffe arg argD argQ clock arg unextend code dff = nullptr; endcode match ffcemux select ffcemux->type.in($mux) // ffcemux output must have two users: ffcemux and ff.D select nusers(port(ffcemux, \Y)) == 2 choice 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 BA (AB == \A ? \B : \A) index port(ffcemux, BA)[offset] === argD[0] set ffoffset offset define pol (BA == \B) set ffcepol pol semioptional endmatch code argD argQ dffcemux = ffcemux; if (ffcemux) { SigSpec BA = port(ffcemux, ffcepol ? \B : \A); if (ffoffset + GetSize(argD) > GetSize(BA)) reject; for (int i = 1; i < GetSize(argD); i++) if (BA[ffoffset+i] != argD[i]) reject; SigSpec Y = port(ffcemux, \Y); argQ = argD; argD.replace(BA, Y); argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B)); dffcemux = ffcemux; dffcepol = ffcepol; } endcode match ffrstmux select ffrstmux->type.in($mux) // ffrstmux output must have two users: ffrstmux and ff.D select nusers(port(ffrstmux, \Y)) == 2 choice BA {\B, \A} // DSP48E1 only supports reset to zero select port(ffrstmux, BA).is_fully_zero() slice offset GetSize(port(ffrstmux, \Y)) define AB (BA == \B ? \A : \B) index port(ffrstmux, AB)[offset] === argD[0] filter !ffcemux || ffoffset == offset set ffoffset offset define pol (AB == \A) set ffrstpol pol semioptional endmatch code argD argQ dffrstmux = ffrstmux; if (ffrstmux) { SigSpec AB = port(ffrstmux, ffcepol ? \A : \B); if (ffoffset + GetSize(argD) > GetSize(AB)) reject; for (int i = 1; i < GetSize(argD); i++) if (AB[ffoffset+i] != argD[i]) reject; SigSpec Y = port(ffrstmux, \Y); argD.replace(AB, Y); dffrstmux = ffrstmux; dffrstpol = ffrstpol; } endcode 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 port(ff, \D)[offset] === argD[0] filter (!ffcemux && !ffrstmux) || ffoffset == offset set ffoffset offset semioptional endmatch code argQ if (ff) { if (clock != SigBit()) { if (port(ff, \CLK) != clock) reject; } SigSpec D = port(ff, \D); if (ffoffset + GetSize(argD) > GetSize(D)) reject; for (int i = 1; i < GetSize(argD); i++) if (D[ffoffset+i] != argD[i]) reject; SigSpec Q = port(ff, \Q); if (ffcemux) { for (int i = 0; i < GetSize(argQ); i++) if (Q[ffoffset+i] != argQ[i]) reject; } else { argQ = argD; argQ.replace(D, Q); } for (auto c : argQ.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; } dff = ff; dffQ = argQ; dffclock = port(dff, \CLK); } // No enable/reset mux possible without flop else if (dffcemux || dffrstmux) reject; endcode