mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 19:52:31 +00:00 
			
		
		
		
	xilinx_dsp_cascade to also cascade AREG and BREG
This commit is contained in:
		
							parent
							
								
									832216dab0
								
							
						
					
					
						commit
						af59856ba1
					
				
					 2 changed files with 176 additions and 458 deletions
				
			
		|  | @ -609,30 +609,26 @@ struct XilinxDspPass : public Pass { | |||
| 		for (auto module : design->selected_modules()) { | ||||
| 			xilinx_simd_pack(module, module->selected_cells()); | ||||
| 
 | ||||
|             { | ||||
|                 xilinx_dsp_pm pm(module, module->selected_cells()); | ||||
|                 pm.run_xilinx_dsp_pack(xilinx_dsp_pack); | ||||
|             } | ||||
|             // Separating out CREG packing 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 CREG into a
 | ||||
|             //   downstream DSP that should have otherwise been a
 | ||||
|             //   PREG of an upstream DSP that had not been pattern
 | ||||
|             //   matched yet
 | ||||
|             { | ||||
|                 xilinx_dsp_CREG_pm pm(module, module->selected_cells()); | ||||
|                 pm.run_xilinx_dsp_packC(xilinx_dsp_packC); | ||||
|             } | ||||
| 
 | ||||
| 			do { | ||||
| 				did_something = false; | ||||
| 			{ | ||||
| 				xilinx_dsp_pm pm(module, module->selected_cells()); | ||||
| 				pm.run_xilinx_dsp_pack(xilinx_dsp_pack); | ||||
| 			} | ||||
| 			// Separating out CREG packing 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 CREG into a
 | ||||
| 			//   downstream DSP that should have otherwise been a
 | ||||
| 			//   PREG of an upstream DSP that had not been pattern
 | ||||
| 			//   matched yet
 | ||||
| 			{ | ||||
| 				xilinx_dsp_CREG_pm pm(module, module->selected_cells()); | ||||
| 				pm.run_xilinx_dsp_packC(xilinx_dsp_packC); | ||||
| 			} | ||||
| 			{ | ||||
| 				xilinx_dsp_cascade_pm pm(module, module->selected_cells()); | ||||
| 				pm.run_xilinx_dsp_cascadeP(); | ||||
| 				//pm.run_xilinx_dsp_cascadeAB();
 | ||||
| 				break; | ||||
| 			} while (did_something); | ||||
| 				pm.run_xilinx_dsp_cascade(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } XilinxDspPass; | ||||
|  |  | |||
|  | @ -1,6 +1,19 @@ | |||
| pattern xilinx_dsp_cascadeP | ||||
| pattern xilinx_dsp_cascade | ||||
| 
 | ||||
| udata <vector<std::pair<Cell*,bool>>> chain longest_chain | ||||
| udata <std::function<SigSpec(const SigSpec&)>> unextend | ||||
| udata <vector<std::tuple<Cell*,int,int,int>>> chain longest_chain | ||||
| state <Cell*> next | ||||
| state <SigSpec> clock | ||||
| state <int> AREG BREG | ||||
| 
 | ||||
| // 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 | ||||
| #define MAX_DSP_CASCADE 20 | ||||
|  | @ -14,41 +27,71 @@ endmatch | |||
| 
 | ||||
| code | ||||
| 	longest_chain.clear(); | ||||
| 	chain.emplace_back(first, false); | ||||
| 	chain.emplace_back(first, -1, -1, -1); | ||||
| 	subpattern(tail); | ||||
| finally | ||||
| 	chain.pop_back(); | ||||
| 	log_assert(chain.empty()); | ||||
| 	if (GetSize(longest_chain) > 1) { | ||||
| 		Cell *dsp = longest_chain.front().first; | ||||
| 		Cell *dsp = std::get<0>(longest_chain.front()); | ||||
| 
 | ||||
| 		Cell *dsp_pcin; | ||||
| 		int P, AREG, BREG; | ||||
| 		for (int i = 1; i < GetSize(longest_chain); i++) { | ||||
| 			Cell *dsp_pcin = longest_chain[i].first; | ||||
| 			bool shift17 = longest_chain[i].second; | ||||
| 			std::tie(dsp_pcin,P,AREG,BREG) = longest_chain[i]; | ||||
| 
 | ||||
| 			dsp_pcin->setPort(ID(C), Const(0, 48)); | ||||
| 
 | ||||
| 			if (i % MAX_DSP_CASCADE > 0) { | ||||
| 				Wire *cascade = module->addWire(NEW_ID, 48); | ||||
| 				dsp_pcin->setPort(ID(PCIN), cascade); | ||||
| 				dsp->setPort(ID(PCOUT), cascade); | ||||
| 				add_siguser(cascade, dsp_pcin); | ||||
| 				add_siguser(cascade, dsp); | ||||
| 				if (P >= 0) { | ||||
| 					Wire *cascade = module->addWire(NEW_ID, 48); | ||||
| 					dsp_pcin->setPort(ID(PCIN), cascade); | ||||
| 					dsp->setPort(ID(PCOUT), cascade); | ||||
| 					add_siguser(cascade, dsp_pcin); | ||||
| 					add_siguser(cascade, dsp); | ||||
| 
 | ||||
| 				SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7)); | ||||
| 				if (shift17) | ||||
| 					opmode[6] = State::S1; | ||||
| 				else | ||||
| 					opmode[6] = State::S0; | ||||
| 					SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7)); | ||||
| 					if (P == 17) | ||||
| 						opmode[6] = State::S1; | ||||
| 					else if (P == 0) | ||||
| 						opmode[6] = State::S0; | ||||
| 					else log_abort(); | ||||
| 
 | ||||
| 				opmode[5] = State::S0; | ||||
| 				opmode[4] = State::S1; | ||||
| 				dsp_pcin->setPort(\OPMODE, opmode); | ||||
| 					opmode[5] = State::S0; | ||||
| 					opmode[4] = State::S1; | ||||
| 					dsp_pcin->setPort(\OPMODE, opmode); | ||||
| 
 | ||||
| 				log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); | ||||
| 					log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); | ||||
| 				} | ||||
| 				if (AREG >= 0) { | ||||
| 					Wire *cascade = module->addWire(NEW_ID, 30); | ||||
| 					dsp_pcin->setPort(ID(ACIN), cascade); | ||||
| 					dsp->setPort(ID(ACOUT), cascade); | ||||
| 					dsp_pcin->unsetPort(ID(A)); | ||||
| 					add_siguser(cascade, dsp_pcin); | ||||
| 					add_siguser(cascade, dsp); | ||||
| 
 | ||||
| 					dsp->setParam(ID(ACASCREG), AREG); | ||||
| 					dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE")); | ||||
| 
 | ||||
| 					log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); | ||||
| 				} | ||||
| 				if (BREG >= 0) { | ||||
| 					Wire *cascade = module->addWire(NEW_ID, 18); | ||||
| 					dsp_pcin->setPort(ID(BCIN), cascade); | ||||
| 					dsp->setPort(ID(BCOUT), cascade); | ||||
| 					dsp_pcin->unsetPort(ID(B)); | ||||
| 					add_siguser(cascade, dsp_pcin); | ||||
| 					add_siguser(cascade, dsp); | ||||
| 
 | ||||
| 					dsp->setParam(ID(BCASCREG), BREG); | ||||
| 					dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE")); | ||||
| 
 | ||||
| 					log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); | ||||
| 				} | ||||
| 			} | ||||
| 			else { | ||||
| 				log_debug("Blocking PCOUT -> PCIN cascade for %s -> %s (exceeds max: %d)\n", log_id(dsp), log_id(dsp_pcin), MAX_DSP_CASCADE); | ||||
| 				log_debug("  Blocking %s -> %s cascade (exceeds max: %d)\n", log_id(dsp), log_id(dsp_pcin), MAX_DSP_CASCADE); | ||||
| 			} | ||||
| 
 | ||||
