FPGA——浅谈跨时钟域

本篇文章仅用于个人学习,如有雷同,我抄他的。


跨时钟域是每个FPGA初学者都会遇到的问题,跨时钟域分情况有以下几种:

单bit跨时钟域

慢时钟域到快时钟域

快时钟域到慢时钟域

多bit跨时钟域        


单bit跨时钟域

慢时钟域到快时钟域

        首先谈谈单bit数据的跨时钟域问题,当从慢时钟域到快时钟域时,常用方法为打两拍。首先快时钟域是肯定可以采集到慢时钟域的数据的,所以需要解决的就是亚稳态的问题。打两拍的基本原理就是,数据(处于10Mhz时钟下)在跳变过程中不是瞬时的,总有一个跳变时间。如果在clk(处于125Mhz下)的上升沿采集到了数据的跳变过程时,此时的数据是不确定的,可能是1,可能是0,这就导致了亚稳态的产生。具体如下图所示,图片来自以下博客。   

解决跨时钟域问题的三大方法_zuokai的博客-CSDN博客_跨时钟域https://blog.csdn.net/weixin_43343190/article/details/82956033?ops_request_misc=&request_id=&biz_id=102&utm_term=%E8%B7%A8%E6%97%B6%E9%92%9F%E5%9F%9F&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-82956033.first_rank_v2_pc_rank_v29&spm=1018.2226.3001.4187

FPGA——浅谈跨时钟域_第1张图片  

         此时Q1的采集就会有很大概率出现亚稳态的问题,但在下个时钟的上升沿就可以采集到一个确定的数值了,此时就可以很大程度上解决亚稳态的问题。顺便记录一个跨时钟域的使能信号的传输方法,具体代码如下。

reg  R_data_en    ;
reg  R_data_en_1  ;
reg  R_data_en_2  ;
wire W_data_en    ;//125mhz下的使能信号

always@(posedge I_clk_125mhz or I_reset_n)
begin
    if (~I_reset_n) begin        
        R_data_en    <= 'b0;
        R_data_en_1  <= 'b0;
        R_data_en_2  <= 'b0;
    end
    else begin
        R_data_en    <= I_data_en  ;//I_data_en 是时钟为10mhz下的使能信号
        R_data_en_1  <= R_data_en  ;
        R_data_en_2  <= R_data_en_1;
    end
end

assign W_data_en = R_data_en_1 & (!R_data_en);  

         时序图如下所示,红色表示亚稳态采集的数据是个不确定的值。一般来说,数据延时一个时钟后,R_data_en基本可以满足R_data_en1的建立时间和保持时间的要求。

FPGA——浅谈跨时钟域_第2张图片

快时钟域到慢时钟域

        而后就是单bit数据快时钟域到慢时钟域的跨时钟域,慢时钟域想采集到快时钟域的数据是非常困难的,因此我们需要对数据进行展宽。 

        单bit数据大多数都是使能信号或者标志类信号,此时只需要将信号展宽到慢时钟域能采集到的长度即可,一般来说要超过慢时钟域的一个时钟周期长度。如下图所示,如果是第一种情况,慢时钟很明显可以采集的到,但如果是第二种情况,在慢时钟的上升沿,就采集不到输入信号的变化了。

FPGA——浅谈跨时钟域_第3张图片

        展宽的方式在网上看到许多种 ,比较常用的是结绳法,反馈法。具体参照以下博客。
单bit控制信号的跨时钟域传输_拉钩上吊一百年的博客-CSDN博客icon-default.png?t=LA92https://blog.csdn.net/qq_42322644/article/details/115725735?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%8D%95bit%20%E8%B7%A8%E6%97%B6%E9%92%9F%E5%9F%9F&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-9-115725735.first_rank_v2_pc_rank_v29&spm=1018.2226.3001.4187

多bit跨时钟域        

        多bit数据的跨时钟域常用方法之一是使用异步FIFO( First Input First Output),在vivado中有许多的IP核可供使用,其中的FIFO IP核就是非常常用的IP核,FIFO的基本使用方法如以下博客。

Vivado IP核fifo使用指南_Kevin-CSDN博客https://blog.csdn.net/baidu_25816669/article/details/88941458?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163806879416780261961570%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163806879416780261961570&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-88941458.first_rank_v2_pc_rank_v29&utm_term=FIFO+ip&spm=1018.2226.3001.4187        需要注意的几个点是,如果FIFO IP的读写位宽不一致的话,都是先 出/入 的在高位,后出/入 在低位。

        跨时钟域有两种实现方案,第一种是边读边写,第二种是分时读写。

1、边读边写:

        比如读时钟为100MHz,写时钟为10MHz,便可以让读数据一方进行计数,每计数10个时钟便读出一个数据,反过来,若写时钟为100MHz,读时钟为10MHz,便10个时钟写一个数据。

2、分时读写

        分时读写,其实本质就是乒乓操作

FPGA重要的设计思想——乒乓操作_dongdongnihao_的博客-CSDN博客_乒乓操作icon-default.png?t=LA92https://blog.csdn.net/dongdongnihao_/article/details/80037171?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163808144916780357220465%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163808144916780357220465&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-4-80037171.first_rank_v2_pc_rank_v29&utm_term=%E4%B9%92%E4%B9%93%E6%93%8D%E4%BD%9C&spm=1018.2226.3001.4187        基本原理就是有两个存储单元,比较常用的存储单元为双口RAM(Dual RAM),SRAM,SDRAM,FIFO等。第一个存储周期对第一个存储单元进行写操作,第二个存储周期对第二个存储单元进行写操作,并与此同时对第一个存储单元进行读操作。第三个存储周期对第一个存储单元进行写操作,并对第二个存储单元进行读操作,循环往复。

乒乓操作的优点:

        通过“输入数据选择单元”和“输出数据选择单元”按节拍、相互配合的切换,将经过缓冲的数据流没有停顿地送到“数据流运算处理模块”进行运算与处理。把乒乓操作模块当做一个整体,站在这个模块的两端看数据,输入数据流和输出数据流都是连续不断的,没有任何停顿,因此非常适合对数据流进行流水线式处理。所以乒乓操作常常应用于流水线式算法,完成数据的无缝缓冲与处理。

        当写数据的时钟远慢于读时钟,可以只使用一个存储单元,例如写时钟为10MHz,读时钟为100MHz,每当写入10个数据时,读已经可以读出100个数据了,此时就可以使用一个深度为128的FIFO,读写双方各自计数。我们保持写操作持续不断,每当写数据计数到100时,便开始读数据100个,当读完后便等待下一次写数据计数到100,此时最多同时占用110的深度,仍还有18个空余位置,所以读写操作不会产生冲突导致写数据时产生拥塞,导致数据丢失的问题,其实就等同于乒乓操作中的两个存储单元在同一FIFO中。

如有错漏,后续补充

你可能感兴趣的:(FPGA基础,嵌入式硬件,verilog,fifo)