diff options
author | whitequark <whitequark@whitequark.org> | 2020-12-02 08:25:27 +0000 |
---|---|---|
committer | whitequark <whitequark@whitequark.org> | 2020-12-02 08:25:27 +0000 |
commit | 5beab5bc17f9fa1f2340b491e073cfb973ad2e2b (patch) | |
tree | 3f1417445d1012a9afeb7b81e6440ea6dc280d8a /backends/cxxrtl | |
parent | 7b0cfd5c36af774ae255459d4ef0fa0934929902 (diff) | |
download | yosys-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).
Diffstat (limited to 'backends/cxxrtl')
-rw-r--r-- | backends/cxxrtl/cxxrtl.h | 29 | ||||
-rw-r--r-- | backends/cxxrtl/cxxrtl_backend.cc | 40 | ||||
-rw-r--r-- | backends/cxxrtl/cxxrtl_capi.cc | 4 | ||||
-rw-r--r-- | backends/cxxrtl/cxxrtl_capi.h | 8 |
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. // |