`timescale 1ns / 1ps // ============================================================================ // CDC FOR MULTI-BIT DATA (ADVANCED) // Uses Gray-code encoding with synchronous reset on sync chain to avoid // latch inference. ASYNC_REG attributes ensure Vivado places synchronizer // FFs in the same slice for optimal MTBF. // ============================================================================ module cdc_adc_to_processing #( parameter WIDTH = 8, parameter STAGES = 3 )( input wire src_clk, input wire dst_clk, input wire reset_n, input wire [WIDTH-1:0] src_data, input wire src_valid, output wire [WIDTH-1:0] dst_data, output wire dst_valid ); // Gray encoding for safe CDC function [WIDTH-1:0] binary_to_gray; input [WIDTH-1:0] binary; binary_to_gray = binary ^ (binary >> 1); endfunction function [WIDTH-1:0] gray_to_binary; input [WIDTH-1:0] gray; reg [WIDTH-1:0] binary; integer i; begin binary[WIDTH-1] = gray[WIDTH-1]; for (i = WIDTH-2; i >= 0; i = i - 1) begin binary[i] = binary[i+1] ^ gray[i]; end gray_to_binary = binary; end endfunction // Source domain registers reg [WIDTH-1:0] src_data_reg; reg [1:0] src_toggle = 2'b00; // Destination domain synchronizer registers // ASYNC_REG on memory arrays applies to all elements (* ASYNC_REG = "TRUE" *) reg [WIDTH-1:0] dst_data_gray [0:STAGES-1]; (* ASYNC_REG = "TRUE" *) reg [1:0] dst_toggle_sync [0:STAGES-1]; reg [WIDTH-1:0] dst_data_reg; reg dst_valid_reg = 0; reg [1:0] prev_dst_toggle = 2'b00; // Source domain: capture data and toggle always @(posedge src_clk or negedge reset_n) begin if (!reset_n) begin src_data_reg <= 0; src_toggle <= 2'b00; end else if (src_valid) begin src_data_reg <= src_data; src_toggle <= src_toggle + 1; end end // CDC synchronization chain for data — SYNCHRONOUS RESET // Using synchronous reset avoids latch inference in Vivado. // For CDC synchronizers, synchronous reset is preferred because // the reset value is sampled safely within the clock domain. genvar i; generate for (i = 0; i < STAGES; i = i + 1) begin : data_sync_chain always @(posedge dst_clk) begin if (!reset_n) begin dst_data_gray[i] <= 0; end else begin if (i == 0) begin // Convert to gray code at domain crossing dst_data_gray[i] <= binary_to_gray(src_data_reg); end else begin dst_data_gray[i] <= dst_data_gray[i-1]; end end end end for (i = 0; i < STAGES; i = i + 1) begin : toggle_sync_chain always @(posedge dst_clk) begin if (!reset_n) begin dst_toggle_sync[i] <= 2'b00; end else begin if (i == 0) begin dst_toggle_sync[i] <= src_toggle; end else begin dst_toggle_sync[i] <= dst_toggle_sync[i-1]; end end end end endgenerate // Detect new data always @(posedge dst_clk or negedge reset_n) begin if (!reset_n) begin dst_data_reg <= 0; dst_valid_reg <= 0; prev_dst_toggle <= 2'b00; end else begin // Convert from gray code dst_data_reg <= gray_to_binary(dst_data_gray[STAGES-1]); // Check if toggle changed (new data) if (dst_toggle_sync[STAGES-1] != prev_dst_toggle) begin dst_valid_reg <= 1'b1; prev_dst_toggle <= dst_toggle_sync[STAGES-1]; end else begin dst_valid_reg <= 1'b0; end end end assign dst_data = dst_data_reg; assign dst_valid = dst_valid_reg; endmodule // ============================================================================ // CDC FOR SINGLE BIT SIGNALS // ============================================================================ module cdc_single_bit #( parameter STAGES = 3 )( input wire src_clk, input wire dst_clk, input wire reset_n, input wire src_signal, output wire dst_signal ); (* ASYNC_REG = "TRUE" *) reg [STAGES-1:0] sync_chain; always @(posedge dst_clk or negedge reset_n) begin if (!reset_n) begin sync_chain <= 0; end else begin sync_chain <= {sync_chain[STAGES-2:0], src_signal}; end end assign dst_signal = sync_chain[STAGES-1]; endmodule // ============================================================================ // CDC FOR MULTI-BIT WITH HANDSHAKE // ============================================================================ module cdc_handshake #( parameter WIDTH = 32 )( input wire src_clk, input wire dst_clk, input wire reset_n, input wire [WIDTH-1:0] src_data, input wire src_valid, output wire src_ready, output wire [WIDTH-1:0] dst_data, output wire dst_valid, input wire dst_ready ); // Source domain reg [WIDTH-1:0] src_data_reg; reg src_busy = 0; reg src_ack_sync = 0; (* ASYNC_REG = "TRUE" *) reg [1:0] src_ack_sync_chain = 2'b00; // Destination domain reg [WIDTH-1:0] dst_data_reg; reg dst_valid_reg = 0; reg dst_req_sync = 0; (* ASYNC_REG = "TRUE" *) reg [1:0] dst_req_sync_chain = 2'b00; reg dst_ack = 0; // Source clock domain always @(posedge src_clk or negedge reset_n) begin if (!reset_n) begin src_data_reg <= 0; src_busy <= 0; src_ack_sync <= 0; src_ack_sync_chain <= 2'b00; end else begin // Sync acknowledge from destination src_ack_sync_chain <= {src_ack_sync_chain[0], dst_ack}; src_ack_sync <= src_ack_sync_chain[1]; if (!src_busy && src_valid) begin src_data_reg <= src_data; src_busy <= 1'b1; end else if (src_busy && src_ack_sync) begin src_busy <= 1'b0; end end end // Destination clock domain always @(posedge dst_clk or negedge reset_n) begin if (!reset_n) begin dst_data_reg <= 0; dst_valid_reg <= 0; dst_req_sync <= 0; dst_req_sync_chain <= 2'b00; dst_ack <= 0; end else begin // Sync request from source dst_req_sync_chain <= {dst_req_sync_chain[0], src_busy}; dst_req_sync <= dst_req_sync_chain[1]; // Capture data when request arrives if (dst_req_sync && !dst_valid_reg) begin dst_data_reg <= src_data_reg; dst_valid_reg <= 1'b1; dst_ack <= 1'b1; end else if (dst_valid_reg && dst_ready) begin dst_valid_reg <= 1'b0; end // Clear acknowledge after source sees it if (dst_ack && !dst_req_sync) begin dst_ack <= 1'b0; end end end assign src_ready = !src_busy; assign dst_data = dst_data_reg; assign dst_valid = dst_valid_reg; endmodule