aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwhitequark <whitequark@whitequark.org>2020-12-02 08:25:27 +0000
committerwhitequark <whitequark@whitequark.org>2020-12-02 08:25:27 +0000
commit5beab5bc17f9fa1f2340b491e073cfb973ad2e2b (patch)
tree3f1417445d1012a9afeb7b81e6440ea6dc280d8a
parent7b0cfd5c36af774ae255459d4ef0fa0934929902 (diff)
downloadyosys-5beab5bc17f9fa1f2340b491e073cfb973ad2e2b.tar.gz
yosys-5beab5bc17f9fa1f2340b491e073cfb973ad2e2b.tar.bz2
yosys-5beab5bc17f9fa1f2340b491e073cfb973ad2e2b.zip
cxxrtl: provide a way to perform unobtrusive power-on reset.
Although it is always possible to destroy and recreate the design to simulate a power-on reset, this has two drawbacks: * Black boxes are also destroyed and recreated, which causes them to reacquire their resources, which might be costly and/or erase important state. * Pointers into the design are invalidated and have to be acquired again, which is costly and might be very inconvenient if they are captured elsewhere (especially through the C API).
-rw-r--r--backends/cxxrtl/cxxrtl.h29
-rw-r--r--backends/cxxrtl/cxxrtl_backend.cc40
-rw-r--r--backends/cxxrtl/cxxrtl_capi.cc4
-rw-r--r--backends/cxxrtl/cxxrtl_capi.h8
4 files changed, 78 insertions, 3 deletions
diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h
index 41089a153..4528a7d8f 100644
--- a/backends/cxxrtl/cxxrtl.h
+++ b/backends/cxxrtl/cxxrtl.h
@@ -96,9 +96,11 @@ struct value : public expr_base<value<Bits>> {
explicit constexpr value(Init ...init) : data{init...} {}
value(const value<Bits> &) = default;
- value(value<Bits> &&) = default;
value<Bits> &operator=(const value<Bits> &) = default;
+ value(value<Bits> &&) = default;
+ value<Bits> &operator=(value<Bits> &&) = default;
+
// A (no-op) helper that forces the cast to value<>.
CXXRTL_ALWAYS_INLINE
const value<Bits> &val() const {
@@ -647,10 +649,16 @@ struct wire {
template<typename... Init>
explicit constexpr wire(Init ...init) : curr{init...}, next{init...} {}
+ // Copying and copy-assigning values is natural. If, however, a value is replaced with a wire,
+ // e.g. because a module is built with a different optimization level, then existing code could
+ // unintentionally copy a wire instead, which would create a subtle but serious bug. To make sure
+ // this doesn't happen, prohibit copying and copy-assigning wires.
wire(const wire<Bits> &) = delete;
- wire(wire<Bits> &&) = default;
wire<Bits> &operator=(const wire<Bits> &) = delete;
+ wire(wire<Bits> &&) = default;
+ wire<Bits> &operator=(wire<Bits> &&) = default;
+
template<class IntegerT>
CXXRTL_ALWAYS_INLINE
IntegerT get() const {
@@ -692,6 +700,9 @@ struct memory {
memory(const memory<Width> &) = delete;
memory<Width> &operator=(const memory<Width> &) = delete;
+ memory(memory<Width> &&) = default;
+ memory<Width> &operator=(memory<Width> &&) = default;
+
// The only way to get the compiler to put the initializer in .rodata and do not copy it on stack is to stuff it
// into a plain array. You'd think an std::initializer_list would work here, but it doesn't, because you can't
// construct an initializer_list in a constexpr (or something) and so if you try to do that the whole thing is
@@ -815,7 +826,7 @@ struct metadata {
typedef std::map<std::string, metadata> metadata_map;
-// Helper class to disambiguate values/wires and their aliases.
+// Tag class to disambiguate values/wires and their aliases.
struct debug_alias {};
// This structure is intended for consumption via foreign function interfaces, like Python's ctypes.
@@ -965,13 +976,25 @@ struct debug_items {
}
};
+// Tag class to disambiguate module move constructor and module constructor that takes black boxes
+// out of another instance of the module.
+struct adopt {};
+
struct module {
module() {}
virtual ~module() {}
+ // Modules with black boxes cannot be copied. Although not all designs include black boxes,
+ // delete the copy constructor and copy assignment operator to make sure that any downstream
+ // code that manipulates modules doesn't accidentally depend on their availability.
module(const module &) = delete;
module &operator=(const module &) = delete;
+ module(module &&) = default;
+ module &operator=(module &&) = default;
+
+ virtual void reset() = 0;
+
virtual bool eval() = 0;
virtual bool commit() = 0;
diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc
index a48ea5b23..aab415720 100644
--- a/backends/cxxrtl/cxxrtl_backend.cc
+++ b/backends/cxxrtl/cxxrtl_backend.cc
@@ -1869,6 +1869,46 @@ struct CxxrtlWorker {
}
if (has_cells)
f << "\n";
+ f << indent << mangle(module) << "() {}\n";
+ if (has_cells) {
+ f << indent << mangle(module) << "(adopt, " << mangle(module) << " other) :\n";
+ bool first = true;
+ for (auto cell : module->cells()) {
+ if (is_internal_cell(cell->type))
+ continue;
+ if (first) {
+ first = false;
+ } else {
+ f << ",\n";
+ }
+ RTLIL::Module *cell_module = module->design->module(cell->type);
+ if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) {
+ f << indent << " " << mangle(cell) << "(std::move(other." << mangle(cell) << "))";
+ } else {
+ f << indent << " " << mangle(cell) << "(adopt {}, std::move(other." << mangle(cell) << "))";
+ }
+ }
+ f << " {\n";
+ inc_indent();
+ for (auto cell : module->cells()) {
+ if (is_internal_cell(cell->type))
+ continue;
+ RTLIL::Module *cell_module = module->design->module(cell->type);
+ if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox)))
+ f << indent << mangle(cell) << "->reset();\n";
+ }
+ dec_indent();
+ f << indent << "}\n";
+ } else {
+ f << indent << mangle(module) << "(adopt, " << mangle(module) << " other) {}\n";
+ }
+ f << "\n";
+ f << indent << "void reset() override {\n";
+ inc_indent();
+ f << indent << "*this = " << mangle(module) << "(adopt {}, std::move(*this));\n";
+ dec_indent();
+ f << indent << "}\n";
+ f << "\n";
f << indent << "bool eval() override;\n";
f << indent << "bool commit() override;\n";
if (debug_info)
diff --git a/backends/cxxrtl/cxxrtl_capi.cc b/backends/cxxrtl/cxxrtl_capi.cc
index b77e4c491..1c3c63e3e 100644
--- a/backends/cxxrtl/cxxrtl_capi.cc
+++ b/backends/cxxrtl/cxxrtl_capi.cc
@@ -43,6 +43,10 @@ void cxxrtl_destroy(cxxrtl_handle handle) {
delete handle;
}
+void cxxrtl_reset(cxxrtl_handle handle) {
+ handle->module->reset();
+}
+
int cxxrtl_eval(cxxrtl_handle handle) {
return handle->module->eval();
}
diff --git a/backends/cxxrtl/cxxrtl_capi.h b/backends/cxxrtl/cxxrtl_capi.h
index 385d6dcf3..662bf2c20 100644
--- a/backends/cxxrtl/cxxrtl_capi.h
+++ b/backends/cxxrtl/cxxrtl_capi.h
@@ -55,6 +55,14 @@ cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design);
// Release all resources used by a design and its handle.
void cxxrtl_destroy(cxxrtl_handle handle);
+// Reinitialize the design, replacing the internal state with the reset values while preserving
+// black boxes.
+//
+// This operation is essentially equivalent to a power-on reset. Values, wires, and memories are
+// returned to their reset state while preserving the state of black boxes and keeping all of
+// the interior pointers obtained with e.g. `cxxrtl_get` valid.
+void cxxrtl_reset(cxxrtl_handle handle);
+
// Evaluate the design, propagating changes on inputs to the `next` value of internal state and
// output wires.
//