From a9a873a1d26c27aea8f584fc0a85a6585926c7da Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 5 Mar 2021 11:05:19 +0000 Subject: cxxrtl: don't crash on empty designs. --- backends/cxxrtl/cxxrtl_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 39046bd78..a312c1e21 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -2763,7 +2763,7 @@ struct CxxrtlWorker { // Recheck the design if it was modified. if (did_anything) check_design(design, has_top, has_sync_init, has_packed_mem); - log_assert(has_top && !has_sync_init && !has_packed_mem); + log_assert(!has_sync_init && !has_packed_mem); log_pop(); if (did_anything) log_spacer(); -- cgit v1.2.3 From 8471808834bd15a51f1f41fdbf4a3cbffb0b4b02 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 5 Mar 2021 11:44:39 +0000 Subject: cxxrtl: add pass debug flag to show assigned wire types. Refs #2543. --- backends/cxxrtl/cxxrtl_backend.cc | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 39046bd78..8c86cf75d 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -614,6 +614,8 @@ struct CxxrtlWorker { std::ostream *impl_f = nullptr; std::ostream *intf_f = nullptr; + bool print_wire_types = false; + bool print_debug_wire_types = false; bool run_hierarchy = false; bool run_flatten = false; bool run_proc = false; @@ -2684,6 +2686,35 @@ struct CxxrtlWorker { if (debug_live_nodes[node]) debug_schedule[module].push_back(*node); } + + auto show_wire_type = [&](const RTLIL::Wire* wire, const WireType &wire_type) { + const char *type_str; + switch (wire_type.type) { + case WireType::UNUSED: type_str = "UNUSED"; break; + case WireType::BUFFERED: type_str = "BUFFERED"; break; + case WireType::MEMBER: type_str = "MEMBER"; break; + case WireType::OUTLINE: type_str = "OUTLINE"; break; + case WireType::LOCAL: type_str = "LOCAL"; break; + case WireType::INLINE: type_str = "INLINE"; break; + case WireType::ALIAS: type_str = "ALIAS"; break; + case WireType::CONST: type_str = "CONST"; break; + default: type_str = "(invalid)"; + } + if (wire_type.sig_subst.empty()) + log_debug(" %s: %s\n", log_signal((RTLIL::Wire*)wire), type_str); + else + log_debug(" %s: %s = %s\n", log_signal((RTLIL::Wire*)wire), type_str, log_signal(wire_type.sig_subst)); + }; + if (print_wire_types && !wire_types.empty()) { + log_debug("Wire types:\n"); + for (auto wire_type : wire_types) + show_wire_type(wire_type.first, wire_type.second); + } + if (print_debug_wire_types && !debug_wire_types.empty()) { + log_debug("Debug wire types:\n"); + for (auto debug_wire_type : debug_wire_types) + show_wire_type(debug_wire_type.first, debug_wire_type.second); + } } if (has_feedback_arcs || has_buffered_comb_wires) { // Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated @@ -2934,6 +2965,9 @@ struct CxxrtlBackend : public Backend { log("\n"); log("The following options are supported by this backend:\n"); log("\n"); + log(" -print-wire-types, -print-debug-wire-types\n"); + log(" enable additional debug logging, for pass developers.\n"); + log("\n"); log(" -header\n"); log(" generate separate interface (.h) and implementation (.cc) files.\n"); log(" if specified, the backend must be called with a filename, and filename\n"); @@ -3013,6 +3047,8 @@ struct CxxrtlBackend : public Backend { void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { + bool print_wire_types = false; + bool print_debug_wire_types = false; bool nohierarchy = false; bool noflatten = false; bool noproc = false; @@ -3025,6 +3061,14 @@ struct CxxrtlBackend : public Backend { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-print-wire-types") { + print_wire_types = true; + continue; + } + if (args[argidx] == "-print-debug-wire-types") { + print_debug_wire_types = true; + continue; + } if (args[argidx] == "-nohierarchy") { nohierarchy = true; continue; @@ -3076,6 +3120,8 @@ struct CxxrtlBackend : public Backend { } extra_args(f, filename, args, argidx); + worker.print_wire_types = print_wire_types; + worker.print_debug_wire_types = print_debug_wire_types; worker.run_hierarchy = !nohierarchy; worker.run_flatten = !noflatten; worker.run_proc = !noproc; -- cgit v1.2.3 From 14ce8bdaa6a1120c48a934697488c463ffb15b48 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 5 Mar 2021 12:08:48 +0000 Subject: cxxrtl: follow aliases to outlines when emitting $memrd.ADDR. --- backends/cxxrtl/cxxrtl_backend.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 39046bd78..f2ff8a468 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1231,7 +1231,9 @@ struct CxxrtlWorker { RTLIL::Memory *memory = cell->module->memories[cell->getParam(ID::MEMID).decode_string()]; std::string valid_index_temp = fresh_temporary(); f << indent << "auto " << valid_index_temp << " = memory_index("; - dump_sigspec_rhs(cell->getPort(ID::ADDR)); + // Almost all non-elidable cells cannot appear in debug_eval(), but $memrd is an exception; asynchronous + // memory read ports can. + dump_sigspec_rhs(cell->getPort(ID::ADDR), for_debug); f << ", " << memory->start_offset << ", " << memory->size << ");\n"; if (cell->type == ID($memrd)) { bool has_enable = cell->getParam(ID::CLK_ENABLE).as_bool() && !cell->getPort(ID::EN).is_fully_ones(); -- cgit v1.2.3 From d1de08e38a31c312cb0bd23e7376e80461bdcb22 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 7 Mar 2021 14:28:45 +0000 Subject: cxxrtl: allow `always` sync rules in debug_eval. These can be produced from `always @*` processes, if `-noproc` is used. --- backends/cxxrtl/cxxrtl_backend.cc | 42 +++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 17 deletions(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index d7402087f..b6cc4bb6d 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1415,30 +1415,30 @@ struct CxxrtlWorker { collect_sigspec_rhs(port.second, for_debug, cells); } - void dump_assign(const RTLIL::SigSig &sigsig) + void dump_assign(const RTLIL::SigSig &sigsig, bool for_debug = false) { f << indent; - dump_sigspec_lhs(sigsig.first); + dump_sigspec_lhs(sigsig.first, for_debug); f << " = "; - dump_sigspec_rhs(sigsig.second); + dump_sigspec_rhs(sigsig.second, for_debug); f << ";\n"; } - void dump_case_rule(const RTLIL::CaseRule *rule) + void dump_case_rule(const RTLIL::CaseRule *rule, bool for_debug = false) { for (auto action : rule->actions) - dump_assign(action); + dump_assign(action, for_debug); for (auto switch_ : rule->switches) - dump_switch_rule(switch_); + dump_switch_rule(switch_, for_debug); } - void dump_switch_rule(const RTLIL::SwitchRule *rule) + void dump_switch_rule(const RTLIL::SwitchRule *rule, bool for_debug = false) { // The switch attributes are printed before the switch condition is captured. dump_attrs(rule); std::string signal_temp = fresh_temporary(); f << indent << "const value<" << rule->signal.size() << "> &" << signal_temp << " = "; - dump_sigspec(rule->signal, /*is_lhs=*/false); + dump_sigspec(rule->signal, /*is_lhs=*/false, for_debug); f << ";\n"; bool first = true; @@ -1458,7 +1458,7 @@ struct CxxrtlWorker { first = false; if (compare.is_fully_def()) { f << signal_temp << " == "; - dump_sigspec(compare, /*is_lhs=*/false); + dump_sigspec(compare, /*is_lhs=*/false, for_debug); } else if (compare.is_fully_const()) { RTLIL::Const compare_mask, compare_value; for (auto bit : compare.as_const()) { @@ -1492,26 +1492,28 @@ struct CxxrtlWorker { } f << "{\n"; inc_indent(); - dump_case_rule(case_); + dump_case_rule(case_, for_debug); dec_indent(); } f << indent << "}\n"; } - void dump_process_case(const RTLIL::Process *proc) + void dump_process_case(const RTLIL::Process *proc, bool for_debug = false) { dump_attrs(proc); f << indent << "// process " << proc->name.str() << " case\n"; // The case attributes (for root case) are always empty. log_assert(proc->root_case.attributes.empty()); - dump_case_rule(&proc->root_case); + dump_case_rule(&proc->root_case, for_debug); } - void dump_process_syncs(const RTLIL::Process *proc) + void dump_process_syncs(const RTLIL::Process *proc, bool for_debug = false) { dump_attrs(proc); f << indent << "// process " << proc->name.str() << " syncs\n"; for (auto sync : proc->syncs) { + log_assert(!for_debug || sync->type == RTLIL::STa); + RTLIL::SigBit sync_bit; if (!sync->signal.empty()) { sync_bit = sync->signal[0]; @@ -1556,7 +1558,7 @@ struct CxxrtlWorker { f << ") {\n"; inc_indent(); for (auto action : sync->actions) - dump_assign(action); + dump_assign(action, for_debug); dec_indent(); f << indent << "}\n"; } @@ -1725,12 +1727,12 @@ struct CxxrtlWorker { case FlowGraph::Node::Type::CELL_EVAL: dump_cell_eval(node.cell); break; - case FlowGraph::Node::Type::PROCESS_SYNC: - dump_process_syncs(node.process); - break; case FlowGraph::Node::Type::PROCESS_CASE: dump_process_case(node.process); break; + case FlowGraph::Node::Type::PROCESS_SYNC: + dump_process_syncs(node.process); + break; } } } @@ -1754,6 +1756,12 @@ struct CxxrtlWorker { case FlowGraph::Node::Type::CELL_EVAL: dump_cell_eval(node.cell, /*for_debug=*/true); break; + case FlowGraph::Node::Type::PROCESS_CASE: + dump_process_case(node.process, /*for_debug=*/true); + break; + case FlowGraph::Node::Type::PROCESS_SYNC: + dump_process_syncs(node.process, /*for_debug=*/true); + break; default: log_abort(); } -- cgit v1.2.3 From ab76d9cec5ba38539f1578a78f3c810a659ea092 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 7 Mar 2021 14:29:30 +0000 Subject: cxxrtl: don't assert on edge sync rules tied to a constant. These are commonly the result of tying an async reset to an inactive level. --- backends/cxxrtl/cxxrtl_backend.cc | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index b6cc4bb6d..0071bda7f 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1518,6 +1518,8 @@ struct CxxrtlWorker { if (!sync->signal.empty()) { sync_bit = sync->signal[0]; sync_bit = sigmaps[sync_bit.wire->module](sync_bit); + if (!sync_bit.is_wire()) + continue; // a clock, or more commonly a reset, can be tied to a constant driver } pool events; @@ -2285,6 +2287,8 @@ struct CxxrtlWorker { void register_edge_signal(SigMap &sigmap, RTLIL::SigSpec signal, RTLIL::SyncType type) { signal = sigmap(signal); + if (signal.is_fully_const()) + return; // a clock, or more commonly a reset, can be tied to a constant driver log_assert(is_valid_clock(signal)); log_assert(type == RTLIL::STp || type == RTLIL::STn || type == RTLIL::STe); -- cgit v1.2.3 From d5c9595668c33b72a59644b56693ef781733adb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sat, 10 Jul 2021 03:55:51 +0200 Subject: cxxrtl: Convert to Mem helpers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This *only* does conversion, but doesn't add any new functionality — support for memory read port init/reset is still upcoming. --- backends/cxxrtl/cxxrtl_backend.cc | 482 ++++++++++++++++++++++---------------- 1 file changed, 276 insertions(+), 206 deletions(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 0071bda7f..e6941fda1 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -216,7 +216,7 @@ bool is_internal_cell(RTLIL::IdString type) bool is_effectful_cell(RTLIL::IdString type) { - return type == ID($memwr) || type.isPublic(); + return type.isPublic(); } bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell) @@ -274,12 +274,16 @@ struct FlowGraph { CELL_EVAL, PROCESS_SYNC, PROCESS_CASE, + MEM_RDPORT, + MEM_WRPORTS, }; Type type; RTLIL::SigSig connect = {}; - const RTLIL::Cell *cell = NULL; - const RTLIL::Process *process = NULL; + const RTLIL::Cell *cell = nullptr; + const RTLIL::Process *process = nullptr; + const Mem *mem = nullptr; + int portidx; }; std::vector nodes; @@ -414,7 +418,7 @@ struct FlowGraph { if (cell->output(conn.first)) { if (is_inlinable_cell(cell->type)) add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/true); - else if (is_ff_cell(cell->type) || (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool())) + else if (is_ff_cell(cell->type)) add_defs(node, conn.second, /*is_ff=*/true, /*inlinable=*/false); else if (is_internal_cell(cell->type)) add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/false); @@ -502,6 +506,49 @@ struct FlowGraph { add_case_rule_defs_uses(node, &process->root_case); return node; } + + // Memories + void add_node(const Mem *mem) { + for (int i = 0; i < GetSize(mem->rd_ports); i++) { + auto &port = mem->rd_ports[i]; + Node *node = new Node; + node->type = Node::Type::MEM_RDPORT; + node->mem = mem; + node->portidx = i; + nodes.push_back(node); + add_defs(node, port.data, /*is_ff=*/port.clk_enable, /*inlinable=*/false); + add_uses(node, port.clk); + add_uses(node, port.en); + add_uses(node, port.arst); + add_uses(node, port.srst); + add_uses(node, port.addr); + if (port.transparent && port.clk_enable) { + // Our implementation of transparent read ports reads en, addr and data from every write port + // in the same domain. + for (auto &wrport : mem->wr_ports) { + if (wrport.clk_enable && wrport.clk == port.clk && wrport.clk_polarity == port.clk_polarity) { + add_uses(node, wrport.en); + add_uses(node, wrport.addr); + add_uses(node, wrport.data); + } + } + // Also we read the address twice in this case (prevent inlining). + add_uses(node, port.addr); + } + } + if (!mem->wr_ports.empty()) { + Node *node = new Node; + node->type = Node::Type::MEM_WRPORTS; + node->mem = mem; + nodes.push_back(node); + for (auto &port : mem->wr_ports) { + add_uses(node, port.clk); + add_uses(node, port.en); + add_uses(node, port.addr); + add_uses(node, port.data); + } + } + } }; std::vector split_by(const std::string &str, const std::string &sep) @@ -637,10 +684,9 @@ struct CxxrtlWorker { int temporary = 0; dict sigmaps; + dict> mod_memories; pool edge_wires; dict edge_types; - pool writable_memories; - dict> transparent_for; dict> schedule, debug_schedule; dict wire_types, debug_wire_types; dict bit_has_state; @@ -724,9 +770,9 @@ struct CxxrtlWorker { return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl_blackbox))); } - std::string mangle(const RTLIL::Memory *memory) + std::string mangle(const Mem *mem) { - return mangle_memory_name(memory->name); + return mangle_memory_name(mem->memid); } std::string mangle(const RTLIL::Cell *cell) @@ -1216,114 +1262,6 @@ struct CxxrtlWorker { dump_sigspec_rhs(cell->getPort(ID::CLR)); f << (cell->getParam(ID::CLR_POLARITY).as_bool() ? "" : ".bit_not()") << ");\n"; } - // Memory ports - } else if (cell->type.in(ID($memrd), ID($memwr))) { - if (cell->getParam(ID::CLK_ENABLE).as_bool()) { - log_assert(!for_debug); - RTLIL::SigBit clk_bit = cell->getPort(ID::CLK)[0]; - clk_bit = sigmaps[clk_bit.wire->module](clk_bit); - if (clk_bit.wire) { - f << indent << "if (" << (cell->getParam(ID::CLK_POLARITY).as_bool() ? "posedge_" : "negedge_") - << mangle(clk_bit) << ") {\n"; - } else { - f << indent << "if (false) {\n"; - } - inc_indent(); - } - RTLIL::Memory *memory = cell->module->memories[cell->getParam(ID::MEMID).decode_string()]; - std::string valid_index_temp = fresh_temporary(); - f << indent << "auto " << valid_index_temp << " = memory_index("; - // Almost all non-elidable cells cannot appear in debug_eval(), but $memrd is an exception; asynchronous - // memory read ports can. - dump_sigspec_rhs(cell->getPort(ID::ADDR), for_debug); - f << ", " << memory->start_offset << ", " << memory->size << ");\n"; - if (cell->type == ID($memrd)) { - bool has_enable = cell->getParam(ID::CLK_ENABLE).as_bool() && !cell->getPort(ID::EN).is_fully_ones(); - if (has_enable) { - f << indent << "if ("; - dump_sigspec_rhs(cell->getPort(ID::EN)); - f << ") {\n"; - inc_indent(); - } - // The generated code has two bounds checks; one in an assertion, and another that guards the read. - // This is done so that the code does not invoke undefined behavior under any conditions, but nevertheless - // loudly crashes if an illegal condition is encountered. The assert may be turned off with -DCXXRTL_NDEBUG - // not only for release builds, but also to make sure the simulator (which is presumably embedded in some - // larger program) will never crash the code that calls into it. - // - // If assertions are disabled, out of bounds reads are defined to return zero. - f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds read\");\n"; - f << indent << "if(" << valid_index_temp << ".valid) {\n"; - inc_indent(); - if (writable_memories[memory]) { - std::string lhs_temp = fresh_temporary(); - f << indent << "value<" << memory->width << "> " << lhs_temp << " = " - << mangle(memory) << "[" << valid_index_temp << ".index];\n"; - std::vector memwr_cells(transparent_for[cell].begin(), transparent_for[cell].end()); - if (!memwr_cells.empty()) { - std::string addr_temp = fresh_temporary(); - f << indent << "const value<" << cell->getPort(ID::ADDR).size() << "> &" << addr_temp << " = "; - dump_sigspec_rhs(cell->getPort(ID::ADDR)); - f << ";\n"; - std::sort(memwr_cells.begin(), memwr_cells.end(), - [](const RTLIL::Cell *a, const RTLIL::Cell *b) { - return a->getParam(ID::PRIORITY).as_int() < b->getParam(ID::PRIORITY).as_int(); - }); - for (auto memwr_cell : memwr_cells) { - f << indent << "if (" << addr_temp << " == "; - dump_sigspec_rhs(memwr_cell->getPort(ID::ADDR)); - f << ") {\n"; - inc_indent(); - f << indent << lhs_temp << " = " << lhs_temp; - f << ".update("; - dump_sigspec_rhs(memwr_cell->getPort(ID::DATA)); - f << ", "; - dump_sigspec_rhs(memwr_cell->getPort(ID::EN)); - f << ");\n"; - dec_indent(); - f << indent << "}\n"; - } - } - f << indent; - dump_sigspec_lhs(cell->getPort(ID::DATA)); - f << " = " << lhs_temp << ";\n"; - } else { - f << indent; - dump_sigspec_lhs(cell->getPort(ID::DATA)); - f << " = " << mangle(memory) << "[" << valid_index_temp << ".index];\n"; - } - dec_indent(); - f << indent << "} else {\n"; - inc_indent(); - f << indent; - dump_sigspec_lhs(cell->getPort(ID::DATA)); - f << " = value<" << memory->width << "> {};\n"; - dec_indent(); - f << indent << "}\n"; - if (has_enable) { - dec_indent(); - f << indent << "}\n"; - } - } else /*if (cell->type == ID($memwr))*/ { - log_assert(writable_memories[memory]); - // See above for rationale of having both the assert and the condition. - // - // If assertions are disabled, out of bounds writes are defined to do nothing. - f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n"; - f << indent << "if (" << valid_index_temp << ".valid) {\n"; - inc_indent(); - f << indent << mangle(memory) << ".update(" << valid_index_temp << ".index, "; - dump_sigspec_rhs(cell->getPort(ID::DATA)); - f << ", "; - dump_sigspec_rhs(cell->getPort(ID::EN)); - f << ", " << cell->getParam(ID::PRIORITY).as_int() << ");\n"; - dec_indent(); - f << indent << "}\n"; - } - if (cell->getParam(ID::CLK_ENABLE).as_bool()) { - dec_indent(); - f << indent << "}\n"; - } // Internal cells } else if (is_internal_cell(cell->type)) { log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str()); @@ -1567,6 +1505,161 @@ struct CxxrtlWorker { } } + void dump_mem_rdport(const Mem *mem, int portidx, bool for_debug = false) + { + auto &port = mem->rd_ports[portidx]; + dump_attrs(&port); + f << indent << "// memory " << mem->memid.str() << " read port " << portidx << "\n"; + if (port.clk_enable) { + log_assert(!for_debug); + RTLIL::SigBit clk_bit = port.clk[0]; + clk_bit = sigmaps[clk_bit.wire->module](clk_bit); + if (clk_bit.wire) { + f << indent << "if (" << (port.clk_polarity ? "posedge_" : "negedge_") + << mangle(clk_bit) << ") {\n"; + } else { + f << indent << "if (false) {\n"; + } + inc_indent(); + } + std::vector inlined_cells_addr; + collect_sigspec_rhs(port.addr, for_debug, inlined_cells_addr); + if (!inlined_cells_addr.empty()) + dump_inlined_cells(inlined_cells_addr); + std::string valid_index_temp = fresh_temporary(); + f << indent << "auto " << valid_index_temp << " = memory_index("; + // Almost all non-elidable cells cannot appear in debug_eval(), but $memrd is an exception; asynchronous + // memory read ports can. + dump_sigspec_rhs(port.addr, for_debug); + f << ", " << mem->start_offset << ", " << mem->size << ");\n"; + bool has_enable = port.clk_enable && !port.en.is_fully_ones(); + if (has_enable) { + std::vector inlined_cells_en; + collect_sigspec_rhs(port.en, for_debug, inlined_cells_en); + if (!inlined_cells_en.empty()) + dump_inlined_cells(inlined_cells_en); + f << indent << "if ("; + dump_sigspec_rhs(port.en); + f << ") {\n"; + inc_indent(); + } + // The generated code has two bounds checks; one in an assertion, and another that guards the read. + // This is done so that the code does not invoke undefined behavior under any conditions, but nevertheless + // loudly crashes if an illegal condition is encountered. The assert may be turned off with -DCXXRTL_NDEBUG + // not only for release builds, but also to make sure the simulator (which is presumably embedded in some + // larger program) will never crash the code that calls into it. + // + // If assertions are disabled, out of bounds reads are defined to return zero. + f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds read\");\n"; + f << indent << "if(" << valid_index_temp << ".valid) {\n"; + inc_indent(); + if (!mem->wr_ports.empty()) { + std::string lhs_temp = fresh_temporary(); + f << indent << "value<" << mem->width << "> " << lhs_temp << " = " + << mangle(mem) << "[" << valid_index_temp << ".index];\n"; + if (port.transparent && port.clk_enable) { + std::string addr_temp = fresh_temporary(); + f << indent << "const value<" << port.addr.size() << "> &" << addr_temp << " = "; + dump_sigspec_rhs(port.addr); + f << ";\n"; + for (auto &wrport : mem->wr_ports) { + if (!wrport.clk_enable) + continue; + if (wrport.clk != port.clk) + continue; + if (wrport.clk_polarity != port.clk_polarity) + continue; + f << indent << "if (" << addr_temp << " == "; + dump_sigspec_rhs(wrport.addr); + f << ") {\n"; + inc_indent(); + f << indent << lhs_temp << " = " << lhs_temp; + f << ".update("; + dump_sigspec_rhs(wrport.data); + f << ", "; + dump_sigspec_rhs(wrport.en); + f << ");\n"; + dec_indent(); + f << indent << "}\n"; + } + } + f << indent; + dump_sigspec_lhs(port.data); + f << " = " << lhs_temp << ";\n"; + } else { + f << indent; + dump_sigspec_lhs(port.data); + f << " = " << mangle(mem) << "[" << valid_index_temp << ".index];\n"; + } + dec_indent(); + f << indent << "} else {\n"; + inc_indent(); + f << indent; + dump_sigspec_lhs(port.data); + f << " = value<" << mem->width << "> {};\n"; + dec_indent(); + f << indent << "}\n"; + if (has_enable) { + dec_indent(); + f << indent << "}\n"; + } + if (port.clk_enable) { + dec_indent(); + f << indent << "}\n"; + } + } + + void dump_mem_wrports(const Mem *mem, bool for_debug = false) + { + log_assert(!for_debug); + for (int portidx = 0; portidx < GetSize(mem->wr_ports); portidx++) { + auto &port = mem->wr_ports[portidx]; + dump_attrs(&port); + f << indent << "// memory " << mem->memid.str() << " write port " << portidx << "\n"; + if (port.clk_enable) { + RTLIL::SigBit clk_bit = port.clk[0]; + clk_bit = sigmaps[clk_bit.wire->module](clk_bit); + if (clk_bit.wire) { + f << indent << "if (" << (port.clk_polarity ? "posedge_" : "negedge_") + << mangle(clk_bit) << ") {\n"; + } else { + f << indent << "if (false) {\n"; + } + inc_indent(); + } + std::vector inlined_cells_addr; + collect_sigspec_rhs(port.addr, for_debug, inlined_cells_addr); + if (!inlined_cells_addr.empty()) + dump_inlined_cells(inlined_cells_addr); + std::string valid_index_temp = fresh_temporary(); + f << indent << "auto " << valid_index_temp << " = memory_index("; + dump_sigspec_rhs(port.addr); + f << ", " << mem->start_offset << ", " << mem->size << ");\n"; + // See above for rationale of having both the assert and the condition. + // + // If assertions are disabled, out of bounds writes are defined to do nothing. + f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n"; + f << indent << "if (" << valid_index_temp << ".valid) {\n"; + inc_indent(); + std::vector inlined_cells; + collect_sigspec_rhs(port.data, for_debug, inlined_cells); + collect_sigspec_rhs(port.en, for_debug, inlined_cells); + if (!inlined_cells.empty()) + dump_inlined_cells(inlined_cells); + f << indent << mangle(mem) << ".update(" << valid_index_temp << ".index, "; + dump_sigspec_rhs(port.data); + f << ", "; + dump_sigspec_rhs(port.en); + f << ", " << portidx << ");\n"; + dec_indent(); + f << indent << "}\n"; + if (port.clk_enable) { + dec_indent(); + f << indent << "}\n"; + } + } + } + void dump_wire(const RTLIL::Wire *wire, bool is_local) { const auto &wire_type = wire_types[wire]; @@ -1650,41 +1743,28 @@ struct CxxrtlWorker { f << "value<" << wire->width << "> " << mangle(wire) << ";\n"; } - void dump_memory(RTLIL::Module *module, const RTLIL::Memory *memory) + void dump_memory(Mem *mem) { - vector init_cells; - for (auto cell : module->cells()) - if (cell->type == ID($meminit) && cell->getParam(ID::MEMID).decode_string() == memory->name.str()) - init_cells.push_back(cell); - - std::sort(init_cells.begin(), init_cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) { - int a_addr = a->getPort(ID::ADDR).as_int(), b_addr = b->getPort(ID::ADDR).as_int(); - int a_prio = a->getParam(ID::PRIORITY).as_int(), b_prio = b->getParam(ID::PRIORITY).as_int(); - return a_prio > b_prio || (a_prio == b_prio && a_addr < b_addr); - }); - - dump_attrs(memory); - f << indent << "memory<" << memory->width << "> " << mangle(memory) - << " { " << memory->size << "u"; - if (init_cells.empty()) { + dump_attrs(mem); + f << indent << "memory<" << mem->width << "> " << mangle(mem) + << " { " << mem->size << "u"; + if (!GetSize(mem->inits)) { f << " };\n"; } else { f << ",\n"; inc_indent(); - for (auto cell : init_cells) { - dump_attrs(cell); - RTLIL::Const data = cell->getPort(ID::DATA).as_const(); - size_t width = cell->getParam(ID::WIDTH).as_int(); - size_t words = cell->getParam(ID::WORDS).as_int(); - f << indent << "memory<" << memory->width << ">::init<" << words << "> { " - << stringf("%#x", cell->getPort(ID::ADDR).as_int()) << ", {"; + for (auto &init : mem->inits) { + dump_attrs(&init); + int words = GetSize(init.data) / mem->width; + f << indent << "memory<" << mem->width << ">::init<" << words << "> { " + << stringf("%#x", init.addr.as_int()) << ", {"; inc_indent(); - for (size_t n = 0; n < words; n++) { + for (int n = 0; n < words; n++) { if (n % 4 == 0) f << "\n" << indent; else f << " "; - dump_const(data, width, n * width, /*fixed_width=*/true); + dump_const(init.data, mem->width, n * mem->width, /*fixed_width=*/true); f << ","; } dec_indent(); @@ -1735,6 +1815,12 @@ struct CxxrtlWorker { case FlowGraph::Node::Type::PROCESS_SYNC: dump_process_syncs(node.process); break; + case FlowGraph::Node::Type::MEM_RDPORT: + dump_mem_rdport(node.mem, node.portidx); + break; + case FlowGraph::Node::Type::MEM_WRPORTS: + dump_mem_wrports(node.mem); + break; } } } @@ -1764,6 +1850,12 @@ struct CxxrtlWorker { case FlowGraph::Node::Type::PROCESS_SYNC: dump_process_syncs(node.process, /*for_debug=*/true); break; + case FlowGraph::Node::Type::MEM_RDPORT: + dump_mem_rdport(node.mem, node.portidx, /*for_debug=*/true); + break; + case FlowGraph::Node::Type::MEM_WRPORTS: + dump_mem_wrports(node.mem, /*for_debug=*/true); + break; default: log_abort(); } @@ -1783,10 +1875,10 @@ struct CxxrtlWorker { f << indent << "if (" << mangle(wire) << ".commit()) changed = true;\n"; } if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { - for (auto memory : module->memories) { - if (!writable_memories[memory.second]) + for (auto &mem : mod_memories[module]) { + if (!GetSize(mem.wr_ports)) continue; - f << indent << "if (" << mangle(memory.second) << ".commit()) changed = true;\n"; + f << indent << "if (" << mangle(&mem) << ".commit()) changed = true;\n"; } for (auto cell : module->cells()) { if (is_internal_cell(cell->type)) @@ -1928,12 +2020,12 @@ struct CxxrtlWorker { } } if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { - for (auto &memory_it : module->memories) { - if (!memory_it.first.isPublic()) + for (auto &mem : mod_memories[module]) { + if (!mem.memid.isPublic()) continue; - 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"; + f << indent << "items.add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem)); + f << ", debug_item(" << mangle(&mem) << ", "; + f << mem.start_offset << "));\n"; } for (auto cell : module->cells()) { if (is_internal_cell(cell->type)) @@ -2048,8 +2140,8 @@ struct CxxrtlWorker { for (auto wire : module->wires()) dump_debug_wire(wire, /*is_local=*/false); bool has_memories = false; - for (auto memory : module->memories) { - dump_memory(module, memory.second); + for (auto &mem : mod_memories[module]) { + dump_memory(&mem); has_memories = true; } if (has_memories) @@ -2313,6 +2405,11 @@ struct CxxrtlWorker { SigMap &sigmap = sigmaps[module]; sigmap.set(module); + std::vector &memories = mod_memories[module]; + memories = Mem::get_all_memories(module); + for (auto &mem : memories) + mem.narrow(); + if (module->get_bool_attribute(ID(cxxrtl_blackbox))) { for (auto port : module->ports) { RTLIL::Wire *wire = module->wire(port); @@ -2357,13 +2454,13 @@ struct CxxrtlWorker { for (auto conn : module->connections()) flow.add_node(conn); - dict memrw_cell_nodes; - dict, - pool> memwr_per_domain; for (auto cell : module->cells()) { if (!cell->known()) log_cmd_error("Unknown cell `%s'.\n", log_id(cell->type)); + if (cell->is_mem_cell()) + continue; + RTLIL::Module *cell_module = design->module(cell->type); if (cell_module && cell_module->get_blackbox_attribute() && @@ -2375,7 +2472,7 @@ struct CxxrtlWorker { cell_module->get_bool_attribute(ID(cxxrtl_template))) blackbox_specializations[cell_module].insert(template_args(cell)); - FlowGraph::Node *node = flow.add_node(cell); + flow.add_node(cell); // Various DFF cells are treated like posedge/negedge processes, see above for details. if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($adffe), ID($dffsr), ID($dffsre), ID($sdff), ID($sdffe), ID($sdffce))) { @@ -2383,43 +2480,23 @@ struct CxxrtlWorker { register_edge_signal(sigmap, cell->getPort(ID::CLK), cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn); } - // Similar for memory port cells. - if (cell->type.in(ID($memrd), ID($memwr))) { - if (cell->getParam(ID::CLK_ENABLE).as_bool()) { - if (is_valid_clock(cell->getPort(ID::CLK))) - register_edge_signal(sigmap, cell->getPort(ID::CLK), - cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn); - } - memrw_cell_nodes[cell] = node; - } - // Optimize access to read-only memories. - if (cell->type == ID($memwr)) - writable_memories.insert(module->memories[cell->getParam(ID::MEMID).decode_string()]); - // Collect groups of memory write ports in the same domain. - if (cell->type == ID($memwr) && cell->getParam(ID::CLK_ENABLE).as_bool() && is_valid_clock(cell->getPort(ID::CLK))) { - RTLIL::SigBit clk_bit = sigmap(cell->getPort(ID::CLK))[0]; - const RTLIL::Memory *memory = module->memories[cell->getParam(ID::MEMID).decode_string()]; - memwr_per_domain[{clk_bit, memory}].insert(cell); - } - // Handling of packed memories is delegated to the `memory_unpack` pass, so we can rely on the presence - // of RTLIL memory objects and $memrd/$memwr/$meminit cells. - if (cell->type.in(ID($mem))) - log_assert(false); } - for (auto cell : module->cells()) { - // Collect groups of memory write ports read by every transparent read port. - if (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool() && is_valid_clock(cell->getPort(ID::CLK)) && - cell->getParam(ID::TRANSPARENT).as_bool()) { - RTLIL::SigBit clk_bit = sigmap(cell->getPort(ID::CLK))[0]; - const RTLIL::Memory *memory = module->memories[cell->getParam(ID::MEMID).decode_string()]; - for (auto memwr_cell : memwr_per_domain[{clk_bit, memory}]) { - transparent_for[cell].insert(memwr_cell); - // Our implementation of transparent $memrd cells reads \EN, \ADDR and \DATA from every $memwr cell - // in the same domain, which isn't directly visible in the netlist. Add these uses explicitly. - flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID::EN)); - flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID::ADDR)); - flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID::DATA)); - } + + for (auto &mem : memories) { + flow.add_node(&mem); + + // Clocked memory cells are treated like posedge/negedge processes as well. + for (auto &port : mem.rd_ports) { + if (port.clk_enable) + if (is_valid_clock(port.clk)) + register_edge_signal(sigmap, port.clk, + port.clk_polarity ? RTLIL::STp : RTLIL::STn); + } + for (auto &port : mem.wr_ports) { + if (port.clk_enable) + if (is_valid_clock(port.clk)) + register_edge_signal(sigmap, port.clk, + port.clk_polarity ? RTLIL::STp : RTLIL::STn); } } @@ -2518,6 +2595,8 @@ struct CxxrtlWorker { for (auto node : flow.nodes) { if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type)) worklist.insert(node); // node has effects + else if (node->type == FlowGraph::Node::Type::MEM_WRPORTS) + worklist.insert(node); // node is memory write else if (flow.node_sync_defs.count(node)) worklist.insert(node); // node is a flip-flop else if (flow.node_comb_defs.count(node)) { @@ -2747,9 +2826,9 @@ struct CxxrtlWorker { } } - void check_design(RTLIL::Design *design, bool &has_top, bool &has_sync_init, bool &has_packed_mem) + void check_design(RTLIL::Design *design, bool &has_top, bool &has_sync_init) { - has_sync_init = has_packed_mem = has_top = false; + has_sync_init = has_top = false; for (auto module : design->modules()) { if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl_blackbox))) @@ -2768,20 +2847,15 @@ struct CxxrtlWorker { for (auto sync : proc.second->syncs) if (sync->type == RTLIL::STi) has_sync_init = true; - - // The Mem constructor also checks for well-formedness of $meminit cells, if any. - for (auto &mem : Mem::get_all_memories(module)) - if (mem.packed) - has_packed_mem = true; } } void prepare_design(RTLIL::Design *design) { bool did_anything = false; - bool has_top, has_sync_init, has_packed_mem; + bool has_top, has_sync_init; log_push(); - check_design(design, has_top, has_sync_init, has_packed_mem); + check_design(design, has_top, has_sync_init); if (run_hierarchy && !has_top) { Pass::call(design, "hierarchy -auto-top"); did_anything = true; @@ -2801,14 +2875,10 @@ struct CxxrtlWorker { Pass::call(design, "proc_init"); did_anything = true; } - if (has_packed_mem) { - Pass::call(design, "memory_unpack"); - did_anything = true; - } // Recheck the design if it was modified. if (did_anything) - check_design(design, has_top, has_sync_init, has_packed_mem); - log_assert(!has_sync_init && !has_packed_mem); + check_design(design, has_top, has_sync_init); + log_assert(!has_sync_init); log_pop(); if (did_anything) log_spacer(); -- cgit v1.2.3 From be5cf296997a203cdf195d7355426fa4cd187b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sat, 10 Jul 2021 14:33:16 +0200 Subject: cxxrtl: Add support for mem read port initial data. --- backends/cxxrtl/cxxrtl_backend.cc | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index e6941fda1..b312878c3 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -686,6 +686,7 @@ struct CxxrtlWorker { dict sigmaps; dict> mod_memories; pool edge_wires; + dict wire_init; dict edge_types; dict> schedule, debug_schedule; dict wire_types, debug_wire_types; @@ -1681,17 +1682,17 @@ struct CxxrtlWorker { f << "<" << wire->width << ">"; } f << " " << mangle(wire); - if (wire->has_attribute(ID::init)) { + if (wire_init.count(wire)) { f << " "; - dump_const_init(wire->attributes.at(ID::init)); + dump_const_init(wire_init.at(wire)); } f << ";\n"; if (edge_wires[wire]) { if (!wire_type.is_buffered()) { f << indent << "value<" << wire->width << "> prev_" << mangle(wire); - if (wire->has_attribute(ID::init)) { + if (wire_init.count(wire)) { f << " "; - dump_const_init(wire->attributes.at(ID::init)); + dump_const_init(wire_init.at(wire)); } f << ";\n"; } @@ -2447,6 +2448,10 @@ struct CxxrtlWorker { continue; } + for (auto wire : module->wires()) + if (wire->has_attribute(ID::init)) + wire_init[wire] = wire->attributes.at(ID::init); + // Construct a flow graph where each node is a basic computational operation generally corresponding // to a fragment of the RTLIL netlist. FlowGraph flow; @@ -2491,6 +2496,19 @@ struct CxxrtlWorker { if (is_valid_clock(port.clk)) register_edge_signal(sigmap, port.clk, port.clk_polarity ? RTLIL::STp : RTLIL::STn); + // For read ports, also move initial value to wire_init (if any). + for (int i = 0; i < GetSize(port.data); i++) { + if (port.init_value[i] != State::Sx) { + SigBit bit = port.data[i]; + if (bit.wire) { + auto &init = wire_init[bit.wire]; + if (init == RTLIL::Const()) { + init = RTLIL::Const(State::Sx, GetSize(bit.wire)); + } + init[bit.offset] = port.init_value[i]; + } + } + } } for (auto &port : mem.wr_ports) { if (port.clk_enable) -- cgit v1.2.3 From af7fa62251b101266bf8419ab69e510fe5bc7e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sat, 10 Jul 2021 23:47:01 +0200 Subject: cxxrtl: Add support for memory read port reset. --- backends/cxxrtl/cxxrtl_backend.cc | 42 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index b312878c3..2c93c8ad5 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1600,7 +1600,29 @@ struct CxxrtlWorker { f << " = value<" << mem->width << "> {};\n"; dec_indent(); f << indent << "}\n"; - if (has_enable) { + if (has_enable && !port.ce_over_srst) { + dec_indent(); + f << indent << "}\n"; + } + if (port.srst != State::S0) { + // Synchronous reset + std::vector inlined_cells_srst; + collect_sigspec_rhs(port.srst, for_debug, inlined_cells_srst); + if (!inlined_cells_srst.empty()) + dump_inlined_cells(inlined_cells_srst); + f << indent << "if ("; + dump_sigspec_rhs(port.srst); + f << " == value<1> {1u}) {\n"; + inc_indent(); + f << indent; + dump_sigspec_lhs(port.data); + f << " = "; + dump_const(port.srst_value); + f << ";\n"; + dec_indent(); + f << indent << "}\n"; + } + if (has_enable && port.ce_over_srst) { dec_indent(); f << indent << "}\n"; } @@ -1608,6 +1630,24 @@ struct CxxrtlWorker { dec_indent(); f << indent << "}\n"; } + if (port.arst != State::S0) { + // Asynchronous reset + std::vector inlined_cells_arst; + collect_sigspec_rhs(port.arst, for_debug, inlined_cells_arst); + if (!inlined_cells_arst.empty()) + dump_inlined_cells(inlined_cells_arst); + f << indent << "if ("; + dump_sigspec_rhs(port.arst); + f << " == value<1> {1u}) {\n"; + inc_indent(); + f << indent; + dump_sigspec_lhs(port.data); + f << " = "; + dump_const(port.arst_value); + f << ";\n"; + dec_indent(); + f << indent << "}\n"; + } } void dump_mem_wrports(const Mem *mem, bool for_debug = false) -- cgit v1.2.3 From 37506d737cb651cd7da56e4d298df4c6f0eb616d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Mon, 12 Jul 2021 01:00:57 +0200 Subject: cxxrtl: Support memory writes in processes. --- backends/cxxrtl/cxxrtl_backend.cc | 61 +++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 6 deletions(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 2c93c8ad5..ddb954073 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -226,6 +226,14 @@ bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell) return cell_module->get_bool_attribute(ID(cxxrtl_blackbox)); } +bool is_memwr_process(const RTLIL::Process *process) +{ + for (auto sync : process->syncs) + if (!sync->mem_write_actions.empty()) + return true; + return false; +} + enum class CxxrtlPortType { UNKNOWN = 0, // or mixed comb/sync COMB = 1, @@ -481,14 +489,20 @@ struct FlowGraph { void add_sync_rules_defs_uses(Node *node, const RTLIL::Process *process) { - for (auto sync : process->syncs) - for (auto action : sync->actions) { + for (auto sync : process->syncs) { + for (auto &action : sync->actions) { if (sync->type == RTLIL::STp || sync->type == RTLIL::STn || sync->type == RTLIL::STe) - add_defs(node, action.first, /*is_ff=*/true, /*inlinable=*/false); + add_defs(node, action.first, /*is_ff=*/true, /*inlinable=*/false); else add_defs(node, action.first, /*is_ff=*/false, /*inlinable=*/false); add_uses(node, action.second); } + for (auto &memwr : sync->mem_write_actions) { + add_uses(node, memwr.address); + add_uses(node, memwr.data); + add_uses(node, memwr.enable); + } + } } Node *add_node(const RTLIL::Process *process) @@ -685,6 +699,7 @@ struct CxxrtlWorker { dict sigmaps; dict> mod_memories; + pool> writable_memories; pool edge_wires; dict wire_init; dict edge_types; @@ -776,6 +791,11 @@ struct CxxrtlWorker { return mangle_memory_name(mem->memid); } + std::string mangle(const RTLIL::Memory *memory) + { + return mangle_memory_name(memory->name); + } + std::string mangle(const RTLIL::Cell *cell) { return mangle_cell_name(cell->name); @@ -1498,8 +1518,28 @@ struct CxxrtlWorker { } f << ") {\n"; inc_indent(); - for (auto action : sync->actions) + for (auto &action : sync->actions) dump_assign(action, for_debug); + for (auto &memwr : sync->mem_write_actions) { + RTLIL::Memory *memory = proc->module->memories.at(memwr.memid); + std::string valid_index_temp = fresh_temporary(); + f << indent << "auto " << valid_index_temp << " = memory_index("; + dump_sigspec_rhs(memwr.address); + f << ", " << memory->start_offset << ", " << memory->size << ");\n"; + // See below for rationale of having both the assert and the condition. + // + // If assertions are disabled, out of bounds writes are defined to do nothing. + f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n"; + f << indent << "if (" << valid_index_temp << ".valid) {\n"; + inc_indent(); + f << indent << mangle(memory) << ".update(" << valid_index_temp << ".index, "; + dump_sigspec_rhs(memwr.data); + f << ", "; + dump_sigspec_rhs(memwr.enable); + f << ");\n"; + dec_indent(); + f << indent << "}\n"; + } dec_indent(); f << indent << "}\n"; } @@ -1917,7 +1957,7 @@ struct CxxrtlWorker { } if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { for (auto &mem : mod_memories[module]) { - if (!GetSize(mem.wr_ports)) + if (!writable_memories.count({module, mem.memid})) continue; f << indent << "if (" << mangle(&mem) << ".commit()) changed = true;\n"; } @@ -2556,12 +2596,15 @@ struct CxxrtlWorker { register_edge_signal(sigmap, port.clk, port.clk_polarity ? RTLIL::STp : RTLIL::STn); } + + if (!mem.wr_ports.empty()) + writable_memories.insert({module, mem.memid}); } for (auto proc : module->processes) { flow.add_node(proc.second); - for (auto sync : proc.second->syncs) + for (auto sync : proc.second->syncs) { switch (sync->type) { // Edge-type sync rules require pre-registration. case RTLIL::STp: @@ -2584,6 +2627,10 @@ struct CxxrtlWorker { case RTLIL::STi: log_assert(false); } + for (auto &memwr : sync->mem_write_actions) { + writable_memories.insert({module, memwr.memid}); + } + } } // Construct a linear order of the flow graph that minimizes the amount of feedback arcs. A flow graph @@ -2655,6 +2702,8 @@ struct CxxrtlWorker { worklist.insert(node); // node has effects else if (node->type == FlowGraph::Node::Type::MEM_WRPORTS) worklist.insert(node); // node is memory write + else if (node->type == FlowGraph::Node::Type::PROCESS_SYNC && is_memwr_process(node->process)) + worklist.insert(node); // node is memory write else if (flow.node_sync_defs.count(node)) worklist.insert(node); // node is a flip-flop else if (flow.node_comb_defs.count(node)) { -- cgit v1.2.3 From 8bf9cb407d929255ae985b33a537ac6d489553c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Mon, 12 Jul 2021 20:04:59 +0200 Subject: kernel/mem: Add a coalesce_inits helper. While this helper is already useful to squash sequential initializations into one in cxxrtl, its main purpose is to squash overlapping masked memory initializations (when they land) and avoid having to deal with them in cxxrtl runtime. --- backends/cxxrtl/cxxrtl_backend.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index ddb954073..70a3add5d 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1835,6 +1835,8 @@ struct CxxrtlWorker { f << ",\n"; inc_indent(); for (auto &init : mem->inits) { + if (init.removed) + continue; dump_attrs(&init); int words = GetSize(init.data) / mem->width; f << indent << "memory<" << mem->width << ">::init<" << words << "> { " @@ -2488,8 +2490,10 @@ struct CxxrtlWorker { std::vector &memories = mod_memories[module]; memories = Mem::get_all_memories(module); - for (auto &mem : memories) + for (auto &mem : memories) { mem.narrow(); + mem.coalesce_inits(); + } if (module->get_bool_attribute(ID(cxxrtl_blackbox))) { for (auto port : module->ports) { -- cgit v1.2.3 From 54b6cb645fe7ecd492521dd05072baf9086ebdb1 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 15 Jul 2021 22:27:27 +0000 Subject: cxxrtl: mark dead local wires as unused even with inlining disabled. Fixes #2739. --- backends/cxxrtl/cxxrtl_backend.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 70a3add5d..26c534bec 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -2733,12 +2733,14 @@ struct CxxrtlWorker { for (auto wire : module->wires()) { auto &wire_type = wire_types[wire]; if (!wire_type.is_local()) continue; - if (!wire->name.isPublic() && !inline_internal) continue; - if (wire->name.isPublic() && !inline_public) continue; - if (live_wires[wire].empty()) { wire_type = {WireType::UNUSED}; // wire never used - } else if (flow.is_inlinable(wire, live_wires[wire])) { + continue; + } + + if (!wire->name.isPublic() && !inline_internal) continue; + if (wire->name.isPublic() && !inline_public) continue; + if (flow.is_inlinable(wire, live_wires[wire])) { if (flow.wire_comb_defs[wire].size() > 1) log_cmd_error("Wire %s.%s has multiple drivers!\n", log_id(module), log_id(wire)); log_assert(flow.wire_comb_defs[wire].size() == 1); -- cgit v1.2.3 From 44a3d924ce30adfd3a09ffea40031a8d28445e25 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 16 Jul 2021 07:36:18 +0000 Subject: cxxrtl: don't mark buffered internal wires as UNUSED for debug. Public wires may alias buffered internal wires, so keep BUFFERED wires in debug information even if they are private. Debug items are only created for public wires, so this does not otherwise affect how debug information is emitted. Fixes #2540. Fixes #2841. --- backends/cxxrtl/cxxrtl_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 26c534bec..eea8103a7 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -2795,7 +2795,7 @@ struct CxxrtlWorker { const auto &wire_type = wire_types[wire]; auto &debug_wire_type = debug_wire_types[wire]; if (wire_type.type == WireType::UNUSED) continue; - if (!wire->name.isPublic()) continue; + if (!wire->name.isPublic() && !wire_type.is_buffered()) continue; if (!debug_info) continue; if (wire->port_input || wire_type.is_buffered()) -- cgit v1.2.3 From b28ca7f5accccae869aab1852c5b680147b3614b Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 16 Jul 2021 09:51:15 +0000 Subject: cxxrtl: don't expect user cell inputs to be wires. Ports can be connected to constants, too. (Usually resets.) Fixes #2521. --- backends/cxxrtl/cxxrtl_backend.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index eea8103a7..bf93c932a 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1295,7 +1295,7 @@ struct CxxrtlWorker { for (auto conn : cell->connections()) if (cell->input(conn.first)) { RTLIL::Module *cell_module = cell->module->design->module(cell->type); - log_assert(cell_module != nullptr && cell_module->wire(conn.first) && conn.second.is_wire()); + log_assert(cell_module != nullptr && cell_module->wire(conn.first)); RTLIL::Wire *cell_module_wire = cell_module->wire(conn.first); f << indent << mangle(cell) << access << mangle_wire_name(conn.first); if (!is_cxxrtl_blackbox_cell(cell) && wire_types[cell_module_wire].is_buffered()) { @@ -1305,7 +1305,7 @@ struct CxxrtlWorker { f << " = "; dump_sigspec_rhs(conn.second); f << ";\n"; - if (getenv("CXXRTL_VOID_MY_WARRANTY")) { + if (getenv("CXXRTL_VOID_MY_WARRANTY") && conn.second.is_wire()) { // Until we have proper clock tree detection, this really awful hack that opportunistically // propagates prev_* values for clocks can be used to estimate how much faster a design could // be if only one clock edge was simulated by replacing: -- cgit v1.2.3 From 09218896d606726c7e3cd9caa635445694338fba Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 16 Jul 2021 10:05:24 +0000 Subject: cxxrtl: emit debug items for unused public wires. This greatly improves debug information coverage. Fixes #2500. --- backends/cxxrtl/cxxrtl_backend.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index eea8103a7..bc9f516ee 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -2794,12 +2794,12 @@ struct CxxrtlWorker { for (auto wire : module->wires()) { const auto &wire_type = wire_types[wire]; auto &debug_wire_type = debug_wire_types[wire]; - if (wire_type.type == WireType::UNUSED) continue; - if (!wire->name.isPublic() && !wire_type.is_buffered()) continue; if (!debug_info) continue; if (wire->port_input || wire_type.is_buffered()) debug_wire_type = wire_type; // wire contains state + else if (!wire->name.isPublic()) + continue; // internal and stateless if (!debug_member) continue; if (wire_type.is_member()) @@ -2863,7 +2863,7 @@ struct CxxrtlWorker { auto &debug_wire_type = debug_wire_types[wire]; if (wire->name.isPublic()) continue; - if (live_wires[wire].empty() || debug_live_wires[wire].empty()) { + if (debug_live_wires[wire].empty()) { continue; // wire never used } else if (flow.is_inlinable(wire, debug_live_wires[wire])) { log_assert(flow.wire_comb_defs[wire].size() == 1); -- cgit v1.2.3 From 5b003d6e5cf1790297f5d0f9fdfc95e0b64aa808 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 16 Jul 2021 10:27:47 +0000 Subject: cxxrtl: run hierarchy pass regardless of (*top*) attribute presence. The hierarchy pass does a lot more than just finding the top module, mainly resolving implicit (positional, wildcard) module connections. Fixes #2589. --- backends/cxxrtl/cxxrtl_backend.cc | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index eea8103a7..d035dc56b 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -2939,9 +2939,9 @@ struct CxxrtlWorker { } } - void check_design(RTLIL::Design *design, bool &has_top, bool &has_sync_init) + void check_design(RTLIL::Design *design, bool &has_sync_init) { - has_sync_init = has_top = false; + has_sync_init = false; for (auto module : design->modules()) { if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl_blackbox))) @@ -2953,9 +2953,6 @@ struct CxxrtlWorker { if (!design->selected_module(module)) continue; - if (module->get_bool_attribute(ID::top)) - has_top = true; - for (auto proc : module->processes) for (auto sync : proc.second->syncs) if (sync->type == RTLIL::STi) @@ -2966,10 +2963,10 @@ struct CxxrtlWorker { void prepare_design(RTLIL::Design *design) { bool did_anything = false; - bool has_top, has_sync_init; + bool has_sync_init; log_push(); - check_design(design, has_top, has_sync_init); - if (run_hierarchy && !has_top) { + check_design(design, has_sync_init); + if (run_hierarchy) { Pass::call(design, "hierarchy -auto-top"); did_anything = true; } @@ -2990,7 +2987,7 @@ struct CxxrtlWorker { } // Recheck the design if it was modified. if (did_anything) - check_design(design, has_top, has_sync_init); + check_design(design, has_sync_init); log_assert(!has_sync_init); log_pop(); if (did_anything) -- cgit v1.2.3 From 4aa65f406f3f793978278454fe8d8000a442e2f2 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 17 Jul 2021 14:23:57 +0000 Subject: cxxrtl: treat internal wires used only for debug as constants. Fixes #2739 (again). --- backends/cxxrtl/cxxrtl_backend.cc | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 7ff344e66..56305258a 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -2881,6 +2881,12 @@ struct CxxrtlWorker { debug_live_nodes.erase(node); } else if (wire_type.is_local()) { debug_wire_type = {WireType::LOCAL}; // wire not inlinable + } else if (wire_type.type == WireType::UNUSED) { + if (wire_init.count(wire)) { + debug_wire_type = {WireType::CONST, wire_init.at(wire)}; + } else { + debug_wire_type = {WireType::CONST, RTLIL::SigSpec(RTLIL::S0, wire->width)}; + } // wire never modified } else { log_assert(wire_type.is_member()); debug_wire_type = wire_type; // wire is a member -- cgit v1.2.3 From 225af830c131084194378ad1926d2601ff0963da Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 20 Jul 2021 10:10:42 +0000 Subject: cxxrtl: treat assignable internal wires used only for debug as locals. This issue was introduced in commit 4aa65f40 while fixing #2739. Fixes #2882. --- backends/cxxrtl/cxxrtl_backend.cc | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 56305258a..24422712b 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -2879,17 +2879,19 @@ struct CxxrtlWorker { default: continue; } debug_live_nodes.erase(node); - } else if (wire_type.is_local()) { - debug_wire_type = {WireType::LOCAL}; // wire not inlinable - } else if (wire_type.type == WireType::UNUSED) { - if (wire_init.count(wire)) { - debug_wire_type = {WireType::CONST, wire_init.at(wire)}; - } else { - debug_wire_type = {WireType::CONST, RTLIL::SigSpec(RTLIL::S0, wire->width)}; - } // wire never modified + } else if (wire_type.is_member() || wire_type.is_local()) { + debug_wire_type = wire_type; // wire not inlinable } else { - log_assert(wire_type.is_member()); - debug_wire_type = wire_type; // wire is a member + log_assert(wire_type.type == WireType::UNUSED); + if (flow.wire_comb_defs[wire].size() == 0) { + if (wire_init.count(wire)) { // wire never modified + debug_wire_type = {WireType::CONST, wire_init.at(wire)}; + } else { + debug_wire_type = {WireType::CONST, RTLIL::SigSpec(RTLIL::S0, wire->width)}; + } + } else { + debug_wire_type = {WireType::LOCAL}; // wire used only for debug + } } } -- cgit v1.2.3 From 1a6ddf78921290851ca7bbe7605d9e146055dc39 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 20 Jul 2021 10:30:39 +0000 Subject: cxxrtl: treat wires with multiple defs as not inlinable. Fixes #2883. --- backends/cxxrtl/cxxrtl_backend.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 56305258a..46759e8fa 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -326,8 +326,14 @@ struct FlowGraph { for (auto bit : sig.bits()) bit_has_state[bit] |= is_ff; // Only comb defs of an entire wire in the right order can be inlined. - if (!is_ff && sig.is_wire()) - wire_def_inlinable[sig.as_wire()] = inlinable; + if (!is_ff && sig.is_wire()) { + // Only a single def of a wire can be inlined. (Multiple defs of a wire are unsound, but we + // handle them anyway to avoid assertion failures later.) + if (!wire_def_inlinable.count(sig.as_wire())) + wire_def_inlinable[sig.as_wire()] = inlinable; + else + wire_def_inlinable[sig.as_wire()] = false; + } } void add_uses(Node *node, const RTLIL::SigSpec &sig) -- cgit v1.2.3 From e6f3d1c225abecf736782f43af4f36526c63f4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sat, 31 Jul 2021 23:21:37 +0200 Subject: kernel/mem: Introduce transparency masks. --- backends/cxxrtl/cxxrtl_backend.cc | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 95ad6a86e..40e61e5af 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -542,19 +542,21 @@ struct FlowGraph { add_uses(node, port.arst); add_uses(node, port.srst); add_uses(node, port.addr); - if (port.transparent && port.clk_enable) { - // Our implementation of transparent read ports reads en, addr and data from every write port - // in the same domain. - for (auto &wrport : mem->wr_ports) { - if (wrport.clk_enable && wrport.clk == port.clk && wrport.clk_polarity == port.clk_polarity) { - add_uses(node, wrport.en); - add_uses(node, wrport.addr); - add_uses(node, wrport.data); - } + bool transparent = false; + for (int j = 0; j < GetSize(mem->wr_ports); j++) { + auto &wrport = mem->wr_ports[j]; + if (port.transparency_mask[j]) { + // Our implementation of transparent read ports reads en, addr and data from every write port + // the read port is transparent with. + add_uses(node, wrport.en); + add_uses(node, wrport.addr); + add_uses(node, wrport.data); + transparent = true; } - // Also we read the address twice in this case (prevent inlining). - add_uses(node, port.addr); } + // Also we read the read address twice in this case (prevent inlining). + if (transparent) + add_uses(node, port.addr); } if (!mem->wr_ports.empty()) { Node *node = new Node; @@ -1604,17 +1606,18 @@ struct CxxrtlWorker { std::string lhs_temp = fresh_temporary(); f << indent << "value<" << mem->width << "> " << lhs_temp << " = " << mangle(mem) << "[" << valid_index_temp << ".index];\n"; - if (port.transparent && port.clk_enable) { + bool transparent = false; + for (auto bit : port.transparency_mask) + if (bit) + transparent = true; + if (transparent) { std::string addr_temp = fresh_temporary(); f << indent << "const value<" << port.addr.size() << "> &" << addr_temp << " = "; dump_sigspec_rhs(port.addr); f << ";\n"; - for (auto &wrport : mem->wr_ports) { - if (!wrport.clk_enable) - continue; - if (wrport.clk != port.clk) - continue; - if (wrport.clk_polarity != port.clk_polarity) + for (int i = 0; i < GetSize(mem->wr_ports); i++) { + auto &wrport = mem->wr_ports[i]; + if (!port.transparency_mask[i]) continue; f << indent << "if (" << addr_temp << " == "; dump_sigspec_rhs(wrport.addr); -- cgit v1.2.3 From e7d89e653c9d295d3cc9547b83660658e4d1c95b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Sat, 2 Oct 2021 01:23:43 +0200 Subject: Hook up $aldff support in various passes. --- backends/cxxrtl/cxxrtl_backend.cc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 40e61e5af..ff28c20b3 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -206,6 +206,7 @@ bool is_ff_cell(RTLIL::IdString type) return type.in( ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce), ID($adff), ID($adffe), ID($dffsr), ID($dffsre), + ID($aldff), ID($aldffe), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr)); } @@ -1267,6 +1268,20 @@ struct CxxrtlWorker { dec_indent(); f << indent << "}\n"; } + if (cell->hasPort(ID::ALOAD)) { + // Asynchronous load + f << indent << "if ("; + dump_sigspec_rhs(cell->getPort(ID::ALOAD)); + f << " == value<1> {" << cell->getParam(ID::ALOAD_POLARITY).as_bool() << "u}) {\n"; + inc_indent(); + f << indent; + dump_sigspec_lhs(cell->getPort(ID::Q)); + f << " = "; + dump_sigspec_rhs(cell->getPort(ID::AD)); + f << ";\n"; + dec_indent(); + f << indent << "}\n"; + } if (cell->hasPort(ID::SET)) { // Asynchronous set (for individual bits) f << indent; @@ -2573,7 +2588,7 @@ struct CxxrtlWorker { flow.add_node(cell); // Various DFF cells are treated like posedge/negedge processes, see above for details. - if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($adffe), ID($dffsr), ID($dffsre), ID($sdff), ID($sdffe), ID($sdffce))) { + if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre), ID($sdff), ID($sdffe), ID($sdffce))) { if (is_valid_clock(cell->getPort(ID::CLK))) register_edge_signal(sigmap, cell->getPort(ID::CLK), cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn); -- cgit v1.2.3 From 55c9fb3b18cee0e2171486f9d4dfbd9b9e106354 Mon Sep 17 00:00:00 2001 From: Catherine Date: Sat, 11 Dec 2021 15:38:43 +0000 Subject: cxxrtl: preserve interior memory pointers across reset. Before this commit, values, wires, and memories with an initializer were value-initialized in emitted C++ code. After this commit, all values, wires, and memories are default-initialized, and the default constructor of generated modules calls the reset() method, which assigns the members that have an initializer. --- backends/cxxrtl/cxxrtl_backend.cc | 138 ++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 74 deletions(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index ff28c20b3..6623e025e 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -934,11 +934,6 @@ struct CxxrtlWorker { 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) { f << "value<" << width << ">"; @@ -1785,20 +1780,10 @@ struct CxxrtlWorker { } else { f << "<" << wire->width << ">"; } - f << " " << mangle(wire); - if (wire_init.count(wire)) { - f << " "; - dump_const_init(wire_init.at(wire)); - } - f << ";\n"; + f << " " << mangle(wire) << ";\n"; if (edge_wires[wire]) { if (!wire_type.is_buffered()) { - f << indent << "value<" << wire->width << "> prev_" << mangle(wire); - if (wire_init.count(wire)) { - f << " "; - dump_const_init(wire_init.at(wire)); - } - f << ";\n"; + f << indent << "value<" << wire->width << "> prev_" << mangle(wire) << ";\n"; } for (auto edge_type : edge_types) { if (edge_type.first.wire == wire) { @@ -1848,38 +1833,65 @@ struct CxxrtlWorker { f << "value<" << wire->width << "> " << mangle(wire) << ";\n"; } - void dump_memory(Mem *mem) + void dump_reset_method(RTLIL::Module *module) { - dump_attrs(mem); - f << indent << "memory<" << mem->width << "> " << mangle(mem) - << " { " << mem->size << "u"; - if (!GetSize(mem->inits)) { - f << " };\n"; - } else { - f << ",\n"; - inc_indent(); - for (auto &init : mem->inits) { + int mem_init_idx = 0; + inc_indent(); + for (auto wire : module->wires()) { + if (!wire_init.count(wire)) continue; + + f << indent << mangle(wire) << " = "; + if (wire_types[wire].is_buffered()) { + f << "wire<" << wire->width << ">"; + } 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) continue; dump_attrs(&init); - int words = GetSize(init.data) / mem->width; - f << indent << "memory<" << mem->width << ">::init<" << words << "> { " - << stringf("%#x", init.addr.as_int()) << ", {"; + int words = GetSize(init.data) / mem.width; + f << indent << "static const value<" << mem.width << "> "; + f << "mem_init_" << ++mem_init_idx << "[" << words << "] {"; inc_indent(); for (int n = 0; n < words; n++) { if (n % 4 == 0) f << "\n" << indent; else 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 << ","; } 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) @@ -2200,6 +2212,10 @@ struct CxxrtlWorker { dump_wire(wire, /*is_local=*/false); } f << "\n"; + f << indent << "void reset() override {\n"; + dump_reset_method(module); + f << indent << "}\n"; + f << "\n"; f << indent << "bool eval() override {\n"; dump_eval_method(module); f << indent << "}\n"; @@ -2248,7 +2264,9 @@ struct CxxrtlWorker { dump_debug_wire(wire, /*is_local=*/false); bool has_memories = false; 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; } if (has_memories) @@ -2269,52 +2287,20 @@ struct CxxrtlWorker { dump_metadata_map(cell->attributes); f << ");\n"; } else { - f << indent << mangle(cell_module) << " " << mangle(cell) << ";\n"; + f << indent << mangle(cell_module) << " " << mangle(cell) << " {interior()};\n"; } has_cells = true; } 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"; + f << indent << mangle(module) << "(interior) {}\n"; + f << indent << mangle(module) << "() {\n"; inc_indent(); - f << indent << "*this = " << mangle(module) << "(adopt {}, std::move(*this));\n"; + f << indent << "reset();\n"; dec_indent(); - f << indent << "}\n"; + f << indent << "};\n"; f << "\n"; + f << indent << "void reset() override;\n"; f << indent << "bool eval() override;\n"; f << indent << "bool commit() override;\n"; if (debug_info) { @@ -2341,6 +2327,10 @@ struct CxxrtlWorker { { if (module->get_bool_attribute(ID(cxxrtl_blackbox))) return; + f << indent << "void " << mangle(module) << "::reset() {\n"; + dump_reset_method(module); + f << indent << "}\n"; + f << "\n"; f << indent << "bool " << mangle(module) << "::eval() {\n"; dump_eval_method(module); f << indent << "}\n"; -- cgit v1.2.3 From 7f2ea7d222f159842e391d080a1b5bdc6c5a7006 Mon Sep 17 00:00:00 2001 From: Catherine Date: Wed, 15 Dec 2021 08:48:49 +0000 Subject: cxxrtl: demote wires not inlinable only in debug_eval to locals. Fixes #3112. Co-authored-by: Irides --- backends/cxxrtl/cxxrtl_backend.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 6623e025e..ba11179c6 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -2888,15 +2888,16 @@ struct CxxrtlWorker { debug_wire_type = {WireType::INLINE, node->cell}; // wire replaced with cell break; case FlowGraph::Node::Type::CONNECT: - debug_wire_type = {WireType::INLINE, node->connect.second}; // wire replaced with sig + debug_wire_type = {WireType::INLINE, node->connect.second}; // wire replaced with sig break; default: continue; } debug_live_nodes.erase(node); - } else if (wire_type.is_member() || wire_type.is_local()) { + } else if (wire_type.is_member() || wire_type.type == WireType::LOCAL) { debug_wire_type = wire_type; // wire not inlinable } else { - log_assert(wire_type.type == WireType::UNUSED); + log_assert(wire_type.type == WireType::INLINE || + wire_type.type == WireType::UNUSED); if (flow.wire_comb_defs[wire].size() == 0) { if (wire_init.count(wire)) { // wire never modified debug_wire_type = {WireType::CONST, wire_init.at(wire)}; -- cgit v1.2.3 From fc049e84a91dcfbdbd654be6db222673f6c8f26c Mon Sep 17 00:00:00 2001 From: Catherine Date: Sat, 25 Dec 2021 01:06:10 +0000 Subject: cxxrtl: don't reset elided wires with \init attribute. --- backends/cxxrtl/cxxrtl_backend.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index ba11179c6..7a336f8c1 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1838,6 +1838,8 @@ struct CxxrtlWorker { int mem_init_idx = 0; inc_indent(); for (auto wire : module->wires()) { + const auto &wire_type = wire_types[wire]; + if (!wire_type.is_named() || wire_type.is_local()) continue; if (!wire_init.count(wire)) continue; f << indent << mangle(wire) << " = "; -- cgit v1.2.3 From 93508d58dafbbffcedffa70b21a197b6fca8bb30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelina=20Ko=C5=9Bcielnicka?= Date: Mon, 24 Jan 2022 16:02:29 +0100 Subject: Add $bmux and $demux cells. --- backends/cxxrtl/cxxrtl_backend.cc | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'backends/cxxrtl/cxxrtl_backend.cc') diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 7a336f8c1..404755b1e 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -198,7 +198,7 @@ bool is_extending_cell(RTLIL::IdString type) bool is_inlinable_cell(RTLIL::IdString type) { return is_unary_cell(type) || is_binary_cell(type) || type.in( - ID($mux), ID($concat), ID($slice), ID($pmux)); + ID($mux), ID($concat), ID($slice), ID($pmux), ID($bmux), ID($demux)); } bool is_ff_cell(RTLIL::IdString type) @@ -1154,6 +1154,22 @@ struct CxxrtlWorker { for (int part = 0; part < s_width; part++) { f << ")"; } + // Big muxes + } else if (cell->type == ID($bmux)) { + dump_sigspec_rhs(cell->getPort(ID::A), for_debug); + f << ".bmux<"; + f << cell->getParam(ID::WIDTH).as_int(); + f << ">("; + dump_sigspec_rhs(cell->getPort(ID::S), for_debug); + f << ").val()"; + // Demuxes + } else if (cell->type == ID($demux)) { + dump_sigspec_rhs(cell->getPort(ID::A), for_debug); + f << ".demux<"; + f << GetSize(cell->getPort(ID::Y)); + f << ">("; + dump_sigspec_rhs(cell->getPort(ID::S), for_debug); + f << ").val()"; // Concats } else if (cell->type == ID($concat)) { dump_sigspec_rhs(cell->getPort(ID::B), for_debug); -- cgit v1.2.3