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