pattern ice40_dsp

state <SigBit> clock
state <bool> clock_pol clock_vld
state <SigSpec> sigA sigB sigY sigS
state <Cell*> addAB muxAB

match mul
	select mul->type.in($mul)
	select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
	select GetSize(mul->getPort(\Y)) > 10
endmatch

match ffA
	select ffA->type.in($dff)
	// select nusers(port(ffA, \Q)) == 2
	index <SigSpec> port(ffA, \Q) === port(mul, \A)
	optional
endmatch

code sigA clock clock_pol clock_vld
	sigA = port(mul, \A);

	if (ffA) {
		sigA = port(ffA, \D);

		clock = port(ffA, \CLK).as_bit();
		clock_pol = param(ffA, \CLK_POLARITY).as_bool();
		clock_vld = true;
	}
endcode

match ffB
	select ffB->type.in($dff)
	// select nusers(port(ffB, \Q)) == 2
	index <SigSpec> port(ffB, \Q) === port(mul, \B)
	optional
endmatch

code sigB clock clock_pol clock_vld
	sigB = port(mul, \B);

	if (ffB) {
		sigB = port(ffB, \D);
		SigBit c = port(ffB, \CLK).as_bit();
		bool cp = param(ffB, \CLK_POLARITY).as_bool();

		if (clock_vld && (c != clock || cp != clock_pol))
			reject;

		clock = c;
		clock_pol = cp;
		clock_vld = true;
	}
endcode

match ffY
	select ffY->type.in($dff)
	select nusers(port(ffY, \D)) == 2
	index <SigSpec> port(ffY, \D) === port(mul, \Y)
	optional
endmatch

code sigY clock clock_pol clock_vld
	sigY = port(mul, \Y);

	if (ffY) {
		sigY = port(ffY, \Q);
		SigBit c = port(ffY, \CLK).as_bit();
		bool cp = param(ffY, \CLK_POLARITY).as_bool();

		if (clock_vld && (c != clock || cp != clock_pol))
			reject;

		clock = c;
		clock_pol = cp;
		clock_vld = true;
	}
endcode

match addA
	select addA->type.in($add)
	select nusers(port(addA, \A)) == 2
	index <SigSpec> port(addA, \A) === sigY
	optional
endmatch

match addB
	if !addA
	select addB->type.in($add, $sub)
	select nusers(port(addB, \B)) == 2
	index <SigSpec> port(addB, \B) === sigY
	optional
endmatch

code addAB sigS
	if (addA) {
		addAB = addA;
		sigS = port(addA, \B);
	}
	if (addB) {
		addAB = addB;
		sigS = port(addB, \A);
	}
	if (addAB) {
		int natural_mul_width = GetSize(sigA) + GetSize(sigB);
		int actual_mul_width = GetSize(sigY);
		int actual_acc_width = GetSize(sigS);

		if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
			reject;
		if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
			reject;
	}
endcode

match muxA
	if addAB
	select muxA->type.in($mux)
	select nusers(port(muxA, \A)) == 2
	index <SigSpec> port(muxA, \A) === port(addAB, \Y)
	optional
endmatch

match muxB
	if addAB
	if !muxA
	select muxB->type.in($mux)
	select nusers(port(muxB, \B)) == 2
	index <SigSpec> port(muxB, \B) === port(addAB, \Y)
	optional
endmatch

code muxAB
	muxAB = addAB;
	if (muxA)
		muxAB = muxA;
	if (muxB)
		muxAB = muxB;
endcode

match ffS
	if muxAB
	select ffS->type.in($dff)
	select nusers(port(ffS, \D)) == 2
	index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
	index <SigSpec> port(ffS, \Q) === sigS
endmatch

code clock clock_pol clock_vld
	if (ffS) {
		SigBit c = port(ffS, \CLK).as_bit();
		bool cp = param(ffS, \CLK_POLARITY).as_bool();

		if (clock_vld && (c != clock || cp != clock_pol))
			reject;

		clock = c;
		clock_pol = cp;
		clock_vld = true;
	}
	accept;
endcode
