../src/lowrisc_ip_alert_handler_component_0.1/rtl/alert_handler_esc_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 escalation timer, which times the four escalation
   6: // phases. There are two mechanisms that can trigger the escalation protocol:
   7: //
   8: // 1) via accum_trigger_i, which will be asserted once the accumulator value
   9: //    exceeds a programmable amount of alert occurences.
  10: //
  11: // 2) via an interrupt timeout, if this is enabled. If this functionality is
  12: //    enabled, the internal escalation counter is reused to check whether the
  13: //    interrupt times out. If it does time out, the outcome is the same as if
  14: //    accum_trigger_i where asserted.
  15: //
  16: // Note that escalation always takes precedence over the interrupt timeout.
  17: //
  18: 
  19: `include "prim_assert.sv"
  20: 
  21: module alert_handler_esc_timer import alert_pkg::*; (
  22:   input                        clk_i,
  23:   input                        rst_ni,
  24:   input                        en_i,           // enables timeout/escalation
  25:   input                        clr_i,          // aborts escalation
  26:   input                        accum_trig_i,   // this will trigger escalation
  27:   input                        timeout_en_i,   // enables timeout
  28:   input        [EscCntDw-1:0]  timeout_cyc_i,  // interrupt timeout. 0 = disabled
  29:   input        [N_ESC_SEV-1:0] esc_en_i,       // escalation signal enables
  30:   input        [N_ESC_SEV-1:0]
  31:                [PHASE_DW-1:0]  esc_map_i,      // escalation signal / phase map
  32:   input        [N_PHASES-1:0]
  33:                [EscCntDw-1:0]  phase_cyc_i,    // cycle counts of individual phases
  34:   output logic                 esc_trig_o,     // asserted if escalation triggers
  35:   output logic [EscCntDw-1:0]  esc_cnt_o,      // current timeout / escalation count
  36:   output logic [N_ESC_SEV-1:0] esc_sig_en_o,   // escalation signal outputs
  37:   // current state output
  38:   // 000: idle, 001: irq timeout counting 100: phase0, 101: phase1, 110: phase2, 111: phase3
  39:   output cstate_e              esc_state_o
  40: );
  41: 
  42:   /////////////
  43:   // Counter //
  44:   /////////////
  45: 
  46:   cstate_e state_d, state_q;
  47: 
  48:   logic cnt_en, cnt_clr, cnt_ge;
  49:   logic [EscCntDw-1:0] cnt_d, cnt_q;
  50: 
  51:   // escalation counter, used for all phases and the timeout
  52:   assign cnt_d = cnt_q + 1'b1;
  53: 
  54:   // current state output
  55:   assign esc_state_o = state_q;
  56:   assign esc_cnt_o   = cnt_q;
  57: 
  58:   // threshold test, the thresholds are muxed further below
  59:   // depending on the current state
  60:   logic [EscCntDw-1:0] thresh;
  61:   assign cnt_ge    = (cnt_q >= thresh);
  62: 
  63:   //////////////
  64:   // Main FSM //
  65:   //////////////
  66: 
  67:   logic [N_PHASES-1:0] phase_oh;
  68: 
  69:   always_comb begin : p_fsm
  70:     // default
  71:     state_d    = state_q;
  72:     cnt_en     = 1'b0;
  73:     cnt_clr    = 1'b0;
  74:     esc_trig_o = 1'b0;
  75:     phase_oh   = '0;
  76:     thresh     = timeout_cyc_i;
  77: 
  78:     unique case (state_q)
  79:       // wait for an escalation trigger or an alert trigger
  80:       // the latter will trigger an interrupt timeout
  81:       Idle: begin
  82:         cnt_clr = 1'b1;
  83: 
  84:         if (accum_trig_i && en_i && !clr_i) begin
  85:           state_d    = Phase0;
  86:           cnt_en     = 1'b1;
  87:           esc_trig_o = 1'b1;
  88:         // the counter is zero in this state. so if the
  89:         // timeout count is zero (==disabled), cnt_ge will be true.
  90:         end else if (timeout_en_i && !cnt_ge && en_i) begin
  91:           cnt_en  = 1'b1;
  92:           state_d = Timeout;
  93:         end
  94:       end
  95:       // we are in interrupt timeout state
  96:       // in case an escalation comes in, we immediately have to
  97:       // switch over to the first escalation phase.
  98:       // in case the interrupt timeout hits it's cycle count, we
  99:       // also enter escalation phase0.
 100:       // ongoing timeouts can always be cleared.
 101:       Timeout: begin
 102:         if ((accum_trig_i && en_i && !clr_i) || (cnt_ge && timeout_en_i)) begin
 103:           state_d    = Phase0;
 104:           cnt_en     = 1'b1;
 105:           cnt_clr    = 1'b1;
 106:           esc_trig_o = 1'b1;
 107:         // the timeout enable is connected to the irq state
 108:         // if that is cleared, stop the timeout counter
 109:         end else if (timeout_en_i) begin
 110:           cnt_en  = 1'b1;
 111:         end else begin
 112:           state_d = Idle;
 113:           cnt_clr = 1'b1;
 114:         end
 115:       end
 116:       // note: autolocking the clear signal is done in the regfile
 117:       Phase0: begin
 118:         cnt_en      = 1'b1;
 119:         phase_oh[0] = 1'b1;
 120:         thresh      = phase_cyc_i[0];
 121: 
 122:         if (clr_i) begin
 123:           state_d = Idle;
 124:           cnt_clr = 1'b1;
 125:           cnt_en  = 1'b0;
 126:         end else if (cnt_ge) begin
 127:           state_d = Phase1;
 128:           cnt_clr = 1'b1;
 129:           cnt_en  = 1'b1;
 130:         end
 131:       end
 132:       Phase1: begin
 133:         cnt_en      = 1'b1;
 134:         phase_oh[1] = 1'b1;
 135:         thresh      = phase_cyc_i[1];
 136: 
 137:         if (clr_i) begin
 138:           state_d = Idle;
 139:           cnt_clr = 1'b1;
 140:           cnt_en  = 1'b0;
 141:         end else if (cnt_ge) begin
 142:           state_d = Phase2;
 143:           cnt_clr = 1'b1;
 144:           cnt_en  = 1'b1;
 145:         end
 146:       end
 147:       Phase2: begin
 148:         cnt_en      = 1'b1;
 149:         phase_oh[2] = 1'b1;
 150:         thresh      = phase_cyc_i[2];
 151: 
 152:         if (clr_i) begin
 153:           state_d = Idle;
 154:           cnt_clr = 1'b1;
 155:           cnt_en  = 1'b0;
 156:         end else if (cnt_ge) begin
 157:           state_d = Phase3;
 158:           cnt_clr = 1'b1;
 159:         end
 160:       end
 161:       Phase3: begin
 162:         cnt_en      = 1'b1;
 163:         phase_oh[3] = 1'b1;
 164:         thresh      = phase_cyc_i[3];
 165: 
 166:         if (clr_i) begin
 167:           state_d = Idle;
 168:           cnt_clr = 1'b1;
 169:           cnt_en  = 1'b0;
 170:         end else if (cnt_ge) begin
 171:           state_d = Terminal;
 172:           cnt_clr = 1'b1;
 173:           cnt_en  = 1'b0;
 174:         end
 175:       end
 176:       // final, terminal state after escalation.
 177:       // if clr is locked down, only a system reset
 178:       // will get us out of this state
 179:       Terminal: begin
 180:         cnt_clr = 1'b1;
 181:         if (clr_i) begin
 182:           state_d = Idle;
 183:         end
 184:       end
 185:       default: state_d = Idle;
 186:     endcase
 187:   end
 188: 
 189:   logic [N_ESC_SEV-1:0][N_PHASES-1:0] esc_map_oh;
 190:   for (genvar k = 0; k < N_ESC_SEV; k++) begin : gen_phase_map
 191:     // generate configuration mask for escalation enable signals
 192:     assign esc_map_oh[k] = N_ESC_SEV'(esc_en_i[k]) << esc_map_i[k];
 193:     // mask reduce current phase state vector
 194:     assign esc_sig_en_o[k] = |(esc_map_oh[k] & phase_oh);
 195:   end
 196: 
 197:   ///////////////
 198:   // Registers //
 199:   ///////////////
 200: 
 201:   // switch interrupt / escalation mode
 202:   always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
 203:     if (!rst_ni) begin
 204:       state_q <= Idle;
 205:       cnt_q   <= '0;
 206:     end else begin
 207:       state_q <= state_d;
 208: 
 209:       // escalation counter
 210:       if (cnt_en && cnt_clr) begin
 211:         cnt_q <= EscCntDw'(1'b1);
 212:       end else if (cnt_clr) begin
 213:         cnt_q <= '0;
 214:       end else if (cnt_en) begin
 215:         cnt_q <= cnt_d;
 216:       end
 217:     end
 218:   end
 219: 
 220:   ////////////////
 221:   // Assertions //
 222:   ////////////////
 223: 
 224:   // a clear should always bring us back to idle
 225:   `ASSERT(CheckClr, clr_i && !(state_q inside {Idle, Timeout}) |=>
 226:       state_q == Idle)
 227:   // if currently in idle and not enabled, must remain here
 228:   `ASSERT(CheckEn,  state_q == Idle && !en_i |=>
 229:       state_q == Idle)
 230:   // Check if accumulation trigger correctly captured
 231:   `ASSERT(CheckAccumTrig0,  accum_trig_i && state_q == Idle && en_i && !clr_i |=>
 232:       state_q == Phase0)
 233:   `ASSERT(CheckAccumTrig1,  accum_trig_i && state_q == Timeout && en_i && !clr_i |=>
 234:       state_q == Phase0)
 235:   // Check if timeout correctly captured
 236:   `ASSERT(CheckTimeout0, state_q == Idle && timeout_en_i && en_i && timeout_cyc_i != 0 &&
 237:       !accum_trig_i |=> state_q == Timeout)
 238:   `ASSERT(CheckTimeout1, state_q == Timeout && timeout_en_i && cnt_q < timeout_cyc_i &&
 239:       !accum_trig_i |=> state_q == Timeout)
 240:   `ASSERT(CheckTimeout2, state_q == Timeout && !timeout_en_i && !accum_trig_i |=>
 241:       state_q == Idle)
 242:   // Check if timeout correctly triggers escalation
 243:   `ASSERT(CheckTimeoutTrig, state_q == Timeout && timeout_en_i &&
 244:       cnt_q == timeout_cyc_i |=> state_q == Phase0)
 245:   // Check whether escalation phases are correctly switched
 246:   `ASSERT(CheckPhase0, state_q == Phase0 && !clr_i && cnt_q >= phase_cyc_i[0] |=>
 247:       state_q == Phase1)
 248:   `ASSERT(CheckPhase1, state_q == Phase1 && !clr_i && cnt_q >= phase_cyc_i[1] |=>
 249:       state_q == Phase2)
 250:   `ASSERT(CheckPhase2, state_q == Phase2 && !clr_i && cnt_q >= phase_cyc_i[2] |=>
 251:       state_q == Phase3)
 252:   `ASSERT(CheckPhase3, state_q == Phase3 && !clr_i && cnt_q >= phase_cyc_i[3] |=>
 253:       state_q == Terminal)
 254: 
 255: endmodule : alert_handler_esc_timer
 256: