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