mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	Merge pull request #8 from alaindargelas/activity_computation
This commit is contained in:
		
						commit
						24e8ce579b
					
				
					 3 changed files with 327 additions and 1 deletions
				
			
		
							
								
								
									
										1
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -750,6 +750,7 @@ OBJS += passes/cmds/splitfanout.o | |||
| OBJS += passes/cmds/splitnets.o | ||||
| OBJS += passes/cmds/tee.o | ||||
| OBJS += passes/sat/sim.o | ||||
| OBJS += passes/sat/activity.o | ||||
| 
 | ||||
| include $(YOSYS_SRC)/passes/hierarchy/Makefile.inc | ||||
| include $(YOSYS_SRC)/passes/memory/Makefile.inc | ||||
|  |  | |||
							
								
								
									
										155
									
								
								passes/cmds/activity.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								passes/cmds/activity.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,155 @@ | |||
| /*
 | ||||
|  *  yosys -- Yosys Open SYnthesis Suite | ||||
|  * | ||||
|  *  Copyright (C) 2012  Claire Xenia Wolf <claire@yosyshq.com> | ||||
|  *                2024  Alain Dargelas    <alain@silimate.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" | ||||
| 
 | ||||
| USING_YOSYS_NAMESPACE | ||||
| PRIVATE_NAMESPACE_BEGIN | ||||
| 
 | ||||
| struct ActivityProp { | ||||
| 	Module *module; | ||||
| 	SigMap sigmap; | ||||
| 
 | ||||
| 	// Split a string based on separator, returns a vector of tokens as reference argument
 | ||||
| 	// If skipEmpty is true, return "" for string "    ", when separator is " "
 | ||||
| 	void tokenize(std::string_view str, std::string_view separator, std::vector<std::string> &result, bool skipEmpty) | ||||
| 	{ | ||||
| 		std::string::size_type pos{0}; | ||||
| 		const auto sepSize = separator.size(); | ||||
| 		const auto stringSize = str.size(); | ||||
| 		std::string tmp; | ||||
| 		std::string::size_type n = str.find(separator, pos); | ||||
| 		while (n != std::string::npos) { | ||||
| 			tmp = str.substr(pos, n - pos); | ||||
| 			if (!(tmp.empty() && skipEmpty)) | ||||
| 				result.push_back(tmp); | ||||
| 			pos = n + sepSize; | ||||
| 			n = str.find(separator, pos); | ||||
| 		} | ||||
| 		if (pos < stringSize) { // put last part
 | ||||
| 			tmp = str.substr(pos, stringSize - pos); | ||||
| 			if (!(tmp.empty() && skipEmpty)) | ||||
| 				result.push_back(tmp); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Split a string based on separator, returns a vector of tokens
 | ||||
| 	// If skipEmpty is true, return "" for string "    ", when separator is " "
 | ||||
| 	std::vector<std::string> tokenize(std::string_view str, std::string_view separator, bool skipEmpty) | ||||
| 	{ | ||||
| 		std::vector<std::string> result; | ||||
| 		tokenize(str, separator, result, skipEmpty); | ||||
| 		return result; | ||||
| 	} | ||||
| 
 | ||||
| 	ActivityProp(Module *module) : module(module), sigmap(module) | ||||
| 	{ | ||||
| 		std::map<SigBit, std::string> ActivityMap; | ||||
| 		std::map<SigBit, std::string> DutyMap; | ||||
| 		// Build {signal bit - activity} map from the wire activities calculated in the sim pass
 | ||||
| 		for (Wire *wire : module->wires()) { | ||||
| 			SigSpec sig(sigmap(wire)); | ||||
| 			// Retrieve the activity/dutycycle attributes created in the sim pass, attached to wires, in the form:
 | ||||
| 			// $ACKT: 0.1 0.2 .... (Each bit in a bus has its own activity, index 0 of the bus is left most)
 | ||||
| 			std::string act = wire->get_string_attribute("$ACKT"); | ||||
| 			std::string duty = wire->get_string_attribute("$DUTY"); | ||||
| 			// Split the activity lists
 | ||||
| 			std::vector<std::string> activities = tokenize(act, " ", true); | ||||
| 			std::vector<std::string> duties = tokenize(duty, " ", true); | ||||
| 			// Assign them to each SigBit (1 signal bit)
 | ||||
| 			for (int i = 0; i < GetSize(sig); i++) { | ||||
| 				SigBit bit(sig[i]); | ||||
| 				ActivityMap.emplace(bit, activities[i]); | ||||
| 				DutyMap.emplace(bit, duties[i]); | ||||
| 			} | ||||
| 		} | ||||
| 		// Attach port activity to cell using sigmap
 | ||||
| 		for (auto cell : module->cells()) { | ||||
| 			std::string cell_ports_activity; | ||||
| 			std::string cell_ports_duty; | ||||
| 			for (auto conn : cell->connections()) { | ||||
| 				// Recombine individual bit activities for all cell ports into a list attached to the cell
 | ||||
| 				for (int i = 0; i < GetSize(conn.second); i++) { | ||||
| 					SigBit bit(sigmap(conn.second[i])); | ||||
| 					std::string port_name = std::string(conn.first.c_str()) + "[" + std::to_string(i) + "]"; | ||||
| 					{ | ||||
| 						std::map<SigBit, std::string>::iterator itr = ActivityMap.find(bit); | ||||
| 						if (itr != ActivityMap.end()) { | ||||
| 							cell_ports_activity += port_name + "=" + (*itr).second + " "; | ||||
| 						} else { | ||||
| 							RTLIL::SigSpec sigspec(bit); | ||||
| 							if (!sigspec.is_fully_const()) { | ||||
| 							  log_warning("No activity found for : %s/%s/%s", module->name.c_str(), cell->name.c_str(), port_name.c_str()); | ||||
| 							} | ||||
| 							// constants have no activity
 | ||||
| 							cell_ports_activity += port_name + "=" + "0.0 "; | ||||
| 						} | ||||
| 					} | ||||
| 					{ | ||||
| 						std::map<SigBit, std::string>::iterator itr = DutyMap.find(bit); | ||||
| 						if (itr != DutyMap.end()) { | ||||
| 							cell_ports_duty += port_name + "=" + (*itr).second + " "; | ||||
| 						} else { | ||||
| 							RTLIL::SigSpec sigspec(bit); | ||||
| 							if (!sigspec.is_fully_const()) { | ||||
| 							  log_warning("No dutycycle found for : %s/%s/%s", module->name.c_str(), cell->name.c_str(), port_name.c_str()); | ||||
| 							} | ||||
| 							// constant 1 has duty cycle 1, constant 0 has duty cycle 0
 | ||||
| 							cell_ports_duty += port_name + "=" + (sigspec.as_bool() ? "1.0" : "0.0") + " "; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			// Annotate on cells the complete list of ports activities and dutycycles in the form:
 | ||||
| 			// $ACKT: \P1=0.1 \P2=0.2 ....
 | ||||
| 			cell->set_string_attribute("$ACKT:", cell_ports_activity); | ||||
| 			cell->set_string_attribute("$DUTY:", cell_ports_duty); | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct ActivityPropPass : public Pass { | ||||
| 	ActivityPropPass() : Pass("activity_prop", "Attaches wire activity to cell ports") {} | ||||
| 	void help() override | ||||
| 	{ | ||||
| 		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
 | ||||
| 		log("\n"); | ||||
| 		log("    activity_prop\n"); | ||||
| 		log("\n"); | ||||
| 	} | ||||
| 	void execute(std::vector<std::string> args, RTLIL::Design *design) override | ||||
| 	{ | ||||
| 		log_header(design, "Executing Activity propagation pass\n"); | ||||
| 
 | ||||
| 		size_t argidx; | ||||
| 		for (argidx = 1; argidx < args.size(); argidx++) { | ||||
| 			// No options currently. When adding in the future make sure to update docstring with [options]
 | ||||
| 			break; | ||||
| 		} | ||||
| 		extra_args(args, argidx, design); | ||||
| 
 | ||||
| 		for (auto module : design->modules()) { | ||||
| 			ActivityProp worker(module); | ||||
| 		} | ||||
| 	} | ||||
| } ActivityPropPass; | ||||
| 
 | ||||
| PRIVATE_NAMESPACE_END | ||||
|  | @ -1460,7 +1460,7 @@ struct SimWorker : SimShared | |||
| 	{ | ||||
| 		log_assert(top == nullptr); | ||||
| 		fst = new FstData(sim_filename); | ||||
| 
 | ||||
| 		timescale = fst->getTimescaleString(); | ||||
| 		if (scope.empty()) | ||||
| 			log_error("Scope must be defined for co-simulation.\n"); | ||||
| 
 | ||||
|  | @ -2397,6 +2397,172 @@ struct VCDWriter : public OutputWriter | |||
| 	std::ofstream vcdfile; | ||||
| }; | ||||
| 
 | ||||
| struct AnnotateActivity : public OutputWriter { | ||||
| 	AnnotateActivity(SimWorker *worker) : OutputWriter(worker) {} | ||||
| 
 | ||||
| 	struct SignalActivityData { | ||||
| 		std::vector<uint32_t> lastValues; | ||||
| 		std::vector<uint32_t> toggleCounts; | ||||
| 		std::vector<uint32_t> highTimes; | ||||
| 	}; | ||||
| 
 | ||||
| 	typedef std::map<int, SignalActivityData> SignalActivityDataMap; | ||||
| 
 | ||||
| 	void write(std::map<int, bool> &use_signal) override | ||||
| 	{ | ||||
| 		// Init map
 | ||||
| 		SignalActivityDataMap dataMap; | ||||
| 		// For each event (new time when a value changed)
 | ||||
| 		for (auto &d : worker->output_data) { | ||||
| 			// For each signal/values in that time slice
 | ||||
| 			for (auto &data : d.second) { | ||||
| 				int sig = data.first; | ||||
| 				if (!use_signal.at(sig)) | ||||
| 					continue; | ||||
| 				// Create an entry in the map with all zeros for all bits of the signal
 | ||||
| 				SignalActivityDataMap::iterator itr = dataMap.find(sig); | ||||
| 				if (itr == dataMap.end()) { | ||||
| 					Const value = data.second; | ||||
| 					std::vector<uint32_t> vals(GetSize(value), 0); | ||||
| 					SignalActivityData data; | ||||
| 					data.highTimes = vals; | ||||
| 					data.lastValues = vals; | ||||
| 					data.toggleCounts = vals; | ||||
| 					dataMap.emplace(sig, data); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		// Max simulation time
 | ||||
| 		int max_time = 0; | ||||
| 		// clock pin id (highest toggling signal)
 | ||||
| 		int clk = 0; | ||||
| 		int highest_toggle = 0; | ||||
| 		// Used to compute time intervals
 | ||||
| 		int prev_time = 0; | ||||
| 		// For each event (new time when a value changed)
 | ||||
| 		for (auto &d : worker->output_data) { | ||||
| 			int time = d.first; | ||||
| 			// For each signal/values in that time slice
 | ||||
| 			for (auto &data : d.second) { | ||||
| 				int sig = data.first; | ||||
| 				if (!use_signal.at(sig)) | ||||
| 					continue; | ||||
| 				Const value = data.second; | ||||
| 				SignalActivityDataMap::iterator itr = dataMap.find(sig); | ||||
| 				std::vector<uint32_t> &lastVals = itr->second.lastValues; | ||||
| 				std::vector<uint32_t> &toggleCounts = itr->second.toggleCounts; | ||||
| 				std::vector<uint32_t> &highTimes = itr->second.highTimes; | ||||
| 				for (int i = GetSize(value) - 1; i >= 0; i--) { | ||||
| 					int val = '-'; | ||||
| 					switch (value[i]) { | ||||
| 					case State::S0: | ||||
| 						val = '0'; | ||||
| 						break; | ||||
| 					case State::S1: | ||||
| 						val = '1'; | ||||
| 						break; | ||||
| 					case State::Sx: | ||||
| 						val = 'x'; | ||||
| 						break; | ||||
| 					default: | ||||
| 						val = 'z'; | ||||
| 					} | ||||
| 					// If signal toggled
 | ||||
| 					if (val != lastVals[i]) { | ||||
| 						toggleCounts[i]++; | ||||
| 						if (toggleCounts[i] > highest_toggle) { | ||||
| 							highest_toggle = toggleCounts[i]; | ||||
| 							clk = sig; | ||||
| 						} | ||||
| 						lastVals[i] = val; | ||||
| 					} | ||||
| 					if (lastVals[i] == '1') { | ||||
| 						highTimes[i] += time - prev_time; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			prev_time = time; | ||||
| 			max_time = time; | ||||
| 		} | ||||
| 
 | ||||
| 		// Retrieve VCD timescale
 | ||||
| 		std::string timescale = worker->timescale; | ||||
| 		double real_timescale = 1e-12; // ps
 | ||||
| 		if (timescale == "ns") | ||||
| 			real_timescale = 1e-9; | ||||
| 		if (timescale == "fs") | ||||
| 			real_timescale = 1e-15; | ||||
| 
 | ||||
| 		// TODO: remove all debug sections when dev is completed
 | ||||
| 		bool debug = false; | ||||
| 
 | ||||
| 		// Compute clock period, find the highest toggling signal and compute its average period
 | ||||
| 		SignalActivityDataMap::iterator itr = dataMap.find(clk); | ||||
| 		std::vector<uint32_t> &clktoggleCounts = itr->second.toggleCounts; | ||||
| 		double clk_period = real_timescale * (double)max_time / (clktoggleCounts[0] / 2); | ||||
| 		if (debug) { | ||||
| 			std::cout << "Clock toggle count: " << clktoggleCounts[0] << "\n"; | ||||
| 			std::cout << "Max time: " << max_time << "\n"; | ||||
| 			std::cout << "Clock period: " << clk_period << "\n"; | ||||
| 		} | ||||
| 		worker->top->write_output_header( | ||||
| 		  [this, debug](IdString name) { | ||||
| 			  if (debug) | ||||
| 				  std::cout << stringf("module %s\n", log_id(name)); | ||||
| 		  }, | ||||
| 			[this, debug]() { | ||||
| 				if (debug) | ||||
| 					std::cout << "endmodule\n"; | ||||
| 			}, | ||||
| 			[this, use_signal, dataMap, max_time, real_timescale, clk_period, debug] | ||||
| 			  (const char *name, int size, Wire *w, int id, bool is_reg) { | ||||
| 				if (!use_signal.at(id)) | ||||
| 					return; | ||||
| 				std::string full_name = form_vcd_name(name, size, w); | ||||
| 				SignalActivityDataMap::const_iterator itr = dataMap.find(id); | ||||
| 				const std::vector<uint32_t> &toggleCounts = itr->second.toggleCounts; | ||||
| 				const std::vector<uint32_t> &highTimes = itr->second.highTimes; | ||||
| 				if (debug) { | ||||
| 					std::cout << full_name << ":\n"; | ||||
| 					std::cout << "     TC: "; | ||||
| 					for (uint32_t i = 0; i < (uint32_t)size; i++) { | ||||
| 						std::cout << toggleCounts[i] << " "; | ||||
| 					} | ||||
| 					std::cout << "\n"; | ||||
| 					std::cout << "     HT: "; | ||||
| 					for (uint32_t i = 0; i < (uint32_t)size; i++) { | ||||
| 						std::cout << highTimes[i] << " "; | ||||
| 					} | ||||
| 					std::cout << "\n"; | ||||
| 					std::cout << "     ACK: "; | ||||
| 				} | ||||
| 				std::string activity_str; | ||||
| 				for (uint32_t i = 0; i < (uint32_t)size; i++) { | ||||
| 					// Compute Activity
 | ||||
| 					double activity = toggleCounts[i] / ((double)max_time * real_timescale / clk_period); | ||||
| 					activity_str += std::to_string(activity) + " "; | ||||
| 				} | ||||
| 				if (debug) { | ||||
| 					std::cout << activity_str; | ||||
| 					std::cout << "\n"; | ||||
| 					std::cout << "     DUTY: "; | ||||
| 				} | ||||
| 				std::string duty_str; | ||||
| 				for (uint32_t i = 0; i < (uint32_t)size; i++) { | ||||
| 					// Compute Duty cycle
 | ||||
| 					double duty = (double)highTimes[i] / (double)max_time; | ||||
| 					duty_str += std::to_string(duty) + " "; | ||||
| 				} | ||||
| 				if (debug) { | ||||
| 					std::cout << duty_str; | ||||
| 					std::cout << "\n"; | ||||
| 				} | ||||
| 				w->set_string_attribute("$ACKT", activity_str); | ||||
| 				w->set_string_attribute("$DUTY", duty_str); | ||||
| 			}); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct FSTWriter : public OutputWriter | ||||
| { | ||||
| 	FSTWriter(SimWorker *worker, std::string filename) : OutputWriter(worker) { | ||||
|  | @ -2871,6 +3037,10 @@ struct SimPass : public Pass { | |||
| 				worker.multiclock = true; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (args[argidx] == "-activity") { | ||||
| 				worker.outputfiles.emplace_back(std::unique_ptr<AnnotateActivity>(new AnnotateActivity(&worker))); | ||||
| 				continue; | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 		extra_args(args, argidx, design); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue