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