hw/ip/alert_handler/rtl/alert_handler_ping_timer.sv Cov: 100%

   1: // Copyright lowRISC contributors.
   2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
   3: // SPDX-License-Identifier: Apache-2.0
   4: //
   5: // This module implements the ping mechanism. Once enabled, this module uses an
   6: // LFSR-based PRNG to
   7: //
   8: // a) determine the next peripheral index to be pinged (can be an alert receiver or an
   9: //    escalation sender). it it is detected that this particular peripheral is disabled,
  10: //    another index will be drawn from the PRNG.
  11: //
  12: // b) determine the amount of pause cycles to wait before pinging the peripheral selected in a).
  13: //
  14: // Once the ping timer waited for the amount of pause cycles determined in b), it asserts
  15: // the ping enable signal of the peripheral determined in a). If that peripheral does
  16: // not respond within the ping timeout window, an internal alert will be raised.
  17: //
  18: // Further, if a spurious ping_ok signal is detected (i.e., a ping ok that has not been
  19: // requested), the ping timer will also raise an internal alert.
  20: //
  21: 
  22: module alert_handler_ping_timer import alert_pkg::*; #(
  23:   // Enable this for DV, disable this for long LFSRs in FPV
  24:   parameter bit                MaxLenSVA  = 1'b1,
  25:   // Can be disabled in cases where entropy
  26:   // inputs are unused in order to not distort coverage
  27:   // (the SVA will be unreachable in such cases)
  28:   parameter bit                LockupSVA  = 1'b1
  29: ) (
  30:   input                          clk_i,
  31:   input                          rst_ni,
  32:   input                          entropy_i,          // from TRNG
  33:   input                          en_i,               // enable ping testing
  34:   input        [NAlerts-1:0]     alert_en_i,         // determines which alerts to ping
  35:   input        [PING_CNT_DW-1:0] ping_timeout_cyc_i, // timeout in cycles
  36:   input        [PING_CNT_DW-1:0] wait_cyc_mask_i,    // wait cycles mask
  37:   output logic [NAlerts-1:0]     alert_ping_en_o,    // enable to alert receivers
  38:   output logic [N_ESC_SEV-1:0]   esc_ping_en_o,      // enable to esc senders
  39:   input        [NAlerts-1:0]     alert_ping_ok_i,    // response from alert receivers
  40:   input        [N_ESC_SEV-1:0]   esc_ping_ok_i,      // response from esc senders
  41:   output logic                   alert_ping_fail_o,  // any of the alert receivers failed
  42:   output logic                   esc_ping_fail_o     // any of the esc senders failed
  43: );
  44: 
  45:   localparam int unsigned NModsToPing = NAlerts + N_ESC_SEV;
  46:   localparam int unsigned IdDw        = $clog2(NModsToPing);
  47: 
  48:   // this defines a random permutation
  49:   localparam int unsigned perm [32] = '{
  50:     4, 11, 25,  3,   //
  51:     15, 16,  1, 10,  //
  52:     2, 22,  7,  0,   //
  53:     23, 28, 30, 19,  //
  54:     27, 12, 24, 26,  //
  55:     14, 21, 18,  5,  //
  56:     13,  8, 29, 31,  //
  57:     20,  6,  9, 17   //
  58:   };
  59: 
  60:   //////////
  61:   // PRNG //
  62:   //////////
  63: 
  64:   logic lfsr_en;
  65:   logic [31:0] lfsr_state, perm_state;
  66:   logic [16-IdDw-1:0] unused_perm_state;
  67: 
  68:   prim_lfsr #(
  69:     .LfsrDw      ( 32         ),
  70:     .EntropyDw   ( 1          ),
  71:     .StateOutDw  ( 32         ),
  72:     .DefaultSeed ( LfsrSeed   ),
  73:     .MaxLenSVA   ( MaxLenSVA  ),
  74:     .LockupSVA   ( LockupSVA  ),
  75:     .ExtSeedSVA  ( 1'b0       ) // ext seed is unused
  76:   ) i_prim_lfsr (
  77:     .clk_i,
  78:     .rst_ni,
  79:     .seed_en_i  ( 1'b0       ),
  80:     .seed_i     ( '0         ),
  81:     .lfsr_en_i  ( lfsr_en    ),
  82:     .entropy_i,
  83:     .state_o    ( lfsr_state )
  84:   );
  85: 
  86:   for (genvar k = 0; k < 32; k++) begin : gen_perm
  87:     assign perm_state[k] = lfsr_state[perm[k]];
  88:   end
  89: 
  90:   logic [IdDw-1:0] id_to_ping;
  91:   logic [PING_CNT_DW-1:0] wait_cyc;
  92:   // we only use bits up to 23, as IdDw is 8bit maximum
  93:   assign id_to_ping = perm_state[16 +: IdDw];
  94: 
  95:   // to avoid lint warnings
  96:   assign unused_perm_state = perm_state[31:16+IdDw];
  97: 
  98:   // concatenate with constant offset, introduce some stagger
  99:   // by concatenating the lower bits below
 100:   assign wait_cyc   = PING_CNT_DW'({perm_state[15:2], 8'h01, perm_state[1:0]}) & wait_cyc_mask_i;
 101: 
 102:   logic [2**IdDw-1:0] enable_mask;
 103:   always_comb begin : p_enable_mask
 104:     enable_mask                        = '0;         // tie off unused
 105:     enable_mask[NAlerts-1:0]           = alert_en_i; // alerts
 106:     enable_mask[NModsToPing-1:NAlerts] = '1;         // escalation senders
 107:   end
 108: 
 109:   logic id_vld;
 110:   // check if the randomly drawn ID is actually valid and the alert is enabled
 111:   assign id_vld  = enable_mask[id_to_ping];
 112: 
 113:   /////////////
 114:   // Counter //
 115:   /////////////
 116: 
 117:   logic [PING_CNT_DW-1:0] cnt_d, cnt_q;
 118:   logic cnt_en, cnt_clr;
 119:   logic wait_ge, timeout_ge;
 120: 
 121:   assign cnt_d      = cnt_q + 1'b1;
 122:   assign wait_ge    = (cnt_q >= wait_cyc);
 123:   assign timeout_ge = (cnt_q >= ping_timeout_cyc_i);
 124: 
 125:   ////////////////////////////
 126:   // Ping and Timeout Logic //
 127:   ////////////////////////////
 128: 
 129:   typedef enum logic [1:0] {Init, RespWait, DoPing} state_e;
 130:   state_e state_d, state_q;
 131:   logic ping_en, ping_ok;
 132:   logic [NModsToPing-1:0] ping_sel;
 133:   logic [NModsToPing-1:0] spurious_ping;
 134:   logic spurious_alert_ping, spurious_esc_ping;
 135: 
 136:   // generate ping enable vector
 137:   assign ping_sel        = NModsToPing'(ping_en) << id_to_ping;
 138:   assign alert_ping_en_o = ping_sel[NAlerts-1:0];
 139:   assign esc_ping_en_o   = ping_sel[NModsToPing-1:NAlerts];
 140: 
 141:   // mask out response
 142:   assign ping_ok             = |({esc_ping_ok_i, alert_ping_ok_i} & ping_sel);
 143:   assign spurious_ping       = ({esc_ping_ok_i, alert_ping_ok_i} & ~ping_sel);
 144:   // under normal operation, these signals should never be asserted.
 145:   // double check that these signals are not optimized away during synthesis.
 146:   // this may need "don't touch" or "no boundary optimization" constraints
 147:   assign spurious_alert_ping = |spurious_ping[NAlerts-1:0];
 148:   assign spurious_esc_ping   = |spurious_ping[NModsToPing-1:NAlerts];
 149: 
 150:   always_comb begin : p_fsm
 151:     // default
 152:     state_d = state_q;
 153:     cnt_en  = 1'b0;
 154:     cnt_clr = 1'b0;
 155:     lfsr_en = 1'b0;
 156:     ping_en = 1'b0;
 157:     // this captures spurious
 158:     alert_ping_fail_o = spurious_alert_ping;
 159:     esc_ping_fail_o   = spurious_esc_ping;
 160: 
 161:     unique case (state_q)
 162:       // wait until activiated
 163:       // we never return to this state
 164:       // once activated!
 165:       Init: begin
 166:         cnt_clr = 1'b1;
 167:         if (en_i) begin
 168:           state_d = RespWait;
 169:         end
 170:       end
 171:       // wait for random amount of cycles
 172:       // draw another ID/wait count if the
 173:       // peripheral ID is not valid
 174:       RespWait: begin
 175:         if (!id_vld) begin
 176:           lfsr_en = 1'b1;
 177:           cnt_clr = 1'b1;
 178:         end else if (wait_ge) begin
 179:           state_d = DoPing;
 180:           cnt_clr = 1'b1;
 181:         end else begin
 182:           cnt_en = 1'b1;
 183:         end
 184:       end
 185:       // send out ping request and wait for a ping
 186:       // response or a ping timeout (whatever comes first)
 187:       DoPing: begin
 188:         cnt_en  = 1'b1;
 189:         ping_en = 1'b1;
 190:         if (timeout_ge || ping_ok) begin
 191:           state_d = RespWait;
 192:           lfsr_en = 1'b1;
 193:           cnt_clr = 1'b1;
 194:           if (timeout_ge) begin
 195:             if (id_to_ping < NAlerts) begin
 196:               alert_ping_fail_o = 1'b1;
 197:             end else begin
 198:               esc_ping_fail_o   = 1'b1;
 199:             end
 200:           end
 201:         end
 202:       end
 203:       // this should never happen
 204:       // if we for some reason end up in this state (e.g. malicious glitching)
 205:       // we are going to assert both ping fails continuously
 206:       default: begin
 207:         alert_ping_fail_o = 1'b1;
 208:         esc_ping_fail_o   = 1'b1;
 209:       end
 210:     endcase
 211:   end
 212: 
 213:   ///////////////
 214:   // Registers //
 215:   ///////////////
 216: 
 217:   always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
 218:     if (!rst_ni) begin
 219:       state_q <= Init;
 220:       cnt_q   <= '0;
 221:     end else begin
 222:       state_q <= state_d;
 223: 
 224:       if (cnt_clr) begin
 225:         cnt_q <= '0;
 226:       end else if (cnt_en) begin
 227:         cnt_q <= cnt_d;
 228:       end
 229:     end
 230:   end
 231: 
 232:   ////////////////
 233:   // Assertions //
 234:   ////////////////
 235: 
 236:   // internals
 237:   `ASSERT(PingOH0_A, $onehot0(ping_sel), clk_i, !rst_ni)
 238:   // we should never get into the ping state without knowing
 239:   // which module to ping
 240:   `ASSERT(PingOH_A, ping_en |-> $onehot(ping_sel), clk_i, !rst_ni)
 241: 
 242: endmodule : alert_handler_ping_timer
 243: