../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: