mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	Merge branch 'eddie/xilinx_srl' into xaig_arrival
This commit is contained in:
		
						commit
						1b08f861b6
					
				
					 13 changed files with 819 additions and 225 deletions
				
			
		|  | @ -36,6 +36,8 @@ Yosys 0.9 .. Yosys 0.9-dev | ||||||
|     - Added "ice40_wrapcarry" to encapsulate SB_LUT+SB_CARRY pairs for techmapping |     - Added "ice40_wrapcarry" to encapsulate SB_LUT+SB_CARRY pairs for techmapping | ||||||
|     - Removed "ice40_unlut" |     - Removed "ice40_unlut" | ||||||
|     - Improvements in pmgen: slices, choices, define, generate |     - Improvements in pmgen: slices, choices, define, generate | ||||||
|  |     - Added "xilinx_srl" for Xilinx shift register extraction | ||||||
|  |     - Removed "shregmap -tech xilinx" (superseded by "xilinx_srl") | ||||||
| 
 | 
 | ||||||
| Yosys 0.8 .. Yosys 0.9 | Yosys 0.8 .. Yosys 0.9 | ||||||
| ---------------------- | ---------------------- | ||||||
|  |  | ||||||
|  | @ -30,3 +30,9 @@ PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg | ||||||
| 
 | 
 | ||||||
| passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) | passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) | ||||||
| 	$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^) | 	$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^) | ||||||
|  | 
 | ||||||
|  | # --------------------------------------
 | ||||||
|  | 
 | ||||||
|  | OBJS += passes/pmgen/xilinx_srl.o | ||||||
|  | passes/pmgen/xilinx_srl.o: passes/pmgen/xilinx_srl_pm.h | ||||||
|  | $(eval $(call add_extra_objs,passes/pmgen/xilinx_srl_pm.h)) | ||||||
|  |  | ||||||
|  | @ -28,6 +28,7 @@ bool did_something; | ||||||
| 
 | 
 | ||||||
| #include "passes/pmgen/test_pmgen_pm.h" | #include "passes/pmgen/test_pmgen_pm.h" | ||||||
| #include "passes/pmgen/ice40_dsp_pm.h" | #include "passes/pmgen/ice40_dsp_pm.h" | ||||||
|  | #include "passes/pmgen/xilinx_srl_pm.h" | ||||||
| #include "passes/pmgen/peepopt_pm.h" | #include "passes/pmgen/peepopt_pm.h" | ||||||
| 
 | 
 | ||||||
| void reduce_chain(test_pmgen_pm &pm) | void reduce_chain(test_pmgen_pm &pm) | ||||||
|  | @ -180,7 +181,7 @@ void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const | ||||||
| 		while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000) | 		while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000) | ||||||
| 		{ | 		{ | ||||||
| 			if (timeout++ > 10000) | 			if (timeout++ > 10000) | ||||||
| 				log_error("pmgen generator is stuck: 10000 iterations an no matching module generated.\n"); | 				log_error("pmgen generator is stuck: 10000 iterations with no matching module generated.\n"); | ||||||
| 
 | 
 | ||||||
| 			pm matcher(mod, mod->cells()); | 			pm matcher(mod, mod->cells()); | ||||||
| 
 | 
 | ||||||
|  | @ -216,7 +217,7 @@ void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const | ||||||
| 			run(matcher, [](){}); | 			run(matcher, [](){}); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (submodcnt) | 		if (submodcnt && maxsubcnt < (1 << 16)) | ||||||
| 			maxsubcnt *= 2; | 			maxsubcnt *= 2; | ||||||
| 
 | 
 | ||||||
| 		design->remove(mod); | 		design->remove(mod); | ||||||
|  | @ -349,13 +350,18 @@ struct TestPmgenPass : public Pass { | ||||||
| 		if (pattern == "ice40_dsp") | 		if (pattern == "ice40_dsp") | ||||||
| 			return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp); | 			return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp); | ||||||
| 
 | 
 | ||||||
|  | 		if (pattern == "xilinx_srl.fixed") | ||||||
|  | 			return GENERATE_PATTERN(xilinx_srl_pm, fixed); | ||||||
|  | 		if (pattern == "xilinx_srl.variable") | ||||||
|  | 			return GENERATE_PATTERN(xilinx_srl_pm, variable); | ||||||
|  | 
 | ||||||
| 		if (pattern == "peepopt-muldiv") | 		if (pattern == "peepopt-muldiv") | ||||||
| 			return GENERATE_PATTERN(peepopt_pm, muldiv); | 			return GENERATE_PATTERN(peepopt_pm, muldiv); | ||||||
| 
 | 
 | ||||||
| 		if (pattern == "peepopt-shiftmul") | 		if (pattern == "peepopt-shiftmul") | ||||||
| 			return GENERATE_PATTERN(peepopt_pm, shiftmul); | 			return GENERATE_PATTERN(peepopt_pm, shiftmul); | ||||||
| 
 | 
 | ||||||
| 		log_cmd_error("Unkown pattern: %s\n", pattern.c_str()); | 		log_cmd_error("Unknown pattern: %s\n", pattern.c_str()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
|  |  | ||||||
							
								
								
									
										262
									
								
								passes/pmgen/xilinx_srl.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								passes/pmgen/xilinx_srl.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,262 @@ | ||||||
|  | /*
 | ||||||
|  |  *  yosys -- Yosys Open SYnthesis Suite | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> | ||||||
|  |  *            (C) 2019  Eddie Hung    <eddie@fpgeh.com> | ||||||
|  |  * | ||||||
|  |  *  Permission to use, copy, modify, and/or distribute this software for any | ||||||
|  |  *  purpose with or without fee is hereby granted, provided that the above | ||||||
|  |  *  copyright notice and this permission notice appear in all copies. | ||||||
|  |  * | ||||||
|  |  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  |  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  |  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  |  *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  |  *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  |  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  |  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "kernel/yosys.h" | ||||||
|  | #include "kernel/sigtools.h" | ||||||
|  | 
 | ||||||
|  | USING_YOSYS_NAMESPACE | ||||||
|  | PRIVATE_NAMESPACE_BEGIN | ||||||
|  | 
 | ||||||
|  | // for peepopt_pm
 | ||||||
|  | bool did_something; | ||||||
|  | 
 | ||||||
|  | #include "passes/pmgen/xilinx_srl_pm.h" | ||||||
|  | #include "passes/pmgen/peepopt_pm.h" | ||||||
|  | 
 | ||||||
|  | void run_fixed(xilinx_srl_pm &pm) | ||||||
|  | { | ||||||
|  | 	auto &st = pm.st_fixed; | ||||||
|  | 	auto &ud = pm.ud_fixed; | ||||||
|  | 	log("Found fixed chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type)); | ||||||
|  | 
 | ||||||
|  | 	SigSpec initval; | ||||||
|  | 	for (auto cell : ud.longest_chain) { | ||||||
|  | 		log_debug("    %s\n", log_id(cell)); | ||||||
|  | 		if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) { | ||||||
|  | 			SigBit Q = cell->getPort(ID(Q)); | ||||||
|  | 			log_assert(Q.wire); | ||||||
|  | 			auto it = Q.wire->attributes.find(ID(init)); | ||||||
|  | 			if (it != Q.wire->attributes.end()) { | ||||||
|  | 				auto &i = it->second[Q.offset]; | ||||||
|  | 				initval.append(i); | ||||||
|  | 				i = State::Sx; | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 				initval.append(State::Sx); | ||||||
|  | 		} | ||||||
|  | 		else if (cell->type.in(ID(FDRE), ID(FDRE_1))) { | ||||||
|  | 			if (cell->parameters.at(ID(INIT), State::S0).as_bool()) | ||||||
|  | 				initval.append(State::S1); | ||||||
|  | 			else | ||||||
|  | 				initval.append(State::S0); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 			log_abort(); | ||||||
|  | 		pm.autoremove(cell); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	auto first_cell = ud.longest_chain.back(); | ||||||
|  | 	auto last_cell = ud.longest_chain.front(); | ||||||
|  | 	Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_)); | ||||||
|  | 	pm.module->swap_names(c, first_cell); | ||||||
|  | 
 | ||||||
|  | 	if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID(FDRE), ID(FDRE_1))) { | ||||||
|  | 		c->setParam(ID(DEPTH), GetSize(ud.longest_chain)); | ||||||
|  | 		c->setParam(ID(INIT), initval.as_const()); | ||||||
|  | 		if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_))) | ||||||
|  | 			c->setParam(ID(CLKPOL), 1); | ||||||
|  | 		else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_), ID(FDRE_1))) | ||||||
|  | 			c->setParam(ID(CLKPOL), 0); | ||||||
|  | 		else if (first_cell->type.in(ID(FDRE))) { | ||||||
|  | 			if (!first_cell->parameters.at(ID(IS_C_INVERTED), State::S0).as_bool()) | ||||||
|  | 				c->setParam(ID(CLKPOL), 1); | ||||||
|  | 			else | ||||||
|  | 				c->setParam(ID(CLKPOL), 0); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 			log_abort(); | ||||||
|  | 		if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_))) | ||||||
|  | 			c->setParam(ID(ENPOL), 1); | ||||||
|  | 		else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_))) | ||||||
|  | 			c->setParam(ID(ENPOL), 0); | ||||||
|  | 		else | ||||||
|  | 			c->setParam(ID(ENPOL), 2); | ||||||
|  | 
 | ||||||
|  | 		c->setPort(ID(C), first_cell->getPort(ID(C))); | ||||||
|  | 		c->setPort(ID(D), first_cell->getPort(ID(D))); | ||||||
|  | 		c->setPort(ID(Q), last_cell->getPort(ID(Q))); | ||||||
|  | 		c->setPort(ID(L), GetSize(ud.longest_chain)-1); | ||||||
|  | 		if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_))) | ||||||
|  | 			c->setPort(ID(E), State::S1); | ||||||
|  | 		else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) | ||||||
|  | 			c->setPort(ID(E), first_cell->getPort(ID(E))); | ||||||
|  | 		else if (first_cell->type.in(ID(FDRE), ID(FDRE_1))) | ||||||
|  | 			c->setPort(ID(E), first_cell->getPort(ID(CE))); | ||||||
|  | 		else | ||||||
|  | 			log_abort(); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		log_abort(); | ||||||
|  | 
 | ||||||
|  | 	log("    -> %s (%s)\n", log_id(c), log_id(c->type)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void run_variable(xilinx_srl_pm &pm) | ||||||
|  | { | ||||||
|  | 	auto &st = pm.st_variable; | ||||||
|  | 	auto &ud = pm.ud_variable; | ||||||
|  | 
 | ||||||
|  | 	log("Found variable chain of length %d (%s):\n", GetSize(ud.chain), log_id(st.first->type)); | ||||||
|  | 
 | ||||||
|  | 	SigSpec initval; | ||||||
|  | 	for (const auto &i : ud.chain) { | ||||||
|  | 		auto cell = i.first; | ||||||
|  | 		auto slice = i.second; | ||||||
|  | 		log_debug("    %s\n", log_id(cell)); | ||||||
|  | 		if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) { | ||||||
|  | 			SigBit Q = cell->getPort(ID(Q))[slice]; | ||||||
|  | 			log_assert(Q.wire); | ||||||
|  | 			auto it = Q.wire->attributes.find(ID(init)); | ||||||
|  | 			if (it != Q.wire->attributes.end()) { | ||||||
|  | 				auto &i = it->second[Q.offset]; | ||||||
|  | 				initval.append(i); | ||||||
|  | 				i = State::Sx; | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 				initval.append(State::Sx); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 			log_abort(); | ||||||
|  | 	} | ||||||
|  | 	pm.autoremove(st.shiftx); | ||||||
|  | 
 | ||||||
|  | 	auto first_cell = ud.chain.back().first; | ||||||
|  | 	auto first_slice = ud.chain.back().second; | ||||||
|  | 
 | ||||||
|  | 	Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_)); | ||||||
|  | 	pm.module->swap_names(c, first_cell); | ||||||
|  | 
 | ||||||
|  | 	if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) { | ||||||
|  | 		c->setParam(ID(DEPTH), GetSize(ud.chain)); | ||||||
|  | 		c->setParam(ID(INIT), initval.as_const()); | ||||||
|  | 		Const clkpol, enpol; | ||||||
|  | 		if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_))) | ||||||
|  | 			clkpol = 1; | ||||||
|  | 		else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_))) | ||||||
|  | 			clkpol = 0; | ||||||
|  | 		else if (first_cell->type.in(ID($dff), ID($dffe))) | ||||||
|  | 			clkpol = first_cell->getParam(ID(CLK_POLARITY)); | ||||||
|  | 		else | ||||||
|  | 			log_abort(); | ||||||
|  | 		if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_))) | ||||||
|  | 			enpol = 1; | ||||||
|  | 		else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_))) | ||||||
|  | 			enpol = 0; | ||||||
|  | 		else if (first_cell->type.in(ID($dffe))) | ||||||
|  | 			enpol = first_cell->getParam(ID(EN_POLARITY)); | ||||||
|  | 		else | ||||||
|  | 			enpol = 2; | ||||||
|  | 		c->setParam(ID(CLKPOL), clkpol); | ||||||
|  | 		c->setParam(ID(ENPOL), enpol); | ||||||
|  | 
 | ||||||
|  | 		if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) | ||||||
|  | 			c->setPort(ID(C), first_cell->getPort(ID(C))); | ||||||
|  | 		else if (first_cell->type.in(ID($dff), ID($dffe))) | ||||||
|  | 			c->setPort(ID(C), first_cell->getPort(ID(CLK))); | ||||||
|  | 		else | ||||||
|  | 			log_abort(); | ||||||
|  | 		c->setPort(ID(D), first_cell->getPort(ID(D))[first_slice]); | ||||||
|  | 		c->setPort(ID(Q), st.shiftx->getPort(ID(Y))); | ||||||
|  | 		c->setPort(ID(L), st.shiftx->getPort(ID(B))); | ||||||
|  | 		if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($dff))) | ||||||
|  | 			c->setPort(ID(E), State::S1); | ||||||
|  | 		else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) | ||||||
|  | 			c->setPort(ID(E), first_cell->getPort(ID(E))); | ||||||
|  | 		else if (first_cell->type.in(ID($dffe))) | ||||||
|  | 			c->setPort(ID(E), first_cell->getPort(ID(EN))); | ||||||
|  | 		else | ||||||
|  | 			log_abort(); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		log_abort(); | ||||||
|  | 
 | ||||||
|  | 	log("    -> %s (%s)\n", log_id(c), log_id(c->type)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct XilinxSrlPass : public Pass { | ||||||
|  | 	XilinxSrlPass() : Pass("xilinx_srl", "Xilinx shift register extraction") { } | ||||||
|  | 	void help() YS_OVERRIDE | ||||||
|  | 	{ | ||||||
|  | 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    xilinx_srl [options] [selection]\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("This pass converts chains of built-in flops (bit-level: $_DFF_[NP]_, $_DFFE_*\n"); | ||||||
|  | 		log("and word-level: $dff, $dffe) as well as Xilinx flops (FDRE, FDRE_1) into a\n"); | ||||||
|  | 		log("$__XILINX_SHREG cell. Chains must be of the same cell type, clock, clock polarity,\n"); | ||||||
|  | 		log("enable, and enable polarity (where relevant).\n"); | ||||||
|  | 		log("Flops with resets cannot be mapped to Xilinx devices and will not be inferred."); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    -minlen N\n"); | ||||||
|  | 		log("        min length of shift register (default = 3)\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    -fixed\n"); | ||||||
|  | 		log("        infer fixed-length shift registers.\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    -variable\n"); | ||||||
|  | 		log("        infer variable-length shift registers (i.e. fixed-length shifts where\n"); | ||||||
|  | 		log("        each element also fans-out to a $shiftx cell).\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
|  | 	{ | ||||||
|  | 		log_header(design, "Executing XILINX_SRL pass (Xilinx shift register extraction).\n"); | ||||||
|  | 
 | ||||||
|  | 		bool fixed = false; | ||||||
|  | 		bool variable = false; | ||||||
|  | 		int minlen = 3; | ||||||
|  | 
 | ||||||
|  | 		size_t argidx; | ||||||
|  | 		for (argidx = 1; argidx < args.size(); argidx++) | ||||||
|  | 		{ | ||||||
|  | 			if (args[argidx] == "-minlen" && argidx+1 < args.size()) { | ||||||
|  | 				minlen = atoi(args[++argidx].c_str()); | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			if (args[argidx] == "-fixed") { | ||||||
|  | 				fixed = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			if (args[argidx] == "-variable") { | ||||||
|  | 				variable = true; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		extra_args(args, argidx, design); | ||||||
|  | 
 | ||||||
|  | 		if (!fixed && !variable) | ||||||
|  | 			log_cmd_error("'-fixed' and/or '-variable' must be specified.\n"); | ||||||
|  | 
 | ||||||
|  | 		for (auto module : design->selected_modules()) { | ||||||
|  | 			auto pm = xilinx_srl_pm(module, module->selected_cells()); | ||||||
|  | 			pm.ud_fixed.minlen = minlen; | ||||||
|  | 			pm.ud_variable.minlen = minlen; | ||||||
|  | 
 | ||||||
|  | 			if (fixed) | ||||||
|  | 				pm.run_fixed(run_fixed); | ||||||
|  | 			if (variable) | ||||||
|  | 				pm.run_variable(run_variable); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } XilinxSrlPass; | ||||||
|  | 
 | ||||||
|  | PRIVATE_NAMESPACE_END | ||||||
							
								
								
									
										326
									
								
								passes/pmgen/xilinx_srl.pmg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										326
									
								
								passes/pmgen/xilinx_srl.pmg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,326 @@ | ||||||
|  | pattern fixed | ||||||
|  | 
 | ||||||
|  | state <IdString> clk_port en_port | ||||||
|  | udata <vector<Cell*>> chain longest_chain | ||||||
|  | udata <pool<Cell*>> non_first_cells | ||||||
|  | udata <int> minlen | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	non_first_cells.clear(); | ||||||
|  | 	subpattern(setup); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | match first | ||||||
|  | 	select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) | ||||||
|  | 	select !first->has_keep_attr() | ||||||
|  | 	select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool() | ||||||
|  | 	select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool() | ||||||
|  | 	select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero() | ||||||
|  | 	filter !non_first_cells.count(first) | ||||||
|  | generate | ||||||
|  | 	SigSpec C = module->addWire(NEW_ID); | ||||||
|  | 	SigSpec D = module->addWire(NEW_ID); | ||||||
|  | 	SigSpec Q = module->addWire(NEW_ID); | ||||||
|  | 	auto r = rng(8); | ||||||
|  | 	Cell* cell; | ||||||
|  | 	switch (r) | ||||||
|  | 	{ | ||||||
|  | 	case 0: | ||||||
|  | 	case 1: | ||||||
|  | 		cell = module->addCell(NEW_ID, \FDRE); | ||||||
|  | 		cell->setPort(\C, C); | ||||||
|  | 		cell->setPort(\D, D); | ||||||
|  | 		cell->setPort(\Q, Q); | ||||||
|  | 		cell->setPort(\CE, module->addWire(NEW_ID)); | ||||||
|  | 		if (r & 1) | ||||||
|  | 			cell->setPort(\R, module->addWire(NEW_ID)); | ||||||
|  | 		else { | ||||||
|  | 			if (rng(2) == 0) | ||||||
|  | 				cell->setPort(\R, State::S0); | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	case 2: | ||||||
|  | 	case 3: | ||||||
|  | 		cell = module->addDffGate(NEW_ID, C, D, Q, r & 1); | ||||||
|  | 		break; | ||||||
|  | 	case 4: | ||||||
|  | 	case 5: | ||||||
|  | 	case 6: | ||||||
|  | 	case 7: | ||||||
|  | 		cell = module->addDffeGate(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 2); | ||||||
|  | 		break; | ||||||
|  | 	default: log_abort(); | ||||||
|  | 	} | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code clk_port en_port | ||||||
|  | 	if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)) | ||||||
|  | 		clk_port = \C; | ||||||
|  | 	else log_abort(); | ||||||
|  | 	if (first->type.in($_DFF_N_, $_DFF_P_)) | ||||||
|  | 		en_port = IdString(); | ||||||
|  | 	else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_)) | ||||||
|  | 		en_port = \E; | ||||||
|  | 	else if (first->type.in(\FDRE, \FDRE_1)) | ||||||
|  | 		en_port = \CE; | ||||||
|  | 	else log_abort(); | ||||||
|  | 
 | ||||||
|  | 	longest_chain.clear(); | ||||||
|  | 	chain.push_back(first); | ||||||
|  | 	subpattern(tail); | ||||||
|  | finally | ||||||
|  | 	chain.pop_back(); | ||||||
|  | 	log_assert(chain.empty()); | ||||||
|  | 	if (GetSize(longest_chain) >= minlen) | ||||||
|  | 		accept; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // ------------------------------------------------------------------ | ||||||
|  | 
 | ||||||
|  | subpattern setup | ||||||
|  | arg clk_port | ||||||
|  | arg en_port | ||||||
|  | 
 | ||||||
|  | match first | ||||||
|  | 	select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) | ||||||
|  | 	select !first->has_keep_attr() | ||||||
|  | 	select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool() | ||||||
|  | 	select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool() | ||||||
|  | 	select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero() | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code clk_port en_port | ||||||
|  | 	if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)) | ||||||
|  | 		clk_port = \C; | ||||||
|  | 	else log_abort(); | ||||||
|  | 	if (first->type.in($_DFF_N_, $_DFF_P_)) | ||||||
|  | 		en_port = IdString(); | ||||||
|  | 	else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_)) | ||||||
|  | 		en_port = \E; | ||||||
|  | 	else if (first->type.in(\FDRE, \FDRE_1)) | ||||||
|  | 		en_port = \CE; | ||||||
|  | 	else log_abort(); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | match next | ||||||
|  | 	select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) | ||||||
|  | 	select !next->has_keep_attr() | ||||||
|  | 	select !port(next, \D)[0].wire->get_bool_attribute(\keep) | ||||||
|  | 	select nusers(port(next, \Q)) == 2 | ||||||
|  | 	index <IdString> next->type === first->type | ||||||
|  | 	index <SigBit> port(next, \Q) === port(first, \D) | ||||||
|  | 	filter port(next, clk_port) == port(first, clk_port) | ||||||
|  | 	filter en_port == IdString() || port(next, en_port) == port(first, en_port) | ||||||
|  | 	filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool() | ||||||
|  | 	filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool() | ||||||
|  | 	filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool() | ||||||
|  | 	filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero() | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	non_first_cells.insert(next); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // ------------------------------------------------------------------ | ||||||
|  | 
 | ||||||
|  | subpattern tail | ||||||
|  | arg first | ||||||
|  | arg clk_port | ||||||
|  | arg en_port | ||||||
|  | 
 | ||||||
|  | match next | ||||||
|  | 	semioptional | ||||||
|  | 	select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) | ||||||
|  | 	select !next->has_keep_attr() | ||||||
|  | 	select !port(next, \D)[0].wire->get_bool_attribute(\keep) | ||||||
|  | 	select nusers(port(next, \Q)) == 2 | ||||||
|  | 	index <IdString> next->type === chain.back()->type | ||||||
|  | 	index <SigBit> port(next, \Q) === port(chain.back(), \D) | ||||||
|  | 	filter port(next, clk_port) == port(first, clk_port) | ||||||
|  | 	filter en_port == IdString() || port(next, en_port) == port(first, en_port) | ||||||
|  | 	filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool() | ||||||
|  | 	filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool() | ||||||
|  | 	filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool() | ||||||
|  | 	filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero() | ||||||
|  | generate | ||||||
|  | 	Cell *cell = module->addCell(NEW_ID, chain.back()->type); | ||||||
|  | 	cell->setPort(\C, chain.back()->getPort(\C)); | ||||||
|  | 	cell->setPort(\D, module->addWire(NEW_ID)); | ||||||
|  | 	cell->setPort(\Q, chain.back()->getPort(\D)); | ||||||
|  | 	if (cell->type == \FDRE) { | ||||||
|  | 		if (rng(2) == 0) | ||||||
|  | 			cell->setPort(\R, chain.back()->connections_.at(\R, State::S0)); | ||||||
|  | 		cell->setPort(\CE, chain.back()->getPort(\CE)); | ||||||
|  | 	} | ||||||
|  | 	else if (cell->type.begins_with("$_DFFE_")) | ||||||
|  | 		cell->setPort(\E, chain.back()->getPort(\E)); | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	if (next) { | ||||||
|  | 		chain.push_back(next); | ||||||
|  | 		subpattern(tail); | ||||||
|  | 	} else { | ||||||
|  | 		if (GetSize(chain) > GetSize(longest_chain)) | ||||||
|  | 			longest_chain = chain; | ||||||
|  | 	} | ||||||
|  | finally | ||||||
|  | 	if (next) | ||||||
|  | 		chain.pop_back(); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // ----------- | ||||||
|  | 
 | ||||||
|  | pattern variable | ||||||
|  | 
 | ||||||
|  | state <IdString> clk_port en_port | ||||||
|  | state <int> shiftx_width | ||||||
|  | state <int> slice | ||||||
|  | udata <int> minlen | ||||||
|  | udata <vector<pair<Cell*,int>>> chain | ||||||
|  | udata <pool<SigBit>> chain_bits | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	chain_bits.clear(); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | match shiftx | ||||||
|  | 	select shiftx->type.in($shiftx) | ||||||
|  | 	select !shiftx->has_keep_attr() | ||||||
|  | 	select param(shiftx, \Y_WIDTH).as_int() == 1 | ||||||
|  | 	filter param(shiftx, \A_WIDTH).as_int() >= minlen | ||||||
|  | generate | ||||||
|  | 	minlen = 3; | ||||||
|  | 	module->addShiftx(NEW_ID, module->addWire(NEW_ID, rng(6)+minlen), module->addWire(NEW_ID, 3), module->addWire(NEW_ID)); | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code shiftx_width | ||||||
|  | 	shiftx_width = param(shiftx, \A_WIDTH).as_int(); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | match first | ||||||
|  | 	select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, $dff, $dffe) | ||||||
|  | 	select !first->has_keep_attr() | ||||||
|  | 	select !port(first, \Q)[0].wire->get_bool_attribute(\keep) | ||||||
|  | 	slice idx GetSize(port(first, \Q)) | ||||||
|  | 	select nusers(port(first, \Q)[idx]) <= 2 | ||||||
|  | 	index <SigBit> port(first, \Q)[idx] === port(shiftx, \A)[shiftx_width-1] | ||||||
|  | 	set slice idx | ||||||
|  | generate | ||||||
|  | 	SigSpec C = module->addWire(NEW_ID); | ||||||
|  | 	auto WIDTH = rng(3)+1; | ||||||
|  | 	SigSpec D = module->addWire(NEW_ID, WIDTH); | ||||||
|  | 	SigSpec Q = module->addWire(NEW_ID, WIDTH); | ||||||
|  | 	auto r = rng(8); | ||||||
|  | 	Cell *cell = nullptr; | ||||||
|  | 	switch (r) | ||||||
|  | 	{ | ||||||
|  | 	case 0: | ||||||
|  | 	case 1: | ||||||
|  | 		cell = module->addDff(NEW_ID, C, D, Q, r & 1); | ||||||
|  | 		break; | ||||||
|  | 	case 2: | ||||||
|  | 	case 3: | ||||||
|  | 	case 4: | ||||||
|  | 	case 5: | ||||||
|  | 		//cell = module->addDffe(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 4); | ||||||
|  | 		//break; | ||||||
|  | 	case 6: | ||||||
|  | 	case 7: | ||||||
|  | 		WIDTH = 1; | ||||||
|  | 		cell = module->addDffGate(NEW_ID, C, D[0], Q[0], r & 1); | ||||||
|  | 		break; | ||||||
|  | 	default: log_abort(); | ||||||
|  | 	} | ||||||
|  | 	shiftx->connections_.at(\A)[shiftx_width-1] = port(cell, \Q)[rng(WIDTH)]; | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code clk_port en_port | ||||||
|  | 	if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_)) | ||||||
|  | 		clk_port = \C; | ||||||
|  | 	else if (first->type.in($dff, $dffe)) | ||||||
|  | 		clk_port = \CLK; | ||||||
|  | 	else log_abort(); | ||||||
|  | 	if (first->type.in($_DFF_N_, $_DFF_P_, $dff)) | ||||||
|  | 		en_port = IdString(); | ||||||
|  | 	else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_)) | ||||||
|  | 		en_port = \E; | ||||||
|  | 	else if (first->type.in($dffe)) | ||||||
|  | 		en_port = \EN; | ||||||
|  | 	else log_abort(); | ||||||
|  | 
 | ||||||
|  | 	chain_bits.insert(port(first, \Q)[slice]); | ||||||
|  | 	chain.emplace_back(first, slice); | ||||||
|  | 	subpattern(tail); | ||||||
|  | finally | ||||||
|  | 	if (GetSize(chain) == shiftx_width) | ||||||
|  | 		accept; | ||||||
|  | 	chain.clear(); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // ------------------------------------------------------------------ | ||||||
|  | 
 | ||||||
|  | subpattern tail | ||||||
|  | arg first | ||||||
|  | arg shiftx | ||||||
|  | arg shiftx_width | ||||||
|  | arg slice | ||||||
|  | arg clk_port | ||||||
|  | arg en_port | ||||||
|  | 
 | ||||||
|  | match next | ||||||
|  | 	semioptional | ||||||
|  | 	select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, $dff, $dffe) | ||||||
|  | 	select !next->has_keep_attr() | ||||||
|  | 	select !port(next, \D)[0].wire->get_bool_attribute(\keep) | ||||||
|  | 	slice idx GetSize(port(next, \Q)) | ||||||
|  | 	select nusers(port(next, \Q)[idx]) <= 3 | ||||||
|  | 	index <IdString> next->type === chain.back().first->type | ||||||
|  | 	index <SigBit> port(next, \Q)[idx] === port(chain.back().first, \D)[chain.back().second] | ||||||
|  | 	index <SigBit> port(next, \Q)[idx] === port(shiftx, \A)[shiftx_width-1-GetSize(chain)] | ||||||
|  | 	filter port(next, clk_port) == port(first, clk_port) | ||||||
|  | 	filter en_port == IdString() || port(next, en_port) == port(first, en_port) | ||||||
|  | 	filter !next->type.in($dff, $dffe) || param(next, \CLK_POLARITY).as_bool() == param(first, \CLK_POLARITY).as_bool() | ||||||
|  | 	filter !next->type.in($dffe) || param(next, \EN_POLARITY).as_bool() == param(first, \EN_POLARITY).as_bool() | ||||||
|  | 	filter !chain_bits.count(port(next, \D)[idx]) | ||||||
|  | 	set slice idx | ||||||
|  | generate | ||||||
|  | 	if (GetSize(chain) < shiftx_width) { | ||||||
|  | 		auto back = chain.back().first; | ||||||
|  | 		auto slice = chain.back().second; | ||||||
|  | 		if (back->type.in($dff, $dffe)) { | ||||||
|  | 			auto WIDTH = GetSize(port(back, \D)); | ||||||
|  | 			if (rng(2) == 0 && slice < WIDTH-1) { | ||||||
|  | 				auto new_slice = slice + rng(WIDTH-1-slice); | ||||||
|  | 				back->connections_.at(\D)[slice] = port(back, \Q)[new_slice]; | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				auto D = module->addWire(NEW_ID, WIDTH); | ||||||
|  | 				if (back->type == $dff) | ||||||
|  | 					module->addDff(NEW_ID, port(back, \CLK), D, port(back, \D), param(back, \CLK_POLARITY).as_bool()); | ||||||
|  | 				else if (back->type == $dffe) | ||||||
|  | 					module->addDffe(NEW_ID, port(back, \CLK), port(back, \EN), D, port(back, \D), param(back, \CLK_POLARITY).as_bool(), param(back, \EN_POLARITY).as_bool()); | ||||||
|  | 				else | ||||||
|  | 					log_abort(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		else if (back->type.begins_with("$_DFF_")) { | ||||||
|  | 			Cell *cell = module->addCell(NEW_ID, back->type); | ||||||
|  | 			cell->setPort(\C, back->getPort(\C)); | ||||||
|  | 			cell->setPort(\D, module->addWire(NEW_ID)); | ||||||
|  | 			cell->setPort(\Q, back->getPort(\D)); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 			log_abort(); | ||||||
|  | 		shiftx->connections_.at(\A)[shiftx_width-1-GetSize(chain)] = port(back, \D)[slice]; | ||||||
|  | 	} | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	if (next) { | ||||||
|  | 		chain_bits.insert(port(next, \Q)[slice]); | ||||||
|  | 		chain.emplace_back(next, slice); | ||||||
|  | 		if (GetSize(chain) < shiftx_width) | ||||||
|  | 			subpattern(tail); | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | @ -26,9 +26,7 @@ PRIVATE_NAMESPACE_BEGIN | ||||||
| struct ShregmapTech | struct ShregmapTech | ||||||
| { | { | ||||||
| 	virtual ~ShregmapTech() { } | 	virtual ~ShregmapTech() { } | ||||||
| 	virtual void init(const Module * /*module*/, const SigMap &/*sigmap*/) {} | 	virtual bool analyze(vector<int> &taps) = 0; | ||||||
| 	virtual void non_chain_user(const SigBit &/*bit*/, const Cell* /*cell*/, IdString /*port*/) {} |  | ||||||
| 	virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0; |  | ||||||
| 	virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0; | 	virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -56,7 +54,7 @@ struct ShregmapOptions | ||||||
| 
 | 
 | ||||||
| struct ShregmapTechGreenpak4 : ShregmapTech | struct ShregmapTechGreenpak4 : ShregmapTech | ||||||
| { | { | ||||||
| 	bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/) | 	bool analyze(vector<int> &taps) | ||||||
| 	{ | 	{ | ||||||
| 		if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) { | 		if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) { | ||||||
| 			taps.clear(); | 			taps.clear(); | ||||||
|  | @ -93,155 +91,6 @@ struct ShregmapTechGreenpak4 : ShregmapTech | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct ShregmapTechXilinx7 : ShregmapTech |  | ||||||
| { |  | ||||||
| 	dict<SigBit, std::tuple<Cell*,int,int>> sigbit_to_shiftx_offset; |  | ||||||
| 	const ShregmapOptions &opts; |  | ||||||
| 
 |  | ||||||
| 	ShregmapTechXilinx7(const ShregmapOptions &opts) : opts(opts) {} |  | ||||||
| 
 |  | ||||||
| 	virtual void init(const Module* module, const SigMap &sigmap) override |  | ||||||
| 	{ |  | ||||||
| 		for (const auto &i : module->cells_) { |  | ||||||
| 			auto cell = i.second; |  | ||||||
| 			if (cell->type == ID($shiftx)) { |  | ||||||
| 				if (cell->getParam(ID(Y_WIDTH)) != 1) continue; |  | ||||||
| 				int j = 0; |  | ||||||
| 				for (auto bit : sigmap(cell->getPort(ID::A))) |  | ||||||
| 					sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0); |  | ||||||
| 				log_assert(j == cell->getParam(ID(A_WIDTH)).as_int()); |  | ||||||
| 			} |  | ||||||
| 			else if (cell->type == ID($mux)) { |  | ||||||
| 				int j = 0; |  | ||||||
| 				for (auto bit : sigmap(cell->getPort(ID::A))) |  | ||||||
| 					sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++); |  | ||||||
| 				j = 0; |  | ||||||
| 				for (auto bit : sigmap(cell->getPort(ID::B))) |  | ||||||
| 					sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 1, j++); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	virtual void non_chain_user(const SigBit &bit, const Cell *cell, IdString port) override |  | ||||||
| 	{ |  | ||||||
| 		auto it = sigbit_to_shiftx_offset.find(bit); |  | ||||||
| 		if (it == sigbit_to_shiftx_offset.end()) |  | ||||||
| 			return; |  | ||||||
| 		if (cell) { |  | ||||||
| 			if (cell->type == ID($shiftx) && port == ID::A) |  | ||||||
| 				return; |  | ||||||
| 			if (cell->type == ID($mux) && port.in(ID::A, ID::B)) |  | ||||||
| 				return; |  | ||||||
| 		} |  | ||||||
| 		sigbit_to_shiftx_offset.erase(it); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) override |  | ||||||
| 	{ |  | ||||||
| 		if (GetSize(taps) == 1) |  | ||||||
| 			return taps[0] >= opts.minlen-1 && sigbit_to_shiftx_offset.count(qbits[0]); |  | ||||||
| 
 |  | ||||||
| 		if (taps.back() < opts.minlen-1) |  | ||||||
| 			return false; |  | ||||||
| 
 |  | ||||||
| 		Cell *shiftx = nullptr; |  | ||||||
| 		int group = 0; |  | ||||||
| 		for (int i = 0; i < GetSize(taps); ++i) { |  | ||||||
| 			auto it = sigbit_to_shiftx_offset.find(qbits[i]); |  | ||||||
| 			if (it == sigbit_to_shiftx_offset.end()) |  | ||||||
| 				return false; |  | ||||||
| 
 |  | ||||||
| 			// Check taps are sequential
 |  | ||||||
| 			if (i != taps[i]) |  | ||||||
| 				return false; |  | ||||||
| 			// Check taps are not connected to a shift register,
 |  | ||||||
| 			// or sequential to the same shift register
 |  | ||||||
| 			if (i == 0) { |  | ||||||
| 				int offset; |  | ||||||
| 				std::tie(shiftx,offset,group) = it->second; |  | ||||||
| 				if (offset != i) |  | ||||||
| 					return false; |  | ||||||
| 			} |  | ||||||
| 			else { |  | ||||||
| 				Cell *shiftx_ = std::get<0>(it->second); |  | ||||||
| 				if (shiftx_ != shiftx) |  | ||||||
| 					return false; |  | ||||||
| 				int offset = std::get<1>(it->second); |  | ||||||
| 				if (offset != i) |  | ||||||
| 					return false; |  | ||||||
| 				int group_ = std::get<2>(it->second); |  | ||||||
| 				if (group_ != group) |  | ||||||
| 					return false; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		log_assert(shiftx); |  | ||||||
| 
 |  | ||||||
| 		// Only map if $shiftx exclusively covers the shift register
 |  | ||||||
| 		if (shiftx->type == ID($shiftx)) { |  | ||||||
| 			if (GetSize(taps) > shiftx->getParam(ID(A_WIDTH)).as_int()) |  | ||||||
| 				return false; |  | ||||||
| 			// Due to padding the most significant bits of A may be 1'bx,
 |  | ||||||
| 			//   and if so, discount them
 |  | ||||||
| 			if (GetSize(taps) < shiftx->getParam(ID(A_WIDTH)).as_int()) { |  | ||||||
| 				const SigSpec A = shiftx->getPort(ID::A); |  | ||||||
| 				const int A_width = shiftx->getParam(ID(A_WIDTH)).as_int(); |  | ||||||
| 				for (int i = GetSize(taps); i < A_width; ++i) |  | ||||||
| 					if (A[i] != RTLIL::Sx) return false; |  | ||||||
| 			} |  | ||||||
| 			else if (GetSize(taps) != shiftx->getParam(ID(A_WIDTH)).as_int()) |  | ||||||
| 				return false; |  | ||||||
| 		} |  | ||||||
| 		else if (shiftx->type == ID($mux)) { |  | ||||||
| 			if (GetSize(taps) != 2) |  | ||||||
| 				return false; |  | ||||||
| 		} |  | ||||||
| 		else log_abort(); |  | ||||||
| 
 |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) override |  | ||||||
| 	{ |  | ||||||
| 		const auto &tap = *taps.begin(); |  | ||||||
| 		auto bit = tap.second; |  | ||||||
| 
 |  | ||||||
| 		auto it = sigbit_to_shiftx_offset.find(bit); |  | ||||||
| 		log_assert(it != sigbit_to_shiftx_offset.end()); |  | ||||||
| 
 |  | ||||||
| 		auto newcell = cell->module->addCell(NEW_ID, ID($__XILINX_SHREG_)); |  | ||||||
| 		newcell->set_src_attribute(cell->get_src_attribute()); |  | ||||||
| 		newcell->setParam(ID(DEPTH), cell->getParam(ID(DEPTH))); |  | ||||||
| 		newcell->setParam(ID(INIT), cell->getParam(ID(INIT))); |  | ||||||
| 		newcell->setParam(ID(CLKPOL), cell->getParam(ID(CLKPOL))); |  | ||||||
| 		newcell->setParam(ID(ENPOL), cell->getParam(ID(ENPOL))); |  | ||||||
| 
 |  | ||||||
| 		newcell->setPort(ID(C), cell->getPort(ID(C))); |  | ||||||
| 		newcell->setPort(ID(D), cell->getPort(ID(D))); |  | ||||||
| 		if (cell->hasPort(ID(E))) |  | ||||||
| 			newcell->setPort(ID(E), cell->getPort(ID(E))); |  | ||||||
| 
 |  | ||||||
| 		Cell* shiftx = std::get<0>(it->second); |  | ||||||
| 		RTLIL::SigSpec l_wire, q_wire; |  | ||||||
| 		if (shiftx->type == ID($shiftx)) { |  | ||||||
| 			l_wire = shiftx->getPort(ID::B); |  | ||||||
| 			q_wire = shiftx->getPort(ID::Y); |  | ||||||
| 			shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID)); |  | ||||||
| 		} |  | ||||||
| 		else if (shiftx->type == ID($mux)) { |  | ||||||
| 			l_wire = shiftx->getPort(ID(S)); |  | ||||||
| 			q_wire = shiftx->getPort(ID::Y); |  | ||||||
| 			shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID)); |  | ||||||
| 		} |  | ||||||
| 		else log_abort(); |  | ||||||
| 
 |  | ||||||
| 		newcell->setPort(ID(Q), q_wire); |  | ||||||
| 		newcell->setPort(ID(L), l_wire); |  | ||||||
| 
 |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| struct ShregmapWorker | struct ShregmapWorker | ||||||
| { | { | ||||||
| 	Module *module; | 	Module *module; | ||||||
|  | @ -264,10 +113,8 @@ struct ShregmapWorker | ||||||
| 		for (auto wire : module->wires()) | 		for (auto wire : module->wires()) | ||||||
| 		{ | 		{ | ||||||
| 			if (wire->port_output || wire->get_bool_attribute(ID::keep)) { | 			if (wire->port_output || wire->get_bool_attribute(ID::keep)) { | ||||||
| 				for (auto bit : sigmap(wire)) { | 				for (auto bit : sigmap(wire)) | ||||||
| 					sigbit_with_non_chain_users.insert(bit); | 					sigbit_with_non_chain_users.insert(bit); | ||||||
| 					if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {}); |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			if (wire->attributes.count(ID(init))) { | 			if (wire->attributes.count(ID(init))) { | ||||||
|  | @ -317,10 +164,8 @@ struct ShregmapWorker | ||||||
| 
 | 
 | ||||||
| 			for (auto conn : cell->connections()) | 			for (auto conn : cell->connections()) | ||||||
| 				if (cell->input(conn.first)) | 				if (cell->input(conn.first)) | ||||||
| 					for (auto bit : sigmap(conn.second)) { | 					for (auto bit : sigmap(conn.second)) | ||||||
| 						sigbit_with_non_chain_users.insert(bit); | 						sigbit_with_non_chain_users.insert(bit); | ||||||
| 						if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first); |  | ||||||
| 					} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -425,7 +270,7 @@ struct ShregmapWorker | ||||||
| 					if (taps.empty() || taps.back() < depth-1) | 					if (taps.empty() || taps.back() < depth-1) | ||||||
| 						taps.push_back(depth-1); | 						taps.push_back(depth-1); | ||||||
| 
 | 
 | ||||||
| 					if (opts.tech->analyze(taps, qbits)) | 					if (opts.tech->analyze(taps)) | ||||||
| 						break; | 						break; | ||||||
| 
 | 
 | ||||||
| 					taps.pop_back(); | 					taps.pop_back(); | ||||||
|  | @ -544,9 +389,6 @@ struct ShregmapWorker | ||||||
| 	ShregmapWorker(Module *module, const ShregmapOptions &opts) : | 	ShregmapWorker(Module *module, const ShregmapOptions &opts) : | ||||||
| 			module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0) | 			module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0) | ||||||
| 	{ | 	{ | ||||||
| 		if (opts.tech) |  | ||||||
| 			opts.tech->init(module, sigmap); |  | ||||||
| 
 |  | ||||||
| 		make_sigbit_chain_next_prev(); | 		make_sigbit_chain_next_prev(); | ||||||
| 		find_chain_start_cells(); | 		find_chain_start_cells(); | ||||||
| 
 | 
 | ||||||
|  | @ -617,11 +459,6 @@ struct ShregmapPass : public Pass { | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    -tech greenpak4\n"); | 		log("    -tech greenpak4\n"); | ||||||
| 		log("        map to greenpak4 shift registers.\n"); | 		log("        map to greenpak4 shift registers.\n"); | ||||||
| 		log("        this option also implies -clkpol pos -zinit\n"); |  | ||||||
| 		log("\n"); |  | ||||||
| 		log("    -tech xilinx\n"); |  | ||||||
| 		log("        map to xilinx dynamic-length shift registers.\n"); |  | ||||||
| 		log("        this option also implies -params -init\n"); |  | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 	} | 	} | ||||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
|  | @ -676,12 +513,6 @@ struct ShregmapPass : public Pass { | ||||||
| 					clkpol = "pos"; | 					clkpol = "pos"; | ||||||
| 					opts.zinit = true; | 					opts.zinit = true; | ||||||
| 					opts.tech = new ShregmapTechGreenpak4; | 					opts.tech = new ShregmapTechGreenpak4; | ||||||
| 				} |  | ||||||
| 				else if (tech == "xilinx") { |  | ||||||
| 					opts.init = true; |  | ||||||
| 					opts.params = true; |  | ||||||
| 					enpol = "any_or_none"; |  | ||||||
| 					opts.tech = new ShregmapTechXilinx7(opts); |  | ||||||
| 				} else { | 				} else { | ||||||
| 					argidx--; | 					argidx--; | ||||||
| 					break; | 					break; | ||||||
|  |  | ||||||
|  | @ -303,9 +303,8 @@ struct SynthXilinxPass : public ScriptPass | ||||||
| 			if (widemux > 0 || help_mode) | 			if (widemux > 0 || help_mode) | ||||||
| 				run("muxpack", "    ('-widemux' only)"); | 				run("muxpack", "    ('-widemux' only)"); | ||||||
| 
 | 
 | ||||||
| 			// shregmap -tech xilinx can cope with $shiftx and $mux
 | 			// xilinx_srl looks for $shiftx cells for identifying variable-length
 | ||||||
| 			//   cells for identifying variable-length shift registers,
 | 			//   shift registers, so attempt to convert $pmux-es to this
 | ||||||
| 			//   so attempt to convert $pmux-es to the former
 |  | ||||||
| 			// Also: wide multiplexer inference benefits from this too
 | 			// Also: wide multiplexer inference benefits from this too
 | ||||||
| 			if (!(nosrl && widemux == 0) || help_mode) { | 			if (!(nosrl && widemux == 0) || help_mode) { | ||||||
| 				run("pmux2shiftx", "(skip if '-nosrl' and '-widemux=0')"); | 				run("pmux2shiftx", "(skip if '-nosrl' and '-widemux=0')"); | ||||||
|  | @ -387,13 +386,8 @@ struct SynthXilinxPass : public ScriptPass | ||||||
| 			} | 			} | ||||||
| 			run("opt -full"); | 			run("opt -full"); | ||||||
| 
 | 
 | ||||||
| 			if (!nosrl || help_mode) { | 			if (!nosrl || help_mode) | ||||||
| 				// shregmap operates on bit-level flops, not word-level,
 | 				run("xilinx_srl -variable -minlen 3", "(skip if '-nosrl')"); | ||||||
| 				//   so break those down here
 |  | ||||||
| 				run("simplemap t:$dff t:$dffe", "       (skip if '-nosrl')"); |  | ||||||
| 				// shregmap with '-tech xilinx' infers variable length shift regs
 |  | ||||||
| 				run("shregmap -tech xilinx -minlen 3", "(skip if '-nosrl')"); |  | ||||||
| 			} |  | ||||||
| 
 | 
 | ||||||
| 			std::string techmap_args = " -map +/techmap.v"; | 			std::string techmap_args = " -map +/techmap.v"; | ||||||
| 			if (help_mode) | 			if (help_mode) | ||||||
|  | @ -421,6 +415,14 @@ struct SynthXilinxPass : public ScriptPass | ||||||
| 			run("clean"); | 			run("clean"); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		if (check_label("map_ffs")) { | ||||||
|  | 				if (abc9 || help_mode) { | ||||||
|  | 						run("techmap -map +/xilinx/ff_map.v", "('-abc9' only)"); | ||||||
|  | 						run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT " | ||||||
|  | 										"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT", "('-abc9' only)"); | ||||||
|  | 				} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if (check_label("map_luts")) { | 		if (check_label("map_luts")) { | ||||||
| 			run("opt_expr -mux_undef"); | 			run("opt_expr -mux_undef"); | ||||||
| 			if (flatten_before_abc) | 			if (flatten_before_abc) | ||||||
|  | @ -448,13 +450,18 @@ struct SynthXilinxPass : public ScriptPass | ||||||
| 			// This shregmap call infers fixed length shift registers after abc
 | 			// This shregmap call infers fixed length shift registers after abc
 | ||||||
| 			//   has performed any necessary retiming
 | 			//   has performed any necessary retiming
 | ||||||
| 			if (!nosrl || help_mode) | 			if (!nosrl || help_mode) | ||||||
| 				run("shregmap -minlen 3 -init -params -enpol any_or_none", "(skip if '-nosrl')"); | 				run("xilinx_srl -fixed -minlen 3", "(skip if '-nosrl')"); | ||||||
| 			std::string techmap_args = "-map +/xilinx/lut_map.v -map +/xilinx/ff_map.v"; | 			std::string techmap_args = "-map +/xilinx/lut_map.v -map +/xilinx/cells_map.v"; | ||||||
| 			if (abc9) | 			if (help_mode) | ||||||
|  | 				techmap_args += " [-map +/xilinx/ff_map.v]"; | ||||||
|  | 			else if (abc9) | ||||||
| 				techmap_args += " -map +/xilinx/abc_unmap.v"; | 				techmap_args += " -map +/xilinx/abc_unmap.v"; | ||||||
|  | 			else | ||||||
|  | 				techmap_args += " -map +/xilinx/ff_map.v"; | ||||||
| 			run("techmap " + techmap_args); | 			run("techmap " + techmap_args); | ||||||
| 			run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT " | 			if (!abc9 || help_mode) | ||||||
| 					"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT"); | 				run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT " | ||||||
|  | 						"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT", "(without '-abc9' only)"); | ||||||
| 			run("clean"); | 			run("clean"); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -31,36 +31,3 @@ sat -verify -prove-asserts -show-ports -seq 5 miter | ||||||
| 
 | 
 | ||||||
| #design -load gate | #design -load gate | ||||||
| #stat | #stat | ||||||
| 
 |  | ||||||
| ########## |  | ||||||
| 
 |  | ||||||
| design -load read |  | ||||||
| design -copy-to model $__XILINX_SHREG_ |  | ||||||
| hierarchy -top shregmap_variable_test |  | ||||||
| prep |  | ||||||
| design -save gold |  | ||||||
| 
 |  | ||||||
| simplemap t:$dff t:$dffe |  | ||||||
| shregmap -tech xilinx |  | ||||||
| 
 |  | ||||||
| #stat |  | ||||||
| # show -width |  | ||||||
| # write_verilog -noexpr -norename |  | ||||||
| select -assert-count 1 t:$_DFF_P_ |  | ||||||
| select -assert-count 2 t:$__XILINX_SHREG_ |  | ||||||
| 
 |  | ||||||
| design -stash gate |  | ||||||
| 
 |  | ||||||
| design -import gold -as gold |  | ||||||
| design -import gate -as gate |  | ||||||
| design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_ |  | ||||||
| prep |  | ||||||
| 
 |  | ||||||
| miter -equiv -flatten -make_assert -make_outputs gold gate miter |  | ||||||
| sat -verify -prove-asserts -show-ports -seq 5 miter |  | ||||||
| 
 |  | ||||||
| # design -load gold |  | ||||||
| # stat |  | ||||||
| 
 |  | ||||||
| # design -load gate |  | ||||||
| # stat |  | ||||||
|  |  | ||||||
							
								
								
									
										3
									
								
								tests/xilinx/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								tests/xilinx/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | /*.log | ||||||
|  | /*.out | ||||||
|  | /run-test.mk | ||||||
							
								
								
									
										57
									
								
								tests/xilinx/pmgen_xilinx_srl.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								tests/xilinx/pmgen_xilinx_srl.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,57 @@ | ||||||
|  | read_verilog -icells <<EOT | ||||||
|  | module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, output SO); | ||||||
|  |   parameter DEPTH = 1; | ||||||
|  |   parameter [DEPTH-1:0] INIT = 0; | ||||||
|  |   parameter CLKPOL = 1; | ||||||
|  |   parameter ENPOL = 2; | ||||||
|  | 
 | ||||||
|  |   wire pos_clk = C == CLKPOL; | ||||||
|  |   reg pos_en; | ||||||
|  |   always @(E) | ||||||
|  |     if (ENPOL == 2) pos_en = 1'b1; | ||||||
|  |     else pos_en = (E == ENPOL[0]); | ||||||
|  | 
 | ||||||
|  |   reg [DEPTH-1:0] r; | ||||||
|  |   always @(posedge pos_clk) | ||||||
|  |     if (pos_en) | ||||||
|  |       r <= {r[DEPTH-2:0], D}; | ||||||
|  | 
 | ||||||
|  |   assign Q = r[L]; | ||||||
|  |   assign SO = r[DEPTH-1]; | ||||||
|  | endmodule | ||||||
|  | EOT | ||||||
|  | read_verilog +/xilinx/cells_sim.v | ||||||
|  | proc | ||||||
|  | design -save model | ||||||
|  | 
 | ||||||
|  | test_pmgen -generate xilinx_srl.fixed | ||||||
|  | hierarchy -top pmtest_xilinx_srl_pm_fixed | ||||||
|  | flatten; opt_clean | ||||||
|  | 
 | ||||||
|  | design -save gold | ||||||
|  | xilinx_srl -fixed | ||||||
|  | techmap -autoproc -map %model | ||||||
|  | design -stash gate | ||||||
|  | 
 | ||||||
|  | design -copy-from gold -as gold pmtest_xilinx_srl_pm_fixed | ||||||
|  | design -copy-from gate -as gate pmtest_xilinx_srl_pm_fixed | ||||||
|  | dff2dffe -unmap # sat does not support flops-with-enable yet | ||||||
|  | miter -equiv -flatten -make_assert gold gate miter | ||||||
|  | sat -set-init-zero -seq 5 -verify -prove-asserts miter | ||||||
|  | 
 | ||||||
|  | design -load model | ||||||
|  | 
 | ||||||
|  | test_pmgen -generate xilinx_srl.variable | ||||||
|  | hierarchy -top pmtest_xilinx_srl_pm_variable | ||||||
|  | flatten; opt_clean | ||||||
|  | 
 | ||||||
|  | design -save gold | ||||||
|  | xilinx_srl -variable | ||||||
|  | techmap -autoproc -map %model | ||||||
|  | design -stash gate | ||||||
|  | 
 | ||||||
|  | design -copy-from gold -as gold pmtest_xilinx_srl_pm_variable | ||||||
|  | design -copy-from gate -as gate pmtest_xilinx_srl_pm_variable | ||||||
|  | dff2dffe -unmap # sat does not support flops-with-enable yet | ||||||
|  | miter -equiv -flatten -make_assert gold gate miter | ||||||
|  | sat -set-init-zero -seq 5 -verify -prove-asserts miter | ||||||
							
								
								
									
										20
									
								
								tests/xilinx/run-test.sh
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										20
									
								
								tests/xilinx/run-test.sh
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,20 @@ | ||||||
|  | #!/usr/bin/env bash | ||||||
|  | set -e | ||||||
|  | { | ||||||
|  | echo "all::" | ||||||
|  | for x in *.ys; do | ||||||
|  | 	echo "all:: run-$x" | ||||||
|  | 	echo "run-$x:" | ||||||
|  | 	echo "	@echo 'Running $x..'" | ||||||
|  | 	echo "	@../../yosys -ql ${x%.ys}.log $x" | ||||||
|  | done | ||||||
|  | for s in *.sh; do | ||||||
|  | 	if [ "$s" != "run-test.sh" ]; then | ||||||
|  | 		echo "all:: run-$s" | ||||||
|  | 		echo "run-$s:" | ||||||
|  | 		echo "	@echo 'Running $s..'" | ||||||
|  | 		echo "	@bash $s" | ||||||
|  | 	fi | ||||||
|  | done | ||||||
|  | } > run-test.mk | ||||||
|  | exec ${MAKE:-make} -f run-test.mk | ||||||
							
								
								
									
										40
									
								
								tests/xilinx/xilinx_srl.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								tests/xilinx/xilinx_srl.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | ||||||
|  | module xilinx_srl_static_test(input i, clk, output [1:0] q); | ||||||
|  | reg head = 1'b0; | ||||||
|  | reg [3:0] shift1 = 4'b0000; | ||||||
|  | reg [3:0] shift2 = 4'b0000; | ||||||
|  | 
 | ||||||
|  | always @(posedge clk) begin | ||||||
|  |     head <= i; | ||||||
|  |     shift1 <= {shift1[2:0], head}; | ||||||
|  |     shift2 <= {shift2[2:0], head}; | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | assign q = {shift2[3], shift1[3]}; | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | module xilinx_srl_variable_test(input i, clk, input [1:0] l1, l2, output [1:0] q); | ||||||
|  | reg head = 1'b0; | ||||||
|  | reg [3:0] shift1 = 4'b0000; | ||||||
|  | reg [3:0] shift2 = 4'b0000; | ||||||
|  | 
 | ||||||
|  | always @(posedge clk) begin | ||||||
|  |     head <= i; | ||||||
|  |     shift1 <= {shift1[2:0], head}; | ||||||
|  |     shift2 <= {shift2[2:0], head}; | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | assign q = {shift2[l2], shift1[l1]}; | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | module $__XILINX_SHREG_(input C, D, E, input [1:0] L, output Q); | ||||||
|  | parameter CLKPOL = 1; | ||||||
|  | parameter ENPOL = 1; | ||||||
|  | parameter DEPTH = 1; | ||||||
|  | parameter [DEPTH-1:0] INIT = {DEPTH{1'b0}}; | ||||||
|  | reg [DEPTH-1:0] r = INIT; | ||||||
|  | wire clk = C ^ CLKPOL; | ||||||
|  | always @(posedge C) | ||||||
|  |     if (E)  | ||||||
|  |         r <= { r[DEPTH-2:0], D }; | ||||||
|  | assign Q = r[L]; | ||||||
|  | endmodule | ||||||
							
								
								
									
										67
									
								
								tests/xilinx/xilinx_srl.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								tests/xilinx/xilinx_srl.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | ||||||
|  | read_verilog xilinx_srl.v | ||||||
|  | design -save read | ||||||
|  | 
 | ||||||
|  | design -copy-to model $__XILINX_SHREG_ | ||||||
|  | hierarchy -top xilinx_srl_static_test | ||||||
|  | prep | ||||||
|  | design -save gold | ||||||
|  | 
 | ||||||
|  | techmap | ||||||
|  | xilinx_srl -fixed | ||||||
|  | opt | ||||||
|  | 
 | ||||||
|  | # stat | ||||||
|  | # show -width | ||||||
|  | select -assert-count 1 t:$_DFF_P_ | ||||||
|  | select -assert-count 2 t:$__XILINX_SHREG_ | ||||||
|  | 
 | ||||||
|  | design -stash gate | ||||||
|  | 
 | ||||||
|  | design -import gold -as gold | ||||||
|  | design -import gate -as gate | ||||||
|  | design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_ | ||||||
|  | prep | ||||||
|  | 
 | ||||||
|  | miter -equiv -flatten -make_assert -make_outputs gold gate miter | ||||||
|  | dump gate | ||||||
|  | sat -verify -prove-asserts -show-ports -seq 5 miter | ||||||
|  | 
 | ||||||
|  | #design -load gold | ||||||
|  | #stat | ||||||
|  | 
 | ||||||
|  | #design -load gate | ||||||
|  | #stat | ||||||
|  | 
 | ||||||
|  | ########## | ||||||
|  | 
 | ||||||
|  | design -load read | ||||||
|  | design -copy-to model $__XILINX_SHREG_ | ||||||
|  | hierarchy -top xilinx_srl_variable_test | ||||||
|  | prep | ||||||
|  | design -save gold | ||||||
|  | 
 | ||||||
|  | xilinx_srl -variable | ||||||
|  | opt | ||||||
|  | 
 | ||||||
|  | #stat | ||||||
|  | # show -width | ||||||
|  | # write_verilog -noexpr -norename | ||||||
|  | select -assert-count 1 t:$dff | ||||||
|  | select -assert-count 1 t:$dff r:WIDTH=1 %i | ||||||
|  | select -assert-count 2 t:$__XILINX_SHREG_ | ||||||
|  | 
 | ||||||
|  | design -stash gate | ||||||
|  | 
 | ||||||
|  | design -import gold -as gold | ||||||
|  | design -import gate -as gate | ||||||
|  | design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_ | ||||||
|  | prep | ||||||
|  | 
 | ||||||
|  | miter -equiv -flatten -make_assert -make_outputs gold gate miter | ||||||
|  | sat -verify -prove-asserts -show-ports -seq 5 miter | ||||||
|  | 
 | ||||||
|  | # design -load gold | ||||||
|  | # stat | ||||||
|  | 
 | ||||||
|  | # design -load gate | ||||||
|  | # stat | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue