From 93d19dc2fb35335607e67cab8ecf5ebe28268a1f Mon Sep 17 00:00:00 2001 From: Jim Lawson Date: Thu, 23 Aug 2018 14:35:11 -0700 Subject: [PATCH] Add support for module instances. Don't pad logical operands to one bit. Use operand width and signedness in $reduce_bool. Shift amounts are unsigned and shouldn't be padded. Group "is invalid" with the wire declaration, not its use (otherwise it is incorrectly wired to 0). --- backends/firrtl/firrtl.cc | 139 +++++++++++++++++++++++++++++++++----- 1 file changed, 122 insertions(+), 17 deletions(-) diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index 4f56f498c..14fae1a83 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -28,10 +28,32 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +bool defparam, noattr; pool used_names; dict namecache; int autoid_counter; +typedef unsigned FDirection; +static const FDirection NODIRECTION = 0x0; +static const FDirection IN = 0x1; +static const FDirection OUT = 0x2; +static const FDirection INOUT = 0x3; + +// Get a port direction with respect to a specific module. +FDirection getPortFDirection(IdString id, Module *module) +{ + Wire *wire = module->wires_.at(id); + FDirection direction = NODIRECTION; + if (wire && wire->port_id) + { + if (wire->port_input) + direction |= IN; + if (wire->port_output) + direction |= OUT; + } + return direction; +} + string next_id() { string new_id; @@ -77,6 +99,8 @@ struct FirrtlWorker dict> reverse_wire_map; string unconn_id; + RTLIL::Design *design; + std::string indent; void register_reverse_wire_map(string id, SigSpec sig) { @@ -84,11 +108,11 @@ struct FirrtlWorker reverse_wire_map[sig[i]] = make_pair(id, i); } - FirrtlWorker(Module *module, std::ostream &f) : module(module), f(f) + FirrtlWorker(Module *module, std::ostream &f, RTLIL::Design *theDesign) : module(module), f(f), design(theDesign), indent(" ") { } - string make_expr(SigSpec sig) + string make_expr(const SigSpec &sig) { string expr; @@ -135,6 +159,74 @@ struct FirrtlWorker return expr; } + std::string fid(RTLIL::IdString internal_id) + { + const char *str = internal_id.c_str(); + return *str == '\\' ? str + 1 : str; + } + + + std::string cellname(RTLIL::Cell *cell) + { + return fid(cell->name).c_str(); + } + + void process_instance(RTLIL::Cell *cell, vector &wire_exprs) + { + // TODO: Deal with cell attributes + if (!noattr && !cell->attributes.empty()) { + + } + std::string cell_type = fid(cell->type); + + // TODO: Deal with cell parameters + if (!defparam && cell->parameters.size() > 0) { + + } + + std::string cell_name = cellname(cell); + std::string cell_name_comment; + if (cell_name != fid(cell->name)) + cell_name_comment = " /* " + fid(cell->name) + " */ "; + else + cell_name_comment = ""; + // Find the module corresponding to this instance. + auto instModule = design->module(cell->type); + wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), cell_type.c_str())); + + for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { + if (it->second.size() > 0) { + const SigSpec &secondSig = it->second; + const std::string firstName = cell_name + "." + make_id(it->first); + const std::string secondName = make_expr(secondSig); + // Find the direction for this port. + FDirection dir = getPortFDirection(it->first, instModule); + std::string source, sink; + switch (dir) { + case INOUT: + log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", log_id(cell_type), log_signal(it->second)); + case OUT: + source = firstName; + sink = secondName; + break; + case NODIRECTION: + log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", log_id(cell_type), log_signal(it->second)); + /* FALL_THROUGH */ + case IN: + source = secondName; + sink = firstName; + break; + default: + log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", log_id(cell_type), log_signal(it->second), dir); + break; + } + wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sink.c_str(), source.c_str())); + } + } + wire_exprs.push_back(stringf("\n")); + + } + void run() { f << stringf(" module %s:\n", make_id(module->name)); @@ -142,21 +234,28 @@ struct FirrtlWorker for (auto wire : module->wires()) { + const auto wireName = make_id(wire->name); if (wire->port_id) { 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", - make_id(wire->name), wire->width)); + wireName, wire->width)); } else { - wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", make_id(wire->name), wire->width)); + wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", wireName, wire->width)); } } for (auto cell : module->cells()) { + // Is this cell is a module instance? + if (cell->type[0] != '$') + { + process_instance(cell, wire_exprs); + continue; + } if (cell->type.in("$not", "$logic_not", "$neg", "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_bool", "$reduce_xnor")) { string y_id = make_id(cell->name); @@ -169,7 +268,10 @@ struct FirrtlWorker a_expr = "asSInt(" + a_expr + ")"; } - a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + // Don't use the results of logical operations (a single bit) to control padding + if (!(cell->type.in("$eq", "$eqx", "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$reduce_bool", "$logic_not") && y_width == 1) ) { + a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + } string primop; bool always_uint = false; @@ -187,9 +289,12 @@ struct FirrtlWorker a_expr = stringf("xorr(%s)", a_expr.c_str()); } if (cell->type == "$reduce_bool") { - primop = "neq"; - a_expr = stringf("%s, UInt(0)", a_expr.c_str()); - } + primop = "neq"; + // Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand. + bool a_signed = cell->parameters.at("\\A_SIGNED").as_bool(); + int a_width = cell->parameters.at("\\A_WIDTH").as_int(); + a_expr = stringf("%s, %cInt<%d>(0)", a_expr.c_str(), a_signed ? 'S' : 'U', a_width); + } string expr = stringf("%s(%s)", primop.c_str(), a_expr.c_str()); @@ -215,16 +320,16 @@ struct FirrtlWorker if (cell->parameters.at("\\A_SIGNED").as_bool()) { a_expr = "asSInt(" + a_expr + ")"; } - if (cell->parameters.at("\\A_SIGNED").as_bool() & (cell->type != "$shr")) { - b_expr = "asSInt(" + b_expr + ")"; + // Shift amount is always unsigned, and needn't be padded to result width. + if (!cell->type.in("$shr", "$sshr", "$shl", "$sshl")) { + if (cell->parameters.at("\\B_SIGNED").as_bool()) { + b_expr = "asSInt(" + b_expr + ")"; + } + b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); } a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); - if ((cell->type != "$shl") && (cell->type != "$sshl")) { - b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); - } - if (cell->parameters.at("\\A_SIGNED").as_bool() & (cell->type == "$shr")) { a_expr = "asUInt(" + a_expr + ")"; } @@ -494,14 +599,14 @@ struct FirrtlWorker if (is_valid) { if (make_unconn_id) { wire_decls.push_back(stringf(" wire %s: UInt<1>\n", unconn_id.c_str())); - cell_exprs.push_back(stringf(" %s is invalid\n", unconn_id.c_str())); + 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())); } else { if (make_unconn_id) { unconn_id.clear(); } - wire_exprs.push_back(stringf(" %s is invalid\n", make_id(wire->name))); + wire_decls.push_back(stringf(" %s is invalid\n", make_id(wire->name))); } } @@ -570,7 +675,7 @@ struct FirrtlBackend : public Backend { for (auto module : design->modules()) { - FirrtlWorker worker(module, *f); + FirrtlWorker worker(module, *f, design); worker.run(); } -- 2.30.2