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: