mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-03 21:09:12 +00:00 
			
		
		
		
	cxxrtl: handle multipart signals.
This avoids losing design visibility when using the `splitnets` pass.
This commit is contained in:
		
							parent
							
								
									fa04b19670
								
							
						
					
					
						commit
						8d712b1095
					
				
					 5 changed files with 94 additions and 27 deletions
				
			
		| 
						 | 
				
			
			@ -815,7 +815,38 @@ struct debug_item : ::cxxrtl_object {
 | 
			
		|||
};
 | 
			
		||||
static_assert(std::is_standard_layout<debug_item>::value, "debug_item is not compatible with C layout");
 | 
			
		||||
 | 
			
		||||
typedef std::map<std::string, debug_item> debug_items;
 | 
			
		||||
struct debug_items {
 | 
			
		||||
	std::map<std::string, std::vector<debug_item>> table;
 | 
			
		||||
 | 
			
		||||
	void add(const std::string &name, debug_item &&item) {
 | 
			
		||||
		std::vector<debug_item> &parts = table[name];
 | 
			
		||||
		parts.emplace_back(item);
 | 
			
		||||
		std::sort(parts.begin(), parts.end(),
 | 
			
		||||
			[](const debug_item &a, const debug_item &b) {
 | 
			
		||||
				return a.lsb_at < b.lsb_at;
 | 
			
		||||
			});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	size_t count(const std::string &name) const {
 | 
			
		||||
		if (table.count(name) == 0)
 | 
			
		||||
			return 0;
 | 
			
		||||
		return table.at(name).size();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const std::vector<debug_item> &parts_at(const std::string &name) const {
 | 
			
		||||
		return table.at(name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const debug_item &at(const std::string &name) const {
 | 
			
		||||
		const std::vector<debug_item> &parts = table.at(name);
 | 
			
		||||
		assert(parts.size() == 1);
 | 
			
		||||
		return parts.at(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const debug_item &operator [](const std::string &name) const {
 | 
			
		||||
		return at(name);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct module {
 | 
			
		||||
	module() {}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1640,19 +1640,19 @@ struct CxxrtlWorker {
 | 
			
		|||
					f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = ";
 | 
			
		||||
					dump_const(debug_const_wires[wire]);
 | 
			
		||||
					f << ";\n";
 | 
			
		||||
					f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(wire));
 | 
			
		||||
					f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
 | 
			
		||||
					f << ", debug_item(const_" << mangle(wire) << ", ";
 | 
			
		||||
					f << wire->start_offset << "));\n";
 | 
			
		||||
					count_const_wires++;
 | 
			
		||||
				} else if (debug_alias_wires.count(wire)) {
 | 
			
		||||
					// Alias of a member wire
 | 
			
		||||
					f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(wire));
 | 
			
		||||
					f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
 | 
			
		||||
					f << ", debug_item(debug_alias(), " << mangle(debug_alias_wires[wire]) << ", ";
 | 
			
		||||
					f << wire->start_offset << "));\n";
 | 
			
		||||
					count_alias_wires++;
 | 
			
		||||
				} else if (!localized_wires.count(wire)) {
 | 
			
		||||
					// Member wire
 | 
			
		||||
					f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(wire));
 | 
			
		||||
					f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
 | 
			
		||||
					f << ", debug_item(" << mangle(wire) << ", ";
 | 
			
		||||
					f << wire->start_offset << "));\n";
 | 
			
		||||
					count_member_wires++;
 | 
			
		||||
| 
						 | 
				
			
			@ -1663,7 +1663,7 @@ struct CxxrtlWorker {
 | 
			
		|||
			for (auto &memory_it : module->memories) {
 | 
			
		||||
				if (memory_it.first[0] != '\\')
 | 
			
		||||
					continue;
 | 
			
		||||
				f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(memory_it.second));
 | 
			
		||||
				f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(memory_it.second));
 | 
			
		||||
				f << ", debug_item(" << mangle(memory_it.second) << ", ";
 | 
			
		||||
				f << memory_it.second->start_offset << "));\n";
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,14 +47,17 @@ size_t cxxrtl_step(cxxrtl_handle handle) {
 | 
			
		|||
	return handle->module->step();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cxxrtl_object *cxxrtl_get(cxxrtl_handle handle, const char *name) {
 | 
			
		||||
	if (handle->objects.count(name) > 0)
 | 
			
		||||
		return static_cast<cxxrtl_object*>(&handle->objects.at(name));
 | 
			
		||||
	return nullptr;
 | 
			
		||||
struct cxxrtl_object *cxxrtl_get_parts(cxxrtl_handle handle, const char *name, size_t *parts) {
 | 
			
		||||
	auto it = handle->objects.table.find(name);
 | 
			
		||||
	if (it == handle->objects.table.end())
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	*parts = it->second.size();
 | 
			
		||||
	return static_cast<cxxrtl_object*>(&it->second[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cxxrtl_enum(cxxrtl_handle handle, void *data,
 | 
			
		||||
                 void (*callback)(void *data, const char *name, cxxrtl_object *object)) {
 | 
			
		||||
	for (auto &it : handle->objects)
 | 
			
		||||
		callback(data, it.first.c_str(), static_cast<cxxrtl_object*>(&it.second));
 | 
			
		||||
                 void (*callback)(void *data, const char *name,
 | 
			
		||||
                                  cxxrtl_object *object, size_t parts)) {
 | 
			
		||||
	for (auto &it : handle->objects.table)
 | 
			
		||||
		callback(data, it.first.c_str(), static_cast<cxxrtl_object*>(&it.second[0]), it.second.size());
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@
 | 
			
		|||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
| 
						 | 
				
			
			@ -146,17 +147,36 @@ struct cxxrtl_object {
 | 
			
		|||
// the top-level module instantiates a module `foo`, which in turn contains a wire `bar`, the full
 | 
			
		||||
// hierarchical name is `\foo \bar`.
 | 
			
		||||
//
 | 
			
		||||
// Returns the object if it was found, NULL otherwise. The returned value is valid until the design
 | 
			
		||||
// is destroyed.
 | 
			
		||||
struct cxxrtl_object *cxxrtl_get(cxxrtl_handle handle, const char *name);
 | 
			
		||||
// The storage of a single abstract object may be split (usually with the `splitnets` pass) into
 | 
			
		||||
// many physical parts, all of which correspond to the same hierarchical name. To handle such cases,
 | 
			
		||||
// this function returns an array and writes its length to `parts`. The array is sorted by `lsb_at`.
 | 
			
		||||
//
 | 
			
		||||
// Returns the object parts if it was found, NULL otherwise. The returned parts are valid until
 | 
			
		||||
// the design is destroyed.
 | 
			
		||||
struct cxxrtl_object *cxxrtl_get_parts(cxxrtl_handle handle, const char *name, size_t *parts);
 | 
			
		||||
 | 
			
		||||
// Retrieve description of a single part simulated object.
 | 
			
		||||
//
 | 
			
		||||
// This function is a shortcut for the most common use of `cxxrtl_get_parts`. It asserts that,
 | 
			
		||||
// if the object exists, it consists of a single part. If assertions are disabled, it returns NULL
 | 
			
		||||
// for multi-part objects.
 | 
			
		||||
inline struct cxxrtl_object *cxxrtl_get(cxxrtl_handle handle, const char *name) {
 | 
			
		||||
	size_t parts = 0;
 | 
			
		||||
	struct cxxrtl_object *object = cxxrtl_get_parts(handle, name, &parts);
 | 
			
		||||
	assert(object == NULL || parts == 1);
 | 
			
		||||
	if (object == NULL || parts == 1)
 | 
			
		||||
		return object;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Enumerate simulated objects.
 | 
			
		||||
//
 | 
			
		||||
// For every object in the simulation, `callback` is called with the provided `data`, the full
 | 
			
		||||
// hierarchical name of the object (see `cxxrtl_get` for details), and the object description.
 | 
			
		||||
// hierarchical name of the object (see `cxxrtl_get` for details), and the object parts.
 | 
			
		||||
// The provided `name` and `object` values are valid until the design is destroyed.
 | 
			
		||||
void cxxrtl_enum(cxxrtl_handle handle, void *data,
 | 
			
		||||
                 void (*callback)(void *data, const char *name, struct cxxrtl_object *object));
 | 
			
		||||
                 void (*callback)(void *data, const char *name,
 | 
			
		||||
                                  struct cxxrtl_object *object, size_t parts));
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,11 +66,19 @@ class vcd_writer {
 | 
			
		|||
		} while (ident != 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void emit_var(const variable &var, const std::string &type, const std::string &name) {
 | 
			
		||||
	void emit_var(const variable &var, const std::string &type, const std::string &name,
 | 
			
		||||
	              size_t lsb_at, bool multipart) {
 | 
			
		||||
		assert(!streaming);
 | 
			
		||||
		buffer += "$var " + type + " " + std::to_string(var.width) + " ";
 | 
			
		||||
		emit_ident(var.ident);
 | 
			
		||||
		buffer += " " + name + " $end\n";
 | 
			
		||||
		buffer += " " + name;
 | 
			
		||||
		if (multipart || name.back() == ']' || lsb_at != 0) {
 | 
			
		||||
			if (var.width == 1)
 | 
			
		||||
				buffer += " [" + std::to_string(lsb_at) + "]";
 | 
			
		||||
			else
 | 
			
		||||
				buffer += " [" + std::to_string(lsb_at + var.width - 1) + ":" + std::to_string(lsb_at) + "]";
 | 
			
		||||
		}
 | 
			
		||||
		buffer += " $end\n";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void emit_enddefinitions() {
 | 
			
		||||
| 
						 | 
				
			
			@ -155,7 +163,7 @@ public:
 | 
			
		|||
		emit_timescale(number, unit);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void add(const std::string &hier_name, const debug_item &item) {
 | 
			
		||||
	void add(const std::string &hier_name, const debug_item &item, bool multipart = false) {
 | 
			
		||||
		std::vector<std::string> scope = split_hierarchy(hier_name);
 | 
			
		||||
		std::string name = scope.back();
 | 
			
		||||
		scope.pop_back();
 | 
			
		||||
| 
						 | 
				
			
			@ -164,17 +172,20 @@ public:
 | 
			
		|||
		switch (item.type) {
 | 
			
		||||
			// Not the best naming but oh well...
 | 
			
		||||
			case debug_item::VALUE:
 | 
			
		||||
				emit_var(register_variable(item.width, item.curr, /*constant=*/item.next == nullptr), "wire", name);
 | 
			
		||||
				emit_var(register_variable(item.width, item.curr, /*constant=*/item.next == nullptr),
 | 
			
		||||
				         "wire", name, item.lsb_at, multipart);
 | 
			
		||||
				break;
 | 
			
		||||
			case debug_item::WIRE:
 | 
			
		||||
				emit_var(register_variable(item.width, item.curr), "reg", name);
 | 
			
		||||
				emit_var(register_variable(item.width, item.curr),
 | 
			
		||||
				         "reg", name, item.lsb_at, multipart);
 | 
			
		||||
				break;
 | 
			
		||||
			case debug_item::MEMORY: {
 | 
			
		||||
				const size_t stride = (item.width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8);
 | 
			
		||||
				for (size_t index = 0; index < item.depth; index++) {
 | 
			
		||||
					chunk_t *nth_curr = &item.curr[stride * index];
 | 
			
		||||
					std::string nth_name = name + '[' + std::to_string(index) + ']';
 | 
			
		||||
					emit_var(register_variable(item.width, nth_curr), "reg", nth_name);
 | 
			
		||||
					emit_var(register_variable(item.width, nth_curr),
 | 
			
		||||
					         "reg", nth_name, item.lsb_at, multipart);
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -183,7 +194,8 @@ public:
 | 
			
		|||
				// can actually change, and must be tracked. In most cases the VCD identifier will be
 | 
			
		||||
				// unified with the aliased reg, but we should handle the case where only the alias is
 | 
			
		||||
				// added to the VCD writer, too.
 | 
			
		||||
				emit_var(register_variable(item.width, item.curr), "wire", name);
 | 
			
		||||
				emit_var(register_variable(item.width, item.curr),
 | 
			
		||||
				         "wire", name, item.lsb_at, multipart);
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -192,9 +204,10 @@ public:
 | 
			
		|||
	void add(const debug_items &items, const Filter &filter) {
 | 
			
		||||
		// `debug_items` is a map, so the items are already sorted in an order optimal for emitting
 | 
			
		||||
		// VCD scope sections.
 | 
			
		||||
		for (auto &it : items)
 | 
			
		||||
			if (filter(it.first, it.second))
 | 
			
		||||
				add(it.first, it.second);
 | 
			
		||||
		for (auto &it : items.table)
 | 
			
		||||
			for (auto &part : it.second)
 | 
			
		||||
				if (filter(it.first, part))
 | 
			
		||||
					add(it.first, part, it.second.size() > 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void add(const debug_items &items) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue