From 2c6cc27af1e981d24342ad1b378ec0f6d93092f1 Mon Sep 17 00:00:00 2001 From: Sahand Kashani Date: Thu, 23 Jul 2020 15:17:58 +0200 Subject: [PATCH 1/3] Add firrtl backend support for generic parameters in blackbox components Previous blackbox components were just emitted with their interface ports, but their generic parameters were never emitted and it was therefore impossible to customize them. This commit adds support for blackbox generic parameters, though support is only provided for INTEGER and STRING parameters. Other types of parameters such as DOUBLEs, ..., would result in undefined behavior here. This allows the emission of custom extmodule instances such as the following: extmodule fourteennm_lcell_comb_: input cin: UInt<1> output combout: UInt<1> output cout: UInt<1> input dataa: UInt<1> input datab: UInt<1> input datac: UInt<1> input datad: UInt<1> input datae: UInt<1> input dataf: UInt<1> input datag: UInt<1> input datah: UInt<1> input sharein: UInt<1> output shareout: UInt<1> output sumout: UInt<1> defname = fourteennm_lcell_comb parameter extended_lut = "off" parameter lut_mask = "b0001001000010010000100100001001000010010000100100001001000010010" parameter shared_arith = "off" --- backends/firrtl/firrtl.cc | 280 ++++++++++++++++++++++++++++++-------- 1 file changed, 222 insertions(+), 58 deletions(-) diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index 9739a7a9f..9a201d83d 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -102,6 +102,188 @@ const char *make_id(IdString id) return namecache.at(id).c_str(); } +std::string dump_const(const RTLIL::Const &data) +{ + std::string dataStr; + + dataStr += "\""; + + if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0) + { + // Emit binary prefix for string. + dataStr += "b"; + + // Emit bits. + int width = data.bits.size(); + for (int i = width - 1; i >= 0; i--) + { + log_assert(i < width); + switch (data.bits[i]) + { + case State::S0: dataStr += stringf("0"); break; + case State::S1: dataStr += stringf("1"); break; + case State::Sx: dataStr += stringf("x"); break; + case State::Sz: dataStr += stringf("z"); break; + case State::Sa: dataStr += stringf("-"); break; + case State::Sm: dataStr += stringf("m"); break; + } + } + } + else + { + std::string str = data.decode_string(); + for (size_t i = 0; i < str.size(); i++) + { + if (str[i] == '\n') + dataStr += "\\n"; + else if (str[i] == '\t') + dataStr += "\\t"; + else if (str[i] < 32) + dataStr += stringf("\\%03o", str[i]); + else if (str[i] == '"') + dataStr += "\\\""; + else if (str[i] == '\\') + dataStr += "\\\\"; + else + dataStr += str[i]; + } + } + + dataStr += "\""; + + return dataStr; +} + +std::string extmodule_name(RTLIL::Cell *cell, RTLIL::Module *mod_instance) +{ + // Since we are creating a custom extmodule for every cell that instantiates + // this blackbox, we need to create a custom name for it. We just use the + // name of the blackbox itself followed by the name of the cell. + const std::string cell_name = std::string(make_id(cell->name)); + const std::string blackbox_name = std::string(make_id(mod_instance->name)); + const std::string extmodule_name = blackbox_name + "_" + cell_name; + return extmodule_name; +} + +/** + * Emits a parameterized extmodule. Instance parameters are obtained from + * ''cell'' as it represents the instantiation of the blackbox defined by + * ''mod_instance'' and therefore contains all its instance parameters. + */ +void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream &f) +{ + const std::string indent = " "; + + const std::string blackbox_name = std::string(make_id(mod_instance->name)); + const std::string exported_name = extmodule_name(cell, mod_instance); + + // We use the cell's fileinfo for this extmodule as its parameters come from + // the cell and not from the module itself (the module contains default + // parameters, not the instance-specific ones we're using to emit the + // extmodule). + const std::string extmoduleFileinfo = getFileinfo(cell); + + // Emit extmodule header. + f << stringf(" extmodule %s: %s\n", exported_name.c_str(), extmoduleFileinfo.c_str()); + + // Emit extmodule ports. + for (auto wire : mod_instance->wires()) + { + const auto wireName = make_id(wire->name); + const std::string wireFileinfo = getFileinfo(wire); + + if (wire->port_input && wire->port_output) + { + log_error("Module port %s.%s is inout!\n", log_id(mod_instance), log_id(wire)); + } + + const std::string portDecl = stringf("%s%s %s: UInt<%d> %s\n", + indent.c_str(), + wire->port_input ? "input" : "output", + wireName, + wire->width, + wireFileinfo.c_str() + ); + + f << portDecl; + } + + // Emit extmodule "defname" field. This is the name of the verilog blackbox + // that is used when verilog is emitted, so we use the name of mod_instance + // here. + f << stringf("%sdefname = %s\n", indent.c_str(), blackbox_name.c_str()); + + // Emit extmodule generic parameters. + for (const auto &p : cell->parameters) + { + std::string param_name(p.first.c_str()); + const std::string param_value = dump_const(p.second); + + // Remove backslashes from parameters as these come from the internal RTLIL + // naming scheme, but should not exist in the emitted firrtl blackboxes. + // When firrtl is converted to verilog and given to downstream synthesis + // tools, these tools expect to find blackbox names and parameters as they + // were originally defined, i.e. without the extra RTLIL naming conventions. + param_name.erase( + std::remove(param_name.begin(), param_name.end(), '\\'), + param_name.end() + ); + + f << stringf("%sparameter %s = %s\n", indent.c_str(), param_name.c_str(), param_value.c_str()); + } + + f << "\n"; +} + +/** + * Emits extmodules for every instantiated blackbox in the design. + * + * RTLIL stores instance parameters at the cell's instantiation location. + * However, firrtl does not support module parameterization (everything is + * already elaborated). Firrtl instead supports external modules (extmodule), + * i.e. blackboxes that are defined by verilog and which have no body in + * firrtl itself other than the declaration of the blackboxes ports and + * parameters. + * + * Furthermore, firrtl does not support parameterization (even of extmodules) + * at a module's instantiation location and users must instead declare + * different extmodules with different instance parameters in the extmodule + * definition itself. + * + * This function goes through the design to identify all RTLIL blackboxes + * and emit parameterized extmodules with a unique name for each of them. The + * name that's given to the extmodule is + * + * _ + * + * Beware that it is therefore necessary for users to replace "parameterized" + * instances in the RTLIL sense with these custom extmodules for the firrtl to + * be valid. + */ +void emit_elaborated_extmodules(RTLIL::Design *design, std::ostream &f) +{ + for (auto module : design->modules()) + { + for (auto cell : module->cells()) + { + // Is this cell a module instance? + bool cellIsModuleInstance = cell->type[0] != '$'; + + if (cellIsModuleInstance) + { + // Find the module corresponding to this instance. + auto modInstance = design->module(cell->type); + bool modIsBlackbox = modInstance->get_blackbox_attribute(); + + if (modIsBlackbox) + { + emit_extmodule(cell, modInstance, f); + } + } + } + } +} + struct FirrtlWorker { Module *module; @@ -328,8 +510,16 @@ struct FirrtlWorker log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str()); return; } + + // If the instance is that of a blackbox, use the modified extmodule name + // that contains per-instance parameterizations. These instances were + // emitted earlier in the firrtl backend. + const std::string instanceName = instModule->get_blackbox_attribute() ? + extmodule_name(cell, instModule) : + instanceOf; + 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())); + wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceName.c_str(), cellFileinfo.c_str())); for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { if (it->second.size() > 0) { @@ -392,33 +582,6 @@ struct FirrtlWorker return result; } - void emit_extmodule() - { - std::string moduleFileinfo = getFileinfo(module); - f << stringf(" extmodule %s: %s\n", make_id(module->name), moduleFileinfo.c_str()); - vector port_decls; - - for (auto wire : module->wires()) - { - const auto wireName = make_id(wire->name); - std::string wireFileinfo = getFileinfo(wire); - - 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> %s\n", wire->port_input ? "input" : "output", - wireName, wire->width, wireFileinfo.c_str())); - } - - for (auto &str : port_decls) - { - f << str; - } - - f << stringf("\n"); - } - void emit_module() { std::string moduleFileinfo = getFileinfo(module); @@ -440,12 +603,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> %s\n", wire->port_input ? "input" : "output", + port_decls.push_back(stringf("%s%s %s: UInt<%d> %s\n", indent.c_str(), wire->port_input ? "input" : "output", wireName, wire->width, wireFileinfo.c_str())); } else { - wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", wireName, wire->width, wireFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), wireName, wire->width, wireFileinfo.c_str())); } } @@ -476,7 +639,7 @@ struct FirrtlWorker if (cell->type.in(ID($not), ID($logic_not), ID($_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> %s\n", y_id.c_str(), y_width, cellFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str())); if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; @@ -516,7 +679,7 @@ struct FirrtlWorker if ((firrtl_is_signed && !always_uint)) expr = stringf("asUInt(%s)", expr.c_str()); - cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -528,7 +691,7 @@ struct FirrtlWorker string a_expr = make_expr(cell->getPort(ID::A)); string b_expr = make_expr(cell->getPort(ID::B)); 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())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str())); if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; @@ -746,7 +909,7 @@ struct FirrtlWorker if ((firrtl_is_signed && !always_uint)) expr = stringf("asUInt(%s)", expr.c_str()); - cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -759,11 +922,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> %s\n", y_id.c_str(), width, cellFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), 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 %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -902,9 +1065,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 %s\n", y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str())); + wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent.c_str(), y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str())); - cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Q)); continue; @@ -923,7 +1086,7 @@ struct FirrtlWorker string a_expr = make_expr(cell->getPort(ID::A)); // Get the initial bit selector 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)); + wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); if (cell->getParam(ID::B_SIGNED).as_bool()) { // Use validif to constrain the selection (test the sign bit) @@ -933,7 +1096,7 @@ struct FirrtlWorker } string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_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", indent.c_str(), y_id.c_str(), expr.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } @@ -945,7 +1108,7 @@ struct FirrtlWorker string b_expr = make_expr(cell->getPort(ID::B)); auto b_string = b_expr.c_str(); string expr; - wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); + wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); if (cell->getParam(ID::B_SIGNED).as_bool()) { // We generate a left or right shift based on the sign of b. @@ -959,7 +1122,7 @@ struct FirrtlWorker } else { expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string); } - cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } @@ -972,8 +1135,8 @@ struct FirrtlWorker if (a_width < y_width) { a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); } - wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); - cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), a_expr.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); + cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), a_expr.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } @@ -986,8 +1149,8 @@ struct FirrtlWorker int y_width = GetSize(conn.first); string expr = make_expr(conn.second); - wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); - cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); + cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str())); register_reverse_wire_map(y_id, conn.first); } @@ -1053,13 +1216,13 @@ struct FirrtlWorker if (is_valid) { if (make_unconn_id) { - wire_decls.push_back(stringf(" wire %s: UInt<1> %s\n", unconn_id.c_str(), wireFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<1> %s\n", indent.c_str(), 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_decls.push_back(stringf("%s%s is invalid\n", indent.c_str(), unconn_id.c_str())); } - wire_exprs.push_back(stringf(" %s <= %s %s\n", make_id(wire->name), expr.c_str(), wireFileinfo.c_str())); + wire_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), make_id(wire->name), expr.c_str(), wireFileinfo.c_str())); } else { if (make_unconn_id) { unconn_id.clear(); @@ -1067,7 +1230,7 @@ struct FirrtlWorker // `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))); + wire_decls.push_back(stringf("%s%s is invalid\n", indent.c_str(), make_id(wire->name))); } } @@ -1112,12 +1275,7 @@ struct FirrtlWorker void run() { - // Blackboxes should be emitted as `extmodule`s in firrtl. Only ports are - // emitted in such a case. - if (module->get_blackbox_attribute()) - emit_extmodule(); - else - emit_module(); + emit_module(); } }; @@ -1180,10 +1338,16 @@ struct FirrtlBackend : public Backend { std::string circuitFileinfo = getFileinfo(top); *f << stringf("circuit %s: %s\n", make_id(top->name), circuitFileinfo.c_str()); + emit_elaborated_extmodules(design, *f); + + // Emit non-blackbox modules. for (auto module : design->modules()) { - FirrtlWorker worker(module, *f, design); - worker.run(); + if (!module->get_blackbox_attribute()) + { + FirrtlWorker worker(module, *f, design); + worker.run(); + } } namecache.clear(); From 5157f5623e2795c6ec2da650a7d8b5587af50c78 Mon Sep 17 00:00:00 2001 From: Sahand Kashani Date: Thu, 6 Aug 2020 00:49:55 +0200 Subject: [PATCH 2/3] Add support for real-valued parameters + preserve type of parameters This commit adds support for real-valued parameters in blackboxes. Additionally, parameters now retain their types are no longer all encoded as strings. There is a caveat with this implementation due to my limited knowledge of yosys, more specifically to how yosys encodes bitwidths of parameter values. The example below can motivate the implementation choice I took. Suppose a verilog component is declared with the following parameters: parameter signed [26:0] test_signed; parameter [26:0] test_unsigned; parameter signed [40:0] test_signed_large; If you instantiate it as follows: defparam .test_signed = 49; defparam .test_unsigned = 40'd35; defparam .test_signed_large = 40'd12; If you peek in the RTLIL::Const structure corresponding to these params, you realize that parameter "test_signed" is being considered as a 32-bit value since it's declared as "49" without a width specifier, even though the parameter is defined to have a maximum width of 27 bits. A similar issue occurs for parameter "test_unsigned" where it is supposed to take a maximum bit width of 27 bits, but if the user supplies a 40-bit value as above, then yosys considers the value to be 40 bits. I suppose this is due to the type being defined by the RHS rather than the definition. Regardless of this, I emit the same widths as what the user specifies on the RHS when generating firrtl IR. --- backends/firrtl/firrtl.cc | 155 ++++++++++++++++++++++++++++---------- 1 file changed, 115 insertions(+), 40 deletions(-) diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index 9a201d83d..5c2807ef4 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -102,56 +102,128 @@ const char *make_id(IdString id) return namecache.at(id).c_str(); } +std::string dump_const_string(const RTLIL::Const &data) +{ + std::string res_str; + + std::string str = data.decode_string(); + for (size_t i = 0; i < str.size(); i++) + { + if (str[i] == '\n') + res_str += "\\n"; + else if (str[i] == '\t') + res_str += "\\t"; + else if (str[i] < 32) + res_str += stringf("\\%03o", str[i]); + else if (str[i] == '"') + res_str += "\\\""; + else if (str[i] == '\\') + res_str += "\\\\"; + else + res_str += str[i]; + } + + return res_str; +} + std::string dump_const(const RTLIL::Const &data) { - std::string dataStr; + std::string res_str; - dataStr += "\""; + // // For debugging purposes to find out how Yosys encodes flags. + // res_str += stringf("flags_%x --> ", data.flags); - if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0) + // Real-valued parameter. + if (data.flags & RTLIL::CONST_FLAG_REAL) { - // Emit binary prefix for string. - dataStr += "b"; - - // Emit bits. - int width = data.bits.size(); - for (int i = width - 1; i >= 0; i--) - { - log_assert(i < width); - switch (data.bits[i]) - { - case State::S0: dataStr += stringf("0"); break; - case State::S1: dataStr += stringf("1"); break; - case State::Sx: dataStr += stringf("x"); break; - case State::Sz: dataStr += stringf("z"); break; - case State::Sa: dataStr += stringf("-"); break; - case State::Sm: dataStr += stringf("m"); break; - } - } + // Yosys stores real values as strings, so we call the string dumping code. + res_str += dump_const_string(data); } + // String parameter. + else if (data.flags & RTLIL::CONST_FLAG_STRING) + { + res_str += "\""; + res_str += dump_const_string(data); + res_str += "\""; + } + // Numeric (non-real) parameter. else { - std::string str = data.decode_string(); - for (size_t i = 0; i < str.size(); i++) + int width = data.bits.size(); + + // If a standard 32-bit int, then emit standard int value like "56" or + // "-56". Firrtl supports negative-valued int literals. + // + // SignedInt + // : ( '+' | '-' ) PosInt + // ; + if (width <= 32) { - if (str[i] == '\n') - dataStr += "\\n"; - else if (str[i] == '\t') - dataStr += "\\t"; - else if (str[i] < 32) - dataStr += stringf("\\%03o", str[i]); - else if (str[i] == '"') - dataStr += "\\\""; - else if (str[i] == '\\') - dataStr += "\\\\"; - else - dataStr += str[i]; + int32_t int_val = 0; + + for (int i = 0; i < width; i++) + { + switch (data.bits[i]) + { + case State::S0: break; + case State::S1: int_val |= (1 << i); break; + default: + log_error("Unexpected int value\n"); + break; + } + } + + res_str += stringf("%d", int_val); + } + else + { + // If value is larger than 32 bits, then emit a binary representation of + // the number. We have to do this as firrtl number literals don't support + // specifying their width, therefore a binary literal is the only way to + // guarantee the parameter widths match that provided on the RHS of a + // verilog parameter assignment. There is a caveat to this approach + // though: + // + // Note that parameter may be defined as having a fixed width as follows: + // + // parameter signed [26:0] test_signed; + // parameter [26:0] test_unsigned; + // parameter signed [40:0] test_signed_large; + // + // However, if you assign a value on the RHS without specifying the + // precision, then yosys considers the value you used as an int and + // assigns it a width of 32 bits regardless of the type of the parameter. + // + // defparam .test_signed = 49; (width = 32, though should be 27 based on definition) + // defparam .test_unsigned = 40'd35; (width = 40, though should be 27 based on definition) + // defparam .test_signed_large = 40'd12; (width = 40) + // + // We therefore may lose the precision of the original verilog literal if + // it was written without it's bitwidth specifier. + + // Emit binary prefix for string. + res_str += "\"b"; + + // Emit bits. + for (int i = width - 1; i >= 0; i--) + { + log_assert(i < width); + switch (data.bits[i]) + { + case State::S0: res_str += "0"; break; + case State::S1: res_str += "1"; break; + case State::Sx: res_str += "x"; break; + case State::Sz: res_str += "z"; break; + case State::Sa: res_str += "-"; break; + case State::Sm: res_str += "m"; break; + } + } + + res_str += "\""; } } - dataStr += "\""; - - return dataStr; + return res_str; } std::string extmodule_name(RTLIL::Cell *cell, RTLIL::Module *mod_instance) @@ -216,8 +288,11 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream // Emit extmodule generic parameters. for (const auto &p : cell->parameters) { - std::string param_name(p.first.c_str()); - const std::string param_value = dump_const(p.second); + const RTLIL::IdString p_id = p.first; + const RTLIL::Const p_value = p.second; + + std::string param_name(p_id.c_str()); + const std::string param_value = dump_const(p_value); // Remove backslashes from parameters as these come from the internal RTLIL // naming scheme, but should not exist in the emitted firrtl blackboxes. From 930a6ae7db905a31d003db0d034177dc0067c84a Mon Sep 17 00:00:00 2001 From: Sahand Kashani Date: Mon, 23 Nov 2020 10:43:59 +0100 Subject: [PATCH 3/3] Formatting fixes --- backends/firrtl/firrtl.cc | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index 5c2807ef4..44c3397da 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -178,11 +178,8 @@ std::string dump_const(const RTLIL::Const &data) else { // If value is larger than 32 bits, then emit a binary representation of - // the number. We have to do this as firrtl number literals don't support - // specifying their width, therefore a binary literal is the only way to - // guarantee the parameter widths match that provided on the RHS of a - // verilog parameter assignment. There is a caveat to this approach - // though: + // the number as integers are not large enough to contain the result. + // There is a caveat to this approach though: // // Note that parameter may be defined as having a fixed width as follows: // @@ -194,12 +191,12 @@ std::string dump_const(const RTLIL::Const &data) // precision, then yosys considers the value you used as an int and // assigns it a width of 32 bits regardless of the type of the parameter. // - // defparam .test_signed = 49; (width = 32, though should be 27 based on definition) - // defparam .test_unsigned = 40'd35; (width = 40, though should be 27 based on definition) - // defparam .test_signed_large = 40'd12; (width = 40) + // defparam .test_signed = 49; (width = 32, though should be 27 based on definition) + // defparam .test_unsigned = 40'd35; (width = 40, though should be 27 based on definition) + // defparam .test_signed_large = 40'd12; (width = 40) // // We therefore may lose the precision of the original verilog literal if - // it was written without it's bitwidth specifier. + // it was written without its bitwidth specifier. // Emit binary prefix for string. res_str += "\"b"; @@ -329,7 +326,7 @@ void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream * and emit parameterized extmodules with a unique name for each of them. The * name that's given to the extmodule is * - * _ + * _ * * Beware that it is therefore necessary for users to replace "parameterized" * instances in the RTLIL sense with these custom extmodules for the firrtl to