Fix unextend method for signed constants
[yosys.git] / passes / pmgen / ice40_dsp.pmg
index 532995da70a49820ae36fdfa66c86203e387ef53..378f251c09fb6a8f2bcea0e1de861f6fbb874c9e 100644 (file)
@@ -6,20 +6,16 @@ state <SigSpec> sigA sigB sigCD sigH sigO
 state <Cell*> add mux
 state <IdString> addAB muxAB
 
-state <bool> ffAcepol ffBcepol ffCDcepol ffOcepol
-state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol
-
-state <Cell*> ffA ffAcemux ffArstmux ffB ffBcemux ffBrstmux ffCD ffCDcemux ffCDrstmux
-state <Cell*> ffFJKG ffFJKGrstmux ffH ffHrstmux ffO ffOcemux ffOrstmux
+state <Cell*> ffA ffB ffCD
+state <Cell*> ffFJKG ffH ffO
 
 // subpattern
+state <bool> argSdff
 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
+udata <Cell*> dff
+udata <bool> dffclock_pol
 
 match mul
        select mul->type.in($mul, \SB_MAC16)
@@ -27,6 +23,18 @@ match mul
 endmatch
 
 code sigA sigB sigH
+       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
+               ++i;
+               return sig.extract(0, i);
+       };
+       sigA = unextend(port(mul, \A));
+       sigB = unextend(port(mul, \B));
+
        SigSpec O;
        if (mul->type == $mul)
                O = mul->getPort(\Y);
@@ -36,34 +44,22 @@ code sigA sigB sigH
        if (GetSize(O) <= 10)
                reject;
 
-       sigA = port(mul, \A);
-       int i;
-       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
+       int i;
        for (i = 0; i < GetSize(O); i++) {
                if (nusers(O[i]) <= 1)
                        break;
                sigH.append(O[i]);
        }
+       // This sigM could have no users if downstream sinks (e.g. $add) is
+       //   narrower than $mul result, for example
+       if (i == 0)
+               reject;
+
        log_assert(nusers(O.extract_end(i)) <= 1);
 endcode
 
-code argQ ffA ffAcemux ffArstmux ffAcepol ffArstpol sigA clock clock_pol
+code argQ ffA sigA clock clock_pol
        if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) {
                argQ = sigA;
                subpattern(in_dffe);
@@ -71,20 +67,12 @@ code argQ ffA ffAcemux ffArstmux ffAcepol ffArstpol sigA clock clock_pol
                        ffA = dff;
                        clock = dffclock;
                        clock_pol = dffclock_pol;
-                       if (dffrstmux) {
-                               ffArstmux = dffrstmux;
-                               ffArstpol = dffrstpol;
-                       }
-                       if (dffcemux) {
-                               ffAcemux = dffcemux;
-                               ffAcepol = dffcepol;
-                       }
                        sigA = dffD;
                }
        }
 endcode
 
-code argQ ffB ffBcemux ffBrstmux ffBcepol ffBrstpol sigB clock clock_pol
+code argQ ffB sigB clock clock_pol
        if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) {
                argQ = sigB;
                subpattern(in_dffe);
@@ -92,85 +80,83 @@ code argQ ffB ffBcemux ffBrstmux ffBcepol ffBrstpol sigB clock clock_pol
                        ffB = dff;
                        clock = dffclock;
                        clock_pol = dffclock_pol;
-                       if (dffrstmux) {
-                               ffBrstmux = dffrstmux;
-                               ffBrstpol = dffrstpol;
-                       }
-                       if (dffcemux) {
-                               ffBcemux = dffcemux;
-                               ffBcepol = dffcepol;
-                       }
                        sigB = dffD;
                }
        }
 endcode
 
-code argD ffFJKG ffFJKGrstmux sigH sigO clock clock_pol
+code argD argSdff ffFJKG sigH clock clock_pol
        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()))) {
                argD = sigH;
+               argSdff = false;
                subpattern(out_dffe);
                if (dff) {
-                       ffFJKG = dff;
-                       clock = dffclock;
-                       clock_pol = dffclock_pol;
-                       if (dffrstmux)
-                               ffFJKGrstmux = dffrstmux;
                        // F/J/K/G do not have a CE-like (hold) input
-                       if (dffcemux)
-                               reject;
+                       if (dff->hasPort(\EN))
+                               goto reject_ffFJKG;
 
                        // Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT)
                        //   shared with A and B
-                       if ((ffArstmux != NULL) != (ffFJKGrstmux != NULL))
-                               reject;
-                       if ((ffBrstmux != NULL) != (ffFJKGrstmux != NULL))
-                               reject;
-                       if (ffArstmux) {
-                               if (port(ffArstmux, \S) != port(ffFJKGrstmux, \S))
-                                       reject;
-                               if (ffArstpol != dffrstpol)
-                                       reject;
+                       if (ffA) {
+                               if (ffA->hasPort(\ARST) != dff->hasPort(\ARST))
+                                       goto reject_ffFJKG;
+                               if (ffA->hasPort(\ARST)) {
+                                       if (port(ffA, \ARST) != port(dff, \ARST))
+                                               goto reject_ffFJKG;
+                                       if (param(ffA, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
+                                               goto reject_ffFJKG;
+                               }
                        }
-                       if (ffBrstmux) {
-                               if (port(ffBrstmux, \S) != port(ffFJKGrstmux, \S))
-                                       reject;
-                               if (ffBrstpol != dffrstpol)
-                                       reject;
+                       if (ffB) {
+                               if (ffB->hasPort(\ARST) != dff->hasPort(\ARST))
+                                       goto reject_ffFJKG;
+                               if (ffB->hasPort(\ARST)) {
+                                       if (port(ffB, \ARST) != port(dff, \ARST))
+                                               goto reject_ffFJKG;
+                                       if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
+                                               goto reject_ffFJKG;
+                               }
                        }
 
+                       ffFJKG = dff;
+                       clock = dffclock;
+                       clock_pol = dffclock_pol;
                        sigH = dffQ;
+
+reject_ffFJKG:                 ;
                }
        }
 endcode
 
-code argD ffH ffHrstmux sigH sigO clock clock_pol
-       if (nusers(sigH) == 2 &&
+code argD argSdff 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;
+               argSdff = false;
                subpattern(out_dffe);
                if (dff) {
-                       ffH = dff;
-                       clock = dffclock;
-                       clock_pol = dffclock_pol;
-                       if (dffrstmux)
-                               ffHrstmux = dffrstmux;
                        // H does not have a CE-like (hold) input
-                       if (dffcemux)
-                               reject;
+                       if (dff->hasPort(\EN))
+                               goto reject_ffH;
 
                        // Reset signal of H (IRSTBOT) shared with B
-                       if ((ffBrstmux != NULL) != (ffHrstmux != NULL))
-                               reject;
-                       if (ffBrstmux) {
-                               if (port(ffBrstmux, \S) != port(ffHrstmux, \S))
-                                       reject;
-                               if (ffBrstpol != dffrstpol)
-                                       reject;
+                       if (ffB->hasPort(\ARST) != dff->hasPort(\ARST))
+                               goto reject_ffH;
+                       if (ffB->hasPort(\ARST)) {
+                               if (port(ffB, \ARST) != port(dff, \ARST))
+                                       goto reject_ffH;
+                               if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
+                                       goto reject_ffH;
                        }
 
+                       ffH = dff;
+                       clock = dffclock;
+                       clock_pol = dffclock_pol;
                        sigH = dffQ;
+
+reject_ffH:            ;
                }
        }
 
@@ -179,12 +165,15 @@ endcode
 
 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)))
+       filter port(add, AB) == sigH.extract(0, GetSize(port(add, AB)))
+       filter nusers(sigH.extract_end(GetSize(port(add, AB)))) <= 1
        set addAB AB
        optional
 endmatch
@@ -211,7 +200,7 @@ endcode
 match mux
        select mux->type == $mux
        choice <IdString> AB {\A, \B}
-       index <int> nusers(port(mux, AB)) === 2
+       select nusers(port(mux, AB)) == 2
        index <SigSpec> port(mux, AB) === sigO
        set muxAB AB
        optional
@@ -222,18 +211,19 @@ code sigO
                sigO = port(mux, \Y);
 endcode
 
-code argD ffO ffOcemux ffOrstmux ffOcepol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo
+code argD argSdff ffO 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) &&
+                       ((param(mul, \TOPOUTPUT_SELECT).as_int() != 1 && param(mul, \BOTOUTPUT_SELECT).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()))) {
+                        (port(mul, \OLOADTOP, State::S0).is_fully_zero() && port(mul, \OLOADBOT, State::S0).is_fully_zero()))) {
 
                dff = nullptr;
 
                // First try entire sigO
                if (nusers(sigO) == 2) {
                        argD = sigO;
+                       argSdff = !mux;
                        subpattern(out_dffe);
                }
 
@@ -241,6 +231,7 @@ code argD ffO ffOcemux ffOrstmux ffOcepol ffOrstpol sigO sigCD clock clock_pol c
                if (!dff && GetSize(sigO) > 16) {
                        argD = sigO.extract(0, 16);
                        if (nusers(argD) == 2) {
+                               argSdff = !mux;
                                subpattern(out_dffe);
                                o_lo = dff;
                        }
@@ -250,14 +241,6 @@ code argD ffO ffOcemux ffOrstmux ffOcepol ffOrstpol sigO sigCD clock clock_pol c
                        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);
                }
@@ -269,29 +252,52 @@ code argD ffO ffOcemux ffOrstmux ffOcepol ffOrstpol sigO sigCD clock clock_pol c
                                reject;
                        sigCD = port(mux, muxAB == \B ? \A : \B);
 
+                       cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
+               } else if (dff && dff->hasPort(\SRST)) {
+                       if (sigCD != sigO)
+                               reject;
+                       sigCD = param(dff, \SRST_VALUE);
+
                        cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
                }
        }
 endcode
 
-code argQ ffCD ffCDcemux ffCDrstmux ffCDcepol ffCDrstpol sigCD clock clock_pol
-       if (!sigCD.empty() &&
+code argQ ffCD sigCD clock clock_pol
+       if (!sigCD.empty() && sigCD != sigO &&
                        (mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) {
                argQ = sigCD;
                subpattern(in_dffe);
                if (dff) {
+                       // Reset signal of C (IRSTTOP) and D (IRSTBOT)
+                       //   shared with A and B
+                       if (ffA) {
+                               if (ffA->hasPort(\ARST) != dff->hasPort(\ARST))
+                                       goto reject_ffCD;
+                               if (ffA->hasPort(\ARST)) {
+                                       if (port(ffA, \ARST) != port(dff, \ARST))
+                                               goto reject_ffCD;
+                                       if (param(ffA, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
+                                               goto reject_ffCD;
+                               }
+                       }
+                       if (ffB) {
+                               if (ffB->hasPort(\ARST) != dff->hasPort(\ARST))
+                                       goto reject_ffCD;
+                               if (ffB->hasPort(\ARST)) {
+                                       if (port(ffB, \ARST) != port(dff, \ARST))
+                                               goto reject_ffCD;
+                                       if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
+                                               goto reject_ffCD;
+                               }
+                       }
+
                        ffCD = dff;
                        clock = dffclock;
                        clock_pol = dffclock_pol;
-                       if (dffrstmux) {
-                               ffCDrstmux = dffrstmux;
-                               ffCDrstpol = dffrstpol;
-                       }
-                       if (dffcemux) {
-                               ffCDcemux = dffcemux;
-                               ffCDcepol = dffcepol;
-                       }
                        sigCD = dffD;
+
+reject_ffCD:           ;
                }
        }
 endcode
@@ -311,16 +317,21 @@ arg argD argQ clock clock_pol
 
 code
        dff = nullptr;
+       if (argQ.empty())
+               reject;
        for (auto c : argQ.chunks()) {
                if (!c.wire)
                        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
 
 match ff
-       select ff->type.in($dff)
+       select ff->type.in($dff, $dffe)
        // DSP48E1 does not support clock inversion
        select param(ff, \CLK_POLARITY).as_bool()
 
@@ -330,8 +341,6 @@ match ff
        // 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
@@ -351,176 +360,34 @@ code argQ argD
        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
 
-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
-
-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
-
-code argD
-       if (ffcemux) {
-               dffcemux = ffcemux;
-               dffcepol = ffcepol;
-               argD = port(ffcemux, ffcepol ? \B : \A);
-               dffD.replace(port(ffcemux, \Y), argD);
-       }
-       else
-               dffcemux = nullptr;
-endcode
-
 // #######################
 
 subpattern out_dffe
-arg argD argQ clock clock_pol
+arg argD argSdff argQ clock clock_pol
 
 code
        dff = nullptr;
-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
-       filter GetSize(BA) >= offset + GetSize(argD)
-       filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
-
-       set ffoffset offset
-       define <bool> pol (BA == \B)
-       set ffcepol pol
-
-       semioptional
-endmatch
-
-code argD argQ
-       dffcemux = ffcemux;
-       if (ffcemux) {
-               SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
-               if (ffoffset + GetSize(argD) > GetSize(BA))
+       for (auto c : argD.chunks())
+               if (c.wire->get_bool_attribute(\keep))
                        reject;
-               for (int i = 1; i < GetSize(argD); i++)
-                       if (BA[ffoffset+i] != argD[i])
-                               reject;
-
-               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
-       filter GetSize(AB) >= offset + GetSize(argD)
-       filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
-
-       set ffoffset offset
-       define <bool> pol (AB == \A)
-       set ffrstpol pol
-
-       semioptional
-endmatch
-
-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;
-       }
 endcode
 
 match ff
-       select ff->type.in($dff)
-       // DSP48E1 does not support clock inversion
+       select ff->type.in($dff, $dffe, $sdff, $sdffce)
+       // SB_MAC16 does not support clock inversion
        select param(ff, \CLK_POLARITY).as_bool()
 
        slice offset GetSize(port(ff, \D))
        index <SigBit> port(ff, \D)[offset] === argD[0]
 
-       // Check that offset is consistent
-       filter (!ffcemux && !ffrstmux) || ffoffset == offset
+       // Only allow sync reset if requested.
+       filter argSdff || ff->type.in($dff, $dffe)
        // 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
-
-       set ffoffset offset
-
-       semioptional
 endmatch
 
 code argQ
@@ -531,17 +398,12 @@ code argQ
                        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);
-               }
+               argQ = argD;
+               argQ.replace(D, Q);
 
                for (auto c : argQ.chunks()) {
-                       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;
@@ -552,7 +414,4 @@ code 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