news 2026/8/12 3:54:01

高云FPGA异步FIFO IP核配置与Verilog驱动实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
高云FPGA异步FIFO IP核配置与Verilog驱动实战

高云FPGA异步FIFO IP核配置与Verilog驱动实战

最近在做一个FPGA项目,需要把摄像头采集的数据传给另一个时钟域的图像处理模块,中间的数据缓冲和跨时钟域传输让我头疼了一阵。后来我用了高云FPGA自带的异步FIFO IP核,问题迎刃而解。今天我就把这个从IP核配置到Verilog驱动、再到仿真验证的完整流程分享给大家,手把手教你搞定异步FIFO。

1. 异步FIFO是什么?为什么需要它?

1.1 FIFO基础:数据流的“排队缓冲区”

咱们先打个比方。FIFO就像银行排队叫号系统,先来的人先办业务(先进先出)。在FPGA里,FIFO就是一个数据缓冲区,用来临时存放数据流,解决数据生产者和消费者速度不匹配的问题。

可能有人会问,这和我学单片机时用的堆栈(Stack)有啥区别?我刚开始也混淆过,这里简单说下:

  • FIFO:就像排队,队尾进,队头出。适合处理连续的数据流,比如视频流、网络数据包。在FPGA里,它常用于数据缓冲、流量控制。
  • 堆栈:就像叠盘子,你只能从最上面放和取(后进先出)。单片机里主要用来管理函数调用、保存临时变量。

简单说,FIFO管“流水线”,堆栈管“函数调用”。咱们今天的主角是前者。

一个FIFO有几个关键参数你得知道:

  • 深度:能存多少个数据。好比排队大厅有多少个座位。
  • 宽度:每个数据有多少位(比如8位、16位)。好比每个人手里拿的号牌大小。
  • 空/满标志:告诉你缓冲区是空的(没数据可读)还是满的(没空间可写),防止读错或写爆。
  • 读写指针:内部用来记录读到哪、写到哪的“记号笔”。

1.2 同步FIFO vs 异步FIFO

FIFO按时钟分两种,这是核心区别:

同步FIFO:读写都用同一个时钟。好比整个银行只有一个挂钟,所有人按同一个时间节奏办事。控制简单,但只能用在同一个时钟域。

异步FIFO:读和写用两个独立的时钟。好比取号机和柜台有各自的钟,速度可能不一样。它就是为了解决跨时钟域数据传递而生的,内部有专门的电路(比如双端口RAM、格雷码计数器)来安全地同步读写指针,避免数据出错。

什么时候用异步FIFO?当你FPGA设计里有两个模块,一个在50MHz时钟下产生数据,另一个在75MHz时钟下处理数据,中间就得用异步FIFO来做缓冲和时钟域隔离。图像处理、音频编解码、高速接口(如DDR、PCIe)的数据缓存都离不开它。

2. 高云云源软件里的FIFO IP核

高云FPGA的开发软件(Gowin云源)提供了现成的FIFO IP核,咱们不用从零写,直接配置调用就行,省时省力还可靠。

打开软件,在IP核生成器里你能找到几种FIFO:

  • FIFO:就是咱们要用的异步FIFO(双时钟)。
  • FIFO SC:同步FIFO(单时钟)。
  • FIFO HS / FIFO SC HS:支持更高时钟频率的版本。

双击“FIFO”,就进入了配置界面。别被一堆选项吓到,我带你一个个看关键的。

2.1 核心参数配置

首先给IP核起个名,别用默认的fifo_top,因为顶层模块通常叫top。我这里改成fifo_ip

1. Options(基本选项)

  • Write Depth/Width:写深度和写数据位宽。比如我设深度128,宽度8位(就是能存128个8位数据)。
  • Read Depth/Width:读深度和读数据位宽。注意:异步FIFO支持读写位宽不同!比如你写是8位,读可以设成16位,IP核会自动计算。公式是:Read Data Width = (Write Depth * Write Data Width) / Read Depth。位宽和深度别设太大,会浪费FPGA里宝贵的Block RAM资源。
  • Output Register Select:勾上后,输出数据会延迟一个时钟周期,能改善时序,一般建议勾选。
  • Controlled by RdEn:输出数据是否受读使能控制。勾选后,只有RdEn有效时数据才出现在输出端口。

2. FIFO Implementation(实现方式)就是用FPGA里的哪种存储资源:

  • Block SRAM (BSRAM):块RAM,容量大,适合深度大于1KB的情况。
  • Shadow SRAM (SSRAM):分布式RAM,灵活,适合小容量FIFO(小于1KB)。
  • REG:用寄存器和查找表搭,极浅的FIFO才用。 通常按建议来,小的用SSRAM,大的用BSRAM。

3. Read Mode(读模式)

  • Standard FIFO:标准模式。需要读使能有效,才会把下一个数据输出。
  • First-Word Fall-Through (FWFT):首字直通模式。第一个写入的数据会立刻出现在输出端,不需要等读使能。后续数据才需要读使能控制。这种模式延迟更低。

4. Data Number & Reset(数据计数与复位)

  • Read/Write Data Num:勾选后,IP核会输出WnumRnum信号,告诉你当前FIFO里存了多少个数据(写计数)和还能读多少个数据(读计数),调试时非常有用。
  • En_Reset:使能复位。勾选后,读写时钟域有独立的复位信号WrResetRdReset
  • Reset_Synchronization:如果选了En_Reset,这个选项让你选择是否使用一个统一的复位信号Reset。为了简单,咱们通常选这个。

5. Flag Control(标志位控制)除了基本的空(Empty)/满(Full)标志,还可以使能“几乎满/几乎空”标志,让你提前知道缓冲区快满了或快空了,方便做流控。

  • Almost Full Flag:使能半满标志Almost_Full。阈值可以设成固定值(Constant Parameter)或者通过输入端口动态设置(Input Parameter)。比如我设固定值120,当FIFO里的数据达到120个时,Almost_Full就拉高。
  • Almost Empty Flag:同理,使能半空标志Almost_Empty。我设固定值10,当FIFO里数据少于等于10个时,Almost_Empty拉高。

其他如ECC校验,一般数据位宽1-64位时可选,用于检错纠错,根据需求来。

配置完点击Generate,软件就会生成一个可用的FIFO模块(.v文件)和对应的例化模板。

3. 手把手编写Verilog驱动模块

IP核生成好了,但它只是个“黑盒子”。我们需要写控制逻辑来告诉它什么时候写、什么时候读。咱们把读写控制分开成两个模块,结构更清晰。

3.1 写控制模块 (fifo_wr.v)

这个模块负责产生写使能和要写入的数据。逻辑是:当FIFO为空时,开始写入;当写计数达到我们设定的最大值(比如128)时,停止写入。

