../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: