[topgen] Use OrderedDicts in more places

In Python 3.5, dicts are not ordered. This causes issues when
regenerating output generated by iterating over a dict, which happens
frequently in topgen.

This commit comprehensively spreads the use of OrderedDict in topgen
such that we can re-generate tops in CI (which uses python 3.5) without
issues.

Signed-off-by: Sam Elliott <selliott@lowrisc.org>
diff --git a/hw/ip/clkmgr/data/clkmgr_pkg.sv.tpl b/hw/ip/clkmgr/data/clkmgr_pkg.sv.tpl
index 5944585..bc90f85 100644
--- a/hw/ip/clkmgr/data/clkmgr_pkg.sv.tpl
+++ b/hw/ip/clkmgr/data/clkmgr_pkg.sv.tpl
@@ -3,6 +3,8 @@
 // SPDX-License-Identifier: Apache-2.0
 
 <%
+from collections import OrderedDict
+
 clks_attr = cfg['clocks']
 grps = clks_attr['groups']
 num_hints = len(hint_clks)
@@ -19,7 +21,15 @@
   };
 
   typedef struct packed {
-% for clk in {**ft_clks, **hint_clks, **rg_clks, **sw_clks}:
+<%
+# Merge Clock Dicts together
+all_clocks = OrderedDict()
+all_clocks.update(ft_clks)
+all_clocks.update(hint_clks)
+all_clocks.update(rg_clks)
+all_clocks.update(sw_clks)
+%>\
+% for clk in all_clocks:
   logic ${clk};
 % endfor
 
diff --git a/hw/top_earlgrey/data/tb__xbar_connect.sv.tpl b/hw/top_earlgrey/data/tb__xbar_connect.sv.tpl
index 63f6d9b..dd0f9f7 100644
--- a/hw/top_earlgrey/data/tb__xbar_connect.sv.tpl
+++ b/hw/top_earlgrey/data/tb__xbar_connect.sv.tpl
@@ -9,18 +9,18 @@
 top_hier = 'tb.dut.top_' + top["name"] + '.'
 clk_hier = top_hier + top["clocks"]["hier_paths"]["top"]
 
-clk_src = {}
+clk_src = OrderedDict()
 for xbar in top["xbar"]:
   for clk, src in xbar["clock_srcs"].items():
     clk_src[clk] = src
 
-clk_freq = {}
+clk_freq = OrderedDict()
 for clock in top["clocks"]["srcs"]:
   if clock["name"] in clk_src.values():
     clk_freq[clock["name"]] = clock["freq"]
 
-hosts = {}
-devices = {}
+hosts = OrderedDict()
+devices = OrderedDict()
 for xbar in top["xbar"]:
   for node in xbar["nodes"]:
     if node["type"] == "host" and not node["xbar"]:
diff --git a/hw/top_earlgrey/data/top_earlgrey.h.tpl b/hw/top_earlgrey/data/top_earlgrey.h.tpl
index 0dd2064..09b1be2 100644
--- a/hw/top_earlgrey/data/top_earlgrey.h.tpl
+++ b/hw/top_earlgrey/data/top_earlgrey.h.tpl
@@ -86,6 +86,8 @@
 
 <%!
 
+from collections import OrderedDict
+
 def camelcase(str):
     """Turns a string from 'snake_case' to 'CamelCase'."""
     return "".join([part.capitalize() for part in str.split("_")])
@@ -107,7 +109,7 @@
 %>\
 ## This dictionary will be used in the C implementation to generate
 ## `top_${top["name"]}_plic_interrupt_for_peripheral`.
-<% c_gen_info["interrupt_id_map"] = {} %>\
+<% c_gen_info["interrupt_id_map"] = OrderedDict() %>\
 /**
  * PLIC Interrupt source peripheral enumeration.
  *
diff --git a/hw/top_earlgrey/data/xbar_env_pkg__params.sv.tpl b/hw/top_earlgrey/data/xbar_env_pkg__params.sv.tpl
index 885135f..cb90d6e 100644
--- a/hw/top_earlgrey/data/xbar_env_pkg__params.sv.tpl
+++ b/hw/top_earlgrey/data/xbar_env_pkg__params.sv.tpl
@@ -5,6 +5,8 @@
 // xbar_env_pkg__params generated by `topgen.py` tool
 
 <%
+  from collections import OrderedDict
+
   def is_device_a_xbar(dev_name):
     for xbar in top["xbar"]:
       if xbar["name"] == dev_name:
@@ -26,7 +28,7 @@
     return edge_devices
 
   # find device xbar and assign all its device nodes to it: "peri" -> "uart, gpio, ..."
-  xbar_device_dict = {}
+  xbar_device_dict = OrderedDict()
 
   for xbar in top["xbar"]:
     for n in xbar["nodes"]:
@@ -34,7 +36,7 @@
         xbar_device_dict[n["name"]] = get_xbar_edge_nodes(n["name"])
 
   # create the mapping: host with the corresponding devices map
-  host_dev_map = {}
+  host_dev_map = OrderedDict()
   for host, devices in top["xbar"][0]["connections"].items():
     dev_list = []
     for dev in devices:
diff --git a/util/topgen.py b/util/topgen.py
index 338d869..08f5e74 100755
--- a/util/topgen.py
+++ b/util/topgen.py
@@ -498,36 +498,36 @@
     # This includes two groups of clocks
     # Clocks fed from the always-on source
     # Clocks fed to the powerup group
-    ft_clks = {
-        clk: src
+    ft_clks = OrderedDict([
+        (clk, src)
         for grp in grps for (clk, src) in grp['clocks'].items()
         if src_aon_attr[src] or grp['name'] == 'powerup'
-    }
+    ])
 
     # root-gate clocks
-    rg_clks = {
-        clk: src
+    rg_clks = OrderedDict([
+        (clk, src)
         for grp in grps for (clk, src) in grp['clocks'].items()
         if grp['name'] != 'powerup' and grp['sw_cg'] == 'no' and
         not src_aon_attr[src]
-    }
+    ])
 
     # direct sw control clocks
-    sw_clks = {
-        clk: src
+    sw_clks = OrderedDict([
+        (clk, src)
         for grp in grps for (clk, src) in grp['clocks'].items()
         if grp['sw_cg'] == 'yes' and not src_aon_attr[src]
-    }
+    ])
 
     # sw hint clocks
-    hint_clks = {
-        clk: src
+    hint_clks = OrderedDict([
+        (clk, src)
         for grp in grps for (clk, src) in grp['clocks'].items()
         if grp['sw_cg'] == 'hint' and not src_aon_attr[src]
-    }
+    ])
 
-    out = StringIO()
     for idx, tpl in enumerate(tpls):
+        out = ""
         with tpl.open(mode='r', encoding='UTF-8') as fin:
             tpl = Template(fin.read())
             try:
diff --git a/util/topgen/merge.py b/util/topgen/merge.py
index cfc970f..f2e8442 100644
--- a/util/topgen/merge.py
+++ b/util/topgen/merge.py
@@ -450,7 +450,7 @@
     for src in clks_attr['srcs']:
         if 'derived' not in src:
             src['derived'] = "no"
-            src['params'] = {}
+            src['params'] = OrderedDict()
 
     # Default assignments
     for group in clks_attr['groups']:
@@ -518,7 +518,7 @@
 def amend_resets(top):
     """Add a path variable to reset declaration
     """
-    reset_paths = {}
+    reset_paths = OrderedDict()
 
     for reset in top["resets"]: