diff options
Diffstat (limited to 'backends/cxxrtl')
-rw-r--r-- | backends/cxxrtl/cxxrtl.cc | 139 |
1 files changed, 73 insertions, 66 deletions
diff --git a/backends/cxxrtl/cxxrtl.cc b/backends/cxxrtl/cxxrtl.cc index 237700b29..0cceecbba 100644 --- a/backends/cxxrtl/cxxrtl.cc +++ b/backends/cxxrtl/cxxrtl.cc @@ -212,14 +212,14 @@ bool is_ff_cell(RTLIL::IdString type) bool is_internal_cell(RTLIL::IdString type) { - return type[0] == '$' && !type.begins_with("$paramod\\"); + return type[0] == '$' && !type.begins_with("$paramod"); } bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell) { RTLIL::Module *cell_module = cell->module->design->module(cell->type); log_assert(cell_module != nullptr); - return cell_module->get_bool_attribute(ID(cxxrtl.blackbox)); + return cell_module->get_bool_attribute(ID(cxxrtl_blackbox)); } enum class CxxrtlPortType { @@ -231,14 +231,14 @@ enum class CxxrtlPortType { CxxrtlPortType cxxrtl_port_type(const RTLIL::Cell *cell, RTLIL::IdString port) { RTLIL::Module *cell_module = cell->module->design->module(cell->type); - if (cell_module == nullptr || !cell_module->get_bool_attribute(ID(cxxrtl.blackbox))) + if (cell_module == nullptr || !cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) return CxxrtlPortType::UNKNOWN; RTLIL::Wire *cell_output_wire = cell_module->wire(port); log_assert(cell_output_wire != nullptr); - bool is_comb = cell_output_wire->get_bool_attribute(ID(cxxrtl.comb)); - bool is_sync = cell_output_wire->get_bool_attribute(ID(cxxrtl.sync)); + bool is_comb = cell_output_wire->get_bool_attribute(ID(cxxrtl_comb)); + bool is_sync = cell_output_wire->get_bool_attribute(ID(cxxrtl_sync)); if (is_comb && is_sync) - log_cmd_error("Port `%s.%s' is marked as both `cxxrtl.comb` and `cxxrtl.sync`.\n", + log_cmd_error("Port `%s.%s' is marked as both `cxxrtl_comb` and `cxxrtl_sync`.\n", log_id(cell_module), log_signal(cell_output_wire)); else if (is_comb) return CxxrtlPortType::COMB; @@ -513,7 +513,6 @@ struct CxxrtlWorker { bool elide_public = false; bool localize_internal = false; bool localize_public = false; - bool run_opt_clean_purge = false; bool run_proc_flatten = false; bool max_opt_level = false; @@ -606,7 +605,7 @@ struct CxxrtlWorker { std::string mangle(const RTLIL::Module *module) { - return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl.blackbox))); + return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl_blackbox))); } std::string mangle(const RTLIL::Memory *memory) @@ -634,19 +633,19 @@ struct CxxrtlWorker { std::vector<std::string> template_param_names(const RTLIL::Module *module) { - if (!module->has_attribute(ID(cxxrtl.template))) + if (!module->has_attribute(ID(cxxrtl_template))) return {}; - if (module->attributes.at(ID(cxxrtl.template)).flags != RTLIL::CONST_FLAG_STRING) - log_cmd_error("Attribute `cxxrtl.template' of module `%s' is not a string.\n", log_id(module)); + if (module->attributes.at(ID(cxxrtl_template)).flags != RTLIL::CONST_FLAG_STRING) + log_cmd_error("Attribute `cxxrtl_template' of module `%s' is not a string.\n", log_id(module)); - std::vector<std::string> param_names = split_by(module->get_string_attribute(ID(cxxrtl.template)), " \t"); + std::vector<std::string> param_names = split_by(module->get_string_attribute(ID(cxxrtl_template)), " \t"); for (const auto ¶m_name : param_names) { // Various lowercase prefixes (p_, i_, cell_, ...) are used for member variables, so require // parameters to start with an uppercase letter to avoid name conflicts. (This is the convention // in both Verilog and C++, anyway.) if (!isupper(param_name[0])) - log_cmd_error("Attribute `cxxrtl.template' of module `%s' includes a parameter `%s', " + log_cmd_error("Attribute `cxxrtl_template' of module `%s' includes a parameter `%s', " "which does not start with an uppercase letter.\n", log_id(module), param_name.c_str()); } @@ -677,7 +676,7 @@ struct CxxrtlWorker { { RTLIL::Module *cell_module = cell->module->design->module(cell->type); log_assert(cell_module != nullptr); - if (!cell_module->get_bool_attribute(ID(cxxrtl.blackbox))) + if (!cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) return ""; std::vector<std::string> param_names = template_param_names(cell_module); @@ -726,12 +725,13 @@ struct CxxrtlWorker { void dump_const_init(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false) { + const int CHUNK_SIZE = 32; f << "{"; while (width > 0) { - const int CHUNK_SIZE = 32; - uint32_t chunk = data.extract(offset, width > CHUNK_SIZE ? CHUNK_SIZE : width).as_int(); + int chunk_width = min(width, CHUNK_SIZE); + uint32_t chunk = data.extract(offset, chunk_width).as_int(); if (fixed_width) - f << stringf("0x%08xu", chunk); + f << stringf("0x%.*xu", (3 + chunk_width) / 4, chunk); else f << stringf("%#xu", chunk); if (width > CHUNK_SIZE) @@ -1418,8 +1418,8 @@ struct CxxrtlWorker { f << indent << "value<" << wire->width << "> " << mangle(wire) << ";\n"; } else { std::string width; - if (wire->module->has_attribute(ID(cxxrtl.blackbox)) && wire->has_attribute(ID(cxxrtl.width))) { - width = wire->get_string_attribute(ID(cxxrtl.width)); + if (wire->module->has_attribute(ID(cxxrtl_blackbox)) && wire->has_attribute(ID(cxxrtl_width))) { + width = wire->get_string_attribute(ID(cxxrtl_width)); } else { width = std::to_string(wire->width); } @@ -1521,7 +1521,7 @@ struct CxxrtlWorker { { inc_indent(); f << indent << "bool converged = " << (eval_converges.at(module) ? "true" : "false") << ";\n"; - if (!module->get_bool_attribute(ID(cxxrtl.blackbox))) { + if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { for (auto wire : module->wires()) { if (edge_wires[wire]) { for (auto edge_type : edge_types) { @@ -1573,10 +1573,10 @@ struct CxxrtlWorker { f << indent << "prev_" << mangle(wire) << " = " << mangle(wire) << ";\n"; continue; } - if (!module->get_bool_attribute(ID(cxxrtl.blackbox)) || wire->port_id != 0) + if (!module->get_bool_attribute(ID(cxxrtl_blackbox)) || wire->port_id != 0) f << indent << "changed |= " << mangle(wire) << ".commit();\n"; } - if (!module->get_bool_attribute(ID(cxxrtl.blackbox))) { + if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { for (auto memory : module->memories) { if (!writable_memories[memory.second]) continue; @@ -1623,8 +1623,8 @@ struct CxxrtlWorker { void dump_module_intf(RTLIL::Module *module) { dump_attrs(module); - if (module->get_bool_attribute(ID(cxxrtl.blackbox))) { - if (module->has_attribute(ID(cxxrtl.template))) + if (module->get_bool_attribute(ID(cxxrtl_blackbox))) { + if (module->has_attribute(ID(cxxrtl_template))) f << indent << "template" << template_params(module, /*is_decl=*/true) << "\n"; f << indent << "struct " << mangle(module) << " : public module {\n"; inc_indent(); @@ -1686,7 +1686,7 @@ struct CxxrtlWorker { dump_attrs(cell); RTLIL::Module *cell_module = module->design->module(cell->type); log_assert(cell_module != nullptr); - if (cell_module->get_bool_attribute(ID(cxxrtl.blackbox))) { + if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) { f << indent << "std::unique_ptr<" << mangle(cell_module) << template_args(cell) << "> "; f << mangle(cell) << " = " << mangle(cell_module) << template_args(cell); f << "::create(" << escape_cxx_string(cell->name.str()) << ", "; @@ -1711,7 +1711,7 @@ struct CxxrtlWorker { void dump_module_impl(RTLIL::Module *module) { - if (module->get_bool_attribute(ID(cxxrtl.blackbox))) + if (module->get_bool_attribute(ID(cxxrtl_blackbox))) return; f << indent << "bool " << mangle(module) << "::eval() {\n"; dump_eval_method(module); @@ -1730,9 +1730,9 @@ struct CxxrtlWorker { for (auto module : design->modules()) { if (!design->selected_module(module)) continue; - if (module->get_bool_attribute(ID(cxxrtl.blackbox))) + if (module->get_bool_attribute(ID(cxxrtl_blackbox))) modules.push_back(module); // cxxrtl blackboxes first - if (module->get_blackbox_attribute() || module->get_bool_attribute(ID(cxxrtl.blackbox))) + if (module->get_blackbox_attribute() || module->get_bool_attribute(ID(cxxrtl_blackbox))) continue; topo_design.node(module); @@ -1821,16 +1821,16 @@ struct CxxrtlWorker { SigMap &sigmap = sigmaps[module]; sigmap.set(module); - if (module->get_bool_attribute(ID(cxxrtl.blackbox))) { + if (module->get_bool_attribute(ID(cxxrtl_blackbox))) { for (auto port : module->ports) { RTLIL::Wire *wire = module->wire(port); - if (wire->has_attribute(ID(cxxrtl.edge))) { - RTLIL::Const edge_attr = wire->attributes[ID(cxxrtl.edge)]; + if (wire->has_attribute(ID(cxxrtl_edge))) { + RTLIL::Const edge_attr = wire->attributes[ID(cxxrtl_edge)]; if (!(edge_attr.flags & RTLIL::CONST_FLAG_STRING) || (int)edge_attr.decode_string().size() != GetSize(wire)) - log_cmd_error("Attribute `cxxrtl.edge' of port `%s.%s' is not a string with one character per bit.\n", + log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' is not a string with one character per bit.\n", log_id(module), log_signal(wire)); - std::string edges = wire->get_string_attribute(ID(cxxrtl.edge)); + std::string edges = wire->get_string_attribute(ID(cxxrtl_edge)); for (int i = 0; i < GetSize(wire); i++) { RTLIL::SigSpec wire_sig = wire; switch (edges[i]) { @@ -1839,7 +1839,7 @@ struct CxxrtlWorker { case 'n': register_edge_signal(sigmap, wire_sig[i], RTLIL::STn); break; case 'a': register_edge_signal(sigmap, wire_sig[i], RTLIL::STe); break; default: - log_cmd_error("Attribute `cxxrtl.edge' of port `%s.%s' contains specifiers " + log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' contains specifiers " "other than '-', 'p', 'n', or 'a'.\n", log_id(module), log_signal(wire)); } @@ -1868,12 +1868,12 @@ struct CxxrtlWorker { RTLIL::Module *cell_module = design->module(cell->type); if (cell_module && cell_module->get_blackbox_attribute() && - !cell_module->get_bool_attribute(ID(cxxrtl.blackbox))) + !cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) log_cmd_error("External blackbox cell `%s' is not marked as a CXXRTL blackbox.\n", log_id(cell->type)); if (cell_module && - cell_module->get_bool_attribute(ID(cxxrtl.blackbox)) && - cell_module->get_bool_attribute(ID(cxxrtl.template))) + cell_module->get_bool_attribute(ID(cxxrtl_blackbox)) && + cell_module->get_bool_attribute(ID(cxxrtl_template))) blackbox_specializations[cell_module].insert(template_args(cell)); FlowGraph::Node *node = flow.add_node(cell); @@ -1942,13 +1942,13 @@ struct CxxrtlWorker { case RTLIL::STa: break; + case RTLIL::STg: + log_cmd_error("Global clock is not supported.\n"); + // Handling of init-type sync rules is delegated to the `proc_init` pass, so we can use the wire // attribute regardless of input. case RTLIL::STi: log_assert(false); - - case RTLIL::STg: - log_cmd_error("Global clock is not supported.\n"); } } @@ -2008,6 +2008,7 @@ struct CxxrtlWorker { log("Module `%s' contains feedback arcs through wires:\n", log_id(module)); for (auto wire : feedback_wires) log(" %s\n", log_id(wire)); + log("\n"); } for (auto wire : module->wires()) { @@ -2039,20 +2040,20 @@ struct CxxrtlWorker { log("Module `%s' contains buffered combinatorial wires:\n", log_id(module)); for (auto wire : buffered_wires) log(" %s\n", log_id(wire)); + log("\n"); } eval_converges[module] = feedback_wires.empty() && buffered_wires.empty(); } if (has_feedback_arcs || has_buffered_wires) { // Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated - // by optimizing the design, if after `opt_clean -purge` there are any feedback wires remaining, it is very + // by optimizing the design, if after `proc; flatten` there are any feedback wires remaining, it is very // likely that these feedback wires are indicative of a true logic loop, so they get emphasized in the message. const char *why_pessimistic = nullptr; if (has_feedback_arcs) why_pessimistic = "feedback wires"; else if (has_buffered_wires) why_pessimistic = "buffered combinatorial wires"; - log("\n"); log_warning("Design contains %s, which require delta cycles during evaluation.\n", why_pessimistic); if (!max_opt_level) log("Increasing the optimization level may eliminate %s from the design.\n", why_pessimistic); @@ -2064,7 +2065,7 @@ struct CxxrtlWorker { has_sync_init = has_packed_mem = false; for (auto module : design->modules()) { - if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl.blackbox))) + if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl_blackbox))) continue; if (!design->selected_whole_module(module)) @@ -2086,34 +2087,39 @@ struct CxxrtlWorker { void prepare_design(RTLIL::Design *design) { + bool did_anything = false; bool has_sync_init, has_packed_mem; log_push(); check_design(design, has_sync_init, has_packed_mem); if (run_proc_flatten) { Pass::call(design, "proc"); Pass::call(design, "flatten"); + did_anything = true; } else if (has_sync_init) { // We're only interested in proc_init, but it depends on proc_prune and proc_clean, so call those // in case they weren't already. (This allows `yosys foo.v -o foo.cc` to work.) Pass::call(design, "proc_prune"); Pass::call(design, "proc_clean"); Pass::call(design, "proc_init"); + did_anything = true; } - if (has_packed_mem) + if (has_packed_mem) { Pass::call(design, "memory_unpack"); + did_anything = true; + } // Recheck the design if it was modified. if (has_sync_init || has_packed_mem) check_design(design, has_sync_init, has_packed_mem); log_assert(!(has_sync_init || has_packed_mem)); - if (run_opt_clean_purge) - Pass::call(design, "opt_clean -purge"); log_pop(); + if (did_anything) + log_spacer(); analyze_design(design); } }; struct CxxrtlBackend : public Backend { - static const int DEFAULT_OPT_LEVEL = 6; + static const int DEFAULT_OPT_LEVEL = 5; CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { } void help() YS_OVERRIDE @@ -2155,12 +2161,12 @@ struct CxxrtlBackend : public Backend { log("For example, the following Verilog code defines a CXXRTL black box interface for\n"); log("a synchronous debug sink:\n"); log("\n"); - log(" (* cxxrtl.blackbox *)\n"); + log(" (* cxxrtl_blackbox *)\n"); log(" module debug(...);\n"); - log(" (* cxxrtl.edge = \"p\" *) input clk;\n"); + log(" (* cxxrtl_edge = \"p\" *) input clk;\n"); log(" input en;\n"); log(" input [7:0] i_data;\n"); - log(" (* cxxrtl.sync *) output [7:0] o_data;\n"); + log(" (* cxxrtl_sync *) output [7:0] o_data;\n"); log(" endmodule\n"); log("\n"); log("For this HDL interface, this backend will generate the following C++ interface:\n"); @@ -2205,13 +2211,13 @@ struct CxxrtlBackend : public Backend { log("port widths. For example, the following Verilog code defines a CXXRTL black box\n"); log("interface for a configurable width debug sink:\n"); log("\n"); - log(" (* cxxrtl.blackbox, cxxrtl.template = \"WIDTH\" *)\n"); + log(" (* cxxrtl_blackbox, cxxrtl_template = \"WIDTH\" *)\n"); log(" module debug(...);\n"); log(" parameter WIDTH = 8;\n"); - log(" (* cxxrtl.edge = \"p\" *) input clk;\n"); + log(" (* cxxrtl_edge = \"p\" *) input clk;\n"); log(" input en;\n"); - log(" (* cxxrtl.width = \"WIDTH\" *) input [WIDTH - 1:0] i_data;\n"); - log(" (* cxxrtl.width = \"WIDTH\" *) output [WIDTH - 1:0] o_data;\n"); + log(" (* cxxrtl_width = \"WIDTH\" *) input [WIDTH - 1:0] i_data;\n"); + log(" (* cxxrtl_width = \"WIDTH\" *) output [WIDTH - 1:0] o_data;\n"); log(" endmodule\n"); log("\n"); log("For this parametric HDL interface, this backend will generate the following C++\n"); @@ -2245,27 +2251,27 @@ struct CxxrtlBackend : public Backend { log("\n"); log("The following attributes are recognized by this backend:\n"); log("\n"); - log(" cxxrtl.blackbox\n"); + log(" cxxrtl_blackbox\n"); log(" only valid on modules. if specified, the module contents are ignored,\n"); log(" and the generated code includes only the module interface and a factory\n"); log(" function, which will be called to instantiate the module.\n"); log("\n"); - log(" cxxrtl.edge\n"); + log(" cxxrtl_edge\n"); log(" only valid on inputs of black boxes. must be one of \"p\", \"n\", \"a\".\n"); log(" if specified on signal `clk`, the generated code includes edge detectors\n"); log(" `posedge_p_clk()` (if \"p\"), `negedge_p_clk()` (if \"n\"), or both (if\n"); log(" \"a\"), simplifying implementation of clocked black boxes.\n"); log("\n"); - log(" cxxrtl.template\n"); + log(" cxxrtl_template\n"); log(" only valid on black boxes. must contain a space separated sequence of\n"); log(" identifiers that have a corresponding black box parameters. for each\n"); log(" of them, the generated code includes a `size_t` template parameter.\n"); log("\n"); - log(" cxxrtl.width\n"); + log(" cxxrtl_width\n"); log(" only valid on ports of black boxes. must be a constant expression, which\n"); log(" is directly inserted into generated code.\n"); log("\n"); - log(" cxxrtl.comb, cxxrtl.sync\n"); + log(" cxxrtl_comb, cxxrtl_sync\n"); log(" only valid on outputs of black boxes. if specified, indicates that every\n"); log(" bit of the output port is driven, correspondingly, by combinatorial or\n"); log(" synchronous logic. this knowledge is used for scheduling optimizations.\n"); @@ -2305,10 +2311,7 @@ struct CxxrtlBackend : public Backend { log(" like -O3, and localize public wires not marked (*keep*) if possible.\n"); log("\n"); log(" -O5\n"); - log(" like -O4, and run `opt_clean -purge` first.\n"); - log("\n"); - log(" -O6\n"); - log(" like -O5, and run `proc; flatten` first.\n"); + log(" like -O4, and run `proc; flatten` first.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -2342,19 +2345,23 @@ struct CxxrtlBackend : public Backend { extra_args(f, filename, args, argidx); switch (opt_level) { - case 6: + // the highest level here must match DEFAULT_OPT_LEVEL + case 5: worker.max_opt_level = true; worker.run_proc_flatten = true; - case 5: - worker.run_opt_clean_purge = true; + YS_FALLTHROUGH case 4: worker.localize_public = true; + YS_FALLTHROUGH case 3: worker.elide_public = true; + YS_FALLTHROUGH case 2: worker.localize_internal = true; + YS_FALLTHROUGH case 1: worker.elide_internal = true; + YS_FALLTHROUGH case 0: break; default: |