mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 11:42:30 +00:00 
			
		
		
		
	check: Use cell edges data in detecting combinational loops
This commit is contained in:
		
							parent
							
								
									c5ae74af34
								
							
						
					
					
						commit
						fa74d0bd1a
					
				
					 1 changed files with 121 additions and 40 deletions
				
			
		|  | @ -19,6 +19,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "kernel/yosys.h" | #include "kernel/yosys.h" | ||||||
| #include "kernel/sigtools.h" | #include "kernel/sigtools.h" | ||||||
|  | #include "kernel/celledges.h" | ||||||
| #include "kernel/celltypes.h" | #include "kernel/celltypes.h" | ||||||
| #include "kernel/utils.h" | #include "kernel/utils.h" | ||||||
| 
 | 
 | ||||||
|  | @ -102,6 +103,7 @@ struct CheckPass : public Pass { | ||||||
| 
 | 
 | ||||||
| 			SigMap sigmap(module); | 			SigMap sigmap(module); | ||||||
| 			dict<SigBit, vector<string>> wire_drivers; | 			dict<SigBit, vector<string>> wire_drivers; | ||||||
|  | 			dict<SigBit, Cell *> driver_cells; | ||||||
| 			dict<SigBit, int> wire_drivers_count; | 			dict<SigBit, int> wire_drivers_count; | ||||||
| 			pool<SigBit> used_wires; | 			pool<SigBit> used_wires; | ||||||
| 			TopoSort<std::pair<RTLIL::IdString, int>> topo; | 			TopoSort<std::pair<RTLIL::IdString, int>> topo; | ||||||
|  | @ -149,6 +151,46 @@ struct CheckPass : public Pass { | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			struct CircuitEdgesDatabase : AbstractCellEdgesDatabase { | ||||||
|  | 				TopoSort<std::pair<RTLIL::IdString, int>> &topo; | ||||||
|  | 				SigMap sigmap; | ||||||
|  | 
 | ||||||
|  | 				CircuitEdgesDatabase(TopoSort<std::pair<RTLIL::IdString, int>> &topo, SigMap &sigmap) | ||||||
|  | 					: topo(topo), sigmap(sigmap) {} | ||||||
|  | 
 | ||||||
|  | 				void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, | ||||||
|  | 							  RTLIL::IdString to_port, int to_bit, int) override { | ||||||
|  | 					SigBit from = sigmap(cell->getPort(from_port))[from_bit]; | ||||||
|  | 					SigBit to = sigmap(cell->getPort(to_port))[to_bit]; | ||||||
|  | 
 | ||||||
|  | 					if (from.wire && to.wire) | ||||||
|  | 						topo.edge(std::make_pair(from.wire->name, from.offset), std::make_pair(to.wire->name, to.offset)); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				bool add_edges_from_cell(Cell *cell) { | ||||||
|  | 					if (AbstractCellEdgesDatabase::add_edges_from_cell(cell)) | ||||||
|  | 						return true; | ||||||
|  | 
 | ||||||
|  | 					// We don't have accurate cell edges, do the fallback of all input-output pairs
 | ||||||
|  | 					for (auto &conn : cell->connections()) { | ||||||
|  | 						if (cell->input(conn.first)) | ||||||
|  | 						for (auto bit : sigmap(conn.second)) | ||||||
|  | 						if (bit.wire) | ||||||
|  | 							topo.edge(std::make_pair(bit.wire->name, bit.offset), | ||||||
|  | 									  std::make_pair(cell->name, -1)); | ||||||
|  | 
 | ||||||
|  | 						if (cell->output(conn.first)) | ||||||
|  | 						for (auto bit : sigmap(conn.second)) | ||||||
|  | 						if (bit.wire) | ||||||
|  | 							topo.edge(std::make_pair(cell->name, -1), | ||||||
|  | 									  std::make_pair(bit.wire->name, bit.offset)); | ||||||
|  | 					} | ||||||
|  | 					return true; | ||||||
|  | 				} | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			CircuitEdgesDatabase edges_db(topo, sigmap); | ||||||
|  | 
 | ||||||
| 			for (auto cell : module->cells()) | 			for (auto cell : module->cells()) | ||||||
| 			{ | 			{ | ||||||
| 				if (mapped && cell->type.begins_with("$") && design->module(cell->type) == nullptr) { | 				if (mapped && cell->type.begins_with("$") && design->module(cell->type) == nullptr) { | ||||||
|  | @ -157,31 +199,29 @@ struct CheckPass : public Pass { | ||||||
| 					counter++; | 					counter++; | ||||||
| 				cell_allowed:; | 				cell_allowed:; | ||||||
| 				} | 				} | ||||||
| 				for (auto &conn : cell->connections()) { |  | ||||||
| 					SigSpec sig = sigmap(conn.second); |  | ||||||
| 					bool logic_cell = yosys_celltypes.cell_evaluable(cell->type); |  | ||||||
| 					if (cell->input(conn.first)) |  | ||||||
| 						for (auto bit : sig) |  | ||||||
| 							if (bit.wire) { |  | ||||||
| 								if (logic_cell && bit.wire) |  | ||||||
| 									topo.edge(std::make_pair(bit.wire->name, bit.offset), |  | ||||||
| 											std::make_pair(cell->name, -1)); |  | ||||||
| 								used_wires.insert(bit); |  | ||||||
| 							} |  | ||||||
| 					if (cell->output(conn.first)) |  | ||||||
| 						for (int i = 0; i < GetSize(sig); i++) { |  | ||||||
| 							if (logic_cell && sig[i].wire) |  | ||||||
| 								topo.edge(std::make_pair(cell->name, -1), |  | ||||||
| 										std::make_pair(sig[i].wire->name, sig[i].offset)); |  | ||||||
| 
 | 
 | ||||||
| 							if (sig[i].wire || !cell->input(conn.first)) | 				for (auto &conn : cell->connections()) { | ||||||
| 								wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)", | 					bool input = cell->input(conn.first); | ||||||
| 										log_id(conn.first), i, log_id(cell), log_id(cell->type))); | 					bool output = cell->output(conn.first); | ||||||
|  | 
 | ||||||
|  | 					SigSpec sig = sigmap(conn.second); | ||||||
|  | 					for (int i = 0; i < sig.size(); i++) { | ||||||
|  | 						SigBit bit = sig[i]; | ||||||
|  | 
 | ||||||
|  | 						if (input && bit.wire) | ||||||
|  | 							used_wires.insert(bit); | ||||||
|  | 						if (output && !input && bit.wire) | ||||||
|  | 							wire_drivers_count[bit]++; | ||||||
|  | 						if (output && (bit.wire || !input)) | ||||||
|  | 							wire_drivers[bit].push_back(stringf("port %s[%d] of cell %s (%s)", log_id(conn.first), i, | ||||||
|  | 																log_id(cell), log_id(cell->type))); | ||||||
|  | 						if (output) | ||||||
|  | 							driver_cells[bit] = cell; | ||||||
| 					} | 					} | ||||||
| 					if (!cell->input(conn.first) && cell->output(conn.first)) |  | ||||||
| 						for (auto bit : sig) |  | ||||||
| 							if (bit.wire) wire_drivers_count[bit]++; |  | ||||||
| 				} | 				} | ||||||
|  | 
 | ||||||
|  | 				if (yosys_celltypes.cell_evaluable(cell->type)) | ||||||
|  | 					edges_db.add_edges_from_cell(cell); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			pool<SigBit> init_bits; | 			pool<SigBit> init_bits; | ||||||
|  | @ -238,27 +278,68 @@ struct CheckPass : public Pass { | ||||||
| 			topo.sort(); | 			topo.sort(); | ||||||
| 			for (auto &loop : topo.loops) { | 			for (auto &loop : topo.loops) { | ||||||
| 				string message = stringf("found logic loop in module %s:\n", log_id(module)); | 				string message = stringf("found logic loop in module %s:\n", log_id(module)); | ||||||
| 				for (auto &pair : loop) { | 
 | ||||||
| 					RTLIL::AttrObject *obj; | 				// `loop` only contains wire bits, or an occassional special helper node for cells for
 | ||||||
| 					if (pair.second == -1) | 				// which we have done the edges fallback. The cell and its ports that led to an edge is
 | ||||||
| 						obj = module->cell(pair.first); | 				// an information we need to recover now. For that we need to have the previous wire bit
 | ||||||
| 					else | 				// of the loop at hand.
 | ||||||
| 						obj = module->wire(pair.first); | 				SigBit prev; | ||||||
| 					log_assert(obj); | 				for (auto it = loop.rbegin(); it != loop.rend(); it++) | ||||||
| 					std::string src; | 				if (it->second != -1) { // skip the fallback helper nodes
 | ||||||
| 					if (obj->has_attribute(ID::src)) { | 					prev = SigBit(module->wire(it->first), it->second); | ||||||
| 						std::string src_attr = obj->get_src_attribute(); | 					break; | ||||||
| 						src = stringf(" source: %s", src_attr.c_str()); |  | ||||||
| 				} | 				} | ||||||
| 					if (pair.second == -1) { | 				log_assert(prev != SigBit()); | ||||||
| 						Cell *cell = module->cell(pair.first); | 
 | ||||||
| 						log_assert(cell); | 				for (auto &pair : loop) { | ||||||
| 						message += stringf("    cell %s (%s)%s\n", log_id(cell), log_id(cell->type), src.c_str()); | 					if (pair.second == -1) | ||||||
| 					} else { | 						continue; // helper node for edges fallback, we can ignore it
 | ||||||
|  | 
 | ||||||
|  | 					struct MatchingEdgePrinter : AbstractCellEdgesDatabase { | ||||||
|  | 						std::string &message; | ||||||
|  | 						SigMap &sigmap; | ||||||
|  | 						SigBit from, to; | ||||||
|  | 						int nhits; | ||||||
|  | 						const int HITS_LIMIT = 3; | ||||||
|  | 
 | ||||||
|  | 						MatchingEdgePrinter(std::string &message, SigMap &sigmap, SigBit from, SigBit to) | ||||||
|  | 							: message(message), sigmap(sigmap), from(from), to(to), nhits(0) {} | ||||||
|  | 
 | ||||||
|  | 						void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, | ||||||
|  | 									  RTLIL::IdString to_port, int to_bit, int) override { | ||||||
|  | 							SigBit edge_from = sigmap(cell->getPort(from_port))[from_bit]; | ||||||
|  | 							SigBit edge_to = sigmap(cell->getPort(to_port))[to_bit]; | ||||||
|  | 
 | ||||||
|  | 							if (edge_from == from && edge_to == to && nhits++ < HITS_LIMIT) | ||||||
|  | 								message += stringf("        %s[%d] --> %s[%d]\n", log_id(from_port), from_bit, | ||||||
|  | 												   log_id(to_port), to_bit); | ||||||
|  | 							if (nhits == HITS_LIMIT) | ||||||
|  | 								message += "        ...\n"; | ||||||
|  | 						} | ||||||
|  | 					}; | ||||||
|  | 
 | ||||||
| 					Wire *wire = module->wire(pair.first); | 					Wire *wire = module->wire(pair.first); | ||||||
| 					log_assert(wire); | 					log_assert(wire); | ||||||
| 						message += stringf("    wire %s%s\n", log_signal(SigBit(wire, pair.second)), src.c_str()); | 					SigBit bit(module->wire(pair.first), pair.second); | ||||||
|  | 					log_assert(driver_cells.count(bit)); | ||||||
|  | 					Cell *driver = driver_cells.at(bit); | ||||||
|  | 
 | ||||||
|  | 					std::string driver_src; | ||||||
|  | 					if (driver->has_attribute(ID::src)) { | ||||||
|  | 						std::string src_attr = driver->get_src_attribute(); | ||||||
|  | 						driver_src = stringf(" source: %s", src_attr.c_str()); | ||||||
| 					} | 					} | ||||||
|  | 					message += stringf("    cell %s (%s)%s\n", log_id(driver), log_id(driver->type), driver_src.c_str()); | ||||||
|  | 					MatchingEdgePrinter printer(message, sigmap, prev, bit); | ||||||
|  | 					printer.add_edges_from_cell(driver); | ||||||
|  | 
 | ||||||
|  | 					std::string wire_src; | ||||||
|  | 					if (wire->has_attribute(ID::src)) { | ||||||
|  | 						std::string src_attr = wire->get_src_attribute(); | ||||||
|  | 						wire_src = stringf(" source: %s", src_attr.c_str()); | ||||||
|  | 					} | ||||||
|  | 					message += stringf("    wire %s%s\n", log_signal(SigBit(wire, pair.second)), wire_src.c_str()); | ||||||
|  | 					prev = bit; | ||||||
| 				} | 				} | ||||||
| 				log_warning("%s", message.c_str()); | 				log_warning("%s", message.c_str()); | ||||||
| 				counter++; | 				counter++; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue