hierarchy: Resolve SV wildcard port connections

Signed-off-by: David Shah <dave@ds0.me>
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index fc8df19..1758354 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -1582,7 +1582,7 @@
 		free_attr($1);
 	} |
 	attr TOK_AUTOCONNECT_ALL {
-		astbuf2->attributes[ID(autoconnect)] = AstNode::mkconst_int(1, false);
+		astbuf2->attributes[ID(implicit_port_conns)] = AstNode::mkconst_int(1, false);
 	};
 
 always_comb_or_latch:
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index d8a6284..0704c26 100644
--- a/passes/hierarchy/hierarchy.cc
+++ b/passes/hierarchy/hierarchy.cc
@@ -548,6 +548,19 @@
 	return NULL;
 }
 
+// Find a matching wire for an implicit port connection; traversing generate block scope
+RTLIL::Wire *find_implicit_port_wire(Module *module, Cell *cell, const std::string& port)
+{
+	const std::string &cellname = cell->name.str();
+	size_t idx = cellname.size();
+	while ((idx = cellname.find_last_of('.', idx-1)) != std::string::npos) {
+		Wire *found = module->wire(cellname.substr(0, idx+1) + port.substr(1));
+		if (found != nullptr)
+			return found;
+	}
+	return module->wire(port);
+}
+
 struct HierarchyPass : public Pass {
 	HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { }
 	void help() YS_OVERRIDE
@@ -970,6 +983,55 @@
 			}
 		}
 
+		// Process SV implicit port connections
+		std::set<Module*> blackbox_derivatives;
+		std::vector<Module*> design_modules = design->modules();
+
+		for (auto module : design_modules)
+		{
+			for (auto cell : module->cells())
+			{
+				if (!cell->get_bool_attribute(ID(implicit_port_conns)))
+					continue;
+				Module *m = design->module(cell->type);
+
+				if (m == nullptr)
+					log_error("Cell %s.%s (%s) has implicit port connections but the module it instantiates is unknown.\n",
+							RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+
+				// Need accurate port widths for error checking; so must derive blackboxes with dynamic port widths
+				if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) {
+					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);
+					}
+				}
+
+				auto old_connections = cell->connections();
+				for (auto wire : m->wires()) {
+					// Find ports of the module that aren't explicitly connected
+					if (!wire->port_input && !wire->port_output)
+						continue;
+					if (old_connections.count(wire->name))
+						continue;
+					// Make sure a wire of correct name exists in the parent
+					Wire* parent_wire = find_implicit_port_wire(module, cell, wire->name.str());
+					if (parent_wire == nullptr)
+						log_error("No matching wire for implicit port connection `%s' of cell %s.%s (%s).\n",
+								RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+					if (parent_wire->width != wire->width)
+						log_error("Width mismatch between wire (%d bits) and port (%d bits) for implicit port connection `%s' of cell %s.%s (%s).\n",
+								parent_wire->width, wire->width,
+								RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type));
+					cell->setPort(wire->name, parent_wire);
+				}
+				cell->attributes.erase(ID(implicit_port_conns));
+			}
+		}
+
 		if (!nodefaults)
 		{
 			dict<IdString, dict<IdString, Const>> defaults_db;
@@ -1000,9 +1062,6 @@
 				}
 		}
 
-		std::set<Module*> blackbox_derivatives;
-		std::vector<Module*> design_modules = design->modules();
-
 		for (auto module : design_modules)
 		{
 			pool<Wire*> wand_wor_index;