Bugfix in hierarchy blackbox module port width handling
[yosys.git] / passes / hierarchy / hierarchy.cc
index 50d0e6e47db78ca8f1e8d374d3be703cb23ca1d5..898763c647c7a3c89d1f65187d2189d3a213e277 100644 (file)
@@ -2,11 +2,11 @@
  *  yosys -- Yosys Open SYnthesis Suite
  *
  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
- *  
+ *
  *  Permission to use, copy, modify, and/or distribute this software for any
  *  purpose with or without fee is hereby granted, provided that the above
  *  copyright notice and this permission notice appear in all copies.
- *  
+ *
  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  *
  */
 
-#include "kernel/register.h"
-#include "kernel/log.h"
+#include "kernel/yosys.h"
 #include <stdlib.h>
 #include <stdio.h>
-#include <fnmatch.h>
 #include <set>
-#include <unistd.h>
-
-namespace {
-       struct generate_port_decl_t {
-               bool input, output;
-               std::string portname;
-               int index;
-       };
-}
 
-static void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, const std::vector<generate_port_decl_t> &portdecls)
+#ifndef _WIN32
+#  include <unistd.h>
+#endif
+
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct generate_port_decl_t {
+       bool input, output;
+       string portname;
+       int index;
+};
+
+void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, const std::vector<generate_port_decl_t> &portdecls)
 {
-       std::set<std::string> found_celltypes;
+       std::set<RTLIL::IdString> found_celltypes;
 
-       for (auto i1 : design->modules)
-       for (auto i2 : i1.second->cells)
+       for (auto i1 : design->modules_)
+       for (auto i2 : i1.second->cells_)
        {
                RTLIL::Cell *cell = i2.second;
-               if (cell->type[0] == '$' || design->modules.count(cell->type) > 0)
+               if (design->has(cell->type))
+                       continue;
+               if (cell->type.substr(0, 1) == "$" && cell->type.substr(0, 3) != "$__")
                        continue;
                for (auto &pattern : celltypes)
-                       if (!fnmatch(pattern.c_str(), RTLIL::unescape_id(cell->type).c_str(), FNM_NOESCAPE))
+                       if (patmatch(pattern.c_str(), RTLIL::unescape_id(cell->type).c_str()))
                                found_celltypes.insert(cell->type);
        }
 
        for (auto &celltype : found_celltypes)
        {
-               std::set<std::string> portnames;
-               std::set<std::string> parameters;
-               std::map<std::string, int> portwidths;
+               std::set<RTLIL::IdString> portnames;
+               std::set<RTLIL::IdString> parameters;
+               std::map<RTLIL::IdString, int> portwidths;
                log("Generate module for cell type %s:\n", celltype.c_str());
 
-               for (auto i1 : design->modules)
-               for (auto i2 : i1.second->cells)
+               for (auto i1 : design->modules_)
+               for (auto i2 : i1.second->cells_)
                        if (i2.second->type == celltype) {
-                               for (auto &conn : i2.second->connections) {
+                               for (auto &conn : i2.second->connections()) {
                                        if (conn.first[0] != '$')
                                                portnames.insert(conn.first);
-                                       portwidths[conn.first] = std::max(portwidths[conn.first], conn.second.width);
+                                       portwidths[conn.first] = max(portwidths[conn.first], conn.second.size());
                                }
                                for (auto &para : i2.second->parameters)
                                        parameters.insert(para.first);
@@ -79,8 +84,8 @@ static void generate(RTLIL::Design *design, const std::vector<std::string> &cell
 
                for (auto &decl : portdecls)
                        if (decl.index > 0) {
-                               portwidths[decl.portname] = std::max(portwidths[decl.portname], 1);
-                               portwidths[decl.portname] = std::max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]);
+                               portwidths[decl.portname] = max(portwidths[decl.portname], 1);
+                               portwidths[decl.portname] = max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]);
                                log("  port %d: %s [%d:0] %s\n", decl.index, decl.input ? decl.output ? "inout" : "input" : "output", portwidths[decl.portname]-1, RTLIL::id2cstr(decl.portname));
                                if (indices.count(decl.index) > ports.size())
                                        log_error("Port index (%d) exceeds number of found ports (%d).\n", decl.index, int(ports.size()));
@@ -92,16 +97,16 @@ static void generate(RTLIL::Design *design, const std::vector<std::string> &cell
                        }
 
                while (portnames.size() > 0) {
-                       std::string portname = *portnames.begin();
+                       RTLIL::IdString portname = *portnames.begin();
                        for (auto &decl : portdecls)
-                               if (decl.index == 0 && !fnmatch(decl.portname.c_str(), RTLIL::unescape_id(portname).c_str(), FNM_NOESCAPE)) {
+                               if (decl.index == 0 && patmatch(decl.portname.c_str(), RTLIL::unescape_id(portname).c_str())) {
                                        generate_port_decl_t d = decl;
-                                       d.portname = portname;
+                                       d.portname = portname.str();
                                        d.index = *indices.begin();
-                                       assert(!indices.empty());
+                                       log_assert(!indices.empty());
                                        indices.erase(d.index);
                                        ports[d.index-1] = d;
-                                       portwidths[d.portname] = std::max(portwidths[d.portname], 1);
+                                       portwidths[d.portname] = max(portwidths[d.portname], 1);
                                        log("  port %d: %s [%d:0] %s\n", d.index, d.input ? d.output ? "inout" : "input" : "output", portwidths[d.portname]-1, RTLIL::id2cstr(d.portname));
                                        goto found_matching_decl;
                                }
@@ -110,23 +115,22 @@ static void generate(RTLIL::Design *design, const std::vector<std::string> &cell
                        portnames.erase(portname);
                }
 
-               assert(indices.empty());
+               log_assert(indices.empty());
 
                RTLIL::Module *mod = new RTLIL::Module;
                mod->name = celltype;
                mod->attributes["\\blackbox"] = RTLIL::Const(1);
-               design->modules[mod->name] = mod;
+               design->add(mod);
 
                for (auto &decl : ports) {
-                       RTLIL::Wire *wire = new RTLIL::Wire;
-                       wire->name = decl.portname;
-                       wire->width = portwidths.at(decl.portname);
+                       RTLIL::Wire *wire = mod->addWire(decl.portname, portwidths.at(decl.portname));
                        wire->port_id = decl.index;
                        wire->port_input = decl.input;
                        wire->port_output = decl.output;
-                       mod->add(wire);
                }
 
+               mod->fixup_ports();
+
                for (auto &para : parameters)
                        log("  ignoring parameter %s.\n", RTLIL::id2cstr(para));
 
@@ -134,34 +138,49 @@ static void generate(RTLIL::Design *design, const std::vector<std::string> &cell
        }
 }
 
-static bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, std::vector<std::string> &libdirs)
+bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, std::vector<std::string> &libdirs)
 {
        bool did_something = false;
+       std::map<RTLIL::Cell*, std::pair<int, int>> array_cells;
        std::string filename;
 
-       for (auto &cell_it : module->cells)
+       for (auto &cell_it : module->cells_)
        {
                RTLIL::Cell *cell = cell_it.second;
 
-               if (design->modules.count(cell->type) == 0)
+               if (cell->type.substr(0, 7) == "$array:") {
+                       int pos_idx = cell->type.str().find_first_of(':');
+                       int pos_num = cell->type.str().find_first_of(':', pos_idx + 1);
+                       int pos_type = cell->type.str().find_first_of(':', pos_num + 1);
+                       int idx = atoi(cell->type.str().substr(pos_idx + 1, pos_num).c_str());
+                       int num = atoi(cell->type.str().substr(pos_num + 1, pos_type).c_str());
+                       array_cells[cell] = std::pair<int, int>(idx, num);
+                       cell->type = cell->type.str().substr(pos_type + 1);
+               }
+
+               if (design->modules_.count(cell->type) == 0)
                {
+                       if (design->modules_.count("$abstract" + cell->type.str()))
+                       {
+                               cell->type = design->modules_.at("$abstract" + cell->type.str())->derive(design, cell->parameters);
+                               cell->parameters.clear();
+                               did_something = true;
+                               continue;
+                       }
+
                        if (cell->type[0] == '$')
                                continue;
 
                        for (auto &dir : libdirs)
                        {
                                filename = dir + "/" + RTLIL::unescape_id(cell->type) + ".v";
-                               if (access(filename.c_str(), F_OK) == 0) {
-                                       std::vector<std::string> args;
-                                       args.push_back(filename);
+                               if (check_file_exists(filename)) {
                                        Frontend::frontend_call(design, NULL, filename, "verilog");
                                        goto loaded_module;
                                }
 
                                filename = dir + "/" + RTLIL::unescape_id(cell->type) + ".il";
-                               if (access(filename.c_str(), F_OK) == 0) {
-                                       std::vector<std::string> args;
-                                       args.push_back(filename);
+                               if (check_file_exists(filename)) {
                                        Frontend::frontend_call(design, NULL, filename, "ilang");
                                        goto loaded_module;
                                }
@@ -173,62 +192,144 @@ static bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool fla
                        continue;
 
                loaded_module:
-                       if (design->modules.count(cell->type) == 0)
+                       if (design->modules_.count(cell->type) == 0)
                                log_error("File `%s' from libdir does not declare module `%s'.\n", filename.c_str(), cell->type.c_str());
                        did_something = true;
+               } else
+               if (flag_check)
+               {
+                       RTLIL::Module *mod = design->module(cell->type);
+                       for (auto &conn : cell->connections())
+                               if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') {
+                                       int id = atoi(conn.first.c_str()+1);
+                                       if (id <= 0 || id > GetSize(mod->ports))
+                                               log_error("Module `%s' referenced in module `%s' in cell `%s' has only %d ports, requested port %d.\n",
+                                                               log_id(cell->type), log_id(module), log_id(cell), GetSize(mod->ports), id);
+                               } else if (mod->wire(conn.first) == nullptr || mod->wire(conn.first)->port_id == 0)
+                                       log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a port named '%s'.\n",
+                                                       log_id(cell->type), log_id(module), log_id(cell), log_id(conn.first));
+                       for (auto &param : cell->parameters)
+                               if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$' && strchr(param.first.c_str(), '.') == NULL)
+                                       log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a parameter named '%s'.\n",
+                                                       log_id(cell->type), log_id(module), log_id(cell), log_id(param.first));
                }
 
                if (cell->parameters.size() == 0)
                        continue;
 
-               if (design->modules.at(cell->type)->get_bool_attribute("\\blackbox"))
+               if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox"))
                        continue;
 
-               RTLIL::Module *mod = design->modules[cell->type];
+               RTLIL::Module *mod = design->modules_[cell->type];
                cell->type = mod->derive(design, cell->parameters);
                cell->parameters.clear();
                did_something = true;
        }
 
+       for (auto &it : array_cells)
+       {
+               RTLIL::Cell *cell = it.first;
+               int idx = it.second.first, num = it.second.second;
+
+               if (design->modules_.count(cell->type) == 0)
+                       log_error("Array cell `%s.%s' of unknown type `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+
+               RTLIL::Module *mod = design->modules_[cell->type];
+
+               for (auto &conn : cell->connections_) {
+                       int conn_size = conn.second.size();
+                       RTLIL::IdString portname = conn.first;
+                       if (portname.substr(0, 1) == "$") {
+                               int port_id = atoi(portname.substr(1).c_str());
+                               for (auto &wire_it : mod->wires_)
+                                       if (wire_it.second->port_id == port_id) {
+                                               portname = wire_it.first;
+                                               break;
+                                       }
+                       }
+                       if (mod->wires_.count(portname) == 0)
+                               log_error("Array cell `%s.%s' connects to unknown port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first));
+                       int port_size = mod->wires_.at(portname)->width;
+                       if (conn_size == port_size)
+                               continue;
+                       if (conn_size != port_size*num)
+                               log_error("Array cell `%s.%s' has invalid port vs. signal size for port `%s'.\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(conn.first));
+                       conn.second = conn.second.extract(port_size*idx, port_size);
+               }
+       }
+
        return did_something;
 }
 
-static void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*> &used, RTLIL::Module *mod, int indent)
+void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> &used, RTLIL::Module *mod, int indent)
 {
        if (used.count(mod) > 0)
                return;
 
        if (indent == 0)
                log("Top module:  %s\n", mod->name.c_str());
-       else
+       else if (!mod->get_bool_attribute("\\blackbox"))
                log("Used module: %*s%s\n", indent, "", mod->name.c_str());
        used.insert(mod);
 
-       for (auto &it : mod->cells) {
-               if (design->modules.count(it.second->type) > 0)
-                       hierarchy_worker(design, used, design->modules[it.second->type], indent+4);
+       for (auto cell : mod->cells()) {
+               std::string celltype = cell->type.str();
+               if (celltype.substr(0, 7) == "$array:") {
+                       int pos_idx = celltype.find_first_of(':');
+                       int pos_num = celltype.find_first_of(':', pos_idx + 1);
+                       int pos_type = celltype.find_first_of(':', pos_num + 1);
+                       celltype = celltype.substr(pos_type + 1);
+               }
+               if (design->module(celltype))
+                       hierarchy_worker(design, used, design->module(celltype), indent+4);
        }
 }
 
-static void hierarchy(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib)
+void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib)
 {
-       std::set<RTLIL::Module*> used;
+       std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> used;
        hierarchy_worker(design, used, top, 0);
 
        std::vector<RTLIL::Module*> del_modules;
-       for (auto &it : design->modules)
+       for (auto &it : design->modules_)
                if (used.count(it.second) == 0)
                        del_modules.push_back(it.second);
 
+       int del_counter = 0;
        for (auto mod : del_modules) {
                if (!purge_lib && mod->get_bool_attribute("\\blackbox"))
                        continue;
                log("Removing unused module `%s'.\n", mod->name.c_str());
-               design->modules.erase(mod->name);
+               design->modules_.erase(mod->name);
+               del_counter++;
                delete mod;
        }
 
-       log("Removed %zd unused modules.\n", del_modules.size());
+       log("Removed %d unused modules.\n", del_counter);
+}
+
+bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod)
+{
+       if (cache.count(mod) == 0)
+               for (auto c : mod->cells()) {
+                       RTLIL::Module *m = mod->design->module(c->type);
+                       if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in("$assert", "$assume", "$live", "$fair", "$cover"))
+                               return cache[mod] = true;
+               }
+       return cache[mod];
+}
+
+int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db)
+{
+       if (db.count(module) == 0) {
+               int score = 0;
+               db[module] = 0;
+               for (auto cell : module->cells())
+                       if (design->module(cell->type))
+                               score = max(score, find_top_mod_score(design, design->module(cell->type), db) + 1);
+               db[module] = score;
+       }
+       return db.at(module);
 }
 
 struct HierarchyPass : public Pass {
@@ -240,7 +341,7 @@ struct HierarchyPass : public Pass {
                log("    hierarchy [-check] [-top <module>]\n");
                log("    hierarchy -generate <cell-types> <port-decls>\n");
                log("\n");
-               log("In parametric designs, a module might exists in serveral variations with\n");
+               log("In parametric designs, a module might exists in several variations with\n");
                log("different parameter values. This pass looks at all modules in the current\n");
                log("design an re-runs the language frontends for the parametric modules as\n");
                log("needed.\n");
@@ -251,17 +352,27 @@ struct HierarchyPass : public Pass {
                log("\n");
                log("    -purge_lib\n");
                log("        by default the hierarchy command will not remove library (blackbox)\n");
-               log("        module. use this options to also remove unused blackbox modules.\n");
+               log("        modules. use this option to also remove unused blackbox modules.\n");
                log("\n");
                log("    -libdir <directory>\n");
                log("        search for files named <module_name>.v in the specified directory\n");
-               log("        for unkown modules and automatically run read_verilog for each\n");
+               log("        for unknown modules and automatically run read_verilog for each\n");
                log("        unknown module.\n");
                log("\n");
                log("    -keep_positionals\n");
                log("        per default this pass also converts positional arguments in cells\n");
                log("        to arguments using port names. this option disables this behavior.\n");
                log("\n");
+               log("    -keep_portwidths\n");
+               log("        per default this pass adjusts the port width on cells that are\n");
+               log("        module instances when the width does not match the module port. this\n");
+               log("        option disables this behavior.\n");
+               log("\n");
+               log("    -nokeep_asserts\n");
+               log("        per default this pass sets the \"keep\" attribute on all modules\n");
+               log("        that directly or indirectly contain one or more $assert cells. this\n");
+               log("        option disables this behavior.\n");
+               log("\n");
                log("    -top <module>\n");
                log("        use the specified top module to built a design hierarchy. modules\n");
                log("        outside this tree (unused modules) are removed.\n");
@@ -270,6 +381,9 @@ struct HierarchyPass : public Pass {
                log("        specified top module. otherwise a module with the 'top' attribute set\n");
                log("        will implicitly be used as top module, if such a module exists.\n");
                log("\n");
+               log("    -auto-top\n");
+               log("        automatically determine the top of the design hierarchy and mark it.\n");
+               log("\n");
                log("In -generate mode this pass generates blackbox modules for the given cell\n");
                log("types (wildcards supported). For this the design is searched for cells that\n");
                log("match the given types and then the given port declarations are used to\n");
@@ -279,7 +393,7 @@ struct HierarchyPass : public Pass {
                log("\n");
                log("Input ports are specified with the 'i' prefix, output ports with the 'o'\n");
                log("prefix and inout ports with the 'io' prefix. The optional <num> specifies\n");
-               log("the position of the port in the parameter list (needed when instanciated\n");
+               log("the position of the port in the parameter list (needed when instantiated\n");
                log("using positional arguments). When <num> is not specified, the <portname> can\n");
                log("also contain wildcard characters.\n");
                log("\n");
@@ -289,15 +403,18 @@ struct HierarchyPass : public Pass {
        }
        virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
        {
-               log_header("Executing HIERARCHY pass (managing design hierarchy).\n");
+               log_header(design, "Executing HIERARCHY pass (managing design hierarchy).\n");
 
                bool flag_check = false;
                bool purge_lib = false;
                RTLIL::Module *top_mod = NULL;
                std::vector<std::string> libdirs;
 
+               bool auto_top_mode = false;
                bool generate_mode = false;
                bool keep_positionals = false;
+               bool keep_portwidths = false;
+               bool nokeep_asserts = false;
                std::vector<std::string> generate_cells;
                std::vector<generate_port_decl_t> generate_ports;
 
@@ -355,6 +472,14 @@ struct HierarchyPass : public Pass {
                                keep_positionals = true;
                                continue;
                        }
+                       if (args[argidx] == "-keep_portwidths") {
+                               keep_portwidths = true;
+                               continue;
+                       }
+                       if (args[argidx] == "-nokeep_asserts") {
+                               nokeep_asserts = true;
+                               continue;
+                       }
                        if (args[argidx] == "-libdir" && argidx+1 < args.size()) {
                                libdirs.push_back(args[++argidx]);
                                continue;
@@ -362,14 +487,20 @@ struct HierarchyPass : public Pass {
                        if (args[argidx] == "-top") {
                                if (++argidx >= args.size())
                                        log_cmd_error("Option -top requires an additional argument!\n");
-                               if (args[argidx][0] != '$' && args[argidx][0] != '\\')
-                                       top_mod = design->modules.count("\\" + args[argidx]) > 0 ? design->modules["\\" + args[argidx]] : NULL;
-                               else
-                                       top_mod = design->modules.count(args[argidx]) > 0 ? design->modules[args[argidx]] : NULL;
+                               top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL;
+                               if (top_mod == NULL && design->modules_.count("$abstract" + RTLIL::escape_id(args[argidx]))) {
+                                       dict<RTLIL::IdString, RTLIL::Const> empty_parameters;
+                                       design->modules_.at("$abstract" + RTLIL::escape_id(args[argidx]))->derive(design, empty_parameters);
+                                       top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL;
+                               }
                                if (top_mod == NULL)
                                        log_cmd_error("Module `%s' not found!\n", args[argidx].c_str());
                                continue;
                        }
+                       if (args[argidx] == "-auto-top") {
+                               auto_top_mode = true;
+                               continue;
+                       }
                        break;
                }
                extra_args(args, argidx, design, false);
@@ -381,66 +512,87 @@ struct HierarchyPass : public Pass {
 
                log_push();
 
-               if (top_mod == NULL)
-                       for (auto &mod_it : design->modules)
+               if (top_mod == nullptr)
+                       for (auto &mod_it : design->modules_)
                                if (mod_it.second->get_bool_attribute("\\top"))
                                        top_mod = mod_it.second;
 
-               if (top_mod != NULL)
-                       hierarchy(design, top_mod, purge_lib);
+               if (top_mod == nullptr && auto_top_mode) {
+                       log_header(design, "Finding top of design hierarchy..\n");
+                       dict<Module*, int> db;
+                       for (Module *mod : design->selected_modules()) {
+                               int score = find_top_mod_score(design, mod, db);
+                               log("root of %3d design levels: %-20s\n", score, log_id(mod));
+                               if (!top_mod || score > db[top_mod])
+                                       top_mod = mod;
+                       }
+                       if (top_mod != nullptr)
+                               log("Automatically selected %s as design top module.\n", log_id(top_mod));
+               }
 
                bool did_something = true;
-               bool did_something_once = false;
-               while (did_something) {
+               while (did_something)
+               {
                        did_something = false;
-                       std::vector<std::string> modnames;
-                       modnames.reserve(design->modules.size());
-                       for (auto &mod_it : design->modules)
-                               modnames.push_back(mod_it.first);
-                       for (auto &modname : modnames) {
-                               if (design->modules.count(modname) == 0)
-                                       continue;
-                               if (expand_module(design, design->modules[modname], flag_check, libdirs))
+
+                       std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> used_modules;
+                       if (top_mod != NULL) {
+                               log_header(design, "Analyzing design hierarchy..\n");
+                               hierarchy_worker(design, used_modules, top_mod, 0);
+                       } else {
+                               for (auto mod : design->modules())
+                                       used_modules.insert(mod);
+                       }
+
+                       for (auto module : used_modules) {
+                               if (expand_module(design, module, flag_check, libdirs))
                                        did_something = true;
                        }
-                       if (did_something)
-                               did_something_once = true;
                }
 
-               if (top_mod != NULL && did_something_once) {
-                       log_header("Re-running hierarchy analysis..\n");
-                       hierarchy(design, top_mod, purge_lib);
+               if (top_mod != NULL) {
+                       log_header(design, "Analyzing design hierarchy..\n");
+                       hierarchy_clean(design, top_mod, purge_lib);
                }
 
                if (top_mod != NULL) {
-                       for (auto &mod_it : design->modules)
+                       for (auto &mod_it : design->modules_)
                                if (mod_it.second == top_mod)
                                        mod_it.second->attributes["\\top"] = RTLIL::Const(1);
                                else
                                        mod_it.second->attributes.erase("\\top");
                }
 
+               if (!nokeep_asserts) {
+                       std::map<RTLIL::Module*, bool> cache;
+                       for (auto mod : design->modules())
+                               if (set_keep_assert(cache, mod)) {
+                                       log("Module %s directly or indirectly contains $assert cells -> setting \"keep\" attribute.\n", log_id(mod));
+                                       mod->set_bool_attribute("\\keep");
+                               }
+               }
+
                if (!keep_positionals)
                {
                        std::set<RTLIL::Module*> pos_mods;
                        std::map<std::pair<RTLIL::Module*,int>, RTLIL::IdString> pos_map;
                        std::vector<std::pair<RTLIL::Module*,RTLIL::Cell*>> pos_work;
 
-                       for (auto &mod_it : design->modules)
-                       for (auto &cell_it : mod_it.second->cells) {
+                       for (auto &mod_it : design->modules_)
+                       for (auto &cell_it : mod_it.second->cells_) {
                                RTLIL::Cell *cell = cell_it.second;
-                               if (design->modules.count(cell->type) == 0)
+                               if (design->modules_.count(cell->type) == 0)
                                        continue;
-                               for (auto &conn : cell->connections)
+                               for (auto &conn : cell->connections())
                                        if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') {
-                                               pos_mods.insert(design->modules.at(cell->type));
+                                               pos_mods.insert(design->modules_.at(cell->type));
                                                pos_work.push_back(std::pair<RTLIL::Module*,RTLIL::Cell*>(mod_it.second, cell));
                                                break;
                                        }
                        }
 
                        for (auto module : pos_mods)
-                       for (auto &wire_it : module->wires) {
+                       for (auto &wire_it : module->wires_) {
                                RTLIL::Wire *wire = wire_it.second;
                                if (wire->port_id > 0)
                                        pos_map[std::pair<RTLIL::Module*,int>(module, wire->port_id)] = wire->name;
@@ -451,11 +603,11 @@ struct HierarchyPass : public Pass {
                                RTLIL::Cell *cell = work.second;
                                log("Mapping positional arguments of cell %s.%s (%s).\n",
                                                RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
-                               std::map<RTLIL::IdString, RTLIL::SigSpec> new_connections;
-                               for (auto &conn : cell->connections)
+                               dict<RTLIL::IdString, RTLIL::SigSpec> new_connections;
+                               for (auto &conn : cell->connections())
                                        if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') {
                                                int id = atoi(conn.first.c_str()+1);
-                                               std::pair<RTLIL::Module*,int> key(design->modules.at(cell->type), id);
+                                               std::pair<RTLIL::Module*,int> key(design->modules_.at(cell->type), id);
                                                if (pos_map.count(key) == 0) {
                                                        log("  Failed to map positional argument %d of cell %s.%s (%s).\n",
                                                                        id, RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
@@ -464,11 +616,78 @@ struct HierarchyPass : public Pass {
                                                        new_connections[pos_map.at(key)] = conn.second;
                                        } else
                                                new_connections[conn.first] = conn.second;
-                               cell->connections = new_connections;
+                               cell->connections_ = new_connections;
                        }
                }
 
+               std::set<Module*> blackbox_derivatives;
+               std::vector<Module*> design_modules = design->modules();
+
+               for (auto module : design_modules)
+               for (auto cell : module->cells())
+               {
+                       Module *m = design->module(cell->type);
+
+                       if (m == nullptr)
+                               continue;
+
+                       if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty()) {
+                               IdString new_m_name = m->derive(design, cell->parameters, true);
+                               if (new_m_name.empty())
+                                       continue;
+                               if (new_m_name != m->name) {
+                                       m = design->module(new_m_name);
+                                       blackbox_derivatives.insert(m);
+                               }
+                       }
+
+                       for (auto &conn : cell->connections())
+                       {
+                               Wire *w = m->wire(conn.first);
+
+                               if (w == nullptr || w->port_id == 0)
+                                       continue;
+
+                               if (GetSize(conn.second) == 0)
+                                       continue;
+
+                               SigSpec sig = conn.second;
+
+                               if (!keep_portwidths && GetSize(w) != GetSize(conn.second))
+                               {
+                                       if (GetSize(w) < GetSize(conn.second))
+                                       {
+                                               int n = GetSize(conn.second) - GetSize(w);
+                                               if (!w->port_input && w->port_output)
+                                                       module->connect(sig.extract(GetSize(w), n), Const(0, n));
+                                               sig.remove(GetSize(w), n);
+                                       }
+                                       else
+                                       {
+                                               int n = GetSize(w) - GetSize(conn.second);
+                                               if (w->port_input && !w->port_output)
+                                                       sig.append(Const(0, n));
+                                               else
+                                                       sig.append(module->addWire(NEW_ID, n));
+                                       }
+
+                                       if (!conn.second.is_fully_const() || !w->port_input || w->port_output)
+                                               log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell),
+                                                               log_id(conn.first), GetSize(conn.second), GetSize(sig));
+                                       cell->setPort(conn.first, sig);
+                               }
+
+                               if (w->port_output && !w->port_input && sig.has_const())
+                                       log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n",
+                                                       log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig));
+                       }
+               }
+
+               for (auto module : blackbox_derivatives)
+                       design->remove(module);
+
                log_pop();
        }
 } HierarchyPass;
+
+PRIVATE_NAMESPACE_END