-pattern xilinx_dsp_cascadeP
+pattern xilinx_dsp_cascade
-udata <vector<std::pair<Cell*,bool>>> chain longest_chain
+udata <std::function<SigSpec(const SigSpec&)>> unextend
+udata <vector<std::tuple<Cell*,int,int,int>>> chain longest_chain
+state <Cell*> next
+state <SigSpec> clock
+state <int> AREG BREG
+
+// subpattern
+state <SigSpec> argQ argD
+state <bool> ffcepol ffrstpol
+state <int> ffoffset
+udata <SigSpec> dffD dffQ
+udata <SigBit> dffclock
+udata <Cell*> dff dffcemux dffrstmux
+udata <bool> dffcepol dffrstpol
code
#define MAX_DSP_CASCADE 20
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;
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 <SigBit> 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 <SigBit> 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 <SigBit> 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 <SigBit> 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])
++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 <SigBit> clock
-state <SigSpec> sigA sigB
-
-state <bool> ffA1cepol ffA2cepol ffB1cepol ffB2cepol
-state <bool> ffArstpol ffBrstpol
-
-state <Cell*> ffA1 ffA1cemux ffA1rstmux ffA2 ffA2cemux ffA2rstmux
-state <Cell*> ffB1 ffB1cemux ffB1rstmux ffB2 ffB2cemux ffB2rstmux
-
-// subpattern
-state <SigSpec> argQ argD
-state <bool> ffcepol ffrstpol
-state <int> ffoffset
-udata <SigSpec> dffD dffQ
-udata <SigBit> dffclock
-udata <Cell*> dff dffcemux dffrstmux
-udata <bool> 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 <SigBit> port(dspQA2, \A)[offset] === sigA[0]
- index <SigBit> 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 <SigBit> port(dspQA1, \A)[offset] === sigA[0]
- index <SigBit> 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 <SigBit> port(dspQB2, \B)[offset] === sigB[0]
- index <SigBit> 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 <SigBit> port(dspQB1, \B)[offset] === sigB[0]
- index <SigBit> 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
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