mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into xaig_dff
This commit is contained in:
		
						commit
						405e974fe5
					
				
					 42 changed files with 1727 additions and 207 deletions
				
			
		|  | @ -81,8 +81,7 @@ struct XAigerWriter | ||||||
| 	pool<SigBit> input_bits, output_bits, external_bits; | 	pool<SigBit> input_bits, output_bits, external_bits; | ||||||
| 	dict<SigBit, SigBit> not_map, alias_map; | 	dict<SigBit, SigBit> not_map, alias_map; | ||||||
| 	dict<SigBit, pair<SigBit, SigBit>> and_map; | 	dict<SigBit, pair<SigBit, SigBit>> and_map; | ||||||
| 	vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits; | 	vector<SigBit> ci_bits, co_bits; | ||||||
| 	vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits; |  | ||||||
| 	dict<SigBit, std::pair<int,int>> ff_bits; | 	dict<SigBit, std::pair<int,int>> ff_bits; | ||||||
| 	dict<SigBit, float> arrival_times; | 	dict<SigBit, float> arrival_times; | ||||||
| 
 | 
 | ||||||
|  | @ -429,7 +428,6 @@ struct XAigerWriter | ||||||
| 							cell->setPort(port_name, rhs); | 							cell->setPort(port_name, rhs); | ||||||
| 						} | 						} | ||||||
| 
 | 
 | ||||||
| 						int offset = 0; |  | ||||||
| 						for (auto b : rhs.bits()) { | 						for (auto b : rhs.bits()) { | ||||||
| 							SigBit I = sigmap(b); | 							SigBit I = sigmap(b); | ||||||
| 							if (b == RTLIL::Sx) | 							if (b == RTLIL::Sx) | ||||||
|  | @ -440,7 +438,7 @@ struct XAigerWriter | ||||||
| 								else | 								else | ||||||
| 									alias_map[b] = I; | 									alias_map[b] = I; | ||||||
| 							} | 							} | ||||||
| 							co_bits.emplace_back(b, cell, port_name, offset++, 0); | 							co_bits.emplace_back(b); | ||||||
| 							unused_bits.erase(I); | 							unused_bits.erase(I); | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
|  | @ -460,9 +458,8 @@ struct XAigerWriter | ||||||
| 							cell->setPort(port_name, rhs); | 							cell->setPort(port_name, rhs); | ||||||
| 						} | 						} | ||||||
| 
 | 
 | ||||||
| 						int offset = 0; |  | ||||||
| 						for (const auto &b : rhs.bits()) { | 						for (const auto &b : rhs.bits()) { | ||||||
| 							ci_bits.emplace_back(b, cell, port_name, offset++); | 							ci_bits.emplace_back(b); | ||||||
| 							SigBit O = sigmap(b); | 							SigBit O = sigmap(b); | ||||||
| 							if (O != b) | 							if (O != b) | ||||||
| 								alias_map[O] = b; | 								alias_map[O] = b; | ||||||
|  | @ -478,7 +475,6 @@ struct XAigerWriter | ||||||
| 					if (rhs.empty()) | 					if (rhs.empty()) | ||||||
| 						log_error("'%s.$abc9_currQ' is not a wire present in module '%s'.\n", log_id(cell), log_id(module)); | 						log_error("'%s.$abc9_currQ' is not a wire present in module '%s'.\n", log_id(cell), log_id(module)); | ||||||
| 
 | 
 | ||||||
| 					int offset = 0; |  | ||||||
| 					for (auto b : rhs) { | 					for (auto b : rhs) { | ||||||
| 						SigBit I = sigmap(b); | 						SigBit I = sigmap(b); | ||||||
| 						if (b == RTLIL::Sx) | 						if (b == RTLIL::Sx) | ||||||
|  | @ -489,7 +485,7 @@ struct XAigerWriter | ||||||
| 							else | 							else | ||||||
| 								alias_map[b] = I; | 								alias_map[b] = I; | ||||||
| 						} | 						} | ||||||
| 						co_bits.emplace_back(b, cell, "\\$abc9_currQ", offset++, 0); | 						co_bits.emplace_back(b); | ||||||
| 						unused_bits.erase(I); | 						unused_bits.erase(I); | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  | @ -571,17 +567,15 @@ struct XAigerWriter | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		dict<SigBit, int> ff_aig_map; | 		dict<SigBit, int> ff_aig_map; | ||||||
| 		for (auto &c : ci_bits) { | 		for (auto &bit : ci_bits) { | ||||||
| 			RTLIL::SigBit bit = std::get<0>(c); |  | ||||||
| 			aig_m++, aig_i++; | 			aig_m++, aig_i++; | ||||||
| 			auto r = aig_map.insert(std::make_pair(bit, 2*aig_m)); | 			auto r = aig_map.insert(std::make_pair(bit, 2*aig_m)); | ||||||
| 			if (!r.second) | 			if (!r.second) | ||||||
| 				ff_aig_map[bit] = 2*aig_m; | 				ff_aig_map[bit] = 2*aig_m; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		for (auto &c : co_bits) { | 		for (auto bit : co_bits) { | ||||||
| 			RTLIL::SigBit bit = std::get<0>(c); | 			ordered_outputs[bit] = aig_o++; | ||||||
| 			std::get<4>(c) = ordered_outputs[bit] = aig_o++; |  | ||||||
| 			aig_outputs.push_back(bit2aig(bit)); | 			aig_outputs.push_back(bit2aig(bit)); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -707,27 +701,38 @@ struct XAigerWriter | ||||||
| 			int port_id = 1; | 			int port_id = 1; | ||||||
| 			int box_count = 0; | 			int box_count = 0; | ||||||
| 			for (auto cell : box_list) { | 			for (auto cell : box_list) { | ||||||
| 				RTLIL::Module* box_module = module->design->module(cell->type); | 				RTLIL::Module* orig_box_module = module->design->module(cell->type); | ||||||
| 				log_assert(box_module); | 				log_assert(orig_box_module); | ||||||
| 				IdString derived_name = box_module->derive(module->design, cell->parameters); | 				IdString derived_name = orig_box_module->derive(module->design, cell->parameters); | ||||||
| 				box_module = module->design->module(derived_name); | 				RTLIL::Module* box_module = module->design->module(derived_name); | ||||||
| 				if (box_module->has_processes()) | 				if (box_module->has_processes()) | ||||||
| 					Pass::call_on_module(module->design, box_module, "proc"); | 					log_error("ABC9 box '%s' contains processes!\n", box_module->name.c_str()); | ||||||
| 
 | 
 | ||||||
| 				int box_inputs = 0, box_outputs = 0; | 				int box_inputs = 0, box_outputs = 0; | ||||||
| 				auto r = cell_cache.insert(std::make_pair(derived_name, nullptr)); | 				auto r = cell_cache.insert(std::make_pair(derived_name, nullptr)); | ||||||
| 				Cell *holes_cell = r.first->second; | 				Cell *holes_cell = r.first->second; | ||||||
| 				if (r.second && !holes_cell && box_module->get_bool_attribute("\\whitebox")) { | 				if (r.second && box_module->get_bool_attribute("\\whitebox")) { | ||||||
| 					holes_cell = holes_module->addCell(cell->name, cell->type); | 					holes_cell = holes_module->addCell(cell->name, cell->type); | ||||||
| 					holes_cell->parameters = cell->parameters; | 					holes_cell->parameters = cell->parameters; | ||||||
| 					r.first->second = holes_cell; | 					r.first->second = holes_cell; | ||||||
|  | 
 | ||||||
|  | 					// Since Module::derive() will create a new module, there
 | ||||||
|  | 					//   is a chance that the ports will be alphabetically ordered
 | ||||||
|  | 					//   again, which is a problem when carry-chains are involved.
 | ||||||
|  | 					//   Inherit the port ordering from the original module here...
 | ||||||
|  | 					//   (and set the port_id below, when iterating through those)
 | ||||||
|  | 					log_assert(GetSize(box_module->ports) == GetSize(orig_box_module->ports)); | ||||||
|  | 					box_module->ports = orig_box_module->ports; | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				// NB: Assume box_module->ports are sorted alphabetically
 | 				// NB: Assume box_module->ports are sorted alphabetically
 | ||||||
| 				//     (as RTLIL::Module::fixup_ports() would do)
 | 				//     (as RTLIL::Module::fixup_ports() would do)
 | ||||||
|  | 				int box_port_id = 1; | ||||||
| 				for (const auto &port_name : box_module->ports) { | 				for (const auto &port_name : box_module->ports) { | ||||||
| 					RTLIL::Wire *w = box_module->wire(port_name); | 					RTLIL::Wire *w = box_module->wire(port_name); | ||||||
| 					log_assert(w); | 					log_assert(w); | ||||||
|  | 					if (r.second) | ||||||
|  | 						w->port_id = box_port_id++; | ||||||
| 					RTLIL::Wire *holes_wire; | 					RTLIL::Wire *holes_wire; | ||||||
| 					RTLIL::SigSpec port_sig; | 					RTLIL::SigSpec port_sig; | ||||||
| 					if (w->port_input) | 					if (w->port_input) | ||||||
|  | @ -829,8 +834,9 @@ struct XAigerWriter | ||||||
| 				//holes_module->fixup_ports();
 | 				//holes_module->fixup_ports();
 | ||||||
| 				holes_module->check(); | 				holes_module->check(); | ||||||
| 
 | 
 | ||||||
| 				// TODO: Should techmap/aigmap/check all lib_whitebox-es just once,
 | 				// Cannot techmap/aigmap/check all lib_whitebox-es outside of write_xaiger
 | ||||||
| 				//       instead of per write_xaiger call
 | 				//   since boxes may contain parameters in which case `flatten` would have
 | ||||||
|  | 				//   created a new $paramod ...
 | ||||||
| 				Pass::call_on_module(holes_module->design, holes_module, "flatten -wb; techmap; aigmap"); | 				Pass::call_on_module(holes_module->design, holes_module, "flatten -wb; techmap; aigmap"); | ||||||
| 
 | 
 | ||||||
| 				dict<SigSig, SigSig> replace; | 				dict<SigSig, SigSig> replace; | ||||||
|  |  | ||||||
|  | @ -22,8 +22,9 @@ $(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h)) | ||||||
| # --------------------------------------
 | # --------------------------------------
 | ||||||
| 
 | 
 | ||||||
| OBJS += passes/pmgen/xilinx_dsp.o | OBJS += passes/pmgen/xilinx_dsp.o | ||||||
| passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h | passes/pmgen/xilinx_dsp.o: passes/pmgen/xilinx_dsp_pm.h passes/pmgen/xilinx_dsp48a_pm.h passes/pmgen/xilinx_dsp_CREG_pm.h passes/pmgen/xilinx_dsp_cascade_pm.h | ||||||
| $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h)) | $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_pm.h)) | ||||||
|  | $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp48a_pm.h)) | ||||||
| $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_CREG_pm.h)) | $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_CREG_pm.h)) | ||||||
| $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h)) | $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h)) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ USING_YOSYS_NAMESPACE | ||||||
| PRIVATE_NAMESPACE_BEGIN | PRIVATE_NAMESPACE_BEGIN | ||||||
| 
 | 
 | ||||||
| #include "passes/pmgen/xilinx_dsp_pm.h" | #include "passes/pmgen/xilinx_dsp_pm.h" | ||||||
|  | #include "passes/pmgen/xilinx_dsp48a_pm.h" | ||||||
| #include "passes/pmgen/xilinx_dsp_CREG_pm.h" | #include "passes/pmgen/xilinx_dsp_CREG_pm.h" | ||||||
| #include "passes/pmgen/xilinx_dsp_cascade_pm.h" | #include "passes/pmgen/xilinx_dsp_cascade_pm.h" | ||||||
| 
 | 
 | ||||||
|  | @ -487,6 +488,190 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm) | ||||||
| 	pm.blacklist(cell); | 	pm.blacklist(cell); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm) | ||||||
|  | { | ||||||
|  | 	auto &st = pm.st_xilinx_dsp48a_pack; | ||||||
|  | 
 | ||||||
|  | 	log("Analysing %s.%s for Xilinx DSP48A/DSP48A1 packing.\n", log_id(pm.module), log_id(st.dsp)); | ||||||
|  | 
 | ||||||
|  | 	log_debug("preAdd:     %s\n", log_id(st.preAdd, "--")); | ||||||
|  | 	log_debug("ffA1:       %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--")); | ||||||
|  | 	log_debug("ffA0:       %s %s %s\n", log_id(st.ffA0, "--"), log_id(st.ffA0cemux, "--"), log_id(st.ffA0rstmux, "--")); | ||||||
|  | 	log_debug("ffB1:       %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--")); | ||||||
|  | 	log_debug("ffB0:       %s %s %s\n", log_id(st.ffB0, "--"), log_id(st.ffB0cemux, "--"), log_id(st.ffB0rstmux, "--")); | ||||||
|  | 	log_debug("ffD:        %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--")); | ||||||
|  | 	log_debug("dsp:        %s\n", log_id(st.dsp, "--")); | ||||||
|  | 	log_debug("ffM:        %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--")); | ||||||
|  | 	log_debug("postAdd:    %s\n", log_id(st.postAdd, "--")); | ||||||
|  | 	log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--")); | ||||||
|  | 	log_debug("ffP:        %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--")); | ||||||
|  | 
 | ||||||
|  | 	Cell *cell = st.dsp; | ||||||
|  | 	SigSpec &opmode = cell->connections_.at(ID(OPMODE)); | ||||||
|  | 
 | ||||||
|  | 	if (st.preAdd) { | ||||||
|  | 		log("  preadder %s (%s)\n", log_id(st.preAdd), log_id(st.preAdd->type)); | ||||||
|  | 		bool D_SIGNED = st.preAdd->getParam(ID(A_SIGNED)).as_bool(); | ||||||
|  | 		bool B_SIGNED = st.preAdd->getParam(ID(B_SIGNED)).as_bool(); | ||||||
|  | 		st.sigB.extend_u0(18, B_SIGNED); | ||||||
|  | 		st.sigD.extend_u0(18, D_SIGNED); | ||||||
|  | 		cell->setPort(ID(B), st.sigB); | ||||||
|  | 		cell->setPort(ID(D), st.sigD); | ||||||
|  | 		opmode[4] = State::S1; | ||||||
|  | 		if (st.preAdd->type == ID($add)) | ||||||
|  | 			opmode[6] = State::S0; | ||||||
|  | 		else if (st.preAdd->type == ID($sub)) | ||||||
|  | 			opmode[6] = State::S1; | ||||||
|  | 		else | ||||||
|  | 			log_assert(!"strange pre-adder type"); | ||||||
|  | 
 | ||||||
|  | 		pm.autoremove(st.preAdd); | ||||||
|  | 	} | ||||||
|  | 	if (st.postAdd) { | ||||||
|  | 		log("  postadder %s (%s)\n", log_id(st.postAdd), log_id(st.postAdd->type)); | ||||||
|  | 
 | ||||||
|  | 		if (st.postAddMux) { | ||||||
|  | 			log_assert(st.ffP); | ||||||
|  | 			opmode[2] = st.postAddMux->getPort(ID(S)); | ||||||
|  | 			pm.autoremove(st.postAddMux); | ||||||
|  | 		} | ||||||
|  | 		else if (st.ffP && st.sigC == st.sigP) | ||||||
|  | 			opmode[2] = State::S0; | ||||||
|  | 		else | ||||||
|  | 			opmode[2] = State::S1; | ||||||
|  | 		opmode[3] = State::S1; | ||||||
|  | 
 | ||||||
|  | 		if (opmode[2] != State::S0) { | ||||||
|  | 			if (st.postAddMuxAB == ID(A)) | ||||||
|  | 				st.sigC.extend_u0(48, st.postAdd->getParam(ID(B_SIGNED)).as_bool()); | ||||||
|  | 			else | ||||||
|  | 				st.sigC.extend_u0(48, st.postAdd->getParam(ID(A_SIGNED)).as_bool()); | ||||||
|  | 			cell->setPort(ID(C), st.sigC); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		pm.autoremove(st.postAdd); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (st.clock != SigBit()) | ||||||
|  | 	{ | ||||||
|  | 		cell->setPort(ID(CLK), st.clock); | ||||||
|  | 
 | ||||||
|  | 		auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) { | ||||||
|  | 			SigSpec D = ff->getPort(ID(D)); | ||||||
|  | 			SigSpec Q = pm.sigmap(ff->getPort(ID(Q))); | ||||||
|  | 			if (!A.empty()) | ||||||
|  | 				A.replace(Q, D); | ||||||
|  | 			if (rstmux) { | ||||||
|  | 				SigSpec Y = rstmux->getPort(ID(Y)); | ||||||
|  | 				SigSpec AB = rstmux->getPort(rstpol ? ID(A) : ID(B)); | ||||||
|  | 				if (!A.empty()) | ||||||
|  | 					A.replace(Y, AB); | ||||||
|  | 				if (rstport != IdString()) { | ||||||
|  | 					SigSpec S = rstmux->getPort(ID(S)); | ||||||
|  | 					cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S)); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			else if (rstport != IdString()) | ||||||
|  | 				cell->setPort(rstport, State::S0); | ||||||
|  | 			if (cemux) { | ||||||
|  | 				SigSpec Y = cemux->getPort(ID(Y)); | ||||||
|  | 				SigSpec BA = cemux->getPort(cepol ? ID(B) : ID(A)); | ||||||
|  | 				SigSpec S = cemux->getPort(ID(S)); | ||||||
|  | 				if (!A.empty()) | ||||||
|  | 					A.replace(Y, BA); | ||||||
|  | 				cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S)); | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 				cell->setPort(ceport, State::S1); | ||||||
|  | 
 | ||||||
|  | 			for (auto c : Q.chunks()) { | ||||||
|  | 				auto it = c.wire->attributes.find(ID(init)); | ||||||
|  | 				if (it == c.wire->attributes.end()) | ||||||
|  | 					continue; | ||||||
|  | 				for (int i = c.offset; i < c.offset+c.width; i++) { | ||||||
|  | 					log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx); | ||||||
|  | 					it->second[i] = State::Sx; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		if (st.ffA0 || st.ffA1) { | ||||||
|  | 			SigSpec A = cell->getPort(ID(A)); | ||||||
|  | 			if (st.ffA1) { | ||||||
|  | 				f(A, st.ffA1, st.ffA1cemux, st.ffAcepol, ID(CEA), st.ffA1rstmux, st.ffArstpol, ID(RSTA)); | ||||||
|  | 				cell->setParam(ID(A1REG), 1); | ||||||
|  | 			} | ||||||
|  | 			if (st.ffA0) { | ||||||
|  | 				f(A, st.ffA0, st.ffA0cemux, st.ffAcepol, ID(CEA), st.ffA0rstmux, st.ffArstpol, ID(RSTA)); | ||||||
|  | 				cell->setParam(ID(A0REG), 1); | ||||||
|  | 			} | ||||||
|  | 			pm.add_siguser(A, cell); | ||||||
|  | 			cell->setPort(ID(A), A); | ||||||
|  | 		} | ||||||
|  | 		if (st.ffB0 || st.ffB1) { | ||||||
|  | 			SigSpec B = cell->getPort(ID(B)); | ||||||
|  | 			if (st.ffB1) { | ||||||
|  | 				f(B, st.ffB1, st.ffB1cemux, st.ffBcepol, ID(CEB), st.ffB1rstmux, st.ffBrstpol, ID(RSTB)); | ||||||
|  | 				cell->setParam(ID(B1REG), 1); | ||||||
|  | 			} | ||||||
|  | 			if (st.ffB0) { | ||||||
|  | 				f(B, st.ffB0, st.ffB0cemux, st.ffBcepol, ID(CEB), st.ffB0rstmux, st.ffBrstpol, ID(RSTB)); | ||||||
|  | 				cell->setParam(ID(B0REG), 1); | ||||||
|  | 			} | ||||||
|  | 			pm.add_siguser(B, cell); | ||||||
|  | 			cell->setPort(ID(B), B); | ||||||
|  | 		} | ||||||
|  | 		if (st.ffD) { | ||||||
|  | 			SigSpec D = cell->getPort(ID(D)); | ||||||
|  | 			f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD)); | ||||||
|  | 			pm.add_siguser(D, cell); | ||||||
|  | 			cell->setPort(ID(D), D); | ||||||
|  | 			cell->setParam(ID(DREG), 1); | ||||||
|  | 		} | ||||||
|  | 		if (st.ffM) { | ||||||
|  | 			SigSpec M; // unused
 | ||||||
|  | 			f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM)); | ||||||
|  | 			st.ffM->connections_.at(ID(Q)).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM))); | ||||||
|  | 			cell->setParam(ID(MREG), State::S1); | ||||||
|  | 		} | ||||||
|  | 		if (st.ffP) { | ||||||
|  | 			SigSpec P; // unused
 | ||||||
|  | 			f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP)); | ||||||
|  | 			st.ffP->connections_.at(ID(Q)).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP))); | ||||||
|  | 			cell->setParam(ID(PREG), State::S1); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		log("  clock: %s (%s)", log_signal(st.clock), "posedge"); | ||||||
|  | 
 | ||||||
|  | 		if (st.ffA0) | ||||||
|  | 			log(" ffA0:%s", log_id(st.ffA0)); | ||||||
|  | 		if (st.ffA1) | ||||||
|  | 			log(" ffA1:%s", log_id(st.ffA1)); | ||||||
|  | 
 | ||||||
|  | 		if (st.ffB0) | ||||||
|  | 			log(" ffB0:%s", log_id(st.ffB0)); | ||||||
|  | 		if (st.ffB1) | ||||||
|  | 			log(" ffB1:%s", log_id(st.ffB1)); | ||||||
|  | 
 | ||||||
|  | 		if (st.ffD) | ||||||
|  | 			log(" ffD:%s", log_id(st.ffD)); | ||||||
|  | 
 | ||||||
|  | 		if (st.ffM) | ||||||
|  | 			log(" ffM:%s", log_id(st.ffM)); | ||||||
|  | 
 | ||||||
|  | 		if (st.ffP) | ||||||
|  | 			log(" ffP:%s", log_id(st.ffP)); | ||||||
|  | 	} | ||||||
|  | 	log("\n"); | ||||||
|  | 
 | ||||||
|  | 	SigSpec P = st.sigP; | ||||||
|  | 	if (GetSize(P) < 48) | ||||||
|  | 		P.append(pm.module->addWire(NEW_ID, 48-GetSize(P))); | ||||||
|  | 	cell->setPort(ID(P), P); | ||||||
|  | 
 | ||||||
|  | 	pm.blacklist(cell); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm) | void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm) | ||||||
| { | { | ||||||
| 	auto &st = pm.st_xilinx_dsp_packC; | 	auto &st = pm.st_xilinx_dsp_packC; | ||||||
|  | @ -592,33 +777,48 @@ struct XilinxDspPass : public Pass { | ||||||
| 		log("P output implementing the operation \"(P >= <power-of-2>)\" will be transformed\n"); | 		log("P output implementing the operation \"(P >= <power-of-2>)\" will be transformed\n"); | ||||||
| 		log("into using the DSP48E1's pattern detector feature for overflow detection.\n"); | 		log("into using the DSP48E1's pattern detector feature for overflow detection.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("    -family {xcup|xcu|xc7|xc6v|xc5v|xc4v|xc6s|xc3sda}\n"); | ||||||
|  | 		log("        select the family to target\n"); | ||||||
|  | 		log("        default: xc7\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 | ||||||
| 	{ | 	{ | ||||||
| 		log_header(design, "Executing XILINX_DSP pass (pack resources into DSPs).\n"); | 		log_header(design, "Executing XILINX_DSP pass (pack resources into DSPs).\n"); | ||||||
| 
 | 
 | ||||||
|  | 		std::string family = "xc7"; | ||||||
| 		size_t argidx; | 		size_t argidx; | ||||||
| 		for (argidx = 1; argidx < args.size(); argidx++) | 		for (argidx = 1; argidx < args.size(); argidx++) | ||||||
| 		{ | 		{ | ||||||
| 			// if (args[argidx] == "-singleton") {
 | 			if ((args[argidx] == "-family" || args[argidx] == "-arch") && argidx+1 < args.size()) { | ||||||
| 			// 	singleton_mode = true;
 | 				family = args[++argidx]; | ||||||
| 			// 	continue;
 | 				continue; | ||||||
| 			// }
 | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		extra_args(args, argidx, design); | 		extra_args(args, argidx, design); | ||||||
| 
 | 
 | ||||||
|  | 		// Don't bother distinguishing between those.
 | ||||||
|  | 		if (family == "xc6v") | ||||||
|  | 			family = "xc7"; | ||||||
|  | 		if (family == "xcup") | ||||||
|  | 			family = "xcu"; | ||||||
|  | 
 | ||||||
| 		for (auto module : design->selected_modules()) { | 		for (auto module : design->selected_modules()) { | ||||||
| 			// Experimental feature: pack $add/$sub cells with
 | 			// Experimental feature: pack $add/$sub cells with
 | ||||||
| 			//   (* use_dsp48="simd" *) into DSP48E1's using its
 | 			//   (* use_dsp48="simd" *) into DSP48E1's using its
 | ||||||
| 			//   SIMD feature
 | 			//   SIMD feature
 | ||||||
| 			xilinx_simd_pack(module, module->selected_cells()); | 			if (family == "xc7") | ||||||
|  | 				xilinx_simd_pack(module, module->selected_cells()); | ||||||
| 
 | 
 | ||||||
| 			// Match for all features ([ABDMP][12]?REG, pre-adder,
 | 			// Match for all features ([ABDMP][12]?REG, pre-adder,
 | ||||||
| 			// post-adder, pattern detector, etc.) except for CREG
 | 			// post-adder, pattern detector, etc.) except for CREG
 | ||||||
| 			{ | 			if (family == "xc7") { | ||||||
| 				xilinx_dsp_pm pm(module, module->selected_cells()); | 				xilinx_dsp_pm pm(module, module->selected_cells()); | ||||||
| 				pm.run_xilinx_dsp_pack(xilinx_dsp_pack); | 				pm.run_xilinx_dsp_pack(xilinx_dsp_pack); | ||||||
|  | 			} else if (family == "xc6s" || family == "xc3sda") { | ||||||
|  | 				xilinx_dsp48a_pm pm(module, module->selected_cells()); | ||||||
|  | 				pm.run_xilinx_dsp48a_pack(xilinx_dsp48a_pack); | ||||||
| 			} | 			} | ||||||
| 			// Separating out CREG packing is necessary since there
 | 			// Separating out CREG packing is necessary since there
 | ||||||
| 			//   is no guarantee that the cell ordering corresponds
 | 			//   is no guarantee that the cell ordering corresponds
 | ||||||
|  |  | ||||||
							
								
								
									
										673
									
								
								passes/pmgen/xilinx_dsp48a.pmg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										673
									
								
								passes/pmgen/xilinx_dsp48a.pmg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,673 @@ | ||||||
|  | // This file describes the main pattern matcher setup (of three total) that | ||||||
|  | //   forms the `xilinx_dsp` pass described in xilinx_dsp.cc - version for | ||||||
|  | //   DSP48A/DSP48A1 (Spartan 3A DSP, Spartan 6). | ||||||
|  | // At a high level, it works as follows: | ||||||
|  | //   ( 1) Starting from a DSP48A/DSP48A1 cell | ||||||
|  | //   ( 2) Match the driver of the 'B' input to a possible $dff cell (B1REG) | ||||||
|  | //        (attached to at most two $mux cells that implement clock-enable or | ||||||
|  | //         reset functionality, using a subpattern discussed below) | ||||||
|  | //        If B1REG matched, treat 'B' input as input of B1REG | ||||||
|  | //   ( 3) Match the driver of the 'B' and 'D' inputs for a possible $add cell | ||||||
|  | //       (pre-adder) | ||||||
|  | //   ( 4) Match 'B' input for B0REG | ||||||
|  | //   ( 5) Match 'A' input for A1REG | ||||||
|  | //        If A1REG, then match 'A' input for A0REG | ||||||
|  | //   ( 6) Match 'D' input for DREG | ||||||
|  | //   ( 7) Match 'P' output that exclusively drives an MREG | ||||||
|  | //   ( 8) Match 'P' output that exclusively drives one of two inputs to an $add | ||||||
|  | //        cell (post-adder). | ||||||
|  | //        The other input to the adder is assumed to come in from the 'C' input | ||||||
|  | //        (note: 'P' -> 'C' connections that exist for accumulators are | ||||||
|  | //         recognised in xilinx_dsp.cc). | ||||||
|  | //   ( 9) Match 'P' output that exclusively drives a PREG | ||||||
|  | //   (10) If post-adder and PREG both present, match for a $mux cell driving | ||||||
|  | //        the 'C' input, where one of the $mux's inputs is the PREG output. | ||||||
|  | //        This indicates an accumulator situation, and one where a $mux exists | ||||||
|  | //        to override the accumulated value: | ||||||
|  | //             +--------------------------------+ | ||||||
|  | //             |   ____                         | | ||||||
|  | //             +--|    \                        | | ||||||
|  | //                |$mux|-+                      | | ||||||
|  | //         'C' ---|____/ |                      | | ||||||
|  | //                       | /-------\   +----+   | | ||||||
|  | //            +----+     +-| post- |___|PREG|---+ 'P' | ||||||
|  | //            |MREG|------ | adder |   +----+ | ||||||
|  | //            +----+       \-------/ | ||||||
|  | // Notes: see the notes in xilinx_dsp.pmg | ||||||
|  | 
 | ||||||
|  | pattern xilinx_dsp48a_pack | ||||||
|  | 
 | ||||||
|  | state <SigBit> clock | ||||||
|  | state <SigSpec> sigA sigB sigC sigD sigM sigP | ||||||
|  | state <IdString> postAddAB postAddMuxAB | ||||||
|  | state <bool> ffAcepol ffBcepol ffDcepol ffMcepol ffPcepol | ||||||
|  | state <bool> ffArstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol | ||||||
|  | state <Cell*> ffA0 ffA0cemux ffA0rstmux ffA1 ffA1cemux ffA1rstmux | ||||||
|  | state <Cell*> ffB0 ffB0cemux ffB0rstmux ffB1 ffB1cemux ffB1rstmux | ||||||
|  | state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux | ||||||
|  | 
 | ||||||
|  | // Variables used for subpatterns | ||||||
|  | state <SigSpec> argQ argD | ||||||
|  | state <bool> ffcepol ffrstpol | ||||||
|  | state <int> ffoffset | ||||||
|  | udata <SigSpec> dffD dffQ | ||||||
|  | udata <SigBit> dffclock | ||||||
|  | udata <Cell*> dff dffcemux dffrstmux | ||||||
|  | udata <bool> dffcepol dffrstpol | ||||||
|  | 
 | ||||||
|  | // (1) Starting from a DSP48A/DSP48A1 cell | ||||||
|  | match dsp | ||||||
|  | 	select dsp->type.in(\DSP48A, \DSP48A1) | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code sigA sigB sigC sigD sigM clock | ||||||
|  | 	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); | ||||||
|  | 	}; | ||||||
|  | 	sigA = unextend(port(dsp, \A)); | ||||||
|  | 	sigB = unextend(port(dsp, \B)); | ||||||
|  | 
 | ||||||
|  | 	sigC = port(dsp, \C, SigSpec()); | ||||||
|  | 	sigD = port(dsp, \D, SigSpec()); | ||||||
|  | 
 | ||||||
|  | 	SigSpec P = port(dsp, \P); | ||||||
|  | 	// Only care about those bits that are used | ||||||
|  | 	int i; | ||||||
|  | 	for (i = GetSize(P)-1; i >= 0; i--) | ||||||
|  | 		if (nusers(P[i]) > 1) | ||||||
|  | 			break; | ||||||
|  | 	i++; | ||||||
|  | 	log_assert(nusers(P.extract_end(i)) <= 1); | ||||||
|  | 	// This sigM could have no users if downstream sinks (e.g. $add) is | ||||||
|  | 	//   narrower than $mul result, for example | ||||||
|  | 	if (i == 0) | ||||||
|  | 		reject; | ||||||
|  | 	sigM = P.extract(0, i); | ||||||
|  | 
 | ||||||
|  | 	clock = port(dsp, \CLK, SigBit()); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (2) Match the driver of the 'B' input to a possible $dff cell (B1REG) | ||||||
|  | //     (attached to at most two $mux cells that implement clock-enable or | ||||||
|  | //      reset functionality, using a subpattern discussed above) | ||||||
|  | //     If matched, treat 'B' input as input of B1REG | ||||||
|  | code argQ ffB1 ffB1cemux ffB1rstmux ffBcepol ffBrstpol sigB clock | ||||||
|  | 	if (param(dsp, \B1REG).as_int() == 0 && param(dsp, \B0REG).as_int() == 0 && port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()) { | ||||||
|  | 		argQ = sigB; | ||||||
|  | 		subpattern(in_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			ffB1 = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 			if (dffrstmux) { | ||||||
|  | 				ffB1rstmux = dffrstmux; | ||||||
|  | 				ffBrstpol = dffrstpol; | ||||||
|  | 			} | ||||||
|  | 			if (dffcemux) { | ||||||
|  | 				ffB1cemux = dffcemux; | ||||||
|  | 				ffBcepol = dffcepol; | ||||||
|  | 			} | ||||||
|  | 			sigB = dffD; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (3) Match the driver of the 'B' and 'D' inputs for a possible $add cell | ||||||
|  | //     (pre-adder) | ||||||
|  | match preAdd | ||||||
|  | 	if sigD.empty() || sigD.is_fully_zero() | ||||||
|  | 	if param(dsp, \B0REG).as_int() == 0 | ||||||
|  | 	// Ensure that preAdder not already used | ||||||
|  | 	if port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero() | ||||||
|  | 
 | ||||||
|  | 	select preAdd->type.in($add, $sub) | ||||||
|  | 	// Output has to be 18 bits or less | ||||||
|  | 	select GetSize(port(preAdd, \Y)) <= 18 | ||||||
|  | 	select nusers(port(preAdd, \Y)) == 2 | ||||||
|  | 	// D port has to be 18 bits or less | ||||||
|  | 	select GetSize(port(preAdd, \A)) <= 18 | ||||||
|  | 	// B port has to be 18 bits or less | ||||||
|  | 	select GetSize(port(preAdd, \B)) <= 18 | ||||||
|  | 	index <SigSpec> port(preAdd, \Y) === sigB | ||||||
|  | 
 | ||||||
|  | 	optional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code sigB sigD | ||||||
|  | 	if (preAdd) { | ||||||
|  | 		sigD = port(preAdd, \A); | ||||||
|  | 		sigB = port(preAdd, \B); | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (4) Match 'B' input for B0REG | ||||||
|  | code argQ ffB0 ffB0cemux ffB0rstmux ffBcepol ffBrstpol sigB clock | ||||||
|  | 	if (param(dsp, \B0REG).as_int() == 0) { | ||||||
|  | 		argQ = sigB; | ||||||
|  | 		subpattern(in_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			if (ffB1) { | ||||||
|  | 				if ((ffB1rstmux != nullptr) ^ (dffrstmux != nullptr)) | ||||||
|  | 					goto ffB0_end; | ||||||
|  | 				if ((ffB1cemux != nullptr) ^ (dffcemux != nullptr)) | ||||||
|  | 					goto ffB0_end; | ||||||
|  | 				if (dffrstmux) { | ||||||
|  | 					if (ffBrstpol != dffrstpol) | ||||||
|  | 						goto ffB0_end; | ||||||
|  | 					if (port(ffB1rstmux, \S) != port(dffrstmux, \S)) | ||||||
|  | 						goto ffB0_end; | ||||||
|  | 					ffB0rstmux = dffrstmux; | ||||||
|  | 				} | ||||||
|  | 				if (dffcemux) { | ||||||
|  | 					if (ffBcepol != dffcepol) | ||||||
|  | 						goto ffB0_end; | ||||||
|  | 					if (port(ffB1cemux, \S) != port(dffcemux, \S)) | ||||||
|  | 						goto ffB0_end; | ||||||
|  | 					ffB0cemux = dffcemux; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			ffB0 = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 			if (dffrstmux) { | ||||||
|  | 				ffB0rstmux = dffrstmux; | ||||||
|  | 				ffBrstpol = dffrstpol; | ||||||
|  | 			} | ||||||
|  | 			if (dffcemux) { | ||||||
|  | 				ffB0cemux = dffcemux; | ||||||
|  | 				ffBcepol = dffcepol; | ||||||
|  | 			} | ||||||
|  | 			sigB = dffD; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | ffB0_end: | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (5) Match 'A' input for A1REG | ||||||
|  | //     If A1REG, then match 'A' input for A0REG | ||||||
|  | code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux ffA0rstmux | ||||||
|  | 	if (param(dsp, \A0REG).as_int() == 0 && param(dsp, \A1REG).as_int() == 0) { | ||||||
|  | 		argQ = sigA; | ||||||
|  | 		subpattern(in_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			ffA1 = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 			if (dffrstmux) { | ||||||
|  | 				ffA1rstmux = dffrstmux; | ||||||
|  | 				ffArstpol = dffrstpol; | ||||||
|  | 			} | ||||||
|  | 			if (dffcemux) { | ||||||
|  | 				ffA1cemux = dffcemux; | ||||||
|  | 				ffAcepol = dffcepol; | ||||||
|  | 			} | ||||||
|  | 			sigA = dffD; | ||||||
|  | 
 | ||||||
|  | 			// Now attempt to match A0 | ||||||
|  | 			if (ffA1) { | ||||||
|  | 				argQ = sigA; | ||||||
|  | 				subpattern(in_dffe); | ||||||
|  | 				if (dff) { | ||||||
|  | 					if ((ffA1rstmux != nullptr) ^ (dffrstmux != nullptr)) | ||||||
|  | 						goto ffA0_end; | ||||||
|  | 					if ((ffA1cemux != nullptr) ^ (dffcemux != nullptr)) | ||||||
|  | 						goto ffA0_end; | ||||||
|  | 					if (dffrstmux) { | ||||||
|  | 						if (ffArstpol != dffrstpol) | ||||||
|  | 							goto ffA0_end; | ||||||
|  | 						if (port(ffA1rstmux, \S) != port(dffrstmux, \S)) | ||||||
|  | 							goto ffA0_end; | ||||||
|  | 						ffA0rstmux = dffrstmux; | ||||||
|  | 					} | ||||||
|  | 					if (dffcemux) { | ||||||
|  | 						if (ffAcepol != dffcepol) | ||||||
|  | 							goto ffA0_end; | ||||||
|  | 						if (port(ffA1cemux, \S) != port(dffcemux, \S)) | ||||||
|  | 							goto ffA0_end; | ||||||
|  | 						ffA0cemux = dffcemux; | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					ffA0 = dff; | ||||||
|  | 					clock = dffclock; | ||||||
|  | 
 | ||||||
|  | 					if (dffcemux) { | ||||||
|  | 						ffA0cemux = dffcemux; | ||||||
|  | 						ffAcepol = dffcepol; | ||||||
|  | 					} | ||||||
|  | 					sigA = dffD; | ||||||
|  | 
 | ||||||
|  | ffA0_end:				; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (6) Match 'D' input for DREG | ||||||
|  | code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock | ||||||
|  | 	if (param(dsp, \DREG).as_int() == 0) { | ||||||
|  | 		argQ = sigD; | ||||||
|  | 		subpattern(in_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			ffD = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 			if (dffrstmux) { | ||||||
|  | 				ffDrstmux = dffrstmux; | ||||||
|  | 				ffDrstpol = dffrstpol; | ||||||
|  | 			} | ||||||
|  | 			if (dffcemux) { | ||||||
|  | 				ffDcemux = dffcemux; | ||||||
|  | 				ffDcepol = dffcepol; | ||||||
|  | 			} | ||||||
|  | 			sigD = dffD; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (7) Match 'P' output that exclusively drives an MREG | ||||||
|  | code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock | ||||||
|  | 	if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) { | ||||||
|  | 		argD = sigM; | ||||||
|  | 		subpattern(out_dffe); | ||||||
|  | 		if (dff) { | ||||||
|  | 			ffM = dff; | ||||||
|  | 			clock = dffclock; | ||||||
|  | 			if (dffrstmux) { | ||||||
|  | 				ffMrstmux = dffrstmux; | ||||||
|  | 				ffMrstpol = dffrstpol; | ||||||
|  | 			} | ||||||
|  | 			if (dffcemux) { | ||||||
|  | 				ffMcemux = dffcemux; | ||||||
|  | 				ffMcepol = dffcepol; | ||||||
|  | 			} | ||||||
|  | 			sigM = dffQ; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	sigP = sigM; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (8) Match 'P' output that exclusively drives one of two inputs to an $add | ||||||
|  | //     cell (post-adder). | ||||||
|  | //     The other input to the adder is assumed to come in from the 'C' input | ||||||
|  | //     (note: 'P' -> 'C' connections that exist for accumulators are | ||||||
|  | //      recognised in xilinx_dsp.cc). | ||||||
|  | match postAdd | ||||||
|  | 	// Ensure that Z mux is not already used | ||||||
|  | 	if port(dsp, \OPMODE, SigSpec()).extract(2,2).is_fully_zero() | ||||||
|  | 
 | ||||||
|  | 	select postAdd->type.in($add) | ||||||
|  | 	select GetSize(port(postAdd, \Y)) <= 48 | ||||||
|  | 	choice <IdString> AB {\A, \B} | ||||||
|  | 	select nusers(port(postAdd, AB)) <= 3 | ||||||
|  | 	filter ffMcemux || nusers(port(postAdd, AB)) == 2 | ||||||
|  | 	filter !ffMcemux || nusers(port(postAdd, AB)) == 3 | ||||||
|  | 
 | ||||||
|  | 	index <SigBit> port(postAdd, AB)[0] === sigP[0] | ||||||
|  | 	filter GetSize(port(postAdd, AB)) >= GetSize(sigP) | ||||||
|  | 	filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP | ||||||
|  | 	// Check that remainder of AB is a sign- or zero-extension | ||||||
|  | 	filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP)) | ||||||
|  | 
 | ||||||
|  | 	set postAddAB AB | ||||||
|  | 	optional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code sigC sigP | ||||||
|  | 	if (postAdd) { | ||||||
|  | 		sigC = port(postAdd, postAddAB == \A ? \B : \A); | ||||||
|  | 		sigP = port(postAdd, \Y); | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (9) Match 'P' output that exclusively drives a PREG | ||||||
|  | code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock | ||||||
|  | 	if (param(dsp, \PREG).as_int() == 0) { | ||||||
|  | 		int users = 2; | ||||||
|  | 		// If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux | ||||||
|  | 		if (ffMcemux && !postAdd) users++; | ||||||
|  | 		if (nusers(sigP) == users) { | ||||||
|  | 			argD = sigP; | ||||||
|  | 			subpattern(out_dffe); | ||||||
|  | 			if (dff) { | ||||||
|  | 				ffP = dff; | ||||||
|  | 				clock = dffclock; | ||||||
|  | 				if (dffrstmux) { | ||||||
|  | 					ffPrstmux = dffrstmux; | ||||||
|  | 					ffPrstpol = dffrstpol; | ||||||
|  | 				} | ||||||
|  | 				if (dffcemux) { | ||||||
|  | 					ffPcemux = dffcemux; | ||||||
|  | 					ffPcepol = dffcepol; | ||||||
|  | 				} | ||||||
|  | 				sigP = dffQ; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (10) If post-adder and PREG both present, match for a $mux cell driving | ||||||
|  | //      the 'C' input, where one of the $mux's inputs is the PREG output. | ||||||
|  | //      This indicates an accumulator situation, and one where a $mux exists | ||||||
|  | //      to override the accumulated value: | ||||||
|  | //           +--------------------------------+ | ||||||
|  | //           |   ____                         | | ||||||
|  | //           +--|    \                        | | ||||||
|  | //              |$mux|-+                      | | ||||||
|  | //       'C' ---|____/ |                      | | ||||||
|  | //                     | /-------\   +----+   | | ||||||
|  | //          +----+     +-| post- |___|PREG|---+ 'P' | ||||||
|  | //          |MREG|------ | adder |   +----+ | ||||||
|  | //          +----+       \-------/ | ||||||
|  | match postAddMux | ||||||
|  | 	if postAdd | ||||||
|  | 	if ffP | ||||||
|  | 	select postAddMux->type.in($mux) | ||||||
|  | 	select nusers(port(postAddMux, \Y)) == 2 | ||||||
|  | 	choice <IdString> AB {\A, \B} | ||||||
|  | 	index <SigSpec> port(postAddMux, AB) === sigP | ||||||
|  | 	index <SigSpec> port(postAddMux, \Y) === sigC | ||||||
|  | 	set postAddMuxAB AB | ||||||
|  | 	optional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code sigC | ||||||
|  | 	if (postAddMux) | ||||||
|  | 		sigC = port(postAddMux, postAddMuxAB == \A ? \B : \A); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	accept; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // ####################### | ||||||
|  | 
 | ||||||
|  | // Subpattern for matching against input registers, based on knowledge of the | ||||||
|  | //   'Q' input. Typically, identifying registers with clock-enable and reset | ||||||
|  | //   capability would be a task would be handled by other Yosys passes such as | ||||||
|  | //   dff2dffe, but since DSP inference happens much before this, these patterns | ||||||
|  | //   have to be manually identified. | ||||||
|  | // At a high level: | ||||||
|  | //   (1) Starting from a $dff cell that (partially or fully) drives the given | ||||||
|  | //       'Q' argument | ||||||
|  | //   (2) Match for a $mux cell implementing synchronous reset semantics --- | ||||||
|  | //       one that exclusively drives the 'D' input of the $dff, with one of its | ||||||
|  | //       $mux inputs being fully zero | ||||||
|  | //   (3) Match for a $mux cell implement clock enable semantics --- one that | ||||||
|  | //       exclusively drives the 'D' input of the $dff (or the other input of | ||||||
|  | //       the reset $mux) and where one of this $mux's inputs is connected to | ||||||
|  | //       the 'Q' output of the $dff | ||||||
|  | subpattern in_dffe | ||||||
|  | arg argD argQ clock | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	dff = nullptr; | ||||||
|  | 	if (GetSize(argQ) == 0) | ||||||
|  | 		reject; | ||||||
|  | 	for (const auto &c : argQ.chunks()) { | ||||||
|  | 		// Abandon matches when 'Q' is a constant | ||||||
|  | 		if (!c.wire) | ||||||
|  | 			reject; | ||||||
|  | 		// Abandon matches when 'Q' has the keep attribute set | ||||||
|  | 		if (c.wire->get_bool_attribute(\keep)) | ||||||
|  | 			reject; | ||||||
|  | 		// Abandon matches when 'Q' has a non-zero init attribute set | ||||||
|  | 		// (not supported by DSP48E1) | ||||||
|  | 		Const init = c.wire->attributes.at(\init, Const()); | ||||||
|  | 		if (!init.empty()) | ||||||
|  | 			for (auto b : init.extract(c.offset, c.width)) | ||||||
|  | 				if (b != State::Sx && b != State::S0) | ||||||
|  | 					reject; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (1) Starting from a $dff cell that (partially or fully) drives the given | ||||||
|  | //     'Q' argument | ||||||
|  | match ff | ||||||
|  | 	select ff->type.in($dff) | ||||||
|  | 	// DSP48E1 does not support clock inversion | ||||||
|  | 	select param(ff, \CLK_POLARITY).as_bool() | ||||||
|  | 
 | ||||||
|  | 	slice offset GetSize(port(ff, \D)) | ||||||
|  | 	index <SigBit> port(ff, \Q)[offset] === argQ[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that the rest of argQ is present | ||||||
|  | 	filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ) | ||||||
|  | 	filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ | ||||||
|  | 
 | ||||||
|  | 	filter clock == SigBit() || port(ff, \CLK) == clock | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argQ argD | ||||||
|  | 	SigSpec Q = port(ff, \Q); | ||||||
|  | 	dff = ff; | ||||||
|  | 	dffclock = port(ff, \CLK); | ||||||
|  | 	dffD = argQ; | ||||||
|  | 	argD = port(ff, \D); | ||||||
|  | 	argQ = Q; | ||||||
|  | 	dffD.replace(argQ, argD); | ||||||
|  | 	// Only search for ffrstmux if dffD only | ||||||
|  | 	//   has two (ff, ffrstmux) users | ||||||
|  | 	if (nusers(dffD) > 2) | ||||||
|  | 		argD = SigSpec(); | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (2) Match for a $mux cell implementing synchronous reset semantics --- | ||||||
|  | //     exclusively drives the 'D' input of the $dff, with one of the $mux | ||||||
|  | //     inputs being fully zero | ||||||
|  | match ffrstmux | ||||||
|  | 	if !argD.empty() | ||||||
|  | 	select ffrstmux->type.in($mux) | ||||||
|  | 	index <SigSpec> port(ffrstmux, \Y) === argD | ||||||
|  | 
 | ||||||
|  | 	choice <IdString> BA {\B, \A} | ||||||
|  | 	// DSP48E1 only supports reset to zero | ||||||
|  | 	select port(ffrstmux, BA).is_fully_zero() | ||||||
|  | 
 | ||||||
|  | 	define <bool> pol (BA == \B) | ||||||
|  | 	set ffrstpol pol | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD | ||||||
|  | 	if (ffrstmux) { | ||||||
|  | 		dffrstmux = ffrstmux; | ||||||
|  | 		dffrstpol = ffrstpol; | ||||||
|  | 		argD = port(ffrstmux, ffrstpol ? \A : \B); | ||||||
|  | 		dffD.replace(port(ffrstmux, \Y), argD); | ||||||
|  | 
 | ||||||
|  | 		// Only search for ffcemux if argQ has at | ||||||
|  | 		//   least 3 users (ff, <upstream>, ffrstmux) and | ||||||
|  | 		//   dffD only has two (ff, ffrstmux) | ||||||
|  | 		if (!(nusers(argQ) >= 3 && nusers(dffD) == 2)) | ||||||
|  | 			argD = SigSpec(); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		dffrstmux = nullptr; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (3) Match for a $mux cell implement clock enable semantics --- one that | ||||||
|  | //     exclusively drives the 'D' input of the $dff (or the other input of | ||||||
|  | //     the reset $mux) and where one of this $mux's inputs is connected to | ||||||
|  | //     the 'Q' output of the $dff | ||||||
|  | match ffcemux | ||||||
|  | 	if !argD.empty() | ||||||
|  | 	select ffcemux->type.in($mux) | ||||||
|  | 	index <SigSpec> port(ffcemux, \Y) === argD | ||||||
|  | 	choice <IdString> AB {\A, \B} | ||||||
|  | 	index <SigSpec> port(ffcemux, AB) === argQ | ||||||
|  | 	define <bool> pol (AB == \A) | ||||||
|  | 	set ffcepol pol | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD | ||||||
|  | 	if (ffcemux) { | ||||||
|  | 		dffcemux = ffcemux; | ||||||
|  | 		dffcepol = ffcepol; | ||||||
|  | 		argD = port(ffcemux, ffcepol ? \B : \A); | ||||||
|  | 		dffD.replace(port(ffcemux, \Y), argD); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		dffcemux = nullptr; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // ####################### | ||||||
|  | 
 | ||||||
|  | // Subpattern for matching against output registers, based on knowledge of the | ||||||
|  | //   'D' input. | ||||||
|  | // At a high level: | ||||||
|  | //   (1) Starting from an optional $mux cell that implements clock enable | ||||||
|  | //       semantics --- one where the given 'D' argument (partially or fully) | ||||||
|  | //       drives one of its two inputs | ||||||
|  | //   (2) Starting from, or continuing onto, another optional $mux cell that | ||||||
|  | //       implements synchronous reset semantics --- one where the given 'D' | ||||||
|  | //       argument (or the clock enable $mux output) drives one of its two inputs | ||||||
|  | //       and where the other input is fully zero | ||||||
|  | //   (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the | ||||||
|  | //       output of the previous clock enable or reset $mux cells) | ||||||
|  | subpattern out_dffe | ||||||
|  | arg argD argQ clock | ||||||
|  | 
 | ||||||
|  | code | ||||||
|  | 	dff = nullptr; | ||||||
|  | 	for (auto c : argD.chunks()) | ||||||
|  | 		// Abandon matches when 'D' has the keep attribute set | ||||||
|  | 		if (c.wire->get_bool_attribute(\keep)) | ||||||
|  | 			reject; | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (1) Starting from an optional $mux cell that implements clock enable | ||||||
|  | //     semantics --- one where the given 'D' argument (partially or fully) | ||||||
|  | //     drives one of its two inputs | ||||||
|  | match ffcemux | ||||||
|  | 	select ffcemux->type.in($mux) | ||||||
|  | 	// ffcemux output must have two users: ffcemux and ff.D | ||||||
|  | 	select nusers(port(ffcemux, \Y)) == 2 | ||||||
|  | 
 | ||||||
|  | 	choice <IdString> AB {\A, \B} | ||||||
|  | 	// keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s) | ||||||
|  | 	select nusers(port(ffcemux, AB)) >= 3 | ||||||
|  | 
 | ||||||
|  | 	slice offset GetSize(port(ffcemux, \Y)) | ||||||
|  | 	define <IdString> BA (AB == \A ? \B : \A) | ||||||
|  | 	index <SigBit> port(ffcemux, BA)[offset] === argD[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that the rest of argD is present | ||||||
|  | 	filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD) | ||||||
|  | 	filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | 	define <bool> pol (AB == \A) | ||||||
|  | 	set ffcepol pol | ||||||
|  | 
 | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD argQ | ||||||
|  | 	dffcemux = ffcemux; | ||||||
|  | 	if (ffcemux) { | ||||||
|  | 		SigSpec BA = port(ffcemux, ffcepol ? \B : \A); | ||||||
|  | 		SigSpec Y = port(ffcemux, \Y); | ||||||
|  | 		argQ = argD; | ||||||
|  | 		argD.replace(BA, Y); | ||||||
|  | 		argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B)); | ||||||
|  | 
 | ||||||
|  | 		dffcemux = ffcemux; | ||||||
|  | 		dffcepol = ffcepol; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (2) Starting from, or continuing onto, another optional $mux cell that | ||||||
|  | //     implements synchronous reset semantics --- one where the given 'D' | ||||||
|  | //     argument (or the clock enable $mux output) drives one of its two inputs | ||||||
|  | //     and where the other input is fully zero | ||||||
|  | match ffrstmux | ||||||
|  | 	select ffrstmux->type.in($mux) | ||||||
|  | 	// ffrstmux output must have two users: ffrstmux and ff.D | ||||||
|  | 	select nusers(port(ffrstmux, \Y)) == 2 | ||||||
|  | 
 | ||||||
|  | 	choice <IdString> BA {\B, \A} | ||||||
|  | 	// DSP48E1 only supports reset to zero | ||||||
|  | 	select port(ffrstmux, BA).is_fully_zero() | ||||||
|  | 
 | ||||||
|  | 	slice offset GetSize(port(ffrstmux, \Y)) | ||||||
|  | 	define <IdString> AB (BA == \B ? \A : \B) | ||||||
|  | 	index <SigBit> port(ffrstmux, AB)[offset] === argD[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that offset is consistent | ||||||
|  | 	filter !ffcemux || ffoffset == offset | ||||||
|  | 	// Check that the rest of argD is present | ||||||
|  | 	filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD) | ||||||
|  | 	filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | 	define <bool> pol (AB == \A) | ||||||
|  | 	set ffrstpol pol | ||||||
|  | 
 | ||||||
|  | 	semioptional | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argD argQ | ||||||
|  | 	dffrstmux = ffrstmux; | ||||||
|  | 	if (ffrstmux) { | ||||||
|  | 		SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B); | ||||||
|  | 		SigSpec Y = port(ffrstmux, \Y); | ||||||
|  | 		argD.replace(AB, Y); | ||||||
|  | 
 | ||||||
|  | 		dffrstmux = ffrstmux; | ||||||
|  | 		dffrstpol = ffrstpol; | ||||||
|  | 	} | ||||||
|  | endcode | ||||||
|  | 
 | ||||||
|  | // (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the | ||||||
|  | //     output of the previous clock enable or reset $mux cells) | ||||||
|  | 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, \D)[offset] === argD[0] | ||||||
|  | 
 | ||||||
|  | 	// Check that offset is consistent | ||||||
|  | 	filter (!ffcemux && !ffrstmux) || ffoffset == offset | ||||||
|  | 	// Check that the rest of argD is present | ||||||
|  | 	filter GetSize(port(ff, \D)) >= offset + GetSize(argD) | ||||||
|  | 	filter port(ff, \D).extract(offset, GetSize(argD)) == argD | ||||||
|  | 	// Check that FF.Q is connected to CE-mux | ||||||
|  | 	filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ | ||||||
|  | 
 | ||||||
|  | 	filter clock == SigBit() || port(ff, \CLK) == clock | ||||||
|  | 
 | ||||||
|  | 	set ffoffset offset | ||||||
|  | endmatch | ||||||
|  | 
 | ||||||
|  | code argQ | ||||||
|  | 	SigSpec D = port(ff, \D); | ||||||
|  | 	SigSpec Q = port(ff, \Q); | ||||||
|  | 	if (!ffcemux) { | ||||||
|  | 		argQ = argD; | ||||||
|  | 		argQ.replace(D, Q); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Abandon matches when 'Q' has a non-zero init attribute set | ||||||
|  | 	// (not supported by DSP48E1) | ||||||
|  | 	for (auto c : argQ.chunks()) { | ||||||
|  | 		Const init = c.wire->attributes.at(\init, Const()); | ||||||
|  | 		if (!init.empty()) | ||||||
|  | 			for (auto b : init.extract(c.offset, c.width)) | ||||||
|  | 				if (b != State::Sx && b != State::S0) | ||||||
|  | 					reject; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dff = ff; | ||||||
|  | 	dffQ = argQ; | ||||||
|  | 	dffclock = port(ff, \CLK); | ||||||
|  | endcode | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| // This file describes the second of three pattern matcher setups that | // This file describes the second of three pattern matcher setups that | ||||||
| //   forms the `xilinx_dsp` pass described in xilinx_dsp.cc | //   forms the `xilinx_dsp` pass described in xilinx_dsp.cc | ||||||
| // At a high level, it works as follows: | // At a high level, it works as follows: | ||||||
| //   (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already, | //   (1) Starting from a DSP48* cell that (a) doesn't have a CREG already, | ||||||
| //       and (b) uses the 'C' port | //       and (b) uses the 'C' port | ||||||
| //   (2) Match the driver of the 'C' input to a possible $dff cell (CREG) | //   (2) Match the driver of the 'C' input to a possible $dff cell (CREG) | ||||||
| //       (attached to at most two $mux cells that implement clock-enable or | //       (attached to at most two $mux cells that implement clock-enable or | ||||||
|  | @ -38,10 +38,10 @@ udata <SigBit> dffclock | ||||||
| udata <Cell*> dff dffcemux dffrstmux | udata <Cell*> dff dffcemux dffrstmux | ||||||
| udata <bool> dffcepol dffrstpol | udata <bool> dffcepol dffrstpol | ||||||
| 
 | 
 | ||||||
| // (1) Starting from a DSP48E1 cell that (a) doesn't have a CREG already, | // (1) Starting from a DSP48* cell that (a) doesn't have a CREG already, | ||||||
| //     and (b) uses the 'C' port | //     and (b) uses the 'C' port | ||||||
| match dsp | match dsp | ||||||
| 	select dsp->type.in(\DSP48E1) | 	select dsp->type.in(\DSP48A, \DSP48A1, \DSP48E1) | ||||||
| 	select param(dsp, \CREG, 1).as_int() == 0 | 	select param(dsp, \CREG, 1).as_int() == 0 | ||||||
| 	select nusers(port(dsp, \C, SigSpec())) > 1 | 	select nusers(port(dsp, \C, SigSpec())) > 1 | ||||||
| endmatch | endmatch | ||||||
|  | @ -60,7 +60,8 @@ code sigC sigP clock | ||||||
| 	sigC = unextend(port(dsp, \C, SigSpec())); | 	sigC = unextend(port(dsp, \C, SigSpec())); | ||||||
| 
 | 
 | ||||||
| 	SigSpec P = port(dsp, \P); | 	SigSpec P = port(dsp, \P); | ||||||
| 	if (param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") { | 	if (!dsp->type.in(\DSP48E1) || | ||||||
|  |  param(dsp, \USE_MULT, Const("MULTIPLY")).decode_string() == "MULTIPLY") { | ||||||
| 		// Only care about those bits that are used | 		// Only care about those bits that are used | ||||||
| 		int i; | 		int i; | ||||||
| 		for (i = GetSize(P)-1; i >= 0; i--) | 		for (i = GetSize(P)-1; i >= 0; i--) | ||||||
|  |  | ||||||
|  | @ -62,12 +62,11 @@ code | ||||||
| #define MAX_DSP_CASCADE 20 | #define MAX_DSP_CASCADE 20 | ||||||
| endcode | endcode | ||||||
| 
 | 
 | ||||||
| // (1) Starting from a DSP48E1 cell that (a) has the Z multiplexer | // (1) Starting from a DSP48* cell that (a) has the Z multiplexer | ||||||
| //     (controlled by OPMODE[6:4]) set to zero and (b) doesn't already | //     (controlled by OPMODE[3:2] for DSP48A*, by OPMODE[6:4] for DSP48E1) | ||||||
| //     use the 'PCOUT' port | //     set to zero and (b) doesn't already use the 'PCOUT' port | ||||||
| match first | match first | ||||||
| 	select first->type.in(\DSP48E1) | 	select (first->type.in(\DSP48A, \DSP48A1) && port(first, \OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("00")) || (first->type.in(\DSP48E1) && port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000")) | ||||||
| 	select port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000") |  | ||||||
| 	select nusers(port(first, \PCOUT, SigSpec())) <= 1 | 	select nusers(port(first, \PCOUT, SigSpec())) <= 1 | ||||||
| endmatch | endmatch | ||||||
| 
 | 
 | ||||||
|  | @ -100,14 +99,21 @@ finally | ||||||
| 					add_siguser(cascade, dsp); | 					add_siguser(cascade, dsp); | ||||||
| 
 | 
 | ||||||
| 					SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7)); | 					SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7)); | ||||||
| 					if (P == 17) | 					if (dsp->type.in(\DSP48A, \DSP48A1)) { | ||||||
| 						opmode[6] = State::S1; | 						log_assert(P == 0); | ||||||
| 					else if (P == 0) | 						opmode[3] = State::S0; | ||||||
| 						opmode[6] = State::S0; | 						opmode[2] = State::S1; | ||||||
| 					else log_abort(); | 					} | ||||||
|  | 					else if (dsp->type.in(\DSP48E1)) { | ||||||
|  | 						if (P == 17) | ||||||
|  | 							opmode[6] = State::S1; | ||||||
|  | 						else if (P == 0) | ||||||
|  | 							opmode[6] = State::S0; | ||||||
|  | 						else log_abort(); | ||||||
| 
 | 
 | ||||||
| 					opmode[5] = State::S0; | 						opmode[5] = State::S0; | ||||||
| 					opmode[4] = State::S1; | 						opmode[4] = State::S1; | ||||||
|  | 					} | ||||||
| 					dsp_pcin->setPort(\OPMODE, opmode); | 					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)); | ||||||
|  | @ -120,21 +126,42 @@ finally | ||||||
| 					add_siguser(cascade, dsp_pcin); | 					add_siguser(cascade, dsp_pcin); | ||||||
| 					add_siguser(cascade, dsp); | 					add_siguser(cascade, dsp); | ||||||
| 
 | 
 | ||||||
| 					dsp->setParam(ID(ACASCREG), AREG); | 					if (dsp->type.in(\DSP48E1)) | ||||||
|  | 						dsp->setParam(ID(ACASCREG), AREG); | ||||||
| 					dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE")); | 					dsp_pcin->setParam(ID(A_INPUT), Const("CASCADE")); | ||||||
| 
 | 
 | ||||||
| 					log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); | 					log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); | ||||||
| 				} | 				} | ||||||
| 				if (BREG >= 0) { | 				if (BREG >= 0) { | ||||||
| 					Wire *cascade = module->addWire(NEW_ID, 18); | 					Wire *cascade = module->addWire(NEW_ID, 18); | ||||||
| 					dsp_pcin->setPort(ID(B), Const(0, 18)); | 					if (dsp->type.in(\DSP48A, \DSP48A1)) { | ||||||
| 					dsp_pcin->setPort(ID(BCIN), cascade); | 						// According to UG389 p9 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf] | ||||||
|  | 						// "The DSP48A1 component uses this input when cascading | ||||||
|  | 						//   BCOUT from an adjacent DSP48A1 slice. The tools then | ||||||
|  | 						//   translate BCOUT cascading to the dedicated BCIN input | ||||||
|  | 						//   and set the B_INPUT attribute for implementation." | ||||||
|  | 						dsp_pcin->setPort(ID(B), cascade); | ||||||
|  | 					} | ||||||
|  | 					else { | ||||||
|  | 						dsp_pcin->setPort(ID(B), Const(0, 18)); | ||||||
|  | 						dsp_pcin->setPort(ID(BCIN), cascade); | ||||||
|  | 					} | ||||||
| 					dsp->setPort(ID(BCOUT), cascade); | 					dsp->setPort(ID(BCOUT), cascade); | ||||||
| 					add_siguser(cascade, dsp_pcin); | 					add_siguser(cascade, dsp_pcin); | ||||||
| 					add_siguser(cascade, dsp); | 					add_siguser(cascade, dsp); | ||||||
| 
 | 
 | ||||||
| 					dsp->setParam(ID(BCASCREG), BREG); | 					if (dsp->type.in(\DSP48E1)) { | ||||||
| 					dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE")); | 						dsp->setParam(ID(BCASCREG), BREG); | ||||||
|  | 						// According to UG389 p13 [https://www.xilinx.com/support/documentation/user_guides/ug389.pdf] | ||||||
|  | 						// "The attribute is only used by place and route tools and | ||||||
|  | 						//   is not necessary for the users to set for synthesis. The | ||||||
|  | 						//   attribute is determined by the connection to the B port | ||||||
|  | 						//   of the DSP48A1 slice. If the B port is connected to the | ||||||
|  | 						//   BCOUT of another DSP48A1 slice, then the tools automatically | ||||||
|  | 						//   set the attribute to 'CASCADE', otherwise it is set to | ||||||
|  | 						//   'DIRECT'". | ||||||
|  | 						dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE")); | ||||||
|  | 					} | ||||||
| 
 | 
 | ||||||
| 					log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); | 					log_debug("BCOUT -> BCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); | ||||||
| 				} | 				} | ||||||
|  | @ -156,22 +183,21 @@ subpattern tail | ||||||
| arg first | arg first | ||||||
| arg next | arg next | ||||||
| 
 | 
 | ||||||
| // (2.1) Match another DSP48E1 cell that (a) does not have the CREG enabled, | // (2.1) Match another DSP48* cell that (a) does not have the CREG enabled, | ||||||
| //       (b) has its Z multiplexer output set to the 'C' port, which is | //       (b) has its Z multiplexer output set to the 'C' port, which is | ||||||
| //       driven by the 'P' output of the previous DSP cell, and (c) has its | //       driven by the 'P' output of the previous DSP cell, and (c) has its | ||||||
| //       'PCIN' port unused | //       'PCIN' port unused | ||||||
| match nextP | match nextP | ||||||
| 	select nextP->type.in(\DSP48E1) |  | ||||||
| 	select !param(nextP, \CREG, State::S1).as_bool() | 	select !param(nextP, \CREG, State::S1).as_bool() | ||||||
| 	select port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") | 	select (nextP->type.in(\DSP48A, \DSP48A1) && port(nextP, \OPMODE, Const(0, 8)).extract(2,2) == Const::from_string("11")) || (nextP->type.in(\DSP48E1) && port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011")) | ||||||
| 	select nusers(port(nextP, \C, SigSpec())) > 1 | 	select nusers(port(nextP, \C, SigSpec())) > 1 | ||||||
| 	select nusers(port(nextP, \PCIN, SigSpec())) == 0 | 	select nusers(port(nextP, \PCIN, SigSpec())) == 0 | ||||||
| 	index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0] | 	index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0] | ||||||
| 	semioptional | 	semioptional | ||||||
| endmatch | endmatch | ||||||
| 
 | 
 | ||||||
| // (2.2) Same as (2.1) but with the 'C' port driven by the 'P' output of the | // (2.2) For DSP48E1 only, same as (2.1) but with the 'C' port driven | ||||||
| //       previous DSP cell right-shifted by 17 bits | //       by the 'P' output of the previous DSP cell right-shifted by 17 bits | ||||||
| match nextP_shift17 | match nextP_shift17 | ||||||
| 	if !nextP | 	if !nextP | ||||||
| 	select nextP_shift17->type.in(\DSP48E1) | 	select nextP_shift17->type.in(\DSP48E1) | ||||||
|  | @ -188,6 +214,8 @@ code next | ||||||
| 	if (!nextP) | 	if (!nextP) | ||||||
| 		next = nextP_shift17; | 		next = nextP_shift17; | ||||||
| 	if (next) { | 	if (next) { | ||||||
|  | 		if (next->type != first->type) | ||||||
|  | 			reject; | ||||||
| 		unextend = [](const SigSpec &sig) { | 		unextend = [](const SigSpec &sig) { | ||||||
| 			int i; | 			int i; | ||||||
| 			for (i = GetSize(sig)-1; i > 0; i--) | 			for (i = GetSize(sig)-1; i > 0; i--) | ||||||
|  | @ -202,38 +230,50 @@ code next | ||||||
| endcode | endcode | ||||||
| 
 | 
 | ||||||
| // (3) For this subequent DSP48E1 match (i.e. PCOUT -> PCIN cascade exists) | // (3) For this subequent DSP48E1 match (i.e. PCOUT -> PCIN cascade exists) | ||||||
| //     if (a) the previous DSP48E1 uses either the A2REG or A1REG, (b) this | //     if (a) this DSP48E1 does not already have an ACOUT -> ACIN cascade, | ||||||
| //     DSP48 does not use A2REG nor A1REG, (c) this DSP48E1 does not already | //     (b) the previous DSP does  not already use its ACOUT port, then | ||||||
| //     have an ACOUT -> ACIN cascade, (d) the previous DSP does not already | //     examine if an ACOUT -> ACIN cascade  opportunity exists if | ||||||
| //     use its ACOUT port, then examine if an ACOUT -> ACIN cascade | //     (i) A ports are identical, or (ii) separated by a | ||||||
| //     opportunity exists by matching for a $dff-with-optional-clock-enable- | //     $dff-with-optional-clock-enable-or-reset and checking that the 'D' input | ||||||
| //     or-reset and checking that the 'D' input of this register is the same | //     of this register is the same as the 'A' input of the previous DSP | ||||||
| //     as the 'A' input of the previous DSP | //     TODO: Check for two levels of flops, instead of just one | ||||||
| code argQ clock AREG | code argQ clock AREG | ||||||
| 	AREG = -1; | 	AREG = -1; | ||||||
| 	if (next) { | 	if (next && next->type.in(\DSP48E1)) { | ||||||
| 		Cell *prev = std::get<0>(chain.back()); | 		Cell *prev = std::get<0>(chain.back()); | ||||||
| 		if (param(prev, \AREG, 2).as_int() > 0 && | 
 | ||||||
| 				param(next, \AREG, 2).as_int() > 0 && | 		if (param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && | ||||||
| 				param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && | 				port(next, \ACIN, SigSpec()).is_fully_zero() && | ||||||
| 				nusers(port(prev, \ACOUT, SigSpec())) <= 1) { | 				nusers(port(prev, \ACOUT, SigSpec())) <= 1) { | ||||||
| 			argQ = unextend(port(next, \A)); | 			if (param(prev, \AREG, 2) == 0) { | ||||||
| 			clock = port(prev, \CLK); | 				if (port(prev, \A) == port(next, \A)) | ||||||
| 			subpattern(in_dffe); | 					AREG = 0; | ||||||
| 			if (dff) { | 			} | ||||||
| 				if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0) | 			else { | ||||||
| 					goto reject_AREG; | 				argQ = unextend(port(next, \A)); | ||||||
| 				if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0)) | 				clock = port(prev, \CLK); | ||||||
| 					goto reject_AREG; | 				subpattern(in_dffe); | ||||||
| 				if (!dffcemux && port(prev, \CEA2, State::S0) != State::S0) | 				if (dff) { | ||||||
| 					goto reject_AREG; | 					if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0) | ||||||
| 				if (dffcemux && port(dffcemux, \S) != port(prev, \CEA2, State::S0)) | 						goto reject_AREG; | ||||||
| 					goto reject_AREG; | 					if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0)) | ||||||
| 				if (dffD == unextend(port(prev, \A))) | 						goto reject_AREG; | ||||||
| 					AREG = 1; | 					IdString CEA; | ||||||
| reject_AREG:			; | 					if (param(prev, \AREG, 2) == 1) | ||||||
|  | 						CEA = \CEA2; | ||||||
|  | 					else if (param(prev, \AREG, 2) == 2) | ||||||
|  | 						CEA = \CEA1; | ||||||
|  | 					else log_abort(); | ||||||
|  | 					if (!dffcemux && port(prev, CEA, State::S0) != State::S1) | ||||||
|  | 						goto reject_AREG; | ||||||
|  | 					if (dffcemux && port(dffcemux, \S) != port(prev, CEA, State::S0)) | ||||||
|  | 						goto reject_AREG; | ||||||
|  | 					if (dffD == unextend(port(prev, \A))) | ||||||
|  | 						AREG = 1; | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | reject_AREG:	; | ||||||
| 	} | 	} | ||||||
| endcode | endcode | ||||||
| 
 | 
 | ||||||
|  | @ -242,28 +282,47 @@ code argQ clock BREG | ||||||
| 	BREG = -1; | 	BREG = -1; | ||||||
| 	if (next) { | 	if (next) { | ||||||
| 		Cell *prev = std::get<0>(chain.back()); | 		Cell *prev = std::get<0>(chain.back()); | ||||||
| 		if (param(prev, \BREG, 2).as_int() > 0 && | 		if (param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && | ||||||
| 				param(next, \BREG, 2).as_int() > 0 && |  | ||||||
| 				param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && |  | ||||||
| 				port(next, \BCIN, SigSpec()).is_fully_zero() && | 				port(next, \BCIN, SigSpec()).is_fully_zero() && | ||||||
| 				nusers(port(prev, \BCOUT, SigSpec())) <= 1) { | 				nusers(port(prev, \BCOUT, SigSpec())) <= 1) { | ||||||
| 			argQ = unextend(port(next, \B)); | 			if ((next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) == 0 && param(prev, \B1REG, 1) == 0) || | ||||||
| 			clock = port(prev, \CLK); | 				(next->type.in(\DSP48E1) && param(prev, \BREG, 2) == 0)) { | ||||||
| 			subpattern(in_dffe); | 				if (port(prev, \B) == port(next, \B)) | ||||||
| 			if (dff) { | 					BREG = 0; | ||||||
| 				if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0) | 			} | ||||||
| 					goto reject_BREG; | 			else { | ||||||
| 				if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0)) | 				argQ = unextend(port(next, \B)); | ||||||
| 					goto reject_BREG; | 				clock = port(prev, \CLK); | ||||||
| 				if (!dffcemux && port(prev, \CEB2, State::S0) != State::S0) | 				subpattern(in_dffe); | ||||||
| 					goto reject_BREG; | 				if (dff) { | ||||||
| 				if (dffcemux && port(dffcemux, \S) != port(prev, \CEB2, State::S0)) | 					if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0) | ||||||
| 					goto reject_BREG; | 						goto reject_BREG; | ||||||
| 				if (dffD == unextend(port(prev, \B))) | 					if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0)) | ||||||
| 					BREG = 1; | 						goto reject_BREG; | ||||||
| reject_BREG:			; | 					IdString CEB; | ||||||
|  | 					if (next->type.in(\DSP48A, \DSP48A1)) | ||||||
|  | 						CEB = \CEB; | ||||||
|  | 					else if (next->type.in(\DSP48E1)) { | ||||||
|  | 						if (param(prev, \BREG, 2) == 1) | ||||||
|  | 							CEB = \CEB2; | ||||||
|  | 						else if (param(prev, \BREG, 2) == 2) | ||||||
|  | 							CEB = \CEB1; | ||||||
|  | 						else log_abort(); | ||||||
|  | 					} | ||||||
|  | 					else log_abort(); | ||||||
|  | 					if (!dffcemux && port(prev, CEB, State::S0) != State::S1) | ||||||
|  | 						goto reject_BREG; | ||||||
|  | 					if (dffcemux && port(dffcemux, \S) != port(prev, CEB, State::S0)) | ||||||
|  | 						goto reject_BREG; | ||||||
|  | 					if (dffD == unextend(port(prev, \B))) { | ||||||
|  | 						if (next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) != 0) | ||||||
|  | 							goto reject_BREG; | ||||||
|  | 						BREG = 1; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | reject_BREG:	; | ||||||
| 	} | 	} | ||||||
| endcode | endcode | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -192,11 +192,28 @@ struct IopadmapPass : public Pass { | ||||||
| 			if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty()) | 			if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty()) | ||||||
| 			{ | 			{ | ||||||
| 				dict<SigBit, Cell *> tbuf_bits; | 				dict<SigBit, Cell *> tbuf_bits; | ||||||
|  | 				pool<SigBit> driven_bits; | ||||||
| 
 | 
 | ||||||
|  | 				// Gather tristate buffers and always-on drivers.
 | ||||||
| 				for (auto cell : module->cells()) | 				for (auto cell : module->cells()) | ||||||
| 					if (cell->type == ID($_TBUF_)) { | 					if (cell->type == ID($_TBUF_)) { | ||||||
| 						SigBit bit = cell->getPort(ID::Y).as_bit(); | 						SigBit bit = cell->getPort(ID::Y).as_bit(); | ||||||
| 						tbuf_bits[bit] = cell; | 						tbuf_bits[bit] = cell; | ||||||
|  | 					} else { | ||||||
|  | 						for (auto port : cell->connections()) | ||||||
|  | 							if (!cell->known() || cell->output(port.first)) | ||||||
|  | 								for (auto bit : port.second) | ||||||
|  | 									driven_bits.insert(bit); | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 				// If a wire is a target of an assignment, it is driven, unless the source is 'z.
 | ||||||
|  | 				for (auto &conn : module->connections()) | ||||||
|  | 					for (int i = 0; i < GetSize(conn.first); i++) { | ||||||
|  | 						SigBit dstbit = conn.first[i]; | ||||||
|  | 						SigBit srcbit = conn.second[i]; | ||||||
|  | 						if (!srcbit.wire && srcbit.data == State::Sz) | ||||||
|  | 							continue; | ||||||
|  | 						driven_bits.insert(dstbit); | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 				for (auto wire : module->selected_wires()) | 				for (auto wire : module->selected_wires()) | ||||||
|  | @ -204,41 +221,68 @@ struct IopadmapPass : public Pass { | ||||||
| 					if (!wire->port_output) | 					if (!wire->port_output) | ||||||
| 						continue; | 						continue; | ||||||
| 
 | 
 | ||||||
|  | 					// Don't handle inout ports if we have no suitable buffer type.
 | ||||||
|  | 					if (wire->port_input && tinoutpad_celltype.empty()) | ||||||
|  | 						continue; | ||||||
|  | 
 | ||||||
|  | 					// likewise for output ports.
 | ||||||
|  | 					if (!wire->port_input && toutpad_celltype.empty()) | ||||||
|  | 						continue; | ||||||
|  | 
 | ||||||
| 					for (int i = 0; i < GetSize(wire); i++) | 					for (int i = 0; i < GetSize(wire); i++) | ||||||
| 					{ | 					{ | ||||||
| 						SigBit wire_bit(wire, i); | 						SigBit wire_bit(wire, i); | ||||||
|  | 						Cell *tbuf_cell = nullptr; | ||||||
| 
 | 
 | ||||||
| 						if (tbuf_bits.count(wire_bit) == 0) | 						if (tbuf_bits.count(wire_bit)) | ||||||
| 							continue; | 							tbuf_cell = tbuf_bits.at(wire_bit); | ||||||
| 
 | 
 | ||||||
| 						Cell *tbuf_cell = tbuf_bits.at(wire_bit); | 						SigBit en_sig; | ||||||
|  | 						SigBit data_sig; | ||||||
|  | 						bool is_driven = driven_bits.count(wire_bit); | ||||||
| 
 | 
 | ||||||
| 						if (tbuf_cell == nullptr) | 						if (tbuf_cell != nullptr) { | ||||||
| 							continue; | 							// Found a tristate buffer — use it.
 | ||||||
|  | 							en_sig = tbuf_cell->getPort(ID(E)).as_bit(); | ||||||
|  | 							data_sig = tbuf_cell->getPort(ID::A).as_bit(); | ||||||
|  | 						} else if (is_driven) { | ||||||
|  | 							// No tristate buffer, but an always-on driver is present.
 | ||||||
|  | 							// If this is an inout port, we're creating a tinoutpad
 | ||||||
|  | 							// anyway, just with a constant 1 as enable.
 | ||||||
|  | 							if (!wire->port_input) | ||||||
|  | 								continue; | ||||||
|  | 							en_sig = SigBit(State::S1); | ||||||
|  | 							data_sig = wire_bit; | ||||||
|  | 						} else { | ||||||
|  | 							// No driver on a wire.  Create a tristate pad with always-0
 | ||||||
|  | 							// enable.
 | ||||||
|  | 							en_sig = SigBit(State::S0); | ||||||
|  | 							data_sig = SigBit(State::Sx); | ||||||
|  | 						} | ||||||
| 
 | 
 | ||||||
| 						SigBit en_sig = tbuf_cell->getPort(ID(E)).as_bit(); | 						if (wire->port_input) | ||||||
| 						SigBit data_sig = tbuf_cell->getPort(ID::A).as_bit(); |  | ||||||
| 
 |  | ||||||
| 						if (wire->port_input && !tinoutpad_celltype.empty()) |  | ||||||
| 						{ | 						{ | ||||||
| 							log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str()); | 							log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str()); | ||||||
| 
 | 
 | ||||||
| 							Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype)); | 							Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype)); | ||||||
| 
 | 
 | ||||||
| 							cell->setPort(RTLIL::escape_id(tinoutpad_portname_oe), en_sig); | 							cell->setPort(RTLIL::escape_id(tinoutpad_portname_oe), en_sig); | ||||||
| 							cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit); |  | ||||||
| 							cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig); |  | ||||||
| 							cell->attributes[ID::keep] = RTLIL::Const(1); | 							cell->attributes[ID::keep] = RTLIL::Const(1); | ||||||
| 
 | 
 | ||||||
| 							module->remove(tbuf_cell); | 							if (tbuf_cell) { | ||||||
|  | 								module->remove(tbuf_cell); | ||||||
|  | 								cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit); | ||||||
|  | 								cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig); | ||||||
|  | 							} else if (is_driven) { | ||||||
|  | 								cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), wire_bit); | ||||||
|  | 							} else { | ||||||
|  | 								cell->setPort(RTLIL::escape_id(tinoutpad_portname_o), wire_bit); | ||||||
|  | 								cell->setPort(RTLIL::escape_id(tinoutpad_portname_i), data_sig); | ||||||
|  | 							} | ||||||
| 							skip_wire_bits.insert(wire_bit); | 							skip_wire_bits.insert(wire_bit); | ||||||
| 							if (!tinoutpad_portname_pad.empty()) | 							if (!tinoutpad_portname_pad.empty()) | ||||||
| 								rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(tinoutpad_portname_pad)); | 								rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(tinoutpad_portname_pad)); | ||||||
| 							continue; | 						} else { | ||||||
| 						} |  | ||||||
| 
 |  | ||||||
| 						if (!wire->port_input && !toutpad_celltype.empty()) |  | ||||||
| 						{ |  | ||||||
| 							log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str()); | 							log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str()); | ||||||
| 
 | 
 | ||||||
| 							Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype)); | 							Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype)); | ||||||
|  | @ -247,12 +291,13 @@ struct IopadmapPass : public Pass { | ||||||
| 							cell->setPort(RTLIL::escape_id(toutpad_portname_i), data_sig); | 							cell->setPort(RTLIL::escape_id(toutpad_portname_i), data_sig); | ||||||
| 							cell->attributes[ID::keep] = RTLIL::Const(1); | 							cell->attributes[ID::keep] = RTLIL::Const(1); | ||||||
| 
 | 
 | ||||||
| 							module->remove(tbuf_cell); | 							if (tbuf_cell) { | ||||||
| 							module->connect(wire_bit, data_sig); | 								module->remove(tbuf_cell); | ||||||
|  | 								module->connect(wire_bit, data_sig); | ||||||
|  | 							} | ||||||
| 							skip_wire_bits.insert(wire_bit); | 							skip_wire_bits.insert(wire_bit); | ||||||
| 							if (!toutpad_portname_pad.empty()) | 							if (!toutpad_portname_pad.empty()) | ||||||
| 								rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(toutpad_portname_pad)); | 								rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(toutpad_portname_pad)); | ||||||
| 							continue; |  | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| // --------------------------------------- | // --------------------------------------- | ||||||
| 
 | 
 | ||||||
|  | (* lib_whitebox *) | ||||||
| module LUT4(input A, B, C, D, output Z); | module LUT4(input A, B, C, D, output Z); | ||||||
|     parameter [15:0] INIT = 16'h0000; |     parameter [15:0] INIT = 16'h0000; | ||||||
|     wire [7:0] s3 = D ?     INIT[15:8] :     INIT[7:0]; |     wire [7:0] s3 = D ?     INIT[15:8] :     INIT[7:0]; | ||||||
|  | @ -31,13 +32,8 @@ module CCU2C( | ||||||
| 
 | 
 | ||||||
| 	// First half | 	// First half | ||||||
| 	wire LUT4_0, LUT2_0; | 	wire LUT4_0, LUT2_0; | ||||||
| `ifdef _ABC |  | ||||||
| 	assign LUT4_0 = INIT0[{D0, C0, B0, A0}]; |  | ||||||
| 	assign LUT2_0 = INIT0[{2'b00, B0, A0}]; |  | ||||||
| `else |  | ||||||
| 	LUT4 #(.INIT(INIT0)) lut4_0(.A(A0), .B(B0), .C(C0), .D(D0), .Z(LUT4_0)); | 	LUT4 #(.INIT(INIT0)) lut4_0(.A(A0), .B(B0), .C(C0), .D(D0), .Z(LUT4_0)); | ||||||
| 	LUT2 #(.INIT(INIT0[3:0])) lut2_0(.A(A0), .B(B0), .Z(LUT2_0)); | 	LUT2 #(.INIT(INIT0[3:0])) lut2_0(.A(A0), .B(B0), .Z(LUT2_0)); | ||||||
| `endif |  | ||||||
| 	wire gated_cin_0 = (INJECT1_0 == "YES") ? 1'b0 : CIN; | 	wire gated_cin_0 = (INJECT1_0 == "YES") ? 1'b0 : CIN; | ||||||
| 	assign S0 = LUT4_0 ^ gated_cin_0; | 	assign S0 = LUT4_0 ^ gated_cin_0; | ||||||
| 
 | 
 | ||||||
|  | @ -46,13 +42,8 @@ module CCU2C( | ||||||
| 
 | 
 | ||||||
| 	// Second half | 	// Second half | ||||||
| 	wire LUT4_1, LUT2_1; | 	wire LUT4_1, LUT2_1; | ||||||
| `ifdef _ABC |  | ||||||
| 	assign LUT4_1 = INIT1[{D1, C1, B1, A1}]; |  | ||||||
| 	assign LUT2_1 = INIT1[{2'b00, B1, A1}]; |  | ||||||
| `else |  | ||||||
| 	LUT4 #(.INIT(INIT1)) lut4_1(.A(A1), .B(B1), .C(C1), .D(D1), .Z(LUT4_1)); | 	LUT4 #(.INIT(INIT1)) lut4_1(.A(A1), .B(B1), .C(C1), .D(D1), .Z(LUT4_1)); | ||||||
| 	LUT2 #(.INIT(INIT1[3:0])) lut2_1(.A(A1), .B(B1), .Z(LUT2_1)); | 	LUT2 #(.INIT(INIT1[3:0])) lut2_1(.A(A1), .B(B1), .Z(LUT2_1)); | ||||||
| `endif |  | ||||||
| 	wire gated_cin_1 = (INJECT1_1 == "YES") ? 1'b0 : cout_0; | 	wire gated_cin_1 = (INJECT1_1 == "YES") ? 1'b0 : cout_0; | ||||||
| 	assign S1 = LUT4_1 ^ gated_cin_1; | 	assign S1 = LUT4_1 ^ gated_cin_1; | ||||||
| 
 | 
 | ||||||
|  | @ -209,6 +200,7 @@ endmodule | ||||||
| 
 | 
 | ||||||
| // --------------------------------------- | // --------------------------------------- | ||||||
| 
 | 
 | ||||||
|  | (* lib_whitebox *) | ||||||
| module LUT2(input A, B, output Z); | module LUT2(input A, B, output Z); | ||||||
|     parameter [3:0] INIT = 4'h0; |     parameter [3:0] INIT = 4'h0; | ||||||
|     wire [1:0] s1 = B ?     INIT[ 3:2] :     INIT[1:0]; |     wire [1:0] s1 = B ?     INIT[ 3:2] :     INIT[1:0]; | ||||||
|  |  | ||||||
|  | @ -230,7 +230,7 @@ struct SynthEcp5Pass : public ScriptPass | ||||||
| 	{ | 	{ | ||||||
| 		if (check_label("begin")) | 		if (check_label("begin")) | ||||||
| 		{ | 		{ | ||||||
| 			run("read_verilog -D_ABC -lib +/ecp5/cells_sim.v +/ecp5/cells_bb.v"); | 			run("read_verilog -lib +/ecp5/cells_sim.v +/ecp5/cells_bb.v"); | ||||||
| 			run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); | 			run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2107,7 +2107,7 @@ always @* begin | ||||||
| 		2'b00: XMUX <= 0; | 		2'b00: XMUX <= 0; | ||||||
| 		2'b01: XMUX <= M; | 		2'b01: XMUX <= M; | ||||||
| 		2'b10: XMUX <= P; | 		2'b10: XMUX <= P; | ||||||
| 		2'b11: XMUX <= {D_OUT[11:0], B1_OUT, A1_OUT}; | 		2'b11: XMUX <= {D_OUT[11:0], A1_OUT, B1_OUT}; | ||||||
| 		default: XMUX <= 48'hxxxxxxxxxxxx; | 		default: XMUX <= 48'hxxxxxxxxxxxx; | ||||||
| 	endcase | 	endcase | ||||||
| end | end | ||||||
|  | @ -2125,8 +2125,8 @@ end | ||||||
| // The post-adder. | // The post-adder. | ||||||
| wire signed [48:0] X_EXT; | wire signed [48:0] X_EXT; | ||||||
| wire signed [48:0] Z_EXT; | wire signed [48:0] Z_EXT; | ||||||
| assign X_EXT = XMUX; | assign X_EXT = {1'b0, XMUX}; | ||||||
| assign Z_EXT = ZMUX; | assign Z_EXT = {1'b0, ZMUX}; | ||||||
| assign {CARRYOUT_IN, P_IN} = OPMODE_OUT[7] ? (Z_EXT - (X_EXT + CARRYIN_OUT)) : (Z_EXT + X_EXT + CARRYIN_OUT); | assign {CARRYOUT_IN, P_IN} = OPMODE_OUT[7] ? (Z_EXT - (X_EXT + CARRYIN_OUT)) : (Z_EXT + X_EXT + CARRYIN_OUT); | ||||||
| 
 | 
 | ||||||
| // Cascade outputs. | // Cascade outputs. | ||||||
|  |  | ||||||
|  | @ -64,7 +64,7 @@ struct SynthXilinxPass : public ScriptPass | ||||||
| 		log("        (this feature is experimental and incomplete)\n"); | 		log("        (this feature is experimental and incomplete)\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    -ise\n"); | 		log("    -ise\n"); | ||||||
| 		log("        generate an output netlist suitable for ISE (enables -iopad)\n"); | 		log("        generate an output netlist suitable for ISE\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    -nobram\n"); | 		log("    -nobram\n"); | ||||||
| 		log("        do not use block RAM cells in output netlist\n"); | 		log("        do not use block RAM cells in output netlist\n"); | ||||||
|  | @ -84,11 +84,9 @@ struct SynthXilinxPass : public ScriptPass | ||||||
| 		log("    -nodsp\n"); | 		log("    -nodsp\n"); | ||||||
| 		log("        do not use DSP48E1s to implement multipliers and associated logic\n"); | 		log("        do not use DSP48E1s to implement multipliers and associated logic\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    -iopad\n"); |  | ||||||
| 		log("        enable I/O buffer insertion (selected automatically by -ise)\n"); |  | ||||||
| 		log("\n"); |  | ||||||
| 		log("    -noiopad\n"); | 		log("    -noiopad\n"); | ||||||
| 		log("        disable I/O buffer insertion (only useful with -ise)\n"); | 		log("        disable I/O buffer insertion (useful for hierarchical or \n"); | ||||||
|  | 		log("        out-of-context flows)\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 		log("    -noclkbuf\n"); | 		log("    -noclkbuf\n"); | ||||||
| 		log("        disable automatic clock buffer insertion\n"); | 		log("        disable automatic clock buffer insertion\n"); | ||||||
|  | @ -125,7 +123,7 @@ struct SynthXilinxPass : public ScriptPass | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	std::string top_opt, edif_file, blif_file, family; | 	std::string top_opt, edif_file, blif_file, family; | ||||||
| 	bool flatten, retime, vpr, ise, iopad, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp, uram; | 	bool flatten, retime, vpr, ise, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp, uram; | ||||||
| 	bool abc9, dff_mode; | 	bool abc9, dff_mode; | ||||||
| 	bool flatten_before_abc; | 	bool flatten_before_abc; | ||||||
| 	int widemux; | 	int widemux; | ||||||
|  | @ -140,7 +138,6 @@ struct SynthXilinxPass : public ScriptPass | ||||||
| 		retime = false; | 		retime = false; | ||||||
| 		vpr = false; | 		vpr = false; | ||||||
| 		ise = false; | 		ise = false; | ||||||
| 		iopad = false; |  | ||||||
| 		noiopad = false; | 		noiopad = false; | ||||||
| 		noclkbuf = false; | 		noclkbuf = false; | ||||||
| 		nocarry = false; | 		nocarry = false; | ||||||
|  | @ -218,7 +215,6 @@ struct SynthXilinxPass : public ScriptPass | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| 			if (args[argidx] == "-iopad") { | 			if (args[argidx] == "-iopad") { | ||||||
| 				iopad = true; |  | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| 			if (args[argidx] == "-noiopad") { | 			if (args[argidx] == "-noiopad") { | ||||||
|  | @ -291,7 +287,6 @@ struct SynthXilinxPass : public ScriptPass | ||||||
| 
 | 
 | ||||||
| 	void script() YS_OVERRIDE | 	void script() YS_OVERRIDE | ||||||
| 	{ | 	{ | ||||||
| 		bool do_iopad = iopad || (ise && !noiopad); |  | ||||||
| 		std::string ff_map_file; | 		std::string ff_map_file; | ||||||
| 		if (help_mode) | 		if (help_mode) | ||||||
| 			ff_map_file = "+/xilinx/{family}_ff_map.v"; | 			ff_map_file = "+/xilinx/{family}_ff_map.v"; | ||||||
|  | @ -397,7 +392,10 @@ struct SynthXilinxPass : public ScriptPass | ||||||
| 				run("opt_expr -fine"); | 				run("opt_expr -fine"); | ||||||
| 				run("wreduce"); | 				run("wreduce"); | ||||||
| 				run("select -clear"); | 				run("select -clear"); | ||||||
| 				run("xilinx_dsp"); | 				if (help_mode) | ||||||
|  | 					run("xilinx_dsp -family <family>"); | ||||||
|  | 				else | ||||||
|  | 					run("xilinx_dsp -family " + family); | ||||||
| 				run("chtype -set $mul t:$__soft_mul"); | 				run("chtype -set $mul t:$__soft_mul"); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | @ -524,8 +522,8 @@ struct SynthXilinxPass : public ScriptPass | ||||||
| 
 | 
 | ||||||
| 		if (check_label("map_cells")) { | 		if (check_label("map_cells")) { | ||||||
| 			// Needs to be done before logic optimization, so that inverters (OE vs T) are handled.
 | 			// Needs to be done before logic optimization, so that inverters (OE vs T) are handled.
 | ||||||
| 			if (help_mode || do_iopad) | 			if (help_mode || !noiopad) | ||||||
| 				run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I -toutpad $__XILINX_TOUTPAD OE:I:O -tinoutpad $__XILINX_TINOUTPAD OE:O:I:IO A:top", "(only if '-iopad' or '-ise' and not '-noiopad')"); | 				run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I -toutpad $__XILINX_TOUTPAD OE:I:O -tinoutpad $__XILINX_TINOUTPAD OE:O:I:IO A:top", "(only if not '-noiopad')"); | ||||||
| 			std::string techmap_args = "-map +/techmap.v -map +/xilinx/cells_map.v"; | 			std::string techmap_args = "-map +/techmap.v -map +/xilinx/cells_map.v"; | ||||||
| 			if (widemux > 0) | 			if (widemux > 0) | ||||||
| 				techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", widemux); | 				techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", widemux); | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								techlibs/xilinx/tests/.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								techlibs/xilinx/tests/.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -8,4 +8,8 @@ dsp_work*/ | ||||||
| test_dsp_model_ref.v | test_dsp_model_ref.v | ||||||
| test_dsp_model_uut.v | test_dsp_model_uut.v | ||||||
| test_dsp_model | test_dsp_model | ||||||
|  | test_dsp48a_model_ref.v | ||||||
|  | test_dsp48a1_model_ref.v | ||||||
|  | test_dsp48a1_model_uut.v | ||||||
|  | test_dsp48a1_model | ||||||
| *.vcd | *.vcd | ||||||
|  |  | ||||||
							
								
								
									
										17
									
								
								techlibs/xilinx/tests/test_dsp48a1_model.sh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								techlibs/xilinx/tests/test_dsp48a1_model.sh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | #!/bin/bash | ||||||
|  | set -ex | ||||||
|  | if [ -z $ISE_DIR ]; then | ||||||
|  | 	ISE_DIR=/opt/Xilinx/ISE/14.7 | ||||||
|  | fi | ||||||
|  | sed 's/DSP48A1/MARKER1/; s/DSP48A/DSP48A_UUT/; s/MARKER1/DSP48A1_UUT/; /module DSP48A_UUT/,/endmodule/ p; /module DSP48A1_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp48a1_model_uut.v | ||||||
|  | if [ ! -f "test_dsp48a1_model_ref.v" ]; then | ||||||
|  | 	cp $ISE_DIR/ISE_DS/ISE/verilog/src/unisims/DSP48A1.v test_dsp48a1_model_ref.v | ||||||
|  | fi | ||||||
|  | if [ ! -f "test_dsp48a_model_ref.v" ]; then | ||||||
|  | 	cp $ISE_DIR/ISE_DS/ISE/verilog/src/unisims/DSP48A.v test_dsp48a_model_ref.v | ||||||
|  | fi | ||||||
|  | for tb in mult_allreg mult_noreg mult_inreg | ||||||
|  | do | ||||||
|  | 	iverilog -s $tb -s glbl -o test_dsp48a1_model test_dsp48a1_model.v test_dsp48a1_model_uut.v test_dsp48a1_model_ref.v test_dsp48a_model_ref.v $ISE_DIR/ISE_DS/ISE/verilog/src/glbl.v | ||||||
|  | 	vvp -N ./test_dsp48a1_model | ||||||
|  | done | ||||||
							
								
								
									
										331
									
								
								techlibs/xilinx/tests/test_dsp48a1_model.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								techlibs/xilinx/tests/test_dsp48a1_model.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,331 @@ | ||||||
|  | `timescale 1ns / 1ps | ||||||
|  | 
 | ||||||
|  | module testbench; | ||||||
|  | 	parameter integer A0REG = 1; | ||||||
|  | 	parameter integer A1REG = 1; | ||||||
|  | 	parameter integer B0REG = 1; | ||||||
|  | 	parameter integer B1REG = 1; | ||||||
|  | 	parameter integer CREG = 1; | ||||||
|  | 	parameter integer DREG = 1; | ||||||
|  | 	parameter integer MREG = 1; | ||||||
|  | 	parameter integer PREG = 1; | ||||||
|  | 	parameter integer CARRYINREG = 1; | ||||||
|  | 	parameter integer CARRYOUTREG = 1; | ||||||
|  | 	parameter integer OPMODEREG = 1; | ||||||
|  | 	parameter CARRYINSEL = "OPMODE5"; | ||||||
|  | 	parameter RSTTYPE = "SYNC"; | ||||||
|  | 
 | ||||||
|  | 	reg CLK; | ||||||
|  | 	reg CEA, CEB, CEC, CED, CEM, CEP, CECARRYIN, CEOPMODE; | ||||||
|  | 	reg RSTA, RSTB, RSTC, RSTD, RSTM, RSTP, RSTCARRYIN, RSTOPMODE; | ||||||
|  | 	reg [17:0] A; | ||||||
|  | 	reg [17:0] B; | ||||||
|  | 	reg [47:0] C; | ||||||
|  | 	reg [17:0] D; | ||||||
|  | 	reg [47:0] PCIN; | ||||||
|  | 	reg [7:0] OPMODE; | ||||||
|  | 	reg CARRYIN; | ||||||
|  | 
 | ||||||
|  | 	output CARRYOUTF, REF_CARRYOUTF; | ||||||
|  | 	output CARRYOUT, REF_CARRYOUT, REF_OLD_CARRYOUT; | ||||||
|  | 	output [35:0] M, REF_M; | ||||||
|  | 	output [47:0] P, REF_P, REF_OLD_P; | ||||||
|  | 	output [17:0] BCOUT, REF_BCOUT, REF_OLD_BCOUT; | ||||||
|  | 	output [47:0] PCOUT, REF_PCOUT, REF_OLD_PCOUT; | ||||||
|  | 
 | ||||||
|  | 	integer errcount = 0; | ||||||
|  | 
 | ||||||
|  | 	reg ERROR_FLAG = 0; | ||||||
|  | 
 | ||||||
|  | 	task clkcycle; | ||||||
|  | 		begin | ||||||
|  | 			#5; | ||||||
|  | 			CLK = ~CLK; | ||||||
|  | 			#10; | ||||||
|  | 			CLK = ~CLK; | ||||||
|  | 			#2; | ||||||
|  | 			ERROR_FLAG = 0; | ||||||
|  | 			if (REF_BCOUT !== BCOUT || REF_OLD_BCOUT != BCOUT) begin | ||||||
|  | 				$display("ERROR at %1t: REF_BCOUT=%b REF_OLD_BCOUT=%b UUT_BCOUT=%b DIFF=%b", $time, REF_BCOUT, REF_OLD_BCOUT, BCOUT, REF_BCOUT ^ BCOUT); | ||||||
|  | 				errcount = errcount + 1; | ||||||
|  | 				ERROR_FLAG = 1; | ||||||
|  | 			end | ||||||
|  | 			if (REF_M !== M) begin | ||||||
|  | 				$display("ERROR at %1t: REF_M=%b UUT_M=%b DIFF=%b", $time, REF_M, M, REF_M ^ M); | ||||||
|  | 				errcount = errcount + 1; | ||||||
|  | 				ERROR_FLAG = 1; | ||||||
|  | 			end | ||||||
|  | 			if (REF_P !== P || REF_OLD_P != P) begin | ||||||
|  | 				$display("ERROR at %1t: REF_P=%b REF_OLD_P=%b UUT_P=%b DIFF=%b", $time, REF_P, REF_OLD_P, P, REF_P ^ P); | ||||||
|  | 				errcount = errcount + 1; | ||||||
|  | 				ERROR_FLAG = 1; | ||||||
|  | 			end | ||||||
|  | 			if (REF_PCOUT !== PCOUT || REF_OLD_PCOUT != PCOUT) begin | ||||||
|  | 				$display("ERROR at %1t: REF_PCOUT=%b REF_OLD_PCOUT=%b UUT_PCOUT=%b DIFF=%b", $time, REF_PCOUT, REF_OLD_PCOUT, PCOUT, REF_PCOUT ^ PCOUT); | ||||||
|  | 				errcount = errcount + 1; | ||||||
|  | 				ERROR_FLAG = 1; | ||||||
|  | 			end | ||||||
|  | 			if (REF_CARRYOUT !== CARRYOUT || (REF_OLD_CARRYOUT != CARRYOUT && !CARRYOUTREG)) begin | ||||||
|  | 				$display("ERROR at %1t: REF_CARRYOUT=%b REF_OLD_CARRYOUT=%b UUT_CARRYOUT=%b DIFF=%b", $time, REF_CARRYOUT, REF_OLD_CARRYOUT, CARRYOUT, REF_CARRYOUT ^ CARRYOUT); | ||||||
|  | 				errcount = errcount + 1; | ||||||
|  | 				ERROR_FLAG = 1; | ||||||
|  | 			end | ||||||
|  | 			if (REF_CARRYOUTF !== CARRYOUTF) begin | ||||||
|  | 				$display("ERROR at %1t: REF_CARRYOUTF=%b UUT_CARRYOUTF=%b", $time, REF_CARRYOUTF, CARRYOUTF); | ||||||
|  | 				errcount = errcount + 1; | ||||||
|  | 				ERROR_FLAG = 1; | ||||||
|  | 			end | ||||||
|  | 			#3; | ||||||
|  | 		end | ||||||
|  | 	endtask | ||||||
|  | 
 | ||||||
|  | 	reg config_valid = 0; | ||||||
|  | 	task drc; | ||||||
|  | 		begin | ||||||
|  | 			config_valid = 1; | ||||||
|  | 
 | ||||||
|  | 			if (OPMODE[1:0] == 2'b10 && PREG != 1) config_valid = 0; | ||||||
|  | 			if (OPMODE[3:2] == 2'b10 && PREG != 1) config_valid = 0; | ||||||
|  | 		end | ||||||
|  | 	endtask | ||||||
|  | 
 | ||||||
|  | 	initial begin | ||||||
|  | 		$dumpfile("test_dsp48a1_model.vcd"); | ||||||
|  | 		$dumpvars(0, testbench); | ||||||
|  | 
 | ||||||
|  | 		#2; | ||||||
|  | 		CLK = 1'b0; | ||||||
|  | 		{CEA, CEB, CEC, CED, CEM, CEP, CECARRYIN, CEOPMODE} = 8'b11111111; | ||||||
|  | 		{A, B, C, D, PCIN, OPMODE, CARRYIN} = 0; | ||||||
|  | 		{RSTA, RSTB, RSTC, RSTD, RSTM, RSTP, RSTCARRYIN, RSTOPMODE} = 8'b11111111; | ||||||
|  | 		repeat (10) begin | ||||||
|  | 			#10; | ||||||
|  | 			CLK = 1'b1; | ||||||
|  | 			#10; | ||||||
|  | 			CLK = 1'b0; | ||||||
|  | 			#10; | ||||||
|  | 			CLK = 1'b1; | ||||||
|  | 			#10; | ||||||
|  | 			CLK = 1'b0; | ||||||
|  | 		end | ||||||
|  | 		{RSTA, RSTB, RSTC, RSTD, RSTM, RSTP, RSTCARRYIN, RSTOPMODE} = 0; | ||||||
|  | 
 | ||||||
|  | 		repeat (10000) begin | ||||||
|  | 			clkcycle; | ||||||
|  | 			config_valid = 0; | ||||||
|  | 			while (!config_valid) begin | ||||||
|  | 				A = $urandom; | ||||||
|  | 				B = $urandom; | ||||||
|  | 				C = {$urandom, $urandom}; | ||||||
|  | 				D = $urandom; | ||||||
|  | 				PCIN = {$urandom, $urandom}; | ||||||
|  | 
 | ||||||
|  | 				{CEA, CEB, CEC, CED, CEM, CEP, CECARRYIN, CEOPMODE} = $urandom | $urandom | $urandom; | ||||||
|  | 				{RSTA, RSTB, RSTC, RSTD, RSTM, RSTP, RSTCARRYIN, RSTOPMODE} = $urandom & $urandom & $urandom & $urandom & $urandom & $urandom; | ||||||
|  | 				{CARRYIN, OPMODE} = $urandom; | ||||||
|  | 
 | ||||||
|  | 				drc; | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 
 | ||||||
|  | 		if (errcount == 0) begin | ||||||
|  | 			$display("All tests passed."); | ||||||
|  | 			$finish; | ||||||
|  | 		end else begin | ||||||
|  | 			$display("Caught %1d errors.", errcount); | ||||||
|  | 			$stop; | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | 
 | ||||||
|  | 	DSP48A #( | ||||||
|  | 		.A0REG              (A0REG), | ||||||
|  | 		.A1REG              (A1REG), | ||||||
|  | 		.B0REG              (B0REG), | ||||||
|  | 		.B1REG              (B1REG), | ||||||
|  | 		.CREG               (CREG), | ||||||
|  | 		.DREG               (DREG), | ||||||
|  | 		.MREG               (MREG), | ||||||
|  | 		.PREG               (PREG), | ||||||
|  | 		.CARRYINREG         (CARRYINREG), | ||||||
|  | 		.OPMODEREG          (OPMODEREG), | ||||||
|  | 		.CARRYINSEL         (CARRYINSEL), | ||||||
|  | 		.RSTTYPE            (RSTTYPE) | ||||||
|  | 	) ref_old ( | ||||||
|  | 		.A             (A), | ||||||
|  | 		.B             (B), | ||||||
|  | 		.C             (C), | ||||||
|  | 		.D             (D), | ||||||
|  | 		.PCIN          (PCIN), | ||||||
|  | 		.CARRYIN       (CARRYIN), | ||||||
|  | 		.OPMODE        (OPMODE), | ||||||
|  | 		.BCOUT         (REF_OLD_BCOUT), | ||||||
|  | 		.CARRYOUT      (REF_OLD_CARRYOUT), | ||||||
|  | 		.P             (REF_OLD_P), | ||||||
|  | 		.PCOUT         (REF_OLD_PCOUT), | ||||||
|  | 		.CEA           (CEA), | ||||||
|  | 		.CEB           (CEB), | ||||||
|  | 		.CEC           (CEC), | ||||||
|  | 		.CED           (CED), | ||||||
|  | 		.CEM           (CEM), | ||||||
|  | 		.CEP           (CEP), | ||||||
|  | 		.CECARRYIN     (CECARRYIN), | ||||||
|  | 		.CEOPMODE      (CEOPMODE), | ||||||
|  | 		.CLK           (CLK), | ||||||
|  | 		.RSTA          (RSTA), | ||||||
|  | 		.RSTB          (RSTB), | ||||||
|  | 		.RSTC          (RSTC), | ||||||
|  | 		.RSTD          (RSTD), | ||||||
|  | 		.RSTM          (RSTM), | ||||||
|  | 		.RSTP          (RSTP), | ||||||
|  | 		.RSTCARRYIN    (RSTCARRYIN), | ||||||
|  | 		.RSTOPMODE     (RSTOPMODE) | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	DSP48A1 #( | ||||||
|  | 		.A0REG              (A0REG), | ||||||
|  | 		.A1REG              (A1REG), | ||||||
|  | 		.B0REG              (B0REG), | ||||||
|  | 		.B1REG              (B1REG), | ||||||
|  | 		.CREG               (CREG), | ||||||
|  | 		.DREG               (DREG), | ||||||
|  | 		.MREG               (MREG), | ||||||
|  | 		.PREG               (PREG), | ||||||
|  | 		.CARRYINREG         (CARRYINREG), | ||||||
|  | 		.CARRYOUTREG        (CARRYOUTREG), | ||||||
|  | 		.OPMODEREG          (OPMODEREG), | ||||||
|  | 		.CARRYINSEL         (CARRYINSEL), | ||||||
|  | 		.RSTTYPE            (RSTTYPE) | ||||||
|  | 	) ref ( | ||||||
|  | 		.A             (A), | ||||||
|  | 		.B             (B), | ||||||
|  | 		.C             (C), | ||||||
|  | 		.D             (D), | ||||||
|  | 		.PCIN          (PCIN), | ||||||
|  | 		.CARRYIN       (CARRYIN), | ||||||
|  | 		.OPMODE        (OPMODE), | ||||||
|  | 		.BCOUT         (REF_BCOUT), | ||||||
|  | 		.CARRYOUTF     (REF_CARRYOUTF), | ||||||
|  | 		.CARRYOUT      (REF_CARRYOUT), | ||||||
|  | 		.P             (REF_P), | ||||||
|  | 		.M             (REF_M), | ||||||
|  | 		.PCOUT         (REF_PCOUT), | ||||||
|  | 		.CEA           (CEA), | ||||||
|  | 		.CEB           (CEB), | ||||||
|  | 		.CEC           (CEC), | ||||||
|  | 		.CED           (CED), | ||||||
|  | 		.CEM           (CEM), | ||||||
|  | 		.CEP           (CEP), | ||||||
|  | 		.CECARRYIN     (CECARRYIN), | ||||||
|  | 		.CEOPMODE      (CEOPMODE), | ||||||
|  | 		.CLK           (CLK), | ||||||
|  | 		.RSTA          (RSTA), | ||||||
|  | 		.RSTB          (RSTB), | ||||||
|  | 		.RSTC          (RSTC), | ||||||
|  | 		.RSTD          (RSTD), | ||||||
|  | 		.RSTM          (RSTM), | ||||||
|  | 		.RSTP          (RSTP), | ||||||
|  | 		.RSTCARRYIN    (RSTCARRYIN), | ||||||
|  | 		.RSTOPMODE     (RSTOPMODE) | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	DSP48A1_UUT #( | ||||||
|  | 		.A0REG              (A0REG), | ||||||
|  | 		.A1REG              (A1REG), | ||||||
|  | 		.B0REG              (B0REG), | ||||||
|  | 		.B1REG              (B1REG), | ||||||
|  | 		.CREG               (CREG), | ||||||
|  | 		.DREG               (DREG), | ||||||
|  | 		.MREG               (MREG), | ||||||
|  | 		.PREG               (PREG), | ||||||
|  | 		.CARRYINREG         (CARRYINREG), | ||||||
|  | 		.CARRYOUTREG        (CARRYOUTREG), | ||||||
|  | 		.OPMODEREG          (OPMODEREG), | ||||||
|  | 		.CARRYINSEL         (CARRYINSEL), | ||||||
|  | 		.RSTTYPE            (RSTTYPE) | ||||||
|  | 	) uut ( | ||||||
|  | 		.A             (A), | ||||||
|  | 		.B             (B), | ||||||
|  | 		.C             (C), | ||||||
|  | 		.D             (D), | ||||||
|  | 		.PCIN          (PCIN), | ||||||
|  | 		.CARRYIN       (CARRYIN), | ||||||
|  | 		.OPMODE        (OPMODE), | ||||||
|  | 		.BCOUT         (BCOUT), | ||||||
|  | 		.CARRYOUTF     (CARRYOUTF), | ||||||
|  | 		.CARRYOUT      (CARRYOUT), | ||||||
|  | 		.P             (P), | ||||||
|  | 		.M             (M), | ||||||
|  | 		.PCOUT         (PCOUT), | ||||||
|  | 		.CEA           (CEA), | ||||||
|  | 		.CEB           (CEB), | ||||||
|  | 		.CEC           (CEC), | ||||||
|  | 		.CED           (CED), | ||||||
|  | 		.CEM           (CEM), | ||||||
|  | 		.CEP           (CEP), | ||||||
|  | 		.CECARRYIN     (CECARRYIN), | ||||||
|  | 		.CEOPMODE      (CEOPMODE), | ||||||
|  | 		.CLK           (CLK), | ||||||
|  | 		.RSTA          (RSTA), | ||||||
|  | 		.RSTB          (RSTB), | ||||||
|  | 		.RSTC          (RSTC), | ||||||
|  | 		.RSTD          (RSTD), | ||||||
|  | 		.RSTM          (RSTM), | ||||||
|  | 		.RSTP          (RSTP), | ||||||
|  | 		.RSTCARRYIN    (RSTCARRYIN), | ||||||
|  | 		.RSTOPMODE     (RSTOPMODE) | ||||||
|  | 	); | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | module mult_noreg; | ||||||
|  | 	testbench #( | ||||||
|  | 		.A0REG              (0), | ||||||
|  | 		.A1REG              (0), | ||||||
|  | 		.B0REG              (0), | ||||||
|  | 		.B1REG              (0), | ||||||
|  | 		.CREG               (0), | ||||||
|  | 		.DREG               (0), | ||||||
|  | 		.MREG               (0), | ||||||
|  | 		.PREG               (0), | ||||||
|  | 		.CARRYINREG         (0), | ||||||
|  | 		.CARRYOUTREG        (0), | ||||||
|  | 		.OPMODEREG          (0), | ||||||
|  | 		.CARRYINSEL         ("CARRYIN"), | ||||||
|  | 		.RSTTYPE            ("SYNC") | ||||||
|  | 	) testbench (); | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | module mult_allreg; | ||||||
|  | 	testbench #( | ||||||
|  | 		.A0REG              (1), | ||||||
|  | 		.A1REG              (1), | ||||||
|  | 		.B0REG              (1), | ||||||
|  | 		.B1REG              (1), | ||||||
|  | 		.CREG               (1), | ||||||
|  | 		.DREG               (1), | ||||||
|  | 		.MREG               (1), | ||||||
|  | 		.PREG               (1), | ||||||
|  | 		.CARRYINREG         (1), | ||||||
|  | 		.CARRYOUTREG        (1), | ||||||
|  | 		.OPMODEREG          (1), | ||||||
|  | 		.CARRYINSEL         ("OPMODE5"), | ||||||
|  | 		.RSTTYPE            ("SYNC") | ||||||
|  | 	) testbench (); | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | module mult_inreg; | ||||||
|  | 	testbench #( | ||||||
|  | 		.A0REG              (1), | ||||||
|  | 		.A1REG              (1), | ||||||
|  | 		.B0REG              (1), | ||||||
|  | 		.B1REG              (1), | ||||||
|  | 		.CREG               (1), | ||||||
|  | 		.DREG               (1), | ||||||
|  | 		.MREG               (0), | ||||||
|  | 		.PREG               (0), | ||||||
|  | 		.CARRYINREG         (1), | ||||||
|  | 		.CARRYOUTREG        (0), | ||||||
|  | 		.OPMODEREG          (0), | ||||||
|  | 		.CARRYINSEL         ("CARRYIN"), | ||||||
|  | 		.RSTTYPE            ("SYNC") | ||||||
|  | 	) testbench (); | ||||||
|  | endmodule | ||||||
|  | @ -1,14 +1,17 @@ | ||||||
| #!/bin/bash | #!/bin/bash | ||||||
| set -ex | set -ex | ||||||
|  | if [ -z $VIVADO_DIR ]; then | ||||||
|  | 	VIVADO_DIR=/opt/Xilinx/Vivado/2019.1 | ||||||
|  | fi | ||||||
| sed 's/DSP48E1/DSP48E1_UUT/; /DSP48E1_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v | sed 's/DSP48E1/DSP48E1_UUT/; /DSP48E1_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v | ||||||
| if [ ! -f "test_dsp_model_ref.v" ]; then | if [ ! -f "test_dsp_model_ref.v" ]; then | ||||||
| 	cat /opt/Xilinx/Vivado/2019.1/data/verilog/src/unisims/DSP48E1.v > test_dsp_model_ref.v | 	cp $VIVADO_DIR/data/verilog/src/unisims/DSP48E1.v test_dsp_model_ref.v | ||||||
| fi | fi | ||||||
| for tb in macc_overflow_underflow \ | for tb in macc_overflow_underflow \ | ||||||
|     simd24_preadd_noreg_nocasc simd12_preadd_noreg_nocasc \ | 	simd24_preadd_noreg_nocasc simd12_preadd_noreg_nocasc \ | ||||||
|     mult_allreg_nopreadd_nocasc mult_noreg_nopreadd_nocasc  \ | 	 mult_allreg_nopreadd_nocasc mult_noreg_nopreadd_nocasc  \ | ||||||
| 	mult_allreg_preadd_nocasc mult_noreg_preadd_nocasc mult_inreg_preadd_nocasc | 	mult_allreg_preadd_nocasc mult_noreg_preadd_nocasc mult_inreg_preadd_nocasc | ||||||
| do | do | ||||||
| 	iverilog -s $tb -s glbl -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v /opt/Xilinx/Vivado/2019.1/data/verilog/src/glbl.v | 	iverilog -s $tb -s glbl -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v $VIVADO_DIR/data/verilog/src/glbl.v | ||||||
| 	vvp -N ./test_dsp_model | 	vvp -N ./test_dsp_model | ||||||
| done | done | ||||||
|  |  | ||||||
|  | @ -27,7 +27,7 @@ module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y); | ||||||
| 		.D(18'b0), | 		.D(18'b0), | ||||||
| 		.P(P_48), | 		.P(P_48), | ||||||
| 
 | 
 | ||||||
| 		.OPMODE(8'b0000010) | 		.OPMODE(8'b0000001) | ||||||
| 	); | 	); | ||||||
| 	assign Y = P_48; | 	assign Y = P_48; | ||||||
| endmodule | endmodule | ||||||
|  |  | ||||||
|  | @ -27,7 +27,7 @@ module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y); | ||||||
| 		.D(18'b0), | 		.D(18'b0), | ||||||
| 		.P(P_48), | 		.P(P_48), | ||||||
| 
 | 
 | ||||||
| 		.OPMODE(8'b0000010) | 		.OPMODE(8'b0000001) | ||||||
| 	); | 	); | ||||||
| 	assign Y = P_48; | 	assign Y = P_48; | ||||||
| endmodule | endmodule | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								tests/arch/ecp5/bug1598.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/arch/ecp5/bug1598.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | read_verilog <<EOT | ||||||
|  | module led_blink ( | ||||||
|  |         input clk, | ||||||
|  |         output ledc | ||||||
|  |     ); | ||||||
|  |   | ||||||
|  |     reg [6:0] led_counter = 0; | ||||||
|  |     always @( posedge clk ) begin | ||||||
|  |             led_counter <= led_counter + 1; | ||||||
|  |     end | ||||||
|  |     assign ledc = !led_counter[ 6:3 ]; | ||||||
|  |   | ||||||
|  | endmodule | ||||||
|  | EOT | ||||||
|  | proc | ||||||
|  | equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5 -abc9 | ||||||
|  | @ -39,8 +39,8 @@ proc | ||||||
| equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5 # equivalency check | equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5 # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd mux16 # Constrain all select calls below inside the top module | cd mux16 # Constrain all select calls below inside the top module | ||||||
| select -assert-count 8 t:L6MUX21 | select -assert-count 12 t:L6MUX21 | ||||||
| select -assert-count 26 t:LUT4 | select -assert-count 34 t:LUT4 | ||||||
| select -assert-count 12 t:PFUMX | select -assert-count 17 t:PFUMX | ||||||
| 
 | 
 | ||||||
| select -assert-none t:LUT4 t:L6MUX21 t:PFUMX %% t:* %D | select -assert-none t:LUT4 t:L6MUX21 t:PFUMX %% t:* %D | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								tests/arch/ice40/bug1598.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/arch/ice40/bug1598.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | read_verilog <<EOT | ||||||
|  | module led_blink ( | ||||||
|  |         input clk, | ||||||
|  |         output ledc | ||||||
|  |     ); | ||||||
|  |   | ||||||
|  |     reg [6:0] led_counter = 0; | ||||||
|  |     always @( posedge clk ) begin | ||||||
|  |             led_counter <= led_counter + 1; | ||||||
|  |     end | ||||||
|  |     assign ledc = !led_counter[ 6:3 ]; | ||||||
|  |   | ||||||
|  | endmodule | ||||||
|  | EOT | ||||||
|  | proc | ||||||
|  | equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 -abc9 | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| read_verilog ../common/add_sub.v | read_verilog ../common/add_sub.v | ||||||
| hierarchy -top top | hierarchy -top top | ||||||
| proc | proc | ||||||
| equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd top # Constrain all select calls below inside the top module | cd top # Constrain all select calls below inside the top module | ||||||
| select -assert-count 14 t:LUT2 | select -assert-count 14 t:LUT2 | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ design -save read | ||||||
| 
 | 
 | ||||||
| hierarchy -top adff | hierarchy -top adff | ||||||
| proc | proc | ||||||
| equiv_opt -async2sync  -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -async2sync  -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd adff # Constrain all select calls below inside the top module | cd adff # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:BUFG | select -assert-count 1 t:BUFG | ||||||
|  | @ -15,7 +15,7 @@ select -assert-none t:BUFG t:FDCE %% t:* %D | ||||||
| design -load read | design -load read | ||||||
| hierarchy -top adffn | hierarchy -top adffn | ||||||
| proc | proc | ||||||
| equiv_opt -async2sync  -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -async2sync  -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd adffn # Constrain all select calls below inside the top module | cd adffn # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:BUFG | select -assert-count 1 t:BUFG | ||||||
|  | @ -28,7 +28,7 @@ select -assert-none t:BUFG t:FDCE t:INV %% t:* %D | ||||||
| design -load read | design -load read | ||||||
| hierarchy -top dffs | hierarchy -top dffs | ||||||
| proc | proc | ||||||
| equiv_opt -async2sync  -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -async2sync  -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd dffs # Constrain all select calls below inside the top module | cd dffs # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:BUFG | select -assert-count 1 t:BUFG | ||||||
|  | @ -40,7 +40,7 @@ select -assert-none t:BUFG t:FDSE %% t:* %D | ||||||
| design -load read | design -load read | ||||||
| hierarchy -top ndffnr | hierarchy -top ndffnr | ||||||
| proc | proc | ||||||
| equiv_opt -async2sync  -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -async2sync  -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd ndffnr # Constrain all select calls below inside the top module | cd ndffnr # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:BUFG | select -assert-count 1 t:BUFG | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| # Check that blockram memory without parameters is not modified | # Check that blockram memory without parameters is not modified | ||||||
| read_verilog ../common/memory_attributes/attributes_test.v | read_verilog ../common/memory_attributes/attributes_test.v | ||||||
| hierarchy -top block_ram | hierarchy -top block_ram | ||||||
| synth_xilinx -top block_ram | synth_xilinx -top block_ram -noiopad | ||||||
| cd block_ram # Constrain all select calls below inside the top module | cd block_ram # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:RAMB18E1 | select -assert-count 1 t:RAMB18E1 | ||||||
|   |   | ||||||
|  | @ -9,7 +9,7 @@ select -assert-count 1 t:RAMB18E1 | ||||||
| design -reset | design -reset | ||||||
| read_verilog ../common/memory_attributes/attributes_test.v | read_verilog ../common/memory_attributes/attributes_test.v | ||||||
| hierarchy -top distributed_ram | hierarchy -top distributed_ram | ||||||
| synth_xilinx -top distributed_ram | synth_xilinx -top distributed_ram -noiopad | ||||||
| cd distributed_ram # Constrain all select calls below inside the top module | cd distributed_ram # Constrain all select calls below inside the top module | ||||||
| select -assert-count 8 t:RAM32X1D | select -assert-count 8 t:RAM32X1D | ||||||
|   |   | ||||||
|  | @ -18,7 +18,7 @@ design -reset | ||||||
| read_verilog ../common/memory_attributes/attributes_test.v | read_verilog ../common/memory_attributes/attributes_test.v | ||||||
| prep | prep | ||||||
| setattr -mod -set ram_style "distributed" block_ram | setattr -mod -set ram_style "distributed" block_ram | ||||||
| synth_xilinx -top block_ram | synth_xilinx -top block_ram -noiopad | ||||||
| cd block_ram # Constrain all select calls below inside the top module | cd block_ram # Constrain all select calls below inside the top module | ||||||
| select -assert-count 32 t:RAM128X1D | select -assert-count 32 t:RAM128X1D | ||||||
|   |   | ||||||
|  | @ -27,7 +27,7 @@ design -reset | ||||||
| read_verilog ../common/memory_attributes/attributes_test.v | read_verilog ../common/memory_attributes/attributes_test.v | ||||||
| prep | prep | ||||||
| setattr -mod -set logic_block 1 block_ram | setattr -mod -set logic_block 1 block_ram | ||||||
| synth_xilinx -top block_ram | synth_xilinx -top block_ram -noiopad | ||||||
| cd block_ram # Constrain all select calls below inside the top module | cd block_ram # Constrain all select calls below inside the top module | ||||||
| select -assert-count 0 t:RAMB18E1 | select -assert-count 0 t:RAMB18E1 | ||||||
| select -assert-count 32 t:RAM128X1D | select -assert-count 32 t:RAM128X1D | ||||||
|  | @ -35,13 +35,13 @@ select -assert-count 32 t:RAM128X1D | ||||||
| # Set ram_style block to a distributed memory; will be implemented as blockram | # Set ram_style block to a distributed memory; will be implemented as blockram | ||||||
| design -reset | design -reset | ||||||
| read_verilog ../common/memory_attributes/attributes_test.v | read_verilog ../common/memory_attributes/attributes_test.v | ||||||
| synth_xilinx -top distributed_ram_manual | synth_xilinx -top distributed_ram_manual -noiopad | ||||||
| cd distributed_ram_manual # Constrain all select calls below inside the top module | cd distributed_ram_manual # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:RAMB18E1 | select -assert-count 1 t:RAMB18E1 | ||||||
|   |   | ||||||
| # Set synthesis, ram_block block to a distributed memory; will be implemented as blockram | # Set synthesis, ram_block block to a distributed memory; will be implemented as blockram | ||||||
| design -reset | design -reset | ||||||
| read_verilog ../common/memory_attributes/attributes_test.v | read_verilog ../common/memory_attributes/attributes_test.v | ||||||
| synth_xilinx -top distributed_ram_manual_syn | synth_xilinx -top distributed_ram_manual_syn -noiopad | ||||||
| cd distributed_ram_manual_syn # Constrain all select calls below inside the top module | cd distributed_ram_manual_syn # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:RAMB18E1 | select -assert-count 1 t:RAMB18E1 | ||||||
|  |  | ||||||
|  | @ -3,28 +3,28 @@ | ||||||
| # Memory bits <= 18K; Data width <= 36; Address width <= 14: -> RAMB18E1 | # Memory bits <= 18K; Data width <= 36; Address width <= 14: -> RAMB18E1 | ||||||
| read_verilog ../common/blockram.v | read_verilog ../common/blockram.v | ||||||
| chparam -set ADDRESS_WIDTH 10 -set DATA_WIDTH 1 sync_ram_sdp | chparam -set ADDRESS_WIDTH 10 -set DATA_WIDTH 1 sync_ram_sdp | ||||||
| synth_xilinx -top sync_ram_sdp | synth_xilinx -top sync_ram_sdp -noiopad | ||||||
| cd sync_ram_sdp | cd sync_ram_sdp | ||||||
| select -assert-count 1 t:RAMB18E1 | select -assert-count 1 t:RAMB18E1 | ||||||
| 
 | 
 | ||||||
| design -reset | design -reset | ||||||
| read_verilog ../common/blockram.v | read_verilog ../common/blockram.v | ||||||
| chparam -set ADDRESS_WIDTH 8 -set DATA_WIDTH 18 sync_ram_sdp | chparam -set ADDRESS_WIDTH 8 -set DATA_WIDTH 18 sync_ram_sdp | ||||||
| synth_xilinx -top sync_ram_sdp | synth_xilinx -top sync_ram_sdp -noiopad | ||||||
| cd sync_ram_sdp | cd sync_ram_sdp | ||||||
| select -assert-count 1 t:RAMB18E1 | select -assert-count 1 t:RAMB18E1 | ||||||
| 
 | 
 | ||||||
| design -reset | design -reset | ||||||
| read_verilog ../common/blockram.v | read_verilog ../common/blockram.v | ||||||
| chparam -set ADDRESS_WIDTH 14 -set DATA_WIDTH 1 sync_ram_sdp | chparam -set ADDRESS_WIDTH 14 -set DATA_WIDTH 1 sync_ram_sdp | ||||||
| synth_xilinx -top sync_ram_sdp | synth_xilinx -top sync_ram_sdp -noiopad | ||||||
| cd sync_ram_sdp | cd sync_ram_sdp | ||||||
| select -assert-count 1 t:RAMB18E1 | select -assert-count 1 t:RAMB18E1 | ||||||
| 
 | 
 | ||||||
| design -reset | design -reset | ||||||
| read_verilog ../common/blockram.v | read_verilog ../common/blockram.v | ||||||
| chparam -set ADDRESS_WIDTH 9 -set DATA_WIDTH 36 sync_ram_sdp | chparam -set ADDRESS_WIDTH 9 -set DATA_WIDTH 36 sync_ram_sdp | ||||||
| synth_xilinx -top sync_ram_sdp | synth_xilinx -top sync_ram_sdp -noiopad | ||||||
| cd sync_ram_sdp | cd sync_ram_sdp | ||||||
| select -assert-count 1 t:RAMB18E1 | select -assert-count 1 t:RAMB18E1 | ||||||
| 
 | 
 | ||||||
|  | @ -32,7 +32,7 @@ select -assert-count 1 t:RAMB18E1 | ||||||
| design -reset | design -reset | ||||||
| read_verilog ../common/blockram.v | read_verilog ../common/blockram.v | ||||||
| chparam -set ADDRESS_WIDTH 8 -set DATA_WIDTH 2 sync_ram_sdp | chparam -set ADDRESS_WIDTH 8 -set DATA_WIDTH 2 sync_ram_sdp | ||||||
| synth_xilinx -top sync_ram_sdp | synth_xilinx -top sync_ram_sdp -noiopad | ||||||
| cd sync_ram_sdp | cd sync_ram_sdp | ||||||
| select -assert-count 0 t:RAMB18E1 | select -assert-count 0 t:RAMB18E1 | ||||||
| select -assert-count 4 t:RAM128X1D | select -assert-count 4 t:RAM128X1D | ||||||
|  | @ -41,7 +41,7 @@ select -assert-count 4 t:RAM128X1D | ||||||
| design -reset | design -reset | ||||||
| read_verilog ../common/blockram.v | read_verilog ../common/blockram.v | ||||||
| chparam -set ADDRESS_WIDTH 10 -set DATA_WIDTH 36 sync_ram_sdp | chparam -set ADDRESS_WIDTH 10 -set DATA_WIDTH 36 sync_ram_sdp | ||||||
| synth_xilinx -top sync_ram_sdp | synth_xilinx -top sync_ram_sdp -noiopad | ||||||
| cd sync_ram_sdp | cd sync_ram_sdp | ||||||
| select -assert-count 1 t:RAMB36E1 | select -assert-count 1 t:RAMB36E1 | ||||||
| 
 | 
 | ||||||
|  | @ -52,7 +52,7 @@ design -reset | ||||||
| read_verilog ../common/blockram.v | read_verilog ../common/blockram.v | ||||||
| hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1  | hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1  | ||||||
| setattr -set ram_style "block" m:memory | setattr -set ram_style "block" m:memory | ||||||
| synth_xilinx -top sync_ram_sdp | synth_xilinx -top sync_ram_sdp -noiopad | ||||||
| cd sync_ram_sdp | cd sync_ram_sdp | ||||||
| select -assert-count 1 t:RAMB18E1 | select -assert-count 1 t:RAMB18E1 | ||||||
| 
 | 
 | ||||||
|  | @ -60,7 +60,7 @@ design -reset | ||||||
| read_verilog ../common/blockram.v | read_verilog ../common/blockram.v | ||||||
| hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1  | hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1  | ||||||
| setattr -set ram_block 1 m:memory | setattr -set ram_block 1 m:memory | ||||||
| synth_xilinx -top sync_ram_sdp | synth_xilinx -top sync_ram_sdp -noiopad | ||||||
| cd sync_ram_sdp | cd sync_ram_sdp | ||||||
| select -assert-count 1 t:RAMB18E1 | select -assert-count 1 t:RAMB18E1 | ||||||
| 
 | 
 | ||||||
|  | @ -68,7 +68,7 @@ design -reset | ||||||
| read_verilog ../common/blockram.v | read_verilog ../common/blockram.v | ||||||
| hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1  | hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1  | ||||||
| setattr -set ram_style "dont_infer_a_ram_pretty_please" m:memory | setattr -set ram_style "dont_infer_a_ram_pretty_please" m:memory | ||||||
| synth_xilinx -top sync_ram_sdp | synth_xilinx -top sync_ram_sdp -noiopad | ||||||
| cd sync_ram_sdp | cd sync_ram_sdp | ||||||
| select -assert-count 0 t:RAMB18E1 | select -assert-count 0 t:RAMB18E1 | ||||||
| 
 | 
 | ||||||
|  | @ -76,7 +76,7 @@ design -reset | ||||||
| read_verilog ../common/blockram.v | read_verilog ../common/blockram.v | ||||||
| hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1  | hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1  | ||||||
| setattr -set logic_block 1 m:memory | setattr -set logic_block 1 m:memory | ||||||
| synth_xilinx -top sync_ram_sdp | synth_xilinx -top sync_ram_sdp -noiopad | ||||||
| cd sync_ram_sdp | cd sync_ram_sdp | ||||||
| select -assert-count 0 t:RAMB18E1 | select -assert-count 0 t:RAMB18E1 | ||||||
| 
 | 
 | ||||||
|  | @ -84,7 +84,7 @@ design -reset | ||||||
| read_verilog ../common/blockram.v | read_verilog ../common/blockram.v | ||||||
| hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 8 -chparam DATA_WIDTH 1 | hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 8 -chparam DATA_WIDTH 1 | ||||||
| setattr -set ram_style "block" m:memory | setattr -set ram_style "block" m:memory | ||||||
| synth_xilinx -top sync_ram_sdp | synth_xilinx -top sync_ram_sdp -noiopad | ||||||
| cd sync_ram_sdp | cd sync_ram_sdp | ||||||
| select -assert-count 1 t:RAMB18E1 | select -assert-count 1 t:RAMB18E1 | ||||||
| 
 | 
 | ||||||
|  | @ -92,6 +92,6 @@ design -reset | ||||||
| read_verilog ../common/blockram.v | read_verilog ../common/blockram.v | ||||||
| hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 8 -chparam DATA_WIDTH 1 | hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 8 -chparam DATA_WIDTH 1 | ||||||
| setattr -set ram_block 1 m:memory | setattr -set ram_block 1 m:memory | ||||||
| synth_xilinx -top sync_ram_sdp | synth_xilinx -top sync_ram_sdp -noiopad | ||||||
| cd sync_ram_sdp | cd sync_ram_sdp | ||||||
| select -assert-count 1 t:RAMB18E1 | select -assert-count 1 t:RAMB18E1 | ||||||
|  |  | ||||||
|  | @ -28,7 +28,7 @@ module register_file( | ||||||
| endmodule | endmodule | ||||||
| EOT | EOT | ||||||
| 
 | 
 | ||||||
| synth_xilinx | synth_xilinx -noiopad | ||||||
| cd register_file | cd register_file | ||||||
| select -assert-count 32 t:RAM32M | select -assert-count 32 t:RAM32M | ||||||
| select -assert-none t:* t:BUFG %d t:RAM32M %d | select -assert-none t:* t:BUFG %d t:RAM32M %d | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								tests/arch/xilinx/bug1598.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/arch/xilinx/bug1598.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | read_verilog <<EOT | ||||||
|  | module led_blink ( | ||||||
|  |         input clk, | ||||||
|  |         output ledc | ||||||
|  |     ); | ||||||
|  |   | ||||||
|  |     reg [6:0] led_counter = 0; | ||||||
|  |     always @( posedge clk ) begin | ||||||
|  |             led_counter <= led_counter + 1; | ||||||
|  |     end | ||||||
|  |     assign ledc = !led_counter[ 6:3 ]; | ||||||
|  |   | ||||||
|  | endmodule | ||||||
|  | EOT | ||||||
|  | proc | ||||||
|  | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -abc9 | ||||||
|  | @ -2,7 +2,7 @@ read_verilog ../common/counter.v | ||||||
| hierarchy -top top | hierarchy -top top | ||||||
| proc | proc | ||||||
| flatten | flatten | ||||||
| equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd top # Constrain all select calls below inside the top module | cd top # Constrain all select calls below inside the top module | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ design -save read | ||||||
| 
 | 
 | ||||||
| hierarchy -top dff | hierarchy -top dff | ||||||
| proc | proc | ||||||
| equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd dff # Constrain all select calls below inside the top module | cd dff # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:BUFG | select -assert-count 1 t:BUFG | ||||||
|  | @ -15,7 +15,7 @@ select -assert-none t:BUFG t:FDRE %% t:* %D | ||||||
| design -load read | design -load read | ||||||
| hierarchy -top dffe | hierarchy -top dffe | ||||||
| proc | proc | ||||||
| equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd dffe # Constrain all select calls below inside the top module | cd dffe # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:BUFG | select -assert-count 1 t:BUFG | ||||||
|  |  | ||||||
							
								
								
									
										89
									
								
								tests/arch/xilinx/dsp_cascade.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								tests/arch/xilinx/dsp_cascade.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,89 @@ | ||||||
|  | design -reset | ||||||
|  | read_verilog <<EOT | ||||||
|  | module cascade(input clk, input [4:0] a, input [4:0] b, output reg [9:0] o); | ||||||
|  | reg [4:0] ar1, ar2, ar3, br1, br2, br3; | ||||||
|  | reg [9:0] m, n; | ||||||
|  | always @(posedge clk) begin | ||||||
|  | ar1 <= a; | ||||||
|  | ar2 <= ar1; | ||||||
|  | ar3 <= ar2; | ||||||
|  | br1 <= b; | ||||||
|  | br2 <= br1; | ||||||
|  | br3 <= br2; | ||||||
|  | m <= ar1 * br1; | ||||||
|  | n <= ar2 * br2 + m; | ||||||
|  | o <= ar3 * br3 + n; | ||||||
|  | end | ||||||
|  | endmodule | ||||||
|  | EOT | ||||||
|  | proc | ||||||
|  | design -save read | ||||||
|  | 
 | ||||||
|  | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||||
|  | design -load postopt | ||||||
|  | cd cascade | ||||||
|  | select -assert-count 3 t:DSP48E1 | ||||||
|  | select -assert-none t:DSP48E1 t:BUFG %% t:* %D | ||||||
|  | # Very crude method of checking that DSP48E1.PCOUT -> DSP48E1.PCIN | ||||||
|  | # (i.e. Take all DSP48E1s, expand to find all wires connected | ||||||
|  | #       to its PCOUT port, then remove all DSP48E1s from this | ||||||
|  | #       selection, then expand again to find all cells where | ||||||
|  | #       those wires are connected to the PCIN port, then remove | ||||||
|  | #       all wires from this selection, and lastly intersect | ||||||
|  | #       this selection with all DSP48E1 cells (to check that | ||||||
|  | #       the connected cells are indeed DSPs) | ||||||
|  | select -assert-count 2 t:DSP48E1 %co:+[PCOUT] t:DSP48E1 %d %co:+[PCIN] w:* %d t:DSP48E1 %i | ||||||
|  | 
 | ||||||
|  | design -load read | ||||||
|  | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc6s -noiopad | ||||||
|  | design -load postopt | ||||||
|  | cd cascade | ||||||
|  | select -assert-count 3 t:DSP48A1 | ||||||
|  | select -assert-count 5 t:FDRE # No cascade for A input | ||||||
|  | select -assert-none t:DSP48A1 t:BUFG t:FDRE %% t:* %D | ||||||
|  | # Very crude method of checking that DSP48E1.PCOUT -> DSP48E1.PCIN | ||||||
|  | #   (see above for explanation) | ||||||
|  | select -assert-count 2 t:DSP48A1 %co:+[PCOUT] t:DSP48A1 %d %co:+[PCIN] w:* %d t:DSP48A1 %i | ||||||
|  | 
 | ||||||
|  | design -reset | ||||||
|  | read_verilog <<EOT | ||||||
|  | module cascade(input clk, input [4:0] a, input [4:0] b, output reg [9:0] o); | ||||||
|  | reg [4:0] ar1, ar2, ar3, br1, br2, br3; | ||||||
|  | reg [9:0] m; | ||||||
|  | always @(posedge clk) begin | ||||||
|  | ar1 <= a; | ||||||
|  | ar2 <= ar1; | ||||||
|  | ar3 <= ar2; | ||||||
|  | br1 <= b; | ||||||
|  | br2 <= br1; | ||||||
|  | br3 <= br2; | ||||||
|  | m <= ar2 * br2; | ||||||
|  | o <= ar3 * br3 + m; | ||||||
|  | end | ||||||
|  | endmodule | ||||||
|  | EOT | ||||||
|  | proc | ||||||
|  | design -save read | ||||||
|  | 
 | ||||||
|  | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||||
|  | design -load postopt | ||||||
|  | cd cascade | ||||||
|  | select -assert-count 2 t:DSP48E1 | ||||||
|  | select -assert-none t:DSP48E1 t:BUFG %% t:* %D | ||||||
|  | # Very crude method of checking that DSP48E1.PCOUT -> DSP48E1.PCIN | ||||||
|  | #   (see above for explanation) | ||||||
|  | select -assert-count 1 t:DSP48E1 %co:+[PCOUT] t:DSP48E1 %d %co:+[PCIN] w:* %d t:DSP48E1 %i | ||||||
|  | 
 | ||||||
|  | design -load read | ||||||
|  | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc6s -noiopad | ||||||
|  | design -load postopt | ||||||
|  | cd cascade | ||||||
|  | select -assert-count 2 t:DSP48A1 | ||||||
|  | select -assert-count 10 t:FDRE # Cannot cascade because first 'm' DSP | ||||||
|  |                                # uses both B0REG and B1REG, whereas 'o' | ||||||
|  |                                # only requires 1 | ||||||
|  | select -assert-none t:DSP48A1 t:BUFG t:FDRE %% t:* %D | ||||||
|  | # Very crude method of checking that DSP48E1.PCOUT -> DSP48E1.PCIN | ||||||
|  | #   (see above for explanation) | ||||||
|  | select -assert-count 1 t:DSP48A1 %co:+[PCOUT] t:DSP48A1 %d %co:+[PCIN] w:* %d t:DSP48A1 %i | ||||||
|  | 
 | ||||||
|  | @ -63,7 +63,7 @@ module fastfir_dynamictaps(i_clk, i_reset, i_tap_wr, i_tap, i_ce, i_sample, o_re | ||||||
| endmodule | endmodule | ||||||
| EOT | EOT | ||||||
| 
 | 
 | ||||||
| synth_xilinx | synth_xilinx -noiopad | ||||||
| cd fastfir_dynamictaps | cd fastfir_dynamictaps | ||||||
| select -assert-count 2 t:DSP48E1 | select -assert-count 2 t:DSP48E1 | ||||||
| select -assert-none t:* t:DSP48E1 %d t:BUFG %d | select -assert-none t:* t:DSP48E1 %d t:BUFG %d | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ hierarchy -top fsm | ||||||
| proc | proc | ||||||
| flatten | flatten | ||||||
| 
 | 
 | ||||||
| equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx | equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||||
| miter -equiv -make_assert -flatten gold gate miter | miter -equiv -make_assert -flatten gold gate miter | ||||||
| sat -verify -prove-asserts -show-public -set-at 1 in_reset 1 -seq 20 -prove-skip 1 miter | sat -verify -prove-asserts -show-public -set-at 1 in_reset 1 -seq 20 -prove-skip 1 miter | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ design -save read | ||||||
| 
 | 
 | ||||||
| hierarchy -top latchp | hierarchy -top latchp | ||||||
| proc | proc | ||||||
| equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd latchp # Constrain all select calls below inside the top module | cd latchp # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:LDCE | select -assert-count 1 t:LDCE | ||||||
|  | @ -14,7 +14,7 @@ select -assert-none t:LDCE %% t:* %D | ||||||
| design -load read | design -load read | ||||||
| hierarchy -top latchn | hierarchy -top latchn | ||||||
| proc | proc | ||||||
| equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd latchn # Constrain all select calls below inside the top module | cd latchn # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:LDCE | select -assert-count 1 t:LDCE | ||||||
|  | @ -26,7 +26,7 @@ select -assert-none t:LDCE t:INV %% t:* %D | ||||||
| design -load read | design -load read | ||||||
| hierarchy -top latchsr | hierarchy -top latchsr | ||||||
| proc | proc | ||||||
| equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd latchsr # Constrain all select calls below inside the top module | cd latchsr # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:LDCE | select -assert-count 1 t:LDCE | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| read_verilog ../common/logic.v | read_verilog ../common/logic.v | ||||||
| hierarchy -top top | hierarchy -top top | ||||||
| proc | proc | ||||||
| equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd top # Constrain all select calls below inside the top module | cd top # Constrain all select calls below inside the top module | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| #hierarchy -top lutram_1w1r -chparam A_WIDTH 4 | #hierarchy -top lutram_1w1r -chparam A_WIDTH 4 | ||||||
| #proc | #proc | ||||||
| #memory -nomap | #memory -nomap | ||||||
| #equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx | #equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||||
| #memory | #memory | ||||||
| #opt -full | #opt -full | ||||||
| # | # | ||||||
|  | @ -22,7 +22,7 @@ read_verilog ../common/lutram.v | ||||||
| hierarchy -top lutram_1w1r -chparam A_WIDTH 5 | hierarchy -top lutram_1w1r -chparam A_WIDTH 5 | ||||||
| proc | proc | ||||||
| memory -nomap | memory -nomap | ||||||
| equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx | equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||||
| memory | memory | ||||||
| opt -full | opt -full | ||||||
| 
 | 
 | ||||||
|  | @ -42,7 +42,7 @@ read_verilog ../common/lutram.v | ||||||
| hierarchy -top lutram_1w1r | hierarchy -top lutram_1w1r | ||||||
| proc | proc | ||||||
| memory -nomap | memory -nomap | ||||||
| equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx | equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||||
| memory | memory | ||||||
| opt -full | opt -full | ||||||
| 
 | 
 | ||||||
|  | @ -62,7 +62,7 @@ read_verilog ../common/lutram.v | ||||||
| hierarchy -top lutram_1w3r | hierarchy -top lutram_1w3r | ||||||
| proc | proc | ||||||
| memory -nomap | memory -nomap | ||||||
| equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx | equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||||
| memory | memory | ||||||
| opt -full | opt -full | ||||||
| 
 | 
 | ||||||
|  | @ -82,7 +82,7 @@ read_verilog ../common/lutram.v | ||||||
| hierarchy -top lutram_1w3r -chparam A_WIDTH 6 | hierarchy -top lutram_1w3r -chparam A_WIDTH 6 | ||||||
| proc | proc | ||||||
| memory -nomap | memory -nomap | ||||||
| equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx | equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||||
| memory | memory | ||||||
| opt -full | opt -full | ||||||
| 
 | 
 | ||||||
|  | @ -102,7 +102,7 @@ read_verilog ../common/lutram.v | ||||||
| hierarchy -top lutram_1w1r -chparam A_WIDTH 5 -chparam D_WIDTH 6 | hierarchy -top lutram_1w1r -chparam A_WIDTH 5 -chparam D_WIDTH 6 | ||||||
| proc | proc | ||||||
| memory -nomap | memory -nomap | ||||||
| equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx | equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||||
| memory | memory | ||||||
| opt -full | opt -full | ||||||
| 
 | 
 | ||||||
|  | @ -122,7 +122,7 @@ read_verilog ../common/lutram.v | ||||||
| hierarchy -top lutram_1w1r -chparam A_WIDTH 6 -chparam D_WIDTH 6 | hierarchy -top lutram_1w1r -chparam A_WIDTH 6 -chparam D_WIDTH 6 | ||||||
| proc | proc | ||||||
| memory -nomap | memory -nomap | ||||||
| equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx | equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||||
| memory | memory | ||||||
| opt -full | opt -full | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,3 +1,6 @@ | ||||||
| ../../../yosys -qp "synth_xilinx -top macc2; rename -top macc2_uut" -o macc_uut.v macc.v | ../../../yosys -qp "synth_xilinx -top macc2; rename -top macc2_uut" -o macc_uut.v macc.v | ||||||
| iverilog -o test_macc macc_tb.v macc_uut.v macc.v ../../../techlibs/xilinx/cells_sim.v | iverilog -o test_macc macc_tb.v macc_uut.v macc.v ../../../techlibs/xilinx/cells_sim.v | ||||||
| vvp -N ./test_macc | vvp -N ./test_macc | ||||||
|  | ../../../yosys -qp "synth_xilinx -family xc6s -top macc2; rename -top macc2_uut" -o macc_uut.v macc.v | ||||||
|  | iverilog -o test_macc macc_tb.v macc_uut.v macc.v ../../../techlibs/xilinx/cells_sim.v | ||||||
|  | vvp -N ./test_macc | ||||||
|  |  | ||||||
|  | @ -3,8 +3,8 @@ design -save read | ||||||
| 
 | 
 | ||||||
| hierarchy -top macc | hierarchy -top macc | ||||||
| proc | proc | ||||||
| #equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx ### TODO | #equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad ### TODO | ||||||
| equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx | equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||||
| miter -equiv -flatten -make_assert -make_outputs gold gate miter | miter -equiv -flatten -make_assert -make_outputs gold gate miter | ||||||
| sat -verify -prove-asserts -seq 10 -show-inputs -show-outputs miter | sat -verify -prove-asserts -seq 10 -show-inputs -show-outputs miter | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
|  | @ -17,8 +17,8 @@ select -assert-none t:BUFG t:FDRE t:DSP48E1 %% t:* %D | ||||||
| design -load read | design -load read | ||||||
| hierarchy -top macc2 | hierarchy -top macc2 | ||||||
| proc | proc | ||||||
| #equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx ### TODO | #equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad ### TODO | ||||||
| equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx | equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||||
| miter -equiv -flatten -make_assert -make_outputs gold gate miter | miter -equiv -flatten -make_assert -make_outputs gold gate miter | ||||||
| sat -verify -prove-asserts -seq 10 -show-inputs -show-outputs miter | sat -verify -prove-asserts -seq 10 -show-inputs -show-outputs miter | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
|  |  | ||||||
|  | @ -1,9 +1,21 @@ | ||||||
| read_verilog ../common/mul.v | read_verilog ../common/mul.v | ||||||
| hierarchy -top top | hierarchy -top top | ||||||
| proc | proc | ||||||
| equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd top # Constrain all select calls below inside the top module | cd top # Constrain all select calls below inside the top module | ||||||
| 
 | 
 | ||||||
| select -assert-count 1 t:DSP48E1 | select -assert-count 1 t:DSP48E1 | ||||||
| select -assert-none t:DSP48E1 %% t:* %D | select -assert-none t:DSP48E1 %% t:* %D | ||||||
|  | 
 | ||||||
|  | design -reset | ||||||
|  | 
 | ||||||
|  | read_verilog ../common/mul.v | ||||||
|  | hierarchy -top top | ||||||
|  | proc | ||||||
|  | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc6s -noiopad # equivalency check | ||||||
|  | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
|  | cd top # Constrain all select calls below inside the top module | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 t:DSP48A1 | ||||||
|  | select -assert-none t:DSP48A1 %% t:* %D | ||||||
|  |  | ||||||
|  | @ -2,10 +2,24 @@ read_verilog mul_unsigned.v | ||||||
| hierarchy -top mul_unsigned | hierarchy -top mul_unsigned | ||||||
| proc | proc | ||||||
| 
 | 
 | ||||||
| equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd mul_unsigned # Constrain all select calls below inside the top module | cd mul_unsigned # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:BUFG | select -assert-count 1 t:BUFG | ||||||
| select -assert-count 1 t:DSP48E1 | select -assert-count 1 t:DSP48E1 | ||||||
| select -assert-count 30 t:FDRE | select -assert-count 30 t:FDRE | ||||||
| select -assert-none t:DSP48E1 t:FDRE t:BUFG %% t:* %D | select -assert-none t:DSP48E1 t:FDRE t:BUFG %% t:* %D | ||||||
|  | 
 | ||||||
|  | design -reset | ||||||
|  | 
 | ||||||
|  | read_verilog mul_unsigned.v | ||||||
|  | hierarchy -top mul_unsigned | ||||||
|  | proc | ||||||
|  | 
 | ||||||
|  | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc6s -noiopad # equivalency check | ||||||
|  | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
|  | cd mul_unsigned # Constrain all select calls below inside the top module | ||||||
|  | select -assert-count 1 t:BUFG | ||||||
|  | select -assert-count 1 t:DSP48A1 | ||||||
|  | select -assert-count 30 t:FDRE | ||||||
|  | select -assert-none t:DSP48A1 t:FDRE t:BUFG %% t:* %D | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ design -save read | ||||||
| 
 | 
 | ||||||
| hierarchy -top mux2 | hierarchy -top mux2 | ||||||
| proc | proc | ||||||
| equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd mux2 # Constrain all select calls below inside the top module | cd mux2 # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:LUT3 | select -assert-count 1 t:LUT3 | ||||||
|  | @ -14,7 +14,7 @@ select -assert-none t:LUT3 %% t:* %D | ||||||
| design -load read | design -load read | ||||||
| hierarchy -top mux4 | hierarchy -top mux4 | ||||||
| proc | proc | ||||||
| equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd mux4 # Constrain all select calls below inside the top module | cd mux4 # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:LUT6 | select -assert-count 1 t:LUT6 | ||||||
|  | @ -25,7 +25,7 @@ select -assert-none t:LUT6 %% t:* %D | ||||||
| design -load read | design -load read | ||||||
| hierarchy -top mux8 | hierarchy -top mux8 | ||||||
| proc | proc | ||||||
| equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd mux8 # Constrain all select calls below inside the top module | cd mux8 # Constrain all select calls below inside the top module | ||||||
| select -assert-count 1 t:LUT3 | select -assert-count 1 t:LUT3 | ||||||
|  | @ -37,7 +37,7 @@ select -assert-none t:LUT3 t:LUT6 %% t:* %D | ||||||
| design -load read | design -load read | ||||||
| hierarchy -top mux16 | hierarchy -top mux16 | ||||||
| proc | proc | ||||||
| equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd mux16 # Constrain all select calls below inside the top module | cd mux16 # Constrain all select calls below inside the top module | ||||||
| select -assert-min 5 t:LUT6 | select -assert-min 5 t:LUT6 | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ read_verilog ../common/shifter.v | ||||||
| hierarchy -top top | hierarchy -top top | ||||||
| proc | proc | ||||||
| flatten | flatten | ||||||
| equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx # equivalency check | equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd top # Constrain all select calls below inside the top module | cd top # Constrain all select calls below inside the top module | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ synth | ||||||
| equiv_opt -assert -map +/xilinx/cells_sim.v -map +/simcells.v synth_xilinx # equivalency check | equiv_opt -assert -map +/xilinx/cells_sim.v -map +/simcells.v synth_xilinx # equivalency check | ||||||
| design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) | ||||||
| cd tristate # Constrain all select calls below inside the top module | cd tristate # Constrain all select calls below inside the top module | ||||||
| # TODO :: Tristate logic not yet supported; see https://github.com/YosysHQ/yosys/issues/1225 | select -assert-count 2 t:IBUF | ||||||
| select -assert-count 1 t:$_TBUF_ | select -assert-count 1 t:INV | ||||||
| select -assert-none t:$_TBUF_ %% t:* %D | select -assert-count 1 t:OBUFT | ||||||
|  | select -assert-none t:IBUF t:INV t:OBUFT %% t:* %D | ||||||
|  |  | ||||||
|  | @ -28,6 +28,20 @@ assign io = oe ? i : 1'bz; | ||||||
| assign o2 = io; | assign o2 = io; | ||||||
| assign o3 = ~io; | assign o3 = ~io; | ||||||
| endmodule | endmodule | ||||||
|  | 
 | ||||||
|  | module f(output o, o2); | ||||||
|  | assign o = 1'bz; | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | module g(inout io, output o); | ||||||
|  | assign o = io; | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | module h(inout io, output o, input i); | ||||||
|  | assign io = i; | ||||||
|  | assign o = io; | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
| EOT | EOT | ||||||
| 
 | 
 | ||||||
| opt_clean | opt_clean | ||||||
|  | @ -97,3 +111,12 @@ select -assert-count 1 @oeb %co %co @iob %i | ||||||
| select -assert-count 1 @iob %co %co @o2b %i | select -assert-count 1 @iob %co %co @o2b %i | ||||||
| select -assert-count 1 @iob %co %co t:$_NOT_ %i | select -assert-count 1 @iob %co %co t:$_NOT_ %i | ||||||
| select -assert-count 1 @o3b %ci %ci t:$_NOT_ %i | select -assert-count 1 @o3b %ci %ci t:$_NOT_ %i | ||||||
|  | 
 | ||||||
|  | select -assert-count 2 f/t:obuft | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 g/t:obuf | ||||||
|  | select -assert-count 1 g/t:iobuf | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 h/t:ibuf | ||||||
|  | select -assert-count 1 h/t:iobuf | ||||||
|  | select -assert-count 1 h/t:obuf | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue