cxxrtl: provide a way to perform unobtrusive power-on reset.
authorwhitequark <whitequark@whitequark.org>
Wed, 2 Dec 2020 08:25:27 +0000 (08:25 +0000)
committerwhitequark <whitequark@whitequark.org>
Wed, 2 Dec 2020 08:25:27 +0000 (08:25 +0000)
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).

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

index 41089a153d5fb32d50a5fa3683b1e8dd508a221f..4528a7d8fbe21ec5c8d34f08a6d4273664efa4e7 100644 (file)
@@ -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;
 
index a48ea5b23a8115d682b4c33d0f830d33fa47c3c7..aab415720ee69e1e9136d86e11dbfb7df07ac287 100644 (file)
@@ -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)
index b77e4c491413e5a0387b13f6f17cfd854f728bad..1c3c63e3e54df39e4b54313be5817413d5e66f5a 100644 (file)
@@ -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();
 }
index 385d6dcf386080c80a878ac34a9979807984bb8d..662bf2c20e0ff98718dd7ade5716c45d8c40f71b 100644 (file)
@@ -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.
 //