[clkmgr] Add clock-gated indication signals

This adds "clock gated" indication output signals that are
encoded using the `lc_tx_t`. These signals will be wired
over to the alert handler in a subsequent PR in order to
connect them to the alert low-power groups (LPGs).

Note that these signals will be passed through 2-stage
synchronizers on the alert handler side. The timing of
these signals does not have to be cycle accurate.

Note, this is dependent on #8417 (first commit will disappear).

Signed-off-by: Michael Schaffner <msf@google.com>
diff --git a/hw/ip/clkmgr/data/clkmgr.hjson.tpl b/hw/ip/clkmgr/data/clkmgr.hjson.tpl
index f75e6d0..7ce0f55 100644
--- a/hw/ip/clkmgr/data/clkmgr.hjson.tpl
+++ b/hw/ip/clkmgr/data/clkmgr.hjson.tpl
@@ -49,6 +49,13 @@
       package: "clkmgr_pkg",
     },
 
+    { struct:  "clkmgr_cg_en",
+      type:    "uni",
+      name:    "cg_en",
+      act:     "req",
+      package: "clkmgr_pkg",
+    },
+
     { struct:  "lc_tx",
       type:    "uni",
       name:    "lc_dft_en",
diff --git a/hw/ip/clkmgr/data/clkmgr.sv.tpl b/hw/ip/clkmgr/data/clkmgr.sv.tpl
index f4601d8..30ebb24 100644
--- a/hw/ip/clkmgr/data/clkmgr.sv.tpl
+++ b/hw/ip/clkmgr/data/clkmgr.sv.tpl
@@ -61,6 +61,9 @@
   // jittery enable
   output logic jitter_en_o,
 
+  // clock gated indications going to alert handlers
+  output clkmgr_cg_en_t cg_en_o,
+
   // clock output interface
 % for intf in cfg['exported_clks']:
   output clkmgr_${intf}_out_t clocks_${intf}_o,
@@ -189,6 +192,9 @@
     .clk_i(clk_${v.src.name}_i),
     .clk_o(clocks_o.${k})
   );
+
+  // clock gated indication for alert handler: these clocks are never gated.
+  assign cg_en_o.${k} = lc_ctrl_pkg::Off;
 % endfor
 
   ////////////////////////////////////////////////////
@@ -332,6 +338,14 @@
   ////////////////////////////////////////////////////
 % for k,v in typed_clocks.rg_clks.items():
   assign clocks_o.${k} = clk_${v.src.name}_root;
+
+  // clock gated indication for alert handler
+  prim_lc_sender u_prim_lc_sender_${k} (
+    .clk_i(clk_${v.src.name}_i),
+    .rst_ni(rst_${v.src.name}_ni),
+    .lc_en_i(((clk_${v.src.name}_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.${k})
+  );
 % endfor
 
   ////////////////////////////////////////////////////
@@ -363,15 +377,27 @@
     .lc_en_o(${k}_scanmode)
   );
 
+  logic ${k}_combined_en;
+  assign ${k}_combined_en = ${k}_sw_en & clk_${v.src.name}_en;
   prim_clock_gating #(
     .FpgaBufGlobal(1'b1) // This clock spans across multiple clock regions.
   ) u_${k}_cg (
     .clk_i(clk_${v.src.name}_root),
-    .en_i(${k}_sw_en & clk_${v.src.name}_en),
+    .en_i(${k}_combined_en),
     .test_en_i(${k}_scanmode == lc_ctrl_pkg::On),
     .clk_o(clocks_o.${k})
   );
 
+  // clock gated indication for alert handler
+  prim_lc_sender #(
+    .ResetValueIsOn(1)
+  ) u_prim_lc_sender_${k} (
+    .clk_i(clk_${v.src.name}_i),
+    .rst_ni(rst_${v.src.name}_ni),
+    .lc_en_i(((${k}_combined_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.${k})
+  );
+
 % endfor
 
   ////////////////////////////////////////////////////
@@ -408,15 +434,33 @@
     .lc_en_o(${clk}_scanmode)
   );
 
+  // Add a prim buf here to make sure the CG and the lc sender inputs
+  // are derived from the same physical signal.
+  logic ${clk}_combined_en;
+  prim_buf u_prim_buf_${clk}_en (
+    .in_i(${clk}_en & clk_${sig.src.name}_en),
+    .out_o(${clk}_combined_en)
+  );
+
   prim_clock_gating #(
     .FpgaBufGlobal(1'b0) // This clock is used primarily locally.
   ) u_${clk}_cg (
     .clk_i(clk_${sig.src.name}_root),
-    .en_i(${clk}_en & clk_${sig.src.name}_en),
+    .en_i(${clk}_combined_en),
     .test_en_i(${clk}_scanmode == lc_ctrl_pkg::On),
     .clk_o(clocks_o.${clk})
   );
 
+  // clock gated indication for alert handler
+  prim_lc_sender #(
+    .ResetValueIsOn(1)
+  ) u_prim_lc_sender_${clk} (
+    .clk_i(clk_${sig.src.name}_i),
+    .rst_ni(rst_${sig.src.name}_ni),
+    .lc_en_i(((${clk}_combined_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.${clk})
+  );
+
 % endfor
 
   // state readback
@@ -454,5 +498,6 @@
   `ASSERT_KNOWN(ExportClocksKownO_A, clocks_${intf}_o)
 % endfor
   `ASSERT_KNOWN(ClocksKownO_A, clocks_o)
+  `ASSERT_KNOWN(CgEnKnownO_A, cg_en_o)
 
 endmodule // clkmgr
diff --git a/hw/ip/clkmgr/data/clkmgr_pkg.sv.tpl b/hw/ip/clkmgr/data/clkmgr_pkg.sv.tpl
index 84c569b..21e74e8 100644
--- a/hw/ip/clkmgr/data/clkmgr_pkg.sv.tpl
+++ b/hw/ip/clkmgr/data/clkmgr_pkg.sv.tpl
@@ -14,13 +14,23 @@
 % endfor
   } hint_names_e;
 
+  // clocks generated and broadcast
   typedef struct packed {
 % for clk in typed_clocks.all_clocks():
     logic ${clk};
 % endfor
-
   } clkmgr_out_t;
 
+  // clock gating indication for alert handler
+  typedef struct packed {
+<% n_clk = 0 %>\
+% for clk in typed_clocks.all_clocks():
+    lc_ctrl_pkg::lc_tx_t ${clk};<% n_clk += 1 %>
+% endfor
+  } clkmgr_cg_en_t;
+
+  parameter int NumOutputClk = ${n_clk};
+
 % for intf, eps in cfg['exported_clks'].items():
   typedef struct packed {
   % for ep, clks in eps.items():
diff --git a/hw/ip/clkmgr/dv/tb.sv b/hw/ip/clkmgr/dv/tb.sv
index c08dd7b..2d05d58 100644
--- a/hw/ip/clkmgr/dv/tb.sv
+++ b/hw/ip/clkmgr/dv/tb.sv
@@ -105,6 +105,8 @@
     .lc_clk_byp_req_i (clkmgr_if.lc_clk_byp_req),
     .lc_clk_byp_ack_o (clkmgr_if.lc_clk_byp_ack),
 
+    .cg_en_o    ( ),
+
     .jitter_en_o(clkmgr_if.jitter_en_o),
     .clocks_o   (clkmgr_if.clocks_o)
   );
diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
index 364488a..6992eca 100644
--- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
+++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
@@ -2839,6 +2839,16 @@
           index: -1
         }
         {
+          name: cg_en
+          struct: clkmgr_cg_en
+          package: clkmgr_pkg
+          type: uni
+          act: req
+          width: 1
+          inst_name: clkmgr_aon
+          index: -1
+        }
+        {
           name: lc_dft_en
           struct: lc_tx
           package: lc_ctrl_pkg
@@ -14919,6 +14929,16 @@
         index: -1
       }
       {
+        name: cg_en
+        struct: clkmgr_cg_en
+        package: clkmgr_pkg
+        type: uni
+        act: req
+        width: 1
+        inst_name: clkmgr_aon
+        index: -1
+      }
+      {
         name: lc_dft_en
         struct: lc_tx
         package: lc_ctrl_pkg
diff --git a/hw/top_earlgrey/ip/clkmgr/clkmgr.core b/hw/top_earlgrey/ip/clkmgr/clkmgr.core
index 74f8842..211fcd5 100644
--- a/hw/top_earlgrey/ip/clkmgr/clkmgr.core
+++ b/hw/top_earlgrey/ip/clkmgr/clkmgr.core
@@ -12,6 +12,7 @@
       - lowrisc:ip:pwrmgr_pkg
       - lowrisc:ip:tlul
       - lowrisc:prim:all
+      - lowrisc:prim:buf
       - lowrisc:prim:clock_buf
       - lowrisc:prim:clock_div
       - lowrisc:prim:clock_gating
diff --git a/hw/top_earlgrey/ip/clkmgr/data/autogen/clkmgr.hjson b/hw/top_earlgrey/ip/clkmgr/data/autogen/clkmgr.hjson
index 6d02c08..d61d868 100644
--- a/hw/top_earlgrey/ip/clkmgr/data/autogen/clkmgr.hjson
+++ b/hw/top_earlgrey/ip/clkmgr/data/autogen/clkmgr.hjson
@@ -55,6 +55,13 @@
       package: "clkmgr_pkg",
     },
 
+    { struct:  "clkmgr_cg_en",
+      type:    "uni",
+      name:    "cg_en",
+      act:     "req",
+      package: "clkmgr_pkg",
+    },
+
     { struct:  "lc_tx",
       type:    "uni",
       name:    "lc_dft_en",
diff --git a/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr.sv b/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr.sv
index 0d09f1d..d906e94 100644
--- a/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr.sv
+++ b/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr.sv
@@ -70,6 +70,9 @@
   // jittery enable
   output logic jitter_en_o,
 
+  // clock gated indications going to alert handlers
+  output clkmgr_cg_en_t cg_en_o,
+
   // clock output interface
   output clkmgr_out_t clocks_o
 
@@ -213,43 +216,73 @@
     .clk_i(clk_io_div4_i),
     .clk_o(clocks_o.clk_io_div4_powerup)
   );
+
+  // clock gated indication for alert handler: these clocks are never gated.
+  assign cg_en_o.clk_io_div4_powerup = lc_ctrl_pkg::Off;
   prim_clock_buf u_clk_aon_powerup_buf (
     .clk_i(clk_aon_i),
     .clk_o(clocks_o.clk_aon_powerup)
   );
+
+  // clock gated indication for alert handler: these clocks are never gated.
+  assign cg_en_o.clk_aon_powerup = lc_ctrl_pkg::Off;
   prim_clock_buf u_clk_main_powerup_buf (
     .clk_i(clk_main_i),
     .clk_o(clocks_o.clk_main_powerup)
   );
+
+  // clock gated indication for alert handler: these clocks are never gated.
+  assign cg_en_o.clk_main_powerup = lc_ctrl_pkg::Off;
   prim_clock_buf u_clk_io_powerup_buf (
     .clk_i(clk_io_i),
     .clk_o(clocks_o.clk_io_powerup)
   );
+
+  // clock gated indication for alert handler: these clocks are never gated.
+  assign cg_en_o.clk_io_powerup = lc_ctrl_pkg::Off;
   prim_clock_buf u_clk_usb_powerup_buf (
     .clk_i(clk_usb_i),
     .clk_o(clocks_o.clk_usb_powerup)
   );
+
+  // clock gated indication for alert handler: these clocks are never gated.
+  assign cg_en_o.clk_usb_powerup = lc_ctrl_pkg::Off;
   prim_clock_buf u_clk_io_div2_powerup_buf (
     .clk_i(clk_io_div2_i),
     .clk_o(clocks_o.clk_io_div2_powerup)
   );
+
+  // clock gated indication for alert handler: these clocks are never gated.
+  assign cg_en_o.clk_io_div2_powerup = lc_ctrl_pkg::Off;
   prim_clock_buf u_clk_aon_infra_buf (
     .clk_i(clk_aon_i),
     .clk_o(clocks_o.clk_aon_infra)
   );
+
+  // clock gated indication for alert handler: these clocks are never gated.
+  assign cg_en_o.clk_aon_infra = lc_ctrl_pkg::Off;
   prim_clock_buf u_clk_aon_secure_buf (
     .clk_i(clk_aon_i),
     .clk_o(clocks_o.clk_aon_secure)
   );
+
+  // clock gated indication for alert handler: these clocks are never gated.
+  assign cg_en_o.clk_aon_secure = lc_ctrl_pkg::Off;
   prim_clock_buf u_clk_aon_peri_buf (
     .clk_i(clk_aon_i),
     .clk_o(clocks_o.clk_aon_peri)
   );
+
+  // clock gated indication for alert handler: these clocks are never gated.
+  assign cg_en_o.clk_aon_peri = lc_ctrl_pkg::Off;
   prim_clock_buf u_clk_aon_timers_buf (
     .clk_i(clk_aon_i),
     .clk_o(clocks_o.clk_aon_timers)
   );
 
+  // clock gated indication for alert handler: these clocks are never gated.
+  assign cg_en_o.clk_aon_timers = lc_ctrl_pkg::Off;
+
   ////////////////////////////////////////////////////
   // Root gating
   ////////////////////////////////////////////////////
@@ -559,12 +592,60 @@
   // Clocks with only root gate
   ////////////////////////////////////////////////////
   assign clocks_o.clk_io_div4_infra = clk_io_div4_root;
+
+  // clock gated indication for alert handler
+  prim_lc_sender u_prim_lc_sender_clk_io_div4_infra (
+    .clk_i(clk_io_div4_i),
+    .rst_ni(rst_io_div4_ni),
+    .lc_en_i(((clk_io_div4_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_io_div4_infra)
+  );
   assign clocks_o.clk_main_infra = clk_main_root;
+
+  // clock gated indication for alert handler
+  prim_lc_sender u_prim_lc_sender_clk_main_infra (
+    .clk_i(clk_main_i),
+    .rst_ni(rst_main_ni),
+    .lc_en_i(((clk_main_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_main_infra)
+  );
   assign clocks_o.clk_io_div4_secure = clk_io_div4_root;
+
+  // clock gated indication for alert handler
+  prim_lc_sender u_prim_lc_sender_clk_io_div4_secure (
+    .clk_i(clk_io_div4_i),
+    .rst_ni(rst_io_div4_ni),
+    .lc_en_i(((clk_io_div4_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_io_div4_secure)
+  );
   assign clocks_o.clk_main_secure = clk_main_root;
+
+  // clock gated indication for alert handler
+  prim_lc_sender u_prim_lc_sender_clk_main_secure (
+    .clk_i(clk_main_i),
+    .rst_ni(rst_main_ni),
+    .lc_en_i(((clk_main_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_main_secure)
+  );
   assign clocks_o.clk_usb_secure = clk_usb_root;
+
+  // clock gated indication for alert handler
+  prim_lc_sender u_prim_lc_sender_clk_usb_secure (
+    .clk_i(clk_usb_i),
+    .rst_ni(rst_usb_ni),
+    .lc_en_i(((clk_usb_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_usb_secure)
+  );
   assign clocks_o.clk_io_div4_timers = clk_io_div4_root;
 
+  // clock gated indication for alert handler
+  prim_lc_sender u_prim_lc_sender_clk_io_div4_timers (
+    .clk_i(clk_io_div4_i),
+    .rst_ni(rst_io_div4_ni),
+    .lc_en_i(((clk_io_div4_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_io_div4_timers)
+  );
+
   ////////////////////////////////////////////////////
   // Software direct control group
   ////////////////////////////////////////////////////
@@ -594,15 +675,27 @@
     .lc_en_o(clk_io_div4_peri_scanmode)
   );
 
+  logic clk_io_div4_peri_combined_en;
+  assign clk_io_div4_peri_combined_en = clk_io_div4_peri_sw_en & clk_io_div4_en;
   prim_clock_gating #(
     .FpgaBufGlobal(1'b1) // This clock spans across multiple clock regions.
   ) u_clk_io_div4_peri_cg (
     .clk_i(clk_io_div4_root),
-    .en_i(clk_io_div4_peri_sw_en & clk_io_div4_en),
+    .en_i(clk_io_div4_peri_combined_en),
     .test_en_i(clk_io_div4_peri_scanmode == lc_ctrl_pkg::On),
     .clk_o(clocks_o.clk_io_div4_peri)
   );
 
+  // clock gated indication for alert handler
+  prim_lc_sender #(
+    .ResetValueIsOn(1)
+  ) u_prim_lc_sender_clk_io_div4_peri (
+    .clk_i(clk_io_div4_i),
+    .rst_ni(rst_io_div4_ni),
+    .lc_en_i(((clk_io_div4_peri_combined_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_io_div4_peri)
+  );
+
   prim_flop_2sync #(
     .Width(1)
   ) u_clk_io_div2_peri_sw_en_sync (
@@ -623,15 +716,27 @@
     .lc_en_o(clk_io_div2_peri_scanmode)
   );
 
+  logic clk_io_div2_peri_combined_en;
+  assign clk_io_div2_peri_combined_en = clk_io_div2_peri_sw_en & clk_io_div2_en;
   prim_clock_gating #(
     .FpgaBufGlobal(1'b1) // This clock spans across multiple clock regions.
   ) u_clk_io_div2_peri_cg (
     .clk_i(clk_io_div2_root),
-    .en_i(clk_io_div2_peri_sw_en & clk_io_div2_en),
+    .en_i(clk_io_div2_peri_combined_en),
     .test_en_i(clk_io_div2_peri_scanmode == lc_ctrl_pkg::On),
     .clk_o(clocks_o.clk_io_div2_peri)
   );
 
+  // clock gated indication for alert handler
+  prim_lc_sender #(
+    .ResetValueIsOn(1)
+  ) u_prim_lc_sender_clk_io_div2_peri (
+    .clk_i(clk_io_div2_i),
+    .rst_ni(rst_io_div2_ni),
+    .lc_en_i(((clk_io_div2_peri_combined_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_io_div2_peri)
+  );
+
   prim_flop_2sync #(
     .Width(1)
   ) u_clk_io_peri_sw_en_sync (
@@ -652,15 +757,27 @@
     .lc_en_o(clk_io_peri_scanmode)
   );
 
+  logic clk_io_peri_combined_en;
+  assign clk_io_peri_combined_en = clk_io_peri_sw_en & clk_io_en;
   prim_clock_gating #(
     .FpgaBufGlobal(1'b1) // This clock spans across multiple clock regions.
   ) u_clk_io_peri_cg (
     .clk_i(clk_io_root),
-    .en_i(clk_io_peri_sw_en & clk_io_en),
+    .en_i(clk_io_peri_combined_en),
     .test_en_i(clk_io_peri_scanmode == lc_ctrl_pkg::On),
     .clk_o(clocks_o.clk_io_peri)
   );
 
+  // clock gated indication for alert handler
+  prim_lc_sender #(
+    .ResetValueIsOn(1)
+  ) u_prim_lc_sender_clk_io_peri (
+    .clk_i(clk_io_i),
+    .rst_ni(rst_io_ni),
+    .lc_en_i(((clk_io_peri_combined_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_io_peri)
+  );
+
   prim_flop_2sync #(
     .Width(1)
   ) u_clk_usb_peri_sw_en_sync (
@@ -681,15 +798,27 @@
     .lc_en_o(clk_usb_peri_scanmode)
   );
 
+  logic clk_usb_peri_combined_en;
+  assign clk_usb_peri_combined_en = clk_usb_peri_sw_en & clk_usb_en;
   prim_clock_gating #(
     .FpgaBufGlobal(1'b1) // This clock spans across multiple clock regions.
   ) u_clk_usb_peri_cg (
     .clk_i(clk_usb_root),
-    .en_i(clk_usb_peri_sw_en & clk_usb_en),
+    .en_i(clk_usb_peri_combined_en),
     .test_en_i(clk_usb_peri_scanmode == lc_ctrl_pkg::On),
     .clk_o(clocks_o.clk_usb_peri)
   );
 
+  // clock gated indication for alert handler
+  prim_lc_sender #(
+    .ResetValueIsOn(1)
+  ) u_prim_lc_sender_clk_usb_peri (
+    .clk_i(clk_usb_i),
+    .rst_ni(rst_usb_ni),
+    .lc_en_i(((clk_usb_peri_combined_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_usb_peri)
+  );
+
 
   ////////////////////////////////////////////////////
   // Software hint group
@@ -730,15 +859,33 @@
     .lc_en_o(clk_main_aes_scanmode)
   );
 
+  // Add a prim buf here to make sure the CG and the lc sender inputs
+  // are derived from the same physical signal.
+  logic clk_main_aes_combined_en;
+  prim_buf u_prim_buf_clk_main_aes_en (
+    .in_i(clk_main_aes_en & clk_main_en),
+    .out_o(clk_main_aes_combined_en)
+  );
+
   prim_clock_gating #(
     .FpgaBufGlobal(1'b0) // This clock is used primarily locally.
   ) u_clk_main_aes_cg (
     .clk_i(clk_main_root),
-    .en_i(clk_main_aes_en & clk_main_en),
+    .en_i(clk_main_aes_combined_en),
     .test_en_i(clk_main_aes_scanmode == lc_ctrl_pkg::On),
     .clk_o(clocks_o.clk_main_aes)
   );
 
+  // clock gated indication for alert handler
+  prim_lc_sender #(
+    .ResetValueIsOn(1)
+  ) u_prim_lc_sender_clk_main_aes (
+    .clk_i(clk_main_i),
+    .rst_ni(rst_main_ni),
+    .lc_en_i(((clk_main_aes_combined_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_main_aes)
+  );
+
   assign clk_main_hmac_en = clk_main_hmac_hint | ~idle_i[HintMainHmac];
 
   prim_flop_2sync #(
@@ -761,15 +908,33 @@
     .lc_en_o(clk_main_hmac_scanmode)
   );
 
+  // Add a prim buf here to make sure the CG and the lc sender inputs
+  // are derived from the same physical signal.
+  logic clk_main_hmac_combined_en;
+  prim_buf u_prim_buf_clk_main_hmac_en (
+    .in_i(clk_main_hmac_en & clk_main_en),
+    .out_o(clk_main_hmac_combined_en)
+  );
+
   prim_clock_gating #(
     .FpgaBufGlobal(1'b0) // This clock is used primarily locally.
   ) u_clk_main_hmac_cg (
     .clk_i(clk_main_root),
-    .en_i(clk_main_hmac_en & clk_main_en),
+    .en_i(clk_main_hmac_combined_en),
     .test_en_i(clk_main_hmac_scanmode == lc_ctrl_pkg::On),
     .clk_o(clocks_o.clk_main_hmac)
   );
 
+  // clock gated indication for alert handler
+  prim_lc_sender #(
+    .ResetValueIsOn(1)
+  ) u_prim_lc_sender_clk_main_hmac (
+    .clk_i(clk_main_i),
+    .rst_ni(rst_main_ni),
+    .lc_en_i(((clk_main_hmac_combined_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_main_hmac)
+  );
+
   assign clk_main_kmac_en = clk_main_kmac_hint | ~idle_i[HintMainKmac];
 
   prim_flop_2sync #(
@@ -792,15 +957,33 @@
     .lc_en_o(clk_main_kmac_scanmode)
   );
 
+  // Add a prim buf here to make sure the CG and the lc sender inputs
+  // are derived from the same physical signal.
+  logic clk_main_kmac_combined_en;
+  prim_buf u_prim_buf_clk_main_kmac_en (
+    .in_i(clk_main_kmac_en & clk_main_en),
+    .out_o(clk_main_kmac_combined_en)
+  );
+
   prim_clock_gating #(
     .FpgaBufGlobal(1'b0) // This clock is used primarily locally.
   ) u_clk_main_kmac_cg (
     .clk_i(clk_main_root),
-    .en_i(clk_main_kmac_en & clk_main_en),
+    .en_i(clk_main_kmac_combined_en),
     .test_en_i(clk_main_kmac_scanmode == lc_ctrl_pkg::On),
     .clk_o(clocks_o.clk_main_kmac)
   );
 
+  // clock gated indication for alert handler
+  prim_lc_sender #(
+    .ResetValueIsOn(1)
+  ) u_prim_lc_sender_clk_main_kmac (
+    .clk_i(clk_main_i),
+    .rst_ni(rst_main_ni),
+    .lc_en_i(((clk_main_kmac_combined_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_main_kmac)
+  );
+
   assign clk_main_otbn_en = clk_main_otbn_hint | ~idle_i[HintMainOtbn];
 
   prim_flop_2sync #(
@@ -823,15 +1006,33 @@
     .lc_en_o(clk_main_otbn_scanmode)
   );
 
+  // Add a prim buf here to make sure the CG and the lc sender inputs
+  // are derived from the same physical signal.
+  logic clk_main_otbn_combined_en;
+  prim_buf u_prim_buf_clk_main_otbn_en (
+    .in_i(clk_main_otbn_en & clk_main_en),
+    .out_o(clk_main_otbn_combined_en)
+  );
+
   prim_clock_gating #(
     .FpgaBufGlobal(1'b0) // This clock is used primarily locally.
   ) u_clk_main_otbn_cg (
     .clk_i(clk_main_root),
-    .en_i(clk_main_otbn_en & clk_main_en),
+    .en_i(clk_main_otbn_combined_en),
     .test_en_i(clk_main_otbn_scanmode == lc_ctrl_pkg::On),
     .clk_o(clocks_o.clk_main_otbn)
   );
 
+  // clock gated indication for alert handler
+  prim_lc_sender #(
+    .ResetValueIsOn(1)
+  ) u_prim_lc_sender_clk_main_otbn (
+    .clk_i(clk_main_i),
+    .rst_ni(rst_main_ni),
+    .lc_en_i(((clk_main_otbn_combined_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_main_otbn)
+  );
+
   assign clk_io_div4_otbn_en = clk_io_div4_otbn_hint | ~idle_i[HintIoDiv4Otbn];
 
   prim_flop_2sync #(
@@ -854,15 +1055,33 @@
     .lc_en_o(clk_io_div4_otbn_scanmode)
   );
 
+  // Add a prim buf here to make sure the CG and the lc sender inputs
+  // are derived from the same physical signal.
+  logic clk_io_div4_otbn_combined_en;
+  prim_buf u_prim_buf_clk_io_div4_otbn_en (
+    .in_i(clk_io_div4_otbn_en & clk_io_div4_en),
+    .out_o(clk_io_div4_otbn_combined_en)
+  );
+
   prim_clock_gating #(
     .FpgaBufGlobal(1'b0) // This clock is used primarily locally.
   ) u_clk_io_div4_otbn_cg (
     .clk_i(clk_io_div4_root),
-    .en_i(clk_io_div4_otbn_en & clk_io_div4_en),
+    .en_i(clk_io_div4_otbn_combined_en),
     .test_en_i(clk_io_div4_otbn_scanmode == lc_ctrl_pkg::On),
     .clk_o(clocks_o.clk_io_div4_otbn)
   );
 
+  // clock gated indication for alert handler
+  prim_lc_sender #(
+    .ResetValueIsOn(1)
+  ) u_prim_lc_sender_clk_io_div4_otbn (
+    .clk_i(clk_io_div4_i),
+    .rst_ni(rst_io_div4_ni),
+    .lc_en_i(((clk_io_div4_otbn_combined_en) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On)),
+    .lc_en_o(cg_en_o.clk_io_div4_otbn)
+  );
+
 
   // state readback
   assign hw2reg.clk_hints_status.clk_main_aes_val.de = 1'b1;
@@ -895,5 +1114,6 @@
   `ASSERT_KNOWN(LcCtrlClkBypAckKnownO_A, lc_clk_byp_ack_o)
   `ASSERT_KNOWN(JitterEnableKnownO_A, jitter_en_o)
   `ASSERT_KNOWN(ClocksKownO_A, clocks_o)
+  `ASSERT_KNOWN(CgEnKnownO_A, cg_en_o)
 
 endmodule // clkmgr
diff --git a/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr_pkg.sv b/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr_pkg.sv
index 223acf4..c78c40d 100644
--- a/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr_pkg.sv
+++ b/hw/top_earlgrey/ip/clkmgr/rtl/autogen/clkmgr_pkg.sv
@@ -20,6 +20,7 @@
     HintMainOtbn = 4
   } hint_names_e;
 
+  // clocks generated and broadcast
   typedef struct packed {
     logic clk_io_div4_powerup;
     logic clk_aon_powerup;
@@ -46,9 +47,39 @@
     logic clk_io_div2_peri;
     logic clk_io_peri;
     logic clk_usb_peri;
-
   } clkmgr_out_t;
 
+  // clock gating indication for alert handler
+  typedef struct packed {
+    lc_ctrl_pkg::lc_tx_t clk_io_div4_powerup;
+    lc_ctrl_pkg::lc_tx_t clk_aon_powerup;
+    lc_ctrl_pkg::lc_tx_t clk_main_powerup;
+    lc_ctrl_pkg::lc_tx_t clk_io_powerup;
+    lc_ctrl_pkg::lc_tx_t clk_usb_powerup;
+    lc_ctrl_pkg::lc_tx_t clk_io_div2_powerup;
+    lc_ctrl_pkg::lc_tx_t clk_aon_infra;
+    lc_ctrl_pkg::lc_tx_t clk_aon_secure;
+    lc_ctrl_pkg::lc_tx_t clk_aon_peri;
+    lc_ctrl_pkg::lc_tx_t clk_aon_timers;
+    lc_ctrl_pkg::lc_tx_t clk_main_aes;
+    lc_ctrl_pkg::lc_tx_t clk_main_hmac;
+    lc_ctrl_pkg::lc_tx_t clk_main_kmac;
+    lc_ctrl_pkg::lc_tx_t clk_main_otbn;
+    lc_ctrl_pkg::lc_tx_t clk_io_div4_otbn;
+    lc_ctrl_pkg::lc_tx_t clk_io_div4_infra;
+    lc_ctrl_pkg::lc_tx_t clk_main_infra;
+    lc_ctrl_pkg::lc_tx_t clk_io_div4_secure;
+    lc_ctrl_pkg::lc_tx_t clk_main_secure;
+    lc_ctrl_pkg::lc_tx_t clk_usb_secure;
+    lc_ctrl_pkg::lc_tx_t clk_io_div4_timers;
+    lc_ctrl_pkg::lc_tx_t clk_io_div4_peri;
+    lc_ctrl_pkg::lc_tx_t clk_io_div2_peri;
+    lc_ctrl_pkg::lc_tx_t clk_io_peri;
+    lc_ctrl_pkg::lc_tx_t clk_usb_peri;
+  } clkmgr_cg_en_t;
+
+  parameter int NumOutputClk = 25;
+
 
   typedef struct packed {
     logic [5-1:0] idle;
diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
index 77b12e5..680198d 100644
--- a/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
+++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey.sv
@@ -1570,6 +1570,7 @@
 
       // Inter-module signals
       .clocks_o(clkmgr_aon_clocks),
+      .cg_en_o(),
       .lc_dft_en_i(lc_ctrl_lc_dft_en),
       .ast_clk_byp_req_o(ast_clk_byp_req_o),
       .ast_clk_byp_ack_i(ast_clk_byp_ack_i),