-
Notifications
You must be signed in to change notification settings - Fork 813
/
Copy pathprim_alert_sender.sv
392 lines (350 loc) · 13.8 KB
/
prim_alert_sender.sv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// The alert sender primitive module differentially encodes and transmits an
// alert signal to the prim_alert_receiver module. An alert will be signalled
// by a full handshake on alert_p/n and ack_p/n. The alert_req_i signal may
// be continuously asserted, in which case the alert signalling handshake
// will be repeatedly initiated.
//
// The alert_req_i signal may also be used as part of req/ack. The parent module
// can keep alert_req_i asserted until it has been ack'd (transferred to the alert
// receiver). The parent module is not required to use this.
//
// In case the alert sender parameter IsFatal is set to 1, an incoming alert
// alert_req_i is latched in a local register until the next reset, causing the
// alert sender to behave as if alert_req_i were continously asserted.
// The alert_state_o output reflects the state of this internal latching register.
//
// The alert sender also exposes an alert test input, which can be used to trigger
// single alert handshakes. This input behaves exactly the same way as the
// alert_req_i input with IsFatal set to 0. Test alerts do not cause alert_ack_o
// to be asserted, nor are they latched until reset (regardless of the value of the
// IsFatal parameter).
//
// Further, this module supports in-band ping testing, which means that a level
// change on the ping_p/n diff pair will result in a full-handshake response
// on alert_p/n and ack_p/n.
//
// The protocol works in both asynchronous and synchronous cases. In the
// asynchronous case, the parameter AsyncOn must be set to 1'b1 in order to
// instantiate additional synchronization logic. Further, it must be ensured
// that the timing skew between all diff pairs is smaller than the shortest
// clock period of the involved clocks.
//
// Incorrectly encoded diff inputs can be detected and will be signalled
// to the receiver by placing an inconsistent diff value on the differential
// output (and continuously toggling it).
//
// See also: prim_alert_receiver, prim_diff_decode, alert_handler
`include "prim_assert.sv"
module prim_alert_sender
import prim_alert_pkg::*;
#(
// enables additional synchronization logic
parameter bit AsyncOn = 1'b1,
// alert sender will latch the incoming alert event permanently and
// keep on sending alert events until the next reset.
parameter bit IsFatal = 1'b0
) (
input clk_i,
input rst_ni,
// alert test trigger (this will never be latched, even if IsFatal == 1)
input alert_test_i,
// native alert from the peripheral
input alert_req_i,
output logic alert_ack_o,
// state of the alert latching register
output logic alert_state_o,
// ping input diff pair and ack diff pair
input alert_rx_t alert_rx_i,
// alert output diff pair
output alert_tx_t alert_tx_o
);
/////////////////////////////////
// decode differential signals //
/////////////////////////////////
logic ping_sigint, ping_event, ping_n, ping_p;
// This prevents further tool optimizations of the differential signal.
prim_sec_anchor_buf #(
.Width(2)
) u_prim_buf_ping (
.in_i({alert_rx_i.ping_n,
alert_rx_i.ping_p}),
.out_o({ping_n,
ping_p})
);
prim_diff_decode #(
.AsyncOn(AsyncOn)
) u_decode_ping (
.clk_i,
.rst_ni,
.diff_pi ( ping_p ),
.diff_ni ( ping_n ),
.level_o ( ),
.rise_o ( ),
.fall_o ( ),
.event_o ( ping_event ),
.sigint_o ( ping_sigint )
);
logic ack_sigint, ack_level, ack_n, ack_p;
// This prevents further tool optimizations of the differential signal.
prim_sec_anchor_buf #(
.Width(2)
) u_prim_buf_ack (
.in_i({alert_rx_i.ack_n,
alert_rx_i.ack_p}),
.out_o({ack_n,
ack_p})
);
prim_diff_decode #(
.AsyncOn(AsyncOn)
) u_decode_ack (
.clk_i,
.rst_ni,
.diff_pi ( ack_p ),
.diff_ni ( ack_n ),
.level_o ( ack_level ),
.rise_o ( ),
.fall_o ( ),
.event_o ( ),
.sigint_o ( ack_sigint )
);
///////////////////////////////////////////////////
// main protocol FSM that drives the diff output //
///////////////////////////////////////////////////
typedef enum logic [2:0] {
Idle,
AlertHsPhase1,
AlertHsPhase2,
PingHsPhase1,
PingHsPhase2,
Pause0,
Pause1
} state_e;
state_e state_d, state_q;
logic alert_pq, alert_nq, alert_pd, alert_nd;
logic sigint_detected;
assign sigint_detected = ack_sigint | ping_sigint;
// diff pair output
assign alert_tx_o.alert_p = alert_pq;
assign alert_tx_o.alert_n = alert_nq;
// alert and ping set regs
logic alert_set_d, alert_set_q, alert_clr;
logic alert_test_set_d, alert_test_set_q;
logic ping_set_d, ping_set_q, ping_clr;
logic alert_req_trigger, alert_test_trigger, ping_trigger;
// if handshake is ongoing, capture additional alert requests.
logic alert_req;
prim_sec_anchor_buf #(
.Width(1)
) u_prim_buf_in_req (
.in_i(alert_req_i),
.out_o(alert_req)
);
assign alert_req_trigger = alert_req | alert_set_q;
if (IsFatal) begin : gen_fatal
assign alert_set_d = alert_req_trigger;
end else begin : gen_recov
assign alert_set_d = (alert_clr) ? 1'b0 : alert_req_trigger;
end
// the alert test request is always cleared.
assign alert_test_trigger = alert_test_i | alert_test_set_q;
assign alert_test_set_d = (alert_clr) ? 1'b0 : alert_test_trigger;
logic alert_trigger;
assign alert_trigger = alert_req_trigger | alert_test_trigger;
assign ping_trigger = ping_set_q | ping_event;
assign ping_set_d = (ping_clr) ? 1'b0 : ping_trigger;
// alert event acknowledge and state (not affected by alert_test_i)
assign alert_ack_o = alert_clr & alert_set_q;
assign alert_state_o = alert_set_q;
// this FSM performs a full four phase handshake upon a ping or alert trigger.
// note that the latency of the alert_p/n diff pair is at least one cycle
// until it enters the receiver FSM. the same holds for the ack_* diff pair
// input. in case a signal integrity issue is detected, the FSM bails out,
// sets the alert_p/n diff pair to the same value and toggles it in order to
// signal that condition over to the receiver.
always_comb begin : p_fsm
// default
state_d = state_q;
alert_pd = 1'b0;
alert_nd = 1'b1;
ping_clr = 1'b0;
alert_clr = 1'b0;
unique case (state_q)
Idle: begin
// alert always takes precedence
if (alert_trigger || ping_trigger) begin
state_d = (alert_trigger) ? AlertHsPhase1 : PingHsPhase1;
alert_pd = 1'b1;
alert_nd = 1'b0;
end
end
// waiting for ack from receiver
AlertHsPhase1: begin
if (ack_level) begin
state_d = AlertHsPhase2;
end else begin
alert_pd = 1'b1;
alert_nd = 1'b0;
end
end
// wait for deassertion of ack
AlertHsPhase2: begin
if (!ack_level) begin
state_d = Pause0;
alert_clr = 1'b1;
end
end
// waiting for ack from receiver
PingHsPhase1: begin
if (ack_level) begin
state_d = PingHsPhase2;
end else begin
alert_pd = 1'b1;
alert_nd = 1'b0;
end
end
// wait for deassertion of ack
PingHsPhase2: begin
if (!ack_level) begin
ping_clr = 1'b1;
state_d = Pause0;
end
end
// pause cycles between back-to-back handshakes
Pause0: begin
state_d = Pause1;
end
// clear and ack alert request if it was set
Pause1: begin
state_d = Idle;
end
// catch parasitic states
default : state_d = Idle;
endcase
// we have a signal integrity issue at one of the incoming diff pairs. this condition is
// signalled by setting the output diffpair to zero. If the sigint has disappeared, we clear
// the ping request state of this sender and go back to idle.
if (sigint_detected) begin
state_d = Idle;
alert_pd = 1'b0;
alert_nd = 1'b0;
ping_clr = 1'b1;
alert_clr = 1'b0;
end
end
// This prevents further tool optimizations of the differential signal.
prim_sec_anchor_flop #(
.Width (2),
.ResetValue(2'b10)
) u_prim_flop_alert (
.clk_i,
.rst_ni,
.d_i({alert_nd, alert_pd}),
.q_o({alert_nq, alert_pq})
);
always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg
if (!rst_ni) begin
state_q <= Idle;
alert_set_q <= 1'b0;
alert_test_set_q <= 1'b0;
ping_set_q <= 1'b0;
end else begin
state_q <= state_d;
alert_set_q <= alert_set_d;
alert_test_set_q <= alert_test_set_d;
ping_set_q <= ping_set_d;
end
end
////////////////
// assertions //
////////////////
// however, since we use sequence constructs below, we need to wrap the entire block again.
// typically, the ASSERT macros already contain this INC_ASSERT macro.
`ifdef INC_ASSERT
// check whether all outputs have a good known state after reset
`ASSERT_KNOWN(AlertPKnownO_A, alert_tx_o)
if (AsyncOn) begin : gen_async_assert
sequence PingSigInt_S;
alert_rx_i.ping_p == alert_rx_i.ping_n [*2];
endsequence
sequence AckSigInt_S;
alert_rx_i.ping_p == alert_rx_i.ping_n [*2];
endsequence
`ifndef FPV_ALERT_NO_SIGINT_ERR
// check propagation of sigint issues to output within three cycles, or four due to CDC
// shift sequence to the right to avoid reset effects.
`ASSERT(SigIntPing_A, ##1 PingSigInt_S |->
##[3:4] alert_tx_o.alert_p == alert_tx_o.alert_n)
`ASSERT(SigIntAck_A, ##1 AckSigInt_S |->
##[3:4] alert_tx_o.alert_p == alert_tx_o.alert_n)
`endif
// Test in-band FSM reset request (via signal integrity error)
`ASSERT(InBandInitFsm_A, PingSigInt_S or AckSigInt_S |-> ##[3:4] state_q == Idle)
`ASSERT(InBandInitPing_A, PingSigInt_S or AckSigInt_S |-> ##[3:4] !ping_set_q)
// output must be driven diff unless sigint issue detected
`ASSERT(DiffEncoding_A, (alert_rx_i.ack_p ^ alert_rx_i.ack_n) &&
(alert_rx_i.ping_p ^ alert_rx_i.ping_n) |->
##[3:5] alert_tx_o.alert_p ^ alert_tx_o.alert_n)
// handshakes can take indefinite time if blocked due to sigint on outgoing
// lines (which is not visible here). thus, we only check whether the
// handshake is correctly initiated and defer the full handshake checking to the testbench.
`ASSERT(PingHs_A, ##1 $changed(alert_rx_i.ping_p) &&
(alert_rx_i.ping_p ^ alert_rx_i.ping_n) ##2 state_q == Idle |=>
##[0:1] $rose(alert_tx_o.alert_p), clk_i,
!rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n))
end else begin : gen_sync_assert
sequence PingSigInt_S;
alert_rx_i.ping_p == alert_rx_i.ping_n;
endsequence
sequence AckSigInt_S;
alert_rx_i.ping_p == alert_rx_i.ping_n;
endsequence
`ifndef FPV_ALERT_NO_SIGINT_ERR
// check propagation of sigint issues to output within one cycle
`ASSERT(SigIntPing_A, PingSigInt_S |=>
alert_tx_o.alert_p == alert_tx_o.alert_n)
`ASSERT(SigIntAck_A, AckSigInt_S |=>
alert_tx_o.alert_p == alert_tx_o.alert_n)
`endif
// Test in-band FSM reset request (via signal integrity error)
`ASSERT(InBandInitFsm_A, PingSigInt_S or AckSigInt_S |=> state_q == Idle)
`ASSERT(InBandInitPing_A, PingSigInt_S or AckSigInt_S |=> !ping_set_q)
// output must be driven diff unless sigint issue detected
`ASSERT(DiffEncoding_A, (alert_rx_i.ack_p ^ alert_rx_i.ack_n) &&
(alert_rx_i.ping_p ^ alert_rx_i.ping_n) |=> alert_tx_o.alert_p ^ alert_tx_o.alert_n)
// handshakes can take indefinite time if blocked due to sigint on outgoing
// lines (which is not visible here). thus, we only check whether the handshake
// is correctly initiated and defer the full handshake checking to the testbench.
`ASSERT(PingHs_A, ##1 $changed(alert_rx_i.ping_p) && state_q == Idle |=>
$rose(alert_tx_o.alert_p), clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n))
end
// Test the alert state output.
`ASSERT(AlertState0_A, alert_set_q === alert_state_o)
if (IsFatal) begin : gen_fatal_assert
`ASSERT(AlertState1_A, alert_req_i |=> alert_state_o)
`ASSERT(AlertState2_A, alert_state_o |=> $stable(alert_state_o))
`ASSERT(AlertState3_A, alert_ack_o |=> alert_state_o)
end else begin : gen_recov_assert
`ASSERT(AlertState1_A, alert_req_i && !alert_clr |=> alert_state_o)
`ASSERT(AlertState2_A, alert_req_i && alert_ack_o |=> !alert_state_o)
end
// The alert test input should not set the alert state register.
`ASSERT(AlertTest1_A, alert_test_i && !alert_req_i && !alert_state_o |=> $stable(alert_state_o))
// if alert_req_i is true, handshakes should be continuously repeated
`ASSERT(AlertHs_A, alert_req_i && state_q == Idle |=> $rose(alert_tx_o.alert_p),
clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n))
// if alert_test_i is true, handshakes should be continuously repeated
`ASSERT(AlertTestHs_A, alert_test_i && state_q == Idle |=> $rose(alert_tx_o.alert_p),
clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n))
`endif
`ifdef FPV_ALERT_NO_SIGINT_ERR
// Assumptions for FPV security countermeasures to ensure the alert protocol functions collectly.
`ASSUME_FPV(AckPFollowsAlertP_S, alert_rx_i.ack_p == $past(alert_tx_o.alert_p))
`ASSUME_FPV(AckNFollowsAlertN_S, alert_rx_i.ack_n == $past(alert_tx_o.alert_n))
`ASSUME_FPV(TriggerAlertInit_S, $stable(rst_ni) == 0 |=> alert_rx_i.ping_p == alert_rx_i.ping_n)
`ASSUME_FPV(PingDiffPair_S, ##2 alert_rx_i.ping_p != alert_rx_i.ping_n)
`endif
endmodule : prim_alert_sender