跨时钟域信号的处理方法

2019-10-29 16:03:46 浏览数 (2)

说到同步器就得先说亚稳态

在Digital Systems Engineering一书中,亚稳态是这么定义的 When sampling a changing data signal with a clock ... the order of the events determines the outcome.The smaller the time difference between the events, the longer it takes to determine which came first.When two events occur very close together, the decision process can take longer than the time allotted,and a synchronization failure occurs. 亚稳态将导致下一级输入为未知的状态。 为了防止出现亚稳态传输到下一级就要使用同步器了。 在Digital Systems Engineering一书中,同步器是这么定义的 A synchronizer is a device that samples an asynchronous signal and outputs a version of the signal that has transitions synchronized to a local or sample clock. 最常见的就同步器就是两级同步。 在使用两级同步的时候得注意被采样信号的宽度必须大于采样时钟的周期,不然就会出现下图的问题,漏采样。

换句话说,慢时钟域的信号同步到快时钟域两级同步没问题,快到慢的就可能会出现问题。

快时钟域信号到慢时钟域有可能的情况是:

快时钟域信号宽度比慢时钟信号周期窄,导致漏采。

解决的方法有:

1.将快时钟域信号延长,至少有慢时钟周期的一到两个周期宽

2. 使用反馈的方法,快时钟域信号有效直到慢时钟域有反馈信号,表示已经正确采样此信号,然后快时钟域信号无效。

通过反馈的方式很安全,但是从上图可以看出来延时是非常大的。慢时钟采快时钟信号,然后反馈信号再由快时钟采。

以上是简单的单个信号同步器的基本方法。

多个信号跨时钟域

多个控制信号跨时钟域仅仅通过简单的同步器同步有可能是不安全的。

简单举例,b_load和b_en同步至a_clk时钟域,如果这两个信号有一个小的skew,将导致在a_clk时钟域中两个信号并不是在同一时刻起作用,与在b_clk中的逻辑关系不同。

解决的方法应该比较简单,就是将b_load和b_en信号在b_clk时钟域中合并成一个信号,然后同步至a_clk中。

如果遇到不能合并的情况,如译码信号。如下图:

如果Bdec[0]、bdec[1]间存在skew将导致同步至a_clk中后译码错误,出现误码。在这种情况下,建议加入另一个控制信号,确保 bdec[0]、bec[1]稳定时采。例如在bdec[0]、bec[1]稳定输出后一到两个周期b_clk域输出一个en信号,通知a_clk域此时 可以采bdec[0]、bec[1]信号。这样可确保正确采样。

数据路径同步

对数据进行跨时钟域处理时,如果采用控制信号同步的方式进行处理的话,将是非常浩大的工程,而且是不安全的。

简单来说,数据同步有两种常见的方式:

1.握手方式

2.FIFO

简要说下握手方式,无非就是a_clk域中首先将data_valid信号有效,同时数据保持不变,然后等待b_clk中反馈回采样结束的信号,然后 data_valid信号无效,数据变化。如有数据需要同步则重复上述过程。握手方式传输效率低,比较适用于数据传输不是很频繁的,数据量不大的情况。

FIFO则适合数据量大的情况,FIFO两端可同时进行读/写操作,效率较高。而且如果控制信号比较多,也可采用fifo方式进行同步,将控制信息与数据打包,写入FIFO,在另一端读取,解码,取得数据和控制信息。

----------------------------------------------------------------------------------------------------------

举例:

异步时钟脉冲同步的实现


设计代码:

代码语言:javascript复制
module edge_detect(
                   input sclk_1,//100M
                   input sclk_2,//50M
                   input p_in,
                   output p_out
                   );   reg p_in_reg=0;
   reg dly1,dly2,dly3;
   wire mux_2;    //组合逻辑产生毛刺,p_in没有和sclk_1 同步
   assign mux_2 = (p_in==1'b1)? (~p_in_reg):p_in_reg;   always @(posedge sclk_1)
       p_in_reg <= mux_2;   always @(posedge sclk_2)
       {dly3,dly2,dly1} <= {dly2,dly1,p_in_reg};//流水移位赋值   assign p_out = dly3 ^ dly2;endmodule

//仿真tb

代码语言:javascript复制
`timescale 1ns/1ps
module edge_detect_tb;
   reg sclk_1,sclk_2;
   reg p_in;
   reg p_in_r;//消除mux_2中的毛刺   wire p_out;   initial sclk_1 = 1;
   //always #5 sclk_1 = ~sclk_1;
   always #20 sclk_1 = ~sclk_1;   initial sclk_2 = 1;
   always #10 sclk_2 = ~sclk_2;   //pin和sclk_1 进行同步
   always @(posedge sclk_1)
       p_in_r <= p_in;   initial begin
       p_in = 0;
       p_in_r = 0;        #200;
       @(posedge sclk_1)
       p_in = 1;
       @(posedge sclk_1)
       p_in = 0;        #200;
       @(posedge sclk_1)
       p_in = 1;
       @(posedge sclk_1)
       p_in = 0;
   end   edge_detect edge_detect_inst(
                   .sclk_1(sclk_1),//100M
                   .sclk_2(sclk_2),//50M
                   .p_in(p_in_r),
                   .p_out(p_out)
                   );
endmodule

//这里使用的是run.do脚本仿真,不需要再quartus建立工程了,直接用modelsim就可以了 //run.do脚本代码

代码语言:javascript复制
quit -sim.main clearvlib workvlog edge_detect_tb.vvlog ./../src/edge_detect.vvsim -voptargs= acc work.edge_detect_tbadd wave edge_detect_tb/edge_detect_inst/*run 1us

根据图示很容易就能写出代码, 需要注意的是 组合逻辑MUX_2的变化是和p_in有关,所以输入信号p_in要同步输入时钟,消除组合逻辑中产生的毛刺

具体仿真效果可以动手做一下,不做永远不知道哪里会出错

欢迎关注通院专用集成电路课程学习公众号

0 人点赞