python: Adding helper functions for netlist modification

Signed-off-by: David Shah <dave@ds0.me>
diff --git a/common/arch_pybindings_shared.h b/common/arch_pybindings_shared.h
index b547dab..b90e80e 100644
--- a/common/arch_pybindings_shared.h
+++ b/common/arch_pybindings_shared.h
@@ -20,6 +20,22 @@
         conv_from_str<IdString>, conv_from_str<IdString>>::def_wrap(ctx_cls, "constrainCellToRegion");
 
 
+fn_wrapper_1a<Context, decltype(&Context::createNet), &Context::createNet, deref_and_wrap<NetInfo>,
+conv_from_str<IdString>>::def_wrap(ctx_cls, "createNet");
+fn_wrapper_3a_v<Context, decltype(&Context::connectPort), &Context::connectPort, conv_from_str<IdString>, conv_from_str<IdString>,
+conv_from_str<IdString>>::def_wrap(ctx_cls, "connectPort");
+fn_wrapper_2a_v<Context, decltype(&Context::disconnectPort), &Context::disconnectPort, conv_from_str<IdString>,
+conv_from_str<IdString>>::def_wrap(ctx_cls, "disconnectPort");
+fn_wrapper_1a_v<Context, decltype(&Context::ripupNet), &Context::ripupNet, conv_from_str<IdString>>::def_wrap(
+        ctx_cls, "ripupNet");
+fn_wrapper_1a_v<Context, decltype(&Context::lockNetRouting), &Context::lockNetRouting, conv_from_str<IdString>>::def_wrap(
+        ctx_cls, "lockNetRouting");
+
+fn_wrapper_2a<Context, decltype(&Context::createCell), &Context::createCell, deref_and_wrap<CellInfo>,
+conv_from_str<IdString>, conv_from_str<IdString>>::def_wrap(ctx_cls, "createCell");
+fn_wrapper_2a_v<Context, decltype(&Context::copyBelPorts), &Context::copyBelPorts,
+conv_from_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls, "copyBelPorts");
+
 fn_wrapper_1a<Context, decltype(&Context::getBelType), &Context::getBelType, conv_to_str<IdString>,
 conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelType");
 fn_wrapper_1a<Context, decltype(&Context::checkBelAvail), &Context::checkBelAvail, pass_through<bool>,
diff --git a/common/nextpnr.cc b/common/nextpnr.cc
index d73c0c0..933f124 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
@@ -144,6 +145,27 @@
 
 Property::Property(State bit) : is_string(false), str(std::string("") + char(bit)), intval(bit == S1) {}
 
+void CellInfo::addInput(IdString name)
+{
+    ports[name].name = name;
+    ports[name].type = PORT_IN;
+}
+void CellInfo::addOutput(IdString name)
+{
+    ports[name].name = name;
+    ports[name].type = PORT_OUT;
+}
+void CellInfo::addInout(IdString name)
+{
+    ports[name].name = name;
+    ports[name].type = PORT_INOUT;
+}
+
+void CellInfo::setParam(IdString name, Property value) { params[name] = value; }
+void CellInfo::unsetParam(IdString name) { params.erase(name); }
+void CellInfo::setAttr(IdString name, Property value) { attrs[name] = value; }
+void CellInfo::unsetAttr(IdString name) { attrs.erase(name); }
+
 std::string Property::to_string() const
 {
     if (is_string) {
@@ -638,4 +660,67 @@
     getCtx()->assignArchInfo();
 }
 
+NetInfo *BaseCtx::createNet(IdString name)
+{
+    NPNR_ASSERT(!nets.count(name));
+    NPNR_ASSERT(!net_aliases.count(name));
+    std::unique_ptr<NetInfo> net{new NetInfo};
+    net->name = name;
+    net_aliases[name] = name;
+    NetInfo *ptr = net.get();
+    nets[name] = std::move(net);
+    refreshUi();
+    return ptr;
+}
+
+void BaseCtx::connectPort(IdString net, IdString cell, IdString port)
+{
+    NetInfo *net_info = getNetByAlias(net);
+    CellInfo *cell_info = cells.at(cell).get();
+    connect_port(getCtx(), net_info, cell_info, port);
+}
+
+void BaseCtx::disconnectPort(IdString cell, IdString port)
+{
+    CellInfo *cell_info = cells.at(cell).get();
+    disconnect_port(getCtx(), cell_info, port);
+}
+
+void BaseCtx::ripupNet(IdString name)
+{
+    NetInfo *net_info = getNetByAlias(name);
+    std::vector<WireId> to_unbind;
+    for (auto &wire : net_info->wires)
+        to_unbind.push_back(wire.first);
+    for (auto &unbind : to_unbind)
+        getCtx()->unbindWire(unbind);
+}
+void BaseCtx::lockNetRouting(IdString name)
+{
+    NetInfo *net_info = getNetByAlias(name);
+    for (auto &wire : net_info->wires)
+        wire.second.strength = STRENGTH_USER;
+}
+
+CellInfo *BaseCtx::createCell(IdString name, IdString type)
+{
+    NPNR_ASSERT(!cells.count(name));
+    std::unique_ptr<CellInfo> cell{new CellInfo};
+    cell->name = name;
+    cell->type = type;
+    CellInfo *ptr = cell.get();
+    cells[name] = std::move(cell);
+    refreshUi();
+    return ptr;
+}
+
+void BaseCtx::copyBelPorts(IdString cell, BelId bel)
+{
+    CellInfo *cell_info = cells.at(cell).get();
+    for (auto pin : getCtx()->getBelPins(bel)) {
+        cell_info->ports[pin].name = pin;
+        cell_info->ports[pin].type = getCtx()->getBelPinType(bel, pin);
+    }
+}
+
 NEXTPNR_NAMESPACE_END
diff --git a/common/nextpnr.h b/common/nextpnr.h
index 4f9f7f2..bae828f 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -445,6 +445,15 @@
 
     Region *region = nullptr;
     TimingConstrObjectId tmg_id;
+
+    void addInput(IdString name);
+    void addOutput(IdString name);
+    void addInout(IdString name);
+
+    void setParam(IdString name, Property value);
+    void unsetParam(IdString name);
+    void setAttr(IdString name, Property value);
+    void unsetAttr(IdString name);
 };
 
 enum TimingPortClass
@@ -741,7 +750,10 @@
     TimingConstrObjectId timingCellObject(CellInfo *cell);
     TimingConstrObjectId timingPortObject(CellInfo *cell, IdString port);
 
-    NetInfo *getNetByAlias(IdString alias) const { return nets.at(net_aliases.at(alias)).get(); }
+    NetInfo *getNetByAlias(IdString alias) const
+    {
+        return nets.count(alias) ? nets.at(alias).get() : nets.at(net_aliases.at(alias)).get();
+    }
 
     void addConstraint(std::unique_ptr<TimingConstraint> constr);
     void removeConstraint(IdString constrName);
@@ -752,6 +764,16 @@
     void addBelToRegion(IdString name, BelId bel);
     void constrainCellToRegion(IdString cell, IdString region_name);
 
+    // Helper functions for Python bindings
+    NetInfo *createNet(IdString name);
+    void connectPort(IdString net, IdString cell, IdString port);
+    void disconnectPort(IdString cell, IdString port);
+    void ripupNet(IdString name);
+    void lockNetRouting(IdString name);
+
+    CellInfo *createCell(IdString name, IdString type);
+    void copyBelPorts(IdString cell, BelId bel);
+
     // 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..0397923 100644
--- a/common/pybindings.cc
+++ b/common/pybindings.cc
@@ -160,6 +160,22 @@
     readonly_wrapper<CellInfo &, decltype(&CellInfo::pins), &CellInfo::pins, wrap_context<PinMap &>>::def_wrap(ci_cls,
                                                                                                                "pins");
 
+    fn_wrapper_1a_v<CellInfo &, decltype(&CellInfo::addInput), &CellInfo::addInput, conv_from_str<IdString>>::def_wrap(
+            ci_cls, "addInput");
+    fn_wrapper_1a_v<CellInfo &, decltype(&CellInfo::addOutput), &CellInfo::addOutput,
+                    conv_from_str<IdString>>::def_wrap(ci_cls, "addOutput");
+    fn_wrapper_1a_v<CellInfo &, decltype(&CellInfo::addInout), &CellInfo::addInout, conv_from_str<IdString>>::def_wrap(
+            ci_cls, "addInout");
+
+    fn_wrapper_2a_v<CellInfo &, decltype(&CellInfo::setParam), &CellInfo::setParam, conv_from_str<IdString>,
+                    conv_from_str<Property>>::def_wrap(ci_cls, "setParam");
+    fn_wrapper_1a_v<CellInfo &, decltype(&CellInfo::unsetParam), &CellInfo::unsetParam,
+                    conv_from_str<IdString>>::def_wrap(ci_cls, "unsetParam");
+    fn_wrapper_2a_v<CellInfo &, decltype(&CellInfo::setAttr), &CellInfo::setAttr, conv_from_str<IdString>,
+                    conv_from_str<Property>>::def_wrap(ci_cls, "setAttr");
+    fn_wrapper_1a_v<CellInfo &, decltype(&CellInfo::unsetAttr), &CellInfo::unsetAttr,
+                    conv_from_str<IdString>>::def_wrap(ci_cls, "unsetAttr");
+
     auto pi_cls = class_<ContextualWrapper<PortInfo &>>("PortInfo", no_init);
     readwrite_wrapper<PortInfo &, decltype(&PortInfo::name), &PortInfo::name, conv_to_str<IdString>,
                       conv_from_str<IdString>>::def_wrap(pi_cls, "name");