FPGA笔记——跨时钟域处理

FPGA跨时钟域处理

参考文章1:https://blog.csdn.net/baidu_25816669/article/details/103817314
参考文章2:http://dengkanwen.com/238.html

跨时钟域问题

1.跨时钟域
若一个电路launch时钟和capture时钟不是同一个时钟,就是跨时钟域电路

若两个时钟是同步时钟就是同步时钟域
若两个时钟是异步时钟就是异步时钟域
FPGA笔记——跨时钟域处理_第1张图片
lanch时钟为CLKA,capture时钟为CLKB,该电路为典型的跨时钟域的例子。
跨时钟例子:
单个模块:UART, USB等
FPGA笔记——跨时钟域处理_第2张图片

CLK1与CLK2来自不同时钟源,由于时钟源不同,对于REG2和REG3来说,在同一时刻,一个认为REG1的输出是1,另一个认为是0,这将会导致电路判断出现错误,即亚稳态问题。

跨时钟处理有三种方式:

1.控制信号脉冲检测法,适合于快时钟采慢时钟;
2.握手信号法,适合慢时钟采快时钟;
3.异步fifo法,适合大量数据的传输

一、单bit数据传输

慢时钟域到快时钟域 (快采慢)

       在快时钟的上升沿,使用两级或三级寄存器,检测慢时钟域的使能信号,然后在快时钟域产生一个周期的脉冲信号。
        处理跨时钟域的数据有单bit和多bit之分,而打两拍的方式常见于处理单bit数据的跨时钟域问题。打两拍的方式,其实说白了,就是定义两级寄存器,对输入的数据进行延拍。如下图所示。
FPGA笔记——跨时钟域处理_第3张图片
       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

三、异步fifo法/异步双口RAM

       读写时钟各自按照两个时钟域的时钟,控制wreq、和req进行读写命令操作。

       处理多bit数据的跨时钟域,一般采用异步双口RAM。假设我们现在有一个信号采集平台,ADC芯片提供源同步时钟60MHz,ADC芯片输出的数据在60MHz的时钟上升沿变化,而FPGA内部需要使用100MHz的时钟来处理ADC采集到的数据(多bit)。

       在这种类似的场景中,我们便可以使用异步双口RAM来做跨时钟域处理。先利用ADC芯片提供的60MHz时钟将ADC输出的数据写入异步双口RAM,然后使用100MHz的时钟从RAM中读出。

       对于使用异步双口RAM来处理多bit数据的跨时钟域,相信大家还是可以理解的。当然,在能使用异步双口RAM来处理跨时钟域的场景中,也可以使用异步FIFO来达到同样的目的。

你可能感兴趣的:(FPGA,集成电路,fpga)