pattern xilinx_dsp_cascadeP udata > unextend state sigC match dsp_pcin select dsp_pcin->type.in(\DSP48E1) select !param(dsp_pcin, \CREG, State::S1).as_bool() select port(dsp_pcin, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") select nusers(port(dsp_pcin, \C, SigSpec())) > 1 select nusers(port(dsp_pcin, \PCIN, SigSpec())) == 0 endmatch code sigC 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); }; sigC = unextend(port(dsp_pcin, \C)); endcode match dsp_pcout select dsp_pcout->type.in(\DSP48E1) select nusers(port(dsp_pcout, \P, SigSpec())) > 1 select nusers(port(dsp_pcout, \PCOUT, SigSpec())) <= 1 index port(dsp_pcout, \P)[0] === sigC[0] filter GetSize(port(dsp_pcin, \P)) >= GetSize(sigC) filter port(dsp_pcout, \P).extract(0, GetSize(sigC)) == sigC optional endmatch match dsp_pcout_shift17 if !dsp_pcout select dsp_pcout_shift17->type.in(\DSP48E1) select nusers(port(dsp_pcout_shift17, \P, SigSpec())) > 1 select nusers(port(dsp_pcout_shift17, \PCOUT, SigSpec())) <= 1 index port(dsp_pcout_shift17, \P)[17] === sigC[0] filter GetSize(port(dsp_pcout_shift17, \P)) >= GetSize(sigC)+17 filter port(dsp_pcout_shift17, \P).extract(17, GetSize(sigC)) == sigC endmatch code Cell *dsp; if (dsp_pcout) dsp = dsp_pcout; else if (dsp_pcout_shift17) dsp = dsp_pcout_shift17; else log_abort(); dsp_pcin->setPort(ID(C), Const(0, 48)); Wire *cascade = module->addWire(NEW_ID, 48); dsp_pcin->setPort(ID(PCIN), cascade); dsp->setPort(ID(PCOUT), cascade); add_siguser(cascade, dsp_pcin); add_siguser(cascade, dsp); SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7)); if (dsp_pcout) opmode[6] = State::S0; else if (dsp_pcout_shift17) opmode[6] = State::S1; else log_abort(); opmode[5] = State::S0; opmode[4] = State::S1; dsp_pcin->setPort(\OPMODE, opmode); log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); if (nusers(port(dsp_pcin, \PCOUT, SigSpec())) > 1) { log_debug(" Saturated PCIN/PCOUT on %s\n", log_id(dsp_pcin)); blacklist(dsp_pcin); } if (nusers(port(dsp, \PCIN, SigSpec())) > 1) { log_debug(" Saturated PCIN/PCOUT on %s\n", log_id(dsp)); blacklist(dsp_pcout); } did_something = true; accept; endcode // ########## pattern xilinx_dsp_cascadeAB udata > unextend state clock state sigA sigB state ffA1cepol ffA2cepol ffB1cepol ffB2cepol state ffArstpol ffBrstpol state ffA1 ffA1cemux ffA1rstmux ffA2 ffA2cemux ffA2rstmux state ffB1 ffB1cemux ffB1rstmux ffB2 ffB2cemux ffB2rstmux // subpattern state argQ argD state ffcepol ffrstpol state ffoffset udata dffD dffQ udata dffclock udata dff dffcemux dffrstmux udata dffcepol dffrstpol code 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); }; endcode match dspD select dspD->type.in(\DSP48E1) select (param(dspD, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && nusers(port(dspD, \A, SigSpec())) > 1 && nusers(port(dspD, \ACIN, SigSpec())) == 0) || (param(dspD, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && nusers(port(dspD, \B, SigSpec())) > 1 && nusers(port(dspD, \BCIN, SigSpec())) == 0) endmatch code sigA sigB if (param(dspD, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT") sigA = unextend(port(dspD, \A)); if (param(dspD, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT") sigB = unextend(port(dspD, \B)); endcode code argQ ffA2 ffA2cemux ffA2rstmux ffA2cepol ffArstpol ffA1 ffA1cemux ffA1rstmux ffA1cepol sigA clock if (!sigA.empty()) { argQ = sigA; subpattern(in_dffe); if (dff) { ffA2 = dff; clock = dffclock; if (dffrstmux) { ffA2rstmux = dffrstmux; ffArstpol = dffrstpol; } if (dffcemux) { ffA2cemux = dffcemux; ffA2cepol = dffcepol; } sigA = dffD; // Now attempt to match A1 argQ = sigA; subpattern(in_dffe); if (dff) { if ((ffA2rstmux != nullptr) ^ (dffrstmux != nullptr)) goto reject_ffA1; if (dffrstmux) { if (ffArstpol != dffrstpol) goto reject_ffA1; if (port(ffA2rstmux, \S) != port(dffrstmux, \S)) goto reject_ffA1; ffA1rstmux = dffrstmux; } ffA1 = dff; clock = dffclock; if (dffcemux) { ffA1cemux = dffcemux; ffA1cepol = dffcepol; } sigA = dffD; reject_ffA1: ; } } } endcode match dspQA2 if ffA1 select dspQA2->type.in(\DSP48E1) select param(dspQA2, \A_REG, 2).as_int() == 2 select nusers(port(dspQA2, \A, SigSpec())) > 1 select nusers(port(dspQA2, \ACOUT, SigSpec())) == 0 slice offset GetSize(port(dspQA2, \A)) index port(dspQA2, \A)[offset] === sigA[0] index port(dspQA2, \CLK, State::S0) === port(dspD, \CLK, State::S0) // Check that the rest of sigA is present filter GetSize(port(dspQA2, \A)) >= offset + GetSize(sigA) filter port(dspQA2, \A).extract(offset, GetSize(sigA)) == sigA optional endmatch code if (dspQA2) { // Check CE and RST are compatible if ((ffA1cemux != nullptr) == port(dspQA2, \CEA1, State::S1).is_fully_const()) reject; if ((ffA2cemux != nullptr) == port(dspQA2, \CEA2, State::S1).is_fully_const()) reject; if ((ffA1rstmux != nullptr) == port(dspQA2, \RSTA, State::S0).is_fully_const()) reject; if ((ffA2rstmux != nullptr) == port(dspQA2, \RSTA, State::S0).is_fully_const()) reject; if (ffA1cemux) { if (port(dspQA2, \CEA1) != port(ffA1cemux, \S)) reject; // TODO: Support inversions if (!ffA1cepol) reject; } if (ffA2cemux) { if (port(dspQA2, \CEA2) != port(ffA2cemux, \S)) reject; // TODO: Support inversions if (!ffA2cepol) reject; } if (ffA1rstmux) { if (port(dspQA2, \RSTA) != port(ffA1rstmux, \S)) reject; // TODO: Support inversions if (!ffArstpol) reject; } if (ffA2rstmux) { if (port(dspQA2, \RSTA) != port(ffA2rstmux, \S)) reject; // TODO: Support inversions if (!ffArstpol) reject; } } endcode match dspQA1 if !dspQA1 && !ffA1 if ffA2 select dspQA1->type.in(\DSP48E1) select param(dspQA1, \A_REG, 2).as_int() == 1 select nusers(port(dspQA1, \A, SigSpec())) > 1 select nusers(port(dspQA1, \ACOUT, SigSpec())) == 0 slice offset GetSize(port(dspQA1, \A)) index port(dspQA1, \A)[offset] === sigA[0] index port(dspQA1, \CLK, State::S0) === port(dspD, \CLK, State::S0) // Check that the rest of sigA is present filter GetSize(port(dspQA1, \A)) >= offset + GetSize(sigA) filter port(dspQA1, \A).extract(offset, GetSize(sigA)) == sigA optional endmatch code if (dspQA1) { // Check CE and RST are compatible if ((ffA2cemux != NULL) == port(dspQA1, \CEA2, State::S1).is_fully_const()) reject; if ((ffA2rstmux != NULL) == port(dspQA1, \RSTA, State::S0).is_fully_const()) reject; if (!ffA2cepol || !ffArstpol) reject; if (ffA2cemux) { if (port(dspQA1, \CEA2) != port(ffA2cemux, \S)) reject; // TODO: Support inversions if (!ffA2cepol) reject; } if (ffA2rstmux) { if (port(dspQA1, \RSTA) != port(ffA2rstmux, \S)) reject; // TODO: Support inversions if (!ffArstpol) reject; } } endcode code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol ffB1 ffB1cemux ffB1rstmux ffB1cepol sigB clock if (!sigB.empty()) { argQ = sigB; subpattern(in_dffe); if (dff) { ffB2 = dff; clock = dffclock; if (dffrstmux) { ffB2rstmux = dffrstmux; ffBrstpol = dffrstpol; } if (dffcemux) { ffB2cemux = dffcemux; ffB2cepol = dffcepol; } sigB = dffD; // Now attempt to match B1 argQ = sigB; subpattern(in_dffe); if (dff) { if ((ffB2rstmux != nullptr) ^ (dffrstmux != nullptr)) goto reject_ffB1; if (dffrstmux) { if (ffBrstpol != dffrstpol) goto reject_ffB1; if (port(ffB2rstmux, \S) != port(dffrstmux, \S)) goto reject_ffB1; ffB1rstmux = dffrstmux; } ffB1 = dff; clock = dffclock; if (dffcemux) { ffB1cemux = dffcemux; ffB1cepol = dffcepol; } sigB = dffD; reject_ffB1: ; } } } endcode match dspQB2 if ffB1 select dspQB2->type.in(\DSP48E1) select param(dspQB2, \B_REG, 2).as_int() == 2 select nusers(port(dspQB2, \B, SigSpec())) > 1 select nusers(port(dspQB2, \BCOUT, SigSpec())) == 0 slice offset GetSize(port(dspQB2, \B)) index port(dspQB2, \B)[offset] === sigB[0] index port(dspQB2, \CLK, State::S0) === port(dspD, \CLK, State::S0) // Check that the rest of sigB is present filter GetSize(port(dspQB2, \B)) >= offset + GetSize(sigB) filter port(dspQB2, \B).extract(offset, GetSize(sigB)) == sigB optional endmatch code if (dspQB2) { // Check CE and RST are compatible if ((ffB1cemux != nullptr) == port(dspQB2, \CEB1, State::S1).is_fully_const()) reject; if ((ffB2cemux != NULL) == port(dspQB2, \CEB2, State::S1).is_fully_const()) reject; if ((ffB1rstmux != NULL) == port(dspQB2, \RSTB, State::S0).is_fully_const()) reject; if ((ffB2rstmux != NULL) == port(dspQB2, \RSTB, State::S0).is_fully_const()) reject; if (ffB1cemux) { if (port(dspQB2, \CEB1) != port(ffB1cemux, \S)) reject; // TODO: Support inversions if (!ffB1cepol) reject; } if (ffB2cemux) { if (port(dspQB2, \CEB2) != port(ffB2cemux, \S)) reject; // TODO: Support inversions if (!ffB2cepol) reject; } if (ffB2rstmux) { if (port(dspQB2, \RSTB) != port(ffB2rstmux, \S)) reject; // TODO: Support inversions if (!ffBrstpol) reject; } } endcode match dspQB1 if !dspQB1 && !ffB1 if ffB2 select dspQB1->type.in(\DSP48E1) select param(dspQB1, \B_REG, 2).as_int() >= 1 select nusers(port(dspQB1, \B, SigSpec())) > 1 select nusers(port(dspQB1, \BCOUT, SigSpec())) == 0 slice offset GetSize(port(dspQB1, \B)) index port(dspQB1, \B)[offset] === sigB[0] index port(dspQB1, \CLK, State::S0) === port(dspD, \CLK, State::S0) // Check that the rest of sigB is present filter GetSize(port(dspQB1, \B)) >= offset + GetSize(sigB) filter port(dspQB1, \B).extract(offset, GetSize(sigB)) == sigB optional endmatch code if (dspQB1) { // Check CE and RST are compatible if ((ffB2cemux != NULL) != port(dspQB1, \CEB2, State::S1).is_fully_const()) reject; if ((ffB2rstmux != NULL) != port(dspQB1, \RSTB, State::S0).is_fully_const()) reject; if (!ffA2cepol || !ffArstpol) reject; if (ffA2cemux) { if (port(dspQB1, \CEB2) != port(ffB2cemux, \S)) reject; // TODO: Support inversions if (!ffA2cepol) reject; } if (ffA2rstmux) { if (port(dspQB1, \RSTB) != port(ffB2rstmux, \S)) reject; // TODO: Support inversions if (!ffArstpol) reject; } } endcode code if (dspQA1 || dspQA2) { dspD->setParam(\A_INPUT, Const("CASCADE")); dspD->setPort(\A, Const(0, 30)); Wire *cascade = module->addWire(NEW_ID, 30); if (dspQA1) { dspQA1->setParam(\ACASCREG, 1); dspQA1->setPort(\ACOUT, cascade); log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dspQA1), log_id(dspD)); } else if (dspQA2) { dspQA2->setParam(\ACASCREG, 2); dspQA2->setPort(\ACOUT, cascade); log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dspQA2), log_id(dspD)); } else log_abort(); dspD->setPort(\ACIN, cascade); did_something = true; } if (dspQB1 || dspQB2) { dspD->setParam(\B_INPUT, Const("CASCADE")); dspD->setPort(\B, Const(0, 18)); Wire *cascade = module->addWire(NEW_ID, 18); if (dspQB1) { dspQB1->setParam(\BCASCREG, 1); dspQB1->setPort(\BCOUT, cascade); log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dspQB1), log_id(dspD)); } else if (dspQB2) { dspQB2->setParam(\BCASCREG, 2); dspQB2->setPort(\BCOUT, cascade); log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dspQB2), log_id(dspD)); } else log_abort(); dspD->setPort(\BCIN, cascade); did_something = true; } 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] // Check that the rest of argQ is present filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ set ffoffset offset endmatch code argQ argD { if (clock != SigBit() && port(ff, \CLK) != clock) reject; SigSpec Q = port(ff, \Q); 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 ffcemux 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; argD = port(ffcemux, ffcepol ? \B : \A); dffD.replace(port(ffcemux, \Y), argD); } else dffcemux = nullptr; endcode