hw/ip/prim/rtl/prim_esc_receiver.sv Cov: 100%

   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: // This module decodes escalation enable pulses that have been encoded using
   6: // the prim_esc_sender module.
   7: //
   8: // The module supports in-band ping testing of the escalation
   9: // wires. This is accomplished by the sender module that places a single-cycle,
  10: // differentially encoded pulse on esc_p/n which will be interpreted as a ping
  11: // request by the receiver module. The receiver module responds by sending back
  12: // the response pattern "1010".
  13: //
  14: // Native escalation enable pulses are differentiated from ping
  15: // requests by making sure that these pulses are always longer than 1 cycle.
  16: //
  17: // See also: prim_esc_sender, prim_diff_decode, alert_handler
  18: 
  19: module prim_esc_receiver import prim_pkg::*; (
  20:   input           clk_i,
  21:   input           rst_ni,
  22:   // escalation enable
  23:   output logic    esc_en_o,
  24:   // escalation / ping response
  25:   output esc_rx_t esc_rx_o,
  26:   // escalation output diff pair
  27:   input esc_tx_t  esc_tx_i
  28: );
  29: 
  30:   /////////////////////////////////
  31:   // decode differential signals //
  32:   /////////////////////////////////
  33: 
  34:   logic esc_level, sigint_detected;
  35: 
  36:   prim_diff_decode #(
  37:     .AsyncOn(1'b0)
  38:   ) i_decode_esc (
  39:     .clk_i,
  40:     .rst_ni,
  41:     .diff_pi  ( esc_tx_i.esc_p  ),
  42:     .diff_ni  ( esc_tx_i.esc_n  ),
  43:     .level_o  ( esc_level       ),
  44:     .rise_o   (                 ),
  45:     .fall_o   (                 ),
  46:     .event_o  (                 ),
  47:     .sigint_o ( sigint_detected )
  48:   );
  49: 
  50:   /////////////////
  51:   // RX/TX Logic //
  52:   /////////////////
  53: 
  54:   typedef enum logic [2:0] {Idle, Check, PingResp, EscResp, SigInt} state_e;
  55:   state_e state_d, state_q;
  56:   logic resp_pd, resp_pq, resp_nd, resp_nq;
  57: 
  58:   assign esc_rx_o.resp_p = resp_pq;
  59:   assign esc_rx_o.resp_n = resp_nq;
  60: 
  61: 
  62:   always_comb begin : p_fsm
  63:     // default
  64:     state_d  = state_q;
  65:     resp_pd  = 1'b0;
  66:     resp_nd  = 1'b1;
  67:     esc_en_o = 1'b0;
  68: 
  69:     unique case (state_q)
  70:       // wait for the esc_p/n diff pair
  71:       Idle: begin
  72:         if (esc_level) begin
  73:           state_d = Check;
  74:           resp_pd = 1'b1;
  75:           resp_nd = 1'b0;
  76:         end
  77:       end
  78:       // we decide here whether this is only a ping request or
  79:       // whether this is an escalation enable
  80:       Check: begin
  81:         state_d = PingResp;
  82:         if (esc_level) begin
  83:           state_d  = EscResp;
  84:           esc_en_o = 1'b1;
  85:         end
  86:       end
  87:       // finish ping response. in case esc_level is again asserted,
  88:       // we got an escalation signal (pings cannot occur back to back)
  89:       PingResp: begin
  90:         state_d = Idle;
  91:         resp_pd = 1'b1;
  92:         resp_nd = 1'b0;
  93:         if (esc_level) begin
  94:           state_d  = EscResp;
  95:           esc_en_o = 1'b1;
  96:         end
  97:       end
  98:       // we have got an escalation enable pulse,
  99:       // keep on toggling the outputs
 100:       EscResp: begin
 101:         state_d = Idle;
 102:         if (esc_level) begin
 103:           state_d  = EscResp;
 104:           resp_pd  = ~resp_pq;
 105:           resp_nd  = resp_pq;
 106:           esc_en_o = 1'b1;
 107:         end
 108:       end
 109:       // we have a signal integrity issue at one of
 110:       // the incoming diff pairs. this condition is
 111:       // signalled to the sender by setting the resp
 112:       // diffpair to the same value and continuously
 113:       // toggling them.
 114:       SigInt: begin
 115:         state_d = Idle;
 116:         if (sigint_detected) begin
 117:           state_d = SigInt;
 118:           resp_pd = ~resp_pq;
 119:           resp_nd = ~resp_pq;
 120:         end
 121:       end
 122:       default : state_d = Idle;
 123:     endcase
 124: 
 125:     // bail out if a signal integrity issue has been detected
 126:     if (sigint_detected && (state_q != SigInt)) begin
 127:       state_d  = SigInt;
 128:       resp_pd  = 1'b0;
 129:       resp_nd  = 1'b0;
 130:     end
 131:   end
 132: 
 133: 
 134:   ///////////////
 135:   // Registers //
 136:   ///////////////
 137: 
 138:   always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
 139:     if (!rst_ni) begin
 140:       state_q <= Idle;
 141:       resp_pq <= 1'b0;
 142:       resp_nq <= 1'b1;
 143:     end else begin
 144:       state_q <= state_d;
 145:       resp_pq <= resp_pd;
 146:       resp_nq <= resp_nd;
 147:     end
 148:   end
 149: 
 150:   ////////////////
 151:   // assertions //
 152:   ////////////////
 153: 
 154:   // check whether all outputs have a good known state after reset
 155:   `ASSERT_KNOWN(EscEnKnownO_A, esc_en_o, clk_i, !rst_ni)
 156:   `ASSERT_KNOWN(RespPKnownO_A, esc_rx_o, clk_i, !rst_ni)
 157: 
 158:   `ASSERT(SigIntCheck0_A, esc_tx_i.esc_p == esc_tx_i.esc_n |=>
 159:       esc_rx_o.resp_p == esc_rx_o.resp_n, clk_i, !rst_ni)
 160:   `ASSERT(SigIntCheck1_A, esc_tx_i.esc_p == esc_tx_i.esc_n |=> state_q == SigInt, clk_i, !rst_ni)
 161:   // correct diff encoding
 162:   `ASSERT(DiffEncCheck_A, esc_tx_i.esc_p ^ esc_tx_i.esc_n |=>
 163:       esc_rx_o.resp_p ^ esc_rx_o.resp_n, clk_i, !rst_ni)
 164:   // disable in case of ping integrity issue
 165:   `ASSERT(PingRespCheck_A, $rose(esc_tx_i.esc_p) |=> $fell(esc_tx_i.esc_p) |->
 166:       $rose(esc_rx_o.resp_p) |=> $fell(esc_rx_o.resp_p),
 167:       clk_i, !rst_ni || (esc_tx_i.esc_p == esc_tx_i.esc_n))
 168:   // escalation response needs to continuously toggle
 169:   `ASSERT(EscRespCheck_A, esc_tx_i.esc_p && $past(esc_tx_i.esc_p) &&
 170:       (esc_tx_i.esc_p ^ esc_tx_i.esc_n) && $past(esc_tx_i.esc_p ^ esc_tx_i.esc_n)
 171:       |=> esc_rx_o.resp_p != $past(esc_rx_o.resp_p), clk_i, !rst_ni)
 172:   // detect escalation pulse
 173:   `ASSERT(EscEnCheck_A, esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) && state_q != SigInt
 174:       |=> esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) |-> esc_en_o, clk_i, !rst_ni )
 175: 
 176: endmodule : prim_esc_receiver
 177: