json: Add support for net aliases

Signed-off-by: David Shah <dave@ds0.me>
diff --git a/common/nextpnr.cc b/common/nextpnr.cc
index ab4601a..d73c0c0 100644
--- a/common/nextpnr.cc
+++ b/common/nextpnr.cc
@@ -474,10 +474,10 @@
     cc->period = getCtx()->getDelayFromNS(1000 / freq);
     cc->high = getCtx()->getDelayFromNS(500 / freq);
     cc->low = getCtx()->getDelayFromNS(500 / freq);
-    if (!nets.count(net)) {
+    if (!net_aliases.count(net)) {
         log_warning("net '%s' does not exist in design, ignoring clock constraint\n", net.c_str(this));
     } else {
-        nets.at(net)->clkconstr = std::move(cc);
+        getNetByAlias(net)->clkconstr = std::move(cc);
         log_info("constraining clock net '%s' to %.02f MHz\n", net.c_str(this), freq);
     }
 }
diff --git a/common/nextpnr.h b/common/nextpnr.h
index 177048b..4f9f7f2 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -609,6 +609,9 @@
     std::unordered_map<IdString, std::unique_ptr<NetInfo>> nets;
     std::unordered_map<IdString, std::unique_ptr<CellInfo>> cells;
 
+    // Aliases for nets, which may have more than one name due to assignments and hierarchy
+    std::unordered_map<IdString, IdString> net_aliases;
+
     // Top-level ports
     std::unordered_map<IdString, PortInfo> ports;
 
@@ -738,6 +741,8 @@
     TimingConstrObjectId timingCellObject(CellInfo *cell);
     TimingConstrObjectId timingPortObject(CellInfo *cell, IdString port);
 
+    NetInfo *getNetByAlias(IdString alias) const { return nets.at(net_aliases.at(alias)).get(); }
+
     void addConstraint(std::unique_ptr<TimingConstraint> constr);
     void removeConstraint(IdString constrName);
 
diff --git a/ecp5/arch_pybindings.cc b/ecp5/arch_pybindings.cc
index 18d2111..275956f 100644
--- a/ecp5/arch_pybindings.cc
+++ b/ecp5/arch_pybindings.cc
@@ -125,12 +125,17 @@
 
     typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
     typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
+    typedef std::unordered_map<IdString, IdString> AliasMap;
 
     readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls,
                                                                                                              "cells");
     readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls,
                                                                                                           "nets");
+    readonly_wrapper<Context, decltype(&Context::net_aliases), &Context::net_aliases,
+                     wrap_context<AliasMap &>>::def_wrap(ctx_cls, "net_aliases");
 
+    fn_wrapper_1a<Context, decltype(&Context::getNetByAlias), &Context::getNetByAlias, deref_and_wrap<NetInfo>,
+                  conv_from_str<IdString>>::def_wrap(ctx_cls, "getNetByAlias");
     fn_wrapper_2a_v<Context, decltype(&Context::addClock), &Context::addClock, conv_from_str<IdString>,
                     pass_through<float>>::def_wrap(ctx_cls, "addClock");
     fn_wrapper_5a_v<Context, decltype(&Context::createRectangularRegion), &Context::createRectangularRegion,
diff --git a/ice40/arch_pybindings.cc b/ice40/arch_pybindings.cc
index bc0bfb8..194d7c6 100644
--- a/ice40/arch_pybindings.cc
+++ b/ice40/arch_pybindings.cc
@@ -136,12 +136,17 @@
 
     typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
     typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
+    typedef std::unordered_map<IdString, IdString> AliasMap;
 
     readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls,
                                                                                                              "cells");
     readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls,
                                                                                                           "nets");
+    readonly_wrapper<Context, decltype(&Context::net_aliases), &Context::net_aliases,
+                     wrap_context<AliasMap &>>::def_wrap(ctx_cls, "net_aliases");
 
+    fn_wrapper_1a<Context, decltype(&Context::getNetByAlias), &Context::getNetByAlias, deref_and_wrap<NetInfo>,
+                  conv_from_str<IdString>>::def_wrap(ctx_cls, "getNetByAlias");
     fn_wrapper_2a_v<Context, decltype(&Context::addClock), &Context::addClock, conv_from_str<IdString>,
                     pass_through<float>>::def_wrap(ctx_cls, "addClock");
     fn_wrapper_5a_v<Context, decltype(&Context::createRectangularRegion), &Context::createRectangularRegion,
diff --git a/json/jsonparse.cc b/json/jsonparse.cc
index ad21daf..fb712b2 100644
--- a/json/jsonparse.cc
+++ b/json/jsonparse.cc
@@ -805,7 +805,7 @@
     };
 
     // Import netnames
-    std::vector<std::string> netlabels;
+    std::vector<std::vector<std::string>> netlabels;
     if (node->data_dict.count("netnames")) {
         JsonNode *cell_parent = node->data_dict.at("netnames");
         for (int nnid = 0; nnid < GetSize(cell_parent->data_dict_keys); nnid++) {
@@ -838,15 +838,26 @@
                         ndx = start_offset + num_bits - i - 1;
                     std::string name =
                             basename + (num_bits == 1 ? "" : std::string("[") + std::to_string(ndx) + std::string("]"));
-                    if (prefer_netlabel(name, netlabels.at(netid)))
-                        netlabels.at(netid) = name;
+                    netlabels.at(netid).push_back(name);
                 }
             }
         }
     }
     std::vector<IdString> netids;
-    std::transform(netlabels.begin(), netlabels.end(), std::back_inserter(netids),
-                   [ctx](const std::string &s) { return ctx->id(s); });
+    for (size_t i = 0; i < netlabels.size(); i++) {
+        auto &labels = netlabels.at(i);
+        if (labels.empty()) {
+            // Backup for unnamed nets (not sure if these should actually happen)
+            netids.push_back(ctx->id("$nextpnr$unknown_netname$" + std::to_string(i)));
+        } else {
+            // Pick a primary name for the net according to a simple heuristic
+            std::string pref = labels.at(0);
+            for (size_t j = 1; j < labels.size(); j++)
+                if (prefer_netlabel(labels.at(j), pref))
+                    pref = labels.at(j);
+            netids.push_back(ctx->id(pref));
+        }
+    }
     if (node->data_dict.count("cells")) {
         JsonNode *cell_parent = node->data_dict.at("cells");
         //
@@ -921,6 +932,17 @@
             }
         }
     }
+    // Import net aliases
+    for (size_t i = 0; i < netids.size(); i++) {
+        IdString netname = netids.at(i);
+        if (!ctx->nets.count(netname))
+            continue;
+        for (auto &label : netlabels.at(i)) {
+            IdString labelid = ctx->id(label);
+            NPNR_ASSERT(!ctx->net_aliases.count(labelid));
+            ctx->net_aliases[labelid] = netname;
+        }
+    }
     check_all_nets_driven(ctx);
     ctx->settings[ctx->id("synth")] = 1;
 }