一、题目分析本题要求实现一个脉冲同步电路将 A 时钟域中的单周期脉冲 data_in同步到 B 时钟域中并在 B 时钟域重新生成一个单周期宽度的脉冲 dataout。题目中 clk_fast 是 A 时钟域时钟clk_slow 是 B 时钟域时钟。A 时钟域频率是 B 时钟域的 10 倍也就是说 clk_fast 比 clk_slow 快很多。如果直接把 data_in 接到慢时钟域中采样就可能出现慢时钟采不到快时钟单周期脉冲的情况。因此本题不能简单地使用两级寄存器直接同步 data_in而是需要先在快时钟域中把脉冲转换成电平变化再把这个电平变化同步到慢时钟域中最后在慢时钟域中通过边沿检测重新产生脉冲。二、解题思路由于 data_in 是 clk_fast 时钟域中的单周期脉冲而 clk_slow 比 clk_fast 慢很多如果 data_in 只保持一个 clk_fast 周期那么 clk_slow 很可能刚好错过这个脉冲导致同步失败。所以这里使用“脉冲转翻转信号”的方式。具体做法是在 clk_fast 时钟域中定义一个寄存器 data_in_fast_reg。当检测到 data_in 为 1 时不是直接传递这个脉冲而是让 data_in_fast_reg 翻转一次。这样一来原本很窄的脉冲就变成了一个稳定的电平变化。只要 data_in_fast_reg 发生翻转就代表快时钟域中出现过一次输入脉冲。然后在 clk_slow 时钟域中对 data_in_fast_reg 进行多级寄存器同步。代码中使用了三个寄存器 data_in_slow_reg1、data_in_slow_reg2 和 data_in_slow_reg3。前两级主要用于降低跨时钟域亚稳态的影响第三级用于和第二级做比较。最后通过异或运算dataout data_in_slow_reg2 ^ data_in_slow_reg3;当同步后的信号发生变化时第二级和第三级寄存器的值会有一个时钟周期不同此时异或结果为 1于是在慢时钟域中产生一个单周期脉冲 dataout。这样就实现了从快时钟域到慢时钟域的脉冲同步。三、Verilog 代码timescale 1ns/1ns module pulse_detect( input clk_fast , input clk_slow , input rst_n , input data_in , output dataout ); reg data_in_fast_reg; always (posedge clk_fast or negedge rst_n) begin if(~rst_n) begin data_in_fast_reg 1b0; end else begin if(data_in 1b1) data_in_fast_reg ~data_in_fast_reg; else data_in_fast_reg data_in_fast_reg; end end reg data_in_slow_reg1; reg data_in_slow_reg2; reg data_in_slow_reg3; always (posedge clk_slow or negedge rst_n) begin if(~rst_n) begin data_in_slow_reg1 1b0; data_in_slow_reg2 1b0; data_in_slow_reg3 1b0; end else begin data_in_slow_reg1 data_in_fast_reg; data_in_slow_reg2 data_in_slow_reg1; data_in_slow_reg3 data_in_slow_reg2; end end assign dataout data_in_slow_reg2 ^ data_in_slow_reg3; endmodule四、波形分析从代码可以知道data_in 是快时钟域 clk_fast 下的输入脉冲。当 data_in 为 1 时data_in_fast_reg 会在 clk_fast 上升沿翻转一次。对应到波形中可以看到每来一个 data_in 脉冲data_in_fast_reg 就会翻转一次。这样就说明快时钟域中的窄脉冲已经被转换成了电平变化避免了被慢时钟漏采的问题。之后 data_in_fast_reg 进入慢时钟域在 clk_slow 下依次经过 data_in_slow_reg1、data_in_slow_reg2 和 data_in_slow_reg3。波形中也能看到这几个寄存器的变化是逐拍传递的符合代码中的三级打拍逻辑。最后 dataout 是由 data_in_slow_reg2 和 data_in_slow_reg3 异或得到的。当这两个信号不同的时候dataout 拉高当二者相同后dataout 又恢复为 0。从波形中可以看到每次 data_in_fast_reg 的变化同步到慢时钟域后dataout 都会产生一个 clk_slow 周期宽度的脉冲。说明该设计能够把快时钟域的单周期脉冲正确同步成慢时钟域下的单周期脉冲。五、总结本题的重点是跨时钟域脉冲同步。对于单 bit 信号来说如果是从慢时钟域同步到快时钟域直接使用两级寄存器同步通常就可以采到。但是如果是从快时钟域同步到慢时钟域尤其是单周期脉冲就不能直接同步因为慢时钟可能采不到这个很窄的脉冲。为了解决这个问题常用的方法是先把脉冲转换成翻转信号。每来一个脉冲就让寄存器状态翻转一次。这样脉冲信息就不会因为宽度太窄而丢失慢时钟域只要采到这个电平变化就能知道快时钟域中发生过一次脉冲。代码中的 data_in_fast_reg 就是这个翻转寄存器。当 data_in 为 1 时data_in_fast_reg 取反。注意这里不是保持高电平而是每来一次脉冲就翻转一次。进入慢时钟域后使用 data_in_slow_reg1、data_in_slow_reg2、data_in_slow_reg3 进行同步和延迟。前两级寄存器主要是为了降低亚稳态风险这是跨时钟域处理中很常见的做法。第三级寄存器则用于保存上一个慢时钟周期的同步结果。最后通过 data_in_slow_reg2 和 data_in_slow_reg3 异或就可以检测同步信号是否发生变化。只要发生变化异或结果就会在一个 clk_slow 周期内为 1因此 dataout 就是慢时钟域中的单周期脉冲。