hw/ip/prim/rtl/prim_alert_receiver.sv Cov: 84%

   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: