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: