[alert_handler] Switch to standardized prim_count

Signed-off-by: Michael Schaffner <msf@opentitan.org>
diff --git a/hw/ip_templates/alert_handler/alert_handler_component.core b/hw/ip_templates/alert_handler/alert_handler_component.core
index 6965391..97cb604 100644
--- a/hw/ip_templates/alert_handler/alert_handler_component.core
+++ b/hw/ip_templates/alert_handler/alert_handler_component.core
@@ -12,6 +12,7 @@
       - lowrisc:prim:all
       - lowrisc:prim:esc
       - lowrisc:prim:double_lfsr
+      - lowrisc:prim:count
       - lowrisc:prim:edn_req
       - lowrisc:prim:buf
       - lowrisc:prim:mubi
diff --git a/hw/ip_templates/alert_handler/rtl/alert_handler_accu.sv b/hw/ip_templates/alert_handler/rtl/alert_handler_accu.sv
index 8e599f7..74b2280 100644
--- a/hw/ip_templates/alert_handler/rtl/alert_handler_accu.sv
+++ b/hw/ip_templates/alert_handler/rtl/alert_handler_accu.sv
@@ -24,48 +24,34 @@
   output logic                 accu_fail_o   // asserted if the tandem accu counters are not equal
 );
 
-  logic trig_gated_unbuf;
-  assign trig_gated_unbuf = class_trig_i & class_en_i;
+  logic trig_gated, accu_en;
+  assign trig_gated = class_trig_i & class_en_i;
+  assign accu_en = trig_gated && !(&accu_cnt_o);
 
   // We employ two redundant counters to guard against FI attacks.
   // If any of the two is glitched and the two counter states do not agree,
   // the check_fail_o signal is asserted which will move the corresponding escalation
   // FSM into a terminal error state where all escalation actions will be permanently asserted.
-  logic [1:0][AccuCntDw-1:0] accu_q;
-  for (genvar k = 0; k < 2; k++) begin : gen_double_accu
+  prim_count #(
+    .Width(AccuCntDw),
+    .OutSelDnCnt(0), // count up
+    .CntStyle(prim_count_pkg::CrossCnt),
+    // The alert handler behaves differently than other comportable IP. I.e., instead of sending out
+    // an alert signal, this condition is handled internally in the alert handler.
+    .EnableAlertTriggerSVA(0)
+  ) u_prim_count (
+    .clk_i,
+    .rst_ni,
+    .clr_i,
+    .set_i(1'b0),
+    .set_cnt_i('0),
+    .en_i(accu_en),
+    .step_i(AccuCntDw'(1)),
+    .cnt_o(accu_cnt_o),
+    .err_o(accu_fail_o)
+  );
 
-    logic trig_gated_buf, clr_buf;
-    logic [AccuCntDw-1:0] accu_d;
-
-    // These size_only buffers are instantiated in order to prevent
-    // optimization / merging of the two counters.
-    prim_buf u_prim_buf_clr (
-      .in_i(clr_i),
-      .out_o(clr_buf)
-    );
-
-    prim_buf u_prim_buf_trig (
-      .in_i(trig_gated_unbuf),
-      .out_o(trig_gated_buf)
-    );
-
-    assign accu_d = (clr_buf)                         ? '0               : // clear
-                    (trig_gated_buf && !(&accu_q[k])) ? accu_q[k] + 1'b1 : // saturate counter
-                                                        accu_q[k];
-
-    prim_flop #(
-      .Width(AccuCntDw)
-    ) u_prim_flop (
-      .clk_i,
-      .rst_ni,
-      .d_i(accu_d),
-      .q_o(accu_q[k])
-    );
-  end
-
-  assign accu_cnt_o = accu_q[0];
-  assign accu_trig_o = (accu_q[0] >= thresh_i) & trig_gated_unbuf;
-  assign accu_fail_o = accu_q[0] != accu_q[1];
+  assign accu_trig_o = (accu_cnt_o >= thresh_i) & trig_gated;
 
   ////////////////
   // Assertions //
@@ -73,7 +59,5 @@
 
   `ASSERT(DisabledNoTrigFwd_A, !class_en_i |-> !accu_trig_o)
   `ASSERT(DisabledNoTrigBkwd_A, accu_trig_o |-> class_en_i)
-  `ASSERT(CheckFailFwd_A, accu_q[0] != accu_q[1] |-> accu_fail_o)
-  `ASSERT(CheckFailBkwd_A, accu_fail_o |-> accu_q[0] != accu_q[1])
 
 endmodule : alert_handler_accu
diff --git a/hw/ip_templates/alert_handler/rtl/alert_handler_esc_timer.sv b/hw/ip_templates/alert_handler/rtl/alert_handler_esc_timer.sv
index 83cd101..e5d2f2e 100644
--- a/hw/ip_templates/alert_handler/rtl/alert_handler_esc_timer.sv
+++ b/hw/ip_templates/alert_handler/rtl/alert_handler_esc_timer.sv
@@ -46,56 +46,36 @@
   // Tandem Counter //
   ////////////////////
 
-  logic cnt_en, cnt_clr, cnt_ge;
-  logic [1:0][EscCntDw-1:0] cnt_q;
-
   // We employ two redundant counters to guard against FI attacks.
   // If any of the two is glitched and the two counter states do not agree,
   // the FSM below is moved into a terminal error state and escalation actions
   // are permanently asserted.
-  for (genvar k = 0; k < 2; k++) begin : gen_double_cnt
+  logic cnt_en, cnt_clr, cnt_error;
 
-    logic cnt_en_buf, cnt_clr_buf;
-
-    // These size_only buffers are instantiated in order to prevent
-    // optimization / merging of the two counters.
-    prim_buf u_prim_buf_clr (
-      .in_i(cnt_clr),
-      .out_o(cnt_clr_buf)
-    );
-
-    prim_buf u_prim_buf_en (
-      .in_i(cnt_en),
-      .out_o(cnt_en_buf)
-    );
-
-    // escalation counter, used for all phases and the timeout
-    logic [EscCntDw-1:0] cnt_d;
-    assign cnt_d = (cnt_clr_buf && cnt_en_buf) ? EscCntDw'(1'b1) :
-                   (cnt_clr_buf)               ? '0              :
-                   (cnt_en_buf)                ? cnt_q[k] + 1'b1 : cnt_q[k];
-
-    prim_flop #(
-      .Width(EscCntDw)
-    ) u_prim_flop (
-      .clk_i,
-      .rst_ni,
-      .d_i(cnt_d),
-      .q_o(cnt_q[k])
-    );
-  end
+  prim_count #(
+    .Width(EscCntDw),
+    .OutSelDnCnt(0), // count up
+    .CntStyle(prim_count_pkg::DupCnt),
+    // The alert handler behaves differently than other comportable IP. I.e., instead of sending out
+    // an alert signal, this condition is handled internally in the alert handler.
+    .EnableAlertTriggerSVA(0)
+  ) u_prim_count (
+    .clk_i,
+    .rst_ni,
+    .clr_i(cnt_clr && !cnt_en),
+    .set_i(cnt_clr && cnt_en),
+    .set_cnt_i(EscCntDw'(1)),
+    .en_i(!cnt_clr && cnt_en),
+    .step_i(EscCntDw'(1)),
+    .cnt_o(esc_cnt_o),
+    .err_o(cnt_error)
+  );
 
   // threshold test, the thresholds are muxed further below
   // depending on the current state
+  logic cnt_ge;
   logic [EscCntDw-1:0] thresh;
-  assign cnt_ge    = (cnt_q[0] >= thresh);
-
-  // current counter output
-  assign esc_cnt_o   = cnt_q[0];
-
-  // consistency check
-  logic cnt_check_fail;
-  assign cnt_check_fail = cnt_q[0] != cnt_q[1];
+  assign cnt_ge = (esc_cnt_o >= thresh);
 
   //////////////
   // Main FSM //
@@ -289,7 +269,7 @@
 
     // if any of the duplicate counter pairs has an inconsistent state
     // we move into the terminal FSM error state.
-    if (accu_fail_i || cnt_check_fail) begin
+    if (accu_fail_i || cnt_error) begin
       state_d = FsmErrorSt;
     end
   end
@@ -373,7 +353,7 @@
       !accu_fail_i &&
       state_q == TimeoutSt &&
       timeout_en_i &&
-      cnt_q[0] < timeout_cyc_i &&
+      esc_cnt_o < timeout_cyc_i &&
       !accu_trig_i
       |=>
       state_q == TimeoutSt)
@@ -389,7 +369,7 @@
       !accu_fail_i &&
       state_q == TimeoutSt &&
       timeout_en_i &&
-      cnt_q[0] == timeout_cyc_i
+      esc_cnt_o == timeout_cyc_i
       |=>
       state_q == Phase0St)
   // Check whether escalation phases are correctly switched
@@ -397,28 +377,28 @@
       !accu_fail_i &&
       state_q == Phase0St &&
       !clr_i &&
-      cnt_q[0] >= phase_cyc_i[0]
+      esc_cnt_o >= phase_cyc_i[0]
       |=>
       state_q == Phase1St)
   `ASSERT(CheckPhase1_A,
       !accu_fail_i &&
       state_q == Phase1St &&
       !clr_i &&
-      cnt_q[0] >= phase_cyc_i[1]
+      esc_cnt_o >= phase_cyc_i[1]
       |=>
       state_q == Phase2St)
   `ASSERT(CheckPhase2_A,
       !accu_fail_i &&
       state_q == Phase2St &&
       !clr_i &&
-      cnt_q[0] >= phase_cyc_i[2]
+      esc_cnt_o >= phase_cyc_i[2]
       |=>
       state_q == Phase3St)
   `ASSERT(CheckPhase3_A,
       !accu_fail_i &&
       state_q == Phase3St &&
       !clr_i &&
-      cnt_q[0] >= phase_cyc_i[3]
+      esc_cnt_o >= phase_cyc_i[3]
       |=>
       state_q == TerminalSt)
   `ASSERT(AccuFailToFsmError_A,
diff --git a/hw/ip_templates/alert_handler/rtl/alert_handler_ping_timer.sv b/hw/ip_templates/alert_handler/rtl/alert_handler_ping_timer.sv
index e515f97..346c2ad 100644
--- a/hw/ip_templates/alert_handler/rtl/alert_handler_ping_timer.sv
+++ b/hw/ip_templates/alert_handler/rtl/alert_handler_ping_timer.sv
@@ -83,7 +83,7 @@
   // Tandem LFSR Instances //
   ///////////////////////////
 
-  logic lfsr_en, lfsr_err;
+  logic cnt_set, lfsr_err;
   logic [LfsrWidth-1:0] entropy;
   logic [PING_CNT_DW + IdDw - 1:0] lfsr_state;
   assign entropy = (reseed_en) ? edn_data_i[LfsrWidth-1:0] : '0;
@@ -107,7 +107,7 @@
     .rst_ni,
     .seed_en_i  ( 1'b0                 ),
     .seed_i     ( '0                   ),
-    .lfsr_en_i  ( reseed_en || lfsr_en ),
+    .lfsr_en_i  ( reseed_en || cnt_set ),
     .entropy_i  ( entropy              ),
     .state_o    ( lfsr_state           ),
     .err_o      ( lfsr_err             )
@@ -154,66 +154,75 @@
   // In order to have enough margin, the escalation receiver timeout counters use a threshold that
   // is 4x higher than the value calculated above. With N_ESC_SEV = 4, PING_CNT_DW = 16 and
   // NUM_WAIT_COUNT = NUM_TIMEOUT_COUNT = 2 this amounts to a 22bit timeout threshold.
-
-  logic esc_cnt_en;
-  logic [1:0][PING_CNT_DW-1:0] esc_cnt_q;
-
+  //
   // We employ two redundant counters to guard against FI attacks.
   // If any of the two is glitched and the two counter states do not agree,
   // the FSM below is moved into a terminal error state and all ping alerts
   // are permanently asserted.
-  for (genvar k = 0; k < 2; k++) begin : gen_double_esc_cnt
 
-    logic [PING_CNT_DW-1:0] esc_cnt_d;
-    assign esc_cnt_d = (esc_cnt_q[k] >= PING_CNT_DW'(N_ESC_SEV-1)) ? '0 : (esc_cnt_q[k] + 1'b1);
+  logic esc_cnt_en, esc_cnt_clr, esc_cnt_error;
+  logic [PING_CNT_DW-1:0] esc_cnt;
+  assign esc_cnt_clr = (esc_cnt >= PING_CNT_DW'(N_ESC_SEV-1)) && esc_cnt_en;
 
-    prim_flop_en #(
-      .Width(PING_CNT_DW)
-    ) u_prim_flop_en (
-      .clk_i,
-      .rst_ni,
-      .en_i  ( esc_cnt_en   ),
-      .d_i   ( esc_cnt_d    ),
-      .q_o   ( esc_cnt_q[k] )
-    );
-  end
+  prim_count #(
+    .Width(PING_CNT_DW),
+    .OutSelDnCnt(0), // count up
+    .CntStyle(prim_count_pkg::CrossCnt),
+    // The alert handler behaves differently than other comportable IP. I.e., instead of sending out
+    // an alert signal, this condition is handled internally in the alert handler.
+    .EnableAlertTriggerSVA(0)
+  ) u_prim_count_esc_cnt (
+    .clk_i,
+    .rst_ni,
+    .clr_i(esc_cnt_clr),
+    .set_i(1'b0),
+    .set_cnt_i('0),
+    .en_i(esc_cnt_en),
+    .step_i(PING_CNT_DW'(1)),
+    .cnt_o(esc_cnt),
+    .err_o(esc_cnt_error)
+  );
 
   /////////////////////////////
   // Timer Counter Instances //
   /////////////////////////////
 
-  logic [1:0][PING_CNT_DW-1:0] cnt_q;
-  logic wait_cnt_load, timeout_cnt_load, timer_expired;
-  assign timer_expired = (cnt_q[0] == '0);
-  assign lfsr_en = wait_cnt_load || timeout_cnt_load;
-
   // We employ two redundant counters to guard against FI attacks.
   // If any of the two is glitched and the two counter states do not agree,
   // the FSM below is moved into a terminal error state and all ping alerts
   // are permanently asserted.
-  for (genvar k = 0; k < 2; k++) begin : gen_double_cnt
+  logic [PING_CNT_DW-1:0] cnt, cnt_setval;
+  logic wait_cnt_set, timeout_cnt_set, timer_expired, cnt_error;
+  assign timer_expired = (cnt == '0);
+  assign cnt_set = wait_cnt_set || timeout_cnt_set;
 
-    // the constant offset ensures a minimum cycle spacing between pings.
-    logic unused_bits;
-    logic [PING_CNT_DW-1:0] wait_cyc;
-    assign wait_cyc = (lfsr_state[PING_CNT_DW-1:0] | PING_CNT_DW'(3'b100));
-    assign unused_bits = lfsr_state[2];
+  prim_count #(
+    .Width(PING_CNT_DW),
+    .OutSelDnCnt(1), // count down
+    .CntStyle(prim_count_pkg::CrossCnt),
+    // The alert handler behaves differently than other comportable IP. I.e., instead of sending out
+    // an alert signal, this condition is handled internally in the alert handler.
+    .EnableAlertTriggerSVA(0)
+  ) u_prim_count_cnt (
+    .clk_i,
+    .rst_ni,
+    .clr_i(1'b0),
+    .set_i(cnt_set),
+    .set_cnt_i(cnt_setval),
+    .en_i(!timer_expired),
+    .step_i(PING_CNT_DW'(1)),
+    .cnt_o(cnt),
+    .err_o(cnt_error)
+  );
 
-    // note that the masks are used for DV/FPV only in order to reduce the state space.
-    logic [PING_CNT_DW-1:0] cnt_d;
-    assign cnt_d = (wait_cnt_load)    ? (wait_cyc & wait_cyc_mask_i) :
-                   (timeout_cnt_load) ? (ping_timeout_cyc_i)         :
-                   (cnt_q[k] > '0)    ? cnt_q[k] - 1'b1              : '0;
+  // the constant offset ensures a minimum cycle spacing between pings.
+  logic unused_bits;
+  logic [PING_CNT_DW-1:0] wait_cyc;
+  assign wait_cyc = (lfsr_state[PING_CNT_DW-1:0] | PING_CNT_DW'(3'b100));
+  assign unused_bits = lfsr_state[2];
 
-    prim_flop #(
-      .Width(PING_CNT_DW)
-    ) u_prim_flop (
-      .clk_i,
-      .rst_ni,
-      .d_i   ( cnt_d    ),
-      .q_o   ( cnt_q[k] )
-    );
-  end
+  // note that the masks are used for DV/FPV only in order to reduce the state space.
+  assign cnt_setval = (wait_cnt_set) ? (wait_cyc & wait_cyc_mask_i) : ping_timeout_cyc_i;
 
   ////////////////////////////
   // Ping and Timeout Logic //
@@ -224,7 +233,7 @@
 
   // generate ping enable vector
   assign alert_ping_req_o = NAlerts'(alert_ping_en) << id_to_ping;
-  assign esc_ping_req_o   = N_ESC_SEV'(esc_ping_en) << esc_cnt_q[0];
+  assign esc_ping_req_o   = N_ESC_SEV'(esc_ping_en) << esc_cnt;
 
   // under normal operation, these signals should never be asserted.
   // we place hand instantiated buffers here such that these signals are not
@@ -276,8 +285,8 @@
   always_comb begin : p_fsm
     // default
     state_d          = state_q;
-    wait_cnt_load    = 1'b0;
-    timeout_cnt_load = 1'b0;
+    wait_cnt_set    = 1'b0;
+    timeout_cnt_set = 1'b0;
     esc_cnt_en       = 1'b0;
     alert_ping_en    = 1'b0;
     esc_ping_en      = 1'b0;
@@ -292,14 +301,14 @@
       InitSt: begin
         if (en_i) begin
           state_d = AlertWaitSt;
-          wait_cnt_load = 1'b1;
+          wait_cnt_set = 1'b1;
         end
       end
       // wait for random amount of cycles
       AlertWaitSt: begin
         if (timer_expired) begin
           state_d = AlertPingSt;
-          timeout_cnt_load = 1'b1;
+          timeout_cnt_set = 1'b1;
         end
       end
       // send out an alert ping request and wait for a ping
@@ -310,7 +319,7 @@
         alert_ping_en = id_vld;
         if (timer_expired || |(alert_ping_ok_i & alert_ping_req_o) || !id_vld) begin
           state_d           = EscWaitSt;
-          wait_cnt_load     = 1'b1;
+          wait_cnt_set     = 1'b1;
           if (timer_expired) begin
             alert_ping_fail_o = 1'b1;
           end
@@ -320,7 +329,7 @@
       EscWaitSt: begin
         if (timer_expired) begin
           state_d          = EscPingSt;
-          timeout_cnt_load = 1'b1;
+          timeout_cnt_set = 1'b1;
         end
       end
       // send out an escalation ping request and wait for a ping
@@ -329,7 +338,7 @@
         esc_ping_en = 1'b1;
         if (timer_expired || |(esc_ping_ok_i & esc_ping_req_o)) begin
           state_d         = AlertWaitSt;
-          wait_cnt_load   = 1'b1;
+          wait_cnt_set   = 1'b1;
           esc_cnt_en      = 1'b1;
           if (timer_expired) begin
             esc_ping_fail_o = 1'b1;
@@ -350,9 +359,7 @@
 
     // if the two LFSR or counter states do not agree,
     // we move into the terminal state.
-    if (lfsr_err                  ||
-        cnt_q[0]      != cnt_q[1] ||
-        esc_cnt_q[0]  != esc_cnt_q[1]) begin
+    if (lfsr_err || cnt_error || esc_cnt_error) begin
        state_d = FsmErrorSt;
     end
   end
diff --git a/hw/top_earlgrey/ip_autogen/alert_handler/alert_handler_component.core b/hw/top_earlgrey/ip_autogen/alert_handler/alert_handler_component.core
index 6965391..97cb604 100644
--- a/hw/top_earlgrey/ip_autogen/alert_handler/alert_handler_component.core
+++ b/hw/top_earlgrey/ip_autogen/alert_handler/alert_handler_component.core
@@ -12,6 +12,7 @@
       - lowrisc:prim:all
       - lowrisc:prim:esc
       - lowrisc:prim:double_lfsr
+      - lowrisc:prim:count
       - lowrisc:prim:edn_req
       - lowrisc:prim:buf
       - lowrisc:prim:mubi
diff --git a/hw/top_earlgrey/ip_autogen/alert_handler/rtl/alert_handler_accu.sv b/hw/top_earlgrey/ip_autogen/alert_handler/rtl/alert_handler_accu.sv
index 8e599f7..74b2280 100644
--- a/hw/top_earlgrey/ip_autogen/alert_handler/rtl/alert_handler_accu.sv
+++ b/hw/top_earlgrey/ip_autogen/alert_handler/rtl/alert_handler_accu.sv
@@ -24,48 +24,34 @@
   output logic                 accu_fail_o   // asserted if the tandem accu counters are not equal
 );
 
-  logic trig_gated_unbuf;
-  assign trig_gated_unbuf = class_trig_i & class_en_i;
+  logic trig_gated, accu_en;
+  assign trig_gated = class_trig_i & class_en_i;
+  assign accu_en = trig_gated && !(&accu_cnt_o);
 
   // We employ two redundant counters to guard against FI attacks.
   // If any of the two is glitched and the two counter states do not agree,
   // the check_fail_o signal is asserted which will move the corresponding escalation
   // FSM into a terminal error state where all escalation actions will be permanently asserted.
-  logic [1:0][AccuCntDw-1:0] accu_q;
-  for (genvar k = 0; k < 2; k++) begin : gen_double_accu
+  prim_count #(
+    .Width(AccuCntDw),
+    .OutSelDnCnt(0), // count up
+    .CntStyle(prim_count_pkg::CrossCnt),
+    // The alert handler behaves differently than other comportable IP. I.e., instead of sending out
+    // an alert signal, this condition is handled internally in the alert handler.
+    .EnableAlertTriggerSVA(0)
+  ) u_prim_count (
+    .clk_i,
+    .rst_ni,
+    .clr_i,
+    .set_i(1'b0),
+    .set_cnt_i('0),
+    .en_i(accu_en),
+    .step_i(AccuCntDw'(1)),
+    .cnt_o(accu_cnt_o),
+    .err_o(accu_fail_o)
+  );
 
-    logic trig_gated_buf, clr_buf;
-    logic [AccuCntDw-1:0] accu_d;
-
-    // These size_only buffers are instantiated in order to prevent
-    // optimization / merging of the two counters.
-    prim_buf u_prim_buf_clr (
-      .in_i(clr_i),
-      .out_o(clr_buf)
-    );
-
-    prim_buf u_prim_buf_trig (
-      .in_i(trig_gated_unbuf),
-      .out_o(trig_gated_buf)
-    );
-
-    assign accu_d = (clr_buf)                         ? '0               : // clear
-                    (trig_gated_buf && !(&accu_q[k])) ? accu_q[k] + 1'b1 : // saturate counter
-                                                        accu_q[k];
-
-    prim_flop #(
-      .Width(AccuCntDw)
-    ) u_prim_flop (
-      .clk_i,
-      .rst_ni,
-      .d_i(accu_d),
-      .q_o(accu_q[k])
-    );
-  end
-
-  assign accu_cnt_o = accu_q[0];
-  assign accu_trig_o = (accu_q[0] >= thresh_i) & trig_gated_unbuf;
-  assign accu_fail_o = accu_q[0] != accu_q[1];
+  assign accu_trig_o = (accu_cnt_o >= thresh_i) & trig_gated;
 
   ////////////////
   // Assertions //
@@ -73,7 +59,5 @@
 
   `ASSERT(DisabledNoTrigFwd_A, !class_en_i |-> !accu_trig_o)
   `ASSERT(DisabledNoTrigBkwd_A, accu_trig_o |-> class_en_i)
-  `ASSERT(CheckFailFwd_A, accu_q[0] != accu_q[1] |-> accu_fail_o)
-  `ASSERT(CheckFailBkwd_A, accu_fail_o |-> accu_q[0] != accu_q[1])
 
 endmodule : alert_handler_accu
diff --git a/hw/top_earlgrey/ip_autogen/alert_handler/rtl/alert_handler_esc_timer.sv b/hw/top_earlgrey/ip_autogen/alert_handler/rtl/alert_handler_esc_timer.sv
index 83cd101..e5d2f2e 100644
--- a/hw/top_earlgrey/ip_autogen/alert_handler/rtl/alert_handler_esc_timer.sv
+++ b/hw/top_earlgrey/ip_autogen/alert_handler/rtl/alert_handler_esc_timer.sv
@@ -46,56 +46,36 @@
   // Tandem Counter //
   ////////////////////
 
-  logic cnt_en, cnt_clr, cnt_ge;
-  logic [1:0][EscCntDw-1:0] cnt_q;
-
   // We employ two redundant counters to guard against FI attacks.
   // If any of the two is glitched and the two counter states do not agree,
   // the FSM below is moved into a terminal error state and escalation actions
   // are permanently asserted.
-  for (genvar k = 0; k < 2; k++) begin : gen_double_cnt
+  logic cnt_en, cnt_clr, cnt_error;
 
-    logic cnt_en_buf, cnt_clr_buf;
-
-    // These size_only buffers are instantiated in order to prevent
-    // optimization / merging of the two counters.
-    prim_buf u_prim_buf_clr (
-      .in_i(cnt_clr),
-      .out_o(cnt_clr_buf)
-    );
-
-    prim_buf u_prim_buf_en (
-      .in_i(cnt_en),
-      .out_o(cnt_en_buf)
-    );
-
-    // escalation counter, used for all phases and the timeout
-    logic [EscCntDw-1:0] cnt_d;
-    assign cnt_d = (cnt_clr_buf && cnt_en_buf) ? EscCntDw'(1'b1) :
-                   (cnt_clr_buf)               ? '0              :
-                   (cnt_en_buf)                ? cnt_q[k] + 1'b1 : cnt_q[k];
-
-    prim_flop #(
-      .Width(EscCntDw)
-    ) u_prim_flop (
-      .clk_i,
-      .rst_ni,
-      .d_i(cnt_d),
-      .q_o(cnt_q[k])
-    );
-  end
+  prim_count #(
+    .Width(EscCntDw),
+    .OutSelDnCnt(0), // count up
+    .CntStyle(prim_count_pkg::DupCnt),
+    // The alert handler behaves differently than other comportable IP. I.e., instead of sending out
+    // an alert signal, this condition is handled internally in the alert handler.
+    .EnableAlertTriggerSVA(0)
+  ) u_prim_count (
+    .clk_i,
+    .rst_ni,
+    .clr_i(cnt_clr && !cnt_en),
+    .set_i(cnt_clr && cnt_en),
+    .set_cnt_i(EscCntDw'(1)),
+    .en_i(!cnt_clr && cnt_en),
+    .step_i(EscCntDw'(1)),
+    .cnt_o(esc_cnt_o),
+    .err_o(cnt_error)
+  );
 
   // threshold test, the thresholds are muxed further below
   // depending on the current state
+  logic cnt_ge;
   logic [EscCntDw-1:0] thresh;
-  assign cnt_ge    = (cnt_q[0] >= thresh);
-
-  // current counter output
-  assign esc_cnt_o   = cnt_q[0];
-
-  // consistency check
-  logic cnt_check_fail;
-  assign cnt_check_fail = cnt_q[0] != cnt_q[1];
+  assign cnt_ge = (esc_cnt_o >= thresh);
 
   //////////////
   // Main FSM //
@@ -289,7 +269,7 @@
 
     // if any of the duplicate counter pairs has an inconsistent state
     // we move into the terminal FSM error state.
-    if (accu_fail_i || cnt_check_fail) begin
+    if (accu_fail_i || cnt_error) begin
       state_d = FsmErrorSt;
     end
   end
@@ -373,7 +353,7 @@
       !accu_fail_i &&
       state_q == TimeoutSt &&
       timeout_en_i &&
-      cnt_q[0] < timeout_cyc_i &&
+      esc_cnt_o < timeout_cyc_i &&
       !accu_trig_i
       |=>
       state_q == TimeoutSt)
@@ -389,7 +369,7 @@
       !accu_fail_i &&
       state_q == TimeoutSt &&
       timeout_en_i &&
-      cnt_q[0] == timeout_cyc_i
+      esc_cnt_o == timeout_cyc_i
       |=>
       state_q == Phase0St)
   // Check whether escalation phases are correctly switched
@@ -397,28 +377,28 @@
       !accu_fail_i &&
       state_q == Phase0St &&
       !clr_i &&
-      cnt_q[0] >= phase_cyc_i[0]
+      esc_cnt_o >= phase_cyc_i[0]
       |=>
       state_q == Phase1St)
   `ASSERT(CheckPhase1_A,
       !accu_fail_i &&
       state_q == Phase1St &&
       !clr_i &&
-      cnt_q[0] >= phase_cyc_i[1]
+      esc_cnt_o >= phase_cyc_i[1]
       |=>
       state_q == Phase2St)
   `ASSERT(CheckPhase2_A,
       !accu_fail_i &&
       state_q == Phase2St &&
       !clr_i &&
-      cnt_q[0] >= phase_cyc_i[2]
+      esc_cnt_o >= phase_cyc_i[2]
       |=>
       state_q == Phase3St)
   `ASSERT(CheckPhase3_A,
       !accu_fail_i &&
       state_q == Phase3St &&
       !clr_i &&
-      cnt_q[0] >= phase_cyc_i[3]
+      esc_cnt_o >= phase_cyc_i[3]
       |=>
       state_q == TerminalSt)
   `ASSERT(AccuFailToFsmError_A,
diff --git a/hw/top_earlgrey/ip_autogen/alert_handler/rtl/alert_handler_ping_timer.sv b/hw/top_earlgrey/ip_autogen/alert_handler/rtl/alert_handler_ping_timer.sv
index e515f97..346c2ad 100644
--- a/hw/top_earlgrey/ip_autogen/alert_handler/rtl/alert_handler_ping_timer.sv
+++ b/hw/top_earlgrey/ip_autogen/alert_handler/rtl/alert_handler_ping_timer.sv
@@ -83,7 +83,7 @@
   // Tandem LFSR Instances //
   ///////////////////////////
 
-  logic lfsr_en, lfsr_err;
+  logic cnt_set, lfsr_err;
   logic [LfsrWidth-1:0] entropy;
   logic [PING_CNT_DW + IdDw - 1:0] lfsr_state;
   assign entropy = (reseed_en) ? edn_data_i[LfsrWidth-1:0] : '0;
@@ -107,7 +107,7 @@
     .rst_ni,
     .seed_en_i  ( 1'b0                 ),
     .seed_i     ( '0                   ),
-    .lfsr_en_i  ( reseed_en || lfsr_en ),
+    .lfsr_en_i  ( reseed_en || cnt_set ),
     .entropy_i  ( entropy              ),
     .state_o    ( lfsr_state           ),
     .err_o      ( lfsr_err             )
@@ -154,66 +154,75 @@
   // In order to have enough margin, the escalation receiver timeout counters use a threshold that
   // is 4x higher than the value calculated above. With N_ESC_SEV = 4, PING_CNT_DW = 16 and
   // NUM_WAIT_COUNT = NUM_TIMEOUT_COUNT = 2 this amounts to a 22bit timeout threshold.
-
-  logic esc_cnt_en;
-  logic [1:0][PING_CNT_DW-1:0] esc_cnt_q;
-
+  //
   // We employ two redundant counters to guard against FI attacks.
   // If any of the two is glitched and the two counter states do not agree,
   // the FSM below is moved into a terminal error state and all ping alerts
   // are permanently asserted.
-  for (genvar k = 0; k < 2; k++) begin : gen_double_esc_cnt
 
-    logic [PING_CNT_DW-1:0] esc_cnt_d;
-    assign esc_cnt_d = (esc_cnt_q[k] >= PING_CNT_DW'(N_ESC_SEV-1)) ? '0 : (esc_cnt_q[k] + 1'b1);
+  logic esc_cnt_en, esc_cnt_clr, esc_cnt_error;
+  logic [PING_CNT_DW-1:0] esc_cnt;
+  assign esc_cnt_clr = (esc_cnt >= PING_CNT_DW'(N_ESC_SEV-1)) && esc_cnt_en;
 
-    prim_flop_en #(
-      .Width(PING_CNT_DW)
-    ) u_prim_flop_en (
-      .clk_i,
-      .rst_ni,
-      .en_i  ( esc_cnt_en   ),
-      .d_i   ( esc_cnt_d    ),
-      .q_o   ( esc_cnt_q[k] )
-    );
-  end
+  prim_count #(
+    .Width(PING_CNT_DW),
+    .OutSelDnCnt(0), // count up
+    .CntStyle(prim_count_pkg::CrossCnt),
+    // The alert handler behaves differently than other comportable IP. I.e., instead of sending out
+    // an alert signal, this condition is handled internally in the alert handler.
+    .EnableAlertTriggerSVA(0)
+  ) u_prim_count_esc_cnt (
+    .clk_i,
+    .rst_ni,
+    .clr_i(esc_cnt_clr),
+    .set_i(1'b0),
+    .set_cnt_i('0),
+    .en_i(esc_cnt_en),
+    .step_i(PING_CNT_DW'(1)),
+    .cnt_o(esc_cnt),
+    .err_o(esc_cnt_error)
+  );
 
   /////////////////////////////
   // Timer Counter Instances //
   /////////////////////////////
 
-  logic [1:0][PING_CNT_DW-1:0] cnt_q;
-  logic wait_cnt_load, timeout_cnt_load, timer_expired;
-  assign timer_expired = (cnt_q[0] == '0);
-  assign lfsr_en = wait_cnt_load || timeout_cnt_load;
-
   // We employ two redundant counters to guard against FI attacks.
   // If any of the two is glitched and the two counter states do not agree,
   // the FSM below is moved into a terminal error state and all ping alerts
   // are permanently asserted.
-  for (genvar k = 0; k < 2; k++) begin : gen_double_cnt
+  logic [PING_CNT_DW-1:0] cnt, cnt_setval;
+  logic wait_cnt_set, timeout_cnt_set, timer_expired, cnt_error;
+  assign timer_expired = (cnt == '0);
+  assign cnt_set = wait_cnt_set || timeout_cnt_set;
 
-    // the constant offset ensures a minimum cycle spacing between pings.
-    logic unused_bits;
-    logic [PING_CNT_DW-1:0] wait_cyc;
-    assign wait_cyc = (lfsr_state[PING_CNT_DW-1:0] | PING_CNT_DW'(3'b100));
-    assign unused_bits = lfsr_state[2];
+  prim_count #(
+    .Width(PING_CNT_DW),
+    .OutSelDnCnt(1), // count down
+    .CntStyle(prim_count_pkg::CrossCnt),
+    // The alert handler behaves differently than other comportable IP. I.e., instead of sending out
+    // an alert signal, this condition is handled internally in the alert handler.
+    .EnableAlertTriggerSVA(0)
+  ) u_prim_count_cnt (
+    .clk_i,
+    .rst_ni,
+    .clr_i(1'b0),
+    .set_i(cnt_set),
+    .set_cnt_i(cnt_setval),
+    .en_i(!timer_expired),
+    .step_i(PING_CNT_DW'(1)),
+    .cnt_o(cnt),
+    .err_o(cnt_error)
+  );
 
-    // note that the masks are used for DV/FPV only in order to reduce the state space.
-    logic [PING_CNT_DW-1:0] cnt_d;
-    assign cnt_d = (wait_cnt_load)    ? (wait_cyc & wait_cyc_mask_i) :
-                   (timeout_cnt_load) ? (ping_timeout_cyc_i)         :
-                   (cnt_q[k] > '0)    ? cnt_q[k] - 1'b1              : '0;
+  // the constant offset ensures a minimum cycle spacing between pings.
+  logic unused_bits;
+  logic [PING_CNT_DW-1:0] wait_cyc;
+  assign wait_cyc = (lfsr_state[PING_CNT_DW-1:0] | PING_CNT_DW'(3'b100));
+  assign unused_bits = lfsr_state[2];
 
-    prim_flop #(
-      .Width(PING_CNT_DW)
-    ) u_prim_flop (
-      .clk_i,
-      .rst_ni,
-      .d_i   ( cnt_d    ),
-      .q_o   ( cnt_q[k] )
-    );
-  end
+  // note that the masks are used for DV/FPV only in order to reduce the state space.
+  assign cnt_setval = (wait_cnt_set) ? (wait_cyc & wait_cyc_mask_i) : ping_timeout_cyc_i;
 
   ////////////////////////////
   // Ping and Timeout Logic //
@@ -224,7 +233,7 @@
 
   // generate ping enable vector
   assign alert_ping_req_o = NAlerts'(alert_ping_en) << id_to_ping;
-  assign esc_ping_req_o   = N_ESC_SEV'(esc_ping_en) << esc_cnt_q[0];
+  assign esc_ping_req_o   = N_ESC_SEV'(esc_ping_en) << esc_cnt;
 
   // under normal operation, these signals should never be asserted.
   // we place hand instantiated buffers here such that these signals are not
@@ -276,8 +285,8 @@
   always_comb begin : p_fsm
     // default
     state_d          = state_q;
-    wait_cnt_load    = 1'b0;
-    timeout_cnt_load = 1'b0;
+    wait_cnt_set    = 1'b0;
+    timeout_cnt_set = 1'b0;
     esc_cnt_en       = 1'b0;
     alert_ping_en    = 1'b0;
     esc_ping_en      = 1'b0;
@@ -292,14 +301,14 @@
       InitSt: begin
         if (en_i) begin
           state_d = AlertWaitSt;
-          wait_cnt_load = 1'b1;
+          wait_cnt_set = 1'b1;
         end
       end
       // wait for random amount of cycles
       AlertWaitSt: begin
         if (timer_expired) begin
           state_d = AlertPingSt;
-          timeout_cnt_load = 1'b1;
+          timeout_cnt_set = 1'b1;
         end
       end
       // send out an alert ping request and wait for a ping
@@ -310,7 +319,7 @@
         alert_ping_en = id_vld;
         if (timer_expired || |(alert_ping_ok_i & alert_ping_req_o) || !id_vld) begin
           state_d           = EscWaitSt;
-          wait_cnt_load     = 1'b1;
+          wait_cnt_set     = 1'b1;
           if (timer_expired) begin
             alert_ping_fail_o = 1'b1;
           end
@@ -320,7 +329,7 @@
       EscWaitSt: begin
         if (timer_expired) begin
           state_d          = EscPingSt;
-          timeout_cnt_load = 1'b1;
+          timeout_cnt_set = 1'b1;
         end
       end
       // send out an escalation ping request and wait for a ping
@@ -329,7 +338,7 @@
         esc_ping_en = 1'b1;
         if (timer_expired || |(esc_ping_ok_i & esc_ping_req_o)) begin
           state_d         = AlertWaitSt;
-          wait_cnt_load   = 1'b1;
+          wait_cnt_set   = 1'b1;
           esc_cnt_en      = 1'b1;
           if (timer_expired) begin
             esc_ping_fail_o = 1'b1;
@@ -350,9 +359,7 @@
 
     // if the two LFSR or counter states do not agree,
     // we move into the terminal state.
-    if (lfsr_err                  ||
-        cnt_q[0]      != cnt_q[1] ||
-        esc_cnt_q[0]  != esc_cnt_q[1]) begin
+    if (lfsr_err || cnt_error || esc_cnt_error) begin
        state_d = FsmErrorSt;
     end
   end