[top] Add power attribute to the design

- split reset manager outputs into always-on and non-always-on
- add domain attribute to modules, memories and xbars
- do appropriate checks for power domain
- create appropriate top level paths when referencing reset

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

[util] typo fixes

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

[top] updates to tie off unused resets

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

[util] minor cleanup

Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/util/topgen.py b/util/topgen.py
index 04acd46..0735004 100755
--- a/util/topgen.py
+++ b/util/topgen.py
@@ -693,6 +693,7 @@
             tpl = Template(fin.read())
             try:
                 out = tpl.render(clks=clks,
+                                 power_domains=topcfg['power']['domains'],
                                  num_rstreqs=n_rstreqs,
                                  sw_rsts=sw_rsts,
                                  output_rsts=output_rsts,
diff --git a/util/topgen/lib.py b/util/topgen/lib.py
index c887595..099b61c 100644
--- a/util/topgen/lib.py
+++ b/util/topgen/lib.py
@@ -231,11 +231,43 @@
         return "clk_{}_i".format(clk)
 
 
-def get_reset_path(resets, name):
+def get_reset_path(reset, domain, reset_cfg):
     """Return the appropriate reset path given name
     """
-    for reset in resets:
-        if reset['name'] == name:
-            return reset['path']
+    # find matching node for reset
+    node_match = [node for node in reset_cfg['nodes'] if node['name'] == reset]
+    assert len(node_match) == 1
+    reset_type = node_match[0]['type']
 
-    return "none"
+    # find matching path
+    hier_path = ""
+    if reset_type == "int":
+        log.debug("{} used as internal reset".format(reset["name"]))
+    else:
+        hier_path = reset_cfg['hier_paths'][reset_type]
+
+    # find domain selection
+    domain_sel = ''
+    if reset_type not in ["ext", "int"]:
+        domain_sel = "[rstmgr_pkg::Domain{}Sel]".format(domain)
+
+    reset_path = ""
+    if reset_type == "ext":
+        reset_path = reset
+    else:
+        reset_path = "{}rst_{}_n{}".format(hier_path, reset, domain_sel)
+
+    return reset_path
+
+
+def get_unused_resets(top):
+    """Return dict of unused resets and associated domain
+    """
+    unused_resets = OrderedDict()
+    unused_resets = {reset['name']: domain
+                     for reset in top['resets']['nodes']
+                     for domain in top['power']['domains']
+                     if reset['type'] == 'top' and domain not in reset['domains']}
+
+    log.debug("Unused resets are {}".format(unused_resets))
+    return unused_resets
diff --git a/util/topgen/merge.py b/util/topgen/merge.py
index d7cc97b..35e5a32 100644
--- a/util/topgen/merge.py
+++ b/util/topgen/merge.py
@@ -672,29 +672,10 @@
 
 
 def amend_resets(top):
-    """Add a path variable to reset declaration
+
+    """Generate exported reset structure and automatically connect to
+       intermodule.
     """
-    reset_paths = OrderedDict()
-    reset_hiers = top["resets"]['hier_paths']
-
-    for reset in top["resets"]["nodes"]:
-
-        if "type" not in reset:
-            log.error("{} missing type field".format(reset["name"]))
-            return
-
-        if reset["type"] == "top":
-            reset_paths[reset["name"]] = "{}rst_{}_n".format(
-                reset_hiers["top"], reset["name"])
-        elif reset["type"] == "ext":
-            reset_paths[reset["name"]] = "{}rst_ni".format(reset_hiers["ext"])
-        elif reset["type"] == "int":
-            log.info("{} used as internal reset".format(reset["name"]))
-        else:
-            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"]:
@@ -720,6 +701,36 @@
     for intf in top['exported_rsts']:
         top['inter_module']['external']['rstmgr.resets_{}'.format(intf)] = "rsts_{}".format(intf)
 
+    """Discover the full path and selection to each reset connection.
+       This is done by modifying the reset connection of each end point.
+    """
+    for end_point in top['module'] + top['memory'] + top['xbar']:
+        for port, net in end_point['reset_connections'].items():
+            reset_path = lib.get_reset_path(net, end_point['domain'], top['resets'])
+            end_point['reset_connections'][port] = reset_path
+
+    # reset paths are still needed temporarily until host only modules are properly automated
+    reset_paths = OrderedDict()
+    reset_hiers = top["resets"]['hier_paths']
+
+    for reset in top["resets"]["nodes"]:
+        if "type" not in reset:
+            log.error("{} missing type field".format(reset["name"]))
+            return
+
+        if reset["type"] == "top":
+            reset_paths[reset["name"]] = "{}rst_{}_n".format(
+                reset_hiers["top"], reset["name"])
+
+        elif reset["type"] == "ext":
+            reset_paths[reset["name"]] = reset_hiers["ext"] + reset['name']
+        elif reset["type"] == "int":
+            log.info("{} used as internal reset".format(reset["name"]))
+        else:
+            log.error("{} type is invalid".format(reset["type"]))
+
+    top["reset_paths"] = reset_paths
+
     return
 
 
@@ -952,9 +963,6 @@
     # as part of alerts.
     # amend_clocks(gencfg)
 
-    # Add path names to declared resets
-    amend_resets(gencfg)
-
     # Combine the wakeups
     amend_wkup(gencfg)
     amend_reset_request(gencfg)
@@ -977,6 +985,10 @@
     for xbar in gencfg["xbar"]:
         xbar_cross(xbar, gencfg["xbar"])
 
+    # Add path names to declared resets.
+    # Declare structure for exported resets.
+    amend_resets(gencfg)
+
     # remove unwanted fields 'debug_mem_base_addr'
     gencfg.pop('debug_mem_base_addr', None)
 
diff --git a/util/topgen/validate.py b/util/topgen/validate.py
index ef6764b..145d01e 100644
--- a/util/topgen/validate.py
+++ b/util/topgen/validate.py
@@ -468,6 +468,46 @@
     return error
 
 
+def check_power_domains(top):
+    error = 0
+
+    # check that the default domain is valid
+    if top['power']['default'] not in top['power']['domains']:
+        error += 1
+        return error
+
+    # check that power domain definition is consistent with reset and module definition
+    for reset in top['resets']['nodes']:
+        if reset['gen']:
+            if 'domains' not in reset:
+                log.error("{} missing domain definition".format(reset['name']))
+                error += 1
+                return error
+            else:
+                for domain in reset['domains']:
+                    if domain not in top['power']['domains']:
+                        log.error("{} defined invalid domain {}".format(reset['name'], domain))
+                        error += 1
+                        return error
+
+    # Check that each module, xbar, memory has a power domain defined.
+    # If not, give it a default.
+    # If there is one defined, check that it is a valid definition
+    for end_point in top['module'] + top['memory'] + top['xbar']:
+        if 'domain' not in end_point:
+            log.warning("{} does not have a power domain defined, \
+            assigning default".format(end_point['name']))
+
+            end_point['domain'] = top['power']['default']
+        elif end_point['domain'] not in top['power']['domains']:
+            log.error("{} defined invalid domain {}".format(end_point['name'], end_point['domain']))
+            error += 1
+            return error
+
+    # arrived without incident, return
+    return error
+
+
 def validate_top(top, ipobjs, xbarobjs):
     # return as it is for now
     error = check_keys(top, top_required, top_optional, top_added, "top")
@@ -489,6 +529,9 @@
     # MEMORY check
     error += check_flash(top)
 
+    # Power domain check
+    error += check_power_domains(top)
+
     # Clock / Reset check
     error += check_clocks_resets(top, ipobjs, ip_idxs, xbarobjs, xbar_idxs)