## 一、跨时钟域信号传输亚稳态问题的由来

在数字电路里,不同模块可能工作在不同的时钟频率下。就好比一群人在不同的节奏下跳舞,当信号从一个时钟域传到另一个时钟域时,就容易出问题。这时候就可能出现亚稳态,啥是亚稳态呢?简单来说,就是信号在一段时间内处于不稳定的状态,既不是 0 也不是 1,这就会让接收端的电路犯迷糊,不知道该把这个信号当成 0 还是 1,从而影响整个系统的可靠性。

举个例子,假如有一个系统,有两个模块,模块 A 工作在 100MHz 的时钟频率下,模块 B 工作在 50MHz 的时钟频率下。模块 A 产生了一个信号要传给模块 B。由于两个模块的时钟节奏不一样,当模块 A 发送信号的时刻和模块 B 采样信号的时刻不匹配时,就可能出现亚稳态。

## 二、关键技术之同步器

1. 简单双触发器同步器

这是最常用的一种解决亚稳态问题的方法。它的原理很简单,就是用两个触发器串联起来,把来自一个时钟域的信号先经过第一个触发器,再经过第二个触发器,最后输出到另一个时钟域。这样可以大大降低亚稳态出现的概率。

下面是一个 Verilog 代码示例:

// Verilog 技术栈
module sync_two_ff (
    input wire clk,  // 目标时钟域的时钟信号
    input wire rst_n, // 异步复位信号,低电平有效
    input wire data_in, // 输入信号,来自源时钟域
    output reg data_out // 输出信号,到目标时钟域
);

reg data_ff1; // 第一个触发器的输出

// 第一个触发器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        data_ff1 <= 1'b0; // 复位时,第一个触发器输出为 0
    end else begin
        data_ff1 <= data_in; // 正常工作时,第一个触发器接收输入信号
    end
end

// 第二个触发器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        data_out <= 1'b0; // 复位时,第二个触发器输出为 0
    end else begin
        data_out <= data_ff1; // 正常工作时,第二个触发器接收第一个触发器的输出
    end
end

endmodule

在这个例子中,data_in 是来自源时钟域的信号,clk 是目标时钟域的时钟信号。信号先经过 data_ff1 这个触发器,再经过 data_out 这个触发器,最后输出到目标时钟域。这样经过两次采样,就可以减少亚稳态的影响。

2. 多级同步器

有时候,简单的双触发器同步器可能还不够,就需要使用多级同步器。多级同步器就是在双触发器同步器的基础上,再增加几个触发器。

下面是一个三级同步器的 Verilog 代码示例:

// Verilog 技术栈
module sync_three_ff (
    input wire clk,  // 目标时钟域的时钟信号
    input wire rst_n, // 异步复位信号,低电平有效
    input wire data_in, // 输入信号,来自源时钟域
    output reg data_out // 输出信号,到目标时钟域
);

reg data_ff1; // 第一个触发器的输出
reg data_ff2; // 第二个触发器的输出

// 第一个触发器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        data_ff1 <= 1'b0; // 复位时,第一个触发器输出为 0
    end else begin
        data_ff1 <= data_in; // 正常工作时,第一个触发器接收输入信号
    end
end

// 第二个触发器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        data_ff2 <= 1'b0; // 复位时,第二个触发器输出为 0
    end else begin
        data_ff2 <= data_ff1; // 正常工作时,第二个触发器接收第一个触发器的输出
    end
end

// 第三个触发器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        data_out <= 1'b0; // 复位时,第三个触发器输出为 0
    end else begin
        data_out <= data_ff2; // 正常工作时,第三个触发器接收第二个触发器的输出
    end
end

endmodule

多级同步器可以进一步降低亚稳态出现的概率,但也会增加信号的延迟。

## 三、关键技术之握手协议

1. 原理

握手协议就像是两个人握手一样,发送方和接收方通过一系列的信号交互来确保信号的正确传输。发送方先发出一个请求信号,告诉接收方有数据要发送。接收方收到请求信号后,发出一个响应信号,表示已经准备好接收数据。发送方收到响应信号后,才会发送数据。

2. 示例代码

// Verilog 技术栈
module handshake (
    input wire clk_a, // 源时钟域的时钟信号
    input wire clk_b, // 目标时钟域的时钟信号
    input wire rst_n, // 异步复位信号,低电平有效
    input wire data_in, // 输入信号,来自源时钟域
    output reg data_out // 输出信号,到目标时钟域
);

// 源时钟域信号
reg req; // 请求信号
reg ack; // 响应信号

// 目标时钟域信号
reg req_sync; // 同步后的请求信号
reg ack_sync; // 同步后的响应信号

// 源时钟域逻辑
always @(posedge clk_a or negedge rst_n) begin
    if (!rst_n) begin
        req <= 1'b0; // 复位时,请求信号为 0
    end else if (!req) begin
        req <= data_in; // 当请求信号为 0 时,根据输入信号设置请求信号
    end else if (ack_sync) begin
        req <= 1'b0; // 当收到响应信号时,请求信号置为 0
    end
end

// 目标时钟域逻辑
always @(posedge clk_b or negedge rst_n) begin
    if (!rst_n) begin
        req_sync <= 1'b0; // 复位时,同步后的请求信号为 0
        ack <= 1'b0; // 复位时,响应信号为 0
        data_out <= 1'b0; // 复位时,输出信号为 0
    end else begin
        req_sync <= req; // 同步请求信号
        if (req_sync && !ack) begin
            data_out <= 1'b1; // 当收到请求信号且响应信号为 0 时,输出信号为 1
            ack <= 1'b1; // 发出响应信号
        end else if (!req_sync) begin
            ack <= 1'b0; // 当请求信号为 0 时,响应信号置为 0
        end
    end
end

// 同步响应信号到源时钟域
always @(posedge clk_a or negedge rst_n) begin
    if (!rst_n) begin
        ack_sync <= 1'b0; // 复位时,同步后的响应信号为 0
    end else begin
        ack_sync <= ack; // 同步响应信号
    end
end

endmodule

在这个例子中,req 是发送方发出的请求信号,ack 是接收方发出的响应信号。通过这两个信号的交互,确保了数据的正确传输。

## 四、应用场景

1. FPGA 开发

在 FPGA 开发中,经常会有不同模块工作在不同的时钟频率下。比如一个 FPGA 系统中,有一个高速数据采集模块工作在 200MHz 的时钟频率下,而另一个数据处理模块工作在 100MHz 的时钟频率下。这时候就需要解决跨时钟域信号传输的亚稳态问题,以确保系统的可靠性。

2. 芯片设计

在芯片设计中,不同的功能模块可能工作在不同的时钟域。例如,一个芯片中有一个 CPU 模块和一个外设模块,CPU 模块工作在较高的时钟频率下,外设模块工作在较低的时钟频率下。信号在这两个模块之间传输时,就需要采用合适的技术来解决亚稳态问题。

## 五、技术优缺点

1. 同步器

优点:

  • 实现简单,只需要几个触发器就可以实现。
  • 成本低,不需要额外的复杂逻辑。

缺点:

  • 会增加信号的延迟,尤其是多级同步器。
  • 对于一些高速信号,可能无法完全消除亚稳态。

2. 握手协议

优点:

  • 可靠性高,通过信号交互确保数据的正确传输。
  • 可以适应不同的时钟频率差异。

缺点:

  • 实现复杂,需要额外的逻辑来处理请求和响应信号。
  • 数据传输效率相对较低,因为需要等待请求和响应信号的交互。

## 六、注意事项

1. 同步器的选择

在选择同步器时,要根据信号的频率和系统的要求来决定使用双触发器同步器还是多级同步器。如果信号频率较低,双触发器同步器可能就足够了;如果信号频率较高,可能需要使用多级同步器。

2. 握手协议的时序

在使用握手协议时,要注意请求和响应信号的时序。确保请求信号和响应信号的交互时间合适,避免出现信号丢失或错误。

3. 时钟抖动

时钟抖动会影响同步器和握手协议的性能。在设计系统时,要尽量减少时钟抖动,例如采用高质量的时钟源。

## 七、文章总结

在 Verilog 中解决跨时钟域信号传输的亚稳态问题是确保系统可靠性的关键。通过使用同步器和握手协议等技术,可以有效地降低亚稳态出现的概率。同步器实现简单、成本低,但会增加信号延迟;握手协议可靠性高,但实现复杂、数据传输效率相对较低。在实际应用中,要根据具体的场景和要求选择合适的技术,并注意同步器的选择、握手协议的时序和时钟抖动等问题。