跨时钟域同步(CDC)握手协议

跨时钟域同步(CDC)握手协议

FPGA中的多bit数据跨时钟域同步(CDC)握手协议是高质量设计的核心,使用场景非常广泛。让我用一个完整的工程视角来解析:

1. 核心问题:为什么多bit不能简单打两拍?

对于 单bit控制信号 :打两拍(两级同步器)是标准做法,主要解决亚稳态问题。

但对于 多bit数据总线

  • 每bit的延迟可能不同(布线差异、时序差异)
  • 如果简单地对每bit独立打两拍,数据可能被错位采样
  • 例如:32位数据 0x0000FFFF0x00010000,可能出现中间态 0x0000FFFE(毛刺状态)

这就是所谓的"数据歪斜"(data skew)问题。


2. 握手协议的基本原理

握手协议通过请求(req)应答(ack) 两个控制信号,确保接收方在数据稳定后才采样。

text

复制代码
发送域(clk_src)          接收域(clk_dst)
    数据[7:0] ──────────┐
        ▲               │
        │               ▼
    数据准备好 → req ────┐
        │               │
        │               ├─ 同步req到clk_dst
        │               │
        │               ▼
    ack ←─────────────┐ │
        ▲               │ │
        │               ▼ ▼
    等待ack有效        采样数据

关键特征

  • 数据总线 不需要同步器 (它只是物理连线)
  • 只有 reqack需要做CDC同步
  • 数据在 req有效期间必须保持稳定

3. 经典握手协议的四种状态

状态转移(以发送方视角)

复制代码
IDLE → SEND_DATA → ASSERT_REQ → WAIT_ACK → DEASSERT_REQ → IDLE
      (数据准备)    (发出请求)   (等待应答)   (撤销请求)

Verilog实现示例

复制代码
module cdc_handshake_sender #(
    parameter DATA_WIDTH = 32
)(
    input  wire clk_src,
    input  wire rst_n_src,
    input  wire [DATA_WIDTH-1:0] data_in,  // 要发送的数据
    input  wire valid_in,                   // 发送请求
    output reg  ready_out,                  // 发送方准备好接收新数据
    // CDC接口
    output reg  req_out,                    // 到接收域的请求
    input  wire ack_sync,                   // 从接收域同步回来的应答
    output reg  [DATA_WIDTH-1:0] data_out   // 跨域数据(直接连线)
);

    // 发送方状态机
    typedef enum logic [1:0] {
        S_IDLE,
        S_ASSERT_REQ,
        S_WAIT_ACK,
        S_DEASSERT
    } state_t;
  
    state_t state, next_state;
  
    // 状态寄存器
    always @(posedge clk_src or negedge rst_n_src) begin
        if (!rst_n_src) begin
            state <= S_IDLE;
            req_out <= 1'b0;
            ready_out <= 1'b1;
        end else begin
            state <= next_state;
          
            case (next_state)
                S_IDLE: begin
                    req_out <= 1'b0;
                    ready_out <= 1'b1;
                end
                S_ASSERT_REQ: begin
                    req_out <= 1'b1;
                    ready_out <= 1'b0;  // 发送中,不能接收新数据
                end
                S_WAIT_ACK: begin
                    req_out <= 1'b1;
                    ready_out <= 1'b0;
                end
                S_DEASSERT: begin
                    req_out <= 1'b0;
                    ready_out <= 1'b0;
                end
            endcase
        end
    end
  
    // 状态转移逻辑
    always_comb begin
        next_state = state;
      
        case (state)
            S_IDLE: begin
                if (valid_in && ready_out)
                    next_state = S_ASSERT_REQ;
            end
          
            S_ASSERT_REQ: begin
                // 数据已稳定,进入等待应答状态
                next_state = S_WAIT_ACK;
            end
          
            S_WAIT_ACK: begin
                if (ack_sync)  // 收到接收域的应答
                    next_state = S_DEASSERT;
            end
          
            S_DEASSERT: begin
                // 等待ack变低,确保接收方看到req撤销
                if (!ack_sync)
                    next_state = S_IDLE;
            end
        endcase
    end
  
    // 数据寄存器:在valid_in有效时锁存数据
    always @(posedge clk_src or negedge rst_n_src) begin
        if (!rst_n_src) begin
            data_out <= '0;
        end else if (valid_in && ready_out) begin
            data_out <= data_in;
        end
    end

endmodule

verilog

复制代码
module cdc_handshake_receiver #(
    parameter DATA_WIDTH = 32
)(
    input  wire clk_dst,
    input  wire rst_n_dst,
    // CDC接口
    input  wire [DATA_WIDTH-1:0] data_in,  // 来自发送域的数据
    input  wire req_in,                    // 来自发送域的请求(需要同步)
    output reg  ack_out,                   // 到发送域的应答
    // 接收方接口
    output reg  [DATA_WIDTH-1:0] data_out,
    output reg  valid_out,
    input  wire ready_in                   // 下游模块准备好
);

    // 同步链:将req同步到接收时钟域
    reg [2:0] req_sync;
    wire req_synced = req_sync[2];
  
    always @(posedge clk_dst or negedge rst_n_dst) begin
        if (!rst_n_dst)
            req_sync <= 3'b000;
        else
            req_sync <= {req_sync[1:0], req_in};
    end
  
    // 检测req上升沿(表示新数据到达)
    reg req_synced_prev;
    wire req_pos_edge = req_synced && !req_synced_prev;
  
    always @(posedge clk_dst or negedge rst_n_dst) begin
        if (!rst_n_dst)
            req_synced_prev <= 1'b0;
        else
            req_synced_prev <= req_synced;
    end
  
    // 接收方状态机
    typedef enum logic {
        R_IDLE,
        R_ACK
    } r_state_t;
  
    r_state_t r_state, r_next_state;
  
    always @(posedge clk_dst or negedge rst_n_dst) begin
        if (!rst_n_dst) begin
            r_state <= R_IDLE;
            ack_out <= 1'b0;
            valid_out <= 1'b0;
            data_out <= '0;
        end else begin
            r_state <= r_next_state;
          
            case (r_next_state)
                R_IDLE: begin
                    ack_out <= 1'b0;
                    if (req_pos_edge) begin
                        // 采样数据
                        data_out <= data_in;
                        valid_out <= 1'b1;
                    end else if (valid_out && ready_in) begin
                        valid_out <= 1'b0;
                    end
                end
              
                R_ACK: begin
                    ack_out <= 1'b1;
                    valid_out <= 1'b0;  // 数据已给出
                end
            endcase
        end
    end
  
    always_comb begin
        r_next_state = r_state;
      
        case (r_state)
            R_IDLE: begin
                if (req_pos_edge)
                    r_next_state = R_ACK;
            end
          
            R_ACK: begin
                if (!req_synced)  // 发送方已撤销req
                    r_next_state = R_IDLE;
            end
        endcase
    end

endmodule

4. 握手协议的时序图

text

复制代码
clk_src:    __/  \__/  \__/  \__/  \__/  \__/  \__
clk_dst:  __/  \__/  \__/  \__/  \__/  \__/  \__/  \__
data:     XXXX< D1 >XXXXXXXXXXXXXXXXXXXX< D2 >XXXXXX
valid_in: ______/                     \__________
ready_out: \______________________________/       \__
req_out:   ______________/                 \________
ack_sync:  XXXXXXXXXXXXXXX\_____________/   \______
req_synced: ______________/                 \________
valid_out:  _______________/     \__________________
ack_out:   ______________/                 \________

关键点:
1. D1稳定后,req拉高
2. req被同步到clk_dst域(req_synced)
3. 接收方检测到req_synced上升沿,采样D1,拉高ack
4. ack被同步回clk_src域(ack_sync)
5. 发送方看到ack_sync,撤销req
6. 接收方看到req_synced变低,撤销ack

5. 握手协议的优缺点

优点:

  1. 绝对安全 :确保多bit数据被完整正确地传输
  2. 适应性强 :适用于任意频率比、任意相位关系的时钟域
  3. 吞吐量可预测 :完成一次传输需要固定周期数

缺点:

  1. 带宽较低 :完成一次握手需要多个时钟周期
  • 最佳情况:4个src周期 + 4个dst周期
  • 实际:通常6-10个周期
  1. 控制逻辑复杂 :需要两个状态机
  2. req/ack本身需要CDC :增加同步延迟

6. 工程实践中的变体

流水线握手(增加吞吐量)

verilog

复制代码
// 允许最多N个传输同时在管道中
module cdc_handshake_pipelined #(
    parameter DEPTH = 2  // 流水线深度
)(
    // 接口类似,但内部有多个并行的握手单元
);

带缓冲的握手(解耦吞吐)

verilog

复制代码
// 发送方和接收方各加一个FIFO
// 握手发生在FIFO的push/pop之间

Valid-Ready握手变体

verilog

复制代码
// 类似AXI的valid/ready握手
// 但valid需要同步,ready需要反向同步
// 更高效但实现更复杂

7. 选择指南:何时用握手?

场景 推荐方案
低频数据传输(如配置寄存器) 握手协议
高频数据流(如视频流) 异步FIFO
控制信号(单bit或格雷码) 打两拍
计数器同步 格雷码+打两拍
地址/数据总线 握手或异步FIFO

经验法则

  • 如果数据传输间隔 > 10个时钟周期 → 握手协议
  • 如果数据连续流 → 异步FIFO
  • 如果只是状态/控制标志 → 格雷码同步

8. 关键设计检查点

  1. 数据稳定窗口 :req有效期间,数据必须绝对稳定

  2. 脉冲展宽 :req脉冲必须足够宽,确保能被慢时钟采样到

  3. 撤销顺序 :必须先撤销req,再等待ack撤销

  4. 复位同步 :确保两个域的复位释放时间差不会导致错误握手

  5. 时序约束
    tcl

    复制代码
    # 握手信号需要设set_false_path
    set_false_path -from [get_clocks clk_src] -to [get_pins req_sync_reg*/D]
    set_false_path -from [get_clocks clk_dst] -to [get_pins ack_sync_reg*/D]

握手协议是FPGA/ASIC设计中 最可靠、最通用的多bit CDC方法 。虽然效率不是最高,但它的确定性和安全性使其成为许多关键接口的首选方案。

相关推荐
Flamingˢ4 小时前
Verilog中reg与wire的区别:从语法到实战
学习·fpga开发·硬件工程
数字芯片实验室4 小时前
边界值测试:一个”==”引发的芯片bug
fpga开发·bug
9527华安4 小时前
FPGA实现Aurora8B10B视频转UVC传输,基于GTP高速收发器+FT602芯片架构,提供4套工程源码和技术支持
fpga开发·gtp·uvc·aurora8b10b·ft602
tiantianuser4 小时前
RDMA设计31:RoCE v2 发送模块3
fpga开发·rdma·cmac·roce v2
海涛高软1 天前
verlog中阻塞赋值和非阻塞赋值
fpga开发
tiantianuser1 天前
RDMA设计29:RoCE v2 发送及接收模块设计2
服务器·fpga开发·rdma·fpga设计·高速传输
9527华安1 天前
FPGA实现GTP光口视频转USB3.0 UVC,基于Aurora8B10B+FT602芯片架构,提供4套工程源码和技术支持
fpga开发·gtp·usb3.0·uvc·aurora8b10b·ft602
zy135380675731 天前
12V输入5V/2A输出升降压芯片AH4002
科技·单片机·物联网·fpga开发·硬件工程·智能电视
dadaobusi1 天前
verilog的generate
fpga开发