3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-13 04:28:18 +00:00

Merge pull request #3105 from whitequark/cxxrtl-reset-memories-2

cxxrtl: preserve interior memory pointers across reset
This commit is contained in:
Catherine 2021-12-12 01:23:03 +00:00 committed by GitHub
commit bdc6ba019c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 80 additions and 108 deletions

View file

@ -722,50 +722,32 @@ std::ostream &operator<<(std::ostream &os, const wire<Bits> &val) {
template<size_t Width> template<size_t Width>
struct memory { struct memory {
std::vector<value<Width>> data; const size_t depth;
std::unique_ptr<value<Width>[]> data;
size_t depth() const { explicit memory(size_t depth) : depth(depth), data(new value<Width>[depth]) {}
return data.size();
}
memory() = delete;
explicit memory(size_t depth) : data(depth) {}
memory(const memory<Width> &) = delete; memory(const memory<Width> &) = delete;
memory<Width> &operator=(const memory<Width> &) = delete; memory<Width> &operator=(const memory<Width> &) = delete;
memory(memory<Width> &&) = default; memory(memory<Width> &&) = default;
memory<Width> &operator=(memory<Width> &&) = default; memory<Width> &operator=(memory<Width> &&other) {
assert(depth == other.depth);
// The only way to get the compiler to put the initializer in .rodata and do not copy it on stack is to stuff it data = std::move(other.data);
// into a plain array. You'd think an std::initializer_list would work here, but it doesn't, because you can't write_queue = std::move(other.write_queue);
// construct an initializer_list in a constexpr (or something) and so if you try to do that the whole thing is return *this;
// first copied on the stack (probably overflowing it) and then again into `data`.
template<size_t Size>
struct init {
size_t offset;
value<Width> data[Size];
};
template<size_t... InitSize>
explicit memory(size_t depth, const init<InitSize> &...init) : data(depth) {
data.resize(depth);
// This utterly reprehensible construct is the most reasonable way to apply a function to every element
// of a parameter pack, if the elements all have different types and so cannot be cast to an initializer list.
auto _ = {std::move(std::begin(init.data), std::end(init.data), data.begin() + init.offset)...};
(void)_;
} }
// An operator for direct memory reads. May be used at any time during the simulation. // An operator for direct memory reads. May be used at any time during the simulation.
const value<Width> &operator [](size_t index) const { const value<Width> &operator [](size_t index) const {
assert(index < data.size()); assert(index < depth);
return data[index]; return data[index];
} }
// An operator for direct memory writes. May only be used before the simulation is started. If used // An operator for direct memory writes. May only be used before the simulation is started. If used
// after the simulation is started, the design may malfunction. // after the simulation is started, the design may malfunction.
value<Width> &operator [](size_t index) { value<Width> &operator [](size_t index) {
assert(index < data.size()); assert(index < depth);
return data[index]; return data[index];
} }
@ -790,7 +772,7 @@ struct memory {
std::vector<write> write_queue; std::vector<write> write_queue;
void update(size_t index, const value<Width> &val, const value<Width> &mask, int priority = 0) { void update(size_t index, const value<Width> &val, const value<Width> &mask, int priority = 0) {
assert(index < data.size()); assert(index < depth);
// Queue up the write while keeping the queue sorted by priority. // Queue up the write while keeping the queue sorted by priority.
write_queue.insert( write_queue.insert(
std::upper_bound(write_queue.begin(), write_queue.end(), priority, std::upper_bound(write_queue.begin(), write_queue.end(), priority,
@ -947,9 +929,9 @@ struct debug_item : ::cxxrtl_object {
flags = 0; flags = 0;
width = Width; width = Width;
lsb_at = 0; lsb_at = 0;
depth = item.data.size(); depth = item.depth;
zero_at = zero_offset; zero_at = zero_offset;
curr = item.data.empty() ? nullptr : item.data[0].data; curr = item.data ? item.data[0].data : nullptr;
next = nullptr; next = nullptr;
outline = nullptr; outline = nullptr;
} }
@ -1051,9 +1033,9 @@ struct debug_items {
} }
}; };
// Tag class to disambiguate module move constructor and module constructor that takes black boxes // Tag class to disambiguate the default constructor used by the toplevel module that calls reset(),
// out of another instance of the module. // and the constructor of interior modules that should not call it.
struct adopt {}; struct interior {};
struct module { struct module {
module() {} module() {}

View file

@ -934,11 +934,6 @@ struct CxxrtlWorker {
f << "}"; f << "}";
} }
void dump_const_init(const RTLIL::Const &data)
{
dump_const_init(data, data.size());
}
void dump_const(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false) void dump_const(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false)
{ {
f << "value<" << width << ">"; f << "value<" << width << ">";
@ -1785,20 +1780,10 @@ struct CxxrtlWorker {
} else { } else {
f << "<" << wire->width << ">"; f << "<" << wire->width << ">";
} }
f << " " << mangle(wire); f << " " << mangle(wire) << ";\n";
if (wire_init.count(wire)) {
f << " ";
dump_const_init(wire_init.at(wire));
}
f << ";\n";
if (edge_wires[wire]) { if (edge_wires[wire]) {
if (!wire_type.is_buffered()) { if (!wire_type.is_buffered()) {
f << indent << "value<" << wire->width << "> prev_" << mangle(wire); f << indent << "value<" << wire->width << "> prev_" << mangle(wire) << ";\n";
if (wire_init.count(wire)) {
f << " ";
dump_const_init(wire_init.at(wire));
}
f << ";\n";
} }
for (auto edge_type : edge_types) { for (auto edge_type : edge_types) {
if (edge_type.first.wire == wire) { if (edge_type.first.wire == wire) {
@ -1848,38 +1833,65 @@ struct CxxrtlWorker {
f << "value<" << wire->width << "> " << mangle(wire) << ";\n"; f << "value<" << wire->width << "> " << mangle(wire) << ";\n";
} }
void dump_memory(Mem *mem) void dump_reset_method(RTLIL::Module *module)
{ {
dump_attrs(mem); int mem_init_idx = 0;
f << indent << "memory<" << mem->width << "> " << mangle(mem) inc_indent();
<< " { " << mem->size << "u"; for (auto wire : module->wires()) {
if (!GetSize(mem->inits)) { if (!wire_init.count(wire)) continue;
f << " };\n";
} else { f << indent << mangle(wire) << " = ";
f << ",\n"; if (wire_types[wire].is_buffered()) {
inc_indent(); f << "wire<" << wire->width << ">";
for (auto &init : mem->inits) { } else {
f << "value<" << wire->width << ">";
}
dump_const_init(wire_init.at(wire), wire->width);
f << ";\n";
if (edge_wires[wire] && !wire_types[wire].is_buffered()) {
f << indent << "prev_" << mangle(wire) << " = ";
dump_const(wire_init.at(wire), wire->width);
f << ";\n";
}
}
for (auto &mem : mod_memories[module]) {
for (auto &init : mem.inits) {
if (init.removed) if (init.removed)
continue; continue;
dump_attrs(&init); dump_attrs(&init);
int words = GetSize(init.data) / mem->width; int words = GetSize(init.data) / mem.width;
f << indent << "memory<" << mem->width << ">::init<" << words << "> { " f << indent << "static const value<" << mem.width << "> ";
<< stringf("%#x", init.addr.as_int()) << ", {"; f << "mem_init_" << ++mem_init_idx << "[" << words << "] {";
inc_indent(); inc_indent();
for (int n = 0; n < words; n++) { for (int n = 0; n < words; n++) {
if (n % 4 == 0) if (n % 4 == 0)
f << "\n" << indent; f << "\n" << indent;
else else
f << " "; f << " ";
dump_const(init.data, mem->width, n * mem->width, /*fixed_width=*/true); dump_const(init.data, mem.width, n * mem.width, /*fixed_width=*/true);
f << ","; f << ",";
} }
dec_indent(); dec_indent();
f << "\n" << indent << "}},\n"; f << "\n";
f << indent << "};\n";
f << indent << "std::copy(std::begin(mem_init_" << mem_init_idx << "), ";
f << "std::end(mem_init_" << mem_init_idx << "), ";
f << "&" << mangle(&mem) << ".data[" << stringf("%#x", init.addr.as_int()) << "]);\n";
} }
dec_indent(); }
f << indent << "};\n"; for (auto cell : module->cells()) {
} if (is_internal_cell(cell->type))
continue;
f << indent << mangle(cell);
RTLIL::Module *cell_module = module->design->module(cell->type);
if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) {
f << "->reset();\n";
} else {
f << ".reset();\n";
}
}
dec_indent();
} }
void dump_eval_method(RTLIL::Module *module) void dump_eval_method(RTLIL::Module *module)
@ -2200,6 +2212,10 @@ struct CxxrtlWorker {
dump_wire(wire, /*is_local=*/false); dump_wire(wire, /*is_local=*/false);
} }
f << "\n"; f << "\n";
f << indent << "void reset() override {\n";
dump_reset_method(module);
f << indent << "}\n";
f << "\n";
f << indent << "bool eval() override {\n"; f << indent << "bool eval() override {\n";
dump_eval_method(module); dump_eval_method(module);
f << indent << "}\n"; f << indent << "}\n";
@ -2248,7 +2264,9 @@ struct CxxrtlWorker {
dump_debug_wire(wire, /*is_local=*/false); dump_debug_wire(wire, /*is_local=*/false);
bool has_memories = false; bool has_memories = false;
for (auto &mem : mod_memories[module]) { for (auto &mem : mod_memories[module]) {
dump_memory(&mem); dump_attrs(&mem);
f << indent << "memory<" << mem.width << "> " << mangle(&mem)
<< " { " << mem.size << "u };\n";
has_memories = true; has_memories = true;
} }
if (has_memories) if (has_memories)
@ -2269,52 +2287,20 @@ struct CxxrtlWorker {
dump_metadata_map(cell->attributes); dump_metadata_map(cell->attributes);
f << ");\n"; f << ");\n";
} else { } else {
f << indent << mangle(cell_module) << " " << mangle(cell) << ";\n"; f << indent << mangle(cell_module) << " " << mangle(cell) << " {interior()};\n";
} }
has_cells = true; has_cells = true;
} }
if (has_cells) if (has_cells)
f << "\n"; f << "\n";
f << indent << mangle(module) << "() {}\n"; f << indent << mangle(module) << "(interior) {}\n";
if (has_cells) { f << indent << mangle(module) << "() {\n";
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(); inc_indent();
f << indent << "*this = " << mangle(module) << "(adopt {}, std::move(*this));\n"; f << indent << "reset();\n";
dec_indent(); dec_indent();
f << indent << "}\n"; f << indent << "};\n";
f << "\n"; f << "\n";
f << indent << "void reset() override;\n";
f << indent << "bool eval() override;\n"; f << indent << "bool eval() override;\n";
f << indent << "bool commit() override;\n"; f << indent << "bool commit() override;\n";
if (debug_info) { if (debug_info) {
@ -2341,6 +2327,10 @@ struct CxxrtlWorker {
{ {
if (module->get_bool_attribute(ID(cxxrtl_blackbox))) if (module->get_bool_attribute(ID(cxxrtl_blackbox)))
return; return;
f << indent << "void " << mangle(module) << "::reset() {\n";
dump_reset_method(module);
f << indent << "}\n";
f << "\n";
f << indent << "bool " << mangle(module) << "::eval() {\n"; f << indent << "bool " << mangle(module) << "::eval() {\n";
dump_eval_method(module); dump_eval_method(module);
f << indent << "}\n"; f << indent << "}\n";