同步与异步FIFO IP核功能测试详解
FIFO原理、分类与FPGA实现测试
摘要:本文系统介绍了 FIFO(First In First Out)的基本原理、与 ROM/RAM 的区别、分类方式,并通过 Quartus 调用实例和 Verilog 代码演示了同步与异步 FIFO 的功能测试。文章涵盖 FIFO 的环状存储特性、握手信号机制、时钟分类(同步/异步)、工作模式(标准/先出),并提供了完整的测试代码与仿真波形分析,适合 FPGA 初学者和数字电路设计者参考。
目录
- 一、FIFO 简介
- 1.1 与 ROM 和 RAM 的区别
- 1.2 基本单元
- 1.3 基本应用
- 二、FIFO 的分类
- 2.1 按时钟分类
- 2.2 按工作模式分类
- 三、FIFO 的调用
- 四、FIFO 的功能测试
- 4.1 同步FIFO测试
- 4.2 异步FIFO测试
一、FIFO简介
1.1与ROM和RAM的区别
- FIFO(First In First Out 先进先出)没有地址信号,依赖于读写指针,类似环状存储,而 ROM 和 RAM 类似于管状存储,需要依赖地址信号。
- FIFO 在读出数据的时候读的是数据本身所以读出数据之后,数据会消失,但是 RAM 因为是随机存取存储器,读出的只是数据的映射,所以数据依然保留。
- FIFO 的靠的是指针的偏移去指向存储空间,有数据量信号,因为每隔一个存储空间做一次切换与格雷码相似,所以存储空间的编码一般采用格雷码。
- FIFO 具有很多的握手信号
1.2基本单元
- 构成资源:LUT + M9k + D触发器(不同系列芯片的构成不同)
1.3 基本应用
- 连续数据、跨时钟域传输。
二、FIFO的分类
2.1 按时钟分类
- 同步FIFO(单时钟)
- 异步 FIFO(双时钟,读写各用一个时钟),双时钟又分为普通双时钟和异步双时钟
2.2 按工作模式分类
- 标准模式(读使能打开之后,经过一个时钟周期读出数据)
- 先出模式(先准备好数据,等读使能打开之后,更新数据)
三、FIFO 的调用(这里以同步为例,异步的调用大家可以参考同步去理解之后再尝试)
快捷键 Ctrl+7 调出 IP 搜索栏,搜索 FIFO 点开
选择你的 HDL 语言以及 IP 核文件存放位置
由于我们选择的是同步 FIFO 所以不能调整(读)输出的位宽,在异步 FIFO 里可以进行修改,本次异步测试使用的是 50MHz 和 100MHz 的时钟,若输入 16 位宽,输出 8 位宽,则可以在理论上形成读写动态平衡,同时读写的速率会一致。
* 勾选配置 FIFO 各类可选状态控制信号(握手信号)与清零模式,可根据需求勾选
后面因为目前还没涉及到,所以都默认点击next,到最后一页选择inst仿真例化文件后finish就完成调用了
四、FIFO 的功能测试
4.1 同步FIFO测试
- 测试代码
//同步FIFO module top ( input wire clk , input wire rst_n ); //fifo:8*256 //写 reg [7:0] data ;//输入数据 reg wrreq ;//写使能 //读 reg rdreq ;//读使能 wire [7:0] q ;//读出的数据 //握手信号 wire almost_empty ;//将空信号 wire almost_full ;//将满信号 wire empty ;//空信号,读写指针在同一圈重合(出现空溢出,数据断裂) wire full ;//满信号,写指针超过读指针一圈且重合(出现满溢出,数据丢失) wire [7:0] usedw ;//数据量 reg [9:0] cnt ;//0--1023计数器 //生成数据以及使能信号的转变 //0-1023 always @(posedge clk or negedge rst_n) if(rst_n == 0) cnt <= 0; else cnt <= cnt + 1;//因为位宽限制这里就不用再写清零条件了 //写 always @(posedge clk or negedge rst_n) if(rst_n == 0)begin wrreq <= 0; data <= 0; end else if(cnt < 512)begin wrreq <= 1; data <= data + 1;//前512个周期开始写数据 end else begin wrreq <= 0; data <= 0; end //读 always @(posedge clk or negedge rst_n) if(rst_n == 0) rdreq <= 0; else if(cnt >= 512) rdreq <= 1; else rdreq <= 0; fifo fifo_inst ( .aclr ( !rst_n ), .clock ( clk ), .data ( data ), .rdreq ( rdreq ), .wrreq ( wrreq ), .almost_empty ( almost_empty ), .almost_full ( almost_full ), .empty ( empty ), .full ( full ), .q ( q ), .usedw ( usedw ) ); endmodule- 仿真代码
`timescale 1ns/1ps module tb (); reg clk ; reg rst_n ; initial begin clk = 0; rst_n = 0; #20 rst_n = 1; #100000 $stop ; end always #10 clk = ~clk ;//产生 50MHz 时钟信号 top top_inst( . clk (clk ), . rst_n (rst_n) ); endmodule- 测试仿真波形
4.2 异步FIFO测试
- 测试代码
//异步FIFO module top ( input wire clk , input wire rst_n ); //pll :两路时钟 wire clk_50 , clk_100 ; //FIFO:8*256 //写 reg [7:0] data ; //输入数据 reg wrreq ; //写使能 //读 reg rdreq ; //读使能 wire [7:0] q ; //读出的数据 // 握手信号 wire rdempty; //读空信号 wire rdfull ; //读满信号 wire wrempty; //写空信号 wire wrfull ; //写满信号 // 数据量 wire [7:0] wrusedw; //写数据量 wire [7:0] rdusedw; //读数据量 // 与同步 FIFO 的代码同理 reg [9:0] cnt ; //0--1023计数器 //生成数据以及使能信号的转变 //0-1023 always @(posedge clk_50 or negedge rst_n) if(rst_n == 0) cnt <= 0; else cnt <= cnt + 1; //因为位宽限制这里就不用再写清零条件了 //写 always @(posedge clk_50 or negedge rst_n) if(rst_n == 0)begin wrreq <= 0; data <= 0; end else if(cnt < 512)begin wrreq <= 1; data <= data + 1; //前512个周期开始写数据 end else begin wrreq <= 0; data <= 0; end //读 always @(posedge clk_100 or negedge rst_n) if(rst_n == 0) rdreq <= 0; else if(cnt >= 512) rdreq <= 1; else rdreq <= 0; clock clock_inst ( .areset ( !rst_n ), .inclk0 ( clk ), .c0 ( clk_50 ), .c1 ( clk_100 ) ); dfifo dfifo_inst ( .aclr ( !rst_n ), .data ( data ), .rdclk ( clk_100 ), .rdreq ( rdreq ), .wrclk ( clk_50 ), .wrreq ( wrreq ), .q ( q ), .rdempty ( rdempty ), .rdfull ( rdfull ), .rdusedw ( rdusedw ), .wrempty ( wrempty ), .wrfull ( wrfull ), .wrusedw ( wrusedw ) ); endmodule- 仿真代码与同步fifo一致
- 测试仿真波形(可以看到大致规律和同步差不多,波形也很整齐漂亮,大家可以修改测试代码查看在不同条件下的波形,如上文所说产生的动态平衡)