`timescale 1 ns/1 ps // ============================================================================= // cdc_strobe_data // - One-deep mailbox for (strobe + data) crossing clock domains. // - Uses toggle req/ack with 2FF sync for toggles. // - Wide bus is held stable by source until ack, destination samples-until-stable. // ============================================================================= module cdc_strobe_data #( parameter integer WIDTH = 32, parameter integer STABLE_SAMPLES = 2 // >=2 recommended )( // Source domain input wire s_clk, input wire s_rst, // async OK (posedge) if used consistently input wire s_pulse, // strobe (1+ cycles). Accepted when not busy. input wire [WIDTH-1:0] s_data, output wire s_busy, // 1 = mailbox full / waiting for ack output wire s_accepted, // 1-cycle pulse when we accepted s_pulse // Destination domain input wire d_clk, input wire d_rst, output reg d_pulse, // 1-cycle pulse on new data output reg [WIDTH-1:0] d_data // updated when d_pulse asserted; held otherwise ); // ---------------------------- // Source: hold + req toggle // ---------------------------- reg [WIDTH-1:0] s_hold; reg s_req_tog; reg s_inflight; // Ack toggle synchronized into source domain (* ASYNC_REG="TRUE" *) reg s_ack_sync1, s_ack_sync2; assign s_busy = s_inflight; wire do_accept = s_pulse && !s_inflight; assign s_accepted = do_accept; // d_ack_tog is generated in destination domain (declared below as reg) // and is synced here with 2FF. always @(posedge s_clk or posedge s_rst) begin if (s_rst) begin s_hold <= {WIDTH{1'b0}}; s_req_tog <= 1'b0; s_inflight <= 1'b0; s_ack_sync1 <= 1'b0; s_ack_sync2 <= 1'b0; end else begin s_ack_sync1 <= d_ack_tog; s_ack_sync2 <= s_ack_sync1; // clear inflight when ack matches current req toggle if (s_inflight && (s_ack_sync2 == s_req_tog)) s_inflight <= 1'b0; // accept new item if (do_accept) begin s_hold <= s_data; s_req_tog <= ~s_req_tog; s_inflight <= 1'b1; end end end // ---------------------------- // Destination: sync req toggle, sample-until-stable, then ack toggle // ---------------------------- (* ASYNC_REG="TRUE" *) reg d_req_sync1, d_req_sync2; reg d_req_seen; reg d_ack_tog; reg [WIDTH-1:0] samp; reg [WIDTH-1:0] samp_prev; integer stable_cnt; reg capturing; wire d_new_req = (d_req_sync2 != d_req_seen); always @(posedge d_clk or posedge d_rst) begin if (d_rst) begin d_req_sync1 <= 1'b0; d_req_sync2 <= 1'b0; d_req_seen <= 1'b0; d_ack_tog <= 1'b0; d_pulse <= 1'b0; d_data <= {WIDTH{1'b0}}; samp <= {WIDTH{1'b0}}; samp_prev <= {WIDTH{1'b0}}; stable_cnt <= 0; capturing <= 1'b0; end else begin d_pulse <= 1'b0; d_req_sync1 <= s_req_tog; d_req_sync2 <= d_req_sync1; if (d_new_req && !capturing) begin capturing <= 1'b1; stable_cnt <= 0; samp_prev <= s_hold; samp <= s_hold; end else if (capturing) begin samp <= s_hold; if (samp == samp_prev) begin if (stable_cnt < (STABLE_SAMPLES-1)) stable_cnt <= stable_cnt + 1; else begin // accept d_data <= samp; d_pulse <= 1'b1; d_req_seen <= d_req_sync2; d_ack_tog <= ~d_ack_tog; capturing <= 1'b0; end end else begin stable_cnt <= 0; end samp_prev <= samp; end end end endmodule