hw/ip/alert_handler/rtl/alert_handler_esc_timer.sv Cov: 95.9%
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: module alert_handler_esc_timer import alert_pkg::*; (
20: input clk_i,
21: input rst_ni,
22: input en_i, // enables timeout/escalation
23: input clr_i, // aborts escalation
24: input accum_trig_i, // this will trigger escalation
25: input timeout_en_i, // enables timeout
26: input [EscCntDw-1:0] timeout_cyc_i, // interrupt timeout. 0 = disabled
27: input [N_ESC_SEV-1:0] esc_en_i, // escalation signal enables
28: input [N_ESC_SEV-1:0]
29: [PHASE_DW-1:0] esc_map_i, // escalation signal / phase map
30: input [N_PHASES-1:0]
31: [EscCntDw-1:0] phase_cyc_i, // cycle counts of individual phases
32: output logic esc_trig_o, // asserted if escalation triggers
33: output logic [EscCntDw-1:0] esc_cnt_o, // current timeout / escalation count
34: output logic [N_ESC_SEV-1:0] esc_sig_en_o, // escalation signal outputs
35: // current state output
36: // 000: idle, 001: irq timeout counting 100: phase0, 101: phase1, 110: phase2, 111: phase3
37: output cstate_e esc_state_o
38: );
39:
40: /////////////
41: // Counter //
42: /////////////
43:
44: cstate_e state_d, state_q;
45:
46: logic cnt_en, cnt_clr, cnt_ge;
47: logic [EscCntDw-1:0] cnt_d, cnt_q;
48:
49: // escalation counter, used for all phases and the timeout
50: assign cnt_d = cnt_q + 1'b1;
51:
52: // current state output
53: assign esc_state_o = state_q;
54: assign esc_cnt_o = cnt_q;
55:
56: // threshold test, the thresholds are muxed further below
57: // depending on the current state
58: logic [EscCntDw-1:0] thresh;
59: assign cnt_ge = (cnt_q >= thresh);
60:
61: //////////////
62: // Main FSM //
63: //////////////
64:
65: logic [N_PHASES-1:0] phase_oh;
66:
67: always_comb begin : p_fsm
68: // default
69: state_d = state_q;
70: cnt_en = 1'b0;
71: cnt_clr = 1'b0;
72: esc_trig_o = 1'b0;
73: phase_oh = '0;
74: thresh = timeout_cyc_i;
75:
76: unique case (state_q)
77: // wait for an escalation trigger or an alert trigger
78: // the latter will trigger an interrupt timeout
79: // note, clr_i is intentionally not used in Idle such that any trigger
80: // will have to go through escalation, if enabled
81: Idle: begin
82: cnt_clr = 1'b1;
83:
84: if (accum_trig_i && en_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 || (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: end
174: end
175: // final, terminal state after escalation.
176: // if clr is locked down, only a system reset
177: // will get us out of this state
178: Terminal: begin
179: cnt_clr = 1'b1;
180: if (clr_i) begin
181: state_d = Idle;
182: end
183: end
184: default: state_d = Idle;
185: endcase
186: end
187:
188: logic [N_ESC_SEV-1:0][N_PHASES-1:0] esc_map_oh;
189: for (genvar k = 0; k < N_ESC_SEV; k++) begin : gen_phase_map
190: // generate configuration mask for escalation enable signals
191: assign esc_map_oh[k] = N_ESC_SEV'(esc_en_i[k]) << esc_map_i[k];
192: // mask reduce current phase state vector
193: assign esc_sig_en_o[k] = |(esc_map_oh[k] & phase_oh);
194: end
195:
196: ///////////////
197: // Registers //
198: ///////////////
199:
200: // switch interrupt / escalation mode
201: always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
202: if (!rst_ni) begin
203: state_q <= Idle;
204: cnt_q <= '0;
205: end else begin
206: state_q <= state_d;
207:
208: // escalation counter
209: if (cnt_en && cnt_clr) begin
210: cnt_q <= EscCntDw'(1'b1);
211: end else if (cnt_clr) begin
212: cnt_q <= '0;
213: end else if (cnt_en) begin
214: cnt_q <= cnt_d;
215: end
216: end
217: end
218:
219: ////////////////
220: // Assertions //
221: ////////////////
222:
223: // a clear should always bring us back to idle
224: `ASSERT(CheckClr, clr_i && !(state_q inside {Idle, Timeout}) |=>
225: state_q == Idle, clk_i, !rst_ni)
226: // if currently in idle and not enabled, must remain here
227: `ASSERT(CheckEn, state_q == Idle && !en_i |=>
228: state_q == Idle, clk_i, !rst_ni)
229: // Check if accumulation trigger correctly captured
230: `ASSERT(CheckAccumTrig0, accum_trig_i && state_q == Idle && en_i |=>
231: state_q == Phase0, clk_i, !rst_ni)
232: `ASSERT(CheckAccumTrig1, accum_trig_i && state_q == Timeout && en_i |=>
233: state_q == Phase0, clk_i, !rst_ni)
234: // Check if timeout correctly captured
235: `ASSERT(CheckTimeout0, state_q == Idle && timeout_en_i && en_i && timeout_cyc_i != 0 |=>
236: state_q == Timeout, clk_i, !rst_ni || accum_trig_i)
237: `ASSERT(CheckTimeout1, state_q == Timeout && timeout_en_i && cnt_q < timeout_cyc_i |=>
238: state_q == Timeout, clk_i, !rst_ni || accum_trig_i)
239: `ASSERT(CheckTimeout2, state_q == Timeout && !timeout_en_i |=>
240: state_q == Idle, clk_i, !rst_ni || accum_trig_i)
241: // Check if timeout correctly triggers escalation
242: `ASSERT(CheckTimeoutTrig, state_q == Timeout && timeout_en_i &&
243: cnt_q == timeout_cyc_i |=> state_q == Phase0, clk_i, !rst_ni)
244: // Check whether escalation phases are correctly switched
245: `ASSERT(CheckPhase0, state_q == Phase0 && !clr_i && cnt_q >= phase_cyc_i[0] |=>
246: state_q == Phase1, clk_i, !rst_ni)
247: `ASSERT(CheckPhase1, state_q == Phase1 && !clr_i && cnt_q >= phase_cyc_i[1] |=>
248: state_q == Phase2, clk_i, !rst_ni)
249: `ASSERT(CheckPhase2, state_q == Phase2 && !clr_i && cnt_q >= phase_cyc_i[2] |=>
250: state_q == Phase3, clk_i, !rst_ni)
251: `ASSERT(CheckPhase3, state_q == Phase3 && !clr_i && cnt_q >= phase_cyc_i[3] |=>
252: state_q == Terminal, clk_i, !rst_ni)
253:
254: endmodule : alert_handler_esc_timer
255: