pattern xilinx_dsp_cascadeP
-udata <std::function<SigSpec(const SigSpec&)>> unextend
-state <SigSpec> 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
+udata <vector<std::pair<Cell*,bool>>> chain longest_chain
+
+code
+#define MAX_DSP_CASCADE 20
+endcode
+
+match first
+ select first->type.in(\DSP48E1)
+ select port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000")
+ select nusers(port(first, \PCOUT, SigSpec())) <= 1
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));
+code
+ longest_chain.clear();
+ chain.emplace_back(first, false);
+ subpattern(tail);
+finally
+ chain.pop_back();
+ log_assert(chain.empty());
+ if (GetSize(longest_chain) > 1) {
+ Cell *dsp = longest_chain.front().first;
+
+ for (int i = 1; i < GetSize(longest_chain); i++) {
+ Cell *dsp_pcin = longest_chain[i].first;
+ bool shift17 = longest_chain[i].second;
+
+ 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));
+ }
+ else {
+ log_debug("Blocking PCOUT -> PCIN cascade for %s -> %s (exceeds max: %d)\n", log_id(dsp), log_id(dsp_pcin), MAX_DSP_CASCADE);
+ }
+
+ dsp = dsp_pcin;
+ }
+
+ did_something = true;
+ accept;
+ }
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 <SigBit> 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
+subpattern tail
+arg first
- optional
+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]
+ semioptional
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 <SigBit> 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
+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]
+ semioptional
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);
- }
+code next
+ if (!next)
+ next = next_shift17;
+ if (next) {
+ chain.emplace_back(next, next_shift17);
+
+ 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);
+ };
+ 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);
- did_something = true;
- accept;
+ }
+ } else {
+ if (GetSize(chain) > GetSize(longest_chain))
+ longest_chain = chain;
+ }
+finally
+ if (next)
+ chain.pop_back();
endcode
// ##########
pattern xilinx_dsp_cascadeAB
-udata <std::function<SigSpec(const SigSpec&)>> unextend
state <SigBit> clock
state <SigSpec> sigA sigB
udata <Cell*> dff dffcemux dffrstmux
udata <bool> dffcepol dffrstpol
-code
- unextend = [](const SigSpec &sig) {
+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])
++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")