../src/lowrisc_prim_all_0.1/rtl/prim_sync_reqack.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: // REQ/ACK synchronizer
   6: //
   7: // This module synchronizes a REQ/ACK handshake across a clock domain crossing.
   8: // Both domains will see a handshake with the duration of one clock cycle.
   9: //
  10: // Notes:
  11: // - Once asserted, the source domain is not allowed to de-assert REQ without ACK.
  12: // - The destination domain is not allowed to send an ACK without a REQ.
  13: // - This module works both when syncing from a faster to a slower clock domain and vice versa.
  14: // - Internally, this module uses a return-to-zero, four-phase handshake protocol. Assuming the
  15: //   destination side responds with an ACK immediately, the latency from asserting the REQ on the
  16: //   source side is:
  17: //   - 1 source + 2 destination clock cycles until the handshake is performed on the
  18: //     destination side,
  19: //   - 1 source + 2 destination + 1 destination + 2 source clock cycles until the handshake is
  20: //     performed on the source side.
  21: //   - It takes another round trip (3 source + 3 destination clock cycles) before the next
  22: //     REQ is starting to be propagated to the destination side. The module is thus not suitable
  23: //     for high-bandwidth communication.
  24: 
  25: `include "prim_assert.sv"
  26: 
  27: module prim_sync_reqack (
  28:   input  clk_src_i,       // REQ side, SRC domain
  29:   input  rst_src_ni,      // REQ side, SRC domain
  30:   input  clk_dst_i,       // ACK side, DST domain
  31:   input  rst_dst_ni,      // ACK side, DST domain
  32: 
  33:   input  logic src_req_i, // REQ side, SRC domain
  34:   output logic src_ack_o, // REQ side, SRC domain
  35:   output logic dst_req_o, // ACK side, DST domain
  36:   input  logic dst_ack_i  // ACK side, DST domain
  37: );
  38: 
  39:   // Types
  40:   typedef enum logic {
  41:     HANDSHAKE, SYNC
  42:   } sync_reqack_fsm_e;
  43: 
  44:   // Signals
  45:   sync_reqack_fsm_e src_fsm_ns, src_fsm_cs;
  46:   sync_reqack_fsm_e dst_fsm_ns, dst_fsm_cs;
  47:   logic src_req_d, src_req_q, src_ack;
  48:   logic dst_ack_d, dst_ack_q, dst_req;
  49: 
  50:   // Move REQ over to ACK side.
  51:   prim_flop_2sync #(
  52:     .Width(1)
  53:   ) req_sync (
  54:     .clk_i  (clk_dst_i),
  55:     .rst_ni (rst_dst_ni),
  56:     .d      (src_req_q),
  57:     .q      (dst_req)
  58:   );
  59: 
  60:   // Move ACK over to REQ side.
  61:   prim_flop_2sync #(
  62:     .Width(1)
  63:   ) ack_sync (
  64:     .clk_i  (clk_src_i),
  65:     .rst_ni (rst_src_ni),
  66:     .d      (dst_ack_q),
  67:     .q      (src_ack)
  68:   );
  69: 
  70:   // REQ-side FSM (source domain)
  71:   always_comb begin : src_fsm
  72:     src_fsm_ns = src_fsm_cs;
  73: 
  74:     // By default, we forward the REQ and ACK.
  75:     src_req_d = src_req_i;
  76:     src_ack_o = src_ack;
  77: 
  78:     unique case (src_fsm_cs)
  79: 
  80:       HANDSHAKE: begin
  81:         // The handshake on the REQ side is done for exactly 1 clock cycle.
  82:         if (src_req_i && src_ack) begin
  83:           src_fsm_ns = SYNC;
  84:           // Tell ACK side that we are done.
  85:           src_req_d  = 1'b0;
  86:         end
  87:       end
  88: 
  89:       SYNC: begin
  90:         // Make sure ACK side knows that we are done.
  91:         src_req_d = 1'b0;
  92:         src_ack_o = 1'b0;
  93:         if (!src_ack) begin
  94:           src_fsm_ns = HANDSHAKE;
  95:         end
  96:       end
  97: 
  98:       default: ;
  99:     endcase
 100:   end
 101: 
 102:   // ACK-side FSM (destination domain)
 103:   always_comb begin : dst_fsm
 104:     dst_fsm_ns = dst_fsm_cs;
 105: 
 106:     // By default, we forward the REQ and ACK.
 107:     dst_req_o = dst_req;
 108:     dst_ack_d = dst_ack_i;
 109: 
 110:     unique case (dst_fsm_cs)
 111: 
 112:       HANDSHAKE: begin
 113:         // The handshake on the ACK side is done for exactly 1 clock cycle.
 114:         if (dst_req && dst_ack_i) begin
 115:           dst_fsm_ns = SYNC;
 116:         end
 117:       end
 118: 
 119:       SYNC: begin
 120:         // Don't forward REQ, hold ACK, wait for REQ side.
 121:         dst_req_o  = 1'b0;
 122:         dst_ack_d  = 1'b1;
 123:         if (!dst_req) begin
 124:           dst_fsm_ns = HANDSHAKE;
 125:         end
 126:       end
 127: 
 128:       default: ;
 129:     endcase
 130:   end
 131: 
 132:   // Registers
 133:   always_ff @(posedge clk_src_i or negedge rst_src_ni) begin
 134:     if (!rst_src_ni) begin
 135:       src_fsm_cs <= HANDSHAKE;
 136:       src_req_q  <= 1'b0;
 137:     end else begin
 138:       src_fsm_cs <= src_fsm_ns;
 139:       src_req_q  <= src_req_d;
 140:     end
 141:   end
 142:   always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin
 143:     if (!rst_dst_ni) begin
 144:       dst_fsm_cs <= HANDSHAKE;
 145:       dst_ack_q  <= 1'b0;
 146:     end else begin
 147:       dst_fsm_cs <= dst_fsm_ns;
 148:       dst_ack_q  <= dst_ack_d;
 149:     end
 150:   end
 151: 
 152:   // Source domain cannot de-assert REQ while waiting for ACK.
 153:   `ASSERT(ReqAckSyncHoldReq, $fell(src_req_i) |-> (src_fsm_cs != HANDSHAKE), clk_src_i, rst_src_ni)
 154: 
 155:   // Destination domain cannot assert ACK without REQ.
 156:   `ASSERT(ReqAckSyncAckNeedsReq, dst_ack_i |-> dst_req_o, clk_dst_i, rst_dst_ni)
 157: 
 158: endmodule
 159: