diff options
33 files changed, 1331 insertions, 562 deletions
@@ -64,6 +64,8 @@ Yosys 0.9 .. Yosys 0.9-dev - Added "opt_lut_ins" pass - Added "logger" pass - Removed "dffsr2dff" (use opt_rmdff instead) + - Added "design -delete" + - Added "select -unset" Yosys 0.8 .. Yosys 0.9 ---------------------- @@ -148,7 +148,10 @@ define newline endef ifneq ($(wildcard Makefile.conf),) +# don't echo Makefile.conf contents when invoked to print source versions +ifeq ($(findstring echo-,$(MAKECMDGOALS)),) $(info $(subst $$--$$,$(newline),$(shell sed 's,^,[Makefile.conf] ,; s,$$,$$--$$,;' < Makefile.conf | tr -d '\n' | sed 's,\$$--\$$$$,,'))) +endif include Makefile.conf endif diff --git a/backends/cxxrtl/cxxrtl.cc b/backends/cxxrtl/cxxrtl.cc index d1a855bf0..ef8335e50 100644 --- a/backends/cxxrtl/cxxrtl.cc +++ b/backends/cxxrtl/cxxrtl.cc @@ -171,14 +171,14 @@ struct Scheduler { } }; -static bool is_unary_cell(RTLIL::IdString type) +bool is_unary_cell(RTLIL::IdString type) { return type.in( ID($not), ID($logic_not), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($pos), ID($neg)); } -static bool is_binary_cell(RTLIL::IdString type) +bool is_binary_cell(RTLIL::IdString type) { return type.in( ID($and), ID($or), ID($xor), ID($xnor), ID($logic_and), ID($logic_or), @@ -187,25 +187,25 @@ static bool is_binary_cell(RTLIL::IdString type) ID($add), ID($sub), ID($mul), ID($div), ID($mod)); } -static bool is_elidable_cell(RTLIL::IdString type) +bool is_elidable_cell(RTLIL::IdString type) { return is_unary_cell(type) || is_binary_cell(type) || type.in( ID($mux), ID($concat), ID($slice)); } -static bool is_sync_ff_cell(RTLIL::IdString type) +bool is_sync_ff_cell(RTLIL::IdString type) { return type.in( ID($dff), ID($dffe)); } -static bool is_ff_cell(RTLIL::IdString type) +bool is_ff_cell(RTLIL::IdString type) { return is_sync_ff_cell(type) || type.in( ID($adff), ID($dffsr), ID($dlatch), ID($dlatchsr), ID($sr)); } -static bool is_internal_cell(RTLIL::IdString type) +bool is_internal_cell(RTLIL::IdString type) { return type[0] == '$' && !type.begins_with("$paramod\\"); } @@ -288,7 +288,7 @@ struct FlowGraph { log_assert(cell->known()); for (auto conn : cell->connections()) { if (cell->output(conn.first)) { - if (is_sync_ff_cell(cell->type) || (cell->type == ID($memrd) && cell->getParam(ID(CLK_ENABLE)).as_bool())) + if (is_sync_ff_cell(cell->type) || (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool())) /* non-combinatorial outputs do not introduce defs */; else if (is_elidable_cell(cell->type)) add_defs(node, conn.second, /*elidable=*/true); @@ -356,6 +356,53 @@ struct FlowGraph { } }; +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)); +} + +std::vector<std::string> split_by(const std::string &str, const std::string &sep) +{ + std::vector<std::string> result; + size_t prev = 0; + while (true) { + size_t curr = str.find_first_of(sep, prev + 1); + if (curr > str.size()) + curr = str.size(); + if (curr > prev + 1) + result.push_back(str.substr(prev, curr - prev)); + if (curr == str.size()) + break; + prev = curr; + } + return result; +} + +std::string escape_cxx_string(const std::string &input) +{ + std::string output = "\""; + for (auto c : input) { + if (::isprint(c)) { + if (c == '\\') + output.push_back('\\'); + output.push_back(c); + } else { + char l = c & 0xf, h = (c >> 4) & 0xf; + output.append("\\x"); + output.push_back((h < 10 ? '0' + h : 'a' + h - 10)); + output.push_back((l < 10 ? '0' + l : 'a' + l - 10)); + } + } + output.push_back('"'); + if (output.find('\0') != std::string::npos) { + output.insert(0, "std::string {"); + output.append(stringf(", %zu}", input.size())); + } + return output; +} + struct CxxrtlWorker { bool split_intf = false; std::string intf_filename; @@ -382,6 +429,7 @@ struct CxxrtlWorker { dict<const RTLIL::Wire*, FlowGraph::Node> elided_wires; dict<const RTLIL::Module*, std::vector<FlowGraph::Node>> schedule; pool<const RTLIL::Wire*> localized_wires; + dict<const RTLIL::Module*, pool<std::string>> blackbox_specializations; void inc_indent() { indent += "\t"; @@ -429,9 +477,11 @@ struct CxxrtlWorker { return mangled; } - std::string mangle_module_name(const RTLIL::IdString &name) + std::string mangle_module_name(const RTLIL::IdString &name, bool is_blackbox = false) { // Class namespace. + if (is_blackbox) + return "bb_" + mangle_name(name); return mangle_name(name); } @@ -455,7 +505,7 @@ struct CxxrtlWorker { std::string mangle(const RTLIL::Module *module) { - return mangle_module_name(module->name); + return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl.blackbox))); } std::string mangle(const RTLIL::Memory *memory) @@ -481,6 +531,80 @@ struct CxxrtlWorker { return mangle(sigbit.wire) + "_" + std::to_string(sigbit.offset); } + std::vector<std::string> template_param_names(const RTLIL::Module *module) + { + 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)); + + 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', " + "which does not start with an uppercase letter.\n", + log_id(module), param_name.c_str()); + } + return param_names; + } + + std::string template_params(const RTLIL::Module *module, bool is_decl) + { + std::vector<std::string> param_names = template_param_names(module); + if (param_names.empty()) + return ""; + + std::string params = "<"; + bool first = true; + for (const auto ¶m_name : param_names) { + if (!first) + params += ", "; + first = false; + if (is_decl) + params += "size_t "; + params += param_name; + } + params += ">"; + return params; + } + + std::string template_args(const RTLIL::Cell *cell) + { + RTLIL::Module *cell_module = cell->module->design->module(cell->type); + log_assert(cell_module != nullptr); + if (!cell_module->get_bool_attribute(ID(cxxrtl.blackbox))) + return ""; + + std::vector<std::string> param_names = template_param_names(cell_module); + if (param_names.empty()) + return ""; + + std::string params = "<"; + bool first = true; + for (const auto ¶m_name : param_names) { + if (!first) + params += ", "; + first = false; + params += "/*" + param_name + "=*/"; + RTLIL::IdString id_param_name = '\\' + param_name; + if (!cell->hasParam(id_param_name)) + log_cmd_error("Cell `%s.%s' does not have a parameter `%s', which is required by the templated module `%s'.\n", + log_id(cell->module), log_id(cell), param_name.c_str(), log_id(cell_module)); + RTLIL::Const param_value = cell->getParam(id_param_name); + if (((param_value.flags & ~RTLIL::CONST_FLAG_SIGNED) != 0) || param_value.as_int() < 0) + log_cmd_error("Parameter `%s' of cell `%s.%s', which is required by the templated module `%s', " + "is not a positive integer.\n", + param_name.c_str(), log_id(cell->module), log_id(cell), log_id(cell_module)); + params += std::to_string(cell->getParam(id_param_name).as_int()); + } + params += ">"; + return params; + } + std::string fresh_temporary() { return stringf("tmp_%d", temporary++); @@ -549,7 +673,8 @@ struct CxxrtlWorker { if (is_elidable_cell(node.cell->type)) { dump_cell_elided(node.cell); } else { - f << mangle(node.cell) << "." << mangle_wire_name(cell_wire_defs[node.cell][chunk.wire]) << ".curr"; + const char *access = is_cxxrtl_blackbox_cell(node.cell) ? "->" : "."; + f << mangle(node.cell) << access << mangle_wire_name(cell_wire_defs[node.cell][chunk.wire]) << ".curr"; } break; default: @@ -660,42 +785,42 @@ struct CxxrtlWorker { // Unary cells if (is_unary_cell(cell->type)) { f << cell->type.substr(1) << '_' << - (cell->getParam(ID(A_SIGNED)).as_bool() ? 's' : 'u') << - "<" << cell->getParam(ID(Y_WIDTH)).as_int() << ">("; - dump_sigspec_rhs(cell->getPort(ID(A))); + (cell->getParam(ID::A_SIGNED).as_bool() ? 's' : 'u') << + "<" << cell->getParam(ID::Y_WIDTH).as_int() << ">("; + dump_sigspec_rhs(cell->getPort(ID::A)); f << ")"; // Binary cells } else if (is_binary_cell(cell->type)) { f << cell->type.substr(1) << '_' << - (cell->getParam(ID(A_SIGNED)).as_bool() ? 's' : 'u') << - (cell->getParam(ID(B_SIGNED)).as_bool() ? 's' : 'u') << - "<" << cell->getParam(ID(Y_WIDTH)).as_int() << ">("; - dump_sigspec_rhs(cell->getPort(ID(A))); + (cell->getParam(ID::A_SIGNED).as_bool() ? 's' : 'u') << + (cell->getParam(ID::B_SIGNED).as_bool() ? 's' : 'u') << + "<" << cell->getParam(ID::Y_WIDTH).as_int() << ">("; + dump_sigspec_rhs(cell->getPort(ID::A)); f << ", "; - dump_sigspec_rhs(cell->getPort(ID(B))); + dump_sigspec_rhs(cell->getPort(ID::B)); f << ")"; // Muxes } else if (cell->type == ID($mux)) { f << "("; - dump_sigspec_rhs(cell->getPort(ID(S))); + dump_sigspec_rhs(cell->getPort(ID::S)); f << " ? "; - dump_sigspec_rhs(cell->getPort(ID(B))); + dump_sigspec_rhs(cell->getPort(ID::B)); f << " : "; - dump_sigspec_rhs(cell->getPort(ID(A))); + dump_sigspec_rhs(cell->getPort(ID::A)); f << ")"; // Concats } else if (cell->type == ID($concat)) { - dump_sigspec_rhs(cell->getPort(ID(B))); + dump_sigspec_rhs(cell->getPort(ID::B)); f << ".concat("; - dump_sigspec_rhs(cell->getPort(ID(A))); + dump_sigspec_rhs(cell->getPort(ID::A)); f << ").val()"; // Slices } else if (cell->type == ID($slice)) { - dump_sigspec_rhs(cell->getPort(ID(A))); + dump_sigspec_rhs(cell->getPort(ID::A)); f << ".slice<"; - f << cell->getParam(ID(OFFSET)).as_int() + cell->getParam(ID(Y_WIDTH)).as_int() - 1; + f << cell->getParam(ID::OFFSET).as_int() + cell->getParam(ID::Y_WIDTH).as_int() - 1; f << ","; - f << cell->getParam(ID(OFFSET)).as_int(); + f << cell->getParam(ID::OFFSET).as_int(); f << ">().val()"; } else { log_assert(false); @@ -704,8 +829,8 @@ struct CxxrtlWorker { bool is_cell_elided(const RTLIL::Cell *cell) { - return is_elidable_cell(cell->type) && cell->hasPort(ID(Y)) && cell->getPort(ID(Y)).is_wire() && - elided_wires.count(cell->getPort(ID(Y)).as_wire()); + return is_elidable_cell(cell->type) && cell->hasPort(ID::Y) && cell->getPort(ID::Y).is_wire() && + elided_wires.count(cell->getPort(ID::Y).as_wire()); } void collect_cell(const RTLIL::Cell *cell, std::vector<RTLIL::IdString> &cells) @@ -715,7 +840,7 @@ struct CxxrtlWorker { cells.push_back(cell->name); for (auto port : cell->connections()) - if (port.first != ID(Y)) + if (port.first != ID::Y) collect_sigspec_rhs(port.second, cells); } @@ -729,7 +854,7 @@ struct CxxrtlWorker { std::vector<RTLIL::IdString> elided_cells; if (is_elidable_cell(cell->type)) { for (auto port : cell->connections()) - if (port.first != ID(Y)) + if (port.first != ID::Y) collect_sigspec_rhs(port.second, elided_cells); } if (elided_cells.empty()) { @@ -745,26 +870,26 @@ struct CxxrtlWorker { // Elidable cells if (is_elidable_cell(cell->type)) { f << indent; - dump_sigspec_lhs(cell->getPort(ID(Y))); + dump_sigspec_lhs(cell->getPort(ID::Y)); f << " = "; dump_cell_elided(cell); f << ";\n"; // Parallel (one-hot) muxes } else if (cell->type == ID($pmux)) { - int width = cell->getParam(ID(WIDTH)).as_int(); - int s_width = cell->getParam(ID(S_WIDTH)).as_int(); + int width = cell->getParam(ID::WIDTH).as_int(); + int s_width = cell->getParam(ID::S_WIDTH).as_int(); bool first = true; for (int part = 0; part < s_width; part++) { f << (first ? indent : " else "); first = false; f << "if ("; - dump_sigspec_rhs(cell->getPort(ID(S)).extract(part)); + dump_sigspec_rhs(cell->getPort(ID::S).extract(part)); f << ") {\n"; inc_indent(); f << indent; - dump_sigspec_lhs(cell->getPort(ID(Y))); + dump_sigspec_lhs(cell->getPort(ID::Y)); f << " = "; - dump_sigspec_rhs(cell->getPort(ID(B)).extract(part * width, width)); + dump_sigspec_rhs(cell->getPort(ID::B).extract(part * width, width)); f << ";\n"; dec_indent(); f << indent << "}"; @@ -772,31 +897,31 @@ struct CxxrtlWorker { f << " else {\n"; inc_indent(); f << indent; - dump_sigspec_lhs(cell->getPort(ID(Y))); + dump_sigspec_lhs(cell->getPort(ID::Y)); f << " = "; - dump_sigspec_rhs(cell->getPort(ID(A))); + dump_sigspec_rhs(cell->getPort(ID::A)); f << ";\n"; dec_indent(); f << indent << "}\n"; // Flip-flops } else if (is_ff_cell(cell->type)) { - if (cell->hasPort(ID(CLK)) && cell->getPort(ID(CLK)).is_wire()) { + if (cell->hasPort(ID::CLK) && cell->getPort(ID::CLK).is_wire()) { // Edge-sensitive logic - RTLIL::SigBit clk_bit = cell->getPort(ID(CLK))[0]; + RTLIL::SigBit clk_bit = cell->getPort(ID::CLK)[0]; clk_bit = sigmaps[clk_bit.wire->module](clk_bit); - f << indent << "if (" << (cell->getParam(ID(CLK_POLARITY)).as_bool() ? "posedge_" : "negedge_") + f << indent << "if (" << (cell->getParam(ID::CLK_POLARITY).as_bool() ? "posedge_" : "negedge_") << mangle(clk_bit) << ") {\n"; inc_indent(); if (cell->type == ID($dffe)) { f << indent << "if ("; - dump_sigspec_rhs(cell->getPort(ID(EN))); - f << " == value<1> {" << cell->getParam(ID(EN_POLARITY)).as_bool() << "u}) {\n"; + dump_sigspec_rhs(cell->getPort(ID::EN)); + f << " == value<1> {" << cell->getParam(ID::EN_POLARITY).as_bool() << "u}) {\n"; inc_indent(); } f << indent; - dump_sigspec_lhs(cell->getPort(ID(Q))); + dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; - dump_sigspec_rhs(cell->getPort(ID(D))); + dump_sigspec_rhs(cell->getPort(ID::D)); f << ";\n"; if (cell->type == ID($dffe)) { dec_indent(); @@ -804,77 +929,77 @@ struct CxxrtlWorker { } dec_indent(); f << indent << "}\n"; - } else if (cell->hasPort(ID(EN))) { + } else if (cell->hasPort(ID::EN)) { // Level-sensitive logic f << indent << "if ("; - dump_sigspec_rhs(cell->getPort(ID(EN))); - f << " == value<1> {" << cell->getParam(ID(EN_POLARITY)).as_bool() << "u}) {\n"; + dump_sigspec_rhs(cell->getPort(ID::EN)); + f << " == value<1> {" << cell->getParam(ID::EN_POLARITY).as_bool() << "u}) {\n"; inc_indent(); f << indent; - dump_sigspec_lhs(cell->getPort(ID(Q))); + dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; - dump_sigspec_rhs(cell->getPort(ID(D))); + dump_sigspec_rhs(cell->getPort(ID::D)); f << ";\n"; dec_indent(); f << indent << "}\n"; } - if (cell->hasPort(ID(ARST))) { + if (cell->hasPort(ID::ARST)) { // Asynchronous reset (entire coarse cell at once) f << indent << "if ("; - dump_sigspec_rhs(cell->getPort(ID(ARST))); - f << " == value<1> {" << cell->getParam(ID(ARST_POLARITY)).as_bool() << "u}) {\n"; + dump_sigspec_rhs(cell->getPort(ID::ARST)); + f << " == value<1> {" << cell->getParam(ID::ARST_POLARITY).as_bool() << "u}) {\n"; inc_indent(); f << indent; - dump_sigspec_lhs(cell->getPort(ID(Q))); + dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; - dump_const(cell->getParam(ID(ARST_VALUE))); + dump_const(cell->getParam(ID::ARST_VALUE)); f << ";\n"; dec_indent(); f << indent << "}\n"; } - if (cell->hasPort(ID(SET))) { + if (cell->hasPort(ID::SET)) { // Asynchronous set (for individual bits) f << indent; - dump_sigspec_lhs(cell->getPort(ID(Q))); + dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; - dump_sigspec_lhs(cell->getPort(ID(Q))); + dump_sigspec_lhs(cell->getPort(ID::Q)); f << ".update("; - dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID(WIDTH)).as_int())); + dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int())); f << ", "; - dump_sigspec_rhs(cell->getPort(ID(SET))); - f << (cell->getParam(ID(SET_POLARITY)).as_bool() ? "" : ".bit_not()") << ");\n"; + dump_sigspec_rhs(cell->getPort(ID::SET)); + f << (cell->getParam(ID::SET_POLARITY).as_bool() ? "" : ".bit_not()") << ");\n"; } - if (cell->hasPort(ID(CLR))) { + if (cell->hasPort(ID::CLR)) { // Asynchronous clear (for individual bits; priority over set) f << indent; - dump_sigspec_lhs(cell->getPort(ID(Q))); + dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; - dump_sigspec_lhs(cell->getPort(ID(Q))); + dump_sigspec_lhs(cell->getPort(ID::Q)); f << ".update("; - dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID(WIDTH)).as_int())); + dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int())); f << ", "; - dump_sigspec_rhs(cell->getPort(ID(CLR))); - f << (cell->getParam(ID(CLR_POLARITY)).as_bool() ? "" : ".bit_not()") << ");\n"; + 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()) { - RTLIL::SigBit clk_bit = cell->getPort(ID(CLK))[0]; + if (cell->getParam(ID::CLK_ENABLE).as_bool()) { + RTLIL::SigBit clk_bit = cell->getPort(ID::CLK)[0]; clk_bit = sigmaps[clk_bit.wire->module](clk_bit); - f << indent << "if (" << (cell->getParam(ID(CLK_POLARITY)).as_bool() ? "posedge_" : "negedge_") + f << indent << "if (" << (cell->getParam(ID::CLK_POLARITY).as_bool() ? "posedge_" : "negedge_") << mangle(clk_bit) << ") {\n"; inc_indent(); } - RTLIL::Memory *memory = cell->module->memories[cell->getParam(ID(MEMID)).decode_string()]; + 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))); + dump_sigspec_rhs(cell->getPort(ID::ADDR)); 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(); + 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))); + dump_sigspec_rhs(cell->getPort(ID::EN)); f << ") {\n"; inc_indent(); } @@ -890,8 +1015,8 @@ struct CxxrtlWorker { inc_indent(); if (writable_memories[memory]) { 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 << indent << "const value<" << cell->getPort(ID::ADDR).size() << "> &" << addr_temp << " = "; + dump_sigspec_rhs(cell->getPort(ID::ADDR)); f << ";\n"; std::string lhs_temp = fresh_temporary(); f << indent << "value<" << memory->width << "> " << lhs_temp << " = " @@ -899,35 +1024,35 @@ struct CxxrtlWorker { std::vector<const RTLIL::Cell*> memwr_cells(transparent_for[cell].begin(), transparent_for[cell].end()); 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(); + 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))); + 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))); + dump_sigspec_rhs(memwr_cell->getPort(ID::DATA)); f << ", "; - dump_sigspec_rhs(memwr_cell->getPort(ID(EN))); + dump_sigspec_rhs(memwr_cell->getPort(ID::EN)); f << ");\n"; dec_indent(); f << indent << "}\n"; } f << indent; - dump_sigspec_lhs(cell->getPort(ID(DATA))); + dump_sigspec_lhs(cell->getPort(ID::DATA)); f << " = " << lhs_temp << ";\n"; } else { f << indent; - dump_sigspec_lhs(cell->getPort(ID(DATA))); + 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))); + dump_sigspec_lhs(cell->getPort(ID::DATA)); f << " = value<" << memory->width << "> {};\n"; dec_indent(); f << indent << "}\n"; @@ -944,14 +1069,14 @@ struct CxxrtlWorker { 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))); + dump_sigspec_rhs(cell->getPort(ID::DATA)); f << ", "; - dump_sigspec_rhs(cell->getPort(ID(EN))); - f << ", " << cell->getParam(ID(PRIORITY)).as_int() << ");\n"; + 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()) { + if (cell->getParam(ID::CLK_ENABLE).as_bool()) { dec_indent(); f << indent << "}\n"; } @@ -961,13 +1086,14 @@ struct CxxrtlWorker { // User cells } else { log_assert(cell->known()); + const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : "."; for (auto conn : cell->connections()) if (cell->input(conn.first)) { - f << indent << mangle(cell) << "." << mangle_wire_name(conn.first) << ".next = "; + f << indent << mangle(cell) << access << mangle_wire_name(conn.first) << ".next = "; dump_sigspec_rhs(conn.second); f << ";\n"; } - f << indent << mangle(cell) << ".eval();\n"; + f << indent << mangle(cell) << access << "eval();\n"; for (auto conn : cell->connections()) { if (conn.second.is_wire()) { RTLIL::Wire *wire = conn.second.as_wire(); @@ -979,7 +1105,7 @@ struct CxxrtlWorker { continue; // ignore disconnected ports f << indent; dump_sigspec_lhs(conn.second); - f << " = " << mangle(cell) << "." << mangle_wire_name(conn.first) << ".curr;\n"; + f << " = " << mangle(cell) << access << mangle_wire_name(conn.first) << ".curr;\n"; } } } @@ -1076,24 +1202,34 @@ struct CxxrtlWorker { log_assert(proc->root_case.attributes.empty()); dump_case_rule(&proc->root_case); for (auto sync : proc->syncs) { - RTLIL::SigBit sync_bit = sync->signal[0]; - sync_bit = sigmaps[sync_bit.wire->module](sync_bit); + RTLIL::SigBit sync_bit; + if (!sync->signal.empty()) { + sync_bit = sync->signal[0]; + sync_bit = sigmaps[sync_bit.wire->module](sync_bit); + } pool<std::string> events; switch (sync->type) { case RTLIL::STp: + log_assert(sync_bit.wire != nullptr); events.insert("posedge_" + mangle(sync_bit)); break; case RTLIL::STn: + log_assert(sync_bit.wire != nullptr); events.insert("negedge_" + mangle(sync_bit)); + break; case RTLIL::STe: + log_assert(sync_bit.wire != nullptr); events.insert("posedge_" + mangle(sync_bit)); events.insert("negedge_" + mangle(sync_bit)); break; + case RTLIL::STa: + events.insert("true"); + break; + case RTLIL::ST0: case RTLIL::ST1: - case RTLIL::STa: case RTLIL::STg: case RTLIL::STi: log_assert(false); @@ -1132,11 +1268,18 @@ struct CxxrtlWorker { if (localized_wires.count(wire)) return; + 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)); + } else { + width = std::to_string(wire->width); + } + dump_attrs(wire); - f << indent << "wire<" << wire->width << "> " << mangle(wire); - if (wire->attributes.count(ID(init))) { + f << indent << "wire<" << width << "> " << mangle(wire); + if (wire->has_attribute(ID::init)) { f << " "; - dump_const_init(wire->attributes.at(ID(init))); + dump_const_init(wire->attributes.at(ID::init)); } f << ";\n"; if (sync_wires[wire]) { @@ -1156,18 +1299,17 @@ struct CxxrtlWorker { { vector<const RTLIL::Cell*> init_cells; for (auto cell : module->cells()) - if (cell->type == ID($meminit) && cell->getParam(ID(MEMID)).decode_string() == memory->name.str()) + 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(); + 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 << (writable_memories[memory] ? "" : "const ") - << "memory<" << memory->width << "> " << mangle(memory) + f << indent << "memory<" << memory->width << "> " << mangle(memory) << " { " << memory->size << "u"; if (init_cells.empty()) { f << " };\n"; @@ -1176,11 +1318,11 @@ struct CxxrtlWorker { 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(); + 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()) << ", {"; + << stringf("%#x", cell->getPort(ID::ADDR).as_int()) << ", {"; inc_indent(); for (size_t n = 0; n < words; n++) { if (n % 4 == 0) @@ -1198,54 +1340,24 @@ struct CxxrtlWorker { } } - void dump_module_intf(RTLIL::Module *module) + void dump_eval_method(RTLIL::Module *module) { - dump_attrs(module); - f << "struct " << mangle(module) << " : public module {\n"; inc_indent(); - for (auto wire : module->wires()) - dump_wire(wire, /*is_local=*/false); - f << "\n"; - bool has_memories = false; - for (auto memory : module->memories) { - dump_memory(module, memory.second); - has_memories = true; - } - if (has_memories) - f << "\n"; - bool has_cells = false; - for (auto cell : module->cells()) { - if (is_internal_cell(cell->type)) - continue; - f << indent << mangle_module_name(cell->type) << " " << mangle(cell) << ";\n"; - has_cells = true; - } - if (has_cells) - f << "\n"; - f << indent << "void eval() override;\n"; - f << indent << "bool commit() override;\n"; - dec_indent(); - f << "}; // struct " << mangle(module) << "\n"; - f << "\n"; - } - - void dump_module_impl(RTLIL::Module *module) - { - f << "void " << mangle(module) << "::eval() {\n"; - inc_indent(); - for (auto wire : module->wires()) - dump_wire(wire, /*is_local=*/true); - for (auto node : schedule[module]) { - switch (node.type) { - case FlowGraph::Node::Type::CONNECT: - dump_connect(node.connect); - break; - case FlowGraph::Node::Type::CELL: - dump_cell(node.cell); - break; - case FlowGraph::Node::Type::PROCESS: - dump_process(node.process); - break; + if (!module->get_bool_attribute(ID(cxxrtl.blackbox))) { + for (auto wire : module->wires()) + dump_wire(wire, /*is_local=*/true); + for (auto node : schedule[module]) { + switch (node.type) { + case FlowGraph::Node::Type::CONNECT: + dump_connect(node.connect); + break; + case FlowGraph::Node::Type::CELL: + dump_cell(node.cell); + break; + case FlowGraph::Node::Type::PROCESS: + dump_process(node.process); + break; + } } } for (auto sync_type : sync_types) { @@ -1257,10 +1369,10 @@ struct CxxrtlWorker { } } dec_indent(); - f << "}\n"; - f << "\n"; + } - f << "bool " << mangle(module) << "::commit() {\n"; + void dump_commit_method(RTLIL::Module *module) + { inc_indent(); f << indent << "bool changed = false;\n"; for (auto wire : module->wires()) { @@ -1296,42 +1408,180 @@ struct CxxrtlWorker { } dec_indent(); f << indent << "}\n"; - } else { + } else if (!module->get_bool_attribute(ID(cxxrtl.blackbox)) || wire->port_id != 0) { f << indent << "changed |= " << mangle(wire) << ".commit();\n"; } } - for (auto memory : module->memories) { - if (!writable_memories[memory.second]) - continue; - f << indent << "changed |= " << mangle(memory.second) << ".commit();\n"; + if (!module->get_bool_attribute(ID(cxxrtl.blackbox))) { + for (auto memory : module->memories) { + if (!writable_memories[memory.second]) + continue; + f << indent << "changed |= " << mangle(memory.second) << ".commit();\n"; + } + for (auto cell : module->cells()) { + if (is_internal_cell(cell->type)) + continue; + const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : "."; + f << indent << "changed |= " << mangle(cell) << access << "commit();\n"; + } } - for (auto cell : module->cells()) { - if (is_internal_cell(cell->type)) + f << indent << "return changed;\n"; + dec_indent(); + } + + void dump_metadata_map(const dict<RTLIL::IdString, RTLIL::Const> &metadata_map) + { + if (metadata_map.empty()) { + f << "metadata_map()"; + return; + } + f << "metadata_map({\n"; + inc_indent(); + for (auto metadata_item : metadata_map) { + if (!metadata_item.first.begins_with("\\")) continue; - f << indent << "changed |= " << mangle(cell) << ".commit();\n"; + f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", "; + if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) { + f << std::showpoint << std::stod(metadata_item.second.decode_string()) << std::noshowpoint; + } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) { + f << escape_cxx_string(metadata_item.second.decode_string()); + } else { + f << metadata_item.second.as_int(/*is_signed=*/metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED); + if (!(metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED)) + f << "u"; + } + f << " },\n"; } - f << indent << "return changed;\n"; dec_indent(); - f << "}\n"; + f << indent << "})"; + } + + 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))) + f << indent << "template" << template_params(module, /*is_decl=*/true) << "\n"; + f << indent << "struct " << mangle(module) << " : public module {\n"; + inc_indent(); + for (auto wire : module->wires()) { + if (wire->port_id != 0) + dump_wire(wire, /*is_local=*/false); + } + f << "\n"; + f << indent << "void eval() override {\n"; + dump_eval_method(module); + f << indent << "}\n"; + f << "\n"; + f << indent << "bool commit() override {\n"; + dump_commit_method(module); + f << indent << "}\n"; + f << "\n"; + f << indent << "static std::unique_ptr<" << mangle(module); + f << template_params(module, /*is_decl=*/false) << "> "; + f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n"; + dec_indent(); + f << indent << "}; // struct " << mangle(module) << "\n"; + f << "\n"; + if (blackbox_specializations.count(module)) { + // If templated black boxes are used, the constructor of any module which includes the black box cell + // (which calls the declared but not defined in the generated code `create` function) may only be used + // if (a) the create function is defined in the same translation unit, or (b) the create function has + // a forward-declared explicit specialization. + // + // Option (b) makes it possible to have the generated code and the black box implementation in different + // translation units, which is convenient. Of course, its downside is that black boxes must predefine + // a specialization for every combination of parameters the generated code may use; but since the main + // purpose of templated black boxes is abstracting over datapath width, it is expected that there would + // be very few such combinations anyway. + for (auto specialization : blackbox_specializations[module]) { + f << indent << "template<>\n"; + f << indent << "std::unique_ptr<" << mangle(module) << specialization << "> "; + f << mangle(module) << specialization << "::"; + f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n"; + f << "\n"; + } + } + } else { + f << indent << "struct " << mangle(module) << " : public module {\n"; + inc_indent(); + for (auto wire : module->wires()) + dump_wire(wire, /*is_local=*/false); + f << "\n"; + bool has_memories = false; + for (auto memory : module->memories) { + dump_memory(module, memory.second); + has_memories = true; + } + if (has_memories) + f << "\n"; + bool has_cells = false; + for (auto cell : module->cells()) { + if (is_internal_cell(cell->type)) + continue; + 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))) { + 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()) << ", "; + dump_metadata_map(cell->parameters); + f << ", "; + dump_metadata_map(cell->attributes); + f << ");\n"; + } else { + f << indent << mangle(cell_module) << " " << mangle(cell) << ";\n"; + } + has_cells = true; + } + if (has_cells) + f << "\n"; + f << indent << "void eval() override;\n"; + f << indent << "bool commit() override;\n"; + dec_indent(); + f << indent << "}; // struct " << mangle(module) << "\n"; + f << "\n"; + } + } + + void dump_module_impl(RTLIL::Module *module) + { + if (module->get_bool_attribute(ID(cxxrtl.blackbox))) + return; + f << indent << "void " << mangle(module) << "::eval() {\n"; + dump_eval_method(module); + f << indent << "}\n"; + f << "\n"; + f << indent << "bool " << mangle(module) << "::commit() {\n"; + dump_commit_method(module); + f << indent << "}\n"; f << "\n"; } void dump_design(RTLIL::Design *design) { + std::vector<RTLIL::Module*> modules; TopoSort<RTLIL::Module*> topo_design; for (auto module : design->modules()) { - if (module->get_blackbox_attribute() || !design->selected_module(module)) + if (!design->selected_module(module)) + continue; + 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))) continue; - topo_design.node(module); + topo_design.node(module); for (auto cell : module->cells()) { - if (is_internal_cell(cell->type)) + if (is_internal_cell(cell->type) || is_cxxrtl_blackbox_cell(cell)) continue; - log_assert(design->has(cell->type)); - topo_design.edge(design->module(cell->type), module); + RTLIL::Module *cell_module = design->module(cell->type); + log_assert(cell_module != nullptr); + topo_design.edge(cell_module, module); } } log_assert(topo_design.sort()); + modules.insert(modules.end(), topo_design.sorted.begin(), topo_design.sorted.end()); if (split_intf) { // The only thing more depraved than include guards, is mangling filenames to turn them into include guards. @@ -1347,11 +1597,8 @@ struct CxxrtlWorker { f << "\n"; f << "namespace " << design_ns << " {\n"; f << "\n"; - for (auto module : topo_design.sorted) { - if (!design->selected_module(module)) - continue; + for (auto module : modules) dump_module_intf(module); - } f << "} // namespace " << design_ns << "\n"; f << "\n"; f << "#endif\n"; @@ -1367,9 +1614,7 @@ struct CxxrtlWorker { f << "\n"; f << "namespace " << design_ns << " {\n"; f << "\n"; - for (auto module : topo_design.sorted) { - if (!design->selected_module(module)) - continue; + for (auto module : modules) { if (!split_intf) dump_module_intf(module); dump_module_impl(module); @@ -1407,10 +1652,39 @@ struct CxxrtlWorker { if (!design->selected_module(module)) continue; - FlowGraph flow; SigMap &sigmap = sigmaps[module]; sigmap.set(module); + 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 (!(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_id(module), log_signal(wire)); + + 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]) { + case '-': break; + case 'p': register_edge_signal(sigmap, wire_sig[i], RTLIL::STp); break; + 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 " + "other than '-', 'p', 'n', or 'a'.\n", + log_id(module), log_signal(wire)); + } + } + } + } + continue; + } + + FlowGraph flow; + for (auto conn : module->connections()) flow.add_node(conn); @@ -1418,32 +1692,44 @@ struct CxxrtlWorker { dict<std::pair<RTLIL::SigBit, const RTLIL::Memory*>, pool<const RTLIL::Cell*>> memwr_per_domain; for (auto cell : module->cells()) { + if (!cell->known()) + log_cmd_error("Unknown cell `%s'.\n", log_id(cell->type)); + + RTLIL::Module *cell_module = design->module(cell->type); + if (cell_module && + cell_module->get_blackbox_attribute() && + !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))) + blackbox_specializations[cell_module].insert(template_args(cell)); + FlowGraph::Node *node = 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($dffsr))) { - if (cell->getPort(ID(CLK)).is_wire()) - register_edge_signal(sigmap, cell->getPort(ID(CLK)), - cell->parameters[ID(CLK_POLARITY)].as_bool() ? RTLIL::STp : RTLIL::STn); - // The $adff and $dffsr cells are level-sensitive, not edge-sensitive (in spite of the fact that they - // are inferred from an edge-sensitive Verilog process) and do not correspond to an edge-type sync rule. + if (cell->getPort(ID::CLK).is_wire()) + 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 (cell->getPort(ID(CLK)).is_wire()) - register_edge_signal(sigmap, cell->getPort(ID(CLK)), - cell->parameters[ID(CLK_POLARITY)].as_bool() ? RTLIL::STp : RTLIL::STn); + if (cell->getParam(ID::CLK_ENABLE).as_bool()) { + if (cell->getPort(ID::CLK).is_wire()) + 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()]); + 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() && cell->getPort(ID(CLK)).is_wire()) { - RTLIL::SigBit clk_bit = sigmap(cell->getPort(ID(CLK)))[0]; - const RTLIL::Memory *memory = module->memories[cell->getParam(ID(MEMID)).decode_string()]; + if (cell->type == ID($memwr) && cell->getParam(ID::CLK_ENABLE).as_bool() && cell->getPort(ID::CLK).is_wire()) { + 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 @@ -1453,17 +1739,17 @@ struct CxxrtlWorker { } 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() && cell->getPort(ID(CLK)).is_wire() && - 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()]; + if (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool() && cell->getPort(ID::CLK).is_wire() && + 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))); + 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)); } } } @@ -1499,7 +1785,7 @@ struct CxxrtlWorker { for (auto wire : module->wires()) { if (!flow.is_elidable(wire)) continue; if (wire->port_id != 0) continue; - if (wire->get_bool_attribute(ID(keep))) continue; + if (wire->get_bool_attribute(ID::keep)) continue; if (wire->name.begins_with("$") && !elide_internal) continue; if (wire->name.begins_with("\\") && !elide_public) continue; if (sync_wires[wire]) continue; @@ -1557,7 +1843,7 @@ struct CxxrtlWorker { if (!feedback_wires.empty()) { has_feedback_arcs = true; - log("Module `%s` contains feedback arcs through wires:\n", module->name.c_str()); + log("Module `%s' contains feedback arcs through wires:\n", module->name.c_str()); for (auto wire : feedback_wires) { log(" %s\n", wire->name.c_str()); } @@ -1566,7 +1852,7 @@ struct CxxrtlWorker { for (auto wire : module->wires()) { if (feedback_wires[wire]) continue; if (wire->port_id != 0) continue; - if (wire->get_bool_attribute(ID(keep))) continue; + if (wire->get_bool_attribute(ID::keep)) continue; if (wire->name.begins_with("$") && !localize_internal) continue; if (wire->name.begins_with("\\") && !localize_public) continue; if (sync_wires[wire]) continue; @@ -1585,12 +1871,12 @@ struct CxxrtlWorker { has_sync_init = has_packed_mem = false; for (auto module : design->modules()) { - if (module->get_blackbox_attribute()) + if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl.blackbox))) continue; if (!design->selected_whole_module(module)) if (design->selected_module(module)) - log_cmd_error("Can't handle partially selected module `%s`!\n", id2cstr(module->name)); + log_cmd_error("Can't handle partially selected module `%s'!\n", id2cstr(module->name)); if (!design->selected_module(module)) continue; @@ -1642,21 +1928,145 @@ struct CxxrtlBackend : public Backend { log("\n"); log(" write_cxxrtl [options] [filename]\n"); log("\n"); - log("Write C++ code for simulating the design. The generated code requires a driver;\n"); - log("the following simple driver is provided as an example:\n"); + log("Write C++ code that simulates the design. The generated code requires a driver\n"); + log("that instantiates the design, toggles its clock, and interacts with its ports.\n"); + log("\n"); + log("The following driver may be used as an example for a design with a single clock\n"); + log("driving rising edge triggered flip-flops:\n"); log("\n"); log(" #include \"top.cc\"\n"); log("\n"); log(" int main() {\n"); log(" cxxrtl_design::p_top top;\n"); + log(" top.step();\n"); log(" while (1) {\n"); - log(" top.p_clk.next = value<1> {1u};\n"); - log(" top.step();\n"); + log(" /* user logic */\n"); log(" top.p_clk.next = value<1> {0u};\n"); log(" top.step();\n"); + log(" top.p_clk.next = value<1> {1u};\n"); + log(" top.step();\n"); + log(" }\n"); + log(" }\n"); + log("\n"); + log("Note that CXXRTL simulations, just like the hardware they are simulating, are\n"); + log("subject to race conditions. If, in the example above, the user logic would run\n"); + log("simultaneously with the rising edge of the clock, the design would malfunction.\n"); + log("\n"); + log("This backend supports replacing parts of the design with black boxes implemented\n"); + log("in C++. If a module marked as a CXXRTL black box, its implementation is ignored,\n"); + log("and the generated code consists only of an interface and a factory function.\n"); + log("The driver must implement the factory function that creates an implementation of\n"); + log("the black box, taking into account the parameters it is instantiated with.\n"); + log("\n"); + 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(" module debug(...);\n"); + log(" (* cxxrtl.edge = \"p\" *) input clk;\n"); + log(" input en;\n"); + log(" input [7:0] data;\n"); + log(" endmodule\n"); + log("\n"); + log("For this HDL interface, this backend will generate the following C++ interface:\n"); + log("\n"); + log(" struct bb_p_debug : public module {\n"); + log(" wire<1> p_clk;\n"); + log(" bool posedge_p_clk = false;\n"); + log(" wire<1> p_en;\n"); + log(" wire<8> p_data;\n"); + log("\n"); + log(" void eval() override;\n"); + log(" bool commit() override;\n"); + log("\n"); + log(" static std::unique_ptr<bb_p_debug>\n"); + log(" create(std::string name, metadata_map parameters, metadata_map attributes);\n"); + log(" };\n"); + log("\n"); + log("The `create' function must be implemented by the driver. For example, it could\n"); + log("always provide an implementation logging the values to standard error stream:\n"); + log("\n"); + log(" namespace cxxrtl_design {\n"); + log("\n"); + log(" struct stderr_debug : public bb_p_debug {\n"); + log(" void eval() override {\n"); + log(" if (posedge_p_clk && p_en.curr)\n"); + log(" fprintf(stderr, \"debug: %%02x\\n\", p_data.curr.data[0]);\n"); + log(" bb_p_debug::eval();\n"); log(" }\n"); + log(" };\n"); + log("\n"); + log(" std::unique_ptr<bb_p_debug>\n"); + log(" bb_p_debug::create(std::string name, cxxrtl::metadata_map parameters,\n"); + log(" cxxrtl::metadata_map attributes) {\n"); + log(" return std::make_unique<stderr_debug>();\n"); + log(" }\n"); + log("\n"); log(" }\n"); log("\n"); + log("For complex applications of black boxes, it is possible to parameterize their\n"); + 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(" module debug(...);\n"); + log(" parameter WIDTH = 8;\n"); + log(" (* cxxrtl.edge = \"p\" *) input clk;\n"); + log(" input en;\n"); + log(" (* cxxrtl.width = \"WIDTH\" *) input [WIDTH - 1:0] data;\n"); + log(" endmodule\n"); + log("\n"); + log("For this parametric HDL interface, this backend will generate the following C++\n"); + log("interface (only the differences are shown):\n"); + log("\n"); + log(" template<size_t WIDTH>\n"); + log(" struct bb_p_debug : public module {\n"); + log(" // ...\n"); + log(" wire<WIDTH> p_data;\n"); + log(" // ...\n"); + log(" static std::unique_ptr<bb_p_debug<WIDTH>>\n"); + log(" create(std::string name, metadata_map parameters, metadata_map attributes);\n"); + log(" };\n"); + log("\n"); + log("The `create' function must be implemented by the driver, specialized for every\n"); + log("possible combination of template parameters. (Specialization is necessary to\n"); + log("enable separate compilation of generated code and black box implementations.)\n"); + log("\n"); + log(" template<size_t SIZE>\n"); + log(" struct stderr_debug : public bb_p_debug<SIZE> {\n"); + log(" // ...\n"); + log(" };\n"); + log("\n"); + log(" template<>\n"); + log(" std::unique_ptr<bb_p_debug<8>>\n"); + log(" bb_p_debug<8>::create(std::string name, cxxrtl::metadata_map parameters,\n"); + log(" cxxrtl::metadata_map attributes) {\n"); + log(" return std::make_unique<stderr_debug<8>>();\n"); + log(" }\n"); + log("\n"); + log("The following attributes are recognized by this backend:\n"); + log("\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(" 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 boolean fields\n"); + log(" `posedge_p_clk` (if \"p\"), `negedge_p_clk` (if \"n\"), or both (if \"a\"),\n"); + log(" as well as edge detection logic, simplifying implementation of clocked\n"); + log(" black boxes.\n"); + log("\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(" 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("The following options are supported by this backend:\n"); log("\n"); log(" -header\n"); diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h index 593c31c28..41e6290d1 100644 --- a/backends/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/cxxrtl.h @@ -28,7 +28,9 @@ #include <type_traits> #include <tuple> #include <vector> +#include <map> #include <algorithm> +#include <memory> #include <sstream> // The cxxrtl support library implements compile time specialized arbitrary width arithmetics, as well as provides @@ -604,12 +606,15 @@ struct memory { auto _ = {std::move(std::begin(init.data), std::end(init.data), data.begin() + init.offset)...}; } - value<Width> &operator [](size_t index) { + // An operator for direct memory reads. May be used at any time during the simulation. + const value<Width> &operator [](size_t index) const { assert(index < data.size()); return data[index]; } - const value<Width> &operator [](size_t index) const { + // An operator for direct memory writes. May only be used before the simulation is started. If used + // after the simulation is started, the design may malfunction. + value<Width> &operator [](size_t index) { assert(index < data.size()); return data[index]; } @@ -654,6 +659,57 @@ struct memory { } }; +struct metadata { + const enum { + MISSING = 0, + UINT = 1, + SINT = 2, + STRING = 3, + DOUBLE = 4, + } value_type; + + // In debug mode, using the wrong .as_*() function will assert. + // In release mode, using the wrong .as_*() function will safely return a default value. + union { + const unsigned uint_value = 0; + const signed sint_value; + }; + const std::string string_value = ""; + const double double_value = 0.0; + + metadata() : value_type(MISSING) {} + metadata(unsigned value) : value_type(UINT), uint_value(value) {} + metadata(signed value) : value_type(SINT), sint_value(value) {} + metadata(const std::string &value) : value_type(STRING), string_value(value) {} + metadata(const char *value) : value_type(STRING), string_value(value) {} + metadata(double value) : value_type(DOUBLE), double_value(value) {} + + metadata(const metadata &) = default; + metadata &operator=(const metadata &) = delete; + + unsigned as_uint() const { + assert(value_type == UINT); + return uint_value; + } + + signed as_sint() const { + assert(value_type == SINT); + return sint_value; + } + + const std::string &as_string() const { + assert(value_type == STRING); + return string_value; + } + + double as_double() const { + assert(value_type == DOUBLE); + return double_value; + } +}; + +typedef std::map<std::string, metadata> metadata_map; + struct module { module() {} virtual ~module() {} diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index 1f750b359..40d05a036 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -42,6 +42,13 @@ static const FDirection FD_OUT = 0x2; static const FDirection FD_INOUT = 0x3; static const int FIRRTL_MAX_DSH_WIDTH_ERROR = 20; // For historic reasons, this is actually one greater than the maximum allowed shift width +std::string getFileinfo(const RTLIL::AttrObject *design_entity) +{ + std::string src(design_entity->get_src_attribute()); + std::string fileinfo_str = src.empty() ? "" : "@[" + src + "]"; + return fileinfo_str; +} + // Get a port direction with respect to a specific module. FDirection getPortFDirection(IdString id, Module *module) { @@ -192,9 +199,10 @@ struct FirrtlWorker if (this->width == 0) { log_error("Memory %s has zero width%s\n", this->name.c_str(), this->atLine()); } - } + } + // We need a default constructor for the dict insert. - memory() : pCell(0), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){} + memory() : pCell(0), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){} const char *atLine() { if (srcLine == "") { @@ -298,17 +306,8 @@ struct FirrtlWorker // If this is a parameterized module, its parent module is encoded in the cell type if (cell->type.begins_with("$paramod")) { - std::string::iterator it; - for (it = cell_type.begin(); it < cell_type.end(); it++) - { - switch (*it) { - case '\\': /* FALL_THROUGH */ - case '=': /* FALL_THROUGH */ - case '\'': /* FALL_THROUGH */ - case '$': instanceOf.append("_"); break; - default: instanceOf.append(1, *it); break; - } - } + log_assert(cell->has_attribute(ID::hdlname)); + instanceOf = cell->get_string_attribute(ID::hdlname); } else { @@ -329,7 +328,8 @@ struct FirrtlWorker log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str()); return; } - wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceOf.c_str())); + std::string cellFileinfo = getFileinfo(cell); + wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceOf.c_str(), cellFileinfo.c_str())); for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { if (it->second.size() > 0) { @@ -370,7 +370,7 @@ struct FirrtlWorker // as part of the coalesced subfield assignments for this wire. register_reverse_wire_map(sourceExpr, *sinkSig); } else { - wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sinkExpr.c_str(), sourceExpr.c_str())); + wire_exprs.push_back(stringf("\n%s%s <= %s %s", indent.c_str(), sinkExpr.c_str(), sourceExpr.c_str(), cellFileinfo.c_str())); } } } @@ -394,12 +394,15 @@ struct FirrtlWorker void run() { - f << stringf(" module %s:\n", make_id(module->name)); + std::string moduleFileinfo = getFileinfo(module); + f << stringf(" module %s: %s\n", make_id(module->name), moduleFileinfo.c_str()); vector<string> port_decls, wire_decls, cell_exprs, wire_exprs; for (auto wire : module->wires()) { const auto wireName = make_id(wire->name); + std::string wireFileinfo = getFileinfo(wire); + // If a wire has initial data, issue a warning since FIRRTL doesn't currently support it. if (wire->attributes.count(ID::init)) { log_warning("Initial value (%s) for (%s.%s) not supported\n", @@ -410,12 +413,12 @@ struct FirrtlWorker { if (wire->port_input && wire->port_output) log_error("Module port %s.%s is inout!\n", log_id(module), log_id(wire)); - port_decls.push_back(stringf(" %s %s: UInt<%d>\n", wire->port_input ? "input" : "output", - wireName, wire->width)); + port_decls.push_back(stringf(" %s %s: UInt<%d> %s\n", wire->port_input ? "input" : "output", + wireName, wire->width, wireFileinfo.c_str())); } else { - wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", wireName, wire->width)); + wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", wireName, wire->width, wireFileinfo.c_str())); } } @@ -423,7 +426,7 @@ struct FirrtlWorker { static Const ndef(0, 0); - // Is this cell is a module instance? + // Is this cell is a module instance? if (cell->type[0] != '$') { process_instance(cell, wire_exprs); @@ -441,11 +444,12 @@ struct FirrtlWorker string primop; bool always_uint = false; string y_id = make_id(cell->name); + std::string cellFileinfo = getFileinfo(cell); if (cell->type.in(ID($not), ID($logic_not), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor))) { string a_expr = make_expr(cell->getPort(ID::A)); - wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); + wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", y_id.c_str(), y_width, cellFileinfo.c_str())); if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; @@ -464,16 +468,16 @@ struct FirrtlWorker firrtl_is_signed = true; // Result of "neg" is signed (an SInt). firrtl_width = a_width; } else if (cell->type == ID($logic_not)) { - primop = "eq"; - a_expr = stringf("%s, UInt(0)", a_expr.c_str()); - } + primop = "eq"; + a_expr = stringf("%s, UInt(0)", a_expr.c_str()); + } else if (cell->type == ID($reduce_and)) primop = "andr"; else if (cell->type == ID($reduce_or)) primop = "orr"; else if (cell->type == ID($reduce_xor)) primop = "xorr"; else if (cell->type == ID($reduce_xnor)) { - primop = "not"; - a_expr = stringf("xorr(%s)", a_expr.c_str()); - } + primop = "not"; + a_expr = stringf("xorr(%s)", a_expr.c_str()); + } else if (cell->type == ID($reduce_bool)) { primop = "neq"; // Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand. @@ -485,18 +489,19 @@ struct FirrtlWorker if ((firrtl_is_signed && !always_uint)) expr = stringf("asUInt(%s)", expr.c_str()); - cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($xnor), ID($and), ID($or), ID($eq), ID($eqx), - ID($gt), ID($ge), ID($lt), ID($le), ID($ne), ID($nex), ID($shr), ID($sshr), ID($sshl), ID($shl), - ID($logic_and), ID($logic_or), ID($pow))) + ID($gt), ID($ge), ID($lt), ID($le), ID($ne), ID($nex), ID($shr), ID($sshr), ID($sshl), ID($shl), + ID($logic_and), ID($logic_or), ID($pow))) { string a_expr = make_expr(cell->getPort(ID::A)); string b_expr = make_expr(cell->getPort(ID::B)); - wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); + std::string cellFileinfo = getFileinfo(cell); + wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", y_id.c_str(), y_width, cellFileinfo.c_str())); if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; @@ -579,7 +584,7 @@ struct FirrtlWorker primop = "eq"; always_uint = true; firrtl_width = 1; - } + } else if ((cell->type == ID($ne)) | (cell->type == ID($nex))) { primop = "neq"; always_uint = true; @@ -712,7 +717,7 @@ struct FirrtlWorker if ((firrtl_is_signed && !always_uint)) expr = stringf("asUInt(%s)", expr.c_str()); - cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -724,11 +729,11 @@ struct FirrtlWorker string a_expr = make_expr(cell->getPort(ID::A)); string b_expr = make_expr(cell->getPort(ID::B)); string s_expr = make_expr(cell->getPort(ID::S)); - wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), width)); + wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", y_id.c_str(), width, cellFileinfo.c_str())); string expr = stringf("mux(%s, %s, %s)", s_expr.c_str(), b_expr.c_str(), a_expr.c_str()); - cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -867,9 +872,9 @@ struct FirrtlWorker string expr = make_expr(cell->getPort(ID::D)); string clk_expr = "asClock(" + make_expr(cell->getPort(ID::CLK)) + ")"; - wire_decls.push_back(stringf(" reg %s: UInt<%d>, %s\n", y_id.c_str(), width, clk_expr.c_str())); + wire_decls.push_back(stringf(" reg %s: UInt<%d>, %s %s\n", y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str())); - cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Q)); continue; @@ -959,6 +964,7 @@ struct FirrtlWorker for (auto wire : module->wires()) { string expr; + std::string wireFileinfo = getFileinfo(wire); if (wire->port_input) continue; @@ -1017,14 +1023,20 @@ struct FirrtlWorker if (is_valid) { if (make_unconn_id) { - wire_decls.push_back(stringf(" wire %s: UInt<1>\n", unconn_id.c_str())); + wire_decls.push_back(stringf(" wire %s: UInt<1> %s\n", unconn_id.c_str(), wireFileinfo.c_str())); + // `invalid` is a firrtl construction for simulation so we will not + // tag it with a @[fileinfo] tag as it doesn't directly correspond to + // a specific line of verilog code. wire_decls.push_back(stringf(" %s is invalid\n", unconn_id.c_str())); } - wire_exprs.push_back(stringf(" %s <= %s\n", make_id(wire->name), expr.c_str())); + wire_exprs.push_back(stringf(" %s <= %s %s\n", make_id(wire->name), expr.c_str(), wireFileinfo.c_str())); } else { if (make_unconn_id) { unconn_id.clear(); } + // `invalid` is a firrtl construction for simulation so we will not + // tag it with a @[fileinfo] tag as it doesn't directly correspond to + // a specific line of verilog code. wire_decls.push_back(stringf(" %s is invalid\n", make_id(wire->name))); } } @@ -1123,7 +1135,8 @@ struct FirrtlBackend : public Backend { if (top == nullptr) top = last; - *f << stringf("circuit %s:\n", make_id(top->name)); + std::string circuitFileinfo = getFileinfo(top); + *f << stringf("circuit %s: %s\n", make_id(top->name), circuitFileinfo.c_str()); for (auto module : design->modules()) { diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 4cf329f75..9ddd538b5 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -1158,6 +1158,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire) { current_ast = ast; + current_ast_mod = nullptr; flag_dump_ast1 = dump_ast1; flag_dump_ast2 = dump_ast2; flag_no_dump_ptr = no_dump_ptr; @@ -1224,6 +1225,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump } design->add(process_module(*it, defer)); + current_ast_mod = nullptr; } else if ((*it)->type == AST_PACKAGE) { // process enum/other declarations @@ -1568,6 +1570,9 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dict<RTLIL::Id rewritten.reserve(GetSize(parameters)); AstNode *new_ast = ast->clone(); + if (!new_ast->attributes.count(ID::hdlname)) + new_ast->attributes[ID::hdlname] = AstNode::mkconst_str(stripped_name); + para_counter = 0; for (auto child : new_ast->children) { if (child->type != AST_PARAMETER) diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 2ebe2edbc..837c14ad7 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -64,6 +64,21 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg continue; } + bool got_len = false; + bool got_zlen = false; + int len_value = 0; + + while ('0' <= cformat && cformat <= '9') + { + if (!got_len && cformat == '0') + got_zlen = true; + + got_len = true; + len_value = 10*len_value + (cformat - '0'); + + cformat = sformat[++i]; + } + // Simplify the argument AstNode *node_arg = nullptr; @@ -74,6 +89,9 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg case 'S': case 'd': case 'D': + if (got_len) + goto unsupported_format; + /* fall through */ case 'x': case 'X': if (next_arg >= GetSize(children)) @@ -88,9 +106,12 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg case 'm': case 'M': + if (got_len) + goto unsupported_format; break; default: + unsupported_format: log_file_error(filename, location.first_line, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str()); break; } @@ -104,19 +125,28 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg case 'd': case 'D': - { - char tmp[128]; - snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int()); - sout += tmp; - } + sout += stringf("%d", node_arg->bitsAsConst().as_int()); break; case 'x': case 'X': { - char tmp[128]; - snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int()); - sout += tmp; + Const val = node_arg->bitsAsConst(); + + while (GetSize(val) % 4 != 0) + val.bits.push_back(State::S0); + + int len = GetSize(val) / 4; + for (int i = len; i < len_value; i++) + sout += got_zlen ? '0' : ' '; + + for (int i = len-1; i >= 0; i--) { + Const digit = val.extract(4*i, 4); + if (digit.is_fully_def()) + sout += stringf(cformat == 'x' ? "%x" : "%X", digit.as_int()); + else + sout += cformat == 'x' ? "x" : "X"; + } } break; @@ -1169,7 +1199,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // annotate identifiers using scope resolution and create auto-wires as needed if (type == AST_IDENTIFIER) { if (current_scope.count(str) == 0) { - for (auto node : current_ast_mod->children) { + AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod; + for (auto node : current_scope_ast->children) { //log("looking at mod scope child %s\n", type2str(node->type).c_str()); switch (node->type) { case AST_PARAMETER: @@ -1203,7 +1234,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } if (current_scope.count(str) == 0) { - if (flag_autowire || str == "\\$global_clock") { + if (current_ast_mod == nullptr) { + log_file_error(filename, location.first_line, "Identifier `%s' is implicitly declared outside of a module.\n", str.c_str()); + } else if (flag_autowire || str == "\\$global_clock") { AstNode *auto_wire = new AstNode(AST_AUTOWIRE); auto_wire->str = str; current_ast_mod->children.push_back(auto_wire); diff --git a/frontends/rpc/rpc_frontend.cc b/frontends/rpc/rpc_frontend.cc index a23f7548e..46ee6a733 100644 --- a/frontends/rpc/rpc_frontend.cc +++ b/frontends/rpc/rpc_frontend.cc @@ -217,6 +217,8 @@ struct RpcModule : RTLIL::Module { module.second->name = mangled_name; module.second->design = design; module.second->attributes.erase(ID::top); + if (!module.second->has_attribute(ID::hdlname)) + module.second->set_string_attribute(ID::hdlname, module.first.str()); design->modules_[mangled_name] = module.second; derived_design->modules_.erase(module.first); } diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 76373c2e4..4a5aba79e 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -1924,11 +1924,13 @@ always_events: always_event: TOK_POSEDGE expr { AstNode *node = new AstNode(AST_POSEDGE); + SET_AST_NODE_LOC(node, @1, @1); ast_stack.back()->children.push_back(node); node->children.push_back($2); } | TOK_NEGEDGE expr { AstNode *node = new AstNode(AST_NEGEDGE); + SET_AST_NODE_LOC(node, @1, @1); ast_stack.back()->children.push_back(node); node->children.push_back($2); } | @@ -2244,6 +2246,7 @@ behavioral_stmt: exitTypeScope(); if ($4 != NULL && $8 != NULL && *$4 != *$8) frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $4->c_str()+1, $8->c_str()+1); + SET_AST_NODE_LOC(ast_stack.back(), @2, @8); delete $4; delete $8; ast_stack.pop_back(); @@ -2618,6 +2621,7 @@ basic_expr: bits->str = *$1; SET_AST_NODE_LOC(bits, @1, @1); AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); + SET_AST_NODE_LOC(val, @2, @2); if (val == NULL) log_error("Value conversion failed: `%s'\n", $2->c_str()); $$ = new AstNode(AST_TO_BITS, bits, val); @@ -2626,6 +2630,7 @@ basic_expr: } | integral_number { $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); + SET_AST_NODE_LOC($$, @1, @1); if ($$ == NULL) log_error("Value conversion failed: `%s'\n", $1->c_str()); delete $1; @@ -2644,6 +2649,7 @@ basic_expr: } | TOK_STRING { $$ = AstNode::mkconst_str(*$1); + SET_AST_NODE_LOC($$, @1, @1); delete $1; } | hierarchical_id attr { diff --git a/kernel/hashlib.h b/kernel/hashlib.h index e7cb312ed..996bda38e 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -314,11 +314,11 @@ class dict int do_insert(const K &key, int &hash) { if (hashtable.empty()) { - entries.push_back(entry_t(std::pair<K, T>(key, T()), -1)); + entries.emplace_back(std::pair<K, T>(key, T()), -1); do_rehash(); hash = do_hash(key); } else { - entries.push_back(entry_t(std::pair<K, T>(key, T()), hashtable[hash])); + entries.emplace_back(std::pair<K, T>(key, T()), hashtable[hash]); hashtable[hash] = entries.size() - 1; } return entries.size() - 1; @@ -327,11 +327,25 @@ class dict int do_insert(const std::pair<K, T> &value, int &hash) { if (hashtable.empty()) { - entries.push_back(entry_t(value, -1)); + entries.emplace_back(value, -1); do_rehash(); hash = do_hash(value.first); } else { - entries.push_back(entry_t(value, hashtable[hash])); + entries.emplace_back(value, hashtable[hash]); + hashtable[hash] = entries.size() - 1; + } + return entries.size() - 1; + } + + int do_insert(std::pair<K, T> &&rvalue, int &hash) + { + if (hashtable.empty()) { + auto key = rvalue.first; + entries.emplace_back(std::forward<std::pair<K, T>>(rvalue), -1); + do_rehash(); + hash = do_hash(key); + } else { + entries.emplace_back(std::forward<std::pair<K, T>>(rvalue), hashtable[hash]); hashtable[hash] = entries.size() - 1; } return entries.size() - 1; @@ -441,6 +455,56 @@ public: return std::pair<iterator, bool>(iterator(this, i), true); } + std::pair<iterator, bool> insert(std::pair<K, T> &&rvalue) + { + int hash = do_hash(rvalue.first); + int i = do_lookup(rvalue.first, hash); + if (i >= 0) + return std::pair<iterator, bool>(iterator(this, i), false); + i = do_insert(std::forward<std::pair<K, T>>(rvalue), hash); + return std::pair<iterator, bool>(iterator(this, i), true); + } + + std::pair<iterator, bool> emplace(K const &key, T const &value) + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + if (i >= 0) + return std::pair<iterator, bool>(iterator(this, i), false); + i = do_insert(std::make_pair(key, value), hash); + return std::pair<iterator, bool>(iterator(this, i), true); + } + + std::pair<iterator, bool> emplace(K const &key, T &&rvalue) + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + if (i >= 0) + return std::pair<iterator, bool>(iterator(this, i), false); + i = do_insert(std::make_pair(key, std::forward<T>(rvalue)), hash); + return std::pair<iterator, bool>(iterator(this, i), true); + } + + std::pair<iterator, bool> emplace(K &&rkey, T const &value) + { + int hash = do_hash(rkey); + int i = do_lookup(rkey, hash); + if (i >= 0) + return std::pair<iterator, bool>(iterator(this, i), false); + i = do_insert(std::make_pair(std::forward<K>(rkey), value), hash); + return std::pair<iterator, bool>(iterator(this, i), true); + } + + std::pair<iterator, bool> emplace(K &&rkey, T &&rvalue) + { + int hash = do_hash(rkey); + int i = do_lookup(rkey, hash); + if (i >= 0) + return std::pair<iterator, bool>(iterator(this, i), false); + i = do_insert(std::make_pair(std::forward<K>(rkey), std::forward<T>(rvalue)), hash); + return std::pair<iterator, bool>(iterator(this, i), true); + } + int erase(const K &key) { int hash = do_hash(key); diff --git a/kernel/register.cc b/kernel/register.cc index af8c1b8e8..925d0d776 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -485,20 +485,21 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<s cmd_error(args, argidx, "Extra filename argument in direct file mode."); filename = arg; + //Accommodate heredocs with EOT marker spaced out from "<<", e.g. "<< EOT" vs. "<<EOT" if (filename == "<<" && argidx+1 < args.size()) filename += args[++argidx]; if (filename.compare(0, 2, "<<") == 0) { - if (Frontend::current_script_file == NULL) - log_error("Unexpected here document '%s' outside of script!\n", filename.c_str()); if (filename.size() <= 2) log_error("Missing EOT marker in here document!\n"); std::string eot_marker = filename.substr(2); + if (Frontend::current_script_file == nullptr) + filename = "<stdin>"; last_here_document.clear(); while (1) { std::string buffer; char block[4096]; while (1) { - if (fgets(block, 4096, Frontend::current_script_file) == NULL) + if (fgets(block, 4096, Frontend::current_script_file == nullptr? stdin : Frontend::current_script_file) == nullptr) log_error("Unexpected end of file in here document '%s'!\n", filename.c_str()); buffer += block; if (buffer.size() > 0 && (buffer[buffer.size() - 1] == '\n' || buffer[buffer.size() - 1] == '\r')) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index dc368ead5..8af941c85 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -273,6 +273,11 @@ bool RTLIL::Const::is_fully_undef() const return true; } +bool RTLIL::AttrObject::has_attribute(RTLIL::IdString id) const +{ + return attributes.count(id); +} + void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id, bool value) { if (value) @@ -289,6 +294,23 @@ bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const return it->second.as_bool(); } +void RTLIL::AttrObject::set_string_attribute(RTLIL::IdString id, string value) +{ + if (value.empty()) + attributes.erase(id); + else + attributes[id] = value; +} + +string RTLIL::AttrObject::get_string_attribute(RTLIL::IdString id) const +{ + std::string value; + const auto it = attributes.find(id); + if (it != attributes.end()) + value = it->second.decode_string(); + return value; +} + void RTLIL::AttrObject::set_strpool_attribute(RTLIL::IdString id, const pool<string> &data) { string attrval; @@ -317,23 +339,6 @@ pool<string> RTLIL::AttrObject::get_strpool_attribute(RTLIL::IdString id) const return data; } -void RTLIL::AttrObject::set_src_attribute(const std::string &src) -{ - if (src.empty()) - attributes.erase(ID::src); - else - attributes[ID::src] = src; -} - -std::string RTLIL::AttrObject::get_src_attribute() const -{ - std::string src; - const auto it = attributes.find(ID::src); - if (it != attributes.end()) - src = it->second.decode_string(); - return src; -} - bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) const { if (full_selection) @@ -597,6 +602,7 @@ void RTLIL::Design::remove(RTLIL::Module *module) } log_assert(modules_.at(module->name) == module); + log_assert(refcount_modules_ == 0); modules_.erase(module->name); delete module; } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 17f038e36..f3b1c9ae7 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -656,6 +656,8 @@ struct RTLIL::AttrObject { dict<RTLIL::IdString, RTLIL::Const> attributes; + bool has_attribute(RTLIL::IdString id) const; + void set_bool_attribute(RTLIL::IdString id, bool value=true); bool get_bool_attribute(RTLIL::IdString id) const; @@ -663,12 +665,19 @@ struct RTLIL::AttrObject return get_bool_attribute(ID::blackbox) || (!ignore_wb && get_bool_attribute(ID::whitebox)); } + void set_string_attribute(RTLIL::IdString id, string value); + string get_string_attribute(RTLIL::IdString id) const; + void set_strpool_attribute(RTLIL::IdString id, const pool<string> &data); void add_strpool_attribute(RTLIL::IdString id, const pool<string> &data); pool<string> get_strpool_attribute(RTLIL::IdString id) const; - void set_src_attribute(const std::string &src); - std::string get_src_attribute() const; + void set_src_attribute(const std::string &src) { + set_string_attribute(ID::src, src); + } + std::string get_src_attribute() const { + return get_string_attribute(ID::src); + } }; struct RTLIL::SigChunk diff --git a/libs/sha1/sha1.h b/libs/sha1/sha1.h index 9f526376e..b1346b548 100644 --- a/libs/sha1/sha1.h +++ b/libs/sha1/sha1.h @@ -35,9 +35,9 @@ public: static std::string from_file(const std::string &filename); private: - static const unsigned int DIGEST_INTS = 5; /* number of 32bit integers per SHA1 digest */ - static const unsigned int BLOCK_INTS = 16; /* number of 32bit integers per SHA1 block */ - static const unsigned int BLOCK_BYTES = BLOCK_INTS * 4; + static constexpr unsigned int DIGEST_INTS = 5; /* number of 32bit integers per SHA1 digest */ + static constexpr unsigned int BLOCK_INTS = 16; /* number of 32bit integers per SHA1 block */ + static constexpr unsigned int BLOCK_BYTES = BLOCK_INTS * 4; uint32_t digest[DIGEST_INTS]; std::string buffer; diff --git a/libs/subcircuit/subcircuit.cc b/libs/subcircuit/subcircuit.cc index e8361a67e..4068dc09a 100644 --- a/libs/subcircuit/subcircuit.cc +++ b/libs/subcircuit/subcircuit.cc @@ -316,7 +316,7 @@ class SubCircuit::SolverWorker // helper functions for handling permutations - static const int maxPermutationsLimit = 1000000; + static constexpr int maxPermutationsLimit = 1000000; static int numberOfPermutations(const std::vector<std::string> &list) { @@ -1689,4 +1689,3 @@ void SubCircuit::Solver::clearConfig() { worker->clearConfig(); } - diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index 4612760cc..cfe97067d 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -99,6 +99,11 @@ struct DesignPass : public Pass { log("The Verilog front-end remembers defined macros and top-level declarations\n"); log("between calls to 'read_verilog'. This command resets this memory.\n"); log("\n"); + log(" design -delete <name>\n"); + log("\n"); + log("Delete the design previously saved under the given name.\n"); + log("\n"); + } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { @@ -110,7 +115,7 @@ struct DesignPass : public Pass { bool pop_mode = false; bool import_mode = false; RTLIL::Design *copy_from_design = NULL, *copy_to_design = NULL; - std::string save_name, load_name, as_name; + std::string save_name, load_name, as_name, delete_name; std::vector<RTLIL::Module*> copy_src_modules; size_t argidx; @@ -190,6 +195,13 @@ struct DesignPass : public Pass { as_name = args[++argidx]; continue; } + if (!got_mode && args[argidx] == "-delete" && argidx+1 < args.size()) { + got_mode = true; + delete_name = args[++argidx]; + if (saved_designs.count(delete_name) == 0) + log_cmd_error("No saved design '%s' found!\n", delete_name.c_str()); + continue; + } break; } @@ -340,7 +352,7 @@ struct DesignPass : public Pass { if (reset_mode || !load_name.empty() || push_mode || pop_mode) { - for (auto mod : design->modules()) + for (auto mod : design->modules().to_vector()) design->remove(mod); design->selection_stack.clear(); @@ -379,6 +391,14 @@ struct DesignPass : public Pass { pushed_designs.pop_back(); } } + + if (!delete_name.empty()) + { + auto it = saved_designs.find(delete_name); + log_assert(it != saved_designs.end()); + delete it->second; + saved_designs.erase(it); + } } } DesignPass; diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 9b1830b7b..7d6d84d42 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -32,27 +32,27 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std:: if (module->count_id(to_name)) log_cmd_error("There is already an object `%s' in module `%s'.\n", to_name.c_str(), module->name.c_str()); - for (auto &it : module->wires_) - if (it.first == from_name) { - Wire *w = it.second; - log("Renaming wire %s to %s in module %s.\n", log_id(w), log_id(to_name), log_id(module)); - module->rename(w, to_name); - if (w->port_id || flag_output) { - if (flag_output) - w->port_output = true; - module->fixup_ports(); - } - return; - } + RTLIL::Wire *wire_to_rename = module->wire(from_name); + RTLIL::Cell *cell_to_rename = module->cell(from_name); - for (auto &it : module->cells_) - if (it.first == from_name) { + if (wire_to_rename != nullptr) { + log("Renaming wire %s to %s in module %s.\n", log_id(wire_to_rename), log_id(to_name), log_id(module)); + module->rename(wire_to_rename, to_name); + if (wire_to_rename->port_id || flag_output) { if (flag_output) - log_cmd_error("Called with -output but the specified object is a cell.\n"); - log("Renaming cell %s to %s in module %s.\n", log_id(it.second), log_id(to_name), log_id(module)); - module->rename(it.second, to_name); - return; + wire_to_rename->port_output = true; + module->fixup_ports(); } + return; + } + + if (cell_to_rename != nullptr) { + if (flag_output) + log_cmd_error("Called with -output but the specified object is a cell.\n"); + log("Renaming cell %s to %s in module %s.\n", log_id(cell_to_rename), log_id(to_name), log_id(module)); + module->rename(cell_to_rename, to_name); + return; + } log_cmd_error("Object `%s' not found!\n", from_name.c_str()); } @@ -66,26 +66,26 @@ static std::string derive_name_from_src(const std::string &src, int counter) return stringf("\\%s$%d", src_base.c_str(), counter); } -static IdString derive_name_from_wire(const RTLIL::Cell &cell) +static IdString derive_name_from_cell_output_wire(const RTLIL::Cell *cell) { // Find output const SigSpec *output = nullptr; int num_outputs = 0; - for (auto &connection : cell.connections()) { - if (cell.output(connection.first)) { + for (auto &connection : cell->connections()) { + if (cell->output(connection.first)) { output = &connection.second; num_outputs++; } } if (num_outputs != 1) // Skip cells thad drive multiple outputs - return cell.name; + return cell->name; std::string name = ""; for (auto &chunk : output->chunks()) { // Skip cells that drive privately named wires if (!chunk.wire || chunk.wire->name.str()[0] == '$') - return cell.name; + return cell->name; if (name != "") name += "$"; @@ -99,7 +99,7 @@ static IdString derive_name_from_wire(const RTLIL::Cell &cell) } } - return name + cell.type.str(); + return name + cell->type.str(); } struct RenamePass : public Pass { @@ -210,30 +210,25 @@ struct RenamePass : public Pass { { extra_args(args, argidx, design); - for (auto &mod : design->modules_) + for (auto module : design->selected_modules()) { int counter = 0; + dict<RTLIL::Wire *, IdString> new_wire_names; + dict<RTLIL::Cell *, IdString> new_cell_names; + + for (auto wire : module->selected_wires()) + if (wire->name[0] == '$') + new_wire_names.emplace(wire, derive_name_from_src(wire->get_src_attribute(), counter++)); + + for (auto cell : module->selected_cells()) + if (cell->name[0] == '$') + new_cell_names.emplace(cell, derive_name_from_src(cell->get_src_attribute(), counter++)); - RTLIL::Module *module = mod.second; - if (!design->selected(module)) - continue; - - dict<RTLIL::IdString, RTLIL::Wire*> new_wires; - for (auto &it : module->wires_) { - if (it.first[0] == '$' && design->selected(module, it.second)) - it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++); - new_wires[it.second->name] = it.second; - } - module->wires_.swap(new_wires); - module->fixup_ports(); - - dict<RTLIL::IdString, RTLIL::Cell*> new_cells; - for (auto &it : module->cells_) { - if (it.first[0] == '$' && design->selected(module, it.second)) - it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++); - new_cells[it.second->name] = it.second; - } - module->cells_.swap(new_cells); + for (auto &it : new_wire_names) + module->rename(it.first, it.second); + + for (auto &it : new_cell_names) + module->rename(it.first, it.second); } } else @@ -241,19 +236,13 @@ struct RenamePass : public Pass { { extra_args(args, argidx, design); - for (auto &mod : design->modules_) - { - RTLIL::Module *module = mod.second; - if (!design->selected(module)) - continue; - - dict<RTLIL::IdString, RTLIL::Cell*> new_cells; - for (auto &it : module->cells_) { - if (it.first[0] == '$' && design->selected(module, it.second)) - it.second->name = derive_name_from_wire(*it.second); - new_cells[it.second->name] = it.second; - } - module->cells_.swap(new_cells); + for (auto module : design->selected_modules()) { + dict<RTLIL::Cell *, IdString> new_cell_names; + for (auto cell : module->selected_cells()) + if (cell->name[0] == '$') + new_cell_names[cell] = derive_name_from_cell_output_wire(cell); + for (auto &it : new_cell_names) + module->rename(it.first, it.second); } } else @@ -261,32 +250,33 @@ struct RenamePass : public Pass { { extra_args(args, argidx, design); - for (auto &mod : design->modules_) + for (auto module : design->selected_modules()) { int counter = 0; + dict<RTLIL::Wire *, IdString> new_wire_names; + dict<RTLIL::Cell *, IdString> new_cell_names; + + for (auto wire : module->selected_wires()) + if (wire->name[0] == '$') { + RTLIL::IdString buf; + do buf = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str()); + while (module->wire(buf) != nullptr); + new_wire_names[wire] = buf; + } + + for (auto cell : module->selected_cells()) + if (cell->name[0] == '$') { + RTLIL::IdString buf; + do buf = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str()); + while (module->cell(buf) != nullptr); + new_cell_names[cell] = buf; + } - RTLIL::Module *module = mod.second; - if (!design->selected(module)) - continue; - - dict<RTLIL::IdString, RTLIL::Wire*> new_wires; - for (auto &it : module->wires_) { - if (it.first[0] == '$' && design->selected(module, it.second)) - do it.second->name = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str()); - while (module->count_id(it.second->name) > 0); - new_wires[it.second->name] = it.second; - } - module->wires_.swap(new_wires); - module->fixup_ports(); - - dict<RTLIL::IdString, RTLIL::Cell*> new_cells; - for (auto &it : module->cells_) { - if (it.first[0] == '$' && design->selected(module, it.second)) - do it.second->name = stringf("\\%s%d%s", pattern_prefix.c_str(), counter++, pattern_suffix.c_str()); - while (module->count_id(it.second->name) > 0); - new_cells[it.second->name] = it.second; - } - module->cells_.swap(new_cells); + for (auto &it : new_wire_names) + module->rename(it.first, it.second); + + for (auto &it : new_cell_names) + module->rename(it.first, it.second); } } else @@ -294,30 +284,24 @@ struct RenamePass : public Pass { { extra_args(args, argidx, design); - for (auto &mod : design->modules_) + for (auto module : design->selected_modules()) { - RTLIL::Module *module = mod.second; - if (!design->selected(module)) - continue; - - dict<RTLIL::IdString, RTLIL::Wire*> new_wires; - for (auto &it : module->wires_) { - if (design->selected(module, it.second)) - if (it.first[0] == '\\' && it.second->port_id == 0) - it.second->name = NEW_ID; - new_wires[it.second->name] = it.second; - } - module->wires_.swap(new_wires); - module->fixup_ports(); - - dict<RTLIL::IdString, RTLIL::Cell*> new_cells; - for (auto &it : module->cells_) { - if (design->selected(module, it.second)) - if (it.first[0] == '\\') - it.second->name = NEW_ID; - new_cells[it.second->name] = it.second; - } - module->cells_.swap(new_cells); + dict<RTLIL::Wire *, IdString> new_wire_names; + dict<RTLIL::Cell *, IdString> new_cell_names; + + for (auto wire : module->selected_wires()) + if (wire->name[0] == '\\' && wire->port_id == 0) + new_wire_names[wire] = NEW_ID; + + for (auto cell : module->selected_cells()) + if (cell->name[0] == '\\') + new_cell_names[cell] = NEW_ID; + + for (auto &it : new_wire_names) + module->rename(it.first, it.second); + + for (auto &it : new_cell_names) + module->rename(it.first, it.second); } } else @@ -329,7 +313,7 @@ struct RenamePass : public Pass { IdString new_name = RTLIL::escape_id(args[argidx]); RTLIL::Module *module = design->top_module(); - if (module == NULL) + if (module == nullptr) log_cmd_error("No top module found!\n"); log("Renaming module %s to %s.\n", log_id(module), log_id(new_name)); @@ -345,27 +329,27 @@ struct RenamePass : public Pass { if (!design->selected_active_module.empty()) { - if (design->modules_.count(design->selected_active_module) > 0) - rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name, flag_output); + if (design->module(design->selected_active_module) != nullptr) + rename_in_module(design->module(design->selected_active_module), from_name, to_name, flag_output); } else { if (flag_output) log_cmd_error("Mode -output requires that there is an active module selected.\n"); - for (auto &mod : design->modules_) { - if (mod.first == from_name || RTLIL::unescape_id(mod.first) == from_name) { - to_name = RTLIL::escape_id(to_name); - log("Renaming module %s to %s.\n", mod.first.c_str(), to_name.c_str()); - RTLIL::Module *module = mod.second; - design->modules_.erase(module->name); - module->name = to_name; - design->modules_[module->name] = module; - goto rename_ok; + + RTLIL::Module *module_to_rename = nullptr; + for (auto module : design->modules()) + if (module->name == from_name || RTLIL::unescape_id(module->name) == from_name) { + module_to_rename = module; + break; } - } - log_cmd_error("Object `%s' not found!\n", from_name.c_str()); - rename_ok:; + if (module_to_rename != nullptr) { + to_name = RTLIL::escape_id(to_name); + log("Renaming module %s to %s.\n", module_to_rename->name.c_str(), to_name.c_str()); + design->rename(module_to_rename, to_name); + } else + log_cmd_error("Object `%s' not found!\n", from_name.c_str()); } } } diff --git a/passes/cmds/scatter.cc b/passes/cmds/scatter.cc index 7123ba9fb..a5ef95f02 100644 --- a/passes/cmds/scatter.cc +++ b/passes/cmds/scatter.cc @@ -46,25 +46,19 @@ struct ScatterPass : public Pass { CellTypes ct(design); extra_args(args, 1, design); - for (auto &mod_it : design->modules_) + for (auto module : design->selected_modules()) { - if (!design->selected(mod_it.second)) - continue; - - for (auto &c : mod_it.second->cells_) - for (auto &p : c.second->connections_) - { - RTLIL::Wire *wire = mod_it.second->addWire(NEW_ID, p.second.size()); - - if (ct.cell_output(c.second->type, p.first)) { - RTLIL::SigSig sigsig(p.second, wire); - mod_it.second->connect(sigsig); - } else { - RTLIL::SigSig sigsig(wire, p.second); - mod_it.second->connect(sigsig); + for (auto cell : module->cells()) { + dict<RTLIL::IdString, RTLIL::SigSig> new_connections; + for (auto conn : cell->connections()) + new_connections.emplace(conn.first, RTLIL::SigSig(conn.second, module->addWire(NEW_ID, GetSize(conn.second)))); + for (auto &it : new_connections) { + if (ct.cell_output(cell->type, it.first)) + module->connect(RTLIL::SigSig(it.second.first, it.second.second)); + else + module->connect(RTLIL::SigSig(it.second.second, it.second.first)); + cell->setPort(it.first, it.second.second); } - - p.second = wire; } } } diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index b64b077e4..c04ff438a 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -1007,6 +1007,7 @@ struct SelectPass : public Pass { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" select [ -add | -del | -set <name> ] {-read <filename> | <selection>}\n"); + log(" select [ -unset <name> ]\n"); log(" select [ <assert_option> ] {-read <filename> | <selection>}\n"); log(" select [ -list | -write <filename> | -count | -clear ]\n"); log(" select -module <modname>\n"); @@ -1029,6 +1030,10 @@ struct SelectPass : public Pass { log(" under the given name (see @<name> below). to save the current selection,\n"); log(" use \"select -set <name> %%\"\n"); log("\n"); + log(" -unset <name>\n"); + log(" do not modify the current selection. instead remove a previously saved\n"); + log(" selection under the given name (see @<name> below)."); + log("\n"); log(" -assert-none\n"); log(" do not modify the current selection. instead assert that the given\n"); log(" selection is empty. i.e. produce an error if any object matching the\n"); @@ -1238,7 +1243,7 @@ struct SelectPass : public Pass { int assert_max = -1; int assert_min = -1; std::string write_file, read_file; - std::string set_name, sel_str; + std::string set_name, unset_name, sel_str; work_stack.clear(); @@ -1310,6 +1315,10 @@ struct SelectPass : public Pass { set_name = RTLIL::escape_id(args[++argidx]); continue; } + if (arg == "-unset" && argidx+1 < args.size()) { + unset_name = RTLIL::escape_id(args[++argidx]); + continue; + } if (arg.size() > 0 && arg[0] == '-') log_cmd_error("Unknown option %s.\n", arg.c_str()); bool disable_empty_warning = count_mode || assert_none || assert_any || (assert_count != -1) || (assert_max != -1) || (assert_min != -1); @@ -1358,8 +1367,11 @@ struct SelectPass : public Pass { if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0)) log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any, assert-count, -assert-max, or -assert-min.\n"); - if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0)) - log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n"); + if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || !unset_name.empty() || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0)) + log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -unset, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n"); + + if (!unset_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || !set_name.empty() || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0)) + log_cmd_error("Option -unset can not be combined with -list, -write, -count, -add, -del, -set, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n"); if (work_stack.size() == 0 && got_module) { RTLIL::Selection sel; @@ -1527,6 +1539,13 @@ struct SelectPass : public Pass { return; } + if (!unset_name.empty()) + { + if (!design->selection_vars.erase(unset_name)) + log_error("Selection '%s' does not exist!\n", unset_name.c_str()); + return; + } + if (work_stack.size() == 0) { RTLIL::Selection &sel = design->selection_stack.back(); if (sel.full_selection) diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 3229dd1b2..2b35ace5e 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -1135,9 +1135,24 @@ skip_fine_alu: cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B'); if (cell->type == ID($alu)) { + bool a_signed = cell->parameters[ID::A_SIGNED].as_bool(); + bool b_signed = cell->parameters[ID::B_SIGNED].as_bool(); + bool is_signed = a_signed && b_signed; + RTLIL::SigBit sig_ci = assign_map(cell->getPort(ID::CI)); int y_width = GetSize(cell->getPort(ID::Y)); - module->connect(cell->getPort(ID::X), RTLIL::Const(State::S0, y_width)); - module->connect(cell->getPort(ID::CO), RTLIL::Const(State::S0, y_width)); + if (sig_ci == State::S1) { + /* sub, b is 0 */ + RTLIL::SigSpec a = cell->getPort(ID::A); + a.extend_u0(y_width, is_signed); + module->connect(cell->getPort(ID::X), module->Not(NEW_ID, a)); + module->connect(cell->getPort(ID::CO), RTLIL::Const(State::S1, y_width)); + } else { + /* add */ + RTLIL::SigSpec ab = cell->getPort(identity_wrt_a ? ID::A : ID::B); + ab.extend_u0(y_width, is_signed); + module->connect(cell->getPort(ID::X), ab); + module->connect(cell->getPort(ID::CO), RTLIL::Const(State::S0, y_width)); + } cell->unsetPort(ID::BI); cell->unsetPort(ID::CI); cell->unsetPort(ID::X); diff --git a/passes/sat/qbfsat.cc b/passes/sat/qbfsat.cc index 44691425f..981271770 100644 --- a/passes/sat/qbfsat.cc +++ b/passes/sat/qbfsat.cc @@ -39,7 +39,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct QbfSolutionType { - std::vector<std::string> stdout; + std::vector<std::string> stdout_lines; dict<std::string, std::string> hole_to_value; bool sat; bool unknown; //true if neither 'sat' nor 'unsat' @@ -72,7 +72,7 @@ void recover_solution(QbfSolutionType &sol) { bool sat_regex_found = false; bool unsat_regex_found = false; dict<std::string, bool> hole_value_recovered; - for (const std::string &x : sol.stdout) { + for (const std::string &x : sol.stdout_lines) { if(YS_REGEX_NS::regex_search(x, m, hole_value_regex)) { std::string loc = m[1].str(); std::string val = m[2].str(); @@ -294,7 +294,7 @@ QbfSolutionType qbf_solve(RTLIL::Module *mod, const QbfSolveOptions &opt) { { const std::string cmd = yosys_smtbmc_exe + " -s z3 -t 1 -g --binary " + (opt.dump_final_smt2? "--dump-smt2 " + opt.dump_final_smt2_file + " " : "") + tempdir_name + "/problem.smt2 2>&1"; auto process_line = [&ret, &smtbmc_warning, &show_smtbmc](const std::string &line) { - ret.stdout.push_back(line.substr(0, line.size()-1)); //don't include trailing newline + ret.stdout_lines.push_back(line.substr(0, line.size()-1)); //don't include trailing newline auto warning_pos = line.find(smtbmc_warning); if (warning_pos != std::string::npos) log_warning("%s", line.substr(warning_pos + smtbmc_warning.size() + 1).c_str()); diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 0ee495abd..aff0baa44 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -160,7 +160,7 @@ int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, void mark_port(RTLIL::SigSpec sig) { for (auto &bit : assign_map(sig)) - if (bit.wire != NULL && signal_map.count(bit) > 0) + if (bit.wire != nullptr && signal_map.count(bit) > 0) signal_list[signal_map[bit]].is_port = true; } @@ -197,7 +197,7 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) if (keepff) for (auto &c : sig_q.chunks()) - if (c.wire != NULL) + if (c.wire != nullptr) c.wire->attributes[ID::keep] = 1; assign_map.apply(sig_d); @@ -370,7 +370,7 @@ std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullp void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std::set<int> &workpool, std::vector<int> &in_counts) { - if (f == NULL) + if (f == nullptr) return; log("Dumping loop state graph to slide %d.\n", ++nr); @@ -406,7 +406,7 @@ void handle_loops() std::vector<int> in_edges_count(signal_list.size()); std::set<int> workpool; - FILE *dot_f = NULL; + FILE *dot_f = nullptr; int dot_nr = 0; // uncomment for troubleshooting the loop detection code @@ -464,9 +464,9 @@ void handle_loops() int id2 = edge_it.first; RTLIL::Wire *w1 = signal_list[id1].bit.wire; RTLIL::Wire *w2 = signal_list[id2].bit.wire; - if (w1 == NULL) + if (w1 == nullptr) id1 = id2; - else if (w2 == NULL) + else if (w2 == nullptr) continue; else if (w1->name[0] == '$' && w2->name[0] == '\\') id1 = id2; @@ -485,7 +485,7 @@ void handle_loops() continue; } - log_assert(signal_list[id1].bit.wire != NULL); + log_assert(signal_list[id1].bit.wire != nullptr); std::stringstream sstr; sstr << "$abcloop$" << (autoidx++); @@ -526,7 +526,7 @@ void handle_loops() } } - if (dot_f != NULL) + if (dot_f != nullptr) fclose(dot_f); } @@ -688,15 +688,15 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin en_polarity = false; en_str = en_str.substr(1); } - if (module->wires_.count(RTLIL::escape_id(en_str)) != 0) - en_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(en_str)), 0)); + if (module->wire(RTLIL::escape_id(en_str)) != nullptr) + en_sig = assign_map(module->wire(RTLIL::escape_id(en_str))); } if (clk_str[0] == '!') { clk_polarity = false; clk_str = clk_str.substr(1); } - if (module->wires_.count(RTLIL::escape_id(clk_str)) != 0) - clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0)); + if (module->wire(RTLIL::escape_id(clk_str)) != nullptr) + clk_sig = assign_map(module->wire(RTLIL::escape_id(clk_str))); } if (dff_mode && clk_sig.empty()) @@ -790,13 +790,13 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin for (auto c : cells) extract_cell(c, keepff); - for (auto &wire_it : module->wires_) { - if (wire_it.second->port_id > 0 || wire_it.second->get_bool_attribute(ID::keep)) - mark_port(RTLIL::SigSpec(wire_it.second)); + for (auto wire : module->wires()) { + if (wire->port_id > 0 || wire->get_bool_attribute(ID::keep)) + mark_port(wire); } - for (auto &cell_it : module->cells_) - for (auto &port_it : cell_it.second->connections()) + for (auto cell : module->cells()) + for (auto &port_it : cell->connections()) mark_port(port_it.second); if (clk_sig.size() != 0) @@ -809,7 +809,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin std::string buffer = stringf("%s/input.blif", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); - if (f == NULL) + if (f == nullptr) log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); fprintf(f, ".model netlist\n"); @@ -840,7 +840,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin fprintf(f, "# ys__n%-5d %s\n", si.id, log_signal(si.bit)); for (auto &si : signal_list) { - if (si.bit.wire == NULL) { + if (si.bit.wire == nullptr) { fprintf(f, ".names ys__n%d\n", si.id); if (si.bit == RTLIL::State::S1) fprintf(f, "1\n"); @@ -936,7 +936,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin buffer = stringf("%s/stdcells.genlib", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); - if (f == NULL) + if (f == nullptr) log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); fprintf(f, "GATE ZERO 1 Y=CONST0;\n"); fprintf(f, "GATE ONE 1 Y=CONST1;\n"); @@ -981,7 +981,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (!lut_costs.empty()) { buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); - if (f == NULL) + if (f == nullptr) log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); for (int i = 0; i < GetSize(lut_costs); i++) fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); @@ -1025,11 +1025,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin ifs.close(); log_header(design, "Re-integrating ABC results.\n"); - RTLIL::Module *mapped_mod = mapped_design->modules_[ID(netlist)]; - if (mapped_mod == NULL) + RTLIL::Module *mapped_mod = mapped_design->module(ID(netlist)); + if (mapped_mod == nullptr) log_error("ABC output file does not contain a module `netlist'.\n"); - for (auto &it : mapped_mod->wires_) { - RTLIL::Wire *w = it.second; + for (auto w : mapped_mod->wires()) { RTLIL::Wire *orig_wire = nullptr; RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire)); if (orig_wire != nullptr && orig_wire->attributes.count(ID::src)) @@ -1046,121 +1045,99 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin cell_stats[RTLIL::unescape_id(c->type)]++; if (c->type.in(ID(ZERO), ID(ONE))) { RTLIL::SigSig conn; - conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]); + RTLIL::IdString name_y = remap_name(c->getPort(ID::Y).as_wire()->name); + conn.first = module->wire(name_y); conn.second = RTLIL::SigSpec(c->type == ID(ZERO) ? 0 : 1, 1); module->connect(conn); continue; } if (c->type == ID(BUF)) { RTLIL::SigSig conn; - conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]); - conn.second = RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]); + RTLIL::IdString name_y = remap_name(c->getPort(ID::Y).as_wire()->name); + RTLIL::IdString name_a = remap_name(c->getPort(ID::A).as_wire()->name); + conn.first = module->wire(name_y); + conn.second = module->wire(name_a); module->connect(conn); continue; } if (c->type == ID(NOT)) { RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_NOT_)); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)])); - cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)])); + for (auto name : {ID::A, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } design->select(module, cell); continue; } if (c->type.in(ID(AND), ID(OR), ID(XOR), ID(NAND), ID(NOR), ID(XNOR), ID(ANDNOT), ID(ORNOT))) { RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)])); - cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)])); - cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)])); + for (auto name : {ID::A, ID::B, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } design->select(module, cell); continue; } if (c->type.in(ID(MUX), ID(NMUX))) { RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)])); - cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)])); - cell->setPort(ID::S, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::S).as_wire()->name)])); - cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)])); + for (auto name : {ID::A, ID::B, ID::S, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } design->select(module, cell); continue; } if (c->type == ID(MUX4)) { RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX4_)); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)])); - cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)])); - cell->setPort(ID::C, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::C).as_wire()->name)])); - cell->setPort(ID::D, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::D).as_wire()->name)])); - cell->setPort(ID::S, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::S).as_wire()->name)])); - cell->setPort(ID::T, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::T).as_wire()->name)])); - cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)])); + for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } design->select(module, cell); continue; } if (c->type == ID(MUX8)) { RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX8_)); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)])); - cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)])); - cell->setPort(ID::C, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::C).as_wire()->name)])); - cell->setPort(ID::D, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::D).as_wire()->name)])); - cell->setPort(ID::E, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::E).as_wire()->name)])); - cell->setPort(ID::F, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::F).as_wire()->name)])); - cell->setPort(ID::G, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::G).as_wire()->name)])); - cell->setPort(ID::H, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::H).as_wire()->name)])); - cell->setPort(ID::S, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::S).as_wire()->name)])); - cell->setPort(ID::T, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::T).as_wire()->name)])); - cell->setPort(ID::U, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::U).as_wire()->name)])); - cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)])); + for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::S, ID::T, ID::U, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } design->select(module, cell); continue; } if (c->type == ID(MUX16)) { RTLIL::Cell *cell = module->addCell(remap_name(c->name), ID($_MUX16_)); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)])); - cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)])); - cell->setPort(ID::C, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::C).as_wire()->name)])); - cell->setPort(ID::D, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::D).as_wire()->name)])); - cell->setPort(ID::E, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::E).as_wire()->name)])); - cell->setPort(ID::F, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::F).as_wire()->name)])); - cell->setPort(ID::G, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::G).as_wire()->name)])); - cell->setPort(ID::H, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::H).as_wire()->name)])); - cell->setPort(ID::I, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::I).as_wire()->name)])); - cell->setPort(ID::J, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::J).as_wire()->name)])); - cell->setPort(ID::K, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::K).as_wire()->name)])); - cell->setPort(ID::L, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::L).as_wire()->name)])); - cell->setPort(ID::M, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::M).as_wire()->name)])); - cell->setPort(ID::N, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::N).as_wire()->name)])); - cell->setPort(ID::O, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::O).as_wire()->name)])); - cell->setPort(ID::P, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::P).as_wire()->name)])); - cell->setPort(ID::S, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::S).as_wire()->name)])); - cell->setPort(ID::T, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::T).as_wire()->name)])); - cell->setPort(ID::U, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::U).as_wire()->name)])); - cell->setPort(ID::V, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::V).as_wire()->name)])); - cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)])); + for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::I, ID::J, ID::K, + ID::L, ID::M, ID::N, ID::O, ID::P, ID::S, ID::T, ID::U, ID::V, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } design->select(module, cell); continue; } if (c->type.in(ID(AOI3), ID(OAI3))) { RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)])); - cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)])); - cell->setPort(ID::C, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::C).as_wire()->name)])); - cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)])); + for (auto name : {ID::A, ID::B, ID::C, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } design->select(module, cell); continue; } if (c->type.in(ID(AOI4), ID(OAI4))) { RTLIL::Cell *cell = module->addCell(remap_name(c->name), stringf("$_%s_", c->type.c_str()+1)); if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - cell->setPort(ID::A, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)])); - cell->setPort(ID::B, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::B).as_wire()->name)])); - cell->setPort(ID::C, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::C).as_wire()->name)])); - cell->setPort(ID::D, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::D).as_wire()->name)])); - cell->setPort(ID::Y, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)])); + for (auto name : {ID::A, ID::B, ID::C, ID::D, ID::Y}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } design->select(module, cell); continue; } @@ -1175,8 +1152,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin cell->setPort(ID::E, en_sig); } if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - cell->setPort(ID::D, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::D).as_wire()->name)])); - cell->setPort(ID::Q, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Q).as_wire()->name)])); + for (auto name : {ID::D, ID::Q}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } cell->setPort(ID::C, clk_sig); design->select(module, cell); continue; @@ -1187,7 +1166,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (c->type.in(ID(_const0_), ID(_const1_))) { RTLIL::SigSig conn; - conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]); + conn.first = module->wire(remap_name(c->connections().begin()->second.as_wire()->name)); conn.second = RTLIL::SigSpec(c->type == ID(_const0_) ? 0 : 1, 1); module->connect(conn); continue; @@ -1204,16 +1183,18 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin cell->setPort(ID::E, en_sig); } if (markgroups) cell->attributes[ID::abcgroup] = map_autoidx; - cell->setPort(ID::D, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::D).as_wire()->name)])); - cell->setPort(ID::Q, RTLIL::SigSpec(module->wires_[remap_name(c->getPort(ID::Q).as_wire()->name)])); + for (auto name : {ID::D, ID::Q}) { + RTLIL::IdString remapped_name = remap_name(c->getPort(name).as_wire()->name); + cell->setPort(name, module->wire(remapped_name)); + } cell->setPort(ID::C, clk_sig); design->select(module, cell); continue; } if (c->type == ID($lut) && GetSize(c->getPort(ID::A)) == 1 && c->getParam(ID::LUT).as_int() == 2) { - SigSpec my_a = module->wires_[remap_name(c->getPort(ID::A).as_wire()->name)]; - SigSpec my_y = module->wires_[remap_name(c->getPort(ID::Y).as_wire()->name)]; + SigSpec my_a = module->wire(remap_name(c->getPort(ID::A).as_wire()->name)); + SigSpec my_y = module->wire(remap_name(c->getPort(ID::Y).as_wire()->name)); module->connect(my_y, my_a); continue; } @@ -1227,7 +1208,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (c.width == 0) continue; log_assert(c.width == 1); - newsig.append(module->wires_[remap_name(c.wire->name)]); + newsig.append(module->wire(remap_name(c.wire->name))); } cell->setPort(conn.first, newsig); } @@ -1236,16 +1217,16 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin for (auto conn : mapped_mod->connections()) { if (!conn.first.is_fully_const()) - conn.first = RTLIL::SigSpec(module->wires_[remap_name(conn.first.as_wire()->name)]); + conn.first = module->wire(remap_name(conn.first.as_wire()->name)); if (!conn.second.is_fully_const()) - conn.second = RTLIL::SigSpec(module->wires_[remap_name(conn.second.as_wire()->name)]); + conn.second = module->wire(remap_name(conn.second.as_wire()->name)); module->connect(conn); } if (recover_init) for (auto wire : mapped_mod->wires()) { if (wire->attributes.count(ID::init)) { - Wire *w = module->wires_[remap_name(wire->name)]; + Wire *w = module->wire(remap_name(wire->name)); log_assert(w->attributes.count(ID::init) == 0); w->attributes[ID::init] = wire->attributes.at(ID::init); } @@ -1261,10 +1242,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin RTLIL::SigSig conn; if (si.type != G(NONE)) { conn.first = si.bit; - conn.second = RTLIL::SigSpec(module->wires_[remap_name(buffer)]); + conn.second = module->wire(remap_name(buffer)); out_wires++; } else { - conn.first = RTLIL::SigSpec(module->wires_[remap_name(buffer)]); + conn.first = module->wire(remap_name(buffer)); conn.second = si.bit; in_wires++; } diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc index 8ae1b51ff..78c902866 100644 --- a/passes/techmap/abc9_ops.cc +++ b/passes/techmap/abc9_ops.cc @@ -467,7 +467,12 @@ void prep_lut(RTLIL::Design *design, int maxlut) { TimingInfo timing; - std::vector<std::tuple<int, IdString, int, std::vector<int>>> table; + struct t_lut { + IdString name; + int area; + std::vector<int> delays; + }; + std::map<int,t_lut> table; for (auto module : design->modules()) { auto it = module->attributes.find(ID::abc9_lut); if (it == module->attributes.end()) @@ -476,40 +481,52 @@ void prep_lut(RTLIL::Design *design, int maxlut) auto &t = timing.setup_module(module); TimingInfo::NameBit o; - std::vector<int> specify; + std::vector<int> delays; for (const auto &i : t.comb) { auto &d = i.first.second; if (o == TimingInfo::NameBit()) o = d; else if (o != d) - log_error("(* abc9_lut *) module '%s' with has more than one output.\n", log_id(module)); - specify.push_back(i.second); + log_error("Module '%s' with (* abc9_lut *) has more than one output.\n", log_id(module)); + delays.push_back(i.second); } - if (maxlut && GetSize(specify) > maxlut) + if (GetSize(delays) == 0) + log_error("Module '%s' with (* abc9_lut *) has no specify entries.\n", log_id(module)); + if (maxlut && GetSize(delays) > maxlut) continue; // ABC requires non-decreasing LUT input delays - std::sort(specify.begin(), specify.end()); - table.emplace_back(GetSize(specify), module->name, it->second.as_int(), std::move(specify)); + std::sort(delays.begin(), delays.end()); + + int K = GetSize(delays); + auto entry = t_lut{module->name, it->second.as_int(), std::move(delays)}; + auto r = table.emplace(K, entry); + if (!r.second) { + if (r.first->second.area != entry.area) + log_error("Modules '%s' and '%s' have conflicting (* abc9_lut *) values.\n", log_id(module), log_id(r.first->second.name)); + if (r.first->second.delays != entry.delays) + log_error("Modules '%s' and '%s' have conflicting specify entries.\n", log_id(module), log_id(r.first->second.name)); + } } - // ABC requires ascending size - std::sort(table.begin(), table.end()); + + if (table.empty()) + log_error("Design does not contain any modules with (* abc9_lut *).\n"); std::stringstream ss; - const auto &first = table.front(); + const auto &front = *table.begin(); // If the first entry does not start from a 1-input LUT, // (as ABC requires) crop the first entry to do so - for (int i = 1; i < std::get<0>(first); i++) { + for (int i = 1; i < front.first; i++) { ss << "# $__ABC9_LUT" << i << std::endl; - ss << i << " " << std::get<2>(first); + ss << i << " " << front.second.area; for (int j = 0; j < i; j++) - ss << " " << std::get<3>(first)[j]; + ss << " " << front.second.delays[j]; ss << std::endl; } for (const auto &i : table) { - ss << "# " << log_id(std::get<1>(i)) << std::endl; - ss << std::get<0>(i) << " " << std::get<2>(i); - for (const auto &j : std::get<3>(i)) + ss << "# " << log_id(i.second.name) << std::endl; + ss << i.first << " " << i.second.area; + for (const auto &j : i.second.delays) ss << " " << j; ss << std::endl; } diff --git a/techlibs/ecp5/dsp_map.v b/techlibs/ecp5/dsp_map.v index cb95ddb1c..df54d1d9f 100644 --- a/techlibs/ecp5/dsp_map.v +++ b/techlibs/ecp5/dsp_map.v @@ -10,7 +10,7 @@ module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y); .A0(A[0]), .A1(A[1]), .A2(A[2]), .A3(A[3]), .A4(A[4]), .A5(A[5]), .A6(A[6]), .A7(A[7]), .A8(A[8]), .A9(A[9]), .A10(A[10]), .A11(A[11]), .A12(A[12]), .A13(A[13]), .A14(A[14]), .A15(A[15]), .A16(A[16]), .A17(A[17]), .B0(B[0]), .B1(B[1]), .B2(B[2]), .B3(B[3]), .B4(B[4]), .B5(B[5]), .B6(B[6]), .B7(B[7]), .B8(B[8]), .B9(B[9]), .B10(B[10]), .B11(B[11]), .B12(B[12]), .B13(B[13]), .B14(B[14]), .B15(B[15]), .B16(B[16]), .B17(B[17]), .C17(1'b0), .C16(1'b0), .C15(1'b0), .C14(1'b0), .C13(1'b0), .C12(1'b0), .C11(1'b0), .C10(1'b0), .C9(1'b0), .C8(1'b0), .C7(1'b0), .C6(1'b0), .C5(1'b0), .C4(1'b0), .C3(1'b0), .C2(1'b0), .C1(1'b0), .C0(1'b0), - .SIGNEDA(A_SIGNED), .SIGNEDB(B_SIGNED), .SOURCEA(1'b0), .SOURCEB(1'b0), + .SIGNEDA(A_SIGNED ? 1'b1 : 1'b0), .SIGNEDB(B_SIGNED ? 1'b1 : 1'b0), .SOURCEA(1'b0), .SOURCEB(1'b0), .P0(Y[0]), .P1(Y[1]), .P2(Y[2]), .P3(Y[3]), .P4(Y[4]), .P5(Y[5]), .P6(Y[6]), .P7(Y[7]), .P8(Y[8]), .P9(Y[9]), .P10(Y[10]), .P11(Y[11]), .P12(Y[12]), .P13(Y[13]), .P14(Y[14]), .P15(Y[15]), .P16(Y[16]), .P17(Y[17]), .P18(Y[18]), .P19(Y[19]), .P20(Y[20]), .P21(Y[21]), .P22(Y[22]), .P23(Y[23]), .P24(Y[24]), .P25(Y[25]), .P26(Y[26]), .P27(Y[27]), .P28(Y[28]), .P29(Y[29]), .P30(Y[30]), .P31(Y[31]), .P32(Y[32]), .P33(Y[33]), .P34(Y[34]), .P35(Y[35]) ); diff --git a/tests/arch/ecp5/memories.ys b/tests/arch/ecp5/memories.ys index e1f748e26..f55bf01d2 100644 --- a/tests/arch/ecp5/memories.ys +++ b/tests/arch/ecp5/memories.ys @@ -208,7 +208,6 @@ select -assert-count 1 t:PDPW16KD design -reset; read_verilog ../common/blockrom.v chparam -set ADDRESS_WIDTH 3 -set DATA_WIDTH 36 sync_rom -write_ilang synth_ecp5 -top sync_rom; cd sync_rom select -assert-count 0 t:PDPW16KD # too inefficient select -assert-min 18 t:LUT4 @@ -274,7 +273,6 @@ select -assert-count 1 t:DP16KD design -reset; read_verilog ../common/blockrom.v chparam -set ADDRESS_WIDTH 3 -set DATA_WIDTH 18 sync_rom -write_ilang synth_ecp5 -top sync_rom; cd sync_rom select -assert-count 0 t:DP16KD # too inefficient select -assert-min 9 t:LUT4 diff --git a/tests/arch/ice40/memories.ys b/tests/arch/ice40/memories.ys index 571edec1d..c32f12315 100644 --- a/tests/arch/ice40/memories.ys +++ b/tests/arch/ice40/memories.ys @@ -112,7 +112,6 @@ select -assert-count 1 t:SB_RAM40_4K design -reset; read_verilog ../common/blockrom.v chparam -set ADDRESS_WIDTH 2 -set DATA_WIDTH 8 sync_rom -write_ilang synth_ice40 -top sync_rom; cd sync_rom select -assert-count 0 t:SB_RAM40_4K # too inefficient select -assert-min 1 t:SB_LUT4 diff --git a/tests/opt/opt_expr_alu.ys b/tests/opt/opt_expr_alu.ys index e288bcea6..9121c0096 100644 --- a/tests/opt/opt_expr_alu.ys +++ b/tests/opt/opt_expr_alu.ys @@ -8,7 +8,7 @@ alumacc equiv_opt -assert opt_expr -fine design -load postopt select -assert-count 1 t:$pos -select -assert-count none t:$pos t:* %D +select -assert-none t:$pos t:* %D design -reset @@ -20,7 +20,7 @@ EOT alumacc select -assert-count 1 t:$alu -select -assert-count none t:$alu t:* %D +select -assert-none t:$alu t:* %D design -reset @@ -33,7 +33,7 @@ EOT equiv_opt -assert opt_expr -fine design -load postopt select -assert-count 1 t:$pos -select -assert-count none t:$pos t:* %D +select -assert-none t:$pos t:* %D design -reset @@ -46,7 +46,7 @@ EOT equiv_opt -assert opt_expr -fine design -load postopt select -assert-count 1 t:$pos -select -assert-count none t:$pos t:* %D +select -assert-none t:$pos t:* %D design -reset @@ -60,7 +60,8 @@ alumacc equiv_opt -assert opt_expr -fine design -load postopt select -assert-count 1 t:$pos -select -assert-count none t:$pos t:* %D +select -assert-count 1 t:$not +select -assert-none t:$pos t:$not %% t:* %D design -reset @@ -76,7 +77,7 @@ design -load postopt select -assert-count 1 t:$alu select -assert-count 1 t:$alu r:Y_WIDTH=3 %i select -assert-count 1 t:$not -select -assert-count none t:$alu t:$not t:* %D %D +select -assert-none t:$alu t:$not t:* %D %D design -reset @@ -93,7 +94,7 @@ dump select -assert-count 2 t:$alu select -assert-count 1 t:$alu r:Y_WIDTH=2 %i select -assert-count 1 t:$alu r:Y_WIDTH=3 %i -select -assert-count none t:$alu t:* %D +select -assert-none t:$alu t:* %D design -reset @@ -108,4 +109,61 @@ equiv_opt -assert opt -fine design -load postopt select -assert-count 2 t:$alu select -assert-count 2 t:$alu r:Y_WIDTH=3 %i -select -assert-count none t:$alu t:* %D +select -assert-none t:$alu t:* %D + + +design -reset +read_verilog -icells <<EOT +module test(input [3:0] a, output [3:0] y, output [3:0] x, output [3:0] co); +$alu #( + .A_WIDTH(4), .B_WIDTH(4), .Y_WIDTH(4), + .A_SIGNED(0), .B_SIGNED(0), +) alu ( + .A(a), .B(4'h0), + .BI(1'b0), .CI(1'b0), + .Y(y), .X(x), .CO(co), +); +endmodule +EOT + +equiv_opt -assert opt +design -load postopt +select -assert-none t:$alu + + +design -reset +read_verilog -icells <<EOT +module test(input [3:0] a, output [3:0] y, output [3:0] x, output [3:0] co); +$alu #( + .A_WIDTH(4), .B_WIDTH(4), .Y_WIDTH(4), + .A_SIGNED(0), .B_SIGNED(0), +) alu ( + .A(a), .B(4'h0), + .BI(1'b1), .CI(1'b1), + .Y(y), .X(x), .CO(co), +); +endmodule +EOT + +equiv_opt -assert opt +design -load postopt +select -assert-none t:$alu + + +design -reset +read_verilog -icells <<EOT +module test(input [3:0] a, output [3:0] y, output [3:0] x, output [3:0] co); +$alu #( + .A_WIDTH(4), .B_WIDTH(4), .Y_WIDTH(4), + .A_SIGNED(0), .B_SIGNED(0), +) alu ( + .A(4'h0), .B(a), + .BI(1'b0), .CI(1'b0), + .Y(y), .X(x), .CO(co), +); +endmodule +EOT + +equiv_opt -assert opt +design -load postopt +select -assert-none t:$alu diff --git a/tests/select/unset.ys b/tests/select/unset.ys new file mode 100644 index 000000000..4f60781c2 --- /dev/null +++ b/tests/select/unset.ys @@ -0,0 +1,10 @@ +read_verilog <<EOT +module top(input i, output o); +assign o = i; +endmodule +EOT +select -set foo w:* +select -assert-any @foo +select -unset foo +logger -expect error "Selection '\\foo' does not exist!" 1 +select -unset foo diff --git a/tests/select/unset2.ys b/tests/select/unset2.ys new file mode 100644 index 000000000..456b84c22 --- /dev/null +++ b/tests/select/unset2.ys @@ -0,0 +1,10 @@ +read_verilog <<EOT +module top(input i, output o); +assign o = i; +endmodule +EOT +select -set foo w:* +select -assert-any @foo +select -unset foo +logger -expect error "Selection @foo is not defined!" 1 +select -list @foo diff --git a/tests/various/abc9.ys b/tests/various/abc9.ys index 0c7695089..6e2415ad7 100644 --- a/tests/various/abc9.ys +++ b/tests/various/abc9.ys @@ -53,3 +53,22 @@ assign q = w; endmodule EOT abc9 -lut 4 -dff + + +design -reset +read_verilog -icells -specify <<EOT +(* abc9_lut=1, blackbox *) +module LUT2(input [1:0] i, output o); +parameter [3:0] mask = 0; +assign o = i[0] ? (i[1] ? mask[3] : mask[2]) + : (i[1] ? mask[1] : mask[0]); +specify + (i *> o) = 1; +endspecify +endmodule + +module top(input [1:0] i, output o); +LUT2 #(.mask(4'b0)) lut (.i(i), .o(o)); +endmodule +EOT +abc9 diff --git a/tests/various/design.ys b/tests/various/design.ys new file mode 100644 index 000000000..f13ad8171 --- /dev/null +++ b/tests/various/design.ys @@ -0,0 +1,9 @@ +read_verilog <<EOT +module top(input i, output o); +assign o = i; +endmodule +EOT +design -stash foo +design -delete foo +logger -expect error "No saved design 'foo' found!" 1 +design -delete foo diff --git a/tests/various/design2.ys b/tests/various/design2.ys new file mode 100644 index 000000000..399999020 --- /dev/null +++ b/tests/various/design2.ys @@ -0,0 +1,9 @@ +read_verilog <<EOT +module top(input i, output o); +assign o = i; +endmodule +EOT +design -stash foo +design -delete foo +logger -expect error "No saved design 'foo' found!" 1 +design -load foo diff --git a/tests/various/global_scope.ys b/tests/various/global_scope.ys new file mode 100644 index 000000000..8c8618e10 --- /dev/null +++ b/tests/various/global_scope.ys @@ -0,0 +1,18 @@ +read_verilog -sv <<EOT +parameter A = 10; +parameter B = A; + +typedef enum { + CONST_A = A, + CONST_B = A+1 +} enum_t; + +module top(output [3:0] q, output [3:0] r); +assign q = 10; +assign r = CONST_B; +endmodule +EOT + +hierarchy -top top +sat -verify -prove q 10 top +sat -verify -prove r 11 top |