// This file describes the second of three pattern matcher setups that
//   forms the `xilinx_dsp` pass described in xilinx_dsp.cc
// At a high level, it works as follows:
//   (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already,
//       and (b) uses the 'C' port
//   (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
//       (attached to at most two $mux cells that implement clock-enable or
//        reset functionality, using a subpattern discussed below)
// Notes:
//   - Running CREG packing after xilinx_dsp_pack 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 a CREG into a downstream DSP that should
//     have otherwise been a PREG of an upstream DSP that had not been visited
//     yet
//   - The reason this is separated out from the xilinx_dsp.pmg file is
//     for efficiency --- each *.pmg file creates a class of the same basename,
//     which when constructed, creates a custom database tailored to the
//     pattern(s) contained within. Since the pattern in this file must be
//     executed after the pattern contained in xilinx_dsp.pmg, it is necessary
//     to reconstruct this database. Separating the two patterns into
//     independent files causes two smaller, more specific, databases.

pattern xilinx_dsp_packC

udata <std::function<SigSpec(const SigSpec&)>> unextend
state <SigBit> clock
state <SigSpec> sigC sigP
state <bool> ffCcepol ffCrstpol
state <Cell*> ffC ffCcemux ffCrstmux

// Variables used for subpatterns
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

// (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already,
//     and (b) uses the 'C' port
match dsp
	select dsp->type.in(\DSP48E1)
	select param(dsp, \CREG, 1).as_int() == 0
	select nusers(port(dsp, \C, SigSpec())) > 1
endmatch

code sigC sigP clock
	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, \C, SigSpec()));

	SigSpec P = port(dsp, \P);
	if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") {
		// Only care about those bits that are used
		int i;
		for (i = GetSize(P)-1; i >= 0; i--)
			if (nusers(P[i]) > 1)
				break;
		i++;
		log_assert(nusers(P.extract_end(i)) <= 1);
		sigP = P.extract(0, i);
	}
	else
		sigP = P;

	clock = port(dsp, \CLK, SigBit());
endcode

// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
//     (attached to at most two $mux cells that implement clock-enable or
//      reset functionality, using the in_dffe subpattern)
code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC clock
	argQ = sigC;
	subpattern(in_dffe);
	if (dff) {
		ffC = dff;
		clock = dffclock;
		if (dffrstmux) {
			ffCrstmux = dffrstmux;
			ffCrstpol = dffrstpol;
		}
		if (dffcemux) {
			ffCcemux = dffcemux;
			ffCcepol = dffcepol;
		}
		sigC = dffD;
	}
endcode

code
	if (ffC)
		accept;
endcode

// #######################

// Subpattern for matching against input registers, based on knowledge of the
//   'Q' input. Typically, identifying registers with clock-enable and reset
//   capability would be a task would be handled by other Yosys passes such as
//   dff2dffe, but since DSP inference happens much before this, these patterns
//   have to be manually identified.
// At a high level:
//   (1) Starting from a $dff cell that (partially or fully) drives the given
//       'Q' argument
//   (2) Match for a $mux cell implementing synchronous reset semantics ---
//       one that exclusively drives the 'D' input of the $dff, with one of its
//       $mux inputs being fully zero
//   (3) Match for a $mux cell implement clock enable semantics --- one that
//       exclusively drives the 'D' input of the $dff (or the other input of
//       the reset $mux) and where one of this $mux's inputs is connected to
//       the 'Q' output of the $dff
subpattern in_dffe
arg argD argQ clock

code
	dff = nullptr;
	for (const auto &c : argQ.chunks()) {
		// Abandon matches when 'Q' is a constant
		if (!c.wire)
			reject;
		// Abandon matches when 'Q' has the keep attribute set
		if (c.wire->get_bool_attribute(\keep))
			reject;
		// Abandon matches when 'Q' has a non-zero init attribute set
		// (not supported by DSP48E1)
		Const init = c.wire->attributes.at(\init, Const());
		for (auto b : init.extract(c.offset, c.width))
			if (b != State::Sx && b != State::S0)
				reject;
	}
endcode

// (1) Starting from a $dff cell that (partially or fully) drives the given
//     'Q' argument
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 <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

	filter clock == SigBit() || port(ff, \CLK) == clock

	set ffoffset offset
endmatch

code argQ argD
	SigSpec Q = port(ff, \Q);
	dff = ff;
	dffclock = port(ff, \CLK);
	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

// (2) Match for a $mux cell implementing synchronous reset semantics ---
//     exclusively drives the 'D' input of the $dff, with one of the $mux
//     inputs being fully zero
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

// (3) Match for a $mux cell implement clock enable semantics --- one that
//     exclusively drives the 'D' input of the $dff (or the other input of
//     the reset $mux) and where one of this $mux's inputs is connected to
//     the 'Q' output of the $dff
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