../src/lowrisc_prim_all_0.1/rtl/prim_alert_receiver.sv Cov: 97.6%
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 receiver primitive decodes alerts that have been differentially
6: // encoded and transmitted via a handshake protocol on alert_p/n and
7: // ack_p/n. In case an alert handshake is initiated, the output alert_o will
8: // immediately be asserted (even before completion of the handshake).
9: //
10: // In case the differential input is not correctly encoded, this module will
11: // raise an error by asserting integ_fail_o.
12: //
13: // Further, the module supports ping testing of the alert diff pair. In order to
14: // initiate a ping test, ping_en_i shall be set to 1'b1 until ping_ok_o is
15: // asserted for one cycle. The signal may be de-asserted (e.g. after a long)
16: // timeout period. However note that all ping responses that come in after
17: // deasserting ping_en_i will be treated as native alerts.
18: //
19: // The protocol works in both asynchronous and synchronous cases. In the
20: // asynchronous case, the parameter AsyncOn must be set to 1'b1 in order to
21: // instantiate additional synchronization logic. Further, it must be ensured
22: // that the timing skew between all diff pairs is smaller than the shortest
23: // clock period of the involved clocks.
24: //
25: // Note that in case of synchronous operation, alerts on the diffpair are
26: // decoded combinationally and forwarded on alert_o within the same cycle.
27: //
28: // See also: prim_alert_sender, prim_diff_decode, alert_handler
29:
30: `include "prim_assert.sv"
31:
32: module prim_alert_receiver
33: import prim_alert_pkg::*;
34: #(
35: // enables additional synchronization logic
36: parameter bit AsyncOn = 1'b0
37: ) (
38: input clk_i,
39: input rst_ni,
40: // this triggers a ping test. keep asserted
41: // until ping_ok_o is asserted.
42: input ping_en_i,
43: output logic ping_ok_o,
44: // asserted if signal integrity issue detected
45: output logic integ_fail_o,
46: // alert output (pulsed high) if a handshake is initiated
47: // on alert_p/n and no ping request is outstanding
48: output logic alert_o,
49: // ping input diff pair and ack diff pair
50: output alert_rx_t alert_rx_o,
51: // alert output diff pair
52: input alert_tx_t alert_tx_i
53: );
54:
55:
56: /////////////////////////////////
57: // decode differential signals //
58: /////////////////////////////////
59: logic alert_level, alert_sigint;
60:
61: prim_diff_decode #(
62: .AsyncOn(AsyncOn)
63: ) i_decode_alert (
64: .clk_i,
65: .rst_ni,
66: .diff_pi ( alert_tx_i.alert_p ),
67: .diff_ni ( alert_tx_i.alert_n ),
68: .level_o ( alert_level ),
69: .rise_o ( ),
70: .fall_o ( ),
71: .event_o ( ),
72: .sigint_o ( alert_sigint )
73: );
74:
75: /////////////////////////////////////////////////////
76: // main protocol FSM that drives the diff outputs //
77: /////////////////////////////////////////////////////
78: typedef enum logic [1:0] {Idle, HsAckWait, Pause0, Pause1} state_e;
79: state_e state_d, state_q;
80: logic ping_rise;
81: logic ping_tog_d, ping_tog_q, ack_d, ack_q;
82: logic ping_en_d, ping_en_q;
83: logic ping_pending_d, ping_pending_q;
84:
85: // signal ping request upon positive transition on ping_en_i
86: // signalling is performed by a level change event on the diff output
87: assign ping_en_d = ping_en_i;
88: assign ping_rise = ping_en_i && !ping_en_q;
89: assign ping_tog_d = (ping_rise) ? ~ping_tog_q : ping_tog_q;
90:
91: // the ping pending signal is used to in the FSM to distinguish whether the
92: // incoming handshake shall be treated as an alert or a ping response.
93: // it is important that this is only set on a rising ping_en level change, since
94: // otherwise the ping enable signal could be abused to "mask" all native alerts
95: // as ping responses by constantly tying it to 1.
96: assign ping_pending_d = ping_rise | ((~ping_ok_o) & ping_en_i & ping_pending_q);
97:
98: // diff pair outputs
99: assign alert_rx_o.ack_p = ack_q;
100: assign alert_rx_o.ack_n = ~ack_q;
101: assign alert_rx_o.ping_p = ping_tog_q;
102: assign alert_rx_o.ping_n = ~ping_tog_q;
103:
104: // this FSM receives the four phase handshakes from the alert receiver
105: // note that the latency of the alert_p/n input diff pair is at least one
106: // cycle until it enters the receiver FSM. the same holds for the ack_* diff
107: // pair outputs.
108: always_comb begin : p_fsm
109: // default
110: state_d = state_q;
111: ack_d = 1'b0;
112: ping_ok_o = 1'b0;
113: integ_fail_o = 1'b0;
114: alert_o = 1'b0;
115:
116: unique case (state_q)
117: Idle: begin
118: // wait for handshake to be initiated
119: if (alert_level) begin
120: state_d = HsAckWait;
121: ack_d = 1'b1;
122: // signal either an alert or ping received on the output
123: if (ping_pending_q) begin
124: ping_ok_o = 1'b1;
125: end else begin
126: alert_o = 1'b1;
127: end
128: end
129: end
130: // waiting for deassertion of alert to complete HS
131: HsAckWait: begin
132: if (!alert_level) begin
133: state_d = Pause0;
134: end else begin
135: ack_d = 1'b1;
136: end
137: end
138: // pause cycles between back-to-back handshakes
139: Pause0: state_d = Pause1;
140: Pause1: state_d = Idle;
141: default : ; // full case
142: endcase
143:
144: // override in case of sigint
145: if (alert_sigint) begin
146: state_d = Idle;
147: ack_d = 1'b0;
148: ping_ok_o = 1'b0;
149: integ_fail_o = 1'b1;
150: alert_o = 1'b0;
151: end
152: end
153:
154: always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg
155: if (!rst_ni) begin
156: state_q <= Idle;
157: ack_q <= 1'b0;
158: ping_tog_q <= 1'b0;
159: ping_en_q <= 1'b0;
160: ping_pending_q <= 1'b0;
161: end else begin
162: state_q <= state_d;
163: ack_q <= ack_d;
164: ping_tog_q <= ping_tog_d;
165: ping_en_q <= ping_en_d;
166: ping_pending_q <= ping_pending_d;
167: end
168: end
169:
170:
171: ////////////////
172: // assertions //
173: ////////////////
174:
175: // check whether all outputs have a good known state after reset
176: `ASSERT_KNOWN(PingOkKnownO_A, ping_ok_o)
177: `ASSERT_KNOWN(IntegFailKnownO_A, integ_fail_o)
178: `ASSERT_KNOWN(AlertKnownO_A, alert_o)
179: `ASSERT_KNOWN(PingPKnownO_A, alert_rx_o)
180:
181: // check encoding of outgoing diffpairs
182: `ASSERT(PingDiffOk_A, alert_rx_o.ping_p ^ alert_rx_o.ping_n)
183: `ASSERT(AckDiffOk_A, alert_rx_o.ack_p ^ alert_rx_o.ack_n)
184: // ping request at input -> need to see encoded ping request
185: `ASSERT(PingRequest0_A, ##1 $rose(ping_en_i) |=> $changed(alert_rx_o.ping_p))
186: // ping response implies it has been requested
187: `ASSERT(PingResponse0_A, ping_ok_o |-> ping_pending_q)
188: // correctly latch ping request
189: `ASSERT(PingPending_A, ##1 $rose(ping_en_i) |=> ping_pending_q)
190:
191: if (AsyncOn) begin : gen_async_assert
192: // signal integrity check propagation
193: `ASSERT(SigInt_A, alert_tx_i.alert_p == alert_tx_i.alert_n [*2] |->
194: ##2 integ_fail_o)
195: // TODO: need to add skewed cases as well, the assertions below assume no skew at the moment
196: // ping response
197: `ASSERT(PingResponse1_A, ##1 $rose(alert_tx_i.alert_p) &&
198: (alert_tx_i.alert_p ^ alert_tx_i.alert_n) ##2 state_q == Idle && ping_pending_q |->
199: ping_ok_o, clk_i, !rst_ni || integ_fail_o)
200: // alert
201: `ASSERT(Alert_A, ##1 $rose(alert_tx_i.alert_p) && (alert_tx_i.alert_p ^ alert_tx_i.alert_n) ##2
202: state_q == Idle && !ping_pending_q |-> alert_o, clk_i, !rst_ni || integ_fail_o)
203: end else begin : gen_sync_assert
204: // signal integrity check propagation
205: `ASSERT(SigInt_A, alert_tx_i.alert_p == alert_tx_i.alert_n |-> integ_fail_o)
206: // ping response
207: `ASSERT(PingResponse1_A, ##1 $rose(alert_tx_i.alert_p) && state_q == Idle && ping_pending_q |->
208: ping_ok_o, clk_i, !rst_ni || integ_fail_o)
209: // alert
210: `ASSERT(Alert_A, ##1 $rose(alert_tx_i.alert_p) && state_q == Idle && !ping_pending_q |->
211: alert_o, clk_i, !rst_ni || integ_fail_o)
212: end
213:
214: endmodule : prim_alert_receiver
215: