hw/ip/prim/rtl/prim_diff_decode.sv Cov: 31.4%

   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 a differentially encoded signal and detects
   6: // incorrectly encoded differential states.
   7: //
   8: // In case the differential pair crosses an asynchronous boundary, it has
   9: // to be re-synchronized to the local clock. This can be achieved by
  10: // setting the AsyncOn parameter to 1'b1. In that case, two additional
  11: // input registers are added (to counteract metastability), and
  12: // a pattern detector is instantiated that detects skewed level changes on
  13: // the differential pair (i.e., when level changes on the diff pair are
  14: // sampled one cycle apart due to a timing skew between the two wires).
  15: //
  16: // See also: prim_alert_sender, prim_alert_receiver, alert_handler
  17: 
  18: module prim_diff_decode #(
  19:   // enables additional synchronization logic
  20:   parameter bit AsyncOn = 1'b0
  21: ) (
  22:   input        clk_i,
  23:   input        rst_ni,
  24:   // input diff pair
  25:   input        diff_pi,
  26:   input        diff_ni,
  27:   // logical level and
  28:   // detected edges
  29:   output logic level_o,
  30:   output logic rise_o,
  31:   output logic fall_o,
  32:   // either rise or fall
  33:   output logic event_o,
  34:   //signal integrity issue detected
  35:   output logic sigint_o
  36: );
  37: 
  38:   logic level_d, level_q;
  39: 
  40:   ///////////////////////////////////////////////////////////////
  41:   // synchronization regs for incoming diff pair (if required) //
  42:   ///////////////////////////////////////////////////////////////
  43:   if (AsyncOn) begin : gen_async
  44: 
  45:     typedef enum logic [1:0] {IsStd, IsSkewed, SigInt} state_e;
  46:     state_e state_d, state_q;
  47:     logic diff_p_edge, diff_n_edge, diff_check_ok, level;
  48: 
  49:     // 2 sync regs, one reg for edge detection
  50:     logic diff_pq, diff_nq, diff_pd, diff_nd;
  51: 
  52:     prim_flop_2sync #(
  53:       .Width(1),
  54:       .ResetValue(0)
  55:     ) i_sync_p (
  56:       .clk_i,
  57:       .rst_ni,
  58:       .d(diff_pi),
  59:       .q(diff_pd)
  60:     );
  61: 
  62:     prim_flop_2sync #(
  63:       .Width(1),
  64:       .ResetValue(1)
  65:     ) i_sync_n (
  66:       .clk_i,
  67:       .rst_ni,
  68:       .d(diff_ni),
  69:       .q(diff_nd)
  70:     );
  71: 
  72:     // detect level transitions
  73:     assign diff_p_edge   = diff_pq ^ diff_pd;
  74:     assign diff_n_edge   = diff_nq ^ diff_nd;
  75: 
  76:     // detect sigint issue
  77:     assign diff_check_ok = diff_pd ^ diff_nd;
  78: 
  79:     // this is the current logical level
  80:     assign level         = diff_pd;
  81: 
  82:     // outputs
  83:     assign level_o  = level_d;
  84:     assign event_o = rise_o | fall_o;
  85: 
  86:     // sigint detection is a bit more involved in async case since
  87:     // we might have skew on the diff pair, which can result in a
  88:     // one cycle sampling delay between the two wires
  89:     // so we need a simple pattern matcher
  90:     // the following waves are legal
  91:     // clk    |   |   |   |   |   |   |   |
  92:     //           _______     _______
  93:     // p _______/        ...        \________
  94:     //   _______                     ________
  95:     // n        \_______ ... _______/
  96:     //              ____     ___
  97:     // p __________/     ...    \________
  98:     //   _______                     ________
  99:     // n        \_______ ... _______/
 100:     //
 101:     // i.e., level changes may be off by one cycle - which is permissible
 102:     // as long as this condition is only one cycle long.
 103: 
 104: 
 105:     always_comb begin : p_diff_fsm
 106:       // default
 107:       state_d  = state_q;
 108:       level_d  = level_q;
 109:       rise_o   = 1'b0;
 110:       fall_o   = 1'b0;
 111:       sigint_o = 1'b0;
 112: 
 113:       unique case (state_q)
 114:         // we remain here as long as
 115:         // the diff pair is correctly encoded
 116:         IsStd: begin
 117:           if (diff_check_ok) begin
 118:             level_d = level;
 119:             if (diff_p_edge && diff_n_edge) begin
 120:               if (level) begin
 121:                 rise_o = 1'b1;
 122:               end else begin
 123:                 fall_o = 1'b1;
 124:               end
 125:             end
 126:           end else begin
 127:             if (diff_p_edge || diff_n_edge) begin
 128:               state_d = IsSkewed;
 129:             end else begin
 130:               state_d = SigInt;
 131:               sigint_o = 1'b1;
 132:             end
 133:           end
 134:         end
 135:         // diff pair must be correctly encoded, otherwise we got a sigint
 136:         IsSkewed: begin
 137:           if (diff_check_ok) begin
 138:             state_d = IsStd;
 139:             level_d = level;
 140:             if (level) rise_o = 1'b1;
 141:             else       fall_o = 1'b1;
 142:           end else begin
 143:             state_d  = SigInt;
 144:             sigint_o = 1'b1;
 145:           end
 146:         end
 147:         // Signal integrity issue detected, remain here
 148:         // until resolved
 149:         SigInt: begin
 150:           sigint_o = 1'b1;
 151:           if (diff_check_ok) begin
 152:             state_d  = IsStd;
 153:             sigint_o = 1'b0;
 154:           end
 155:         end
 156:         default : ;
 157:       endcase
 158:     end
 159: 
 160:     always_ff @(posedge clk_i or negedge rst_ni) begin : p_sync_reg
 161:       if (!rst_ni) begin
 162:         state_q  <= IsStd;
 163:         diff_pq  <= 1'b0;
 164:         diff_nq  <= 1'b1;
 165:         level_q  <= 1'b0;
 166:       end else begin
 167:         state_q  <= state_d;
 168:         diff_pq  <= diff_pd;
 169:         diff_nq  <= diff_nd;
 170:         level_q  <= level_d;
 171:       end
 172:     end
 173: 
 174:   //////////////////////////////////////////////////////////
 175:   // fully synchronous case, no skew present in this case //
 176:   //////////////////////////////////////////////////////////
 177:   end else begin : gen_no_async
 178:     logic diff_pq, diff_pd;
 179: 
 180:     // one reg for edge detection
 181:     assign diff_pd = diff_pi;
 182: 
 183:     // incorrect encoding -> signal integrity issue
 184:     assign sigint_o = ~(diff_pi ^ diff_ni);
 185: 
 186:     assign level_o = (sigint_o) ? level_q : diff_pi;
 187:     assign level_d = level_o;
 188: 
 189:     // detect level transitions
 190:     assign rise_o  = (~diff_pq &  diff_pi) & ~sigint_o;
 191:     assign fall_o  = ( diff_pq & ~diff_pi) & ~sigint_o;
 192:     assign event_o = rise_o | fall_o;
 193: 
 194:     always_ff @(posedge clk_i or negedge rst_ni) begin : p_edge_reg
 195:       if (!rst_ni) begin
 196:         diff_pq  <= 1'b0;
 197:         level_q  <= 1'b0;
 198:       end else begin
 199:         diff_pq  <= diff_pd;
 200:         level_q  <= level_d;
 201:       end
 202:     end
 203:   end
 204: 
 205:   ////////////////
 206:   // assertions //
 207:   ////////////////
 208: 
 209:   // shared assertions
 210:   // sigint -> level stays the same during sigint
 211:   // $isunknown is needed to avoid false assertion in first clock cycle
 212:   `ASSERT(SigintLevelCheck_A, ##1 sigint_o |-> $stable(level_o), clk_i, !rst_ni)
 213:   // sigint -> no additional events asserted at output
 214:   `ASSERT(SigintEventCheck_A, sigint_o |-> !event_o, clk_i, !rst_ni)
 215:   `ASSERT(SigintRiseCheck_A,  sigint_o |-> !rise_o, clk_i, !rst_ni)
 216:   `ASSERT(SigintFallCheck_A,  sigint_o |-> !fall_o, clk_i, !rst_ni)
 217: 
 218:   if (AsyncOn) begin : gen_async_assert
 219:     // assertions for asynchronous case
 220:     // correctly detect sigint issue (only one transition cycle of permissible due to skew)
 221:     `ASSERT(SigintCheck0_A, diff_pi == diff_ni [*2] |-> ##[1:2] sigint_o, clk_i, !rst_ni)
 222:     // the synchronizer adds 2 cycles of latency
 223:     `ASSERT(SigintCheck1_A, ##1 (diff_pi ^ diff_ni) && $stable(diff_pi) && $stable(diff_ni) ##1
 224:         $rose(diff_pi) && $stable(diff_ni) ##1 $stable(diff_pi) && $fell(diff_ni) |->
 225:         ##2 rise_o, clk_i, !rst_ni)
 226:     `ASSERT(SigintCheck2_A, ##1 (diff_pi ^ diff_ni) && $stable(diff_pi) && $stable(diff_ni) ##1
 227:         $fell(diff_pi) && $stable(diff_ni) ##1 $stable(diff_pi) && $rose(diff_ni) |->
 228:         ##2 fall_o, clk_i, !rst_ni)
 229:     `ASSERT(SigintCheck3_A, ##1 (diff_pi ^ diff_ni) && $stable(diff_pi) && $stable(diff_ni) ##1
 230:         $rose(diff_ni) && $stable(diff_pi) ##1 $stable(diff_ni) && $fell(diff_pi) |->
 231:         ##2 fall_o, clk_i, !rst_ni)
 232:     `ASSERT(SigintCheck4_A, ##1 (diff_pi ^ diff_ni) && $stable(diff_pi) && $stable(diff_ni) ##1
 233:         $fell(diff_ni) && $stable(diff_pi) ##1 $stable(diff_ni) && $rose(diff_pi) |->
 234:         ##2 rise_o, clk_i, !rst_ni)
 235:     // correctly detect edges
 236:     `ASSERT(RiseCheck_A,  ##1 $rose(diff_pi)     && (diff_pi ^ diff_ni) |->
 237:         ##[2:3] rise_o,  clk_i, !rst_ni || sigint_o)
 238:     `ASSERT(FallCheck_A,  ##1 $fell(diff_pi)     && (diff_pi ^ diff_ni) |->
 239:         ##[2:3] fall_o,  clk_i, !rst_ni || sigint_o)
 240:     `ASSERT(EventCheck_A, ##1 $changed(diff_pi)  && (diff_pi ^ diff_ni) |->
 241:         ##[2:3] event_o, clk_i, !rst_ni || sigint_o)
 242:     // correctly detect level
 243:     `ASSERT(LevelCheck0_A, !sigint_o && (diff_pi ^ diff_ni) [*3] |=> $past(diff_pi, 2) == level_o,
 244:         clk_i, !rst_ni || sigint_o)
 245: 
 246:   end else begin : gen_sync_assert
 247:     // assertions for synchronous case
 248:     // correctly detect sigint issue
 249:     `ASSERT(SigintCheck_A, diff_pi == diff_ni |-> sigint_o, clk_i, !rst_ni)
 250:     // correctly detect edges
 251:     `ASSERT(RiseCheck_A,  ##1 $rose(diff_pi)    && (diff_pi ^ diff_ni) |->  rise_o, clk_i, !rst_ni)
 252:     `ASSERT(FallCheck_A,  ##1 $fell(diff_pi)    && (diff_pi ^ diff_ni) |->  fall_o, clk_i, !rst_ni)
 253:     `ASSERT(EventCheck_A, ##1 $changed(diff_pi) && (diff_pi ^ diff_ni) |-> event_o, clk_i, !rst_ni)
 254:     // correctly detect level
 255:     `ASSERT(LevelCheck_A, (diff_pi ^ diff_ni) |-> diff_pi == level_o, clk_i, !rst_ni)
 256:   end
 257: 
 258: endmodule : prim_diff_decode
 259: