From af59856ba1be1f7cde3154994334f45500af6c22 Mon Sep 17 00:00:00 2001 From: Eddie Hung Date: Thu, 26 Sep 2019 13:29:18 -0700 Subject: [PATCH] xilinx_dsp_cascade to also cascade AREG and BREG --- passes/pmgen/xilinx_dsp.cc | 42 +- passes/pmgen/xilinx_dsp_cascade.pmg | 584 ++++++++-------------------- 2 files changed, 172 insertions(+), 454 deletions(-) diff --git a/passes/pmgen/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc index 4c297a50a..b0251de50 100644 --- a/passes/pmgen/xilinx_dsp.cc +++ b/passes/pmgen/xilinx_dsp.cc @@ -609,30 +609,26 @@ struct XilinxDspPass : public Pass { for (auto module : design->selected_modules()) { xilinx_simd_pack(module, module->selected_cells()); - { - xilinx_dsp_pm pm(module, module->selected_cells()); - pm.run_xilinx_dsp_pack(xilinx_dsp_pack); - } - // Separating out CREG packing is necessary since there - // is no guarantee that the cell ordering corresponds - // to the "expected" case (i.e. the order in which - // they appear in the source) thus the possiblity - // existed that a register got packed as CREG into a - // downstream DSP that should have otherwise been a - // PREG of an upstream DSP that had not been pattern - // matched yet - { - xilinx_dsp_CREG_pm pm(module, module->selected_cells()); - pm.run_xilinx_dsp_packC(xilinx_dsp_packC); - } - - do { - did_something = false; + { + xilinx_dsp_pm pm(module, module->selected_cells()); + pm.run_xilinx_dsp_pack(xilinx_dsp_pack); + } + // Separating out CREG packing is necessary since there + // is no guarantee that the cell ordering corresponds + // to the "expected" case (i.e. the order in which + // they appear in the source) thus the possiblity + // existed that a register got packed as CREG into a + // downstream DSP that should have otherwise been a + // PREG of an upstream DSP that had not been pattern + // matched yet + { + xilinx_dsp_CREG_pm pm(module, module->selected_cells()); + pm.run_xilinx_dsp_packC(xilinx_dsp_packC); + } + { xilinx_dsp_cascade_pm pm(module, module->selected_cells()); - pm.run_xilinx_dsp_cascadeP(); - //pm.run_xilinx_dsp_cascadeAB(); - break; - } while (did_something); + pm.run_xilinx_dsp_cascade(); + } } } } XilinxDspPass; diff --git a/passes/pmgen/xilinx_dsp_cascade.pmg b/passes/pmgen/xilinx_dsp_cascade.pmg index 59cd1267d..2fc943a66 100644 --- a/passes/pmgen/xilinx_dsp_cascade.pmg +++ b/passes/pmgen/xilinx_dsp_cascade.pmg @@ -1,6 +1,19 @@ -pattern xilinx_dsp_cascadeP +pattern xilinx_dsp_cascade -udata >> chain longest_chain +udata > unextend +udata >> chain longest_chain +state next +state clock +state AREG BREG + +// subpattern +state argQ argD +state ffcepol ffrstpol +state ffoffset +udata dffD dffQ +udata dffclock +udata dff dffcemux dffrstmux +udata dffcepol dffrstpol code #define MAX_DSP_CASCADE 20 @@ -14,41 +27,71 @@ endmatch code longest_chain.clear(); - chain.emplace_back(first, false); + chain.emplace_back(first, -1, -1, -1); subpattern(tail); finally chain.pop_back(); log_assert(chain.empty()); if (GetSize(longest_chain) > 1) { - Cell *dsp = longest_chain.front().first; + Cell *dsp = std::get<0>(longest_chain.front()); + Cell *dsp_pcin; + int P, AREG, BREG; for (int i = 1; i < GetSize(longest_chain); i++) { - Cell *dsp_pcin = longest_chain[i].first; - bool shift17 = longest_chain[i].second; + std::tie(dsp_pcin,P,AREG,BREG) = longest_chain[i]; dsp_pcin->setPort(ID(C), Const(0, 48)); if (i % MAX_DSP_CASCADE > 0) { - 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 (shift17) - opmode[6] = State::S1; - else - opmode[6] = State::S0; - - 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 (P >= 0) { + 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 (P == 17) + opmode[6] = State::S1; + else if (P == 0) + opmode[6] = State::S0; + 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 (AREG >= 0) { + Wire *cascade = module->addWire(NEW_ID, 30); + dsp_pcin->setPort(ID(ACIN), cascade); + dsp->setPort(ID(ACOUT), cascade); + dsp_pcin->unsetPort(ID(A)); + add_siguser(cascade, dsp_pcin); + add_siguser(cascade, dsp); + + dsp->setParam(ID(ACASCREG), AREG); + dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE")); + + log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); + } + if (BREG >= 0) { + Wire *cascade = module->addWire(NEW_ID, 18); + dsp_pcin->setPort(ID(BCIN), cascade); + dsp->setPort(ID(BCOUT), cascade); + dsp_pcin->unsetPort(ID(B)); + add_siguser(cascade, dsp_pcin); + add_siguser(cascade, dsp); + + dsp->setParam(ID(BCASCREG), BREG); + dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE")); + + log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); + } } else { - log_debug("Blocking PCOUT -> PCIN cascade for %s -> %s (exceeds max: %d)\n", log_id(dsp), log_id(dsp_pcin), MAX_DSP_CASCADE); + log_debug(" Blocking %s -> %s cascade (exceeds max: %d)\n", log_id(dsp), log_id(dsp_pcin), MAX_DSP_CASCADE); } dsp = dsp_pcin; @@ -63,35 +106,35 @@ endcode subpattern tail arg first - -match next - select next->type.in(\DSP48E1) - select !param(next, \CREG, State::S1).as_bool() - select port(next, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") - select nusers(port(next, \C, SigSpec())) > 1 - select nusers(port(next, \PCIN, SigSpec())) == 0 - index port(next, \C)[0] === port(chain.back().first, \P)[0] +arg next + +match nextP + select nextP->type.in(\DSP48E1) + select !param(nextP, \CREG, State::S1).as_bool() + select port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") + select nusers(port(nextP, \C, SigSpec())) > 1 + select nusers(port(nextP, \PCIN, SigSpec())) == 0 + index port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0] semioptional endmatch -match next_shift17 - if !next_shift17 - select next_shift17->type.in(\DSP48E1) - select !param(next_shift17, \CREG, State::S1).as_bool() - select port(next_shift17, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") - select nusers(port(next_shift17, \C, SigSpec())) > 1 - select nusers(port(next_shift17, \PCIN, SigSpec())) == 0 - index port(next_shift17, \C)[0] === port(chain.back().first, \P)[17] +match nextP_shift17 + if !nextP + select nextP_shift17->type.in(\DSP48E1) + select !param(nextP_shift17, \CREG, State::S1).as_bool() + select port(nextP_shift17, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") + select nusers(port(nextP_shift17, \C, SigSpec())) > 1 + select nusers(port(nextP_shift17, \PCIN, SigSpec())) == 0 + index port(nextP_shift17, \C)[0] === port(std::get<0>(chain.back()), \P)[17] semioptional endmatch code next - if (!next) - next = next_shift17; + next = nextP; + if (!nextP) + next = nextP_shift17; if (next) { - chain.emplace_back(next, next_shift17); - - auto unextend = [](const SigSpec &sig) { + unextend = [](const SigSpec &sig) { int i; for (i = GetSize(sig)-1; i > 0; i--) if (sig[i] != sig[i-1]) @@ -101,418 +144,94 @@ code next ++i; return sig.extract(0, i); }; - SigSpec sigC = unextend(port(next, \C)); - - // TODO: Cannot use 'reject' since semioptional - if (next_shift17) { - if (GetSize(sigC)+17 <= GetSize(port(chain.back().first, \P)) && - port(chain.back().first, \P).extract(17, GetSize(sigC)) != sigC) - subpattern(tail); - } - else { - if (GetSize(sigC) <= GetSize(port(chain.back().first, \P)) && - port(chain.back().first, \P).extract(0, GetSize(sigC)) != sigC) - subpattern(tail); - - } - } else { - if (GetSize(chain) > GetSize(longest_chain)) - longest_chain = chain; } -finally - if (next) - chain.pop_back(); -endcode - -// ########## - -pattern xilinx_dsp_cascadeAB - -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 - -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 - auto 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); - }; - 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; +code argQ clock AREG + AREG = 0; + if (next) { + Cell *prev = std::get<0>(chain.back()); + if (param(prev, \AREG, 2).as_int() > 0 && + param(next, \AREG, 2).as_int() > 0 && + param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && + port(next, \ACIN, SigSpec()).is_fully_zero() && + nusers(port(prev, \ACOUT, SigSpec())) <= 1) { + argQ = unextend(port(next, \A)); + clock = port(prev, \CLK); 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: ; + if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0) + goto reject_AREG; + if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0)) + goto reject_AREG; + if (!dffcemux && port(prev, \CEA2, State::S0) != State::S0) + goto reject_AREG; + if (dffcemux && port(dffcemux, \S) != port(prev, \CEA2, State::S0)) + goto reject_AREG; + if (dffD == unextend(port(prev, \A))) + AREG = 1; +reject_AREG: ; } } } 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; +code argQ clock BREG + BREG = 0; + if (next) { + Cell *prev = std::get<0>(chain.back()); + if (param(prev, \BREG, 2).as_int() > 0 && + param(next, \BREG, 2).as_int() > 0 && + param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && + port(next, \BCIN, SigSpec()).is_fully_zero() && + nusers(port(prev, \BCOUT, SigSpec())) <= 1) { + argQ = unextend(port(next, \B)); + clock = port(prev, \CLK); 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: ; + if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0) + goto reject_BREG; + if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0)) + goto reject_BREG; + if (!dffcemux && port(prev, \CEB2, State::S0) != State::S0) + goto reject_BREG; + if (dffcemux && port(dffcemux, \S) != port(prev, \CEB2, State::S0)) + goto reject_BREG; + if (dffD == unextend(port(prev, \B))) + BREG = 1; +reject_BREG: ; } } } 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 (next) { + chain.emplace_back(next, nextP_shift17 ? 17 : nextP ? 0 : -1, AREG, BREG); - 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 + SigSpec sigC = unextend(port(next, \C)); -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)); + // TODO: Cannot use 'reject' since semioptional + if (nextP_shift17) { + if (GetSize(sigC)+17 <= GetSize(port(std::get<0>(chain.back()), \P)) && + port(std::get<0>(chain.back()), \P).extract(17, GetSize(sigC)) != sigC) + subpattern(tail); } - else - log_abort(); + else { + if (GetSize(sigC) <= GetSize(port(std::get<0>(chain.back()), \P)) && + port(std::get<0>(chain.back()), \P).extract(0, GetSize(sigC)) != sigC) + subpattern(tail); - 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; + } else { + if (GetSize(chain) > GetSize(longest_chain)) + longest_chain = chain; } - - accept; +finally + if (next) + chain.pop_back(); endcode - // ####################### subpattern in_dffe @@ -525,6 +244,9 @@ code reject; 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; } endcode -- 2.30.2