python: Add bindings for hierarchy structures

Signed-off-by: David Shah <dave@ds0.me>
diff --git a/common/arch_pybindings_shared.h b/common/arch_pybindings_shared.h
index f681af9..89a61da 100644
--- a/common/arch_pybindings_shared.h
+++ b/common/arch_pybindings_shared.h
@@ -5,6 +5,10 @@
 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");
+readonly_wrapper<Context, decltype(&Context::hierarchy), &Context::hierarchy, wrap_context<HierarchyMap &>>::def_wrap(
+        ctx_cls, "hierarchy");
+readwrite_wrapper<Context, decltype(&Context::top_module), &Context::top_module, conv_to_str<IdString>,
+                  conv_from_str<IdString>>::def_wrap(ctx_cls, "top_module");
 
 fn_wrapper_1a<Context, decltype(&Context::getNetByAlias), &Context::getNetByAlias, deref_and_wrap<NetInfo>,
               conv_from_str<IdString>>::def_wrap(ctx_cls, "getNetByAlias");
diff --git a/common/nextpnr.h b/common/nextpnr.h
index ceea208..7dfebd6 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -530,7 +530,7 @@
 // Represents the contents of a non-leaf cell in a design
 // with hierarchy
 
-struct HierachicalPort
+struct HierarchicalPort
 {
     IdString name;
     PortType dir;
@@ -539,12 +539,12 @@
     bool upto;
 };
 
-struct HierachicalCell
+struct HierarchicalCell
 {
     IdString name, type, parent, fullpath;
     // Name inside cell instance -> global name
     std::unordered_map<IdString, IdString> leaf_cells, nets;
-    std::unordered_map<IdString, HierachicalPort> ports;
+    std::unordered_map<IdString, HierarchicalPort> ports;
     // Name inside cell instance -> global name
     std::unordered_map<IdString, IdString> hier_cells;
 };
@@ -643,7 +643,7 @@
     std::unordered_map<IdString, std::unique_ptr<CellInfo>> cells;
 
     // Hierarchical (non-leaf) cells by full path
-    std::unordered_map<IdString, HierachicalCell> hierarchy;
+    std::unordered_map<IdString, HierarchicalCell> hierarchy;
     // This is the root of the above structure
     IdString top_module;
 
diff --git a/common/pybindings.cc b/common/pybindings.cc
index 5383028..3b2a374 100644
--- a/common/pybindings.cc
+++ b/common/pybindings.cc
@@ -131,7 +131,7 @@
 
     typedef std::unordered_map<IdString, Property> AttrMap;
     typedef std::unordered_map<IdString, PortInfo> PortMap;
-    typedef std::unordered_map<IdString, IdString> PinMap;
+    typedef std::unordered_map<IdString, IdString> IdIdMap;
     typedef std::unordered_map<IdString, std::unique_ptr<Region>> RegionMap;
 
     class_<BaseCtx, BaseCtx *, boost::noncopyable>("BaseCtx", no_init);
@@ -157,8 +157,8 @@
                       conv_from_str<BelId>>::def_wrap(ci_cls, "bel");
     readwrite_wrapper<CellInfo &, decltype(&CellInfo::belStrength), &CellInfo::belStrength, pass_through<PlaceStrength>,
                       pass_through<PlaceStrength>>::def_wrap(ci_cls, "belStrength");
-    readonly_wrapper<CellInfo &, decltype(&CellInfo::pins), &CellInfo::pins, wrap_context<PinMap &>>::def_wrap(ci_cls,
-                                                                                                               "pins");
+    readonly_wrapper<CellInfo &, decltype(&CellInfo::pins), &CellInfo::pins, wrap_context<IdIdMap &>>::def_wrap(ci_cls,
+                                                                                                                "pins");
 
     fn_wrapper_1a_v<CellInfo &, decltype(&CellInfo::addInput), &CellInfo::addInput, conv_from_str<IdString>>::def_wrap(
             ci_cls, "addInput");
@@ -230,9 +230,25 @@
     readonly_wrapper<Region &, decltype(&Region::wires), &Region::wires, wrap_context<WireSet &>>::def_wrap(region_cls,
                                                                                                             "wires");
 
+    auto hierarchy_cls = class_<ContextualWrapper<HierarchicalCell &>>("HierarchicalCell", no_init);
+    readwrite_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::name), &HierarchicalCell::name,
+                      conv_to_str<IdString>, conv_from_str<IdString>>::def_wrap(hierarchy_cls, "name");
+    readwrite_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::type), &HierarchicalCell::type,
+                      conv_to_str<IdString>, conv_from_str<IdString>>::def_wrap(hierarchy_cls, "type");
+    readwrite_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::parent), &HierarchicalCell::parent,
+                      conv_to_str<IdString>, conv_from_str<IdString>>::def_wrap(hierarchy_cls, "parent");
+    readwrite_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::fullpath), &HierarchicalCell::fullpath,
+                      conv_to_str<IdString>, conv_from_str<IdString>>::def_wrap(hierarchy_cls, "fullpath");
+
+    readonly_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::leaf_cells), &HierarchicalCell::leaf_cells,
+                     wrap_context<IdIdMap &>>::def_wrap(hierarchy_cls, "leaf_cells");
+    readonly_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::nets), &HierarchicalCell::nets,
+                     wrap_context<IdIdMap &>>::def_wrap(hierarchy_cls, "nets");
+    readonly_wrapper<HierarchicalCell &, decltype(&HierarchicalCell::hier_cells), &HierarchicalCell::hier_cells,
+                     wrap_context<IdIdMap &>>::def_wrap(hierarchy_cls, "hier_cells");
     WRAP_MAP(AttrMap, conv_to_str<Property>, "AttrMap");
     WRAP_MAP(PortMap, wrap_context<PortInfo &>, "PortMap");
-    WRAP_MAP(PinMap, conv_to_str<IdString>, "PinMap");
+    WRAP_MAP(IdIdMap, conv_to_str<IdString>, "IdIdMap");
     WRAP_MAP(WireMap, wrap_context<PipMap &>, "WireMap");
     WRAP_MAP_UPTR(RegionMap, "RegionMap");
 
diff --git a/ecp5/arch_pybindings.cc b/ecp5/arch_pybindings.cc
index da6d3e5..cd5e31c 100644
--- a/ecp5/arch_pybindings.cc
+++ b/ecp5/arch_pybindings.cc
@@ -49,6 +49,7 @@
     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;
+    typedef std::unordered_map<IdString, HierarchicalCell> HierarchyMap;
 
     auto belpin_cls = class_<ContextualWrapper<BelPin>>("BelPin", no_init);
     readonly_wrapper<BelPin, decltype(&BelPin::bel), &BelPin::bel, conv_to_str<BelId>>::def_wrap(belpin_cls, "bel");
@@ -64,6 +65,7 @@
 
     WRAP_MAP_UPTR(CellMap, "IdCellMap");
     WRAP_MAP_UPTR(NetMap, "IdNetMap");
+    WRAP_MAP(HierarchyMap, wrap_context<HierarchicalCell &>, "HierarchyMap");
 }
 
 NEXTPNR_NAMESPACE_END
diff --git a/frontend/frontend_base.h b/frontend/frontend_base.h
index be76426..9e16cb2 100644
--- a/frontend/frontend_base.h
+++ b/frontend/frontend_base.h
@@ -500,6 +500,7 @@
         submod.prefix += '.';
         submod.parent_path = m.path;
         submod.path = ctx->id(m.path.str(ctx) + "/" + name);
+        ctx->hierarchy[m.path].hier_cells[ctx->id(name)] = submod.path;
         // Do the submodule import
         auto type = impl.get_cell_type(cd);
         import_module(submod, name, type, mod_refs.at(ctx->id(type)));
diff --git a/generic/arch_pybindings.cc b/generic/arch_pybindings.cc
index 8526e40..2600cac 100644
--- a/generic/arch_pybindings.cc
+++ b/generic/arch_pybindings.cc
@@ -141,6 +141,7 @@
 
     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, HierarchicalCell> HierarchyMap;
 
     readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls,
                                                                                                              "cells");
@@ -231,6 +232,7 @@
 
     WRAP_MAP_UPTR(CellMap, "IdCellMap");
     WRAP_MAP_UPTR(NetMap, "IdNetMap");
+    WRAP_MAP(HierarchyMap, wrap_context<HierarchicalCell &>, "HierarchyMap");
     WRAP_VECTOR(const std::vector<IdString>, conv_to_str<IdString>);
 }
 
diff --git a/ice40/arch_pybindings.cc b/ice40/arch_pybindings.cc
index cef7c58..e202209 100644
--- a/ice40/arch_pybindings.cc
+++ b/ice40/arch_pybindings.cc
@@ -59,6 +59,7 @@
 
     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, HierarchicalCell> HierarchyMap;
     typedef std::unordered_map<IdString, IdString> AliasMap;
 
     auto belpin_cls = class_<ContextualWrapper<BelPin>>("BelPin", no_init);
@@ -75,6 +76,7 @@
 
     WRAP_MAP_UPTR(CellMap, "IdCellMap");
     WRAP_MAP_UPTR(NetMap, "IdNetMap");
+    WRAP_MAP(HierarchyMap, wrap_context<HierarchicalCell &>, "HierarchyMap");
 }
 
 NEXTPNR_NAMESPACE_END
diff --git a/python/report_hierarchy.py b/python/report_hierarchy.py
new file mode 100644
index 0000000..6d409a9
--- /dev/null
+++ b/python/report_hierarchy.py
@@ -0,0 +1,10 @@
+def visit(indent, data):
+	istr = " " * indent
+	print("{}{}: {}".format(istr, data.name, data.type))
+	for lname, gname in data.leaf_cells:
+		print("{}    {} -> {}".format(istr, lname, gname))
+	for lname, gname in data.hier_cells:
+		visit(indent + 4, ctx.hierarchy[gname])
+
+visit(0, ctx.hierarchy[ctx.top_module])
+