mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-07 18:05:24 +00:00
xilinx_dsp_cascade to also cascade AREG and BREG
This commit is contained in:
parent
832216dab0
commit
af59856ba1
|
@ -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;
|
||||
|
|
|
@ -1,6 +1,19 @@
|
|||
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
|
||||
|
@ -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);
|
||||
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 (shift17)
|
||||
opmode[6] = State::S1;
|
||||
else
|
||||
opmode[6] = State::S0;
|
||||
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);
|
||||
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));
|
||||
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
|
||||
arg next
|
||||
|
||||
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]
|
||||
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])
|
||||
|
@ -101,17 +144,82 @@ code next
|
|||
++i;
|
||||
return sig.extract(0, i);
|
||||
};
|
||||
}
|
||||
endcode
|
||||
|
||||
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 (!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
|
||||
|
||||
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 (!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
|
||||
|
||||
code
|
||||
if (next) {
|
||||
chain.emplace_back(next, nextP_shift17 ? 17 : nextP ? 0 : -1, AREG, BREG);
|
||||
|
||||
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)
|
||||
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 {
|
||||
if (GetSize(sigC) <= GetSize(port(chain.back().first, \P)) &&
|
||||
port(chain.back().first, \P).extract(0, GetSize(sigC)) != sigC)
|
||||
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);
|
||||
|
||||
}
|
||||
|
@ -124,395 +232,6 @@ finally
|
|||
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;
|
||||
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 <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;
|
||||
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 <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 (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
|
||||
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue