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 match dsp select dsp->type.in(\DSP48E1) endmatch code unextend sigA sigffAmuxY sigB sigffBmuxY sigC sigffCmuxY sigD sigffDmuxY 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; sigffAmuxY = SigSpec(); sigffBmuxY = SigSpec(); sigffCmuxY = SigSpec(); sigffDmuxY = SigSpec(); endcode match ffAD if param(dsp, \ADREG).as_int() == 0 select ffAD->type.in($dff) // DSP48E1 does not support clock inversion select param(ffAD, \CLK_POLARITY).as_bool() filter GetSize(port(ffAD, \Q)) >= GetSize(sigA) slice offset GetSize(port(ffAD, \Q)) filter offset+GetSize(sigA) <= GetSize(port(ffAD, \Q)) filter port(ffAD, \Q).extract(offset, GetSize(sigA)) == sigA optional endmatch code sigA sigffAmuxY clock if (ffAD) { for (auto b : port(ffAD, \Q)) if (b.wire->get_bool_attribute(\keep)) reject; clock = port(ffAD, \CLK).as_bit(); SigSpec A = sigA; A.replace(port(ffAD, \Q), port(ffAD, \D)); // Only search for ffAmux if ffA.Q has at // least 3 users (ffA, dsp, ffAmux) and // its ffA.D only has two (ffA, ffAmux) if (nusers(sigA) >= 3 && nusers(A) == 2) sigffAmuxY = sigA; sigA = std::move(A); } endcode match ffADmux if !sigffAmuxY.empty() select ffADmux->type.in($mux) index port(ffADmux, \Y) === port(ffAD, \D) filter GetSize(port(ffADmux, \Y)) >= GetSize(sigA) slice offset GetSize(port(ffADmux, \Y)) filter offset+GetSize(sigA) <= GetSize(port(ffADmux, \Y)) filter port(ffADmux, \Y).extract(offset, GetSize(sigA)) == sigA choice AB {\A, \B} filter offset+GetSize(sigffAmuxY) <= GetSize(port(ffADmux, \Y)) filter port(ffADmux, AB).extract(offset, GetSize(sigffAmuxY)) == sigffAmuxY define pol (AB == \A) set ffADenpol pol optional endmatch 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 match ffA if !preAdd if param(dsp, \AREG).as_int() == 0 select ffA->type.in($dff) // DSP48E1 does not support clock inversion select param(ffA, \CLK_POLARITY).as_bool() filter GetSize(port(ffA, \Q)) >= GetSize(sigA) slice offset GetSize(port(ffA, \Q)) filter offset+GetSize(sigA) <= GetSize(port(ffA, \Q)) filter port(ffA, \Q).extract(offset, GetSize(sigA)) == sigA optional endmatch code sigA sigffAmuxY clock if (ffA) { for (auto b : port(ffA, \Q)) if (b.wire->get_bool_attribute(\keep)) reject; clock = port(ffA, \CLK).as_bit(); SigSpec A = sigA; A.replace(port(ffA, \Q), port(ffA, \D)); // Only search for ffAmux if ffA.Q has at // least 3 users (ffA, dsp, ffAmux) and // its ffA.D only has two (ffA, ffAmux) if (nusers(sigA) >= 3 && nusers(A) == 2) sigffAmuxY = sigA; sigA = std::move(A); } else if (!preAdd) { sigffAmuxY = SigSpec(); } endcode match ffAmux if !sigffAmuxY.empty() select ffAmux->type.in($mux) index port(ffAmux, \Y) === port(ffA, \D) filter GetSize(port(ffAmux, \Y)) >= GetSize(sigA) slice offset GetSize(port(ffAmux, \Y)) filter offset+GetSize(sigA) <= GetSize(port(ffAmux, \Y)) filter port(ffAmux, \Y).extract(offset, GetSize(sigA)) == sigA choice AB {\A, \B} filter offset+GetSize(sigffAmuxY) <= GetSize(port(ffAmux, \Y)) filter port(ffAmux, AB).extract(offset, GetSize(sigffAmuxY)) == sigffAmuxY define pol (AB == \A) set ffAenpol pol optional endmatch code ffA ffAmux ffAenpol ffAD ffADmux // Move AD register to A if no pre-adder if (!ffA && !preAdd && ffAD) { ffA = ffAD; ffAmux = ffADmux; ffAenpol = ffADenpol; ffAD = nullptr; ffADmux = nullptr; } endcode match ffB if param(dsp, \BREG).as_int() == 0 select ffB->type.in($dff) // DSP48E1 does not support clock inversion select param(ffB, \CLK_POLARITY).as_bool() filter GetSize(port(ffB, \Q)) >= GetSize(sigB) slice offset GetSize(port(ffB, \Q)) filter offset+GetSize(sigB) <= GetSize(port(ffB, \Q)) filter port(ffB, \Q).extract(offset, GetSize(sigB)) == sigB optional endmatch code sigB sigffBmuxY clock if (ffB) { for (auto b : port(ffB, \Q)) if (b.wire->get_bool_attribute(\keep)) reject; SigBit c = port(ffB, \CLK).as_bit(); if (clock != SigBit() && c != clock) reject; clock = c; SigSpec B = sigB; B.replace(port(ffB, \Q), port(ffB, \D)); // Only search for ffBmux if ffB.Q has at // least 3 users (ffB, dsp, ffBmux) and // its ffB.D only has two (ffB, ffBmux) if (nusers(sigB) >= 3 && nusers(B) == 2) sigffBmuxY = sigB; sigB = std::move(B); } endcode match ffBmux if !sigffBmuxY.empty() select ffBmux->type.in($mux) index port(ffBmux, \Y) === port(ffB, \D) filter GetSize(port(ffBmux, \Y)) >= GetSize(sigB) slice offset GetSize(port(ffBmux, \Y)) filter offset+GetSize(sigB) <= GetSize(port(ffBmux, \Y)) filter port(ffBmux, \Y).extract(offset, GetSize(sigB)) == sigB choice AB {\A, \B} filter offset+GetSize(sigffBmuxY) <= GetSize(port(ffBmux, \Y)) filter port(ffBmux, AB).extract(offset, GetSize(sigffBmuxY)) == sigffBmuxY define pol (AB == \A) set ffBenpol pol optional endmatch match ffD if param(dsp, \DREG).as_int() == 0 select ffD->type.in($dff) // DSP48E1 does not support clock inversion select param(ffD, \CLK_POLARITY).as_bool() filter GetSize(port(ffD, \Q)) >= GetSize(sigD) slice offset GetSize(port(ffD, \Q)) filter offset+GetSize(sigD) <= GetSize(port(ffD, \Q)) filter port(ffD, \Q).extract(offset, GetSize(sigD)) == sigD optional endmatch code sigD sigffDmuxY clock if (ffD) { for (auto b : port(ffD, \Q)) if (b.wire->get_bool_attribute(\keep)) reject; SigBit c = port(ffD, \CLK).as_bit(); if (clock != SigBit() && c != clock) reject; clock = c; SigSpec D = sigD; D.replace(port(ffD, \Q), port(ffD, \D)); // Only search for ffDmux if ffD.Q has at // least 3 users (ffD, dsp, ffDmux) and // its ffD.D only has two (ffD, ffDmux) if (nusers(sigD) >= 3 && nusers(D) == 2) sigffDmuxY = sigD; sigD = std::move(D); } endcode match ffDmux if !sigffDmuxY.empty() select ffDmux->type.in($mux) index port(ffDmux, \Y) === port(ffD, \D) filter GetSize(port(ffDmux, \Y)) >= GetSize(sigD) slice offset GetSize(port(ffDmux, \Y)) filter offset+GetSize(sigD) <= GetSize(port(ffDmux, \Y)) filter port(ffDmux, \Y).extract(offset, GetSize(sigD)) == sigD choice AB {\A, \B} filter offset+GetSize(sigffDmuxY) <= GetSize(port(ffDmux, \Y)) filter port(ffDmux, AB).extract(offset, GetSize(sigffDmuxY)) == sigffDmuxY define pol (AB == \A) set ffDenpol pol optional endmatch 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 match ffC if param(dsp, \CREG).as_int() == 0 select ffC->type.in($dff) // DSP48E1 does not support clock inversion select param(ffC, \CLK_POLARITY).as_bool() filter GetSize(port(ffC, \Q)) >= GetSize(sigD) slice offset GetSize(port(ffC, \Q)) filter offset+GetSize(sigC) <= GetSize(port(ffC, \Q)) filter port(ffC, \Q).extract(offset, GetSize(sigC)) == sigC optional endmatch code sigC sigffCmuxY clock if (ffC) { for (auto b : port(ffC, \Q)) if (b.wire->get_bool_attribute(\keep)) reject; SigBit c = port(ffC, \CLK).as_bit(); if (clock != SigBit() && c != clock) reject; clock = c; SigSpec C = sigC; C.replace(port(ffC, \Q), port(ffC, \D)); // Only search for ffCmux if ffC.Q has at // least 3 users (ffC, dsp, ffCmux) and // its ffC.D only has two (ffC, ffCmux) if (nusers(sigC) >= 3 && nusers(C) == 2) sigffCmuxY = sigC; sigC = std::move(C); } endcode match ffCmux if !sigffCmuxY.empty() select ffCmux->type.in($mux) index port(ffCmux, \Y) === port(ffC, \D) filter GetSize(port(ffCmux, \Y)) >= GetSize(sigC) slice offset GetSize(port(ffCmux, \Y)) filter offset+GetSize(sigC) <= GetSize(port(ffCmux, \Y)) filter port(ffCmux, \Y).extract(offset, GetSize(sigC)) == sigC choice AB {\A, \B} filter offset+GetSize(sigffCmuxY) <= GetSize(port(ffCmux, \Y)) filter port(ffCmux, AB).extract(offset, GetSize(sigffCmuxY)) == sigffCmuxY define pol (AB == \A) set ffCenpol pol optional endmatch code accept; endcode