Move interface expansion in hierarchy.cc into a helper class
authorRupert Swarbrick <rswarbrick@gmail.com>
Tue, 26 May 2020 16:46:10 +0000 (17:46 +0100)
committerZachary Snow <zachary.j.snow@gmail.com>
Thu, 17 Jun 2021 01:48:18 +0000 (21:48 -0400)
There should be no functional change, but this splits up the control
flow across functions, using class fields to hold the state that's
being tracked. The result should be a bit easier to read.

This is part of work to add bind support, but I'm doing some
refactoring in the hierarchy pass to make the code a bit easier to
work with. The idea is that (eventually) the IFExpander object will
hold all the logic for expanding interfaces, and then other code can
do bind insertion.

passes/hierarchy/hierarchy.cc

index 2ea0d4061d9d4f02d4958fd6b58d7c85287d3f10..dadae04a46db4c565f33d5726d7f4d93ec922125 100644 (file)
@@ -156,6 +156,168 @@ std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) {
        return basicType;
 }
 
+// A helper struct for expanding a module's interface connections in expand_module
+struct IFExpander
+{
+       IFExpander (RTLIL::Design &design, RTLIL::Module &m)
+               : module(m), has_interfaces_not_found(false)
+       {
+               // Keep track of all derived interfaces available in the current
+               // module in 'interfaces_in_module':
+               for (auto cell : module.cells()) {
+                       if(!cell->get_bool_attribute(ID::is_interface))
+                               continue;
+
+                       interfaces_in_module[cell->name] = design.module(cell->type);
+               }
+       }
+
+       RTLIL::Module                          &module;
+       dict<RTLIL::IdString, RTLIL::Module*>   interfaces_in_module;
+
+       bool                                    has_interfaces_not_found;
+       std::vector<RTLIL::IdString>            connections_to_remove;
+       std::vector<RTLIL::IdString>            connections_to_add_name;
+       std::vector<RTLIL::SigSpec>             connections_to_add_signal;
+       dict<RTLIL::IdString, RTLIL::Module*>   interfaces_to_add_to_submodule;
+       dict<RTLIL::IdString, RTLIL::IdString>  modports_used_in_submodule;
+
+       // Reset the per-cell state
+       void start_cell()
+       {
+               has_interfaces_not_found = false;
+               connections_to_remove.clear();
+               connections_to_add_name.clear();
+               connections_to_add_signal.clear();
+               interfaces_to_add_to_submodule.clear();
+               modports_used_in_submodule.clear();
+       }
+
+       // Set has_interfaces_not_found if there are pending interfaces that
+       // haven't been found yet (and might be found in the future). Print a
+       // warning if we've already gone over all the cells in the module.
+       void on_missing_interface(RTLIL::IdString interface_name)
+       {
+               // If there are cells that haven't yet been processed, maybe
+               // we'll find this interface in the future.
+               if (module.get_bool_attribute(ID::cells_not_processed)) {
+                       has_interfaces_not_found = true;
+                       return;
+               }
+
+               // Otherwise, we have already gone over all cells in this
+               // module and the interface has still not been found. Warn
+               // about it and don't set has_interfaces_not_found (to avoid a
+               // loop).
+               log_warning("Could not find interface instance for `%s' in `%s'\n",
+                           log_id(interface_name), log_id(&module));
+       }
+
+       // Handle an interface connection from the module
+       void on_interface(RTLIL::Module        &submodule,
+                         RTLIL::IdString       conn_name,
+                         const RTLIL::SigSpec &conn_signals)
+       {
+               // Check if the connected wire is a potential interface in the parent module
+               std::string interface_name_str = conn_signals.bits()[0].wire->name.str();
+               // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name
+               interface_name_str.replace(0,23,"");
+               interface_name_str = "\\" + interface_name_str;
+               RTLIL::IdString interface_name = interface_name_str;
+
+               // If 'interfaces' in the cell have not be been handled yet, we aren't
+               // ready to derive the sub-module either
+               if (!module.get_bool_attribute(ID::interfaces_replaced_in_module)) {
+                       on_missing_interface(interface_name);
+                       return;
+               }
+
+               // Check if the interface instance is present in module. Interface
+               // instances may either have the plain name or the name appended with
+               // '_inst_from_top_dummy'. Check for both of them here
+               int nexactmatch = interfaces_in_module.count(interface_name) > 0;
+               std::string interface_name_str2 =  interface_name_str + "_inst_from_top_dummy";
+               RTLIL::IdString interface_name2 = interface_name_str2;
+               int nmatch2 = interfaces_in_module.count(interface_name2) > 0;
+
+               // If we can't find either name, this is a missing interface.
+               if (! (nexactmatch || nmatch2)) {
+                       on_missing_interface(interface_name);
+                       return;
+               }
+
+               if (nexactmatch != 0) // Choose the one with the plain name if it exists
+                       interface_name2 = interface_name;
+
+               RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2);
+
+               // Go over all wires in interface, and add replacements to lists.
+               for (auto mod_wire : mod_replace_ports->wires()) {
+                       std::string signal_name1 = conn_name.str() + "." + log_id(mod_wire->name);
+                       std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire);
+                       connections_to_add_name.push_back(RTLIL::IdString(signal_name1));
+                       if(module.wire(signal_name2) == nullptr) {
+                               log_error("Could not find signal '%s' in '%s'\n",
+                                         signal_name2.c_str(), log_id(module.name));
+                       }
+                       else {
+                               RTLIL::Wire *wire_in_parent = module.wire(signal_name2);
+                               connections_to_add_signal.push_back(wire_in_parent);
+                       }
+               }
+               connections_to_remove.push_back(conn_name);
+               interfaces_to_add_to_submodule[conn_name] = interfaces_in_module.at(interface_name2);
+
+               // Find if the sub-module has set a modport for the current interface
+               // connection. Add any modports to a dict which will be passed to
+               // AstModule::derive
+               string modport_name = submodule.wire(conn_name)->get_string_attribute(ID::interface_modport);
+               if (!modport_name.empty()) {
+                       modports_used_in_submodule[conn_name] = "\\" + modport_name;
+               }
+       }
+
+       // Handle a single connection from the module, making a note to expand
+       // it if it's an interface connection.
+       void on_connection(RTLIL::Module        &submodule,
+                          RTLIL::IdString       conn_name,
+                          const RTLIL::SigSpec &conn_signals)
+       {
+               // Check if the connection is present as an interface in the sub-module's port list
+               const RTLIL::Wire *wire = submodule.wire(conn_name);
+               if (!wire || !wire->get_bool_attribute(ID::is_interface))
+                       return;
+
+               // If the connection looks like an interface, handle it.
+               const auto &bits = conn_signals.bits();
+               if (bits.size() == 1 && bits[0].wire->get_bool_attribute(ID::is_interface))
+                       on_interface(submodule, conn_name, conn_signals);
+       }
+
+       // Iterate over the connections in a cell, tracking any interface
+       // connections
+       void visit_connections(const RTLIL::Cell &cell,
+                              RTLIL::Module     &submodule)
+       {
+               for (const auto &conn : cell.connections()) {
+                       on_connection(submodule, conn.first, conn.second);
+               }
+       }
+
+       // Add/remove connections to the cell as necessary, replacing any SV
+       // interface port connection with the individual signal connections.
+       void rewrite_interface_connections(RTLIL::Cell &cell) const
+       {
+               for(unsigned int i=0;i<connections_to_add_name.size();i++) {
+                       cell.connections_[connections_to_add_name[i]] = connections_to_add_signal[i];
+               }
+               // Remove the connection for the interface itself:
+               for(unsigned int i=0;i<connections_to_remove.size();i++) {
+                       cell.connections_.erase(connections_to_remove[i]);
+               }
+       }
+};
+
 bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs)
 {
        bool did_something = false;
@@ -173,23 +335,11 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
                }
        }
 
-       // Always keep track of all derived interfaces available in the current module in 'interfaces_in_module':
-       dict<RTLIL::IdString, RTLIL::Module*> interfaces_in_module;
-       for (auto cell : module->cells())
-       {
-               if(cell->get_bool_attribute(ID::is_interface)) {
-                       RTLIL::Module *intf_module = design->module(cell->type);
-                       interfaces_in_module[cell->name] = intf_module;
-               }
-       }
+       IFExpander if_expander(*design, *module);
 
        for (auto cell : module->cells())
        {
-               bool has_interfaces_not_found = false;
-
-               std::vector<RTLIL::IdString> connections_to_remove;
-               std::vector<RTLIL::IdString> connections_to_add_name;
-               std::vector<RTLIL::SigSpec> connections_to_add_signal;
+               if_expander.start_cell();
 
                if (cell->type.begins_with("$array:")) {
                        int pos[3];
@@ -202,10 +352,9 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
                        array_cells[cell] = std::pair<int, int>(idx, num);
                        cell->type = cell->type.substr(pos_type + 1);
                }
-               dict<RTLIL::IdString, RTLIL::Module*> interfaces_to_add_to_submodule;
-               dict<RTLIL::IdString, RTLIL::IdString> modports_used_in_submodule;
 
-               if (design->module(cell->type) == nullptr)
+               RTLIL::Module *mod = design->module(cell->type);
+               if (mod == nullptr)
                {
                        if (design->module("$abstract" + cell->type.str()) != nullptr)
                        {
@@ -243,77 +392,15 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
                        continue;
 
                loaded_module:
-                       if (design->module(cell->type) == nullptr)
+                       mod = design->module(cell->type);
+                       if (mod == nullptr)
                                log_error("File `%s' from libdir does not declare module `%s'.\n", filename.c_str(), cell->type.c_str());
                        did_something = true;
                } else {
 
-               RTLIL::Module *mod = design->module(cell->type);
-
-               // Go over all connections and see if any of them are SV interfaces. If they are, then add the replacements to
-               // some lists, so that the ports for sub-modules can be replaced further down:
-               for (auto &conn : cell->connections()) {
-                       if(mod->wire(conn.first) != nullptr && mod->wire(conn.first)->get_bool_attribute(ID::is_interface)) { // Check if the connection is present as an interface in the sub-module's port list
-                               if(conn.second.bits().size() == 1 && conn.second.bits()[0].wire->get_bool_attribute(ID::is_interface)) { // Check if the connected wire is a potential interface in the parent module
-                                       std::string interface_name_str = conn.second.bits()[0].wire->name.str();
-                                       interface_name_str.replace(0,23,""); // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name
-                                       interface_name_str = "\\" + interface_name_str;
-                                       RTLIL::IdString interface_name = interface_name_str;
-                                       bool not_found_interface = false;
-                                       if(module->get_bool_attribute(ID::interfaces_replaced_in_module)) { // If 'interfaces' in the cell have not be been handled yet, there is no need to derive the sub-module either
-                                               // Check if the interface instance is present in module:
-                                               // Interface instances may either have the plain name or the name appended with '_inst_from_top_dummy'.
-                                               // Check for both of them here
-                                               int nexactmatch = interfaces_in_module.count(interface_name) > 0;
-                                               std::string interface_name_str2 =  interface_name_str + "_inst_from_top_dummy";
-                                               RTLIL::IdString interface_name2 = interface_name_str2;
-                                               int nmatch2 = interfaces_in_module.count(interface_name2) > 0;
-                                               if (nexactmatch > 0 || nmatch2 > 0) {
-                                                       if (nexactmatch != 0) // Choose the one with the plain name if it exists
-                                                               interface_name2 = interface_name;
-                                                       RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2);
-                                                       for (auto mod_wire : mod_replace_ports->wires()) { // Go over all wires in interface, and add replacements to lists.
-                                                               std::string signal_name1 = conn.first.str() + "." + log_id(mod_wire->name);
-                                                               std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire);
-                                                               connections_to_add_name.push_back(RTLIL::IdString(signal_name1));
-                                                               if(module->wire(signal_name2) == nullptr) {
-                                                                       log_error("Could not find signal '%s' in '%s'\n", signal_name2.c_str(), log_id(module->name));
-                                                               }
-                                                               else {
-                                                                       RTLIL::Wire *wire_in_parent = module->wire(signal_name2);
-                                                                       connections_to_add_signal.push_back(wire_in_parent);
-                                                               }
-                                                       }
-                                                       connections_to_remove.push_back(conn.first);
-                                                       interfaces_to_add_to_submodule[conn.first] = interfaces_in_module.at(interface_name2);
-
-                                                       // Find if the sub-module has set a modport for the current
-                                                       // interface connection. Add any modports to a dict which will
-                                                       // be passed to AstModule::derive
-                                                       string modport_name = mod->wire(conn.first)->get_string_attribute(ID::interface_modport);
-                                                       if (!modport_name.empty()) {
-                                                               modports_used_in_submodule[conn.first] = "\\" + modport_name;
-                                                       }
-                                               }
-                                               else not_found_interface = true;
-                                       }
-                                       else not_found_interface = true;
-                                       // If the interface instance has not already been derived in the module, we cannot complete at this stage. Set "has_interfaces_not_found"
-                                       // which will delay the expansion of this cell:
-                                       if (not_found_interface) {
-                                               // If we have already gone over all cells in this module, and the interface has still not been found - flag it as an error:
-                                               if(!(module->get_bool_attribute(ID::cells_not_processed))) {
-                                                       log_warning("Could not find interface instance for `%s' in `%s'\n", log_id(interface_name), log_id(module));
-                                               }
-                                               else {
-                                                       // Only set has_interfaces_not_found if it would be possible to find them, since otherwiser we will end up in an infinite loop:
-                                                       has_interfaces_not_found = true;
-                                               }
-                                       }
-                               }
-                       }
-               }
-               //
+               // Go over all connections and check if any of them are SV
+               // interfaces.
+               if_expander.visit_connections(*cell, *mod);
 
                if (flag_check || flag_simcheck)
                {
@@ -340,9 +427,13 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
 
                }
                }
-               RTLIL::Module *mod = design->module(cell->type);
 
-               if (design->module(cell->type)->get_blackbox_attribute()) {
+               // If we make it out of the if/else block above without leaving
+               // this iteration, mod will equal design->module(cell->type) and
+               // will be non-null.
+               log_assert(mod);
+
+               if (mod->get_blackbox_attribute()) {
                        if (flag_simcheck)
                                log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox/whitebox module.\n",
                                                cell->type.c_str(), module->name.c_str(), cell->name.c_str());
@@ -350,23 +441,18 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
                }
 
                // If interface instances not yet found, skip cell for now, and say we did something, so that we will return back here:
-               if(has_interfaces_not_found) {
+               if(if_expander.has_interfaces_not_found) {
                        did_something = true; // waiting for interfaces to be handled
                        continue;
                }
 
-               // Do the actual replacements of the SV interface port connection with the individual signal connections:
-               for(unsigned int i=0;i<connections_to_add_name.size();i++) {
-                       cell->connections_[connections_to_add_name[i]] = connections_to_add_signal[i];
-               }
-               // Remove the connection for the interface itself:
-               for(unsigned int i=0;i<connections_to_remove.size();i++) {
-                       cell->connections_.erase(connections_to_remove[i]);
-               }
+               if_expander.rewrite_interface_connections(*cell);
 
                // If there are no overridden parameters AND not interfaces, then we can use the existing module instance as the type
                // for the cell:
-               if (cell->parameters.size() == 0 && (interfaces_to_add_to_submodule.size() == 0 || !(cell->get_bool_attribute(ID::module_not_derived)))) {
+               if (cell->parameters.size() == 0 &&
+                   (if_expander.interfaces_to_add_to_submodule.size() == 0 ||
+                    !(cell->get_bool_attribute(ID::module_not_derived)))) {
                        // If the cell being processed is an the interface instance itself, go down to "handle_interface_instance:",
                        // so that the signals of the interface are added to the parent module.
                        if (mod->get_bool_attribute(ID::is_interface)) {
@@ -375,7 +461,10 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
                        continue;
                }
 
-               cell->type = mod->derive(design, cell->parameters, interfaces_to_add_to_submodule, modports_used_in_submodule);
+               cell->type = mod->derive(design,
+                                        cell->parameters,
+                                        if_expander.interfaces_to_add_to_submodule,
+                                        if_expander.modports_used_in_submodule);
                cell->parameters.clear();
                did_something = true;
 
@@ -386,7 +475,7 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
                        if (mod->get_bool_attribute(ID::is_interface) && cell->get_bool_attribute(ID::module_not_derived)) {
                                cell->set_bool_attribute(ID::is_interface);
                                RTLIL::Module *derived_module = design->module(cell->type);
-                               interfaces_in_module[cell->name] = derived_module;
+                               if_expander.interfaces_in_module[cell->name] = derived_module;
                                did_something = true;
                        }
                // We clear 'module_not_derived' such that we will not rederive the cell again (needed when there are interfaces connected to the cell)
@@ -399,8 +488,8 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
 
 
        // If any interface instances or interface ports were found in the module, we need to rederive it completely:
-       if ((interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute(ID::interfaces_replaced_in_module)) {
-               module->reprocess_module(design, interfaces_in_module);
+       if ((if_expander.interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute(ID::interfaces_replaced_in_module)) {
+               module->reprocess_module(design, if_expander.interfaces_in_module);
                return did_something;
        }