cxxrtl: handle multipart signals.
authorwhitequark <whitequark@whitequark.org>
Thu, 11 Jun 2020 13:31:16 +0000 (13:31 +0000)
committerwhitequark <whitequark@whitequark.org>
Thu, 11 Jun 2020 19:34:35 +0000 (19:34 +0000)
This avoids losing design visibility when using the `splitnets` pass.

backends/cxxrtl/cxxrtl.h
backends/cxxrtl/cxxrtl_backend.cc
backends/cxxrtl/cxxrtl_capi.cc
backends/cxxrtl/cxxrtl_capi.h
backends/cxxrtl/cxxrtl_vcd.h

index b8acd02dfcdb92c8f677946df2964ff12c197528..43546bd3c526384cf62135d8497ac15245937446 100644 (file)
@@ -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() {}
index dd2029dc5175ccd4e35952e28e6fbce76b937d2c..94e823075aa2e078ce01b2649270715f662ec336 100644 (file)
@@ -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";
                        }
index 489d72da5c82409a3d7be92052f5c5fd5a18d31d..e0566e152aed9a268d377b11c00ed708499f6014 100644 (file)
@@ -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());
 }
index cdddf63f3d176f5d7c68155a3c1dea74d9c1c776..599284898c00226e57dcdc0d5a81aca11dae4639 100644 (file)
@@ -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
 }
index 4c2021e920dc187401c642178dcaba6b9f80d885..dbeabbaf2d8548862f77e1a69e4fb40af2e02b54 100644 (file)
@@ -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) {