异步dcfifo的读写

异步DCFIFO原理及应用

异步dcfifo的原理

    Dcfifo即是Double clk fifo,意思是双时钟的fifo。或许你现在还不知道什么是fifo,那我就先从fifo(就是同步fifo,不过同步fifo在实际运用中比较少)开始说起吧!

scfifo的原理

一般的fifo是单时钟(读写同步),就是读写用同一个时钟信号,其框图如下:

端口说明如下:

    官方的DCFIFO资料中有这样一个时序图,如下

    上图是写操作,下图是读操作,通过图可以看到读写的时钟频率是不一样,不过这一点没有关系,这里把读写的时钟假设为相同,就是同步fifo

    写操作中,wrreq信号为写申请信号,在写时钟的上升沿有效,将wrreq置高后就可以往fifo送数据,在写满fifo后并在时钟的上升沿,wrfull写满信号有效,表明fifo已经写满了,不能再往fifo内部写如数据,否则就会数据丢失,写了也是白费。如果要对fifo重新写入数据,这时可以通过fifo的异步清零aclr进行操作,aclr为高电平有效,能立刻将fifo内部的数据全部清零,这样就可以再往fifo写数据。

    读操作中,rdreq信号为读申请信号,在读时钟的上升沿有效,将rdreq置高后就可以从fifo读数据,在读空fifo后并在时钟的上升沿,rdempty读空信号有效,表明fifo已经读空了,不能再从fifo内部读如数据。

    当然,fifo中还有一些可以选择调用的信号,可以根据需要进行调用,这里就不作介绍了。

dcfifo的原理

端口说明如下:

    相信在了解了scfifo之后,已经知道fifo的原理了,dcfifoscfifo的区别就在与读写时钟是否同步。

    

    上图的分析上面已经讲解过了,不同的是,在dcfifo中读写时钟是不一致的,这也是很关键的一点,也是dcfifo运用比较多的一点。

    FPGA设计中经常性地会强调"同步"的问题,假设在A模块中采用的时钟频率是100M,在B模块中采用的时钟频率是50M,如果AB要进行数据交换,由于两者的时钟不一样,所以就存在一个同步性的问题,一保证两者数据交换不会出现错误。要解决这个问题,就是解决跨时域操作的问题,跨时域交换数据最常用的就是采用dcfifo做为中间桥梁。假设为A模块往B模块送数据,则A模块以100M的时钟作为dcfifowrclk,往fifo写数据,而B模块则以50M的时钟频率作为dcfifordclk,从fifo读数据,这就解决了跨时域交换数据的问题,当然这里面还有一些细节问题,不过相信你已经知道dcfifo的最大用处了。

异步dcfifo的调用过程

点击tools,选择调用宏功能模块的菜单,弹出下面的菜单;

选择新建一个新的宏功能模块,点击next,弹出下面的菜单;

memory complier中选择FIFO,然后设置输出文件的名称也是这个宏功能模块的名称,然后弹出FIFO的设置向导窗口;

设置fifo的位宽、深度、同步和异步的选择,这里选择如上图所示,选择后点击next

fifo的潜伏期设置,这里选择最低的潜伏期,点击next

选择fifo的接口信号,这里选择写端口的usedw、异步清零信号aclr,点击next

选择fifo的模式,这里选择正常模式,存储资源的类型选择M4K,点击next

点击Finish,调用fifo完成,接下来就是设计模块,将这个dcfifo调用起来;

 

读写dcfifo的控制模块设计

模块框图

    该设计主要分为4个模块和PLL倍頻模块(提供100M的工作时钟),其中,fifo_ctrl为控制模块,负责控制对dcfifoasy_fifo)进行读写操作,与write_fiforead_fifo采用反馈的方式进行控制,具体方法为先控制write_fifoasy_fifo进行写操作,write_fifo写如完成后,向fifo_ctrl反馈一个写入完成信号,fifo_ctrl便关闭对fifo的写操作,向read_fifo发送读使能的信号,read_fifo便在控制信号的控制下对fifo进行读操作,将读出的数据显示在LED上,读完成后便发送读完成信号给fifo_ctrlfifo_ctrl在判断读操作完成后便重新对fifo进行写操作,以此循环。

    其中,对asy_fifo的写时钟为100M(由PLL提供),读asy_fifo的时钟为50M(由外部晶振提供)。

各模块设计原理

    这里采用至上而下的方法对整个工程进行设计:

顶层模块

顶层设计如下

    

fifo控制模块

    fifo的控制模块根据读写的反馈信号,进行控制状态的跳转,同时输出控制信号。

    上电后,控制状态为空闲态,接着转入到写状态,同时将现态的信号作为控制信号输出到write_fiforead_fifo,在输出写控制信号后便对从write_fifo反馈的写入数量的信号进行判断,当写入了256个数据之后,便转到读状态,同时将读控制信号发送到read_fifo模块,read_fifo模块接收到读信号之后便进行读操作,读完成后便反馈读完成信号read_over,当read_over有效后,控制模块的控制状态便转到空闲态,以此循环。

fifo写模块

    fifo写模块负责对fifo进行写操作,实际点就是发送写申请信号和发送数据,关键是什么时候进行写申请和什么时候发送什么数据,这就要听从fifo_ctrlctrl_signal信号,即是对ctrl_signal进行译码操作,配合着输出对fifo的写信号wrreq

    

    将输入的100Mclk直接输入到fifo的读时钟,将wrusedwfifo的当前存储数量)输出到wrdata_num反馈给fifo_ctrl

    ctrl_signalWRITE时,写申请信号wrreq_reg有效,同时用wrdata_cnt对写入的数据数进行计数,当wrdata_cnt等于255时,fifo_ctrl将控制信号转为读状态,这时ctrl_signalREADwrreq_reg被清零,写操作停止,进入读操作阶段。

fifo读模块

fifo读模块负责对fifo进行读操作,实际点就是发送读申请信号和读取数据,关键是什么时候进行读申请和什么时候接收什么数据,这就要听从fifo_ctrlctrl_signal信号,即是对ctrl_signal进行译码操作,配合着输出对fifo的读申请信号rdreq

 

    读申请信号产生模块,这个模块根据ctrl_signal信号对fifo进行读申请,在READ状态时读申请信号rdreq_reg有效,其他状态无效。

读完成信号产生模块,在ctrl_signalREAD时开始计数,判断读取的次数是否达到256次,当达到256次时read_over输出高电平,同时read_over反馈到fifo_ctrl模块,这是current_state跳转到IDLE状态,然后进行新一轮的写读操作。

实验效果

    实验的工程文件为"asy_fifo",其源代码文件如下:

    为了验证实验结果,实验中调用signaltap将从fifo中读取的数据调出来进行观察,

结果如下:

 

大西瓜FPGA-->https://daxiguafpga.taobao.com

 

博客资料、代码、图片、文字等属大西瓜FPGA所有,切勿用于商业! 若引用资料、代码、图片、文字等等请注明出处,谢谢!

 

每日推送不同科技解读,原创深耕解读当下科技,敬请关注微信公众号“科乎”。

转载于:https://www.cnblogs.com/logic3/p/5303563.html

<think>嗯,用户询问的是MAX10 FPGA中异步FIFO的实现方法。从引用内容看,用户可能正在做数字IC设计或FPGA开发,遇到了时钟域交叉问题。用户提供的参考资料涉及异步FIFO的常见问题、格雷码转换、空满判断等关键技术点,说明用户对基础原理已有一定了解,现在需要具体的MAX10实现方案。 用户引用的资料[^2][^3][^5]特别有价值,其中提到格雷码同步、亚稳态处理和时序约束,这些都是异步FIFO的核心难点。结合MAX10的特性,我需要重点说明:1) Quartus中IP核的配置要点;2) 如何规避读写时钟频率差异过大时的风险——用户特别提到华为面试题中读写时钟100倍频差的极端情况[^1],这很关键。 注意到引用[^3]强调set_max_delay约束对格雷码同步的重要性,在MAX10实现时必须考虑这点。另外用户提供的Verilog代码片段[^5]展示了二进制转格雷码的简洁实现,可以在自定义方案中直接参考。 用户应该需要实操指导,所以考虑给出两种实现路径:IP核快速方案和自定义代码方案。对于极端频差场景,需要强调深度计算和格雷码同步的局限性——当读写时钟差异超过100倍时,单纯格雷码可能不够,可能需要结合握手或脉冲同步机制,这点在引用[^2]的问题(4)中也有提及。 最后用户的应用场景可能是数据缓冲[^4],所以实现时要考虑实际带宽需求。技术文档的整理会按逻辑递进:先基础原理,再具体实现,最后极端情况处理,这样比较符合工程师的思考习惯。</think>以下是MAX10 FPGA中异步FIFO的实现方法及技术要点,结合官方文档和工程实践整理: --- ### **一、异步FIFO核心实现原理** 1. **双时钟域结构** - 写时钟域(`wr_clk`):控制数据写入和写指针更新 - 读时钟域(`rd_clk`):控制数据读出和读指针更新 - 指针同步采用格雷码(Gray Code)避免亚稳态[^2][^5]: $$ \text{格雷码转换:} gary\_out[i] = binary\_in[i] \oplus binary\_in[i+1] \quad (0 \leq i < n-1) $$ 2. **空满标志生成** - **写满条件**:`wr_ptr_gray`同步到读时钟域后,与`rd_ptr_gray`高位不同、其余位相同 $$ \text{写满:} wr\_ptr\_gray == \{ \sim rd\_ptr\_gray[PTR], rd\_ptr\_gray[PTR-1:0] \} $$ - **读空条件**:`rd_ptr_gray`同步到写时钟域后与`wr_ptr_gray`完全相等[^2][^4] --- ### **二、MAX10 FPGA专用实现方案** #### **方案1:使用Quartus IP核(推荐)** 1. **调用步骤** - 在Quartus Prime中打开IP Catalog → 搜索"FIFO" → 选择`Scfifo`(单时钟)或`Dcfifo`(双时钟异步) - 关键参数配置: ```verilog FIFO_DEPTH = 1024; // 深度建议为2^N DATA_WIDTH = 32; // 数据位宽 CLOCKS_ARE = "INDEPENDENT"; // 异步模式 USE_ALMOST_EMPTY = 1; // 可选几乎空/满信号 ``` 2. **时序约束** 对跨时钟域的格雷码指针添加约束(避免同步失败): ```tcl set_max_delay -from [get_pins {gray_sync_reg[*]}] -to [get_pins {gray_sync_reg[*]}] 2.0ns # 约束同步链延迟[^3] ``` #### **方案2:自定义RTL实现(Verilog)** ```verilog module async_fifo #(parameter DATA_W = 8, ADDR_W = 4) ( input wr_clk, rd_clk, wr_en, rd_en, input [DATA_W-1:0] din, output [DATA_W-1:0] dout, output full, empty ); // 1. 双端口RAM reg [DATA_W-1:0] mem[(1<<ADDR_W)-1:0]; // 2. 指针生成(二进制+格雷码) reg [ADDR_W:0] wr_ptr_bin, rd_ptr_bin; wire [ADDR_W:0] wr_ptr_gray = (wr_ptr_bin >> 1) ^ wr_ptr_bin; // 二进制转格雷码[^5] wire [ADDR_W:0] rd_ptr_gray = (rd_ptr_bin >> 1) ^ rd_ptr_bin; // 3. 跨时钟域同步(两级触发器) reg [ADDR_W:0] wr_ptr_gray_sync[1:0], rd_ptr_gray_sync[1:0]; always @(posedge rd_clk) rd_ptr_gray_sync <= {rd_ptr_gray_sync[0], wr_ptr_gray}; always @(posedge wr_clk) wr_ptr_gray_sync <= {wr_ptr_gray_sync[0], rd_ptr_gray}; // 4. 空满判断 assign full = (wr_ptr_gray == {~rd_ptr_gray_sync[1][ADDR_W], rd_ptr_gray_sync[1][ADDR_W-1:0]}); assign empty = (rd_ptr_gray == wr_ptr_gray_sync[1]); endmodule ``` --- ### **三、极端时钟比(100:1)问题处理** 当读写时钟频率差异极大时(如100:1): 1. **写快读慢场景** - **风险**:写指针快速溢出导致未读数据被覆盖 - **解决方案**: - 增大FIFO深度:$$ \text{最小深度} = \frac{\text{写速率}}{\text{读速率}} \times \text{突发长度} $$ - 添加流控信号(如`almost_full`)提前停止写入[^1] 2. **读快写慢场景** - **风险**:读指针追上写指针导致虚假空信号 - **解决方案**: - 使用`almost_empty`提前停止读取 - 优化格雷码同步策略:采用三级触发器同步降低亚稳态概率[^2] --- ### **四、MAX10资源优化建议** 1. **使用MLAB资源** MAX10的MLAB(Memory Logic Array Block)支持异步FIFO的小深度实现(≤32深度),延迟更低。 2. **功耗控制** 在Quartus设置中启用`AUTO_POWER_UP`选项,使能FIFO的自动低功耗模式。 --- ### **五、验证与调试** 1. **Signaltap抓取关键信号** - 同步前后的格雷码指针差值 - 空满标志跳变时的读写指针状态 2. **覆盖率测试** - 边界用例:FIFO完全满/完全空时连续读写 - 时钟抖动测试:±10%时钟偏移下的稳定性 > 完整设计参考Intel文档《MAX10 FPGA Memory User Guide》第4章,或访问[Intel Support](https://www.intel.com/content/www/us/en/support/programmable.html)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值