pattern xilinx_dsp state > unextend state clock state sigA sigffAmuxY sigB sigffBmuxY sigC sigffCmuxY sigD sigffDmuxY sigM sigP state postAddAB postAddMuxAB state ffAenpol ffADenpol ffBenpol ffCenpol ffDenpol ffMenpol ffPenpol state ffPoffset state ffAD ffADmux ffA ffAmux ffB ffBmux ffC ffCmux ffD ffDmux ffM ffMmux ffP ffPmux // subpattern state argQ argD state ffenpol udata dffD dffQ udata dffclock udata dff dffmux udata dffenpol 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 ffADenpol sigA clock if (param(dsp, \ADREG).as_int() == 0) { argQ = sigA; subpattern(in_dffe); if (dff) { ffAD = dff; clock = dffclock; if (dffmux) { ffADmux = dffmux; ffADenpol = dffenpol; } 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 ffAenpol sigA clock ffAD ffADmux ffADenpol // 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 (dffmux) { ffAmux = dffmux; ffAenpol = dffenpol; } 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); ffAenpol = ffADenpol; } endcode code argQ ffB ffBmux ffBenpol sigB clock if (param(dsp, \BREG).as_int() == 0) { argQ = sigB; subpattern(in_dffe); if (dff) { ffB = dff; clock = dffclock; if (dffmux) { ffBmux = dffmux; ffBenpol = dffenpol; } sigB = dffD; } } endcode code argQ ffD ffDmux ffDenpol sigD clock if (param(dsp, \DREG).as_int() == 0) { argQ = sigD; subpattern(in_dffe); if (dff) { ffD = dff; clock = dffclock; if (dffmux) { ffDmux = dffmux; ffDenpol = dffenpol; } sigD = dffD; } } endcode code argD ffM ffMmux ffMenpol 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 (dffmux) { ffMmux = dffmux; ffMenpol = dffenpol; } 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 ffPmux ffPenpol 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 ffPmux if ((ffMmux && !postAdd && nusers(sigP) == 3) || // Otherwise new-value net must have exactly two users: dsp and ffPmux ((!ffMmux || postAdd) && nusers(sigP) == 2)) { argD = sigP; subpattern(out_dffe); if (dff) { ffP = dff; clock = dffclock; if (dffmux) { ffPmux = dffmux; ffPenpol = dffenpol; } 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 ffCenpol sigC clock if (param(dsp, \CREG).as_int() == 0) { argQ = sigC; subpattern(in_dffe); if (dff) { ffC = dff; clock = dffclock; if (dffmux) { ffCmux = dffmux; ffCenpol = dffenpol; } sigC = dffD; } } endcode code accept; endcode // ####################### subpattern in_dffe arg argQ clock ffenpol 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 b : argQ) if (b.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 ffmux if ff.Q has at // least 3 users (ff, dsp, ffmux) and // its ff.D only has two (ff, ffmux) if (!(nusers(argQ) >= 3 && nusers(dffD) == 2)) argQ = SigSpec(); } else { dff = nullptr; argQ = SigSpec(); } endcode match ffmux if !argQ.empty() select ffmux->type.in($mux) index port(ffmux, \Y) === port(ff, \D) filter GetSize(port(ffmux, \Y)) >= GetSize(dffD) slice offset GetSize(port(ffmux, \Y)) filter offset+GetSize(dffD) <= GetSize(port(ffmux, \Y)) filter port(ffmux, \Y).extract(offset, GetSize(dffD)) == dffD choice AB {\A, \B} filter offset+GetSize(argQ) <= GetSize(port(ffmux, \Y)) filter port(ffmux, AB).extract(offset, GetSize(argQ)) == argQ define pol (AB == \A) set ffenpol pol semioptional endmatch code if (ffmux) { dffmux = ffmux; dffenpol = ffenpol; dffD = port(ffmux, dffenpol ? \B : \A); } else dffmux = nullptr; endcode // ####################### subpattern out_dffe arg argD clock ffenpol arg unextend match ffmux select ffmux->type.in($mux) // ffmux output must have two users: ffmux and ff.D select nusers(port(ffmux, \Y)) == 2 filter GetSize(port(ffmux, \Y)) >= GetSize(argD) choice BA {\B, \A} // new-value net must have exactly two users: (upstream) and ffmux select nusers(port(ffmux, BA)) == 2 slice offset GetSize(port(ffmux, \Y)) filter offset+GetSize(argD) <= GetSize(port(ffmux, \Y)) filter port(ffmux, BA).extract(offset, GetSize(argD)) == argD define AB (BA == \B ? \A : \B) // keep-last-value net must have at least three users: ffmux, ff, downstream sink(s) select nusers(port(ffmux, AB)) >= 3 filter GetSize(unextend(port(ffmux, BA))) <= GetSize(argD) filter unextend(port(ffmux, BA)) == argD.extract(0, GetSize(unextend(port(ffmux, BA)))) // Remaining bits on argD must not have any other users filter nusers(argD.extract_end(GetSize(unextend(port(ffmux, BA))))) <= 1 define pol (AB == \A) set ffenpol pol semioptional endmatch code argD if (ffmux) { dffmux = ffmux; dffenpol = ffenpol; argD = port(ffmux, \Y); } else dffmux = nullptr; endcode match ff_enable if ffmux 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) === port(ffmux, ffenpol ? \A : \B) 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; log_dump("ffM", dff, dffmux); if (dff) { dffQ = port(dff, \Q); for (auto b : dffQ) if (b.wire->get_bool_attribute(\keep)) reject; if (clock != SigBit()) { if (port(dff, \CLK) != clock) reject; } dffclock = port(dff, \CLK); } // No enable mux possible without flop else if (ffmux) reject; endcode