Add support for module instances.
authorJim Lawson <ucbjrl@berkeley.edu>
Thu, 23 Aug 2018 21:35:11 +0000 (14:35 -0700)
committerJim Lawson <ucbjrl@berkeley.edu>
Thu, 23 Aug 2018 21:35:11 +0000 (14:35 -0700)
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

index 4f56f498ce4fd229708fd9913f50d2e263d429bd..14fae1a83dd37d7575fb809d898fcb3269586599 100644 (file)
 USING_YOSYS_NAMESPACE
 PRIVATE_NAMESPACE_BEGIN
 
+bool defparam, noattr;
 pool<string> used_names;
 dict<IdString, string> 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<SigBit, pair<string, int>> 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<string> &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();
                }