参考文章1:https://blog.csdn.net/baidu_25816669/article/details/103817314
参考文章2:http://dengkanwen.com/238.html
1.跨时钟域
若一个电路launch时钟和capture时钟不是同一个时钟,就是跨时钟域电路
若两个时钟是同步时钟就是同步时钟域
若两个时钟是异步时钟就是异步时钟域
lanch时钟为CLKA,capture时钟为CLKB,该电路为典型的跨时钟域的例子。
跨时钟例子:
单个模块:UART, USB等
CLK1与CLK2来自不同时钟源,由于时钟源不同,对于REG2和REG3来说,在同一时刻,一个认为REG1的输出是1,另一个认为是0,这将会导致电路判断出现错误,即亚稳态问题。
1.控制信号脉冲检测法,适合于快时钟采慢时钟;
2.握手信号法,适合慢时钟采快时钟;
3.异步fifo法,适合大量数据的传输
在快时钟的上升沿,使用两级或三级寄存器,检测慢时钟域的使能信号,然后在快时钟域产生一个周期的脉冲信号。
处理跨时钟域的数据有单bit和多bit之分,而打两拍的方式常见于处理单bit数据的跨时钟域问题。打两拍的方式,其实说白了,就是定义两级寄存器,对输入的数据进行延拍。如下图所示。
data是时钟域1的数据,需要传到时钟域2(clk)进行处理,寄存器1和寄存器2使用的时钟都为clk。假设在clk的上升沿正好采到data的跳变沿(从0变1的上升沿,实际上的数据跳变不可能是瞬时的,所以有短暂的跳变时间),那这时作为寄存器1的输入到底应该是0还是1呢?这是一个不确定的问题。所以Q1的值也不能确定,但至少可以保证,在clk的下一个上升沿,Q1基本可以满足第二级寄存器的保持时间和建立时间要求,出现亚稳态的概率得到了很大的改善。
如果再加上第三级寄存器,由于第二级寄存器对于亚稳态的处理已经起到了很大的改善作用,第三级寄存器在很大程度上可以说只是对于第二级寄存器的延拍,所以意义是不大的。
input clk;
input rst;
input wr_en;
reg wr_r;
reg wr_r2;
wire pos_wr;
always@(posedge clk ,negedge rst)
begin
if(rst)begin
wr_r <= 1'b0;
wr_r2<= 1'b0;
end else
wr_r <= wr_en;
wr_r2 <= wr_r;
end
end
assign pos_wr= wr_r && (~wr_r2 ); //写选通信号上升沿,拉高一个周期的脉冲信号
思路一般都是在
1、将快的时钟域中的信号进行展宽
2、对展宽的信号在慢的时钟域2级同步
3、在慢时钟域中产生反馈信号来对快的展宽信号进行拉低处理
代码思想
快时钟域产生写请求和data,慢时钟域检测到写请求,锁存数据后产生应答信号,快时钟域检测到应答信号后,撤销写请求,至此完成一次写操作。
module handshack (
input clk,
input rst_n,
input req,
input [7:0] datain,
output ack,
output data_out
)
//****************************
//上升沿信号检测
reg req_r ,req_r2,req_r3 ;
always@ (posedge clk or negedge rst_n)
begin
if (!rst_n)begin
req_r <= 1'b1 ;
req_r2 <= 1'b1 ;
req_r3 <= 1'b1 ;
end else begin
req_r <= req ;
req_r2 <= req_r ;
req_r3 <= req_r2 ;
end
end
//pos_req2 比 pos_req1 延时一个时钟周期,确保数据被稳定锁存
wire pos_req1 = req_r && ~ req_r2 ;
wire pos_req2 = req_r2 && ~ req_r3 ;
//***************************************
// 数据锁存
reg [7:0] dataoutr;
always@(posedge clk or negedge rst_n )begin
if(!rst_n)
dataoutr <= 8'h0;
else if(pos_req1)
dataoutr <= datain ; //检测到req有效后锁存输入数据
end
assign dataout =dataoutr
//**************************************
//产生应答信号ack
reg ackr;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
ackr <= 1'b0;
else if (pos_req2)
ackr <= 1'b1;
else if(!req)
ackr <= 1'b0 ;
end
assign ack =ackr;
endmoudle
读写时钟各自按照两个时钟域的时钟,控制wreq、和req进行读写命令操作。
处理多bit数据的跨时钟域,一般采用异步双口RAM。假设我们现在有一个信号采集平台,ADC芯片提供源同步时钟60MHz,ADC芯片输出的数据在60MHz的时钟上升沿变化,而FPGA内部需要使用100MHz的时钟来处理ADC采集到的数据(多bit)。
在这种类似的场景中,我们便可以使用异步双口RAM来做跨时钟域处理。先利用ADC芯片提供的60MHz时钟将ADC输出的数据写入异步双口RAM,然后使用100MHz的时钟从RAM中读出。
对于使用异步双口RAM来处理多bit数据的跨时钟域,相信大家还是可以理解的。当然,在能使用异步双口RAM来处理跨时钟域的场景中,也可以使用异步FIFO来达到同样的目的。