3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-28 03:15:50 +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:
Catherine 2024-02-13 17:43:53 +00:00
parent d903f47d41
commit 1a44645aef
5 changed files with 247 additions and 162 deletions

View file

@ -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;
}

View file

@ -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);

View file

@ -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);
}
};

View file

@ -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);
}