yosys/passes/pmgen/ice40_dsp.pmg

575 lines
13 KiB
Plaintext
Raw Normal View History

pattern ice40_dsp
state <SigBit> clock
2019-09-19 14:00:48 -05:00
state <bool> clock_pol cd_signed o_lo
2019-09-05 20:06:59 -05:00
state <SigSpec> sigA sigB sigCD sigH sigO
2019-09-19 14:00:48 -05:00
state <Cell*> add mux
state <IdString> addAB muxAB
state <bool> ffAcepol ffBcepol ffCDcepol ffOcepol
state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol
2019-09-19 14:00:48 -05:00
2019-09-19 16:02:55 -05:00
state <Cell*> ffA ffAcemux ffArstmux ffB ffBcemux ffBrstmux ffCD ffCDcemux
state <Cell*> ffFJKG ffH ffO ffOcemux ffOrstmux
2019-09-19 14:00:48 -05:00
// 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 dffclock_pol
match mul
select mul->type.in($mul, \SB_MAC16)
select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
endmatch
2019-09-05 20:06:59 -05:00
code sigA sigB sigH
2019-09-05 19:58:19 -05:00
SigSpec O;
if (mul->type == $mul)
2019-09-05 19:58:19 -05:00
O = mul->getPort(\Y);
else if (mul->type == \SB_MAC16)
2019-09-05 19:58:19 -05:00
O = mul->getPort(\O);
else log_abort();
2019-09-05 19:58:19 -05:00
if (GetSize(O) <= 10)
reject;
2019-09-05 20:06:59 -05:00
sigA = port(mul, \A);
2019-09-05 19:58:19 -05:00
int i;
2019-09-05 20:06:59 -05:00
for (i = GetSize(sigA)-1; i > 0; i--)
if (sigA[i] != sigA[i-1])
break;
// Do not remove non-const sign bit
if (sigA[i].wire)
++i;
sigA.remove(i, GetSize(sigA)-i);
sigB = port(mul, \B);
for (i = GetSize(sigB)-1; i > 0; i--)
if (sigB[i] != sigB[i-1])
break;
// Do not remove non-const sign bit
if (sigB[i].wire)
++i;
sigB.remove(i, GetSize(sigB)-i);
// Only care about those bits that are used
2019-09-05 19:58:19 -05:00
for (i = 0; i < GetSize(O); i++) {
if (nusers(O[i]) <= 1)
break;
sigH.append(O[i]);
}
log_assert(nusers(O.extract_end(i)) <= 1);
endcode
2019-09-19 14:00:48 -05:00
code argQ ffA ffAcemux ffArstmux ffAcepol ffArstpol sigA clock clock_pol
if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) {
argQ = sigA;
subpattern(in_dffe);
if (dff) {
ffA = dff;
clock = dffclock;
clock_pol = dffclock_pol;
if (dffrstmux) {
ffArstmux = dffrstmux;
ffArstpol = dffrstpol;
}
if (dffcemux) {
ffAcemux = dffcemux;
ffAcepol = dffcepol;
}
sigA = dffD;
}
}
endcode
2019-09-19 14:00:48 -05:00
code argQ ffB ffBcemux ffBrstmux ffBcepol ffBrstpol sigB clock clock_pol
if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) {
argQ = sigB;
subpattern(in_dffe);
if (dff) {
ffB = dff;
clock = dffclock;
clock_pol = dffclock_pol;
if (dffrstmux) {
ffBrstmux = dffrstmux;
ffBrstpol = dffrstpol;
}
if (dffcemux) {
ffBcemux = dffcemux;
ffBcepol = dffcepol;
}
sigB = dffD;
}
}
endcode
2019-08-07 14:57:10 -05:00
2019-09-19 16:02:55 -05:00
code argD ffFJKG sigH sigO clock clock_pol
2019-09-19 14:00:48 -05:00
if (nusers(sigH) == 2 &&
(mul->type != \SB_MAC16 ||
(!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool()))) {
2019-09-19 14:00:48 -05:00
argD = sigH;
subpattern(out_dffe);
if (dff) {
// F/J/K/G do not have a CE-like (hold) input
if (dffcemux)
2019-09-19 16:02:55 -05:00
goto reject_ffFJKG;
2019-07-19 22:25:28 -05:00
2019-09-19 14:00:48 -05:00
// Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT)
// shared with A and B
2019-09-19 16:02:55 -05:00
if ((ffArstmux != NULL) != (dffrstmux != NULL))
goto reject_ffFJKG;
if ((ffBrstmux != NULL) != (dffrstmux != NULL))
goto reject_ffFJKG;
2019-09-19 14:00:48 -05:00
if (ffArstmux) {
2019-09-19 16:02:55 -05:00
if (port(ffArstmux, \S) != port(dffrstmux, \S))
goto reject_ffFJKG;
if (ffArstpol != dffrstpol)
2019-09-19 16:02:55 -05:00
goto reject_ffFJKG;
2019-09-19 14:00:48 -05:00
}
if (ffBrstmux) {
2019-09-19 16:02:55 -05:00
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
goto reject_ffFJKG;
if (ffBrstpol != dffrstpol)
2019-09-19 16:02:55 -05:00
goto reject_ffFJKG;
}
2019-09-19 16:02:55 -05:00
ffFJKG = dff;
clock = dffclock;
clock_pol = dffclock_pol;
sigH = dffQ;
}
}
2019-09-19 16:02:55 -05:00
if (0) {
reject_ffFJKG: ;
}
endcode
2019-09-19 16:02:55 -05:00
code argD ffH sigH sigO clock clock_pol
if (ffFJKG && nusers(sigH) == 2 &&
(mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) {
argD = sigH;
subpattern(out_dffe);
if (dff) {
// H does not have a CE-like (hold) input
if (dffcemux)
2019-09-19 16:02:55 -05:00
goto reject_ffH;
// Reset signal of H (IRSTBOT) shared with B
2019-09-19 16:02:55 -05:00
if ((ffBrstmux != NULL) != (dffrstmux != NULL))
goto reject_ffH;
if (ffBrstmux) {
2019-09-19 16:02:55 -05:00
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
goto reject_ffH;
if (ffBrstpol != dffrstpol)
2019-09-19 16:02:55 -05:00
goto reject_ffH;
2019-09-19 14:00:48 -05:00
}
2019-09-19 16:02:55 -05:00
ffH = dff;
clock = dffclock;
clock_pol = dffclock_pol;
2019-09-19 14:00:48 -05:00
sigH = dffQ;
}
}
2019-09-19 14:00:48 -05:00
2019-09-19 16:02:55 -05:00
if (0) {
reject_ffH: ;
}
2019-09-19 14:00:48 -05:00
sigO = sigH;
endcode
2019-09-19 14:00:48 -05:00
match add
if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT).as_int() == 3)
select add->type.in($add)
choice <IdString> AB {\A, \B}
select nusers(port(add, AB)) == 2
index <SigBit> port(add, AB)[0] === sigH[0]
filter GetSize(port(add, AB)) <= GetSize(sigH)
filter port(add, AB) == sigH.extract(0, GetSize(port(add, AB)))
set addAB AB
optional
endmatch
2019-09-19 14:00:48 -05:00
code sigCD sigO cd_signed
if (add) {
sigCD = port(add, addAB == \A ? \B : \A);
cd_signed = param(add, addAB == \A ? \B_SIGNED : \A_SIGNED).as_bool();
2019-08-07 14:57:10 -05:00
2019-09-19 14:00:48 -05:00
int natural_mul_width = GetSize(sigA) + GetSize(sigB);
int actual_mul_width = GetSize(sigH);
int actual_acc_width = GetSize(sigCD);
2019-09-19 14:00:48 -05:00
if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
reject;
// If accumulator, check adder width and signedness
if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(add, \A_SIGNED).as_bool()))
reject;
2019-09-19 14:00:48 -05:00
sigO = port(add, \Y);
}
endcode
2019-09-19 14:00:48 -05:00
match mux
select mux->type == $mux
choice <IdString> AB {\A, \B}
index <int> nusers(port(mux, AB)) === 2
index <SigSpec> port(mux, AB) === sigO
set muxAB AB
optional
endmatch
2019-09-19 14:00:48 -05:00
code sigO
if (mux)
sigO = port(mux, \Y);
endcode
2019-08-07 14:57:10 -05:00
2019-09-19 14:00:48 -05:00
code argD ffO ffOcemux ffOrstmux ffOcepol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo
if (mul->type != \SB_MAC16 ||
// Ensure that register is not already used
((mul->parameters.at(\TOPOUTPUT_SELECT, 0).as_int() != 1 && mul->parameters.at(\BOTOUTPUT_SELECT, 0).as_int() != 1) &&
// Ensure that OLOADTOP/OLOADBOT is unused or zero
(mul->connections_.at(\OLOADTOP, State::S0).is_fully_zero() && mul->connections_.at(\OLOADBOT, State::S0).is_fully_zero()))) {
2019-09-19 14:00:48 -05:00
dff = nullptr;
// First try entire sigO
if (nusers(sigO) == 2) {
argD = sigO;
subpattern(out_dffe);
}
2019-09-19 14:00:48 -05:00
// Otherwise try just its least significant 16 bits
if (!dff && GetSize(sigO) > 16) {
argD = sigO.extract(0, 16);
if (nusers(argD) == 2) {
subpattern(out_dffe);
o_lo = dff;
}
}
if (dff) {
ffO = dff;
clock = dffclock;
clock_pol = dffclock_pol;
if (dffrstmux) {
ffOrstmux = dffrstmux;
ffOrstpol = dffrstpol;
}
if (dffcemux) {
ffOcemux = dffcemux;
ffOcepol = dffcepol;
}
sigO.replace(sigO.extract(0, GetSize(dffQ)), dffQ);
}
// Loading value into output register is not
// supported unless using accumulator
if (mux) {
if (sigCD != sigO)
reject;
sigCD = port(mux, muxAB == \B ? \A : \B);
cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
}
}
endcode
2019-09-19 16:02:55 -05:00
code argQ ffCD ffCDcemux ffCDcepol ffCDrstpol sigCD clock clock_pol
if (!sigCD.empty() &&
(mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) {
argQ = sigCD;
subpattern(in_dffe);
if (dff) {
if (dffcemux) {
ffCDcemux = dffcemux;
ffCDcepol = dffcepol;
}
2019-09-19 16:02:55 -05:00
// Reset signal of C (IRSTTOP) and D (IRSTBOT)
// shared with A and B
if ((ffArstmux != NULL) != (dffrstmux != NULL))
goto reject_ffCD;
if ((ffBrstmux != NULL) != (dffrstmux != NULL))
goto reject_ffCD;
if (ffArstmux) {
if (port(ffArstmux, \S) != port(dffrstmux, \S))
goto reject_ffCD;
if (ffArstpol != dffrstpol)
goto reject_ffCD;
}
if (ffBrstmux) {
if (port(ffBrstmux, \S) != port(dffrstmux, \S))
goto reject_ffCD;
if (ffBrstpol != dffrstpol)
goto reject_ffCD;
}
ffCD = dff;
clock = dffclock;
clock_pol = dffclock_pol;
sigCD = dffD;
}
}
2019-09-19 16:02:55 -05:00
if (0) {
reject_ffCD: ;
}
endcode
code sigCD
2019-09-19 14:00:48 -05:00
sigCD.extend_u0(32, cd_signed);
endcode
2019-08-15 14:30:46 -05:00
2019-09-19 14:00:48 -05:00
code
accept;
endcode
2019-09-19 14:00:48 -05:00
// #######################
2019-09-19 14:00:48 -05:00
subpattern in_dffe
arg argD argQ clock clock_pol
2019-09-19 14:00:48 -05:00
code
dff = nullptr;
for (auto c : argQ.chunks()) {
if (!c.wire)
reject;
if (c.wire->get_bool_attribute(\keep))
reject;
}
2019-09-19 14:00:48 -05:00
endcode
2019-09-19 14:00:48 -05:00
match ff
select ff->type.in($dff)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
2019-09-19 14:00:48 -05:00
slice offset GetSize(port(ff, \D))
index <SigBit> 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()) {
if (port(ff, \CLK) != clock)
reject;
2019-09-19 14:00:48 -05:00
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
reject;
}
2019-09-19 14:00:48 -05:00
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
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
2019-09-19 14:00:48 -05:00
match ffrstmux
if !argD.empty()
select ffrstmux->type.in($mux)
index <SigSpec> port(ffrstmux, \Y) === argD
choice <IdString> BA {\B, \A}
// DSP48E1 only supports reset to zero
select port(ffrstmux, BA).is_fully_zero()
define <bool> pol (BA == \B)
set ffrstpol pol
semioptional
endmatch
2019-09-19 14:00:48 -05:00
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, <upstream>, 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 <SigSpec> port(ffcemux, \Y) === argD
choice <IdString> AB {\A, \B}
index <SigSpec> port(ffcemux, AB) === argQ
define <bool> pol (AB == \A)
set ffcepol pol
semioptional
endmatch
2019-09-19 14:00:48 -05:00
code argD
if (ffcemux) {
dffcemux = ffcemux;
dffcepol = ffcepol;
argD = port(ffcemux, ffcepol ? \B : \A);
dffD.replace(port(ffcemux, \Y), argD);
}
else
dffcemux = nullptr;
endcode
2019-09-19 14:00:48 -05:00
// #######################
subpattern out_dffe
arg argD argQ clock clock_pol
code
dff = nullptr;
2019-09-19 16:02:55 -05:00
for (auto c : argD.chunks())
if (c.wire->get_bool_attribute(\keep))
reject;
2019-09-19 14:00:48 -05:00
endcode
match ffcemux
select ffcemux->type.in($mux)
// ffcemux output must have two users: ffcemux and ff.D
select nusers(port(ffcemux, \Y)) == 2
choice <IdString> AB {\A, \B}
// keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s)
select nusers(port(ffcemux, AB)) >= 3
slice offset GetSize(port(ffcemux, \Y))
define <IdString> BA (AB == \A ? \B : \A)
index <SigBit> port(ffcemux, BA)[offset] === argD[0]
// Check that the rest of argD is present
2019-09-19 16:02:55 -05:00
filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD)
2019-09-19 14:00:48 -05:00
filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
set ffoffset offset
define <bool> pol (BA == \B)
set ffcepol pol
semioptional
2019-07-22 18:12:57 -05:00
endmatch
2019-09-19 14:00:48 -05:00
code argD argQ
dffcemux = ffcemux;
if (ffcemux) {
SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
SigSpec Y = port(ffcemux, \Y);
argQ = argD;
argD.replace(BA, Y);
argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B));
dffcemux = ffcemux;
dffcepol = ffcepol;
}
endcode
match ffrstmux
select ffrstmux->type.in($mux)
// ffrstmux output must have two users: ffrstmux and ff.D
select nusers(port(ffrstmux, \Y)) == 2
choice <IdString> BA {\B, \A}
// DSP48E1 only supports reset to zero
select port(ffrstmux, BA).is_fully_zero()
slice offset GetSize(port(ffrstmux, \Y))
define <IdString> AB (BA == \B ? \A : \B)
index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
// Check that offset is consistent
filter !ffcemux || ffoffset == offset
// Check that the rest of argD is present
2019-09-19 16:02:55 -05:00
filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
2019-09-19 14:00:48 -05:00
filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
set ffoffset offset
define <bool> pol (AB == \A)
set ffrstpol pol
semioptional
endmatch
2019-09-19 14:00:48 -05:00
code argD argQ
dffrstmux = ffrstmux;
if (ffrstmux) {
SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
SigSpec Y = port(ffrstmux, \Y);
argD.replace(AB, Y);
dffrstmux = ffrstmux;
dffrstpol = ffrstpol;
2019-09-06 14:16:40 -05:00
}
2019-09-19 14:00:48 -05:00
endcode
2019-07-22 18:12:57 -05:00
2019-09-19 14:00:48 -05:00
match ff
select ff->type.in($dff)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
2019-09-19 14:00:48 -05:00
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \D)[offset] === argD[0]
2019-07-22 18:12:57 -05:00
2019-09-19 14:00:48 -05:00
// Check that offset is consistent
filter (!ffcemux && !ffrstmux) || ffoffset == offset
// Check that the rest of argD is present
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
// Check that FF.Q is connected to CE-mux
filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
2019-07-22 18:12:57 -05:00
2019-09-19 14:00:48 -05:00
set ffoffset offset
endmatch
code argQ
if (ff) {
if (clock != SigBit()) {
if (port(ff, \CLK) != clock)
2019-07-23 16:20:34 -05:00
reject;
2019-09-19 14:00:48 -05:00
if (param(ff, \CLK_POLARITY).as_bool() != clock_pol)
reject;
}
SigSpec D = port(ff, \D);
SigSpec Q = port(ff, \Q);
if (!ffcemux) {
argQ = argD;
argQ.replace(D, Q);
2019-07-23 16:20:34 -05:00
}
2019-09-05 19:58:19 -05:00
2019-09-19 14:00:48 -05:00
for (auto c : argQ.chunks()) {
Const init = c.wire->attributes.at(\init, State::Sx);
if (!init.is_fully_undef() && !init.is_fully_zero())
reject;
}
dff = ff;
dffQ = argQ;
dffclock = port(ff, \CLK);
dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
}
// No enable/reset mux possible without flop
else if (dffcemux || dffrstmux)
reject;
endcode