aboutsummaryrefslogtreecommitdiffstats
path: root/backends
diff options
context:
space:
mode:
authorwhitequark <whitequark@whitequark.org>2020-06-13 04:23:22 +0000
committerGitHub <noreply@github.com>2020-06-13 04:23:22 +0000
commitdc6961f3d46657e501ed7115f819d7dd6372b1ce (patch)
treefbb25450af9b11ac8702fc0dd78707b861ca9498 /backends
parent3783ad625eb99abfa234f63a9d04dca859ac7b07 (diff)
parent8d712b1095c85cd34f8f4d33798d6a7f1f6c5a2d (diff)
downloadyosys-dc6961f3d46657e501ed7115f819d7dd6372b1ce.tar.gz
yosys-dc6961f3d46657e501ed7115f819d7dd6372b1ce.tar.bz2
yosys-dc6961f3d46657e501ed7115f819d7dd6372b1ce.zip
Merge pull request #2145 from whitequark/cxxrtl-splitnets
cxxrtl: handle multipart signals
Diffstat (limited to 'backends')
-rw-r--r--backends/cxxrtl/cxxrtl.h117
-rw-r--r--backends/cxxrtl/cxxrtl_backend.cc20
-rw-r--r--backends/cxxrtl/cxxrtl_capi.cc17
-rw-r--r--backends/cxxrtl/cxxrtl_capi.h36
-rw-r--r--backends/cxxrtl/cxxrtl_vcd.h33
5 files changed, 156 insertions, 67 deletions
diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h
index d4a94fcb4..2458487e1 100644
--- a/backends/cxxrtl/cxxrtl.h
+++ b/backends/cxxrtl/cxxrtl.h
@@ -771,76 +771,119 @@ struct debug_item : ::cxxrtl_object {
debug_item(const ::cxxrtl_object &object) : cxxrtl_object(object) {}
template<size_t Bits>
- debug_item(value<Bits> &item) {
+ debug_item(value<Bits> &item, size_t lsb_offset = 0) {
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
"value<Bits> is not compatible with C layout");
- type = VALUE;
- width = Bits;
- depth = 1;
- curr = item.data;
- next = item.data;
+ type = VALUE;
+ width = Bits;
+ lsb_at = lsb_offset;
+ depth = 1;
+ zero_at = 0;
+ curr = item.data;
+ next = item.data;
}
template<size_t Bits>
- debug_item(const value<Bits> &item) {
+ debug_item(const value<Bits> &item, size_t lsb_offset = 0) {
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
"value<Bits> is not compatible with C layout");
- type = VALUE;
- width = Bits;
- depth = 1;
- curr = const_cast<chunk_t*>(item.data);
- next = nullptr;
+ type = VALUE;
+ width = Bits;
+ lsb_at = lsb_offset;
+ depth = 1;
+ zero_at = 0;
+ curr = const_cast<chunk_t*>(item.data);
+ next = nullptr;
}
template<size_t Bits>
- debug_item(wire<Bits> &item) {
+ debug_item(wire<Bits> &item, size_t lsb_offset = 0) {
static_assert(sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) &&
sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t),
"wire<Bits> is not compatible with C layout");
- type = WIRE;
- width = Bits;
- depth = 1;
- curr = item.curr.data;
- next = item.next.data;
+ type = WIRE;
+ width = Bits;
+ lsb_at = lsb_offset;
+ depth = 1;
+ zero_at = 0;
+ curr = item.curr.data;
+ next = item.next.data;
}
template<size_t Width>
- debug_item(memory<Width> &item) {
+ debug_item(memory<Width> &item, size_t zero_offset = 0) {
static_assert(sizeof(item.data[0]) == value<Width>::chunks * sizeof(chunk_t),
"memory<Width> is not compatible with C layout");
- type = MEMORY;
- width = Width;
- depth = item.data.size();
- curr = item.data.empty() ? nullptr : item.data[0].data;
- next = nullptr;
+ type = MEMORY;
+ width = Width;
+ lsb_at = 0;
+ depth = item.data.size();
+ zero_at = zero_offset;
+ curr = item.data.empty() ? nullptr : item.data[0].data;
+ next = nullptr;
}
template<size_t Bits>
- debug_item(debug_alias, const value<Bits> &item) {
+ debug_item(debug_alias, const value<Bits> &item, size_t lsb_offset = 0) {
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
"value<Bits> is not compatible with C layout");
- type = ALIAS;
- width = Bits;
- depth = 1;
- curr = const_cast<chunk_t*>(item.data);
- next = nullptr;
+ type = ALIAS;
+ width = Bits;
+ lsb_at = lsb_offset;
+ depth = 1;
+ zero_at = 0;
+ curr = const_cast<chunk_t*>(item.data);
+ next = nullptr;
}
template<size_t Bits>
- debug_item(debug_alias, const wire<Bits> &item) {
+ debug_item(debug_alias, const wire<Bits> &item, size_t lsb_offset = 0) {
static_assert(sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) &&
sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t),
"wire<Bits> is not compatible with C layout");
- type = ALIAS;
- width = Bits;
- depth = 1;
- curr = const_cast<chunk_t*>(item.curr.data);
- next = nullptr;
+ type = ALIAS;
+ width = Bits;
+ lsb_at = lsb_offset;
+ depth = 1;
+ zero_at = 0;
+ curr = const_cast<chunk_t*>(item.curr.data);
+ next = nullptr;
}
};
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() {}
diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc
index 2e39891d0..909e1325d 100644
--- a/backends/cxxrtl/cxxrtl_backend.cc
+++ b/backends/cxxrtl/cxxrtl_backend.cc
@@ -1627,18 +1627,21 @@ 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 << ", debug_item(const_" << mangle(wire) << "));\n";
+ 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 << ", debug_item(debug_alias(), " << mangle(debug_alias_wires[wire]) << "));\n";
+ 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 << ", debug_item(" << mangle(wire) << "));\n";
+ 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++;
} else {
count_skipped_wires++;
@@ -1647,8 +1650,9 @@ 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 << ", debug_item(" << mangle(memory_it.second) << "));\n";
+ 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";
}
for (auto cell : module->cells()) {
if (is_internal_cell(cell->type))
diff --git a/backends/cxxrtl/cxxrtl_capi.cc b/backends/cxxrtl/cxxrtl_capi.cc
index 489d72da5..e0566e152 100644
--- a/backends/cxxrtl/cxxrtl_capi.cc
+++ b/backends/cxxrtl/cxxrtl_capi.cc
@@ -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());
}
diff --git a/backends/cxxrtl/cxxrtl_capi.h b/backends/cxxrtl/cxxrtl_capi.h
index 8bd906ea4..599284898 100644
--- a/backends/cxxrtl/cxxrtl_capi.h
+++ b/backends/cxxrtl/cxxrtl_capi.h
@@ -26,6 +26,7 @@
#include <stddef.h>
#include <stdint.h>
+#include <assert.h>
#ifdef __cplusplus
extern "C" {
@@ -113,9 +114,15 @@ struct cxxrtl_object {
// Width of the object in bits.
size_t width;
+ // Index of the least significant bit.
+ size_t lsb_at;
+
// Depth of the object. Only meaningful for memories; for other objects, always 1.
size_t depth;
+ // Index of the first word. Only meaningful for memories; for other objects, always 0;
+ size_t zero_at;
+
// Bits stored in the object, as 32-bit chunks, least significant bits first.
//
// The width is rounded up to a multiple of 32; the padding bits are always set to 0 by
@@ -140,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
}
diff --git a/backends/cxxrtl/cxxrtl_vcd.h b/backends/cxxrtl/cxxrtl_vcd.h
index 4c2021e92..dbeabbaf2 100644
--- a/backends/cxxrtl/cxxrtl_vcd.h
+++ b/backends/cxxrtl/cxxrtl_vcd.h
@@ -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) {