diff options
Diffstat (limited to 'backends')
-rw-r--r-- | backends/blif/blif.cc | 110 | ||||
-rw-r--r-- | backends/firrtl/firrtl.cc | 352 |
2 files changed, 347 insertions, 115 deletions
diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc index 780a16320..088819042 100644 --- a/backends/blif/blif.cc +++ b/backends/blif/blif.cc @@ -86,20 +86,18 @@ struct BlifDumper } } - vector<shared_str> cstr_buf; pool<SigBit> cstr_bits_seen; - const char *cstr(RTLIL::IdString id) + const std::string str(RTLIL::IdString id) { std::string str = RTLIL::unescape_id(id); for (size_t i = 0; i < str.size(); i++) if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>') str[i] = '?'; - cstr_buf.push_back(str); - return cstr_buf.back().c_str(); + return str; } - const char *cstr(RTLIL::SigBit sig) + const std::string str(RTLIL::SigBit sig) { cstr_bits_seen.insert(sig); @@ -117,11 +115,10 @@ struct BlifDumper if (sig.wire->width != 1) str += stringf("[%d]", sig.wire->upto ? sig.wire->start_offset+sig.wire->width-sig.offset-1 : sig.wire->start_offset+sig.offset); - cstr_buf.push_back(str); - return cstr_buf.back().c_str(); + return str; } - const char *cstr_init(RTLIL::SigBit sig) + const std::string str_init(RTLIL::SigBit sig) { sigmap.apply(sig); @@ -130,8 +127,7 @@ struct BlifDumper string str = stringf(" %d", init_bits.at(sig)); - cstr_buf.push_back(str); - return cstr_buf.back().c_str(); + return str; } const char *subckt_or_gate(std::string cell_type) @@ -168,7 +164,7 @@ struct BlifDumper void dump() { f << stringf("\n"); - f << stringf(".model %s\n", cstr(module->name)); + f << stringf(".model %s\n", str(module->name).c_str()); std::map<int, RTLIL::Wire*> inputs, outputs; @@ -183,7 +179,7 @@ struct BlifDumper for (auto &it : inputs) { RTLIL::Wire *wire = it.second; for (int i = 0; i < wire->width; i++) - f << stringf(" %s", cstr(RTLIL::SigSpec(wire, i))); + f << stringf(" %s", str(RTLIL::SigSpec(wire, i)).c_str()); } f << stringf("\n"); @@ -191,7 +187,7 @@ struct BlifDumper for (auto &it : outputs) { RTLIL::Wire *wire = it.second; for (int i = 0; i < wire->width; i++) - f << stringf(" %s", cstr(RTLIL::SigSpec(wire, i))); + f << stringf(" %s", str(RTLIL::SigSpec(wire, i)).c_str()); } f << stringf("\n"); @@ -233,131 +229,131 @@ struct BlifDumper if (config->unbuf_types.count(cell->type)) { auto portnames = config->unbuf_types.at(cell->type); f << stringf(".names %s %s\n1 1\n", - cstr(cell->getPort(portnames.first)), cstr(cell->getPort(portnames.second))); + str(cell->getPort(portnames.first)).c_str(), str(cell->getPort(portnames.second)).c_str()); continue; } if (!config->icells_mode && cell->type == ID($_NOT_)) { f << stringf(".names %s %s\n0 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_AND_)) { f << stringf(".names %s %s %s\n11 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_OR_)) { f << stringf(".names %s %s %s\n1- 1\n-1 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_XOR_)) { f << stringf(".names %s %s %s\n10 1\n01 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_NAND_)) { f << stringf(".names %s %s %s\n0- 1\n-0 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_NOR_)) { f << stringf(".names %s %s %s\n00 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_XNOR_)) { f << stringf(".names %s %s %s\n11 1\n00 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_ANDNOT_)) { f << stringf(".names %s %s %s\n10 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_ORNOT_)) { f << stringf(".names %s %s %s\n1- 1\n-0 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_AOI3_)) { f << stringf(".names %s %s %s %s\n-00 1\n0-0 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::C)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_OAI3_)) { f << stringf(".names %s %s %s %s\n00- 1\n--0 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::C)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_AOI4_)) { f << stringf(".names %s %s %s %s %s\n-0-0 1\n-00- 1\n0--0 1\n0-0- 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), - cstr(cell->getPort(ID::C)), cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), + str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_OAI4_)) { f << stringf(".names %s %s %s %s %s\n00-- 1\n--00 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), - cstr(cell->getPort(ID::C)), cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), + str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_MUX_)) { f << stringf(".names %s %s %s %s\n1-0 1\n-11 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), - cstr(cell->getPort(ID::S)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), + str(cell->getPort(ID::S)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_NMUX_)) { f << stringf(".names %s %s %s %s\n0-0 1\n-01 1\n", - cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), - cstr(cell->getPort(ID::S)), cstr(cell->getPort(ID::Y))); + str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), + str(cell->getPort(ID::S)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_FF_)) { - f << stringf(".latch %s %s%s\n", cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Q)), - cstr_init(cell->getPort(ID::Q))); + f << stringf(".latch %s %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), + str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_DFF_N_)) { - f << stringf(".latch %s %s fe %s%s\n", cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Q)), - cstr(cell->getPort(ID::C)), cstr_init(cell->getPort(ID::Q))); + f << stringf(".latch %s %s fe %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), + str(cell->getPort(ID::C)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_DFF_P_)) { - f << stringf(".latch %s %s re %s%s\n", cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Q)), - cstr(cell->getPort(ID::C)), cstr_init(cell->getPort(ID::Q))); + f << stringf(".latch %s %s re %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), + str(cell->getPort(ID::C)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_DLATCH_N_)) { - f << stringf(".latch %s %s al %s%s\n", cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Q)), - cstr(cell->getPort(ID::E)), cstr_init(cell->getPort(ID::Q))); + f << stringf(".latch %s %s al %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), + str(cell->getPort(ID::E)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_DLATCH_P_)) { - f << stringf(".latch %s %s ah %s%s\n", cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Q)), - cstr(cell->getPort(ID::E)), cstr_init(cell->getPort(ID::Q))); + f << stringf(".latch %s %s ah %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), + str(cell->getPort(ID::E)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } @@ -367,10 +363,10 @@ struct BlifDumper auto width = cell->parameters.at(ID::WIDTH).as_int(); log_assert(inputs.size() == width); for (int i = width-1; i >= 0; i--) - f << stringf(" %s", cstr(inputs.extract(i, 1))); + f << stringf(" %s", str(inputs.extract(i, 1)).c_str()); auto &output = cell->getPort(ID::Y); log_assert(output.size() == 1); - f << stringf(" %s", cstr(output)); + f << stringf(" %s", str(output).c_str()); f << stringf("\n"); RTLIL::SigSpec mask = cell->parameters.at(ID::LUT); for (int i = 0; i < (1 << width); i++) @@ -393,10 +389,10 @@ struct BlifDumper table.push_back(State::S0); log_assert(inputs.size() == width); for (int i = 0; i < width; i++) - f << stringf(" %s", cstr(inputs.extract(i, 1))); + f << stringf(" %s", str(inputs.extract(i, 1)).c_str()); auto &output = cell->getPort(ID::Y); log_assert(output.size() == 1); - f << stringf(" %s", cstr(output)); + f << stringf(" %s", str(output).c_str()); f << stringf("\n"); for (int i = 0; i < depth; i++) { for (int j = 0; j < width; j++) { @@ -411,11 +407,11 @@ struct BlifDumper goto internal_cell; } - f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type)); + f << stringf(".%s %s", subckt_or_gate(cell->type.str()), str(cell->type).c_str()); for (auto &conn : cell->connections()) { if (conn.second.size() == 1) { - f << stringf(" %s=%s", cstr(conn.first), cstr(conn.second[0])); + f << stringf(" %s=%s", str(conn.first).c_str(), str(conn.second[0]).c_str()); continue; } @@ -424,20 +420,20 @@ struct BlifDumper if (w == nullptr) { for (int i = 0; i < GetSize(conn.second); i++) - f << stringf(" %s[%d]=%s", cstr(conn.first), i, cstr(conn.second[i])); + f << stringf(" %s[%d]=%s", str(conn.first).c_str(), i, str(conn.second[i]).c_str()); } else { for (int i = 0; i < std::min(GetSize(conn.second), GetSize(w)); i++) { SigBit sig(w, i); - f << stringf(" %s[%d]=%s", cstr(conn.first), sig.wire->upto ? + f << stringf(" %s[%d]=%s", str(conn.first).c_str(), sig.wire->upto ? sig.wire->start_offset+sig.wire->width-sig.offset-1 : - sig.wire->start_offset+sig.offset, cstr(conn.second[i])); + sig.wire->start_offset+sig.offset, str(conn.second[i]).c_str()); } } } f << stringf("\n"); if (config->cname_mode) - f << stringf(".cname %s\n", cstr(cell->name)); + f << stringf(".cname %s\n", str(cell->name).c_str()); if (config->attr_mode) dump_params(".attr", cell->attributes); if (config->param_mode) @@ -446,7 +442,7 @@ struct BlifDumper if (0) { internal_cell: if (config->iname_mode) - f << stringf(".cname %s\n", cstr(cell->name)); + f << stringf(".cname %s\n", str(cell->name).c_str()); if (config->iattr_mode) dump_params(".attr", cell->attributes); } @@ -462,12 +458,12 @@ struct BlifDumper continue; if (config->conn_mode) - f << stringf(".conn %s %s\n", cstr(rhs_bit), cstr(lhs_bit)); + f << stringf(".conn %s %s\n", str(rhs_bit).c_str(), str(lhs_bit).c_str()); else if (!config->buf_type.empty()) f << stringf(".%s %s %s=%s %s=%s\n", subckt_or_gate(config->buf_type), config->buf_type.c_str(), - config->buf_in.c_str(), cstr(rhs_bit), config->buf_out.c_str(), cstr(lhs_bit)); + config->buf_in.c_str(), str(rhs_bit).c_str(), config->buf_out.c_str(), str(lhs_bit).c_str()); else - f << stringf(".names %s %s\n1 1\n", cstr(rhs_bit), cstr(lhs_bit)); + f << stringf(".names %s %s\n1 1\n", str(rhs_bit).c_str(), str(lhs_bit).c_str()); } f << stringf(".end\n"); diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index 9739a7a9f..44c3397da 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -102,6 +102,260 @@ 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 res_str; + + // // For debugging purposes to find out how Yosys encodes flags. + // res_str += stringf("flags_%x --> ", data.flags); + + // Real-valued parameter. + if (data.flags & RTLIL::CONST_FLAG_REAL) + { + // 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 + { + 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) + { + 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 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: + // + // 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 <inst_name> .test_signed = 49; (width = 32, though should be 27 based on definition) + // defparam <inst_name> .test_unsigned = 40'd35; (width = 40, though should be 27 based on definition) + // defparam <inst_name> .test_signed_large = 40'd12; (width = 40) + // + // We therefore may lose the precision of the original verilog literal if + // it was written without its 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 += "\""; + } + } + + return res_str; +} + +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) + { + 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. + // 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 + * + * <blackbox_name>_<instance_name> + * + * 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 +582,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 +654,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<std::string> 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 +675,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 +711,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 +751,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 +763,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 +981,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 +994,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 +1137,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 +1158,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 +1168,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 +1180,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 +1194,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 +1207,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 +1221,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 +1288,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 +1302,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 +1347,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 +1410,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(); |