mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 13:29:12 +00:00 
			
		
		
		
	WIP for xiinx_dsp_cascadeAB
This commit is contained in:
		
							parent
							
								
									b0ad2592be
								
							
						
					
					
						commit
						0bca366bcd
					
				
					 1 changed files with 499 additions and 3 deletions
				
			
		| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
pattern xilinx_dsp_cascade
 | 
			
		||||
pattern xilinx_dsp_cascadeP
 | 
			
		||||
 | 
			
		||||
udata <std::function<SigSpec(const SigSpec&)>> unextend
 | 
			
		||||
state <SigSpec> sigC
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +33,7 @@ match dsp_pcout
 | 
			
		|||
	select nusers(port(dsp_pcout, \P, SigSpec())) > 1
 | 
			
		||||
	select nusers(port(dsp_pcout, \PCOUT, SigSpec())) <= 1
 | 
			
		||||
 | 
			
		||||
	index <SigSpec> port(dsp_pcout, \P)[0] === sigC[0]
 | 
			
		||||
	index <SigBit> port(dsp_pcout, \P)[0] === sigC[0]
 | 
			
		||||
	filter GetSize(port(dsp_pcin, \P)) >= GetSize(sigC)
 | 
			
		||||
	filter port(dsp_pcout, \P).extract(0, GetSize(sigC)) == sigC
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ match dsp_pcout_shift17
 | 
			
		|||
	select nusers(port(dsp_pcout_shift17, \P, SigSpec())) > 1
 | 
			
		||||
	select nusers(port(dsp_pcout_shift17, \PCOUT, SigSpec())) <= 1
 | 
			
		||||
 | 
			
		||||
	index <SigSpec> port(dsp_pcout_shift17, \P)[17] === sigC[0]
 | 
			
		||||
	index <SigBit> port(dsp_pcout_shift17, \P)[17] === sigC[0]
 | 
			
		||||
	filter GetSize(port(dsp_pcout_shift17, \P)) >= GetSize(sigC)+17
 | 
			
		||||
	filter port(dsp_pcout_shift17, \P).extract(17, GetSize(sigC)) == sigC
 | 
			
		||||
endmatch
 | 
			
		||||
| 
						 | 
				
			
			@ -90,5 +90,501 @@ code
 | 
			
		|||
		blacklist(dsp_pcout);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	did_something = true;
 | 
			
		||||
	accept;
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
// ##########
 | 
			
		||||
 | 
			
		||||
pattern xilinx_dsp_cascadeAB
 | 
			
		||||
 | 
			
		||||
udata <std::function<SigSpec(const SigSpec&)>> unextend
 | 
			
		||||
state <SigBit> clock
 | 
			
		||||
state <SigSpec> sigA sigB
 | 
			
		||||
 | 
			
		||||
state <bool> ffA1cepol ffA2cepol ffB1cepol ffB2cepol
 | 
			
		||||
state <bool> ffArstpol ffBrstpol
 | 
			
		||||
 | 
			
		||||
state <Cell*> ffA1 ffA1cemux ffA1rstmux ffA2 ffA2cemux ffA2rstmux
 | 
			
		||||
state <Cell*> ffB1 ffB1cemux ffB1rstmux ffB2 ffB2cemux ffB2rstmux
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
 | 
			
		||||
code
 | 
			
		||||
	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);
 | 
			
		||||
	};
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
match dspD
 | 
			
		||||
	select dspD->type.in(\DSP48E1)
 | 
			
		||||
	select (param(dspD, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && nusers(port(dspD, \A, SigSpec())) > 1 && nusers(port(dspD, \ACIN, SigSpec())) == 0) || (param(dspD, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && nusers(port(dspD, \B, SigSpec())) > 1 && nusers(port(dspD, \BCIN, SigSpec())) == 0)
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
code sigA sigB
 | 
			
		||||
	if (param(dspD, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT")
 | 
			
		||||
		sigA = unextend(port(dspD, \A));
 | 
			
		||||
	if (param(dspD, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT")
 | 
			
		||||
		sigB = unextend(port(dspD, \B));
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
code argQ ffA2 ffA2cemux ffA2rstmux ffA2cepol ffArstpol ffA1 ffA1cemux ffA1rstmux ffA1cepol sigA clock
 | 
			
		||||
	if (!sigA.empty()) {
 | 
			
		||||
		argQ = sigA;
 | 
			
		||||
		subpattern(in_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
			ffA2 = dff;
 | 
			
		||||
			clock = dffclock;
 | 
			
		||||
			if (dffrstmux) {
 | 
			
		||||
				ffA2rstmux = dffrstmux;
 | 
			
		||||
				ffArstpol = dffrstpol;
 | 
			
		||||
			}
 | 
			
		||||
			if (dffcemux) {
 | 
			
		||||
				ffA2cemux = dffcemux;
 | 
			
		||||
				ffA2cepol = dffcepol;
 | 
			
		||||
			}
 | 
			
		||||
			sigA = dffD;
 | 
			
		||||
 | 
			
		||||
			// Now attempt to match A1
 | 
			
		||||
			argQ = sigA;
 | 
			
		||||
			subpattern(in_dffe);
 | 
			
		||||
			if (dff) {
 | 
			
		||||
				if ((ffA2rstmux != nullptr) ^ (dffrstmux != nullptr))
 | 
			
		||||
					goto reject_ffA1;
 | 
			
		||||
				if (dffrstmux) {
 | 
			
		||||
					if (ffArstpol != dffrstpol)
 | 
			
		||||
						goto reject_ffA1;
 | 
			
		||||
					if (port(ffA2rstmux, \S) != port(dffrstmux, \S))
 | 
			
		||||
						goto reject_ffA1;
 | 
			
		||||
					ffA1rstmux = dffrstmux;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				ffA1 = dff;
 | 
			
		||||
				clock = dffclock;
 | 
			
		||||
 | 
			
		||||
				if (dffcemux) {
 | 
			
		||||
					ffA1cemux = dffcemux;
 | 
			
		||||
					ffA1cepol = dffcepol;
 | 
			
		||||
				}
 | 
			
		||||
				sigA = dffD;
 | 
			
		||||
 | 
			
		||||
reject_ffA1:			;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
match dspQA2
 | 
			
		||||
	if ffA1
 | 
			
		||||
	select dspQA2->type.in(\DSP48E1)
 | 
			
		||||
	select param(dspQA2, \A_REG, 2).as_int() == 2
 | 
			
		||||
	select nusers(port(dspQA2, \A, SigSpec())) > 1
 | 
			
		||||
	select nusers(port(dspQA2, \ACOUT, SigSpec())) == 0
 | 
			
		||||
	slice offset GetSize(port(dspQA2, \A))
 | 
			
		||||
	index <SigBit> port(dspQA2, \A)[offset] === sigA[0]
 | 
			
		||||
	index <SigBit> port(dspQA2, \CLK) === port(dspD, \CLK)
 | 
			
		||||
 | 
			
		||||
	// Check that the rest of sigA is present
 | 
			
		||||
	filter GetSize(port(dspQA2, \A)) >= offset + GetSize(sigA)
 | 
			
		||||
	filter port(dspQA2, \A).extract(offset, GetSize(sigA)) == sigA
 | 
			
		||||
 | 
			
		||||
	optional
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
code
 | 
			
		||||
	if (dspQA2) {
 | 
			
		||||
		// Check CE and RST are compatible
 | 
			
		||||
		if ((ffA1cemux != nullptr) == port(dspQA2, \CEA1, State::S1).is_fully_const())
 | 
			
		||||
			reject;
 | 
			
		||||
		if ((ffA2cemux != nullptr) == port(dspQA2, \CEA2, State::S1).is_fully_const())
 | 
			
		||||
			reject;
 | 
			
		||||
		if ((ffA1rstmux != nullptr) == port(dspQA2, \RSTA, State::S0).is_fully_const())
 | 
			
		||||
			reject;
 | 
			
		||||
		if ((ffA2rstmux != nullptr) == port(dspQA2, \RSTA, State::S0).is_fully_const())
 | 
			
		||||
			reject;
 | 
			
		||||
 | 
			
		||||
		if (ffA1cemux) {
 | 
			
		||||
			if (port(dspQA2, \CEA1) != port(ffA1cemux, \S))
 | 
			
		||||
				reject;
 | 
			
		||||
			// TODO: Support inversions
 | 
			
		||||
			if (!ffA1cepol)
 | 
			
		||||
				reject;
 | 
			
		||||
		}
 | 
			
		||||
		if (ffA2cemux) {
 | 
			
		||||
			if (port(dspQA2, \CEA2) != port(ffA2cemux, \S))
 | 
			
		||||
				reject;
 | 
			
		||||
			// TODO: Support inversions
 | 
			
		||||
			if (!ffA2cepol)
 | 
			
		||||
				reject;
 | 
			
		||||
		}
 | 
			
		||||
		if (ffA1rstmux) {
 | 
			
		||||
			if (port(dspQA2, \RSTA) != port(ffA1rstmux, \S))
 | 
			
		||||
				reject;
 | 
			
		||||
			// TODO: Support inversions
 | 
			
		||||
			if (!ffArstpol)
 | 
			
		||||
				reject;
 | 
			
		||||
		}
 | 
			
		||||
		if (ffA2rstmux) {
 | 
			
		||||
			if (port(dspQA2, \RSTA) != port(ffA2rstmux, \S))
 | 
			
		||||
				reject;
 | 
			
		||||
			// TODO: Support inversions
 | 
			
		||||
			if (!ffArstpol)
 | 
			
		||||
				reject;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
match dspQA1
 | 
			
		||||
	if !dspQA1 && !ffA1
 | 
			
		||||
	if ffA2
 | 
			
		||||
	select dspQA1->type.in(\DSP48E1)
 | 
			
		||||
	select param(dspQA1, \A_REG, 2).as_int() == 1
 | 
			
		||||
	select nusers(port(dspQA1, \A, SigSpec())) > 1
 | 
			
		||||
	select nusers(port(dspQA1, \ACOUT, SigSpec())) == 0
 | 
			
		||||
	slice offset GetSize(port(dspQA1, \A))
 | 
			
		||||
	index <SigBit> port(dspQA1, \A)[offset] === sigA[0]
 | 
			
		||||
	index <SigBit> port(dspQA1, \CLK) === port(dspD, \CLK)
 | 
			
		||||
 | 
			
		||||
	// Check that the rest of sigA is present
 | 
			
		||||
	filter GetSize(port(dspQA1, \A)) >= offset + GetSize(sigA)
 | 
			
		||||
	filter port(dspQA1, \A).extract(offset, GetSize(sigA)) == sigA
 | 
			
		||||
 | 
			
		||||
	optional
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
code
 | 
			
		||||
	if (dspQA1) {
 | 
			
		||||
		// Check CE and RST are compatible
 | 
			
		||||
		if ((ffA2cemux != NULL) == port(dspQA1, \CEA2, State::S1).is_fully_const())
 | 
			
		||||
			reject;
 | 
			
		||||
		if ((ffA2rstmux != NULL) == port(dspQA1, \RSTA, State::S0).is_fully_const())
 | 
			
		||||
			reject;
 | 
			
		||||
 | 
			
		||||
		if (!ffA2cepol || !ffArstpol)
 | 
			
		||||
			reject;
 | 
			
		||||
 | 
			
		||||
		if (ffA2cemux) {
 | 
			
		||||
			if (port(dspQA1, \CEA2) != port(ffA2cemux, \S))
 | 
			
		||||
				reject;
 | 
			
		||||
			// TODO: Support inversions
 | 
			
		||||
			if (!ffA2cepol)
 | 
			
		||||
				reject;
 | 
			
		||||
		}
 | 
			
		||||
		if (ffA2rstmux) {
 | 
			
		||||
			if (port(dspQA1, \RSTA) != port(ffA2rstmux, \S))
 | 
			
		||||
				reject;
 | 
			
		||||
			// TODO: Support inversions
 | 
			
		||||
			if (!ffArstpol)
 | 
			
		||||
				reject;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol ffB1 ffB1cemux ffB1rstmux ffB1cepol sigB clock
 | 
			
		||||
	if (!sigB.empty()) {
 | 
			
		||||
		argQ = sigB;
 | 
			
		||||
		subpattern(in_dffe);
 | 
			
		||||
		if (dff) {
 | 
			
		||||
			ffB2 = dff;
 | 
			
		||||
			clock = dffclock;
 | 
			
		||||
			if (dffrstmux) {
 | 
			
		||||
				ffB2rstmux = dffrstmux;
 | 
			
		||||
				ffBrstpol = dffrstpol;
 | 
			
		||||
			}
 | 
			
		||||
			if (dffcemux) {
 | 
			
		||||
				ffB2cemux = dffcemux;
 | 
			
		||||
				ffB2cepol = dffcepol;
 | 
			
		||||
			}
 | 
			
		||||
			sigB = dffD;
 | 
			
		||||
 | 
			
		||||
			// Now attempt to match B1
 | 
			
		||||
			argQ = sigB;
 | 
			
		||||
			subpattern(in_dffe);
 | 
			
		||||
			if (dff) {
 | 
			
		||||
				if ((ffB2rstmux != nullptr) ^ (dffrstmux != nullptr))
 | 
			
		||||
					goto reject_ffB1;
 | 
			
		||||
				if (dffrstmux) {
 | 
			
		||||
					if (ffBrstpol != dffrstpol)
 | 
			
		||||
						goto reject_ffB1;
 | 
			
		||||
					if (port(ffB2rstmux, \S) != port(dffrstmux, \S))
 | 
			
		||||
						goto reject_ffB1;
 | 
			
		||||
					ffB1rstmux = dffrstmux;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				ffB1 = dff;
 | 
			
		||||
				clock = dffclock;
 | 
			
		||||
 | 
			
		||||
				if (dffcemux) {
 | 
			
		||||
					ffB1cemux = dffcemux;
 | 
			
		||||
					ffB1cepol = dffcepol;
 | 
			
		||||
				}
 | 
			
		||||
				sigB = dffD;
 | 
			
		||||
 | 
			
		||||
reject_ffB1:			;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
match dspQB2
 | 
			
		||||
	if ffB1
 | 
			
		||||
	select dspQB2->type.in(\DSP48E1)
 | 
			
		||||
	select param(dspQB2, \B_REG, 2).as_int() == 2
 | 
			
		||||
	select nusers(port(dspQB2, \B, SigSpec())) > 1
 | 
			
		||||
	select nusers(port(dspQB2, \BCOUT, SigSpec())) == 0
 | 
			
		||||
	slice offset GetSize(port(dspQB2, \B))
 | 
			
		||||
	index <SigBit> port(dspQB2, \B)[offset] === sigB[0]
 | 
			
		||||
	index <SigBit> port(dspQB2, \CLK) === port(dspD, \CLK)
 | 
			
		||||
 | 
			
		||||
	// Check that the rest of sigB is present
 | 
			
		||||
	filter GetSize(port(dspQB2, \B)) >= offset + GetSize(sigB)
 | 
			
		||||
	filter port(dspQB2, \B).extract(offset, GetSize(sigB)) == sigB
 | 
			
		||||
 | 
			
		||||
	optional
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
code
 | 
			
		||||
	if (dspQB2) {
 | 
			
		||||
		// Check CE and RST are compatible
 | 
			
		||||
		if ((ffB1cemux != nullptr) == port(dspQB2, \CEB1, State::S1).is_fully_const())
 | 
			
		||||
			reject;
 | 
			
		||||
		if ((ffB2cemux != NULL) == port(dspQB2, \CEB2, State::S1).is_fully_const())
 | 
			
		||||
			reject;
 | 
			
		||||
		if ((ffB1rstmux != NULL) == port(dspQB2, \RSTB, State::S0).is_fully_const())
 | 
			
		||||
			reject;
 | 
			
		||||
		if ((ffB2rstmux != NULL) == port(dspQB2, \RSTB, State::S0).is_fully_const())
 | 
			
		||||
			reject;
 | 
			
		||||
 | 
			
		||||
		if (ffB1cemux) {
 | 
			
		||||
			if (port(dspQB2, \CEB1) != port(ffB1cemux, \S))
 | 
			
		||||
				reject;
 | 
			
		||||
			// TODO: Support inversions
 | 
			
		||||
			if (!ffB1cepol)
 | 
			
		||||
				reject;
 | 
			
		||||
		}
 | 
			
		||||
		if (ffB2cemux) {
 | 
			
		||||
			if (port(dspQB2, \CEB2) != port(ffB2cemux, \S))
 | 
			
		||||
				reject;
 | 
			
		||||
			// TODO: Support inversions
 | 
			
		||||
			if (!ffB2cepol)
 | 
			
		||||
				reject;
 | 
			
		||||
		}
 | 
			
		||||
		if (ffB2rstmux) {
 | 
			
		||||
			if (port(dspQB2, \RSTB) != port(ffB2rstmux, \S))
 | 
			
		||||
				reject;
 | 
			
		||||
			// TODO: Support inversions
 | 
			
		||||
			if (!ffBrstpol)
 | 
			
		||||
				reject;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
match dspQB1
 | 
			
		||||
	if !dspQB1 && !ffB1
 | 
			
		||||
	if ffB2
 | 
			
		||||
	select dspQB1->type.in(\DSP48E1)
 | 
			
		||||
	select param(dspQB1, \B_REG, 2).as_int() >= 1
 | 
			
		||||
	select nusers(port(dspQB1, \B, SigSpec())) > 1
 | 
			
		||||
	select nusers(port(dspQB1, \BCOUT, SigSpec())) == 0
 | 
			
		||||
	slice offset GetSize(port(dspQB1, \B))
 | 
			
		||||
	index <SigBit> port(dspQB1, \B)[offset] === sigB[0]
 | 
			
		||||
	index <SigBit> port(dspQB1, \CLK) === port(dspD, \CLK)
 | 
			
		||||
 | 
			
		||||
	// Check that the rest of sigB is present
 | 
			
		||||
	filter GetSize(port(dspQB1, \B)) >= offset + GetSize(sigB)
 | 
			
		||||
	filter port(dspQB1, \B).extract(offset, GetSize(sigB)) == sigB
 | 
			
		||||
 | 
			
		||||
	optional
 | 
			
		||||
endmatch
 | 
			
		||||
 | 
			
		||||
code
 | 
			
		||||
	if (dspQB1) {
 | 
			
		||||
		// Check CE and RST are compatible
 | 
			
		||||
		if ((ffB2cemux != NULL) != port(dspQB1, \CEB2, State::S1).is_fully_const())
 | 
			
		||||
			reject;
 | 
			
		||||
		if ((ffB2rstmux != NULL) != port(dspQB1, \RSTB, State::S0).is_fully_const())
 | 
			
		||||
			reject;
 | 
			
		||||
 | 
			
		||||
		if (!ffA2cepol || !ffArstpol)
 | 
			
		||||
			reject;
 | 
			
		||||
 | 
			
		||||
		if (ffA2cemux) {
 | 
			
		||||
			if (port(dspQB1, \CEB2) != port(ffB2cemux, \S))
 | 
			
		||||
				reject;
 | 
			
		||||
			// TODO: Support inversions
 | 
			
		||||
			if (!ffA2cepol)
 | 
			
		||||
				reject;
 | 
			
		||||
		}
 | 
			
		||||
		if (ffA2rstmux) {
 | 
			
		||||
			if (port(dspQB1, \RSTB) != port(ffB2rstmux, \S))
 | 
			
		||||
				reject;
 | 
			
		||||
			// TODO: Support inversions
 | 
			
		||||
			if (!ffArstpol)
 | 
			
		||||
				reject;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
code
 | 
			
		||||
	if (dspQA1 || dspQA2) {
 | 
			
		||||
		dspD->setParam(\A_INPUT, Const("CASCADE"));
 | 
			
		||||
		dspD->setPort(\A, Const(0, 30));
 | 
			
		||||
 | 
			
		||||
		Wire *cascade = module->addWire(NEW_ID, 30);
 | 
			
		||||
		if (dspQA1) {
 | 
			
		||||
			dspQA1->setParam(\ACASCREG, 1);
 | 
			
		||||
			dspQA1->setPort(\ACOUT, cascade);
 | 
			
		||||
			log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dspQA1), log_id(dspD));
 | 
			
		||||
		}
 | 
			
		||||
		else if (dspQA2) {
 | 
			
		||||
			dspQA2->setParam(\ACASCREG, 2);
 | 
			
		||||
			dspQA2->setPort(\ACOUT, cascade);
 | 
			
		||||
			log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dspQA2), log_id(dspD));
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			log_abort();
 | 
			
		||||
 | 
			
		||||
		dspD->setPort(\ACIN, cascade);
 | 
			
		||||
		did_something = true;
 | 
			
		||||
	}
 | 
			
		||||
	if (dspQB1 || dspQB2) {
 | 
			
		||||
		dspD->setParam(\B_INPUT, Const("CASCADE"));
 | 
			
		||||
		dspD->setPort(\B, Const(0, 18));
 | 
			
		||||
 | 
			
		||||
		Wire *cascade = module->addWire(NEW_ID, 18);
 | 
			
		||||
		if (dspQB1) {
 | 
			
		||||
			dspQB1->setParam(\BCASCREG, 1);
 | 
			
		||||
			dspQB1->setPort(\BCOUT, cascade);
 | 
			
		||||
			log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dspQB1), log_id(dspD));
 | 
			
		||||
		}
 | 
			
		||||
		else if (dspQB2) {
 | 
			
		||||
			dspQB2->setParam(\BCASCREG, 2);
 | 
			
		||||
			dspQB2->setPort(\BCOUT, cascade);
 | 
			
		||||
			log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dspQB2), log_id(dspD));
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			log_abort();
 | 
			
		||||
 | 
			
		||||
		dspD->setPort(\BCIN, cascade);
 | 
			
		||||
		did_something = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	accept;
 | 
			
		||||
endcode
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// #######################
 | 
			
		||||
 | 
			
		||||
subpattern in_dffe
 | 
			
		||||
arg argD argQ clock
 | 
			
		||||
 | 
			
		||||
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 <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() && port(ff, \CLK) != clock)
 | 
			
		||||
		reject;
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue