[clkmgr, rstmgr] Add clkmgr/rstmgr option to export selective clocks/resets

- The clocks / resets can be exported to multiple interfaces
- At the moment there is only ast

Signed-off-by: Timothy Chen <timothytim@google.com>

[topgen] automatically add exported clocks/resets to intermodule

Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/ip/clkmgr/data/clkmgr.hjson.tpl b/hw/ip/clkmgr/data/clkmgr.hjson.tpl
index 2182891..5c01a8e 100644
--- a/hw/ip/clkmgr/data/clkmgr.hjson.tpl
+++ b/hw/ip/clkmgr/data/clkmgr.hjson.tpl
@@ -57,6 +57,16 @@
     },
 % endfor
 
+  // Exported clocks
+% for intf in export_clks:
+    { struct:  "clkmgr_${intf}_out",
+      type:    "uni",
+      name:    "clocks_${intf}",
+      act:     "req",
+      package: "clkmgr_pkg",
+    },
+% endfor
+
     { struct:  "pwr_clk",
       type:    "req_rsp",
       name:    "pwr",
diff --git a/hw/ip/clkmgr/data/clkmgr.sv.tpl b/hw/ip/clkmgr/data/clkmgr.sv.tpl
index fc484ef..d093eb0 100644
--- a/hw/ip/clkmgr/data/clkmgr.sv.tpl
+++ b/hw/ip/clkmgr/data/clkmgr.sv.tpl
@@ -47,6 +47,9 @@
   input clk_hint_status_t status_i,
 
   // clock output interface
+% for intf in export_clks:
+  output clkmgr_${intf}_out_t clocks_${intf}_o,
+% endfor
   output clkmgr_out_t clocks_o
 
 );
@@ -227,6 +230,17 @@
   assign hw2reg.clk_hints_status.${k}_val.d = ${k}_en;
 % endfor
 
+  ////////////////////////////////////////////////////
+  // Exported clocks
+  ////////////////////////////////////////////////////
+
+% for intf, eps in export_clks.items():
+  % for ep, clks in eps.items():
+    % for clk in clks:
+  assign clocks_${intf}_o.clk_${intf}_${ep}_${clk} = clocks_o.clk_${clk};
+    % endfor
+  % endfor
+% endfor
 
   ////////////////////////////////////////////////////
   // Assertions
diff --git a/hw/ip/clkmgr/data/clkmgr_pkg.sv.tpl b/hw/ip/clkmgr/data/clkmgr_pkg.sv.tpl
index bc90f85..97b091f 100644
--- a/hw/ip/clkmgr/data/clkmgr_pkg.sv.tpl
+++ b/hw/ip/clkmgr/data/clkmgr_pkg.sv.tpl
@@ -35,6 +35,16 @@
 
   } clkmgr_out_t;
 
+% for intf, eps in export_clks.items():
+  typedef struct packed {
+  % for ep, clks in eps.items():
+    % for clk in clks:
+    logic clk_${intf}_${ep}_${clk};
+    % endfor
+  % endfor
+  } clkmgr_${intf}_out_t;
+% endfor
+
   typedef struct packed {
     logic [${num_hints}-1:0] idle;
   } clk_hint_status_t;
diff --git a/hw/ip/rstmgr/data/rstmgr.hjson.tpl b/hw/ip/rstmgr/data/rstmgr.hjson.tpl
index ef27af8..6dd745c 100644
--- a/hw/ip/rstmgr/data/rstmgr.hjson.tpl
+++ b/hw/ip/rstmgr/data/rstmgr.hjson.tpl
@@ -56,6 +56,16 @@
       act:     "rcv",
       package: "rstmgr_pkg", // Origin package (only needs for the req)
     }
+
+    // Exported resets
+% for intf in export_rsts:
+    { struct:  "rstmgr_${intf}_out",
+      type:    "uni",
+      name:    "resets_${intf}",
+      act:     "req",
+      package: "rstmgr_pkg", // Origin package (only needs for the req)
+    }
+% endfor
   ],
 
   registers: [
diff --git a/hw/ip/rstmgr/data/rstmgr.sv.tpl b/hw/ip/rstmgr/data/rstmgr.sv.tpl
index f285cc7..66d267d 100644
--- a/hw/ip/rstmgr/data/rstmgr.sv.tpl
+++ b/hw/ip/rstmgr/data/rstmgr.sv.tpl
@@ -35,7 +35,11 @@
   input rstmgr_peri_t peri_i,
 
   // Interface to alert handler
-  // always on resets
+
+  // reset outputs
+% for intf in export_rsts:
+  output rstmgr_${intf}_out_t resets_${intf}_o,
+% endfor
   output rstmgr_out_t resets_o
 
 );
@@ -182,6 +186,17 @@
   );
 
   ////////////////////////////////////////////////////
+  // Exported resets                                //
+  ////////////////////////////////////////////////////
+% for intf, eps in export_rsts.items():
+  % for ep, rsts in eps.items():
+    % for rst in rsts:
+  assign resets_${intf}_o.rst_${intf}_${ep}_${rst}_n = resets_o.rst_${rst}_n;
+    % endfor
+  % endfor
+% endfor
+
+  ////////////////////////////////////////////////////
   // Assertions                                     //
   ////////////////////////////////////////////////////
 
diff --git a/hw/ip/rstmgr/data/rstmgr_pkg.sv.tpl b/hw/ip/rstmgr/data/rstmgr_pkg.sv.tpl
index 8db2354..7de607e 100644
--- a/hw/ip/rstmgr/data/rstmgr_pkg.sv.tpl
+++ b/hw/ip/rstmgr/data/rstmgr_pkg.sv.tpl
@@ -45,6 +45,17 @@
     logic ndmreset_req;
   } rstmgr_cpu_t;
 
+  // exported resets
+% for intf, eps in export_rsts.items():
+  typedef struct packed {
+  % for ep, rsts in eps.items():
+    % for rst in rsts:
+    logic rst_${intf}_${ep}_${rst}_n;
+    % endfor
+  % endfor
+  } rstmgr_${intf}_out_t;
+% endfor
+
   // default value for rstmgr_ast_rsp_t (for dangling ports)
   parameter rstmgr_cpu_t RSTMGR_CPU_DEFAULT = '{
     rst_cpu_n: 1'b1,
diff --git a/hw/top_earlgrey/data/top_earlgrey.hjson b/hw/top_earlgrey/data/top_earlgrey.hjson
index dbca431..b0cfd4f 100644
--- a/hw/top_earlgrey/data/top_earlgrey.hjson
+++ b/hw/top_earlgrey/data/top_earlgrey.hjson
@@ -288,6 +288,7 @@
       type: "usbdev",
       clock_srcs: {clk_i: "io", clk_usb_48mhz_i: "usb"},
       clock_group: "peri",
+      clock_reset_export: ["ast"],
       reset_connections: {rst_ni: "sys_io", rst_usb_48mhz_ni: "usb"},
       base_addr: "0x40150000",
     },
@@ -295,6 +296,7 @@
       type: "sensor_ctrl",
       clock_srcs: {clk_i: "io"},
       clock_group: "secure",
+      clock_reset_export: ["ast"],
       reset_connections: {rst_ni: "sys_io"},
       base_addr: "0x40170000",
       top_only: "true"
@@ -414,7 +416,7 @@
         'clkmgr.clk_main': 'clk_main',
         'clkmgr.clk_io': 'clk_io',
         'clkmgr.clk_usb': 'clk_usb',
-        'clkmgr.clk_aon': 'clk_aon'
+        'clkmgr.clk_aon': 'clk_aon',
         'rstmgr.ast': '',
         'pwrmgr.pwr_ast': '',
         'sensor_ctrl.ast_alert': '',
diff --git a/util/topgen.py b/util/topgen.py
index 130fa65..73d9779 100755
--- a/util/topgen.py
+++ b/util/topgen.py
@@ -556,6 +556,7 @@
                                  ft_clks=ft_clks,
                                  rg_clks=rg_clks,
                                  sw_clks=sw_clks,
+                                 export_clks=top['exported_clks'],
                                  hint_clks=hint_clks)
             except:  # noqa: E722
                 log.error(exceptions.text_error_template().render())
@@ -683,7 +684,8 @@
                 out = tpl.render(clks=clks,
                                  sw_rsts=sw_rsts,
                                  output_rsts=output_rsts,
-                                 leaf_rsts=leaf_rsts)
+                                 leaf_rsts=leaf_rsts,
+                                 export_rsts=topcfg['exported_rsts'])
 
             except:  # noqa: E722
                 log.error(exceptions.text_error_template().render())
diff --git a/util/topgen/merge.py b/util/topgen/merge.py
index 6ff00e0..b5cce58 100644
--- a/util/topgen/merge.py
+++ b/util/topgen/merge.py
@@ -472,6 +472,15 @@
     return result
 
 
+# Check if the export field already exists
+# If yes, return it
+# If no, set a default and return that
+def check_clk_rst_export(module):
+    if 'clock_reset_export' not in module:
+        module['clock_reset_export'] = []
+    return module['clock_reset_export']
+
+
 def amend_clocks(top: OrderedDict):
     """Add a list of clocks to each clock group
        Amend the clock connections of each entry to reflect the actual gated clock
@@ -479,6 +488,7 @@
     clks_attr = top['clocks']
     clk_paths = clks_attr['hier_paths']
     groups_in_top = [x["name"].lower() for x in clks_attr['groups']]
+    exported_clks = OrderedDict()
 
     # Assign default parameters to source clocks
     for src in clks_attr['srcs']:
@@ -501,6 +511,9 @@
 
         clock_connections = OrderedDict()
 
+        # Ensure each module has a default case
+        export_if = check_clk_rst_export(ep)
+
         # if no clock group assigned, default is unique
         ep['clock_group'] = 'secure' if 'clock_group' not in ep else ep[
             'clock_group']
@@ -522,22 +535,25 @@
         for port, clk in ep['clock_srcs'].items():
             ep_clks.append(clk)
 
+            name = ''
             hier_name = clk_paths[src]
 
             if src == 'ext':
                 # clock comes from top ports
                 if clk == 'main':
-                    clk_name = "clk_i"
+                    name = "i"
                 else:
-                    clk_name = "clk_{}_i".format(clk)
+                    name = "{}_i".format(clk)
 
             elif unique == "yes":
                 # new unqiue clock name
-                clk_name = "clk_{}_{}".format(clk, ep_name)
+                name = "{}_{}".format(clk, ep_name)
 
             else:
                 # new group clock name
-                clk_name = "clk_{}_{}".format(clk, ep_grp)
+                name = "{}_{}".format(clk, ep_grp)
+
+            clk_name = "clk_" + name
 
             # add clock to a particular group
             clks_attr['groups'][cg_idx]['clocks'][clk_name] = clk
@@ -545,9 +561,31 @@
             # add clock connections
             clock_connections[port] = hier_name + clk_name
 
+            # clocks for this module are exported
+            for intf in export_if:
+                log.info("{} export clock name is {}".format(ep_name, name))
+
+                # create dict entry if it does not exit
+                if intf not in exported_clks:
+                    exported_clks[intf] = OrderedDict()
+
+                # if first time encounter end point, declare
+                if ep_name not in exported_clks[intf]:
+                    exported_clks[intf][ep_name] = []
+
+                # append clocks
+                exported_clks[intf][ep_name].append(name)
+
         # Add to endpoint structure
         ep['clock_connections'] = clock_connections
 
+    # add entry to top level json
+    top['exported_clks'] = exported_clks
+
+    # add entry to inter_module automatically
+    for intf in top['exported_clks']:
+        top['inter_module']['external']['clkmgr.clocks_{}'.format(intf)] = "clks_{}".format(intf)
+
 
 def amend_resets(top):
     """Add a path variable to reset declaration
@@ -572,6 +610,32 @@
             log.error("{} type is invalid".format(reset["type"]))
 
     top["reset_paths"] = reset_paths
+
+    # Generate exported reset list
+    exported_rsts = OrderedDict()
+    for module in top["module"]:
+
+        # This code is here to ensure if amend_clocks/resets switched order
+        # everything would still work
+        export_if = check_clk_rst_export(module)
+
+        # There may be multiple export interfaces
+        for intf in export_if:
+            # create dict entry if it does not exit
+            if intf not in exported_rsts:
+                exported_rsts[intf] = OrderedDict()
+
+            # grab directly from reset_connections definition
+            rsts = [rst for rst in module['reset_connections'].values()]
+            exported_rsts[intf][module['name']] = rsts
+
+    # add entry to top level json
+    top['exported_rsts'] = exported_rsts
+
+    # add entry to inter_module automatically
+    for intf in top['exported_rsts']:
+        top['inter_module']['external']['rstmgr.resets_{}'.format(intf)] = "rsts_{}".format(intf)
+
     return