jsonwrite: Fix bus cell ports

Signed-off-by: David Shah <dave@ds0.me>
diff --git a/json/jsonwrite.cc b/json/jsonwrite.cc
index 3d3b70e..9723d3b 100644
--- a/json/jsonwrite.cc
+++ b/json/jsonwrite.cc
@@ -73,14 +73,17 @@
     PortType dir;
 };
 
-std::vector<PortGroup> group_ports(Context *ctx)
+std::vector<PortGroup> group_ports(Context *ctx, const std::unordered_map<IdString, PortInfo> &ports,
+                                   bool is_cell = false)
 {
     std::vector<PortGroup> groups;
     std::unordered_map<std::string, size_t> base_to_group;
-    for (auto &pair : ctx->ports) {
+    for (auto &pair : ports) {
         std::string name = pair.second.name.str(ctx);
         if ((name.back() != ']') || (name.find('[') == std::string::npos)) {
-            groups.push_back({name, {pair.first.index}, pair.second.type});
+            groups.push_back({name,
+                              {is_cell ? (pair.second.net ? pair.second.net->name.index : -1) : pair.first.index},
+                              pair.second.type});
         } else {
             int off1 = int(name.find_last_of('['));
             std::string basename = name.substr(0, off1);
@@ -95,26 +98,27 @@
             if (int(grp.bits.size()) <= index)
                 grp.bits.resize(index + 1, -1);
             NPNR_ASSERT(grp.bits.at(index) == -1);
-            grp.bits.at(index) = pair.second.net ? pair.second.net->name.index : pair.first.index;
+            grp.bits.at(index) = pair.second.net ? pair.second.net->name.index : (is_cell ? -1 : pair.first.index);
         }
     }
     return groups;
-};
+}
 
-std::string format_port_bits(const PortGroup &port)
+std::string format_port_bits(const PortGroup &port, int &dummy_idx)
 {
     std::stringstream s;
     s << "[ ";
     bool first = true;
-    for (auto bit : port.bits) {
-        if (!first)
-            s << ", ";
-        if (bit == -1)
-            s << "\"x\"";
-        else
-            s << bit;
-        first = false;
-    }
+    if (port.bits.size() != 1 || port.bits.at(0) != -1) // skip single disconnected ports
+        for (auto bit : port.bits) {
+            if (!first)
+                s << ", ";
+            if (bit == -1)
+                s << (++dummy_idx);
+            else
+                s << bit;
+            first = false;
+        }
     s << " ]";
     return s.str();
 }
@@ -122,6 +126,7 @@
 void write_module(std::ostream &f, Context *ctx)
 {
     auto val = ctx->attrs.find(ctx->id("module"));
+    int dummy_idx = int(ctx->idstring_idx_to_str->size()) + 1000;
     if (val != ctx->attrs.end())
         f << stringf("    %s: {\n", get_string(val->second.as_string()).c_str());
     else
@@ -134,14 +139,14 @@
     f << stringf("\n      },\n");
     f << stringf("      \"ports\": {");
 
-    auto ports = group_ports(ctx);
+    auto ports = group_ports(ctx, ctx->ports);
     bool first = true;
     for (auto &port : ports) {
         f << stringf("%s\n", first ? "" : ",");
         f << stringf("        %s: {\n", get_string(port.name).c_str());
         f << stringf("          \"direction\": \"%s\",\n",
                      port.dir == PORT_IN ? "input" : port.dir == PORT_INOUT ? "inout" : "output");
-        f << stringf("          \"bits\": %s\n", format_port_bits(port).c_str());
+        f << stringf("          \"bits\": %s\n", format_port_bits(port, dummy_idx).c_str());
         f << stringf("        }");
         first = false;
     }
@@ -151,6 +156,7 @@
     first = true;
     for (auto &pair : ctx->cells) {
         auto &c = pair.second;
+        auto cell_ports = group_ports(ctx, c->ports, true);
         f << stringf("%s\n", first ? "" : ",");
         f << stringf("        %s: {\n", get_name(c->name, ctx).c_str());
         f << stringf("          \"hide_name\": %s,\n", c->name.c_str(ctx)[0] == '$' ? "1" : "0");
@@ -163,24 +169,18 @@
         f << stringf("\n          },\n");
         f << stringf("          \"port_directions\": {");
         bool first2 = true;
-        for (auto &conn : c->ports) {
-            auto &p = conn.second;
-            std::string direction = (p.type == PORT_IN) ? "input" : (p.type == PORT_OUT) ? "output" : "inout";
+        for (auto &pg : cell_ports) {
+            std::string direction = (pg.dir == PORT_IN) ? "input" : (pg.dir == PORT_OUT) ? "output" : "inout";
             f << stringf("%s\n", first2 ? "" : ",");
-            f << stringf("            %s: \"%s\"", get_name(conn.first, ctx).c_str(), direction.c_str());
+            f << stringf("            %s: \"%s\"", get_string(pg.name).c_str(), direction.c_str());
             first2 = false;
         }
         f << stringf("\n          },\n");
         f << stringf("          \"connections\": {");
         first2 = true;
-        for (auto &conn : c->ports) {
-            auto &p = conn.second;
+        for (auto &pg : cell_ports) {
             f << stringf("%s\n", first2 ? "" : ",");
-            if (p.net)
-                f << stringf("            %s: [ %d ]", get_name(conn.first, ctx).c_str(), p.net->name.index);
-            else
-                f << stringf("            %s: [ ]", get_name(conn.first, ctx).c_str());
-
+            f << stringf("            %s: %s", get_string(pg.name).c_str(), format_port_bits(pg, dummy_idx).c_str());
             first2 = false;
         }
         f << stringf("\n          }\n");