mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-30 19:22:31 +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; | ||||
| 	dict<SigBit, SigBit> not_map, alias_map; | ||||
| 	dict<SigBit, pair<SigBit, SigBit>> and_map; | ||||
| 	vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits; | ||||
| 	vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits; | ||||
| 	vector<SigBit> ci_bits, co_bits; | ||||
| 	dict<SigBit, std::pair<int,int>> ff_bits; | ||||
| 	dict<SigBit, float> arrival_times; | ||||
| 
 | ||||
|  | @ -429,7 +428,6 @@ struct XAigerWriter | |||
| 							cell->setPort(port_name, rhs); | ||||
| 						} | ||||
| 
 | ||||
| 						int offset = 0; | ||||
| 						for (auto b : rhs.bits()) { | ||||
| 							SigBit I = sigmap(b); | ||||
| 							if (b == RTLIL::Sx) | ||||
|  | @ -440,7 +438,7 @@ struct XAigerWriter | |||
| 								else | ||||
| 									alias_map[b] = I; | ||||
| 							} | ||||
| 							co_bits.emplace_back(b, cell, port_name, offset++, 0); | ||||
| 							co_bits.emplace_back(b); | ||||
| 							unused_bits.erase(I); | ||||
| 						} | ||||
| 					} | ||||
|  | @ -460,9 +458,8 @@ struct XAigerWriter | |||
| 							cell->setPort(port_name, rhs); | ||||
| 						} | ||||
| 
 | ||||
| 						int offset = 0; | ||||
| 						for (const auto &b : rhs.bits()) { | ||||
| 							ci_bits.emplace_back(b, cell, port_name, offset++); | ||||
| 							ci_bits.emplace_back(b); | ||||
| 							SigBit O = sigmap(b); | ||||
| 							if (O != b) | ||||
| 								alias_map[O] = b; | ||||
|  | @ -478,7 +475,6 @@ struct XAigerWriter | |||
| 					if (rhs.empty()) | ||||
| 						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) { | ||||
| 						SigBit I = sigmap(b); | ||||
| 						if (b == RTLIL::Sx) | ||||
|  | @ -489,7 +485,7 @@ struct XAigerWriter | |||
| 							else | ||||
| 								alias_map[b] = I; | ||||
| 						} | ||||
| 						co_bits.emplace_back(b, cell, "\\$abc9_currQ", offset++, 0); | ||||
| 						co_bits.emplace_back(b); | ||||
| 						unused_bits.erase(I); | ||||
| 					} | ||||
| 				} | ||||
|  | @ -571,17 +567,15 @@ struct XAigerWriter | |||
| 		} | ||||
| 
 | ||||
| 		dict<SigBit, int> ff_aig_map; | ||||
| 		for (auto &c : ci_bits) { | ||||
| 			RTLIL::SigBit bit = std::get<0>(c); | ||||
| 		for (auto &bit : ci_bits) { | ||||
| 			aig_m++, aig_i++; | ||||
| 			auto r = aig_map.insert(std::make_pair(bit, 2*aig_m)); | ||||
| 			if (!r.second) | ||||
| 				ff_aig_map[bit] = 2*aig_m; | ||||
| 		} | ||||
| 
 | ||||
| 		for (auto &c : co_bits) { | ||||
| 			RTLIL::SigBit bit = std::get<0>(c); | ||||
| 			std::get<4>(c) = ordered_outputs[bit] = aig_o++; | ||||
| 		for (auto bit : co_bits) { | ||||
| 			ordered_outputs[bit] = aig_o++; | ||||
| 			aig_outputs.push_back(bit2aig(bit)); | ||||
| 		} | ||||
| 
 | ||||
|  | @ -707,27 +701,38 @@ struct XAigerWriter | |||
| 			int port_id = 1; | ||||
| 			int box_count = 0; | ||||
| 			for (auto cell : box_list) { | ||||
| 				RTLIL::Module* box_module = module->design->module(cell->type); | ||||
| 				log_assert(box_module); | ||||
| 				IdString derived_name = box_module->derive(module->design, cell->parameters); | ||||
| 				box_module = module->design->module(derived_name); | ||||
| 				RTLIL::Module* orig_box_module = module->design->module(cell->type); | ||||
| 				log_assert(orig_box_module); | ||||
| 				IdString derived_name = orig_box_module->derive(module->design, cell->parameters); | ||||
| 				RTLIL::Module* box_module = module->design->module(derived_name); | ||||
| 				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; | ||||
| 				auto r = cell_cache.insert(std::make_pair(derived_name, nullptr)); | ||||
| 				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->parameters = cell->parameters; | ||||
| 					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
 | ||||
| 				//     (as RTLIL::Module::fixup_ports() would do)
 | ||||
| 				int box_port_id = 1; | ||||
| 				for (const auto &port_name : box_module->ports) { | ||||
| 					RTLIL::Wire *w = box_module->wire(port_name); | ||||
| 					log_assert(w); | ||||
| 					if (r.second) | ||||
| 						w->port_id = box_port_id++; | ||||
| 					RTLIL::Wire *holes_wire; | ||||
| 					RTLIL::SigSpec port_sig; | ||||
| 					if (w->port_input) | ||||
|  | @ -829,8 +834,9 @@ struct XAigerWriter | |||
| 				//holes_module->fixup_ports();
 | ||||
| 				holes_module->check(); | ||||
| 
 | ||||
| 				// TODO: Should techmap/aigmap/check all lib_whitebox-es just once,
 | ||||
| 				//       instead of per write_xaiger call
 | ||||
| 				// Cannot techmap/aigmap/check all lib_whitebox-es outside of write_xaiger
 | ||||
| 				//   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"); | ||||
| 
 | ||||
| 				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 | ||||
| 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_dsp48a_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)) | ||||
| 
 | ||||
|  |  | |||
|  | @ -26,6 +26,7 @@ USING_YOSYS_NAMESPACE | |||
| PRIVATE_NAMESPACE_BEGIN | ||||
| 
 | ||||
| #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_cascade_pm.h" | ||||
| 
 | ||||
|  | @ -487,6 +488,190 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm) | |||
| 	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) | ||||
| { | ||||
| 	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("into using the DSP48E1's pattern detector feature for overflow detection.\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 | ||||
| 	{ | ||||
| 		log_header(design, "Executing XILINX_DSP pass (pack resources into DSPs).\n"); | ||||
| 
 | ||||
| 		std::string family = "xc7"; | ||||
| 		size_t argidx; | ||||
| 		for (argidx = 1; argidx < args.size(); argidx++) | ||||
| 		{ | ||||
| 			// if (args[argidx] == "-singleton") {
 | ||||
| 			// 	singleton_mode = true;
 | ||||
| 			// 	continue;
 | ||||
| 			// }
 | ||||
| 			if ((args[argidx] == "-family" || args[argidx] == "-arch") && argidx+1 < args.size()) { | ||||
| 				family = args[++argidx]; | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		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()) { | ||||
| 			// Experimental feature: pack $add/$sub cells with
 | ||||
| 			//   (* use_dsp48="simd" *) into DSP48E1's using its
 | ||||
| 			//   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,
 | ||||
| 			// post-adder, pattern detector, etc.) except for CREG
 | ||||
| 			{ | ||||
| 			if (family == "xc7") { | ||||
| 				xilinx_dsp_pm pm(module, module->selected_cells()); | ||||
| 				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
 | ||||
| 			//   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 | ||||
| //   forms the `xilinx_dsp` pass described in xilinx_dsp.cc | ||||
| // 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 | ||||
| //   (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 | ||||
|  | @ -38,10 +38,10 @@ udata <SigBit> dffclock | |||
| udata <Cell*> dff dffcemux dffrstmux | ||||
| 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 | ||||
| match dsp | ||||
| 	select dsp->type.in(\DSP48E1) | ||||
| 	select dsp->type.in(\DSP48A, \DSP48A1, \DSP48E1) | ||||
| 	select param(dsp, \CREG, 1).as_int() == 0 | ||||
| 	select nusers(port(dsp, \C, SigSpec())) > 1 | ||||
| endmatch | ||||
|  | @ -60,7 +60,8 @@ code sigC sigP clock | |||
| 	sigC = unextend(port(dsp, \C, SigSpec())); | ||||
| 
 | ||||
| 	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 | ||||
| 		int i; | ||||
| 		for (i = GetSize(P)-1; i >= 0; i--) | ||||
|  |  | |||
|  | @ -62,12 +62,11 @@ code | |||
| #define MAX_DSP_CASCADE 20 | ||||
| endcode | ||||
| 
 | ||||
| // (1) Starting from a DSP48E1 cell that (a) has the Z multiplexer | ||||
| //     (controlled by OPMODE[6:4]) set to zero and (b) doesn't already | ||||
| //     use the 'PCOUT' port | ||||
| // (1) Starting from a DSP48* cell that (a) has the Z multiplexer | ||||
| //     (controlled by OPMODE[3:2] for DSP48A*, by OPMODE[6:4] for DSP48E1) | ||||
| //     set to zero and (b) doesn't already use the 'PCOUT' port | ||||
| match first | ||||
| 	select first->type.in(\DSP48E1) | ||||
| 	select port(first, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("000") | ||||
| 	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 nusers(port(first, \PCOUT, SigSpec())) <= 1 | ||||
| endmatch | ||||
| 
 | ||||
|  | @ -100,14 +99,21 @@ finally | |||
| 					add_siguser(cascade, dsp); | ||||
| 
 | ||||
| 					SigSpec opmode = port(dsp_pcin, \OPMODE, Const(0, 7)); | ||||
| 					if (P == 17) | ||||
| 						opmode[6] = State::S1; | ||||
| 					else if (P == 0) | ||||
| 						opmode[6] = State::S0; | ||||
| 					else log_abort(); | ||||
| 					if (dsp->type.in(\DSP48A, \DSP48A1)) { | ||||
| 						log_assert(P == 0); | ||||
| 						opmode[3] = State::S0; | ||||
| 						opmode[2] = State::S1; | ||||
| 					} | ||||
| 					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[4] = State::S1; | ||||
| 						opmode[5] = State::S0; | ||||
| 						opmode[4] = State::S1; | ||||
| 					} | ||||
| 					dsp_pcin->setPort(\OPMODE, opmode); | ||||
| 
 | ||||
| 					log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); | ||||
|  | @ -120,21 +126,42 @@ finally | |||
| 					add_siguser(cascade, dsp_pcin); | ||||
| 					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")); | ||||
| 
 | ||||
| 					log_debug("ACOUT -> ACIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin)); | ||||
| 				} | ||||
| 				if (BREG >= 0) { | ||||
| 					Wire *cascade = module->addWire(NEW_ID, 18); | ||||
| 					dsp_pcin->setPort(ID(B), Const(0, 18)); | ||||
| 					dsp_pcin->setPort(ID(BCIN), cascade); | ||||
| 					if (dsp->type.in(\DSP48A, \DSP48A1)) { | ||||
| 						// 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); | ||||
| 					add_siguser(cascade, dsp_pcin); | ||||
| 					add_siguser(cascade, dsp); | ||||
| 
 | ||||
| 					dsp->setParam(ID(BCASCREG), BREG); | ||||
| 					dsp_pcin->setParam(ID(B_INPUT), Const("CASCADE")); | ||||
| 					if (dsp->type.in(\DSP48E1)) { | ||||
| 						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)); | ||||
| 				} | ||||
|  | @ -156,22 +183,21 @@ subpattern tail | |||
| arg first | ||||
| 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 | ||||
| //       driven by the 'P' output of the previous DSP cell, and (c) has its | ||||
| //       'PCIN' port unused | ||||
| match nextP | ||||
| 	select nextP->type.in(\DSP48E1) | ||||
| 	select !param(nextP, \CREG, State::S1).as_bool() | ||||
| 	select port(nextP, \OPMODE, Const(0, 7)).extract(4,3) == Const::from_string("011") | ||||
| 	select (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, \PCIN, SigSpec())) == 0 | ||||
| 	index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0] | ||||
| 	semioptional | ||||
| endmatch | ||||
| 
 | ||||
| // (2.2) Same as (2.1) but with the 'C' port driven by the 'P' output of the | ||||
| //       previous DSP cell right-shifted by 17 bits | ||||
| // (2.2) For DSP48E1 only, same as (2.1) but with the 'C' port driven | ||||
| //       by the 'P' output of the previous DSP cell right-shifted by 17 bits | ||||
| match nextP_shift17 | ||||
| 	if !nextP | ||||
| 	select nextP_shift17->type.in(\DSP48E1) | ||||
|  | @ -188,6 +214,8 @@ code next | |||
| 	if (!nextP) | ||||
| 		next = nextP_shift17; | ||||
| 	if (next) { | ||||
| 		if (next->type != first->type) | ||||
| 			reject; | ||||
| 		unextend = [](const SigSpec &sig) { | ||||
| 			int i; | ||||
| 			for (i = GetSize(sig)-1; i > 0; i--) | ||||
|  | @ -202,38 +230,50 @@ code next | |||
| endcode | ||||
| 
 | ||||
| // (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 | ||||
| //     DSP48 does not use A2REG nor A1REG, (c) this DSP48E1 does not already | ||||
| //     have an ACOUT -> ACIN cascade, (d) the previous DSP does not already | ||||
| //     use its ACOUT port, then examine if an ACOUT -> ACIN cascade | ||||
| //     opportunity exists by matching for a $dff-with-optional-clock-enable- | ||||
| //     or-reset and checking that the 'D' input of this register is the same | ||||
| //     as the 'A' input of the previous DSP | ||||
| //     if (a) this DSP48E1 does not already have an ACOUT -> ACIN cascade, | ||||
| //     (b) the previous DSP does  not already use its ACOUT port, then | ||||
| //     examine if an ACOUT -> ACIN cascade  opportunity exists if | ||||
| //     (i) A ports are identical, or (ii) separated by a | ||||
| //     $dff-with-optional-clock-enable-or-reset and checking that the 'D' input | ||||
| //     of this register is the same as the 'A' input of the previous DSP | ||||
| //     TODO: Check for two levels of flops, instead of just one | ||||
| code argQ clock AREG | ||||
| 	AREG = -1; | ||||
| 	if (next) { | ||||
| 	if (next && next->type.in(\DSP48E1)) { | ||||
| 		Cell *prev = std::get<0>(chain.back()); | ||||
| 		if (param(prev, \AREG, 2).as_int() > 0 && | ||||
| 				param(next, \AREG, 2).as_int() > 0 && | ||||
| 				param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && | ||||
| 
 | ||||
| 		if (param(next, \A_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && | ||||
| 				port(next, \ACIN, SigSpec()).is_fully_zero() && | ||||
| 				nusers(port(prev, \ACOUT, SigSpec())) <= 1) { | ||||
| 			argQ = unextend(port(next, \A)); | ||||
| 			clock = port(prev, \CLK); | ||||
| 			subpattern(in_dffe); | ||||
| 			if (dff) { | ||||
| 				if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0) | ||||
| 					goto reject_AREG; | ||||
| 				if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0)) | ||||
| 					goto reject_AREG; | ||||
| 				if (!dffcemux && port(prev, \CEA2, State::S0) != State::S0) | ||||
| 					goto reject_AREG; | ||||
| 				if (dffcemux && port(dffcemux, \S) != port(prev, \CEA2, State::S0)) | ||||
| 					goto reject_AREG; | ||||
| 				if (dffD == unextend(port(prev, \A))) | ||||
| 					AREG = 1; | ||||
| reject_AREG:			; | ||||
| 			if (param(prev, \AREG, 2) == 0) { | ||||
| 				if (port(prev, \A) == port(next, \A)) | ||||
| 					AREG = 0; | ||||
| 			} | ||||
| 			else { | ||||
| 				argQ = unextend(port(next, \A)); | ||||
| 				clock = port(prev, \CLK); | ||||
| 				subpattern(in_dffe); | ||||
| 				if (dff) { | ||||
| 					if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0) | ||||
| 						goto reject_AREG; | ||||
| 					if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0)) | ||||
| 						goto reject_AREG; | ||||
| 					IdString CEA; | ||||
| 					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 | ||||
| 
 | ||||
|  | @ -242,28 +282,47 @@ code argQ clock BREG | |||
| 	BREG = -1; | ||||
| 	if (next) { | ||||
| 		Cell *prev = std::get<0>(chain.back()); | ||||
| 		if (param(prev, \BREG, 2).as_int() > 0 && | ||||
| 				param(next, \BREG, 2).as_int() > 0 && | ||||
| 				param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && | ||||
| 		if (param(next, \B_INPUT, Const("DIRECT")).decode_string() == "DIRECT" && | ||||
| 				port(next, \BCIN, SigSpec()).is_fully_zero() && | ||||
| 				nusers(port(prev, \BCOUT, SigSpec())) <= 1) { | ||||
| 			argQ = unextend(port(next, \B)); | ||||
| 			clock = port(prev, \CLK); | ||||
| 			subpattern(in_dffe); | ||||
| 			if (dff) { | ||||
| 				if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0) | ||||
| 					goto reject_BREG; | ||||
| 				if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0)) | ||||
| 					goto reject_BREG; | ||||
| 				if (!dffcemux && port(prev, \CEB2, State::S0) != State::S0) | ||||
| 					goto reject_BREG; | ||||
| 				if (dffcemux && port(dffcemux, \S) != port(prev, \CEB2, State::S0)) | ||||
| 					goto reject_BREG; | ||||
| 				if (dffD == unextend(port(prev, \B))) | ||||
| 					BREG = 1; | ||||
| reject_BREG:			; | ||||
| 			if ((next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG, 0) == 0 && param(prev, \B1REG, 1) == 0) || | ||||
| 				(next->type.in(\DSP48E1) && param(prev, \BREG, 2) == 0)) { | ||||
| 				if (port(prev, \B) == port(next, \B)) | ||||
| 					BREG = 0; | ||||
| 			} | ||||
| 			else { | ||||
| 				argQ = unextend(port(next, \B)); | ||||
| 				clock = port(prev, \CLK); | ||||
| 				subpattern(in_dffe); | ||||
| 				if (dff) { | ||||
| 					if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0) | ||||
| 						goto reject_BREG; | ||||
| 					if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0)) | ||||
| 						goto reject_BREG; | ||||
| 					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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -192,11 +192,28 @@ struct IopadmapPass : public Pass { | |||
| 			if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty()) | ||||
| 			{ | ||||
| 				dict<SigBit, Cell *> tbuf_bits; | ||||
| 				pool<SigBit> driven_bits; | ||||
| 
 | ||||
| 				// Gather tristate buffers and always-on drivers.
 | ||||
| 				for (auto cell : module->cells()) | ||||
| 					if (cell->type == ID($_TBUF_)) { | ||||
| 						SigBit bit = cell->getPort(ID::Y).as_bit(); | ||||
| 						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()) | ||||
|  | @ -204,41 +221,68 @@ struct IopadmapPass : public Pass { | |||
| 					if (!wire->port_output) | ||||
| 						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++) | ||||
| 					{ | ||||
| 						SigBit wire_bit(wire, i); | ||||
| 						Cell *tbuf_cell = nullptr; | ||||
| 
 | ||||
| 						if (tbuf_bits.count(wire_bit) == 0) | ||||
| 							continue; | ||||
| 						if (tbuf_bits.count(wire_bit)) | ||||
| 							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) | ||||
| 							continue; | ||||
| 						if (tbuf_cell != nullptr) { | ||||
| 							// 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(); | ||||
| 						SigBit data_sig = tbuf_cell->getPort(ID::A).as_bit(); | ||||
| 
 | ||||
| 						if (wire->port_input && !tinoutpad_celltype.empty()) | ||||
| 						if (wire->port_input) | ||||
| 						{ | ||||
| 							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->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); | ||||
| 
 | ||||
| 							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); | ||||
| 							if (!tinoutpad_portname_pad.empty()) | ||||
| 								rewrite_bits[wire][i] = make_pair(cell, RTLIL::escape_id(tinoutpad_portname_pad)); | ||||
| 							continue; | ||||
| 						} | ||||
| 
 | ||||
| 						if (!wire->port_input && !toutpad_celltype.empty()) | ||||
| 						{ | ||||
| 						} else { | ||||
| 							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)); | ||||
|  | @ -247,12 +291,13 @@ struct IopadmapPass : public Pass { | |||
| 							cell->setPort(RTLIL::escape_id(toutpad_portname_i), data_sig); | ||||
| 							cell->attributes[ID::keep] = RTLIL::Const(1); | ||||
| 
 | ||||
| 							module->remove(tbuf_cell); | ||||
| 							module->connect(wire_bit, data_sig); | ||||
| 							if (tbuf_cell) { | ||||
| 								module->remove(tbuf_cell); | ||||
| 								module->connect(wire_bit, data_sig); | ||||
| 							} | ||||
| 							skip_wire_bits.insert(wire_bit); | ||||
| 							if (!toutpad_portname_pad.empty()) | ||||
| 								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); | ||||
|     parameter [15:0] INIT = 16'h0000; | ||||
|     wire [7:0] s3 = D ?     INIT[15:8] :     INIT[7:0]; | ||||
|  | @ -31,13 +32,8 @@ module CCU2C( | |||
| 
 | ||||
| 	// First half | ||||
| 	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)); | ||||
| 	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; | ||||
| 	assign S0 = LUT4_0 ^ gated_cin_0; | ||||
| 
 | ||||
|  | @ -46,13 +42,8 @@ module CCU2C( | |||
| 
 | ||||
| 	// Second half | ||||
| 	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)); | ||||
| 	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; | ||||
| 	assign S1 = LUT4_1 ^ gated_cin_1; | ||||
| 
 | ||||
|  | @ -209,6 +200,7 @@ endmodule | |||
| 
 | ||||
| // --------------------------------------- | ||||
| 
 | ||||
| (* lib_whitebox *) | ||||
| module LUT2(input A, B, output Z); | ||||
|     parameter [3:0] INIT = 4'h0; | ||||
|     wire [1:0] s1 = B ?     INIT[ 3:2] :     INIT[1:0]; | ||||
|  |  | |||
|  | @ -230,7 +230,7 @@ struct SynthEcp5Pass : public ScriptPass | |||
| 	{ | ||||
| 		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())); | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -2107,7 +2107,7 @@ always @* begin | |||
| 		2'b00: XMUX <= 0; | ||||
| 		2'b01: XMUX <= M; | ||||
| 		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; | ||||
| 	endcase | ||||
| end | ||||
|  | @ -2125,8 +2125,8 @@ end | |||
| // The post-adder. | ||||
| wire signed [48:0] X_EXT; | ||||
| wire signed [48:0] Z_EXT; | ||||
| assign X_EXT = XMUX; | ||||
| assign Z_EXT = ZMUX; | ||||
| assign X_EXT = {1'b0, XMUX}; | ||||
| 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); | ||||
| 
 | ||||
| // Cascade outputs. | ||||
|  |  | |||
|  | @ -64,7 +64,7 @@ struct SynthXilinxPass : public ScriptPass | |||
| 		log("        (this feature is experimental and incomplete)\n"); | ||||
| 		log("\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("    -nobram\n"); | ||||
| 		log("        do not use block RAM cells in output netlist\n"); | ||||
|  | @ -84,11 +84,9 @@ struct SynthXilinxPass : public ScriptPass | |||
| 		log("    -nodsp\n"); | ||||
| 		log("        do not use DSP48E1s to implement multipliers and associated logic\n"); | ||||
| 		log("\n"); | ||||
| 		log("    -iopad\n"); | ||||
| 		log("        enable I/O buffer insertion (selected automatically by -ise)\n"); | ||||
| 		log("\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("    -noclkbuf\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; | ||||
| 	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 flatten_before_abc; | ||||
| 	int widemux; | ||||
|  | @ -140,7 +138,6 @@ struct SynthXilinxPass : public ScriptPass | |||
| 		retime = false; | ||||
| 		vpr = false; | ||||
| 		ise = false; | ||||
| 		iopad = false; | ||||
| 		noiopad = false; | ||||
| 		noclkbuf = false; | ||||
| 		nocarry = false; | ||||
|  | @ -218,7 +215,6 @@ struct SynthXilinxPass : public ScriptPass | |||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-iopad") { | ||||
| 				iopad = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-noiopad") { | ||||
|  | @ -291,7 +287,6 @@ struct SynthXilinxPass : public ScriptPass | |||
| 
 | ||||
| 	void script() YS_OVERRIDE | ||||
| 	{ | ||||
| 		bool do_iopad = iopad || (ise && !noiopad); | ||||
| 		std::string ff_map_file; | ||||
| 		if (help_mode) | ||||
| 			ff_map_file = "+/xilinx/{family}_ff_map.v"; | ||||
|  | @ -397,7 +392,10 @@ struct SynthXilinxPass : public ScriptPass | |||
| 				run("opt_expr -fine"); | ||||
| 				run("wreduce"); | ||||
| 				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"); | ||||
| 			} | ||||
| 		} | ||||
|  | @ -524,8 +522,8 @@ struct SynthXilinxPass : public ScriptPass | |||
| 
 | ||||
| 		if (check_label("map_cells")) { | ||||
| 			// Needs to be done before logic optimization, so that inverters (OE vs T) are handled.
 | ||||
| 			if (help_mode || do_iopad) | ||||
| 				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')"); | ||||
| 			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 not '-noiopad')"); | ||||
| 			std::string techmap_args = "-map +/techmap.v -map +/xilinx/cells_map.v"; | ||||
| 			if (widemux > 0) | ||||
| 				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_uut.v | ||||
| test_dsp_model | ||||
| test_dsp48a_model_ref.v | ||||
| test_dsp48a1_model_ref.v | ||||
| test_dsp48a1_model_uut.v | ||||
| test_dsp48a1_model | ||||
| *.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 | ||||
| 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 | ||||
| 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 | ||||
| for tb in macc_overflow_underflow \ | ||||
|     simd24_preadd_noreg_nocasc simd12_preadd_noreg_nocasc \ | ||||
|     mult_allreg_nopreadd_nocasc mult_noreg_nopreadd_nocasc  \ | ||||
| 	simd24_preadd_noreg_nocasc simd12_preadd_noreg_nocasc \ | ||||
| 	 mult_allreg_nopreadd_nocasc mult_noreg_nopreadd_nocasc  \ | ||||
| 	mult_allreg_preadd_nocasc mult_noreg_preadd_nocasc mult_inreg_preadd_nocasc | ||||
| 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 | ||||
| done | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y); | |||
| 		.D(18'b0), | ||||
| 		.P(P_48), | ||||
| 
 | ||||
| 		.OPMODE(8'b0000010) | ||||
| 		.OPMODE(8'b0000001) | ||||
| 	); | ||||
| 	assign Y = P_48; | ||||
| endmodule | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y); | |||
| 		.D(18'b0), | ||||
| 		.P(P_48), | ||||
| 
 | ||||
| 		.OPMODE(8'b0000010) | ||||
| 		.OPMODE(8'b0000001) | ||||
| 	); | ||||
| 	assign Y = P_48; | ||||
| 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 | ||||
| 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 | ||||
| select -assert-count 8 t:L6MUX21 | ||||
| select -assert-count 26 t:LUT4 | ||||
| select -assert-count 12 t:PFUMX | ||||
| select -assert-count 12 t:L6MUX21 | ||||
| select -assert-count 34 t:LUT4 | ||||
| select -assert-count 17 t:PFUMX | ||||
| 
 | ||||
| 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 | ||||
| hierarchy -top top | ||||
| 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) | ||||
| cd top # Constrain all select calls below inside the top module | ||||
| select -assert-count 14 t:LUT2 | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ design -save read | |||
| 
 | ||||
| hierarchy -top adff | ||||
| 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) | ||||
| cd adff # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:BUFG | ||||
|  | @ -15,7 +15,7 @@ select -assert-none t:BUFG t:FDCE %% t:* %D | |||
| design -load read | ||||
| hierarchy -top adffn | ||||
| 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) | ||||
| cd adffn # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:BUFG | ||||
|  | @ -28,7 +28,7 @@ select -assert-none t:BUFG t:FDCE t:INV %% t:* %D | |||
| design -load read | ||||
| hierarchy -top dffs | ||||
| 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) | ||||
| cd dffs # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:BUFG | ||||
|  | @ -40,7 +40,7 @@ select -assert-none t:BUFG t:FDSE %% t:* %D | |||
| design -load read | ||||
| hierarchy -top ndffnr | ||||
| 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) | ||||
| cd ndffnr # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:BUFG | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| # Check that blockram memory without parameters is not modified | ||||
| read_verilog ../common/memory_attributes/attributes_test.v | ||||
| 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 | ||||
| select -assert-count 1 t:RAMB18E1 | ||||
|   | ||||
|  | @ -9,7 +9,7 @@ select -assert-count 1 t:RAMB18E1 | |||
| design -reset | ||||
| read_verilog ../common/memory_attributes/attributes_test.v | ||||
| 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 | ||||
| select -assert-count 8 t:RAM32X1D | ||||
|   | ||||
|  | @ -18,7 +18,7 @@ design -reset | |||
| read_verilog ../common/memory_attributes/attributes_test.v | ||||
| prep | ||||
| 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 | ||||
| select -assert-count 32 t:RAM128X1D | ||||
|   | ||||
|  | @ -27,7 +27,7 @@ design -reset | |||
| read_verilog ../common/memory_attributes/attributes_test.v | ||||
| prep | ||||
| 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 | ||||
| select -assert-count 0 t:RAMB18E1 | ||||
| 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 | ||||
| design -reset | ||||
| 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 | ||||
| select -assert-count 1 t:RAMB18E1 | ||||
|   | ||||
| # Set synthesis, ram_block block to a distributed memory; will be implemented as blockram | ||||
| design -reset | ||||
| 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 | ||||
| select -assert-count 1 t:RAMB18E1 | ||||
|  |  | |||
|  | @ -3,28 +3,28 @@ | |||
| # Memory bits <= 18K; Data width <= 36; Address width <= 14: -> RAMB18E1 | ||||
| read_verilog ../common/blockram.v | ||||
| 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 | ||||
| select -assert-count 1 t:RAMB18E1 | ||||
| 
 | ||||
| design -reset | ||||
| read_verilog ../common/blockram.v | ||||
| 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 | ||||
| select -assert-count 1 t:RAMB18E1 | ||||
| 
 | ||||
| design -reset | ||||
| read_verilog ../common/blockram.v | ||||
| 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 | ||||
| select -assert-count 1 t:RAMB18E1 | ||||
| 
 | ||||
| design -reset | ||||
| read_verilog ../common/blockram.v | ||||
| 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 | ||||
| select -assert-count 1 t:RAMB18E1 | ||||
| 
 | ||||
|  | @ -32,7 +32,7 @@ select -assert-count 1 t:RAMB18E1 | |||
| design -reset | ||||
| read_verilog ../common/blockram.v | ||||
| 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 | ||||
| select -assert-count 0 t:RAMB18E1 | ||||
| select -assert-count 4 t:RAM128X1D | ||||
|  | @ -41,7 +41,7 @@ select -assert-count 4 t:RAM128X1D | |||
| design -reset | ||||
| read_verilog ../common/blockram.v | ||||
| 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 | ||||
| select -assert-count 1 t:RAMB36E1 | ||||
| 
 | ||||
|  | @ -52,7 +52,7 @@ design -reset | |||
| read_verilog ../common/blockram.v | ||||
| hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1  | ||||
| setattr -set ram_style "block" m:memory | ||||
| synth_xilinx -top sync_ram_sdp | ||||
| synth_xilinx -top sync_ram_sdp -noiopad | ||||
| cd sync_ram_sdp | ||||
| select -assert-count 1 t:RAMB18E1 | ||||
| 
 | ||||
|  | @ -60,7 +60,7 @@ design -reset | |||
| read_verilog ../common/blockram.v | ||||
| hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1  | ||||
| setattr -set ram_block 1 m:memory | ||||
| synth_xilinx -top sync_ram_sdp | ||||
| synth_xilinx -top sync_ram_sdp -noiopad | ||||
| cd sync_ram_sdp | ||||
| select -assert-count 1 t:RAMB18E1 | ||||
| 
 | ||||
|  | @ -68,7 +68,7 @@ design -reset | |||
| read_verilog ../common/blockram.v | ||||
| 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 | ||||
| synth_xilinx -top sync_ram_sdp | ||||
| synth_xilinx -top sync_ram_sdp -noiopad | ||||
| cd sync_ram_sdp | ||||
| select -assert-count 0 t:RAMB18E1 | ||||
| 
 | ||||
|  | @ -76,7 +76,7 @@ design -reset | |||
| read_verilog ../common/blockram.v | ||||
| hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 10 -chparam DATA_WIDTH 1  | ||||
| setattr -set logic_block 1 m:memory | ||||
| synth_xilinx -top sync_ram_sdp | ||||
| synth_xilinx -top sync_ram_sdp -noiopad | ||||
| cd sync_ram_sdp | ||||
| select -assert-count 0 t:RAMB18E1 | ||||
| 
 | ||||
|  | @ -84,7 +84,7 @@ design -reset | |||
| read_verilog ../common/blockram.v | ||||
| hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 8 -chparam DATA_WIDTH 1 | ||||
| setattr -set ram_style "block" m:memory | ||||
| synth_xilinx -top sync_ram_sdp | ||||
| synth_xilinx -top sync_ram_sdp -noiopad | ||||
| cd sync_ram_sdp | ||||
| select -assert-count 1 t:RAMB18E1 | ||||
| 
 | ||||
|  | @ -92,6 +92,6 @@ design -reset | |||
| read_verilog ../common/blockram.v | ||||
| hierarchy -top sync_ram_sdp -chparam ADDRESS_WIDTH 8 -chparam DATA_WIDTH 1 | ||||
| setattr -set ram_block 1 m:memory | ||||
| synth_xilinx -top sync_ram_sdp | ||||
| synth_xilinx -top sync_ram_sdp -noiopad | ||||
| cd sync_ram_sdp | ||||
| select -assert-count 1 t:RAMB18E1 | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ module register_file( | |||
| endmodule | ||||
| EOT | ||||
| 
 | ||||
| synth_xilinx | ||||
| synth_xilinx -noiopad | ||||
| cd register_file | ||||
| select -assert-count 32 t:RAM32M | ||||
| 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 | ||||
| proc | ||||
| 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) | ||||
| cd top # Constrain all select calls below inside the top module | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ design -save read | |||
| 
 | ||||
| hierarchy -top dff | ||||
| 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) | ||||
| cd dff # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:BUFG | ||||
|  | @ -15,7 +15,7 @@ select -assert-none t:BUFG t:FDRE %% t:* %D | |||
| design -load read | ||||
| hierarchy -top dffe | ||||
| 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) | ||||
| cd dffe # Constrain all select calls below inside the top module | ||||
| 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 | ||||
| EOT | ||||
| 
 | ||||
| synth_xilinx | ||||
| synth_xilinx -noiopad | ||||
| cd fastfir_dynamictaps | ||||
| select -assert-count 2 t:DSP48E1 | ||||
| select -assert-none t:* t:DSP48E1 %d t:BUFG %d | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ hierarchy -top fsm | |||
| proc | ||||
| 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 | ||||
| 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 | ||||
| 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) | ||||
| cd latchp # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:LDCE | ||||
|  | @ -14,7 +14,7 @@ select -assert-none t:LDCE %% t:* %D | |||
| design -load read | ||||
| hierarchy -top latchn | ||||
| 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) | ||||
| cd latchn # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:LDCE | ||||
|  | @ -26,7 +26,7 @@ select -assert-none t:LDCE t:INV %% t:* %D | |||
| design -load read | ||||
| hierarchy -top latchsr | ||||
| 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) | ||||
| cd latchsr # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:LDCE | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| read_verilog ../common/logic.v | ||||
| hierarchy -top top | ||||
| 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) | ||||
| cd top # Constrain all select calls below inside the top module | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| #hierarchy -top lutram_1w1r -chparam A_WIDTH 4 | ||||
| #proc | ||||
| #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 | ||||
| #opt -full | ||||
| # | ||||
|  | @ -22,7 +22,7 @@ read_verilog ../common/lutram.v | |||
| hierarchy -top lutram_1w1r -chparam A_WIDTH 5 | ||||
| proc | ||||
| 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 | ||||
| opt -full | ||||
| 
 | ||||
|  | @ -42,7 +42,7 @@ read_verilog ../common/lutram.v | |||
| hierarchy -top lutram_1w1r | ||||
| proc | ||||
| 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 | ||||
| opt -full | ||||
| 
 | ||||
|  | @ -62,7 +62,7 @@ read_verilog ../common/lutram.v | |||
| hierarchy -top lutram_1w3r | ||||
| proc | ||||
| 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 | ||||
| opt -full | ||||
| 
 | ||||
|  | @ -82,7 +82,7 @@ read_verilog ../common/lutram.v | |||
| hierarchy -top lutram_1w3r -chparam A_WIDTH 6 | ||||
| proc | ||||
| 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 | ||||
| opt -full | ||||
| 
 | ||||
|  | @ -102,7 +102,7 @@ read_verilog ../common/lutram.v | |||
| hierarchy -top lutram_1w1r -chparam A_WIDTH 5 -chparam D_WIDTH 6 | ||||
| proc | ||||
| 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 | ||||
| opt -full | ||||
| 
 | ||||
|  | @ -122,7 +122,7 @@ read_verilog ../common/lutram.v | |||
| hierarchy -top lutram_1w1r -chparam A_WIDTH 6 -chparam D_WIDTH 6 | ||||
| proc | ||||
| 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 | ||||
| opt -full | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,3 +1,6 @@ | |||
| ../../../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 | ||||
| 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 | ||||
| proc | ||||
| #equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx ### TODO | ||||
| equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx | ||||
| #equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad ### TODO | ||||
| equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||
| miter -equiv -flatten -make_assert -make_outputs gold gate 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) | ||||
|  | @ -17,8 +17,8 @@ select -assert-none t:BUFG t:FDRE t:DSP48E1 %% t:* %D | |||
| design -load read | ||||
| hierarchy -top macc2 | ||||
| proc | ||||
| #equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx ### TODO | ||||
| equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx | ||||
| #equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad ### TODO | ||||
| equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad | ||||
| miter -equiv -flatten -make_assert -make_outputs gold gate 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) | ||||
|  |  | |||
|  | @ -1,9 +1,21 @@ | |||
| read_verilog ../common/mul.v | ||||
| hierarchy -top top | ||||
| 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) | ||||
| cd top # Constrain all select calls below inside the top module | ||||
| 
 | ||||
| select -assert-count 1 t:DSP48E1 | ||||
| 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 | ||||
| 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) | ||||
| cd mul_unsigned # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:BUFG | ||||
| select -assert-count 1 t:DSP48E1 | ||||
| select -assert-count 30 t:FDRE | ||||
| 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 | ||||
| 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) | ||||
| cd mux2 # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:LUT3 | ||||
|  | @ -14,7 +14,7 @@ select -assert-none t:LUT3 %% t:* %D | |||
| design -load read | ||||
| hierarchy -top mux4 | ||||
| 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) | ||||
| cd mux4 # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:LUT6 | ||||
|  | @ -25,7 +25,7 @@ select -assert-none t:LUT6 %% t:* %D | |||
| design -load read | ||||
| hierarchy -top mux8 | ||||
| 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) | ||||
| cd mux8 # Constrain all select calls below inside the top module | ||||
| select -assert-count 1 t:LUT3 | ||||
|  | @ -37,7 +37,7 @@ select -assert-none t:LUT3 t:LUT6 %% t:* %D | |||
| design -load read | ||||
| hierarchy -top mux16 | ||||
| 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) | ||||
| cd mux16 # Constrain all select calls below inside the top module | ||||
| select -assert-min 5 t:LUT6 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ read_verilog ../common/shifter.v | |||
| hierarchy -top top | ||||
| proc | ||||
| 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) | ||||
| 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 | ||||
| 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 | ||||
| # TODO :: Tristate logic not yet supported; see https://github.com/YosysHQ/yosys/issues/1225 | ||||
| select -assert-count 1 t:$_TBUF_ | ||||
| select -assert-none t:$_TBUF_ %% t:* %D | ||||
| select -assert-count 2 t:IBUF | ||||
| select -assert-count 1 t:INV | ||||
| 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 o3 = ~io; | ||||
| 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 | ||||
| 
 | ||||
| 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 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