mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	Merge pull request #3624 from jix/sim_yw
Changes to support SBY trace generation with the sim command
This commit is contained in:
		
						commit
						8180cc4325
					
				
					 15 changed files with 1722 additions and 113 deletions
				
			
		|  | @ -259,5 +259,6 @@ X(WR_PORTS) | |||
| X(WR_PRIORITY_MASK) | ||||
| X(WR_WIDE_CONTINUATION) | ||||
| X(X) | ||||
| X(xprop_decoder) | ||||
| X(Y) | ||||
| X(Y_WIDTH) | ||||
|  |  | |||
							
								
								
									
										172
									
								
								kernel/json.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								kernel/json.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,172 @@ | |||
| /*
 | ||||
|  *  yosys -- Yosys Open SYnthesis Suite | ||||
|  * | ||||
|  *  Copyright (C) 2022  Jannis Harder <jix@yosyshq.com> <me@jix.one> | ||||
|  * | ||||
|  *  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/json.h" | ||||
| 
 | ||||
| USING_YOSYS_NAMESPACE | ||||
| 
 | ||||
| void PrettyJson::emit_to_log() | ||||
| { | ||||
|     struct LogTarget : public Target { | ||||
|         void emit(const char *data) override { log("%s", data); } | ||||
|     }; | ||||
| 
 | ||||
|     targets.push_back(std::unique_ptr<Target>(new LogTarget)); | ||||
| } | ||||
| 
 | ||||
| void PrettyJson::append_to_string(std::string &target) | ||||
| { | ||||
|     struct AppendStringTarget : public Target { | ||||
|         std::string ⌖ | ||||
|         AppendStringTarget(std::string &target) : target(target) {} | ||||
|         void emit(const char *data) override { target += data; } | ||||
|     }; | ||||
| 
 | ||||
|     targets.push_back(std::unique_ptr<Target>(new AppendStringTarget(target))); | ||||
| } | ||||
| 
 | ||||
| bool PrettyJson::write_to_file(const std::string &path) | ||||
| { | ||||
|     struct WriteFileTarget : public Target { | ||||
|         std::ofstream target; | ||||
|         void emit(const char *data) override { target << data; } | ||||
|         void flush() override { target.flush(); } | ||||
|     }; | ||||
| 
 | ||||
|     auto target = std::unique_ptr<WriteFileTarget>(new WriteFileTarget); | ||||
|     target->target.open(path); | ||||
|     if (target->target.fail()) | ||||
|         return false; | ||||
|     targets.push_back(std::unique_ptr<Target>(target.release())); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void PrettyJson::line(bool space_if_inline) | ||||
| { | ||||
|     if (compact_depth != INT_MAX) { | ||||
|         if (space_if_inline) | ||||
|             raw(" "); | ||||
|         return; | ||||
|     } | ||||
|     int indent = state.size() - (state.empty() ? 0 : state.back() == VALUE); | ||||
|     newline_indent.resize(1 + 2 * indent, ' '); | ||||
|     raw(newline_indent.c_str()); | ||||
| } | ||||
| 
 | ||||
| void PrettyJson::raw(const char *raw_json) | ||||
| { | ||||
|     for (auto &target : targets) | ||||
|         target->emit(raw_json); | ||||
| } | ||||
| 
 | ||||
| void PrettyJson::flush() | ||||
| { | ||||
|     for (auto &target : targets) | ||||
|         target->flush(); | ||||
| } | ||||
| 
 | ||||
| void PrettyJson::begin_object() | ||||
| { | ||||
|     begin_value(); | ||||
|     raw("{"); | ||||
|     state.push_back(OBJECT_FIRST); | ||||
| } | ||||
| 
 | ||||
| void PrettyJson::begin_array() | ||||
| { | ||||
|     begin_value(); | ||||
|     raw("["); | ||||
|     state.push_back(ARRAY_FIRST); | ||||
| } | ||||
| 
 | ||||
| void PrettyJson::end_object() | ||||
| { | ||||
|     Scope top_scope = state.back(); | ||||
|     state.pop_back(); | ||||
|     if (top_scope == OBJECT) | ||||
|         line(false); | ||||
|     else | ||||
|         log_assert(top_scope == OBJECT_FIRST); | ||||
|     raw("}"); | ||||
|     end_value(); | ||||
| } | ||||
| 
 | ||||
| void PrettyJson::end_array() | ||||
| { | ||||
|     Scope top_scope = state.back(); | ||||
|     state.pop_back(); | ||||
|     if (top_scope == ARRAY) | ||||
|         line(false); | ||||
|     else | ||||
|         log_assert(top_scope == ARRAY_FIRST); | ||||
|     raw("]"); | ||||
|     end_value(); | ||||
| } | ||||
| 
 | ||||
| void PrettyJson::name(const char *name) | ||||
| { | ||||
|     if (state.back() == OBJECT_FIRST) { | ||||
|         state.back() = OBJECT; | ||||
|         line(false); | ||||
|     } else { | ||||
|         raw(","); | ||||
|         line(); | ||||
|     } | ||||
|     raw(Json(name).dump().c_str()); | ||||
|     raw(": "); | ||||
|     state.push_back(VALUE); | ||||
| } | ||||
| 
 | ||||
| void PrettyJson::begin_value() | ||||
| { | ||||
|     if (state.back() == ARRAY_FIRST) { | ||||
|         line(false); | ||||
|         state.back() = ARRAY; | ||||
|     } else if (state.back() == ARRAY) { | ||||
|         raw(","); | ||||
|         line(); | ||||
|     } else { | ||||
|         log_assert(state.back() == VALUE); | ||||
|         state.pop_back(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PrettyJson::end_value() | ||||
| { | ||||
|     if (state.empty()) { | ||||
|         raw("\n"); | ||||
|         flush(); | ||||
|     } | ||||
|     if (GetSize(state) < compact_depth) | ||||
|         compact_depth = INT_MAX; | ||||
| } | ||||
| 
 | ||||
| void PrettyJson::value_json(const Json &value) | ||||
| { | ||||
|     begin_value(); | ||||
|     raw(value.dump().c_str()); | ||||
|     end_value(); | ||||
| } | ||||
| 
 | ||||
| void PrettyJson::entry_json(const char *name, const Json &value) | ||||
| { | ||||
|     this->name(name); | ||||
|     this->value(value); | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										104
									
								
								kernel/json.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								kernel/json.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,104 @@ | |||
| /*
 | ||||
|  *  yosys -- Yosys Open SYnthesis Suite | ||||
|  * | ||||
|  *  Copyright (C) 2022  Jannis Harder <jix@yosyshq.com> <me@jix.one> | ||||
|  * | ||||
|  *  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. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef JSON_H | ||||
| #define JSON_H | ||||
| 
 | ||||
| #include "kernel/yosys.h" | ||||
| #include "libs/json11/json11.hpp" | ||||
| #include <functional> | ||||
| 
 | ||||
| YOSYS_NAMESPACE_BEGIN | ||||
| 
 | ||||
| using json11::Json; | ||||
| 
 | ||||
| class PrettyJson | ||||
| { | ||||
|     enum Scope { | ||||
|         VALUE, | ||||
|         OBJECT_FIRST, | ||||
|         OBJECT, | ||||
|         ARRAY_FIRST, | ||||
|         ARRAY, | ||||
|     }; | ||||
| 
 | ||||
|     struct Target { | ||||
|         virtual void emit(const char *data) = 0; | ||||
|         virtual void flush() {}; | ||||
|         virtual ~Target() {}; | ||||
|     }; | ||||
| 
 | ||||
|     std::string newline_indent = "\n"; | ||||
|     std::vector<std::unique_ptr<Target>> targets; | ||||
|     std::vector<Scope> state = {VALUE}; | ||||
|     int compact_depth = INT_MAX; | ||||
| public: | ||||
| 
 | ||||
|     void emit_to_log(); | ||||
|     void append_to_string(std::string &target); | ||||
|     bool write_to_file(const std::string &path); | ||||
| 
 | ||||
|     bool active() { return !targets.empty(); } | ||||
| 
 | ||||
|     void compact() { compact_depth = GetSize(state); } | ||||
| 
 | ||||
|     void line(bool space_if_inline = true); | ||||
|     void raw(const char *raw_json); | ||||
|     void flush(); | ||||
|     void begin_object(); | ||||
|     void begin_array(); | ||||
|     void end_object(); | ||||
|     void end_array(); | ||||
|     void name(const char *name); | ||||
|     void begin_value(); | ||||
|     void end_value(); | ||||
|     void value_json(const Json &value); | ||||
|     void value(unsigned int value) { value_json(Json((int)value)); } | ||||
|     template<typename T> | ||||
|     void value(T &&value) { value_json(Json(std::forward<T>(value))); }; | ||||
| 
 | ||||
|     void entry_json(const char *name, const Json &value); | ||||
|     void entry(const char *name, unsigned int value) { entry_json(name, Json((int)value)); } | ||||
|     template<typename T> | ||||
|     void entry(const char *name, T &&value) { entry_json(name, Json(std::forward<T>(value))); }; | ||||
| 
 | ||||
|     template<typename T> | ||||
|     void object(const T &&values) | ||||
|     { | ||||
|         begin_object(); | ||||
|         for (auto &item : values) | ||||
|             entry(item.first, item.second); | ||||
|         end_object(); | ||||
|     } | ||||
| 
 | ||||
|     template<typename T> | ||||
|     void array(const T &&values) | ||||
|     { | ||||
|         begin_object(); | ||||
|         for (auto &item : values) | ||||
|             value(item); | ||||
|         end_object(); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| YOSYS_NAMESPACE_END | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										209
									
								
								kernel/yw.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								kernel/yw.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,209 @@ | |||
| /*
 | ||||
|  *  yosys -- Yosys Open SYnthesis Suite | ||||
|  * | ||||
|  *  Copyright (C) 2022  Jannis Harder <jix@yosyshq.com> <me@jix.one> | ||||
|  * | ||||
|  *  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/yw.h" | ||||
| #include "libs/json11/json11.hpp" | ||||
| 
 | ||||
| USING_YOSYS_NAMESPACE | ||||
| 
 | ||||
| // Use the same formatting as witness.py uses
 | ||||
| static const char *pretty_name(IdString id) | ||||
| { | ||||
| 	const char *c_str = id.c_str(); | ||||
| 	const char *p = c_str; | ||||
| 
 | ||||
| 	if (*p != '\\') | ||||
| 		return c_str; | ||||
| 	p++; | ||||
| 
 | ||||
| 	if (*p == '[') { | ||||
| 		p++; | ||||
| 		while (*p >= '0' && *p <= '9') | ||||
| 			p++; | ||||
| 		if (p[0] != ']' || p[1] != 0) | ||||
| 			return c_str; | ||||
| 		return c_str + 1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!(*p >= 'a' && *p <= 'z') && !(*p >= 'A' && *p <= 'Z') && *p != '_') | ||||
| 		return c_str; | ||||
| 	p++; | ||||
| 	while ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_') | ||||
| 		p++; | ||||
| 
 | ||||
| 	if (*p != 0) | ||||
| 		return c_str; | ||||
| 	return c_str + 1; | ||||
| } | ||||
| 
 | ||||
| std::string IdPath::str() const | ||||
| { | ||||
| 	std::string result; | ||||
| 
 | ||||
| 	for (auto &item : *this) { | ||||
| 		const char *pretty = pretty_name(item); | ||||
| 		if (pretty[0] == '[') { | ||||
| 			result += pretty; | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (!result.empty()) | ||||
| 			result += '.'; | ||||
| 		result += pretty; | ||||
| 		if (pretty[0] == '\\' || pretty[0] == '$') | ||||
| 			result += ' '; | ||||
| 	} | ||||
| 
 | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| bool IdPath::get_address(int &addr) const | ||||
| { | ||||
| 	if (empty()) | ||||
| 		return false; | ||||
| 	auto &last = back(); | ||||
| 	if (!last.begins_with("\\[")) | ||||
| 		return false; | ||||
| 	if (last == "\\[0]") { | ||||
| 		addr = 0; | ||||
| 		return true; | ||||
| 	} | ||||
| 	char first = last.c_str()[2]; | ||||
| 	if (first < '1' || first > '9') | ||||
| 		return false; | ||||
| 	char *endptr; | ||||
| 	addr = std::strtol(last.c_str() + 2, &endptr, 10); | ||||
| 	return endptr[0] == ']' && endptr[1] == 0; | ||||
| } | ||||
| 
 | ||||
| static std::vector<IdString> get_path(const json11::Json &json) | ||||
| { | ||||
| 	std::vector<IdString> result; | ||||
| 	for (auto &path_item : json.array_items()) { | ||||
| 		auto const &path_item_str = path_item.string_value(); | ||||
| 		if (path_item_str.empty()) | ||||
| 			return {};; | ||||
| 		result.push_back(path_item_str); | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| ReadWitness::ReadWitness(const std::string &filename) : | ||||
| 	filename(filename) | ||||
| { | ||||
| 	std::ifstream f(filename.c_str()); | ||||
| 	if (f.fail() || GetSize(filename) == 0) | ||||
| 		log_error("Cannot open file `%s`\n", filename.c_str()); | ||||
| 	std::stringstream buf; | ||||
| 	buf << f.rdbuf(); | ||||
| 	std::string err; | ||||
| 	json11::Json json = json11::Json::parse(buf.str(), err); | ||||
| 	if (!err.empty()) | ||||
| 		log_error("Failed to parse `%s`: %s\n", filename.c_str(), err.c_str()); | ||||
| 
 | ||||
| 	std::string format = json["format"].string_value(); | ||||
| 
 | ||||
| 	if (format.empty()) | ||||
| 		log_error("Failed to parse `%s`: Unknown format\n", filename.c_str()); | ||||
| 	if (format != "Yosys Witness Trace") | ||||
| 		log_error("Failed to parse `%s`: Unsupported format `%s`\n", filename.c_str(), format.c_str()); | ||||
| 
 | ||||
| 	for (auto &clock_json : json["clocks"].array_items()) { | ||||
| 		Clock clock; | ||||
| 		clock.path = get_path(clock_json["path"]); | ||||
| 		if (clock.path.empty()) | ||||
| 			log_error("Failed to parse `%s`: Missing path for clock `%s`\n", filename.c_str(), clock_json.dump().c_str()); | ||||
| 		auto edge_str = clock_json["edge"]; | ||||
| 		if (edge_str.string_value() == "posedge") | ||||
| 			clock.is_posedge = true; | ||||
| 		else if (edge_str.string_value() == "negedge") | ||||
| 			clock.is_negedge = true; | ||||
| 		else | ||||
| 			log_error("Failed to parse `%s`: Unknown edge type for clock `%s`\n", filename.c_str(), clock_json.dump().c_str()); | ||||
| 		if (!clock_json["offset"].is_number()) | ||||
| 			log_error("Failed to parse `%s`: Unknown offset for clock `%s`\n", filename.c_str(), clock_json.dump().c_str()); | ||||
| 		clock.offset = clock_json["offset"].int_value(); | ||||
| 		if (clock.offset < 0) | ||||
| 			log_error("Failed to parse `%s`: Invalid offset for clock `%s`\n", filename.c_str(), clock_json.dump().c_str()); | ||||
| 		clocks.push_back(clock); | ||||
| 	} | ||||
| 
 | ||||
| 	int bits_offset = 0; | ||||
| 	for (auto &signal_json : json["signals"].array_items()) { | ||||
| 		Signal signal; | ||||
| 		signal.bits_offset = bits_offset; | ||||
| 		signal.path = get_path(signal_json["path"]); | ||||
| 		if (signal.path.empty()) | ||||
| 			log_error("Failed to parse `%s`: Missing path for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); | ||||
| 		if (!signal_json["width"].is_number()) | ||||
| 			log_error("Failed to parse `%s`: Unknown width for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); | ||||
| 		signal.width = signal_json["width"].int_value(); | ||||
| 		if (signal.width < 0) | ||||
| 			log_error("Failed to parse `%s`: Invalid width for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); | ||||
| 		bits_offset += signal.width; | ||||
| 		if (!signal_json["offset"].is_number()) | ||||
| 			log_error("Failed to parse `%s`: Unknown offset for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); | ||||
| 		signal.offset = signal_json["offset"].int_value(); | ||||
| 		if (signal.offset < 0) | ||||
| 			log_error("Failed to parse `%s`: Invalid offset for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); | ||||
| 		signal.init_only = signal_json["init_only"].bool_value(); | ||||
| 		signals.push_back(signal); | ||||
| 	} | ||||
| 
 | ||||
| 	for (auto &step_json : json["steps"].array_items()) { | ||||
| 		Step step; | ||||
| 		if (!step_json["bits"].is_string()) | ||||
| 			log_error("Failed to parse `%s`: Expected string as bits value for step %d\n", filename.c_str(), GetSize(steps)); | ||||
| 		step.bits = step_json["bits"].string_value(); | ||||
| 		for (char c : step.bits) { | ||||
| 			if (c != '0' && c != '1' && c != 'x' && c != '?') | ||||
| 				log_error("Failed to parse `%s`: Invalid bit '%c' value for step %d\n", filename.c_str(), c, GetSize(steps)); | ||||
| 		} | ||||
| 		steps.push_back(step); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| RTLIL::Const ReadWitness::get_bits(int t, int bits_offset, int width) const | ||||
| { | ||||
| 	log_assert(t >= 0 && t < GetSize(steps)); | ||||
| 
 | ||||
| 	const std::string &bits = steps[t].bits; | ||||
| 
 | ||||
| 	RTLIL::Const result(State::Sa, width); | ||||
| 	result.bits.reserve(width); | ||||
| 
 | ||||
| 	int read_begin = GetSize(bits) - 1 - bits_offset; | ||||
| 	int read_end = max(-1, read_begin - width); | ||||
| 
 | ||||
| 	min(width, GetSize(bits) - bits_offset); | ||||
| 
 | ||||
| 	for (int i = read_begin, j = 0; i > read_end; i--, j++) { | ||||
| 		RTLIL::State bit = State::Sa; | ||||
| 		switch (bits[i]) { | ||||
| 			case '0': bit = State::S0; break; | ||||
| 			case '1': bit = State::S1; break; | ||||
| 			case 'x': bit = State::Sx; break; | ||||
| 			case '?': bit = State::Sa; break; | ||||
| 			default: | ||||
| 				log_abort(); | ||||
| 		} | ||||
| 		result.bits[j] = bit; | ||||
| 	} | ||||
| 
 | ||||
| 	return result; | ||||
| } | ||||
							
								
								
									
										182
									
								
								kernel/yw.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								kernel/yw.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,182 @@ | |||
| /*
 | ||||
|  *  yosys -- Yosys Open SYnthesis Suite | ||||
|  * | ||||
|  *  Copyright (C) 2022  Jannis Harder <jix@yosyshq.com> <me@jix.one> | ||||
|  * | ||||
|  *  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. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef YW_H | ||||
| #define YW_H | ||||
| 
 | ||||
| #include "kernel/yosys.h" | ||||
| #include "kernel/mem.h" | ||||
| 
 | ||||
| YOSYS_NAMESPACE_BEGIN | ||||
| 
 | ||||
| struct IdPath : public std::vector<RTLIL::IdString> | ||||
| { | ||||
| 	template<typename... T> | ||||
| 	IdPath(T&&... args) : std::vector<RTLIL::IdString>(std::forward<T>(args)...) { } | ||||
| 	IdPath prefix() const { return {begin(), end() - !empty()}; } | ||||
| 	std::string str() const; | ||||
| 
 | ||||
| 	bool has_address() const { int tmp; return get_address(tmp); }; | ||||
| 	bool get_address(int &addr) const; | ||||
| 
 | ||||
| 	int hash() const { return hashlib::hash_ops<std::vector<RTLIL::IdString>>::hash(*this); } | ||||
| }; | ||||
| 
 | ||||
| struct WitnessHierarchyItem { | ||||
| 	RTLIL::Module *module; | ||||
| 	RTLIL::Wire *wire = nullptr; | ||||
| 	RTLIL::Cell *cell = nullptr; | ||||
| 	Mem *mem = nullptr; | ||||
| 
 | ||||
| 	WitnessHierarchyItem(RTLIL::Module *module, RTLIL::Wire *wire) : module(module), wire(wire) {} | ||||
| 	WitnessHierarchyItem(RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) {} | ||||
| 	WitnessHierarchyItem(RTLIL::Module *module, Mem *mem) : module(module), mem(mem) {} | ||||
| }; | ||||
| 
 | ||||
| template<typename D, typename T> | ||||
| void witness_hierarchy(RTLIL::Module *module, D data, T callback); | ||||
| 
 | ||||
| template<class T> static std::vector<std::string> witness_path(T *obj) { | ||||
| 	std::vector<std::string> path; | ||||
| 	if (obj->name.isPublic()) { | ||||
| 		auto hdlname = obj->get_string_attribute(ID::hdlname); | ||||
| 		for (auto token : split_tokens(hdlname)) | ||||
| 			path.push_back("\\" + token); | ||||
| 	} | ||||
| 	if (path.empty()) | ||||
| 		path.push_back(obj->name.str()); | ||||
| 	return path; | ||||
| } | ||||
| 
 | ||||
| struct ReadWitness | ||||
| { | ||||
| 	struct Clock { | ||||
| 		IdPath path; | ||||
| 		int offset; | ||||
| 		bool is_posedge = false; | ||||
| 		bool is_negedge = false; | ||||
| 	}; | ||||
| 
 | ||||
| 	struct Signal { | ||||
| 		IdPath path; | ||||
| 		int offset; | ||||
| 		int width; | ||||
| 		bool init_only; | ||||
| 
 | ||||
| 		int bits_offset; | ||||
| 	}; | ||||
| 
 | ||||
| 	struct Step { | ||||
| 		std::string bits; | ||||
| 	}; | ||||
| 
 | ||||
| 	std::string filename; | ||||
| 	std::vector<Clock> clocks; | ||||
| 	std::vector<Signal> signals; | ||||
| 	std::vector<Step> steps; | ||||
| 
 | ||||
| 	ReadWitness(const std::string &filename); | ||||
| 
 | ||||
| 	RTLIL::Const get_bits(int t, int bits_offset, int width) const; | ||||
| }; | ||||
| 
 | ||||
| template<typename D, typename T> | ||||
| void witness_hierarchy_recursion(IdPath &path, int hdlname_mode, RTLIL::Module *module, D data, T &callback) | ||||
| { | ||||
| 	auto const &const_path = path; | ||||
| 	size_t path_size = path.size(); | ||||
| 	for (auto wire : module->wires()) | ||||
| 	{ | ||||
| 		auto hdlname = hdlname_mode < 0 ? std::vector<std::string>() : wire->get_hdlname_attribute(); | ||||
| 		for (auto item : hdlname) | ||||
| 			path.push_back("\\" + item); | ||||
| 		if (hdlname.size() == 1 && path.back() == wire->name) | ||||
| 			hdlname.clear(); | ||||
| 		if (!hdlname.empty()) | ||||
| 			callback(const_path, WitnessHierarchyItem(module, wire), data); | ||||
| 		path.resize(path_size); | ||||
| 		if (hdlname.empty() || hdlname_mode <= 0) { | ||||
| 			path.push_back(wire->name); | ||||
| 			callback(const_path, WitnessHierarchyItem(module, wire), data); | ||||
| 			path.pop_back(); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (auto cell : module->cells()) | ||||
| 	{ | ||||
| 		Module *child = module->design->module(cell->type); | ||||
| 		if (child == nullptr) | ||||
| 			continue; | ||||
| 
 | ||||
| 		auto hdlname = hdlname_mode < 0 ? std::vector<std::string>() : cell->get_hdlname_attribute(); | ||||
| 		for (auto item : hdlname) | ||||
| 			path.push_back("\\" + item); | ||||
| 		if (hdlname.size() == 1 && path.back() == cell->name) | ||||
| 			hdlname.clear(); | ||||
| 		if (!hdlname.empty()) { | ||||
| 			D child_data = callback(const_path, WitnessHierarchyItem(module, cell), data); | ||||
| 			witness_hierarchy_recursion<D, T>(path, 1, child, child_data, callback); | ||||
| 		} | ||||
| 		path.resize(path_size); | ||||
| 		if (hdlname.empty() || hdlname_mode <= 0) { | ||||
| 			path.push_back(cell->name); | ||||
| 			D child_data = callback(const_path, WitnessHierarchyItem(module, cell), data); | ||||
| 			witness_hierarchy_recursion<D, T>(path, hdlname.empty() ? hdlname_mode : -1, child, child_data, callback); | ||||
| 			path.pop_back(); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (auto mem : Mem::get_all_memories(module)) { | ||||
| 		std::vector<std::string> hdlname; | ||||
| 
 | ||||
| 		if (hdlname_mode >= 0 && mem.cell != nullptr) | ||||
| 			hdlname = mem.cell->get_hdlname_attribute(); | ||||
| 		for (auto item : hdlname) | ||||
| 			path.push_back("\\" + item); | ||||
| 		if (hdlname.size() == 1 && path.back() == mem.cell->name) | ||||
| 			hdlname.clear(); | ||||
| 		if (!hdlname.empty()) { | ||||
| 			callback(const_path, WitnessHierarchyItem(module, &mem), data); | ||||
| 		} | ||||
| 		path.resize(path_size); | ||||
| 
 | ||||
| 		if (hdlname.empty() || hdlname_mode <= 0) { | ||||
| 			path.push_back(mem.memid); | ||||
| 			callback(const_path, WitnessHierarchyItem(module, &mem), data); | ||||
| 			path.pop_back(); | ||||
| 
 | ||||
| 			if (mem.cell != nullptr && mem.cell->name != mem.memid) { | ||||
| 				path.push_back(mem.cell->name); | ||||
| 				callback(const_path, WitnessHierarchyItem(module, &mem), data); | ||||
| 				path.pop_back(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| template<typename D, typename T> | ||||
| void witness_hierarchy(RTLIL::Module *module, D data, T callback) | ||||
| { | ||||
| 	IdPath path; | ||||
| 	witness_hierarchy_recursion<D, T>(path, 0, module, data, callback); | ||||
| } | ||||
| 
 | ||||
| YOSYS_NAMESPACE_END | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue