[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/hw/ip/rstmgr/data/rstmgr.sv.tpl b/hw/ip/rstmgr/data/rstmgr.sv.tpl
index 441e591..5853f19 100644
--- a/hw/ip/rstmgr/data/rstmgr.sv.tpl
+++ b/hw/ip/rstmgr/data/rstmgr.sv.tpl
@@ -51,23 +51,32 @@
   // receive POR and stretch
   // The por is at first stretched and synced on clk_aon
   // The rst_ni and pok_i input will be changed once AST is integrated
-  logic rst_por_aon_n;
-  rstmgr_por u_rst_por_aon (
-    .clk_i(clk_aon_i),
-    .rst_ni(ast_i.aon_pok),
-    .scan_rst_ni,
-    .scanmode_i,
-    .rst_no(rst_por_aon_n)
-  );
+  logic [PowerDomains-1:0] rst_por_aon_n;
 
-  prim_clock_mux2 #(
-    .NoFpgaBufG(1'b1)
-  ) u_rst_por_aon_n_mux (
-    .clk0_i(rst_por_aon_n),
-    .clk1_i(scan_rst_ni),
-    .sel_i(scanmode_i),
-    .clk_o(resets_o.rst_por_aon_n)
-  );
+  for (genvar i = 0; i < PowerDomains; i++) begin : gen_rst_por_aon
+    if (i == DomainAonSel) begin : gen_rst_por_aon_normal
+      rstmgr_por u_rst_por_aon (
+        .clk_i(clk_aon_i),
+        .rst_ni(ast_i.aon_pok),
+        .scan_rst_ni,
+        .scanmode_i,
+        .rst_no(rst_por_aon_n[i])
+      );
+
+      prim_clock_mux2 #(
+        .NoFpgaBufG(1'b1)
+      ) u_rst_por_aon_n_mux (
+        .clk0_i(rst_por_aon_n[i]),
+        .clk1_i(scan_rst_ni),
+        .sel_i(scanmode_i),
+        .clk_o(resets_o.rst_por_aon_n[i])
+      );
+    end else begin : gen_rst_por_aon_tieoff
+      assign rst_por_aon_n[i] = 1'b0;
+      assign resets_o.rst_por_aon_n[i] = rst_por_aon_n[i];
+    end
+  end
+
 
   ////////////////////////////////////////////////////
   // Register Interface                             //
@@ -75,7 +84,7 @@
 
   // local_rst_n is the reset used by the rstmgr for its internal logic
   logic local_rst_n;
-  assign local_rst_n = resets_o.rst_por_io_div2_n;
+  assign local_rst_n = resets_o.rst_por_io_div2_n[DomainAonSel];
 
   rstmgr_reg_pkg::rstmgr_reg2hw_t reg2hw;
   rstmgr_reg_pkg::rstmgr_hw2reg_t hw2reg;
@@ -176,37 +185,43 @@
   // leaf reset in the system                       //
   // These should all be generated                  //
   ////////////////////////////////////////////////////
+  // To simplify generation, each reset generates all associated power domain outputs.
+  // If a reset does not support a particular power domain, that reset is always hard-wired to 0.
 
 % for rst in leaf_rsts:
-  logic rst_${rst['name']}_n;
-
+  logic [PowerDomains-1:0] rst_${rst['name']}_n;
+  % for domain in power_domains:
+    % if domain in rst['domains']:
   prim_flop_2sync #(
     .Width(1),
     .ResetValue('0)
-  ) u_${rst['name']} (
+  ) u_${domain.lower()}_${rst['name']} (
     .clk_i(clk_${rst['clk']}_i),
-  % if "domain" in rst:
-    .rst_ni(rst_${rst['parent']}_n[${rst['domain']}]),
-  % else:
-    .rst_ni(rst_${rst['parent']}_n),
-  % endif
-  % if "sw" in rst:
+    .rst_ni(rst_${rst['parent']}_n[Domain${domain}Sel]),
+      % if "sw" in rst:
     .d_i(sw_rst_ctrl_n[${rst['name'].upper()}]),
-  % else:
+      % else:
     .d_i(1'b1),
-  % endif
-    .q_o(rst_${rst['name']}_n)
+      % endif
+    .q_o(rst_${rst['name']}_n[Domain${domain}Sel])
   );
 
   prim_clock_mux2 #(
     .NoFpgaBufG(1'b1)
-  ) u_${rst['name']}_mux (
-    .clk0_i(rst_${rst['name']}_n),
+  ) u_${domain.lower()}_${rst['name']}_mux (
+    .clk0_i(rst_${rst['name']}_n[Domain${domain}Sel]),
     .clk1_i(scan_rst_ni),
     .sel_i(scanmode_i),
-    .clk_o(resets_o.rst_${rst['name']}_n)
+    .clk_o(resets_o.rst_${rst['name']}_n[Domain${domain}Sel])
   );
 
+    % else:
+  assign rst_${rst['name']}_n[Domain${domain}Sel] = 1'b0;
+  assign resets_o.rst_${rst['name']}_n[Domain${domain}Sel] = rst_${rst['name']}_n[Domain${domain}Sel];
+
+
+    % endif
+  % endfor
 % endfor
 
   ////////////////////////////////////////////////////
diff --git a/hw/ip/rstmgr/data/rstmgr_pkg.sv.tpl b/hw/ip/rstmgr/data/rstmgr_pkg.sv.tpl
index 538c5a1..0f6c4a3 100644
--- a/hw/ip/rstmgr/data/rstmgr_pkg.sv.tpl
+++ b/hw/ip/rstmgr/data/rstmgr_pkg.sv.tpl
@@ -8,14 +8,13 @@
 
 package rstmgr_pkg;
 
-  // global constants
-  parameter int ALWAYS_ON_SEL   = pwrmgr_pkg::ALWAYS_ON_DOMAIN;
+  // Power domain parameters
+  parameter int PowerDomains = ${len(power_domains)};
+  % for power_domain in power_domains:
+  parameter int Domain${power_domain}Sel = ${loop.index};
+  % endfor
 
-  // params that reference pwrmgr, should be replaced once pwrmgr is merged
-  parameter int PowerDomains  = pwrmgr_pkg::PowerDomains;
-  //parameter int HwResetReqs   = pwrmgr_pkg::NumRstReqs;
-
-  // calculated domains
+  // Number of non-always-on domains
   parameter int OffDomains = PowerDomains-1;
 
   // positions of software controllable reset bits
@@ -37,7 +36,7 @@
   // This should be templatized and generated
   typedef struct packed {
 % for rst in output_rsts:
-    logic rst_${rst['name']}_n;
+    logic [PowerDomains-1:0] rst_${rst['name']}_n;
 % endfor
   } rstmgr_out_t;
 
@@ -52,7 +51,7 @@
   typedef struct packed {
   % for ep, rsts in eps.items():
     % for rst in rsts:
-    logic rst_${intf}_${ep}_${rst}_n;
+    logic [PowerDomains-1:0] rst_${intf}_${ep}_${rst}_n;
     % endfor
   % endfor
   } rstmgr_${intf}_out_t;
diff --git a/hw/ip/rstmgr/rtl/rstmgr_ctrl.sv b/hw/ip/rstmgr/rtl/rstmgr_ctrl.sv
index 1bb323f..290d236 100644
--- a/hw/ip/rstmgr/rtl/rstmgr_ctrl.sv
+++ b/hw/ip/rstmgr/rtl/rstmgr_ctrl.sv
@@ -18,12 +18,6 @@
   output logic [PowerDomains-1:0] rst_no
 );
 
-  // The code below always assumes the always on domain is index 0
-  `ASSERT_INIT(AlwaysOnIndex_A, ALWAYS_ON_SEL == 0)
-
-  // when there are multiple on domains, the latter 1 should become another parameter
-  localparam int OffDomainSelStart = ALWAYS_ON_SEL + 1;
-
   // the always on root reset
   logic rst_aon_nq;
 
@@ -47,16 +41,15 @@
   prim_flop u_aon_rst (
     .clk_i,
     .rst_ni,
-    .d_i(~rst_req_i[ALWAYS_ON_SEL] & rst_parent_synced[ALWAYS_ON_SEL]),
+    .d_i(~rst_req_i[DomainAonSel] & rst_parent_synced[DomainAonSel]),
     .q_o(rst_aon_nq)
   );
 
-
   // the non-always-on domains
   // These reset whenever the always on domain reset, to ensure power definition consistency.
   // By extension, they also reset whenever the root (rst_ni) resets
-  assign rst_pd_nd = ~rst_req_i[OffDomainSelStart +: OffDomains] &
-                     rst_parent_synced[OffDomainSelStart +: OffDomains];
+  assign rst_pd_nd = ~rst_req_i[Domain0Sel +: OffDomains] &
+                     rst_parent_synced[Domain0Sel +: OffDomains];
 
   prim_flop u_pd_rst (
     .clk_i,
diff --git a/hw/top_earlgrey/data/top_earlgrey.hjson b/hw/top_earlgrey/data/top_earlgrey.hjson
index 6d6b231..ce314ee 100644
--- a/hw/top_earlgrey/data/top_earlgrey.hjson
+++ b/hw/top_earlgrey/data/top_earlgrey.hjson
@@ -15,6 +15,17 @@
   // 32-bit datawidth
   datawidth: "32",
 
+  // Power information for the design
+  power: {
+    // Power domains supported by the design
+    // Aon represents domain aon
+    // 0 represents domain 0
+    domains: ["Aon", "0"],
+
+    // Default power domain used for the design
+    default: "0"
+  },
+
   // This is the clock data structure of the design.
   // The hier path refers to the clock reference path (struct / port)
   //   - The top/ext desgination follows the same scheme as inter-module
@@ -109,6 +120,7 @@
     // gen: whether the reset is generated
     // true: it is a generated reset inside rstmgr
     // false: it is a hardwired design reset inside rstmgr (roots and por)
+    // For non-generated resets, the parent / domain definitions have no meaning.
     //
     // type: the reset type [ext, top]
     // ext: the reset is coming in from the ports, external to earlgrey
@@ -118,34 +130,32 @@
     // parent: The parent reset
     // If type is "ext", there is no root, since it is external
     //
-    // domain: The power domain
-    // If no domain, it means there is no choice, just inherits from root.
-    // Otherwise, selects the domain to which it is related
-    // 0 is defaulted for always on.
-    // TBD: This should eventually be changed to a name->index project wide lookup
+    // domains: The power domains of a particular reset
+    // This is a list of of the supported power domains.
+    // Valid values are Aon and (power domain)0 ~ (power domain)1.
+    // If no value is supplied, the default is only the Aon version.
     //
     // clk:  related clock domain for synchronous release
     // If type is "por", there is not related clock, since it is
     // likely external or generated from a voltage comparator
     //
     nodes: [
-      { name: "rst_ni",      gen: false, type: "ext"                                                    }
-      { name: "por_aon",     gen: false, type: "top",              parent: "rst_ni",  clk: "aon"        }
-      { name: "lc_src",      gen: false, type: "int",              parent: "por",     clk: "io_div2"    }
-      { name: "sys_src",     gen: false, type: "int",              parent: "por",     clk: "io_div2"    }
-      { name: "por",         gen: true,  type: "top",              parent: "por_aon", clk: "main"       }
-      { name: "por_io",      gen: true,  type: "top",              parent: "por_aon", clk: "io"         }
-      { name: "por_io_div2", gen: true,  type: "top",              parent: "por_aon", clk: "io_div2"    }
-      { name: "por_io_div4", gen: true , type: "top",              parent: "por_aon", clk: "io_div4"    }
-      { name: "por_usb",     gen: true,  type: "top",              parent: "por_aon", clk: "usb"        }
-      { name: "lc",          gen: true,  type: "top", domain: "0", parent: "lc_src",  clk: "main"       }
-      { name: "lc_io",       gen: true,  type: "top", domain: "0", parent: "lc_src",  clk: "io_div4"    }
-      { name: "sys",         gen: true,  type: "top", domain: "0", parent: "sys_src", clk: "main"       }
-      { name: "sys_io",      gen: true,  type: "top", domain: "0", parent: "sys_src", clk: "io_div2"    }
-      { name: "sys_io_div4", gen: true,  type: "top", domain: "0", parent: "sys_src", clk: "io_div4"    }
-      { name: "sys_aon",     gen: true,  type: "top", domain: "0", parent: "sys_src", clk: "aon"        }
-      { name: "spi_device",  gen: true,  type: "top", domain: "0", parent: "sys_src", clk: "io_div2", sw: 1 }
-      { name: "usb",         gen: true,  type: "top", domain: "0", parent: "sys_src", clk: "usb",     sw: 1 }
+      { name: "rst_ni",      gen: false, type: "ext",                                                          }
+      { name: "por_aon",     gen: false, type: "top", domains: ["Aon"     ],                    clk: "aon"     }
+      { name: "lc_src",      gen: false, type: "int", domains: ["Aon", "0"],                    clk: "io_div4" }
+      { name: "sys_src",     gen: false, type: "int", domains: ["Aon", "0"],                    clk: "io_div4" }
+      { name: "por",         gen: true,  type: "top", domains: ["Aon"     ], parent: "por_aon", clk: "main"    }
+      { name: "por_io",      gen: true,  type: "top", domains: ["Aon",    ], parent: "por_aon", clk: "io"      }
+      { name: "por_io_div2", gen: true,  type: "top", domains: ["Aon",    ], parent: "por_aon", clk: "io_div2" }
+      { name: "por_io_div4", gen: true , type: "top", domains: ["Aon",    ], parent: "por_aon", clk: "io_div4" }
+      { name: "por_usb",     gen: true,  type: "top", domains: ["Aon",    ], parent: "por_aon", clk: "usb"     }
+      { name: "lc",          gen: true,  type: "top", domains: [       "0"], parent: "lc_src",  clk: "main"    }
+      { name: "lc_io_div4",  gen: true,  type: "top", domains: [       "0"], parent: "lc_src",  clk: "io_div4" }
+      { name: "sys",         gen: true,  type: "top", domains: ["Aon", "0"], parent: "sys_src", clk: "main"    }
+      { name: "sys_io_div4", gen: true,  type: "top", domains: ["Aon", "0"], parent: "sys_src", clk: "io_div4" }
+      { name: "sys_aon",     gen: true,  type: "top", domains: ["Aon",    ], parent: "sys_src", clk: "aon"     }
+      { name: "spi_device",  gen: true,  type: "top", domains: [       "0"], parent: "sys_src", clk: "io_div2", sw: 1 }
+      { name: "usb",         gen: true,  type: "top", domains: ["Aon",    ], parent: "sys_src", clk: "usb",     sw: 1 }
     ]
   }
 
@@ -241,6 +251,7 @@
       clock_srcs: {clk_i: "main", clk_aon_i: "aon"},
       clock_group: "secure",
       reset_connections: {rst_ni: "sys", rst_aon_ni: "sys_aon"},
+      domain: "Aon",
       base_addr: "0x40070000",
       generated: "true"
     },
@@ -251,6 +262,7 @@
       clock_srcs: {clk_i: "main"},
       clock_group: "secure",
       reset_connections: {rst_ni: "sys"},
+      domain: "Aon",
       base_addr: "0x40160000",
       generated: "true"
     },
@@ -272,6 +284,7 @@
       clock_srcs: {clk_i: "io_div4", clk_slow_i: "aon"},
       clock_group: "powerup",
       reset_connections: {rst_ni: "por", rst_slow_ni: "por_aon"},
+      domain: "Aon",
       base_addr: "0x400A0000",
       generated: "true"         // Indicate this module is generated in the topgen
 
@@ -282,6 +295,7 @@
                    clk_io_div2_i: "io_div2", clk_io_div4_i: "io_div4"},
       clock_group: "powerup",
       reset_connections: {rst_ni: "rst_ni"},
+      domain: "Aon",
       base_addr: "0x400B0000",
       generated: "true"         // Indicate this module is generated in the topgen
     },
@@ -289,8 +303,9 @@
       type: "clkmgr",
       clock_srcs: {clk_i: "io_div4"},
       clock_group: "powerup",
-      reset_connections: {rst_ni: "por_io", rst_main_ni: "por", rst_io_ni: "por_io", rst_usb_ni: "por_usb"
+      reset_connections: {rst_ni: "por_io_div4", rst_main_ni: "por", rst_io_ni: "por_io", rst_usb_ni: "por_usb"
                           rst_io_div2_ni: "por_io_div2", rst_io_div4_ni: "por_io_div4"},
+      domain: "Aon",
       base_addr: "0x400C0000",
       generated: "true"
     },
@@ -309,6 +324,7 @@
       clock_group: "peri",
       clock_reset_export: ["ast"],
       reset_connections: {rst_ni: "sys_io_div4", rst_usb_48mhz_ni: "usb"},
+      domain: "Aon",
       base_addr: "0x40150000",
     },
     { name: "sensor_ctrl",
@@ -317,6 +333,7 @@
       clock_group: "secure",
       clock_reset_export: ["ast"],
       reset_connections: {rst_ni: "sys_io_div4"},
+      domain: "Aon",
       base_addr: "0x40170000",
       top_only: "true"
     },
@@ -331,7 +348,7 @@
       type: "otp_ctrl",
       clock_srcs: {clk_i: "io_div4"},
       clock_group: "timers",
-      reset_connections: {rst_ni: "lc_io"},
+      reset_connections: {rst_ni: "lc_io_div4"},
       base_addr: "0x401b0000",
     },
     { name: "otbn",
@@ -383,9 +400,10 @@
       clock_srcs: {clk_i: "io_div4"},
       clock_group: "infra",
       reset_connections: {rst_ni: "sys_io_div4"},
+      domain: "Aon",
       type: "ram_1p",
       base_addr: "0x18000000",
-      size: "0x1000"
+      size: "0x1000",
       inter_signal_list: [
         { struct: "tl"
           package: "tlul_pkg"
diff --git a/hw/top_earlgrey/data/top_earlgrey.sv.tpl b/hw/top_earlgrey/data/top_earlgrey.sv.tpl
index 5286036..13e37fe 100644
--- a/hw/top_earlgrey/data/top_earlgrey.sv.tpl
+++ b/hw/top_earlgrey/data/top_earlgrey.sv.tpl
@@ -28,6 +28,8 @@
 cpu_clk = top['clocks']['hier_paths']['top'] + "clk_proc_main"
 cpu_rst = top["reset_paths"]["sys"]
 dm_rst = top["reset_paths"]["lc"]
+
+unused_resets = lib.get_unused_resets(top)
 %>\
 module top_${top["name"]} #(
   // Auto-inferred parameters
@@ -185,6 +187,14 @@
 
 ## Inter-module signal collection
 
+  // Unused reset signals
+% for k, v in unused_resets.items():
+  logic unused_d${v.lower()}_rst_${k};
+% endfor
+% for k, v in unused_resets.items():
+  assign unused_d${v.lower()}_rst_${k} = ${lib.get_reset_path(k, v, top['resets'])};
+% endfor
+
   // Non-debug module reset == reset for everything except for the debug module
   logic ndmreset_req;
 
@@ -215,7 +225,7 @@
   ) u_rv_core_ibex (
     // clock and reset
     .clk_i                (${cpu_clk}),
-    .rst_ni               (${cpu_rst}),
+    .rst_ni               (${cpu_rst}[rstmgr_pkg::Domain0Sel]),
     .test_en_i            (1'b0),
     // static pinning
     .hart_id_i            (32'b0),
@@ -247,7 +257,7 @@
     .IdcodeValue (JTAG_IDCODE)
   ) u_dm_top (
     .clk_i         (${cpu_clk}),
-    .rst_ni        (${dm_rst}),
+    .rst_ni        (${dm_rst}[rstmgr_pkg::Domain0Sel]),
     .testmode_i    (1'b0),
     .ndmreset_o    (ndmreset_req),
     .dmactive_o    (),
@@ -272,7 +282,7 @@
   );
 
   assign rstmgr_cpu.ndmreset_req = ndmreset_req;
-  assign rstmgr_cpu.rst_cpu_n = ${top["reset_paths"]["sys"]};
+  assign rstmgr_cpu.rst_cpu_n = ${top["reset_paths"]["sys"]}[rstmgr_pkg::Domain0Sel];
 
 ## Memory Instantiation
 % for m in top["memory"]:
@@ -306,8 +316,8 @@
     % for key in clocks:
     .${key}   (${clocks[key]}),
     % endfor
-    % for key in resets:
-    .${key}   (${top["reset_paths"][resets[key]]}),
+    % for key, value in resets.items():
+    .${key}   (${value}),
     % endfor
     .tl_i     (${m["name"]}_tl_req),
     .tl_o     (${m["name"]}_tl_rsp),
@@ -334,8 +344,8 @@
     % for key in clocks:
     .${key}   (${clocks[key]}),
     % endfor
-    % for key in resets:
-    .${key}   (${top["reset_paths"][resets[key]]}),
+    % for key, value in resets.items():
+    .${key}   (${value}),
     % endfor
 
     .req_i    (${m["name"]}_req),
@@ -371,8 +381,8 @@
     % for key in clocks:
     .${key}   (${clocks[key]}),
     % endfor
-    % for key in resets:
-    .${key}   (${top["reset_paths"][resets[key]]}),
+    % for key, value in resets.items():
+    .${key}   (${value}),
     % endfor
 
     .tl_i     (${m["name"]}_tl_req),
@@ -397,8 +407,8 @@
     % for key in clocks:
     .${key}   (${clocks[key]}),
     % endfor
-    % for key in resets:
-    .${key}   (${top["reset_paths"][resets[key]]}),
+    % for key, value in resets.items():
+    .${key}   (${value}),
     % endfor
     .req_i    (${m["name"]}_req),
     .addr_i   (${m["name"]}_addr),
@@ -427,8 +437,8 @@
     % for key in clocks:
     .${key}   (${clocks[key]}),
     % endfor
-    % for key in resets:
-    .${key}   (${top["reset_paths"][resets[key]]}),
+    % for key, value in resets.items():
+    .${key}   (${value}),
     % endfor
 
     .tl_i     (${m["name"]}_tl_req),
@@ -449,8 +459,8 @@
     % for key in clocks:
     .${key}   (${clocks[key]}),
     % endfor
-    % for key in resets:
-    .${key}   (${top["reset_paths"][resets[key]]}),
+    % for key, value in resets.items():
+    .${key}   (${value}),
     % endfor
     .host_req_i      (flash_host_req),
     .host_addr_i     (flash_host_addr),
@@ -599,7 +609,7 @@
       .${k} (${v}),
     % endfor
     % for k, v in m["reset_connections"].items():
-      .${k} (${top["reset_paths"][v]})${"," if not loop.last else ""}
+      .${k} (${v})${"," if not loop.last else ""}
     % endfor
   );
 
@@ -622,7 +632,7 @@
     .${k} (${v}),
   % endfor
   % for k, v in xbar["reset_connections"].items():
-    .${k} (${top["reset_paths"][v]}),
+    .${k} (${v}),
   % endfor
 
   ## Inter-module signal
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)