| 			dsp = dsp_pcin; | ||||
|  | @ -63,35 +106,35 @@ endcode | |||
| 
 | ||||
| subpattern tail | ||||
| arg first | ||||
| arg next | ||||
| 
 | ||||
| match next | ||||
| 	select next->type.in(\DSP48E1) | ||||
| 	select !param(next, \CREG, State::S1).as_bool() | ||||
| 	select port(next, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") | ||||
| 	select nusers(port(next, \C, SigSpec())) > 1 | ||||
| 	select nusers(port(next, \PCIN, SigSpec())) == 0 | ||||
| 	index <SigBit> port(next, \C)[0] === port(chain.back().first, \P)[0] | ||||
| match nextP | ||||
| 	select nextP->type.in(\DSP48E1) | ||||
| 	select !param(nextP, \CREG, State::S1).as_bool() | ||||
| 	select port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") | ||||
| 	select nusers(port(nextP, \C, SigSpec())) > 1 | ||||
| 	select nusers(port(nextP, \PCIN, SigSpec())) == 0 | ||||
| 	index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0] | ||||
| 	semioptional | ||||
| endmatch | ||||
| 
 | ||||
| match next_shift17 | ||||
| 	if !next_shift17 | ||||
| 	select next_shift17->type.in(\DSP48E1) | ||||
| 	select !param(next_shift17, \CREG, State::S1).as_bool() | ||||
| 	select port(next_shift17, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") | ||||
| 	select nusers(port(next_shift17, \C, SigSpec())) > 1 | ||||
| 	select nusers(port(next_shift17, \PCIN, SigSpec())) == 0 | ||||
| 	index <SigBit> port(next_shift17, \C)[0] === port(chain.back().first, \P)[17] | ||||
| match nextP_shift17 | ||||
| 	if !nextP | ||||
| 	select nextP_shift17->type.in(\DSP48E1) | ||||
| 	select !param(nextP_shift17, \CREG, State::S1).as_bool() | ||||
| 	select port(nextP_shift17, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") | ||||
| 	select nusers(port(nextP_shift17, \C, SigSpec())) > 1 | ||||
| 	select nusers(port(nextP_shift17, \PCIN, SigSpec())) == 0 | ||||
| 	index <SigBit> port(nextP_shift17, \C)[0] === port(std::get<0>(chain.back()), \P)[17] | ||||
| 	semioptional | ||||
| endmatch | ||||
| 
 | ||||
| code next | ||||
| 	if (!next) | ||||
| 		next = next_shift17; | ||||
| 	next = nextP; | ||||
| 	if (!nextP) | ||||
| 		next = nextP_shift17; | ||||
| 	if (next) { | ||||
| 		chain.emplace_back(next, next_shift17); | ||||
| 
 | ||||
| 		auto unextend = [](const SigSpec &sig) { | ||||
| 		unextend = [](const SigSpec &sig) { | ||||
| 			int i; | ||||
| 			for (i = GetSize(sig)-1; i > 0; i--) | ||||
| 				if (sig[i] != sig[i-1]) | ||||
|  | @ -101,17 +144,82 @@ code next | |||
| 				++i; | ||||
| 			return sig.extract(0, i); | ||||
| 		}; | ||||
| 	} | ||||
| endcode | ||||
| 
 | ||||
| code argQ clock AREG | ||||
| 	AREG = 0; | ||||
| 	if (next) { | ||||
| 		Cell *prev = std::get<0>(chain.back()); | ||||
| 		if (param(prev, \AREG, 2).as_int() > 0 && | ||||
| 				param(next, \AREG, 2).as_int() > 0 && | ||||
| 				param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && | ||||
| 				port(next, \ACIN, SigSpec()).is_fully_zero() && | ||||
| 				nusers(port(prev, \ACOUT, SigSpec())) <= 1) { | ||||
| 			argQ = unextend(port(next, \A)); | ||||
| 			clock = port(prev, \CLK); | ||||
| 			subpattern(in_dffe); | ||||
| 			if (dff) { | ||||
| 				if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0) | ||||
| 					goto reject_AREG; | ||||
| 				if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0)) | ||||
| 					goto reject_AREG; | ||||
| 				if (!dffcemux && port(prev, \CEA2, State::S0) != State::S0) | ||||
| 					goto reject_AREG; | ||||
| 				if (dffcemux && port(dffcemux, \S) != port(prev, \CEA2, State::S0)) | ||||
| 					goto reject_AREG; | ||||
| 				if (dffD == unextend(port(prev, \A))) | ||||
| 					AREG = 1; | ||||
| reject_AREG:			; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| endcode | ||||
| 
 | ||||
| code argQ clock BREG | ||||
| 	BREG = 0; | ||||
| 	if (next) { | ||||
| 		Cell *prev = std::get<0>(chain.back()); | ||||
| 		if (param(prev, \BREG, 2).as_int() > 0 && | ||||
| 				param(next, \BREG, 2).as_int() > 0 && | ||||
| 				param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && | ||||
| 				port(next, \BCIN, SigSpec()).is_fully_zero() && | ||||
| 				nusers(port(prev, \BCOUT, SigSpec())) <= 1) { | ||||
| 			argQ = unextend(port(next, \B)); | ||||
| 			clock = port(prev, \CLK); | ||||
| 			subpattern(in_dffe); | ||||
| 			if (dff) { | ||||
| 				if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0) | ||||
| 					goto reject_BREG; | ||||
| 				if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0)) | ||||
| 					goto reject_BREG; | ||||
| 				if (!dffcemux && port(prev, \CEB2, State::S0) != State::S0) | ||||
| 					goto reject_BREG; | ||||
| 				if (dffcemux && port(dffcemux, \S) != port(prev, \CEB2, State::S0)) | ||||
| 					goto reject_BREG; | ||||
| 				if (dffD == unextend(port(prev, \B))) | ||||
| 					BREG = 1; | ||||
| reject_BREG:			; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| endcode | ||||
| 
 | ||||
| code | ||||
| 	if (next) { | ||||
| 		chain.emplace_back(next, nextP_shift17 ? 17 : nextP ? 0 : -1, AREG, BREG); | ||||
| 
 | ||||
| 		SigSpec sigC = unextend(port(next, \C)); | ||||
| 
 | ||||
| 		// TODO: Cannot use 'reject' since semioptional | ||||
| 		if (next_shift17) { | ||||
| 			if (GetSize(sigC)+17 <= GetSize(port(chain.back().first, \P)) && | ||||
| 					port(chain.back().first, \P).extract(17, GetSize(sigC)) != sigC) | ||||
| 		if (nextP_shift17) { | ||||
| 			if (GetSize(sigC)+17 <= GetSize(port(std::get<0>(chain.back()), \P)) && | ||||
| 					port(std::get<0>(chain.back()), \P).extract(17, GetSize(sigC)) != sigC) | ||||
| 				subpattern(tail); | ||||
| 		} | ||||
| 		else { | ||||
| 			if (GetSize(sigC) <= GetSize(port(chain.back().first, \P)) && | ||||
| 					port(chain.back().first, \P).extract(0, GetSize(sigC)) != sigC) | ||||
| 			if (GetSize(sigC) <= GetSize(port(std::get<0>(chain.back()), \P)) && | ||||
| 					port(std::get<0>(chain.back()), \P).extract(0, GetSize(sigC)) != sigC) | ||||
| 				subpattern(tail); | ||||
| 
 | ||||
| 		} | ||||
|  | @ -124,395 +232,6 @@ finally | |||
| 		chain.pop_back(); | ||||
| endcode | ||||
| 
 | ||||
| // ########## | ||||
| 
 | ||||
| pattern xilinx_dsp_cascadeAB | ||||
| 
 | ||||
| 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 | ||||
| 
 | ||||
| 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 | ||||
| 	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 | ||||
| 		if (sig[i].wire) | ||||
| 			++i; | ||||
| 		return sig.extract(0, i); | ||||
| 	}; | ||||
| 	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, State::S0) === port(dspD, \CLK, State::S0) | ||||
| 
 | ||||
| 	// 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, State::S0) === port(dspD, \CLK, State::S0) | ||||
| 
 | ||||
| 	// 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, State::S0) === port(dspD, \CLK, State::S0) | ||||
| 
 | ||||
| 	// 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, State::S0) === port(dspD, \CLK, State::S0) | ||||
| 
 | ||||
| 	// 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 | ||||
|  | @ -525,6 +244,9 @@ code | |||
| 			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 | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue