mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 11:42:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			225 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			225 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  *  yosys -- Yosys Open SYnthesis Suite
 | |
|  *
 | |
|  *  Copyright (C) 2012  Claire Xenia Wolf <claire@yosyshq.com>
 | |
|  *
 | |
|  *  Permission to use, copy, modify, and/or distribute this software for any
 | |
|  *  purpose with or without fee is hereby granted, provided that the above
 | |
|  *  copyright notice and this permission notice appear in all copies.
 | |
|  *
 | |
|  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | |
|  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | |
|  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | |
|  *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | |
|  *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | |
|  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | |
|  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "kernel/yosys.h"
 | |
| #include "kernel/sigtools.h"
 | |
| #include "kernel/mem.h"
 | |
| #include "kernel/ff.h"
 | |
| 
 | |
| USING_YOSYS_NAMESPACE
 | |
| PRIVATE_NAMESPACE_BEGIN
 | |
| 
 | |
| struct OptMemPass : public Pass {
 | |
| 	OptMemPass() : Pass("opt_mem", "optimize memories") { }
 | |
| 	void help() override
 | |
| 	{
 | |
| 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | |
| 		log("\n");
 | |
| 		log("    opt_mem [options] [selection]\n");
 | |
| 		log("\n");
 | |
| 		log("This pass performs various optimizations on memories in the design.\n");
 | |
| 		log("\n");
 | |
| 	}
 | |
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) override
 | |
| 	{
 | |
| 		log_header(design, "Executing OPT_MEM pass (optimize memories).\n");
 | |
| 
 | |
| 		size_t argidx;
 | |
| 		for (argidx = 1; argidx < args.size(); argidx++) {
 | |
| 			// if (args[argidx] == "-nomux") {
 | |
| 			// 	mode_nomux = true;
 | |
| 			// 	continue;
 | |
| 			// }
 | |
| 			break;
 | |
| 		}
 | |
| 		extra_args(args, argidx, design);
 | |
| 
 | |
| 		int total_count = 0;
 | |
| 		for (auto module : design->selected_modules()) {
 | |
| 			if (module->has_processes_warn())
 | |
| 				continue;
 | |
| 
 | |
| 			SigMap sigmap(module);
 | |
| 			FfInitVals initvals(&sigmap, module);
 | |
| 			for (auto &mem : Mem::get_selected_memories(module)) {
 | |
| 				std::vector<bool> always_0(mem.width, true);
 | |
| 				std::vector<bool> always_1(mem.width, true);
 | |
| 				bool changed = false;
 | |
| 				for (auto &port : mem.wr_ports) {
 | |
| 					if (port.en.is_fully_zero()) {
 | |
| 						port.removed = true;
 | |
| 						changed = true;
 | |
| 						total_count++;
 | |
| 					} else {
 | |
| 						for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
 | |
| 							for (int i = 0; i < mem.width; i++) {
 | |
| 								int bit = sub * mem.width + i;
 | |
| 								if (port.en[bit] != State::S0) {
 | |
| 									if (port.data[bit] != State::Sx && port.data[bit] != State::S0) {
 | |
| 										always_0[i] = false;
 | |
| 									}
 | |
| 									if (port.data[bit] != State::Sx && port.data[bit] != State::S1) {
 | |
| 										always_1[i] = false;
 | |
| 									}
 | |
| 								} else {
 | |
| 									if (port.data[bit] != State::Sx) {
 | |
| 										port.data[bit] = State::Sx;
 | |
| 										changed = true;
 | |
| 										total_count++;
 | |
| 									}
 | |
| 								}
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				for (auto &init : mem.inits) {
 | |
| 					for (int i = 0; i < GetSize(init.data); i++) {
 | |
| 						State bit = init.data[i];
 | |
| 						int lane = i % mem.width;
 | |
| 						if (bit != State::Sx && bit != State::S0) {
 | |
| 							always_0[lane] = false;
 | |
| 						}
 | |
| 						if (bit != State::Sx && bit != State::S1) {
 | |
| 							always_1[lane] = false;
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				std::vector<int> swizzle;
 | |
| 				for (int i = 0; i < mem.width; i++) {
 | |
| 					if (!always_0[i] && !always_1[i]) {
 | |
| 						swizzle.push_back(i);
 | |
| 						continue;
 | |
| 					}
 | |
| 					State bit;
 | |
| 					if (!always_0[i]) {
 | |
| 						log("%s.%s: removing const-1 lane %d\n", log_id(module->name), log_id(mem.memid), i);
 | |
| 						bit = State::S1;
 | |
| 					} else if (!always_1[i]) {
 | |
| 						log("%s.%s: removing const-0 lane %d\n", log_id(module->name), log_id(mem.memid), i);
 | |
| 						bit = State::S0;
 | |
| 					} else {
 | |
| 						log("%s.%s: removing const-x lane %d\n", log_id(module->name), log_id(mem.memid), i);
 | |
| 						bit = State::Sx;
 | |
| 					}
 | |
| 					// Reconnect read port data.
 | |
| 					for (auto &port: mem.rd_ports) {
 | |
| 						for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
 | |
| 							int bidx = sub * mem.width + i;
 | |
| 							if (!port.clk_enable) {
 | |
| 								module->connect(port.data[bidx], bit);
 | |
| 							} else {
 | |
| 								// The FF will most likely be redundant, but it's up to opt_dff to deal with this.
 | |
| 								FfData ff(module, &initvals, NEW_ID);
 | |
| 								ff.width = 1;
 | |
| 								ff.has_clk = true;
 | |
| 								ff.sig_clk = port.clk;
 | |
| 								ff.pol_clk = port.clk_polarity;
 | |
| 								if (port.en != State::S1) {
 | |
| 									ff.has_ce = true;
 | |
| 									ff.pol_ce = true;
 | |
| 									ff.sig_ce = port.en;
 | |
| 								}
 | |
| 								if (port.arst != State::S0) {
 | |
| 									ff.has_arst = true;
 | |
| 									ff.pol_arst = true;
 | |
| 									ff.sig_arst = port.arst;
 | |
| 									ff.val_arst = port.arst_value[bidx];
 | |
| 								}
 | |
| 								if (port.srst != State::S0) {
 | |
| 									ff.has_srst = true;
 | |
| 									ff.pol_srst = true;
 | |
| 									ff.sig_srst = port.srst;
 | |
| 									ff.val_srst = port.srst_value[bidx];
 | |
| 								}
 | |
| 								ff.sig_d = bit;
 | |
| 								ff.sig_q = port.data[bidx];
 | |
| 								ff.val_init = port.init_value[bidx];
 | |
| 								ff.emit();
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				if (GetSize(swizzle) == 0) {
 | |
| 					mem.remove();
 | |
| 					total_count++;
 | |
| 					continue;
 | |
| 				}
 | |
| 				if (GetSize(swizzle) != mem.width) {
 | |
| 					for (auto &port: mem.wr_ports) {
 | |
| 						SigSpec new_data;
 | |
| 						SigSpec new_en;
 | |
| 						for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
 | |
| 							for (auto i: swizzle) {
 | |
| 								new_data.append(port.data[sub * mem.width + i]);
 | |
| 								new_en.append(port.en[sub * mem.width + i]);
 | |
| 							}
 | |
| 						}
 | |
| 						port.data = new_data;
 | |
| 						port.en = new_en;
 | |
| 					}
 | |
| 					for (auto &port: mem.rd_ports) {
 | |
| 						SigSpec new_data;
 | |
| 						Const::Builder new_init_bits;
 | |
| 						Const::Builder new_arst_bits;
 | |
| 						Const::Builder new_srst_bits;
 | |
| 						for (int sub = 0; sub < (1 << port.wide_log2); sub++) {
 | |
| 							for (auto i: swizzle) {
 | |
| 								int bidx = sub * mem.width + i;
 | |
| 								new_data.append(port.data[bidx]);
 | |
| 								new_init_bits.push_back(port.init_value[bidx]);
 | |
| 								new_arst_bits.push_back(port.arst_value[bidx]);
 | |
| 								new_srst_bits.push_back(port.srst_value[bidx]);
 | |
| 							}
 | |
| 						}
 | |
| 						port.data = new_data;
 | |
| 						port.init_value = new_init_bits.build();
 | |
| 						port.arst_value = new_arst_bits.build();
 | |
| 						port.srst_value = new_srst_bits.build();
 | |
| 					}
 | |
| 					for (auto &init: mem.inits) {
 | |
| 						Const::Builder new_data_bits;
 | |
| 						for (int s = 0; s < GetSize(init.data); s += mem.width) {
 | |
| 							for (auto i: swizzle) {
 | |
| 								new_data_bits.push_back(init.data[s + i]);
 | |
| 							}
 | |
| 						}
 | |
| 						init.data = new_data_bits.build();
 | |
| 						Const::Builder new_en_bits;
 | |
| 						for (auto i: swizzle) {
 | |
| 							new_en_bits.push_back(init.en[i]);
 | |
| 						}
 | |
| 						init.en = new_en_bits.build();
 | |
| 					}
 | |
| 					mem.width = GetSize(swizzle);
 | |
| 					changed = true;
 | |
| 					total_count++;
 | |
| 				}
 | |
| 				if (changed) {
 | |
| 					mem.emit();
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (total_count)
 | |
| 			design->scratchpad_set_bool("opt.did_something", true);
 | |
| 		log("Performed a total of %d transformations.\n", total_count);
 | |
| 	}
 | |
| } OptMemPass;
 | |
| 
 | |
| PRIVATE_NAMESPACE_END
 |