pattern ice40_dsp state clock state clock_pol cd_signed o_lo state sigA sigB sigCD sigH sigO state add mux state addAB muxAB state ffAcepol ffBcepol ffCDcepol ffOcepol state ffArstpol ffBrstpol ffCDrstpol ffFJKGrstpol ffOrstpol state ffA ffAcemux ffArstmux ffB ffBcemux ffBrstmux ffCD ffCDcemux ffCDrstmux state ffFJKG ffFJKGrstmux ffO ffOcemux ffOrstmux // subpattern state argQ argD state ffcepol ffrstpol state ffoffset udata dffD dffQ udata dffclock udata dff dffcemux dffrstmux udata dffcepol dffrstpol dffclock_pol match mul select mul->type.in($mul, \SB_MAC16) select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10 endmatch code sigA sigB sigH SigSpec O; if (mul->type == $mul) O = mul->getPort(\Y); else if (mul->type == \SB_MAC16) O = mul->getPort(\O); else log_abort(); 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 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 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 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 code argD ffFJKG ffFJKGrstmux ffFJKGrstpol sigH sigO 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_REG2).as_bool()))) { argD = sigH; subpattern(out_dffe); if (dff) { ffFJKG = dff; clock = dffclock; clock_pol = dffclock_pol; if (dffrstmux) { ffFJKGrstmux = dffrstmux; ffFJKGrstpol = dffrstpol; } // F/J/K/G do not have a CE-like (hold) input if (dffcemux) reject; // 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 != ffFJKGrstpol) reject; } if (ffBrstmux) { if (port(ffBrstmux, \S) != port(ffFJKGrstmux, \S)) reject; if (ffBrstpol != ffFJKGrstpol) reject; } sigH = dffQ; } } sigO = sigH; 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 AB {\A, \B} select nusers(port(add, AB)) == 2 index 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 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(); int natural_mul_width = GetSize(sigA) + GetSize(sigB); int actual_mul_width = GetSize(sigH); int actual_acc_width = GetSize(sigCD); 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; sigO = port(add, \Y); } endcode match mux select mux->type == $mux choice AB {\A, \B} index nusers(port(mux, AB)) === 2 index port(mux, AB) === sigO set muxAB AB optional endmatch code sigO if (mux) sigO = port(mux, \Y); endcode 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()))) { dff = nullptr; // First try entire sigO if (nusers(sigO) == 2) { argD = sigO; subpattern(out_dffe); } // 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(); } } sigCD.extend_u0(32, cd_signed); endcode code accept; endcode // ####################### subpattern in_dffe arg argD argQ clock clock_pol code dff = nullptr; for (auto c : argQ.chunks()) { if (!c.wire) reject; if (c.wire->get_bool_attribute(\keep)) reject; } endcode match ff select ff->type.in($dff) // DSP48E1 does not support clock inversion select param(ff, \CLK_POLARITY).as_bool() slice offset GetSize(port(ff, \D)) index 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; if (param(ff, \CLK_POLARITY).as_bool() != clock_pol) reject; } 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 match ffrstmux if !argD.empty() select ffrstmux->type.in($mux) index port(ffrstmux, \Y) === argD choice BA {\B, \A} // DSP48E1 only supports reset to zero select port(ffrstmux, BA).is_fully_zero() define 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, , 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 port(ffcemux, \Y) === argD choice AB {\A, \B} index port(ffcemux, AB) === argQ define 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 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 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 BA (AB == \A ? \B : \A) index 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 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)) 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 BA {\B, \A} // DSP48E1 only supports reset to zero select port(ffrstmux, BA).is_fully_zero() slice offset GetSize(port(ffrstmux, \Y)) define AB (BA == \B ? \A : \B) index 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 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 param(ff, \CLK_POLARITY).as_bool() slice offset GetSize(port(ff, \D)) index port(ff, \D)[offset] === argD[0] // 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 set ffoffset offset semioptional endmatch code argQ if (ff) { if (clock != SigBit()) { if (port(ff, \CLK) != clock) reject; 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); } 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; } 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