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