Fixes for ECP5 partial bitstream demo

Signed-off-by: David Shah <dave@ds0.me>
diff --git a/common/nextpnr.cc b/common/nextpnr.cc
index ab4601a..2597842 100644
--- a/common/nextpnr.cc
+++ b/common/nextpnr.cc
@@ -19,6 +19,7 @@
 
 #include "nextpnr.h"
 #include <boost/algorithm/string.hpp>
+#include "design_utils.h"
 #include "log.h"
 
 NEXTPNR_NAMESPACE_BEGIN
@@ -185,6 +186,10 @@
     return p;
 }
 
+void CellInfo::setParam(IdString name, std::string value) { params[name] = Property::from_string(value); }
+
+void CellInfo::setAttr(IdString name, std::string value) { attrs[name] = Property::from_string(value); }
+
 void BaseCtx::addConstraint(std::unique_ptr<TimingConstraint> constr)
 {
     for (auto fromObj : constr->from)
@@ -497,6 +502,14 @@
     }
     region[name] = std::move(new_region);
 }
+
+void BaseCtx::makeConnection(IdString netname, IdString cellname, IdString portname)
+{
+    NetInfo *net = nets[netname].get();
+    CellInfo *cell = cells[cellname].get();
+    connect_port(getCtx(), net, cell, portname);
+}
+
 void BaseCtx::addBelToRegion(IdString name, BelId bel) { region[name]->bels.insert(bel); }
 void BaseCtx::constrainCellToRegion(IdString cell, IdString region_name)
 {
diff --git a/common/nextpnr.h b/common/nextpnr.h
index 177048b..2358ad7 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -402,6 +402,8 @@
     TimingConstrObjectId tmg_id;
 
     Region *region = nullptr;
+
+    void connectTo(CellInfo *cell, IdString port);
 };
 
 enum PortType
@@ -445,6 +447,10 @@
 
     Region *region = nullptr;
     TimingConstrObjectId tmg_id;
+
+    // For Python bindings
+    void setParam(IdString name, std::string value);
+    void setAttr(IdString name, std::string value);
 };
 
 enum TimingPortClass
@@ -746,6 +752,7 @@
     void createRectangularRegion(IdString name, int x0, int y0, int x1, int y1);
     void addBelToRegion(IdString name, BelId bel);
     void constrainCellToRegion(IdString cell, IdString region_name);
+    void makeConnection(IdString netname, IdString cellname, IdString portname);
 
     // Workaround for lack of wrappable constructors
     DecalXY constructDecalXY(DecalId decal, float x, float y);
diff --git a/common/pybindings.cc b/common/pybindings.cc
index 3f2cb81..c6228d8 100644
--- a/common/pybindings.cc
+++ b/common/pybindings.cc
@@ -159,6 +159,10 @@
                       pass_through<PlaceStrength>>::def_wrap(ci_cls, "belStrength");
     readonly_wrapper<CellInfo &, decltype(&CellInfo::pins), &CellInfo::pins, wrap_context<PinMap &>>::def_wrap(ci_cls,
                                                                                                                "pins");
+    fn_wrapper_2a_v<CellInfo &, decltype(&CellInfo::setParam), &CellInfo::setParam, conv_from_str<IdString>,
+                    pass_through<std::string>>::def_wrap(ci_cls, "setParam");
+    fn_wrapper_2a_v<CellInfo &, decltype(&CellInfo::setAttr), &CellInfo::setAttr, conv_from_str<IdString>,
+                    pass_through<std::string>>::def_wrap(ci_cls, "setAttr");
 
     auto pi_cls = class_<ContextualWrapper<PortInfo &>>("PortInfo", no_init);
     readwrite_wrapper<PortInfo &, decltype(&PortInfo::name), &PortInfo::name, conv_to_str<IdString>,
diff --git a/common/router1.cc b/common/router1.cc
index 02c817c..a88bae6 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -400,7 +400,7 @@
 
                 dst_to_arc[dst_wire] = arc;
 
-                if (net_info->wires.count(src_wire) == 0) {
+                if (net_info->wires.count(dst_wire) == 0) {
                     arc_queue_insert(arc, src_wire, dst_wire);
                     continue;
                 }
diff --git a/ecp5/arch_pybindings.cc b/ecp5/arch_pybindings.cc
index 18d2111..0a86e8e 100644
--- a/ecp5/arch_pybindings.cc
+++ b/ecp5/arch_pybindings.cc
@@ -141,6 +141,8 @@
     fn_wrapper_2a_v<Context, decltype(&Context::constrainCellToRegion), &Context::constrainCellToRegion,
                     conv_from_str<IdString>, conv_from_str<IdString>>::def_wrap(ctx_cls, "constrainCellToRegion");
 
+    fn_wrapper_3a_v<Context, decltype(&Context::makeConnection), &Context::makeConnection, conv_from_str<IdString>,
+                    conv_from_str<IdString>, conv_from_str<IdString>>::def_wrap(ctx_cls, "makeConnection");
     WRAP_RANGE(Bel, conv_to_str<BelId>);
     WRAP_RANGE(Wire, conv_to_str<WireId>);
     WRAP_RANGE(AllPip, conv_to_str<PipId>);
diff --git a/ecp5/globals.cc b/ecp5/globals.cc
index bc5c66d..b6691f9 100644
--- a/ecp5/globals.cc
+++ b/ecp5/globals.cc
@@ -435,7 +435,8 @@
         log_info("Promoting globals...\n");
         auto clocks = get_clocks();
         for (auto clock : clocks) {
-            bool is_noglobal = bool_or_default(clock->attrs, ctx->id("noglobal"), false);
+            bool is_noglobal = bool_or_default(clock->attrs, ctx->id("noglobal"), false) ||
+                               bool_or_default(clock->attrs, ctx->id("ECP5_IS_GLOBAL"), false);
             if (is_noglobal)
                 continue;
             log_info("    promoting clock net %s to global network\n", clock->name.c_str(ctx));
@@ -461,6 +462,8 @@
             CellInfo *ci = cell.second;
             if (ci->type == id_DCCA) {
                 NetInfo *clock = ci->ports.at(id_CLKO).net;
+                if (!clock->wires.empty())
+                    continue;
                 NPNR_ASSERT(clock != nullptr);
                 bool drives_fabric = std::any_of(clock->users.begin(), clock->users.end(),
                                                  [this](const PortRef &port) { return !is_clock_port(port); });