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