mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 11:42:30 +00:00 
			
		
		
		
	cxxrtl: expose scope information in the C++ API.
This commit adds a `debug_scopes` container, which can collect metadata about scopes in a design. Currently the only scope is that of a module. A module scope can be represented either by a module and cell pair, or a `$scopeinfo` cell in a flattened netlist. The metadata produced by the C++ API is identical between these two cases, so flattening remains transparent to a netlist with CXXRTL. The existing `debug_items` method is deprecated. This isn't strictly necessary, but the user experience is better if the path is provided as e.g. `"top "` (as some VCD viewers make it awkward to select topmost anonymous scope), and the upgrade flow encourages that, which should reduce frustration later. While the new `debug_items` method could still be broken in the future as the C++ API permits, this seems unlikely since the debug information can now capture all common netlist aspects and includes several extension points (via `debug_item`, `debug_scope` types). Also, naming of scope paths was normalized to `path` or `top_path`, as applicable.
This commit is contained in:
		
							parent
							
								
									d903f47d41
								
							
						
					
					
						commit
						1a44645aef
					
				
					 5 changed files with 247 additions and 162 deletions
				
			
		|  | @ -35,19 +35,19 @@ cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design) { | |||
| 	return cxxrtl_create_at(design, ""); | ||||
| } | ||||
| 
 | ||||
| cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *root) { | ||||
| 	std::string path = root; | ||||
| 	if (!path.empty()) { | ||||
| cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *top_path_) { | ||||
| 	std::string top_path = top_path_; | ||||
| 	if (!top_path.empty()) { | ||||
| 		// module::debug_info() accepts either an empty path, or a path ending in space to simplify
 | ||||
| 		// the logic in generated code. While this is sketchy at best to expose in the C++ API, this
 | ||||
| 		// would be a lot worse in the C API, so don't expose it here.
 | ||||
| 		assert(path.back() != ' '); | ||||
| 		path += ' '; | ||||
| 		assert(top_path.back() != ' '); | ||||
| 		top_path += ' '; | ||||
| 	} | ||||
| 
 | ||||
| 	cxxrtl_handle handle = new _cxxrtl_handle; | ||||
| 	handle->module = std::move(design->module); | ||||
| 	handle->module->debug_info(handle->objects, path); | ||||
| 	handle->module->debug_info(handle->objects, top_path); | ||||
| 	delete design; | ||||
| 	return handle; | ||||
| } | ||||
|  |  | |||
|  | @ -55,8 +55,8 @@ cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design); | |||
| // Create a design handle at a given hierarchy position from a design toplevel.
 | ||||
| //
 | ||||
| // This operation is similar to `cxxrtl_create`, except the full hierarchical name of every object
 | ||||
| // is prepended with `root`.
 | ||||
| cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *root); | ||||
| // is prepended with `top_path`.
 | ||||
| cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *top_path); | ||||
| 
 | ||||
| // Release all resources used by a design and its handle.
 | ||||
| void cxxrtl_destroy(cxxrtl_handle handle); | ||||
|  |  | |||
|  | @ -1331,14 +1331,15 @@ struct debug_items { | |||
| 	std::map<std::string, std::vector<debug_item>> table; | ||||
| 	std::map<std::string, std::unique_ptr<debug_attrs>> attrs_table; | ||||
| 
 | ||||
| 	void add(const std::string &name, debug_item &&item, metadata_map &&item_attrs = {}) { | ||||
| 		std::unique_ptr<debug_attrs> &attrs = attrs_table[name]; | ||||
| 	void add(const std::string &path, debug_item &&item, metadata_map &&item_attrs = {}) { | ||||
| 		assert((path.empty() || path[path.size() - 1] != ' ') && path.find("  ") == std::string::npos); | ||||
| 		std::unique_ptr<debug_attrs> &attrs = attrs_table[path]; | ||||
| 		if (attrs.get() == nullptr) | ||||
| 			attrs = std::unique_ptr<debug_attrs>(new debug_attrs); | ||||
| 		for (auto attr : item_attrs) | ||||
| 			attrs->map.insert(attr); | ||||
| 		item.attrs = attrs.get(); | ||||
| 		std::vector<debug_item> &parts = table[name]; | ||||
| 		std::vector<debug_item> &parts = table[path]; | ||||
| 		parts.emplace_back(item); | ||||
| 		std::sort(parts.begin(), parts.end(), | ||||
| 			[](const debug_item &a, const debug_item &b) { | ||||
|  | @ -1346,25 +1347,58 @@ struct debug_items { | |||
| 			}); | ||||
| 	} | ||||
| 
 | ||||
| 	size_t count(const std::string &name) const { | ||||
| 		if (table.count(name) == 0) | ||||
| 	size_t count(const std::string &path) const { | ||||
| 		if (table.count(path) == 0) | ||||
| 			return 0; | ||||
| 		return table.at(name).size(); | ||||
| 		return table.at(path).size(); | ||||
| 	} | ||||
| 
 | ||||
| 	const std::vector<debug_item> &at(const std::string &name) const { | ||||
| 		return table.at(name); | ||||
| 	const std::vector<debug_item> &at(const std::string &path) const { | ||||
| 		return table.at(path); | ||||
| 	} | ||||
| 
 | ||||
| 	// Like `at()`, but operates only on single-part debug items.
 | ||||
| 	const debug_item &operator [](const std::string &name) const { | ||||
| 		const std::vector<debug_item> &parts = table.at(name); | ||||
| 	const debug_item &operator [](const std::string &path) const { | ||||
| 		const std::vector<debug_item> &parts = table.at(path); | ||||
| 		assert(parts.size() == 1); | ||||
| 		return parts.at(0); | ||||
| 	} | ||||
| 
 | ||||
| 	const metadata_map &attrs(const std::string &name) const { | ||||
| 		return attrs_table.at(name)->map; | ||||
| 	bool is_memory(const std::string &path) const { | ||||
| 		return at(path).at(0).type == debug_item::MEMORY; | ||||
| 	} | ||||
| 
 | ||||
| 	const metadata_map &attrs(const std::string &path) const { | ||||
| 		return attrs_table.at(path)->map; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| // Only `module` scopes are defined. The type is implicit, since Yosys does not currently support
 | ||||
| // any other scope types.
 | ||||
| struct debug_scope { | ||||
| 	std::string module_name; | ||||
| 	std::unique_ptr<debug_attrs> module_attrs; | ||||
| 	std::unique_ptr<debug_attrs> cell_attrs; | ||||
| }; | ||||
| 
 | ||||
| struct debug_scopes { | ||||
| 	std::map<std::string, debug_scope> table; | ||||
| 
 | ||||
| 	void add(const std::string &path, const std::string &module_name, metadata_map &&module_attrs, metadata_map &&cell_attrs) { | ||||
| 		assert((path.empty() || path[path.size() - 1] != ' ') && path.find("  ") == std::string::npos); | ||||
| 		assert(table.count(path) == 0); | ||||
| 		debug_scope &scope = table[path]; | ||||
| 		scope.module_name = module_name; | ||||
| 		scope.module_attrs = std::unique_ptr<debug_attrs>(new debug_attrs { module_attrs }); | ||||
| 		scope.cell_attrs = std::unique_ptr<debug_attrs>(new debug_attrs { cell_attrs }); | ||||
| 	} | ||||
| 
 | ||||
| 	size_t contains(const std::string &path) const { | ||||
| 		return table.count(path); | ||||
| 	} | ||||
| 
 | ||||
| 	const debug_scope &operator [](const std::string &path) const { | ||||
| 		return table.at(path); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
|  | @ -1412,8 +1446,16 @@ struct module { | |||
| 		return deltas; | ||||
| 	} | ||||
| 
 | ||||
| 	virtual void debug_info(debug_items &items, std::string path = "") { | ||||
| 		(void)items, (void)path; | ||||
| 	virtual void debug_info(debug_items *items, debug_scopes *scopes, std::string path, metadata_map &&cell_attrs = {}) { | ||||
| 		(void)items, (void)scopes, (void)path, (void)cell_attrs; | ||||
| 	} | ||||
| 
 | ||||
| 	// Compatibility method.
 | ||||
| #if __has_attribute(deprecated) | ||||
| 	__attribute__((deprecated("Use `debug_info(path, &items, /*scopes=*/nullptr);` instead. (`path` could be \"top \".)"))) | ||||
| #endif | ||||
| 	void debug_info(debug_items &items, std::string path) { | ||||
| 		debug_info(&items, /*scopes=*/nullptr, path); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -491,9 +491,9 @@ public: | |||
| 	template<typename ...Args> | ||||
| 	recorder(Args &&...args) : writer(std::forward<Args>(args)...) {} | ||||
| 
 | ||||
| 	void start(module &module) { | ||||
| 	void start(module &module, std::string top_path = "") { | ||||
| 		debug_items items; | ||||
| 		module.debug_info(items); | ||||
| 		module.debug_info(&items, /*scopes=*/nullptr, top_path); | ||||
| 		start(items); | ||||
| 	} | ||||
| 
 | ||||
|  | @ -621,9 +621,10 @@ public: | |||
| 	template<typename ...Args> | ||||
| 	player(Args &&...args) : reader(std::forward<Args>(args)...) {} | ||||
| 
 | ||||
| 	void start(module &module) { | ||||
| 	// The `top_path` must match the one given to the recorder.
 | ||||
| 	void start(module &module, std::string top_path = "") { | ||||
| 		debug_items items; | ||||
| 		module.debug_info(items); | ||||
| 		module.debug_info(&items, /*scopes=*/nullptr, top_path); | ||||
| 		start(items); | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue