hw/ip/prim/rtl/prim_alert_sender.sv Cov: 99%
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: // The alert sender primitive module differentially encodes and transmits an
6: // alert signal to the prim_alert_receiver module. An alert will be signalled
7: // by a full handshake on alert_p/n and ack_p/n. The alert_i signal may
8: // be continuously asserted, in which case the alert signalling handshake
9: // will be repeatedly initiated.
10: //
11: // Further, this module supports in-band ping testing, which means that a level
12: // change on the ping_p/n diff pair will result in a full-handshake response
13: // on alert_p/n and ack_p/n.
14: //
15: // The protocol works in both asynchronous and synchronous cases. In the
16: // asynchronous case, the parameter AsyncOn must be set to 1'b1 in order to
17: // instantiate additional synchronization logic. Further, it must be ensured
18: // that the timing skew between all diff pairs is smaller than the shortest
19: // clock period of the involved clocks.
20: //
21: // Incorrectly encoded diff inputs can be detected and will be signalled
22: // to the receiver by placing an inconsistent diff value on the differential
23: // output (and continuously toggling it).
24: //
25: // See also: prim_alert_receiver, prim_diff_decode, alert_handler
26:
27: module prim_alert_sender import prim_pkg::*; #(
28: // enables additional synchronization logic
29: parameter bit AsyncOn = 1'b1
30: ) (
31: input clk_i,
32: input rst_ni,
33: // native alert from the peripheral
34: input alert_i,
35: // ping input diff pair and ack diff pair
36: input alert_rx_t alert_rx_i,
37: // alert output diff pair
38: output alert_tx_t alert_tx_o
39: );
40:
41:
42: /////////////////////////////////
43: // decode differential signals //
44: /////////////////////////////////
45: logic ping_sigint, ping_event;
46:
47: prim_diff_decode #(
48: .AsyncOn(AsyncOn)
49: ) i_decode_ping (
50: .clk_i,
51: .rst_ni,
52: .diff_pi ( alert_rx_i.ping_p ),
53: .diff_ni ( alert_rx_i.ping_n ),
54: .level_o ( ),
55: .rise_o ( ),
56: .fall_o ( ),
57: .event_o ( ping_event ),
58: .sigint_o ( ping_sigint )
59: );
60:
61: logic ack_sigint, ack_level;
62:
63: prim_diff_decode #(
64: .AsyncOn(AsyncOn)
65: ) i_decode_ack (
66: .clk_i,
67: .rst_ni,
68: .diff_pi ( alert_rx_i.ack_p ),
69: .diff_ni ( alert_rx_i.ack_n ),
70: .level_o ( ack_level ),
71: .rise_o ( ),
72: .fall_o ( ),
73: .event_o ( ),
74: .sigint_o ( ack_sigint )
75: );
76:
77:
78: ///////////////////////////////////////////////////
79: // main protocol FSM that drives the diff output //
80: ///////////////////////////////////////////////////
81: typedef enum logic [2:0] {Idle, HsPhase1, HsPhase2, SigInt, Pause0, Pause1} state_e;
82: state_e state_d, state_q;
83: logic alert_pq, alert_nq, alert_pd, alert_nd;
84: logic sigint_detected;
85:
86: assign sigint_detected = ack_sigint | ping_sigint;
87:
88: // diff pair output
89: assign alert_tx_o.alert_p = alert_pq;
90: assign alert_tx_o.alert_n = alert_nq;
91:
92: // alert and ping set regs
93: logic alert_set_d, alert_set_q, alert_clr;
94: logic ping_set_d, ping_set_q, ping_clr;
95: assign alert_set_d = (alert_clr) ? 1'b0 : (alert_set_q | alert_i);
96: assign ping_set_d = (ping_clr) ? 1'b0 : (ping_set_q | ping_event);
97:
98: // this FSM performs a full four phase handshake upon a ping or alert trigger.
99: // note that the latency of the alert_p/n diff pair is at least one cycle
100: // until it enters the receiver FSM. the same holds for the ack_* diff pair
101: // input. in case a signal integrity issue is detected, the FSM bails out,
102: // sets the alert_p/n diff pair to the same value and toggles it in order to
103: // signal that condition over to the receiver.
104: always_comb begin : p_fsm
105: // default
106: state_d = state_q;
107: alert_pd = 1'b0;
108: alert_nd = 1'b1;
109: ping_clr = 1'b0;
110: alert_clr = 1'b0;
111:
112: unique case (state_q)
113: Idle: begin
114: // alert always takes precedence
115: if (alert_i || alert_set_q || ping_event || ping_set_q) begin
116: state_d = HsPhase1;
117: alert_pd = 1'b1;
118: alert_nd = 1'b0;
119: if (ping_event || ping_set_q) begin
120: ping_clr = 1'b1;
121: end else begin
122: alert_clr = 1'b1;
123: end
124: end
125: end
126: // waiting for ack from receiver
127: HsPhase1: begin
128: if (ack_level) begin
129: state_d = HsPhase2;
130: end else begin
131: alert_pd = 1'b1;
132: alert_nd = 1'b0;
133: end
134: end
135: // wait for deassertion of ack
136: HsPhase2: begin
137: if (!ack_level) begin
138: state_d = Pause0;
139: end
140: end
141: // pause cycles between back-to-back handshakes
142: Pause0: state_d = Pause1;
143: Pause1: state_d = Idle;
144: // we have a signal integrity issue at one of
145: // the incoming diff pairs. this condition is
146: // signalled by setting the output diffpair
147: // to the same value and continuously toggling
148: // them.
149: SigInt: begin
150: state_d = Idle;
151: if (sigint_detected) begin
152: state_d = SigInt;
153: alert_pd = ~alert_pq;
154: alert_nd = ~alert_pq;
155: end
156: end
157: // catch parasitic states
158: default : state_d = Idle;
159: endcase
160: // bail out if a signal integrity issue has been detected
161: if (sigint_detected && (state_q != SigInt)) begin
162: state_d = SigInt;
163: alert_pd = 1'b0;
164: alert_nd = 1'b0;
165: ping_clr = 1'b0;
166: alert_clr = 1'b0;
167: end
168: end
169:
170: always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg
171: if (!rst_ni) begin
172: state_q <= Idle;
173: alert_pq <= 1'b0;
174: alert_nq <= 1'b1;
175: alert_set_q <= 1'b0;
176: ping_set_q <= 1'b0;
177: end else begin
178: state_q <= state_d;
179: alert_pq <= alert_pd;
180: alert_nq <= alert_nd;
181: alert_set_q <= alert_set_d;
182: ping_set_q <= ping_set_d;
183: end
184: end
185:
186:
187: ////////////////
188: // assertions //
189: ////////////////
190:
191: // check whether all outputs have a good known state after reset
192: `ASSERT_KNOWN(AlertPKnownO_A, alert_tx_o, clk_i, !rst_ni)
193:
194: if (AsyncOn) begin : gen_async_assert
195: // check propagation of sigint issues to output within three cycles
196: `ASSERT(SigIntPing_A, alert_rx_i.ping_p == alert_rx_i.ping_n [*2] |->
197: ##3 alert_tx_o.alert_p == alert_tx_o.alert_n, clk_i, !rst_ni)
198: `ASSERT(SigIntAck_A, alert_rx_i.ack_p == alert_rx_i.ack_n [*2] |->
199: ##3 alert_tx_o.alert_p == alert_tx_o.alert_n, clk_i, !rst_ni)
200: // output must be driven diff unless sigint issue detected
201: `ASSERT(DiffEncoding_A, (alert_rx_i.ack_p ^ alert_rx_i.ack_n) &&
202: (alert_rx_i.ping_p ^ alert_rx_i.ping_n) |->
203: ##3 alert_tx_o.alert_p ^ alert_tx_o.alert_n, clk_i, !rst_ni)
204:
205: // handshakes can take indefinite time if blocked due to sigint on outgoing
206: // lines (which is not visible here). thus, we only check whether the
207: // handshake is correctly initiated and defer the full handshake checking to the testbench.
208: // TODO: add the staggered cases as well
209: `ASSERT(PingHs_A, ##1 $changed(alert_rx_i.ping_p) &&
210: (alert_rx_i.ping_p ^ alert_rx_i.ping_n) ##2 state_q == Idle |=>
211: $rose(alert_tx_o.alert_p), clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n))
212: end else begin : gen_sync_assert
213: // check propagation of sigint issues to output within one cycle
214: `ASSERT(SigIntPing_A, alert_rx_i.ping_p == alert_rx_i.ping_n |=>
215: alert_tx_o.alert_p == alert_tx_o.alert_n, clk_i, !rst_ni)
216: `ASSERT(SigIntAck_A, alert_rx_i.ack_p == alert_rx_i.ack_n |=>
217: alert_tx_o.alert_p == alert_tx_o.alert_n, clk_i, !rst_ni)
218: // output must be driven diff unless sigint issue detected
219: `ASSERT(DiffEncoding_A, (alert_rx_i.ack_p ^ alert_rx_i.ack_n) &&
220: (alert_rx_i.ping_p ^ alert_rx_i.ping_n) |=> alert_tx_o.alert_p ^ alert_tx_o.alert_n,
221: clk_i, !rst_ni)
222: // handshakes can take indefinite time if blocked due to sigint on outgoing
223: // lines (which is not visible here). thus, we only check whether the handshake
224: // is correctly initiated and defer the full handshake checking to the testbench.
225: `ASSERT(PingHs_A, ##1 $changed(alert_rx_i.ping_p) && state_q == Idle |=>
226: $rose(alert_tx_o.alert_p), clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n))
227: end
228:
229: // if alert_i is true, handshakes should be continuously repeated
230: `ASSERT(AlertHs_A, alert_i && state_q == Idle |=> $rose(alert_tx_o.alert_p),
231: clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n))
232:
233: endmodule : prim_alert_sender
234: