mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-26 09:24:37 +00:00 
			
		
		
		
	xilinx: Add xilinx_dffopt pass (#1557)
This commit is contained in:
		
							parent
							
								
									aff6ad1ce0
								
							
						
					
					
						commit
						a235250403
					
				
					 11 changed files with 638 additions and 27 deletions
				
			
		|  | @ -55,6 +55,7 @@ Yosys 0.9 .. Yosys 0.9-dev | ||||||
|     - Added "check -mapped" |     - Added "check -mapped" | ||||||
|     - Added checking of SystemVerilog always block types (always_comb, |     - Added checking of SystemVerilog always block types (always_comb, | ||||||
|       always_latch and always_ff) |       always_latch and always_ff) | ||||||
|  |     - Added "xilinx_dffopt" pass | ||||||
| 
 | 
 | ||||||
| Yosys 0.8 .. Yosys 0.9 | Yosys 0.8 .. Yosys 0.9 | ||||||
| ---------------------- | ---------------------- | ||||||
|  |  | ||||||
|  | @ -44,6 +44,10 @@ struct EquivOptPass:public ScriptPass | ||||||
| 		log("        expand the modules in this file before proving equivalence. this is\n"); | 		log("        expand the modules in this file before proving equivalence. this is\n"); | ||||||
| 		log("        useful for handling architecture-specific primitives.\n"); | 		log("        useful for handling architecture-specific primitives.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | 		log("    -blacklist <file>\n"); | ||||||
|  | 		log("        Do not match cells or signals that match the names in the file\n"); | ||||||
|  | 		log("        (passed to equiv_make).\n"); | ||||||
|  | 		log("\n"); | ||||||
| 		log("    -assert\n"); | 		log("    -assert\n"); | ||||||
| 		log("        produce an error if the circuits are not equivalent.\n"); | 		log("        produce an error if the circuits are not equivalent.\n"); | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
|  | @ -61,13 +65,14 @@ struct EquivOptPass:public ScriptPass | ||||||
| 		log("\n"); | 		log("\n"); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	std::string command, techmap_opts; | 	std::string command, techmap_opts, make_opts; | ||||||
| 	bool assert, undef, multiclock, async2sync; | 	bool assert, undef, multiclock, async2sync; | ||||||
| 
 | 
 | ||||||
| 	void clear_flags() YS_OVERRIDE | 	void clear_flags() YS_OVERRIDE | ||||||
| 	{ | 	{ | ||||||
| 		command = ""; | 		command = ""; | ||||||
| 		techmap_opts = ""; | 		techmap_opts = ""; | ||||||
|  | 		make_opts = ""; | ||||||
| 		assert = false; | 		assert = false; | ||||||
| 		undef = false; | 		undef = false; | ||||||
| 		multiclock = false; | 		multiclock = false; | ||||||
|  | @ -93,6 +98,10 @@ struct EquivOptPass:public ScriptPass | ||||||
| 				techmap_opts += " -map " + args[++argidx]; | 				techmap_opts += " -map " + args[++argidx]; | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  | 			if (args[argidx] == "-blacklist" && argidx + 1 < args.size()) { | ||||||
|  | 				make_opts += " -blacklist " + args[++argidx]; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
| 			if (args[argidx] == "-assert") { | 			if (args[argidx] == "-assert") { | ||||||
| 				assert = true; | 				assert = true; | ||||||
| 				continue; | 				continue; | ||||||
|  | @ -170,7 +179,12 @@ struct EquivOptPass:public ScriptPass | ||||||
| 				run("clk2fflogic", "(only with -multiclock)"); | 				run("clk2fflogic", "(only with -multiclock)"); | ||||||
| 			if (async2sync || help_mode) | 			if (async2sync || help_mode) | ||||||
| 				run("async2sync", " (only with -async2sync)"); | 				run("async2sync", " (only with -async2sync)"); | ||||||
| 			run("equiv_make gold gate equiv"); | 			string opts; | ||||||
|  | 			if (help_mode) | ||||||
|  | 				opts = " -blacklist <filename> ..."; | ||||||
|  | 			else | ||||||
|  | 				opts = make_opts; | ||||||
|  | 			run("equiv_make" + opts + " gold gate equiv"); | ||||||
| 			if (help_mode) | 			if (help_mode) | ||||||
| 				run("equiv_induct [-undef] equiv"); | 				run("equiv_induct [-undef] equiv"); | ||||||
| 			else if (undef) | 			else if (undef) | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| 
 | 
 | ||||||
| OBJS += techlibs/xilinx/synth_xilinx.o | OBJS += techlibs/xilinx/synth_xilinx.o | ||||||
|  | OBJS += techlibs/xilinx/xilinx_dffopt.o | ||||||
| 
 | 
 | ||||||
| GENFILES += techlibs/xilinx/brams_init_36.vh | GENFILES += techlibs/xilinx/brams_init_36.vh | ||||||
| GENFILES += techlibs/xilinx/brams_init_32.vh | GENFILES += techlibs/xilinx/brams_init_32.vh | ||||||
|  |  | ||||||
|  | @ -329,6 +329,41 @@ module FDSE ( | ||||||
|   endcase endgenerate |   endcase endgenerate | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
|  | module FDRSE ( | ||||||
|  |   output reg Q, | ||||||
|  |   (* clkbuf_sink *) | ||||||
|  |   (* invertible_pin = "IS_C_INVERTED" *) | ||||||
|  |   input C, | ||||||
|  |   (* invertible_pin = "IS_CE_INVERTED" *) | ||||||
|  |   input CE, | ||||||
|  |   (* invertible_pin = "IS_D_INVERTED" *) | ||||||
|  |   input D, | ||||||
|  |   (* invertible_pin = "IS_R_INVERTED" *) | ||||||
|  |   input R, | ||||||
|  |   (* invertible_pin = "IS_S_INVERTED" *) | ||||||
|  |   input S | ||||||
|  | ); | ||||||
|  |   parameter [0:0] INIT = 1'b0; | ||||||
|  |   parameter [0:0] IS_C_INVERTED = 1'b0; | ||||||
|  |   parameter [0:0] IS_CE_INVERTED = 1'b0; | ||||||
|  |   parameter [0:0] IS_D_INVERTED = 1'b0; | ||||||
|  |   parameter [0:0] IS_R_INVERTED = 1'b0; | ||||||
|  |   parameter [0:0] IS_S_INVERTED = 1'b0; | ||||||
|  |   initial Q <= INIT; | ||||||
|  |   wire c = C ^ IS_C_INVERTED; | ||||||
|  |   wire ce = CE ^ IS_CE_INVERTED; | ||||||
|  |   wire d = D ^ IS_D_INVERTED; | ||||||
|  |   wire r = R ^ IS_R_INVERTED; | ||||||
|  |   wire s = S ^ IS_S_INVERTED; | ||||||
|  |   always @(posedge c) | ||||||
|  |     if (r) | ||||||
|  |       Q <= 0; | ||||||
|  |     else if (s) | ||||||
|  |       Q <= 1; | ||||||
|  |     else if (ce) | ||||||
|  |       Q <= d; | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
| module FDCE ( | module FDCE ( | ||||||
|   (* abc9_arrival=303 *) |   (* abc9_arrival=303 *) | ||||||
|   output reg Q, |   output reg Q, | ||||||
|  |  | ||||||
|  | @ -66,7 +66,7 @@ CELLS = [ | ||||||
|     # CLB -- registers/latches. |     # CLB -- registers/latches. | ||||||
|     # Virtex 1/2/4/5, Spartan 3. |     # Virtex 1/2/4/5, Spartan 3. | ||||||
|     Cell('FDCPE', port_attrs={'C': ['clkbuf_sink']}), |     Cell('FDCPE', port_attrs={'C': ['clkbuf_sink']}), | ||||||
|     Cell('FDRSE', port_attrs={'C': ['clkbuf_sink']}), |     # Cell('FDRSE', port_attrs={'C': ['clkbuf_sink']}), | ||||||
|     Cell('LDCPE', port_attrs={'C': ['clkbuf_sink']}), |     Cell('LDCPE', port_attrs={'C': ['clkbuf_sink']}), | ||||||
|     # Virtex 6, Spartan 6, Series 7, Ultrascale. |     # Virtex 6, Spartan 6, Series 7, Ultrascale. | ||||||
|     # Cell('FDCE'), |     # Cell('FDCE'), | ||||||
|  |  | ||||||
|  | @ -17,27 +17,6 @@ module FDCPE (...); | ||||||
|     input PRE; |     input PRE; | ||||||
| endmodule | endmodule | ||||||
| 
 | 
 | ||||||
| module FDRSE (...); |  | ||||||
|     parameter [0:0] INIT = 1'b0; |  | ||||||
|     parameter [0:0] IS_C_INVERTED = 1'b0; |  | ||||||
|     parameter [0:0] IS_CE_INVERTED = 1'b0; |  | ||||||
|     parameter [0:0] IS_D_INVERTED = 1'b0; |  | ||||||
|     parameter [0:0] IS_R_INVERTED = 1'b0; |  | ||||||
|     parameter [0:0] IS_S_INVERTED = 1'b0; |  | ||||||
|     output Q; |  | ||||||
|     (* clkbuf_sink *) |  | ||||||
|     (* invertible_pin = "IS_C_INVERTED" *) |  | ||||||
|     input C; |  | ||||||
|     (* invertible_pin = "IS_CE_INVERTED" *) |  | ||||||
|     input CE; |  | ||||||
|     (* invertible_pin = "IS_D_INVERTED" *) |  | ||||||
|     input D; |  | ||||||
|     (* invertible_pin = "IS_R_INVERTED" *) |  | ||||||
|     input R; |  | ||||||
|     (* invertible_pin = "IS_S_INVERTED" *) |  | ||||||
|     input S; |  | ||||||
| endmodule |  | ||||||
| 
 |  | ||||||
| module LDCPE (...); | module LDCPE (...); | ||||||
|     parameter [0:0] INIT = 1'b0; |     parameter [0:0] INIT = 1'b0; | ||||||
|     parameter [0:0] IS_CLR_INVERTED = 1'b0; |     parameter [0:0] IS_CLR_INVERTED = 1'b0; | ||||||
|  |  | ||||||
|  | @ -570,6 +570,7 @@ struct SynthXilinxPass : public ScriptPass | ||||||
| 			else | 			else | ||||||
| 				techmap_args += " -map " + ff_map_file; | 				techmap_args += " -map " + ff_map_file; | ||||||
| 			run("techmap " + techmap_args); | 			run("techmap " + techmap_args); | ||||||
|  | 			run("xilinx_dffopt"); | ||||||
| 			run("clean"); | 			run("clean"); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										351
									
								
								techlibs/xilinx/xilinx_dffopt.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										351
									
								
								techlibs/xilinx/xilinx_dffopt.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,351 @@ | ||||||
|  | /*
 | ||||||
|  |  *  yosys -- Yosys Open SYnthesis Suite | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> | ||||||
|  |  * | ||||||
|  |  *  Permission to use, copy, modify, and/or distribute this software for any | ||||||
|  |  *  purpose with or without fee is hereby granted, provided that the above | ||||||
|  |  *  copyright notice and this permission notice appear in all copies. | ||||||
|  |  * | ||||||
|  |  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||||
|  |  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||||
|  |  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||||
|  |  *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  |  *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||||
|  |  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||||
|  |  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "kernel/yosys.h" | ||||||
|  | #include "kernel/sigtools.h" | ||||||
|  | 
 | ||||||
|  | USING_YOSYS_NAMESPACE | ||||||
|  | PRIVATE_NAMESPACE_BEGIN | ||||||
|  | 
 | ||||||
|  | typedef std::pair<Const, std::vector<SigBit>> LutData; | ||||||
|  | 
 | ||||||
|  | // Compute a LUT implementing (select ^ select_inv) ? alt_data : data.  Returns true if successful.
 | ||||||
|  | bool merge_lut(LutData &result, const LutData &data, const LutData select, bool select_inv, SigBit alt_data, int max_lut_size) { | ||||||
|  | 	// First, gather input signals.
 | ||||||
|  | 	result.second = data.second; | ||||||
|  | 	int idx_alt = -1; | ||||||
|  | 	if (alt_data.wire) { | ||||||
|  | 		// Check if we already have it.
 | ||||||
|  | 		for (int i = 0; i < GetSize(result.second); i++) | ||||||
|  | 			if (result.second[i] == alt_data) | ||||||
|  | 				idx_alt = i; | ||||||
|  | 		// If not, add it.
 | ||||||
|  | 		if (idx_alt == -1) { | ||||||
|  | 			idx_alt = GetSize(result.second); | ||||||
|  | 			result.second.push_back(alt_data); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	std::vector<int> idx_sel; | ||||||
|  | 	for (auto bit : select.second) { | ||||||
|  | 		int idx = -1; | ||||||
|  | 		for (int i = 0; i < GetSize(result.second); i++) | ||||||
|  | 			if (result.second[i] == bit) | ||||||
|  | 				idx = i; | ||||||
|  | 		if (idx == -1) { | ||||||
|  | 			idx = GetSize(result.second); | ||||||
|  | 			result.second.push_back(bit); | ||||||
|  | 		} | ||||||
|  | 		idx_sel.push_back(idx); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// If LUT would be too large, bail.
 | ||||||
|  | 	if (GetSize(result.second) > max_lut_size) | ||||||
|  | 		return false; | ||||||
|  | 
 | ||||||
|  | 	// Okay, we're doing it — compute the LUT mask.
 | ||||||
|  | 	result.first = Const(0, 1 << GetSize(result.second)); | ||||||
|  | 	for (int i = 0; i < GetSize(result.first); i++) { | ||||||
|  | 		int sel_lut_idx = 0; | ||||||
|  | 		for (int j = 0; j < GetSize(select.second); j++) | ||||||
|  | 			if (i & 1 << idx_sel[j]) | ||||||
|  | 				sel_lut_idx |= 1 << j; | ||||||
|  | 		bool select_val = (select.first.bits[sel_lut_idx] == State::S1); | ||||||
|  | 		bool new_bit; | ||||||
|  | 		if (select_val ^ select_inv) { | ||||||
|  | 			// Use alt_data.
 | ||||||
|  | 			if (alt_data.wire) | ||||||
|  | 				new_bit = (i & 1 << idx_alt) != 0; | ||||||
|  | 			else | ||||||
|  | 				new_bit = alt_data.data == State::S1; | ||||||
|  | 		} else { | ||||||
|  | 			// Use original LUT.
 | ||||||
|  | 			int lut_idx = i & ((1 << GetSize(data.second)) - 1); | ||||||
|  | 			new_bit = data.first.bits[lut_idx] == State::S1; | ||||||
|  | 		} | ||||||
|  | 		result.first.bits[i] = new_bit ? State::S1 : State::S0; | ||||||
|  | 	} | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct XilinxDffOptPass : public Pass { | ||||||
|  | 	XilinxDffOptPass() : Pass("xilinx_dffopt", "Xilinx: optimize FF control signal usage") { } | ||||||
|  | 	void help() YS_OVERRIDE | ||||||
|  | 	{ | ||||||
|  | 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    xilinx_dffopt [options] [selection]\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("Converts hardware clock enable and set/reset signals on FFs to emulation\n"); | ||||||
|  | 		log("using LUTs, if doing so would improve area.  Operates on post-techmap Xilinx\n"); | ||||||
|  | 		log("cells (LUT*, FD*).\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 		log("    -lut4\n"); | ||||||
|  | 		log("        Assume a LUT4-based device (instead of a LUT6-based device).\n"); | ||||||
|  | 		log("\n"); | ||||||
|  | 	} | ||||||
|  | 	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | ||||||
|  | 	{ | ||||||
|  | 		log_header(design, "Executing XILINX_DFFOPT pass (optimize FF control signal usage).\n"); | ||||||
|  | 
 | ||||||
|  | 		size_t argidx; | ||||||
|  | 		int max_lut_size = 6; | ||||||
|  | 		for (argidx = 1; argidx < args.size(); argidx++) | ||||||
|  | 		{ | ||||||
|  | 			if (args[argidx] == "-lut4") { | ||||||
|  | 				max_lut_size = 4; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		extra_args(args, argidx, design); | ||||||
|  | 
 | ||||||
|  | 		for (auto module : design->selected_modules()) | ||||||
|  | 		{ | ||||||
|  | 			log("Optimizing FFs in %s.\n", log_id(module)); | ||||||
|  | 
 | ||||||
|  | 			SigMap sigmap(module); | ||||||
|  | 			dict<SigBit, pair<LutData, Cell *>> bit_to_lut; | ||||||
|  | 			dict<SigBit, int> bit_uses; | ||||||
|  | 
 | ||||||
|  | 			// Gather LUTs.
 | ||||||
|  | 			for (auto cell : module->selected_cells()) | ||||||
|  | 			{ | ||||||
|  | 				for (auto port : cell->connections()) | ||||||
|  | 					for (auto bit : port.second) | ||||||
|  | 						bit_uses[sigmap(bit)]++; | ||||||
|  | 				if (cell->get_bool_attribute(ID::keep)) | ||||||
|  | 					continue; | ||||||
|  | 				if (cell->type == ID(INV)) { | ||||||
|  | 					SigBit sigout = sigmap(cell->getPort(ID(O))); | ||||||
|  | 					SigBit sigin = sigmap(cell->getPort(ID(I))); | ||||||
|  | 					bit_to_lut[sigout] = make_pair(LutData(Const(1, 2), {sigin}), cell); | ||||||
|  | 				} else if (cell->type.in(ID(LUT1), ID(LUT2), ID(LUT3), ID(LUT4), ID(LUT5), ID(LUT6))) { | ||||||
|  | 					SigBit sigout = sigmap(cell->getPort(ID(O))); | ||||||
|  | 					const Const &init = cell->getParam(ID(INIT)); | ||||||
|  | 					std::vector<SigBit> sigin; | ||||||
|  | 					sigin.push_back(sigmap(cell->getPort(ID(I0)))); | ||||||
|  | 					if (cell->type == ID(LUT1)) | ||||||
|  | 						goto lut_sigin_done; | ||||||
|  | 					sigin.push_back(sigmap(cell->getPort(ID(I1)))); | ||||||
|  | 					if (cell->type == ID(LUT2)) | ||||||
|  | 						goto lut_sigin_done; | ||||||
|  | 					sigin.push_back(sigmap(cell->getPort(ID(I2)))); | ||||||
|  | 					if (cell->type == ID(LUT3)) | ||||||
|  | 						goto lut_sigin_done; | ||||||
|  | 					sigin.push_back(sigmap(cell->getPort(ID(I3)))); | ||||||
|  | 					if (cell->type == ID(LUT4)) | ||||||
|  | 						goto lut_sigin_done; | ||||||
|  | 					sigin.push_back(sigmap(cell->getPort(ID(I4)))); | ||||||
|  | 					if (cell->type == ID(LUT5)) | ||||||
|  | 						goto lut_sigin_done; | ||||||
|  | 					sigin.push_back(sigmap(cell->getPort(ID(I5)))); | ||||||
|  | lut_sigin_done: | ||||||
|  | 					bit_to_lut[sigout] = make_pair(LutData(init, sigin), cell); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			for (auto wire : module->wires()) | ||||||
|  | 				if (wire->port_output || wire->port_input) | ||||||
|  | 					for (int i = 0; i < GetSize(wire); i++) | ||||||
|  | 						bit_uses[sigmap(SigBit(wire, i))]++; | ||||||
|  | 
 | ||||||
|  | 			// Iterate through FFs.
 | ||||||
|  | 			for (auto cell : module->selected_cells()) | ||||||
|  | 			{ | ||||||
|  | 				bool has_s = false, has_r = false; | ||||||
|  | 				if (cell->type.in(ID(FDCE), ID(FDPE), ID(FDCPE), ID(FDCE_1), ID(FDPE_1), ID(FDCPE_1))) { | ||||||
|  | 					// Async reset.
 | ||||||
|  | 				} else if (cell->type.in(ID(FDRE), ID(FDRE_1))) { | ||||||
|  | 					has_r = true; | ||||||
|  | 				} else if (cell->type.in(ID(FDSE), ID(FDSE_1))) { | ||||||
|  | 					has_s = true; | ||||||
|  | 				} else if (cell->type.in(ID(FDRSE), ID(FDRSE_1))) { | ||||||
|  | 					has_r = true; | ||||||
|  | 					has_s = true; | ||||||
|  | 				} else { | ||||||
|  | 					// Not a FF.
 | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
|  | 				if (cell->get_bool_attribute(ID::keep)) | ||||||
|  | 					continue; | ||||||
|  | 
 | ||||||
|  | 				// Don't bother if D has more than one use.
 | ||||||
|  | 				SigBit sig_D = sigmap(cell->getPort(ID(D))); | ||||||
|  | 				if (bit_uses[sig_D] > 2) | ||||||
|  | 					continue; | ||||||
|  | 
 | ||||||
|  | 				// Find the D LUT.
 | ||||||
|  | 				auto it_D = bit_to_lut.find(sig_D); | ||||||
|  | 				if (it_D == bit_to_lut.end()) | ||||||
|  | 					continue; | ||||||
|  | 				LutData lut_d = it_D->second.first; | ||||||
|  | 				Cell *cell_d = it_D->second.second; | ||||||
|  | 				if (cell->hasParam(ID(IS_D_INVERTED)) && cell->getParam(ID(IS_D_INVERTED)).as_bool()) { | ||||||
|  | 					// Flip all bits in the LUT.
 | ||||||
|  | 					for (int i = 0; i < GetSize(lut_d.first); i++) | ||||||
|  | 						lut_d.first.bits[i] = (lut_d.first.bits[i] == State::S1) ? State::S0 : State::S1; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				LutData lut_d_post_ce; | ||||||
|  | 				LutData lut_d_post_s; | ||||||
|  | 				LutData lut_d_post_r; | ||||||
|  | 				bool worthy_post_ce = false; | ||||||
|  | 				bool worthy_post_s = false; | ||||||
|  | 				bool worthy_post_r = false; | ||||||
|  | 
 | ||||||
|  | 				// First, unmap CE.
 | ||||||
|  | 				SigBit sig_Q = sigmap(cell->getPort(ID(Q))); | ||||||
|  | 				SigBit sig_CE = sigmap(cell->getPort(ID(CE))); | ||||||
|  | 				LutData lut_ce = LutData(Const(2, 2), {sig_CE}); | ||||||
|  | 				auto it_CE = bit_to_lut.find(sig_CE); | ||||||
|  | 				if (it_CE != bit_to_lut.end()) | ||||||
|  | 					lut_ce = it_CE->second.first; | ||||||
|  | 				if (sig_CE.wire) { | ||||||
|  | 					// Merge CE LUT and D LUT into one.  If it cannot be done, nothing to do about this FF.
 | ||||||
|  | 					if (!merge_lut(lut_d_post_ce, lut_d, lut_ce, true, sig_Q, max_lut_size)) | ||||||
|  | 						continue; | ||||||
|  | 
 | ||||||
|  | 					// If this gets rid of a CE LUT, it's worth it.  If not, it still may be worth it, if we can remove set/reset as well.
 | ||||||
|  | 					if (it_CE != bit_to_lut.end()) | ||||||
|  | 						worthy_post_ce = true; | ||||||
|  | 				} else if (sig_CE.data != State::S1) { | ||||||
|  | 					// Strange.  Should not happen in a reasonable flow, so bail.
 | ||||||
|  | 					continue; | ||||||
|  | 				} else { | ||||||
|  | 					lut_d_post_ce = lut_d; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// Second, unmap S, if any.
 | ||||||
|  | 				lut_d_post_s = lut_d_post_ce; | ||||||
|  | 				if (has_s) { | ||||||
|  | 					SigBit sig_S = sigmap(cell->getPort(ID(S))); | ||||||
|  | 					LutData lut_s = LutData(Const(2, 2), {sig_S}); | ||||||
|  | 					bool inv_s = cell->hasParam(ID(IS_S_INVERTED)) && cell->getParam(ID(IS_S_INVERTED)).as_bool(); | ||||||
|  | 					auto it_S = bit_to_lut.find(sig_S); | ||||||
|  | 					if (it_S != bit_to_lut.end()) | ||||||
|  | 						lut_s = it_S->second.first; | ||||||
|  | 					if (sig_S.wire) { | ||||||
|  | 						// Merge S LUT and D LUT into one.  If it cannot be done, try to at least merge CE.
 | ||||||
|  | 						if (!merge_lut(lut_d_post_s, lut_d_post_ce, lut_s, inv_s, SigBit(State::S1), max_lut_size)) | ||||||
|  | 							goto unmap; | ||||||
|  | 						// If this gets rid of an S LUT, it's worth it.
 | ||||||
|  | 						if (it_S != bit_to_lut.end()) | ||||||
|  | 							worthy_post_s = true; | ||||||
|  | 					} else if (sig_S.data != (inv_s ? State::S1 : State::S0)) { | ||||||
|  | 						// Strange.  Should not happen in a reasonable flow, so bail.
 | ||||||
|  | 						continue; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// Third, unmap R, if any.
 | ||||||
|  | 				lut_d_post_r = lut_d_post_s; | ||||||
|  | 				if (has_r) { | ||||||
|  | 					SigBit sig_R = sigmap(cell->getPort(ID(R))); | ||||||
|  | 					LutData lut_r = LutData(Const(2, 2), {sig_R}); | ||||||
|  | 					bool inv_r = cell->hasParam(ID(IS_R_INVERTED)) && cell->getParam(ID(IS_R_INVERTED)).as_bool(); | ||||||
|  | 					auto it_R = bit_to_lut.find(sig_R); | ||||||
|  | 					if (it_R != bit_to_lut.end()) | ||||||
|  | 						lut_r = it_R->second.first; | ||||||
|  | 					if (sig_R.wire) { | ||||||
|  | 						// Merge R LUT and D LUT into one.  If it cannot be done, try to at least merge CE/S.
 | ||||||
|  | 						if (!merge_lut(lut_d_post_r, lut_d_post_s, lut_r, inv_r, SigBit(State::S0), max_lut_size)) | ||||||
|  | 							goto unmap; | ||||||
|  | 						// If this gets rid of an S LUT, it's worth it.
 | ||||||
|  | 						if (it_R != bit_to_lut.end()) | ||||||
|  | 							worthy_post_r = true; | ||||||
|  | 					} else if (sig_R.data != (inv_r ? State::S1 : State::S0)) { | ||||||
|  | 						// Strange.  Should not happen in a reasonable flow, so bail.
 | ||||||
|  | 						continue; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | unmap: | ||||||
|  | 				LutData final_lut; | ||||||
|  | 				if (worthy_post_r) { | ||||||
|  | 					final_lut = lut_d_post_r; | ||||||
|  | 					log("  Merging R LUT for %s/%s (%d -> %d)\n", log_id(cell), log_id(sig_Q.wire), GetSize(lut_d.second), GetSize(final_lut.second)); | ||||||
|  | 				} else if (worthy_post_s) { | ||||||
|  | 					final_lut = lut_d_post_s; | ||||||
|  | 					log("  Merging S LUT for %s/%s (%d -> %d)\n", log_id(cell), log_id(sig_Q.wire), GetSize(lut_d.second), GetSize(final_lut.second)); | ||||||
|  | 				} else if (worthy_post_ce) { | ||||||
|  | 					final_lut = lut_d_post_ce; | ||||||
|  | 					log("  Merging CE LUT for %s/%s (%d -> %d)\n", log_id(cell), log_id(sig_Q.wire), GetSize(lut_d.second), GetSize(final_lut.second)); | ||||||
|  | 				} else { | ||||||
|  | 					// Nothing to do here.
 | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// Okay, we're doing it.  Unmap ports.
 | ||||||
|  | 				if (worthy_post_r) { | ||||||
|  | 					cell->unsetParam(ID(IS_R_INVERTED)); | ||||||
|  | 					cell->setPort(ID(R), Const(0, 1)); | ||||||
|  | 				} | ||||||
|  | 				if (has_s && (worthy_post_r || worthy_post_s)) { | ||||||
|  | 					cell->unsetParam(ID(IS_S_INVERTED)); | ||||||
|  | 					cell->setPort(ID(S), Const(0, 1)); | ||||||
|  | 				} | ||||||
|  | 				cell->setPort(ID(CE), Const(1, 1)); | ||||||
|  | 				cell->unsetParam(ID(IS_D_INVERTED)); | ||||||
|  | 
 | ||||||
|  | 				// Create the new LUT.
 | ||||||
|  | 				Cell *lut_cell = 0; | ||||||
|  | 				switch (GetSize(final_lut.second)) { | ||||||
|  | 					case 1: | ||||||
|  | 						lut_cell = module->addCell(NEW_ID, ID(LUT1)); | ||||||
|  | 						break; | ||||||
|  | 					case 2: | ||||||
|  | 						lut_cell = module->addCell(NEW_ID, ID(LUT2)); | ||||||
|  | 						break; | ||||||
|  | 					case 3: | ||||||
|  | 						lut_cell = module->addCell(NEW_ID, ID(LUT3)); | ||||||
|  | 						break; | ||||||
|  | 					case 4: | ||||||
|  | 						lut_cell = module->addCell(NEW_ID, ID(LUT4)); | ||||||
|  | 						break; | ||||||
|  | 					case 5: | ||||||
|  | 						lut_cell = module->addCell(NEW_ID, ID(LUT5)); | ||||||
|  | 						break; | ||||||
|  | 					case 6: | ||||||
|  | 						lut_cell = module->addCell(NEW_ID, ID(LUT6)); | ||||||
|  | 						break; | ||||||
|  | 					default: | ||||||
|  | 						log_assert(!"unknown lut size"); | ||||||
|  | 				} | ||||||
|  | 				lut_cell->attributes = cell_d->attributes; | ||||||
|  | 				Wire *lut_out = module->addWire(NEW_ID); | ||||||
|  | 				lut_cell->setParam(ID(INIT), final_lut.first); | ||||||
|  | 				cell->setPort(ID(D), lut_out); | ||||||
|  | 				lut_cell->setPort(ID(O), lut_out); | ||||||
|  | 				lut_cell->setPort(ID(I0), final_lut.second[0]); | ||||||
|  | 				if (GetSize(final_lut.second) >= 2) | ||||||
|  | 					lut_cell->setPort(ID(I1), final_lut.second[1]); | ||||||
|  | 				if (GetSize(final_lut.second) >= 3) | ||||||
|  | 					lut_cell->setPort(ID(I2), final_lut.second[2]); | ||||||
|  | 				if (GetSize(final_lut.second) >= 4) | ||||||
|  | 					lut_cell->setPort(ID(I3), final_lut.second[3]); | ||||||
|  | 				if (GetSize(final_lut.second) >= 5) | ||||||
|  | 					lut_cell->setPort(ID(I4), final_lut.second[4]); | ||||||
|  | 				if (GetSize(final_lut.second) >= 6) | ||||||
|  | 					lut_cell->setPort(ID(I5), final_lut.second[5]); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } XilinxDffOptPass; | ||||||
|  | 
 | ||||||
|  | PRIVATE_NAMESPACE_END | ||||||
|  | 
 | ||||||
|  | @ -14,6 +14,6 @@ select -assert-count 1 t:BUFG | ||||||
| select -assert-count 4 t:FDRE | select -assert-count 4 t:FDRE | ||||||
| select -assert-count 1 t:FDSE | select -assert-count 1 t:FDSE | ||||||
| select -assert-count 1 t:LUT2 | select -assert-count 1 t:LUT2 | ||||||
| select -assert-count 2 t:LUT3 | select -assert-count 3 t:LUT5 | ||||||
| select -assert-count 4 t:LUT5 | select -assert-count 1 t:LUT6 | ||||||
| select -assert-none t:BUFG t:FDRE t:FDSE t:LUT2 t:LUT3 t:LUT5 %% t:* %D | select -assert-none t:BUFG t:FDRE t:FDSE t:LUT2 t:LUT5 t:LUT6 %% t:* %D | ||||||
|  |  | ||||||
							
								
								
									
										216
									
								
								tests/arch/xilinx/xilinx_dffopt.ys
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								tests/arch/xilinx/xilinx_dffopt.ys
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,216 @@ | ||||||
|  | read_verilog << EOT | ||||||
|  | 
 | ||||||
|  | // FDRE, mergeable CE and R. | ||||||
|  | 
 | ||||||
|  | module t0 (...); | ||||||
|  | input wire clk; | ||||||
|  | input wire [7:0] i; | ||||||
|  | output wire [7:0] o; | ||||||
|  | 
 | ||||||
|  | wire [7:0] tmp ; | ||||||
|  | 
 | ||||||
|  | LUT2 #(.INIT(4'h6)) lut0 (.I0(i[0]), .I1(i[1]), .O(tmp[0])); | ||||||
|  | LUT2 #(.INIT(4'h6)) lut1 (.I0(i[1]), .I1(i[2]), .O(tmp[1])); | ||||||
|  | LUT2 #(.INIT(4'h6)) lut2 (.I0(i[3]), .I1(i[4]), .O(tmp[2])); | ||||||
|  | 
 | ||||||
|  | FDRE ff (.D(tmp[0]), .CE(tmp[1]), .R(tmp[2]), .Q(o[0])); | ||||||
|  | 
 | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | EOT | ||||||
|  | 
 | ||||||
|  | design -save t0 | ||||||
|  | 
 | ||||||
|  | equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt | ||||||
|  | design -load postopt | ||||||
|  | clean | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 t:FDRE | ||||||
|  | select -assert-count 1 t:LUT6 | ||||||
|  | select -assert-count 3 t:LUT2 | ||||||
|  | select -assert-none t:FDRE t:LUT6 t:LUT2 %% t:* %D | ||||||
|  | 
 | ||||||
|  | design -load t0 | ||||||
|  | 
 | ||||||
|  | equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt -lut4 | ||||||
|  | design -load postopt | ||||||
|  | clean | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 t:FDRE | ||||||
|  | select -assert-count 1 t:LUT4 | ||||||
|  | select -assert-count 3 t:LUT2 | ||||||
|  | select -assert-none t:FDRE t:LUT4 t:LUT2 %% t:* %D | ||||||
|  | 
 | ||||||
|  | design -reset | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | read_verilog << EOT | ||||||
|  | 
 | ||||||
|  | // FDSE, mergeable CE and S, inversions. | ||||||
|  | 
 | ||||||
|  | module t0 (...); | ||||||
|  | input wire clk; | ||||||
|  | input wire [7:0] i; | ||||||
|  | output wire [7:0] o; | ||||||
|  | 
 | ||||||
|  | wire [7:0] tmp ; | ||||||
|  | 
 | ||||||
|  | LUT2 #(.INIT(4'h6)) lut0 (.I0(i[0]), .I1(i[1]), .O(tmp[0])); | ||||||
|  | LUT2 #(.INIT(4'h6)) lut1 (.I0(i[1]), .I1(i[2]), .O(tmp[1])); | ||||||
|  | LUT2 #(.INIT(4'h6)) lut2 (.I0(i[3]), .I1(i[4]), .O(tmp[2])); | ||||||
|  | 
 | ||||||
|  | FDSE #(.IS_D_INVERTED(1'b1), .IS_S_INVERTED(1'b1)) ff (.D(tmp[0]), .CE(tmp[1]), .S(tmp[2]), .Q(o[0])); | ||||||
|  | 
 | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | EOT | ||||||
|  | 
 | ||||||
|  | design -save t0 | ||||||
|  | 
 | ||||||
|  | equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt | ||||||
|  | design -load postopt | ||||||
|  | clean | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 t:FDSE | ||||||
|  | select -assert-count 1 t:LUT6 | ||||||
|  | select -assert-count 3 t:LUT2 | ||||||
|  | select -assert-none t:FDSE t:LUT6 t:LUT2 %% t:* %D | ||||||
|  | 
 | ||||||
|  | design -load t0 | ||||||
|  | 
 | ||||||
|  | equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt -lut4 | ||||||
|  | design -load postopt | ||||||
|  | clean | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 t:FDSE | ||||||
|  | select -assert-count 1 t:LUT4 | ||||||
|  | select -assert-count 3 t:LUT2 | ||||||
|  | select -assert-none t:FDSE t:LUT4 t:LUT2 %% t:* %D | ||||||
|  | 
 | ||||||
|  | design -reset | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | read_verilog << EOT | ||||||
|  | 
 | ||||||
|  | // FDCE, mergeable CE. | ||||||
|  | 
 | ||||||
|  | module t0 (...); | ||||||
|  | input wire clk; | ||||||
|  | input wire [7:0] i; | ||||||
|  | output wire [7:0] o; | ||||||
|  | 
 | ||||||
|  | wire [7:0] tmp ; | ||||||
|  | 
 | ||||||
|  | LUT2 #(.INIT(4'h6)) lut0 (.I0(i[0]), .I1(i[1]), .O(tmp[0])); | ||||||
|  | LUT2 #(.INIT(4'h6)) lut1 (.I0(i[1]), .I1(i[2]), .O(tmp[1])); | ||||||
|  | LUT2 #(.INIT(4'h6)) lut2 (.I0(i[3]), .I1(i[4]), .O(tmp[2])); | ||||||
|  | 
 | ||||||
|  | FDCE ff (.D(tmp[0]), .CE(tmp[1]), .CLR(tmp[2]), .Q(o[0])); | ||||||
|  | 
 | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | EOT | ||||||
|  | 
 | ||||||
|  | design -save t0 | ||||||
|  | 
 | ||||||
|  | equiv_opt -async2sync -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt | ||||||
|  | design -load postopt | ||||||
|  | clean | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 t:FDCE | ||||||
|  | select -assert-count 1 t:LUT4 | ||||||
|  | select -assert-count 3 t:LUT2 | ||||||
|  | select -assert-none t:FDCE t:LUT4 t:LUT2 %% t:* %D | ||||||
|  | 
 | ||||||
|  | design -reset | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | read_verilog << EOT | ||||||
|  | 
 | ||||||
|  | // FDSE, mergeable CE and S, but CE only not worth it. | ||||||
|  | 
 | ||||||
|  | module t0 (...); | ||||||
|  | input wire clk; | ||||||
|  | input wire [7:0] i; | ||||||
|  | output wire [7:0] o; | ||||||
|  | 
 | ||||||
|  | wire [7:0] tmp ; | ||||||
|  | 
 | ||||||
|  | LUT2 #(.INIT(4'h6)) lut0 (.I0(i[0]), .I1(i[1]), .O(tmp[0])); | ||||||
|  | LUT2 #(.INIT(4'h6)) lut1 (.I0(i[1]), .I1(i[2]), .O(tmp[1])); | ||||||
|  | 
 | ||||||
|  | FDSE ff (.D(tmp[0]), .CE(i[7]), .S(tmp[1]), .Q(o[0])); | ||||||
|  | 
 | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | EOT | ||||||
|  | 
 | ||||||
|  | design -save t0 | ||||||
|  | 
 | ||||||
|  | equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt | ||||||
|  | design -load postopt | ||||||
|  | clean | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 t:FDSE | ||||||
|  | select -assert-count 1 t:LUT5 | ||||||
|  | select -assert-count 2 t:LUT2 | ||||||
|  | select -assert-none t:FDSE t:LUT5 t:LUT2 %% t:* %D | ||||||
|  | 
 | ||||||
|  | design -load t0 | ||||||
|  | 
 | ||||||
|  | equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt -lut4 | ||||||
|  | design -load postopt | ||||||
|  | clean | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 t:FDSE | ||||||
|  | select -assert-count 2 t:LUT2 | ||||||
|  | select -assert-none t:FDSE t:LUT2 %% t:* %D | ||||||
|  | 
 | ||||||
|  | design -reset | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | read_verilog << EOT | ||||||
|  | 
 | ||||||
|  | // FDRSE, mergeable CE, S, R. | ||||||
|  | 
 | ||||||
|  | module t0 (...); | ||||||
|  | input wire clk; | ||||||
|  | input wire [7:0] i; | ||||||
|  | output wire [7:0] o; | ||||||
|  | 
 | ||||||
|  | wire [7:0] tmp ; | ||||||
|  | 
 | ||||||
|  | LUT2 #(.INIT(4'h6)) lut0 (.I0(i[0]), .I1(i[1]), .O(tmp[0])); | ||||||
|  | LUT2 #(.INIT(4'h6)) lut1 (.I0(i[1]), .I1(i[2]), .O(tmp[1])); | ||||||
|  | LUT2 #(.INIT(4'h8)) lut2 (.I0(i[2]), .I1(i[0]), .O(tmp[2])); | ||||||
|  | LUT2 #(.INIT(4'h6)) lut3 (.I0(i[3]), .I1(i[4]), .O(tmp[3])); | ||||||
|  | 
 | ||||||
|  | FDRSE ff (.D(tmp[0]), .CE(tmp[1]), .S(tmp[2]), .R(tmp[3]), .Q(o[0])); | ||||||
|  | 
 | ||||||
|  | endmodule | ||||||
|  | 
 | ||||||
|  | EOT | ||||||
|  | 
 | ||||||
|  | design -save t0 | ||||||
|  | 
 | ||||||
|  | equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt | ||||||
|  | design -load postopt | ||||||
|  | clean | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 t:FDRSE | ||||||
|  | select -assert-count 1 t:LUT6 | ||||||
|  | select -assert-count 4 t:LUT2 | ||||||
|  | select -assert-none t:FDRSE t:LUT6 t:LUT2 %% t:* %D | ||||||
|  | 
 | ||||||
|  | design -load t0 | ||||||
|  | 
 | ||||||
|  | equiv_opt -blacklist xilinx_dffopt_blacklist.txt -assert -map +/xilinx/cells_sim.v xilinx_dffopt -lut4 | ||||||
|  | design -load postopt | ||||||
|  | clean | ||||||
|  | 
 | ||||||
|  | select -assert-count 1 t:FDRSE | ||||||
|  | select -assert-count 1 t:LUT4 | ||||||
|  | select -assert-count 4 t:LUT2 | ||||||
|  | select -assert-none t:FDRSE t:LUT4 t:LUT2 %% t:* %D | ||||||
|  | 
 | ||||||
|  | design -reset | ||||||
							
								
								
									
										13
									
								
								tests/arch/xilinx/xilinx_dffopt_blacklist.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tests/arch/xilinx/xilinx_dffopt_blacklist.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | ||||||
|  | lut0 | ||||||
|  | lut1 | ||||||
|  | lut2 | ||||||
|  | lut3 | ||||||
|  | ff | ||||||
|  | ff.D | ||||||
|  | ff.R | ||||||
|  | ff.S | ||||||
|  | ff.CE | ||||||
|  | ff.d | ||||||
|  | ff.r | ||||||
|  | ff.s | ||||||
|  | ff.ce | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue