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 // subpattern state dffQ state dffenpol_ udata dffD 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); // 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); //if (GetSize(sigM) <= 10) // reject; endcode code dffQ ffAD ffADmux ffADenpol sigA clock if (param(dsp, \ADREG).as_int() == 0) { dffQ = 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 dffQ 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) { dffQ = 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 dffQ ffB ffBmux ffBenpol sigB clock if (param(dsp, \BREG).as_int() == 0) { dffQ = sigB; subpattern(in_dffe); if (dff) { ffB = dff; clock = dffclock; if (dffmux) { ffBmux = dffmux; ffBenpol = dffenpol; } sigB = dffD; } } endcode code dffQ ffD ffDmux ffDenpol sigD clock if (param(dsp, \DREG).as_int() == 0) { dffQ = sigD; subpattern(in_dffe); if (dff) { ffD = dff; clock = dffclock; if (dffmux) { ffDmux = dffmux; ffDenpol = dffenpol; } sigD = dffD; } } endcode match ffMmux if param(dsp, \MREG).as_int() == 0 if nusers(sigM) == 2 select ffMmux->type.in($mux) choice BA {\B, \A} // new-value net must have exactly two users: dsp and ffMmux select nusers(port(ffMmux, BA)) == 2 define AB (BA == \B ? \A : \B) // keep-last-value net must have at least three users: ffMmux, ffM, downstream sink(s) select nusers(port(ffMmux, AB)) >= 3 // ffMmux output must have two users: ffMmux and ffM.D select nusers(port(ffMmux, \Y)) == 2 filter GetSize(unextend(port(ffMmux, BA))) <= GetSize(sigM) filter unextend(port(ffMmux, BA)) == sigM.extract(0, GetSize(unextend(port(ffMmux, BA)))) // Remaining bits on sigM must not have any other users filter nusers(sigM.extract_end(GetSize(unextend(port(ffMmux, BA))))) <= 1 define pol (AB == \A) set ffMenpol pol optional endmatch code sigM if (ffMmux) sigM = port(ffMmux, \Y); endcode match ffM_enable if ffMmux if nusers(sigM) == 2 select ffM_enable->type.in($dff) // DSP48E1 does not support clock inversion select param(ffM_enable, \CLK_POLARITY).as_bool() index port(ffM_enable, \D) === sigM index port(ffM_enable, \Q) === port(ffMmux, ffMenpol ? \A : \B) endmatch match ffM if !ffM_enable if param(dsp, \MREG).as_int() == 0 if nusers(sigM) == 2 select ffM->type.in($dff) // DSP48E1 does not support clock inversion select param(ffM, \CLK_POLARITY).as_bool() index port(ffM, \D) === sigM optional endmatch code ffM clock sigM sigP if (ffM_enable) { log_assert(!ffM); ffM = ffM_enable; } if (ffM) { sigM = port(ffM, \Q); for (auto b : sigM) if (b.wire->get_bool_attribute(\keep)) reject; SigBit c = port(ffM, \CLK).as_bit(); if (clock != SigBit() && c != clock) reject; clock = c; } // No enable mux possible without flop else if (ffMmux) reject; 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 match ffPmux 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 if (ffMmux && !postAdd) || nusers(sigP) == 2 select ffPmux->type.in($mux) // ffPmux output must have two users: ffPmux and ffP.D select nusers(port(ffPmux, \Y)) == 2 filter GetSize(port(ffPmux, \Y)) >= GetSize(sigP) slice offset GetSize(port(ffPmux, \Y)) filter offset+GetSize(sigP) <= GetSize(port(ffPmux, \Y)) choice BA {\B, \A} filter port(ffPmux, BA).extract(offset, GetSize(sigP)) == sigP define AB (BA == \B ? \A : \B) // keep-last-value net must have at least three users: ffPmux, ffP, downstream sink(s) filter nusers(port(ffPmux, AB)) >= 3 define pol (AB == \A) set ffPenpol pol set ffPoffset offset optional endmatch code sigP if (ffPmux) sigP.replace(port(ffPmux, ffPenpol ? \B : \A), port(ffPmux, \Y)); endcode match ffP_enable if ffPmux if nusers(sigP) == 2 select ffP_enable->type.in($dff) // DSP48E1 does not support clock inversion select param(ffP_enable, \CLK_POLARITY).as_bool() index port(ffP_enable, \D) === port(ffPmux, \Y) index port(ffP_enable, \Q) === port(ffPmux, ffPenpol ? \A : \B) filter GetSize(port(ffP_enable, \D)) >= GetSize(sigP) filter ffPoffset+GetSize(sigP) <= GetSize(port(ffP_enable, \D)) filter port(ffP_enable, \D).extract(ffPoffset, GetSize(sigP)) == sigP endmatch match ffP if !ffP_enable 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 if (ffMmux && !postAdd) || nusers(sigP) == 2 select ffP->type.in($dff) // DSP48E1 does not support clock inversion select param(ffP, \CLK_POLARITY).as_bool() filter GetSize(port(ffP, \D)) >= GetSize(sigP) slice offset GetSize(port(ffP, \D)) filter offset+GetSize(sigP) <= GetSize(port(ffP, \D)) filter port(ffP, \D).extract(offset, GetSize(sigP)) == sigP optional endmatch code ffP sigP clock if (ffP_enable) { log_assert(!ffP); ffP = ffP_enable; } if (ffP) { for (auto b : port(ffP, \Q)) if (b.wire->get_bool_attribute(\keep)) reject; SigBit c = port(ffP, \CLK).as_bit(); if (clock != SigBit() && c != clock) reject; clock = c; sigP.replace(port(ffP, \D), port(ffP, \Q)); } // No enable mux possible without flop else if (ffPmux) reject; 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 dffQ ffC ffCmux ffCenpol sigC clock if (param(dsp, \CREG).as_int() == 0) { dffQ = 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 dffQ clock dffenpol_ code dff = nullptr; dffmux = nullptr; endcode 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(dffQ) slice offset GetSize(port(ff, \Q)) filter offset+GetSize(dffQ) <= GetSize(port(ff, \Q)) filter port(ff, \Q).extract(offset, GetSize(dffQ)) == dffQ semioptional endmatch code dffQ if (ff) { for (auto b : dffQ) if (b.wire->get_bool_attribute(\keep)) reject; if (clock != SigBit()) { if (port(ff, \CLK) != clock) reject; } else dffclock = port(ff, \CLK); dff = ff; dffD = dffQ; 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(dffQ) >= 3 && nusers(dffD) == 2)) dffQ = SigSpec(); } else dffQ = SigSpec(); endcode match ffmux if !dffQ.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(dffQ) <= GetSize(port(ffmux, \Y)) filter port(ffmux, AB).extract(offset, GetSize(dffQ)) == dffQ define pol (AB == \A) set dffenpol_ pol semioptional endmatch code if (ffmux) { dffmux = ffmux; dffenpol = dffenpol_; dffD = port(ffmux, dffenpol ? \B : \A); } endcode