module fifo_wr( input wr_clk , // 写时钟信号 input wr_rst_n , // 复位信号,低电平有效 input [7:0] fifo_Wnum_count , // 从IP核来的写计数(当前FIFO中数据量) input fifo_empty , // 从IP核来的空标志 output reg fifo_wr_en , // 输出给IP核的写使能 output reg [6:0] fifo_wr_data // 输出给IP核的写入数据 ); parameter WR_DATA_MAX = 7'd127; // 我们要写入的最大数据值(0-127) // 写使能控制逻辑 always @(posedge wr_clk or negedge wr_rst_n) begin if(!wr_rst_n) fifo_wr_en <= 1'b0; // 复位时关闭写使能 else if(fifo_empty) // 如果FIFO空了,就启动写入 fifo_wr_en <= 1'b1; else if(fifo_Wnum_count == 8'd128) // 如果已经写满了128个,就停止 fifo_wr_en <= 1'b0; else fifo_wr_en <= fifo_wr_en; // 其他情况保持原状态 end // 写入数据生成逻辑:简单地从0递增到127 always @(posedge wr_clk or negedge wr_rst_n) begin if(!wr_rst_n) fifo_wr_data <= 7'd0; else if(fifo_wr_en && (fifo_wr_data < WR_DATA_MAX)) fifo_wr_data <= fifo_wr_data + 7'd1; // 使能有效且未到最大值,数据加1 else fifo_wr_data <= 7'd0; // 否则(如写停止或复位),数据归零 end endmodule

注意:这里fifo_Wnum_count是8位,最大值是255。我们设定的FIFO深度是128,所以当计数到128时表示真的满了。fifo_wr_data是7位,范围0-127,这是我们实际写入的数据内容。

3.2 读控制模块 (fifo_rd.v)

这个模块负责产生读使能。逻辑是:当FIFO为满时,开始读取;当可读数据量快被读空(比如只剩1个)时,停止读取,防止读空。

module fifo_rd( input rd_clk , // 读时钟信号 input rd_rst_n , // 复位信号 input [7:0] fifo_Rnum_count , // 从IP核来的读计数(当前可读数据量) input fifo_full , // 从IP核来的满标志 output reg fifo_rd_en // 输出给IP核的读使能 ); parameter RD_EMPTY_TH = 8'd1; // 读空阈值,当可读数据小于等于1时停读 always @(posedge rd_clk or negedge rd_rst_n) begin if(!rd_rst_n) fifo_rd_en <= 1'b0; else if(fifo_full) // FIFO满了,赶紧开始读 fifo_rd_en <= 1'b1; else if(fifo_Rnum_count <= RD_EMPTY_TH) // 快读空了,停 fifo_rd_en <= 1'b0; else fifo_rd_en <= fifo_rd_en; // 保持 end endmodule

3.3 顶层模块整合 (fifo_top.v)

现在我们把IP核和两个控制模块“连线”起来。这个顶层模块就是整个设计的“总指挥部”。

module fifo_top( input sys_clk, // 系统时钟(这里为了简化,读写用同一时钟,实际可接不同时钟) input sys_rst_n, // 系统复位,低电平有效 output [6:0] fifo_rd_data // FIFO读出的数据,引到顶层输出便于观察 ); // 内部连线声明 wire fifo_wr_en; wire fifo_rd_en; wire [6:0] fifo_wr_data; wire almost_full; wire almost_empty; wire fifo_full; wire fifo_empty; wire [7:0] fifo_Wnum_count; wire [7:0] fifo_Rnum_count; // 例化FIFO IP核 fifo_ip u_fifo_ip ( .Data (fifo_wr_data), // input [6:0] .Reset (~sys_rst_n), // input, 注意IP核复位是高有效,我们系统复位是低有效,所以取反 .WrClk (sys_clk), // input .RdClk (sys_clk), // input, 实际异步应用时这里接不同的时钟 .WrEn (fifo_wr_en), // input .RdEn (fifo_rd_en), // input .Wnum (fifo_Wnum_count), // output [7:0] .Rnum (fifo_Rnum_count), // output [7:0] .Almost_Empty (almost_empty), // output .Almost_Full (almost_full), // output .Q (fifo_rd_data), // output [6:0] .Empty (fifo_empty), // output .Full (fifo_full) // output ); // 例化写控制模块 fifo_wr u_fifo_wr ( .wr_clk (sys_clk), .wr_rst_n (sys_rst_n), .fifo_Wnum_count (fifo_Wnum_count), .fifo_empty (fifo_empty), .fifo_wr_en (fifo_wr_en), .fifo_wr_data (fifo_wr_data) ); // 例化读控制模块 fifo_rd u_fifo_rd ( .rd_clk (sys_clk), .rd_rst_n (sys_rst_n), .fifo_Rnum_count (fifo_Rnum_count), .fifo_full (fifo_full), .fifo_rd_en (fifo_rd_en) ); endmodule

提示:在这个例子里,为了仿真方便,读写时钟都接了sys_clk在实际的异步FIFO应用中,WrClkRdClk必须接两个不同源或不同频率的时钟信号,这才是异步FIFO的价值所在。

4. Modelsim仿真验证

代码写完了,对不对?上仿真!用Modelsim跑一下最放心。这里有个高云FPGA仿真的要特别注意:如果设计里用了IP核或者一些底层原语,必须在仿真测试文件里例化一个GSR单元,否则仿真会报错。

`timescale 1ns / 1ps // 定义仿真时间单位/精度 module fifo_tb(); // ---- 关键!必须添加的GSR实例化 ---- GSR GSR_inst (.GSRI(1'b1)); // --------------------------------- // 产生测试时钟和复位 reg sys_clk; reg sys_rst_n; always #10 sys_clk = ~sys_clk; // 产生50MHz时钟(周期20ns) initial begin sys_clk = 1'b0; sys_rst_n = 1'b0; // 开始复位 #200; // 复位保持200ns sys_rst_n = 1'b1; // 释放复位 #5000; // 仿真运行5000ns $stop; // 结束仿真 end // 连接被测设计 wire [6:0] rd_data; fifo_top u_fifo_top ( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n), .fifo_rd_data (rd_data) ); endmodule

把上面这个测试文件、以及我们写的三个模块(fifo_top.v,fifo_wr.v,fifo_rd.v)和IP核生成的fifo_ip.v一起加到Modelsim工程里编译仿真。

看波形关键点:

  1. 复位后fifo_empty应为高,fifo_full为低,写计数和读计数为0。
  2. 启动写入fifo_empty变低后,fifo_wr_en拉高,fifo_wr_data从0开始递增。同时fifo_Wnum_count(写计数)增加。
  3. 触发半满:当fifo_Wnum_count增加到我们设定的阈值120时,almost_full信号应该拉高。这说明FIFO快满了。
  4. 写满停写:当fifo_Wnum_count达到128(深度)时,fifo_full拉高,同时我们的写控制模块应关闭fifo_wr_en
  5. 启动读取fifo_full拉高后,fifo_rd_en应拉高,开始读出数据。fifo_Rnum_count(读计数)减少,fifo_rd_data输出数据。
  6. 触发半空:当fifo_Rnum_count减少到我们设定的阈值10时,almost_empty拉高。
  7. 读空停读:当fifo_Rnum_count为0时,fifo_empty拉高,读使能fifo_rd_en关闭。

波形如果完全按这个顺序变化,恭喜你,仿真通过了!

5. 上板实测与在线调试

仿真通过后,就可以综合、布局布线,生成比特流下载到高云FPGA开发板了。

引脚约束:根据你的板子,把顶层模块的时钟(sys_clk)和复位(sys_rst_n)信号分配到实际的晶振引脚和按键引脚上。输出数据fifo_rd_data可以接到LED或者用在线逻辑分析仪看。

高云在线逻辑分析仪(GAO):这是调试利器。把fifo_wr_en,fifo_rd_en,fifo_wr_data,fifo_rd_data,almost_full,almost_empty,full,empty这些关键信号加进去观察。

上板抓到的波形应该和仿真一致。可能会看到数据在almost_fullalmost_empty阈值附近有些毛刺或抖动,这是正常的,因为在线采样和时钟域不同步。只要主要逻辑正确就没问题。

6. 实际项目中的几点心得

  1. 深度计算要留余量:别可丁可卯地设深度。比如你计算出来需要缓存100个数据,最好设成128或256,防止偶尔的数据突发导致溢出。
  2. 异步时钟要约束:如果读写真是异步时钟,一定要在设计约束文件里定义这两个时钟,并设置set_clock_groups -asynchronous来告诉工具它们是异步的,避免工具做无谓的时序优化。
  3. 标志位延迟emptyfull标志的产生有延迟(通常1-2个读时钟或写时钟周期)。所以你的控制逻辑不能看到full才停写,看到empty才停读,要提前(用almost_full/empty)或留出安全余量。
  4. 复位处理:异步FIFO的复位需要特别小心,确保复位信号在各自时钟域下被正确同步和释放。用IP核的话,它内部一般会处理好。

好了,从IP核配置、Verilog驱动到仿真上板的完整流程就是这样。异步FIFO是FPGA跨时钟域通信的基石,把这个练熟了,后面做图像、通信项目就顺手多了。遇到问题多看看波形,理解数据流和标志位的变化,调试起来就不难了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/12 3:51:09

抖音无水印视频高效采集:零基础掌握的零成本解决方案

抖音无水印视频高效采集&#xff1a;零基础掌握的零成本解决方案 【免费下载链接】douyin-downloader 项目地址: https://gitcode.com/GitHub_Trending/do/douyin-downloader 在数字内容创作爆炸的时代&#xff0c;如何高效获取抖音平台的无水印视频成为内容创作者、企…

作者头像 李华
网站建设 2026/8/12 3:51:08

Qwen3-TTS语音合成实战:文本预处理与音色选择技巧

Qwen3-TTS语音合成实战&#xff1a;文本预处理与音色选择技巧 1. 引言&#xff1a;为什么需要关注文本预处理与音色选择 语音合成技术已经发展到令人惊叹的水平&#xff0c;但很多用户在实际使用中仍然会遇到合成效果不理想的情况。这往往不是因为模型能力不足&#xff0c;而…

作者头像 李华
网站建设 2026/7/14 15:42:30

机器学习进阶:惩罚函数如何优化模型性能

1. 惩罚函数&#xff1a;模型优化的秘密武器 第一次听说"惩罚函数"这个词时&#xff0c;我脑海中浮现的是小时候做错事被老师罚站的场景。但机器学习中的惩罚函数可没那么简单粗暴&#xff0c;它更像是位严格的教练&#xff0c;在训练过程中不断纠正模型的坏习惯。想…

作者头像 李华
网站建设 2026/7/14 15:42:47

1.14 梁山派GD32F470驱动4.0寸ILI9488彩屏:16位并口移植与引脚配置详解

梁山派GD32F470驱动4.0寸ILI9488彩屏&#xff1a;16位并口移植与引脚配置详解 最近在梁山派GD32F470开发板上做项目&#xff0c;需要用到一块4.0寸的彩色液晶屏。这块屏用的是ILI9488驱动芯片&#xff0c;通信方式是16位并口。网上找的例程大多是针对STM32或者别的开发板的&…

作者头像 李华
网站建设 2026/7/14 15:42:34

数据可视化新宠:旭日图在企业财务分析中的5个高级技巧

数据可视化新宠&#xff1a;旭日图在企业财务分析中的5个高级技巧 财务数据从来都不是孤立存在的数字堆砌&#xff0c;而是由无数层级关系编织而成的复杂网络。想象一下&#xff0c;当你面对一份包含集团总部、各子公司、部门、项目组四级结构的年度预算报表时&#xff0c;传统…

作者头像 李华