前言
fifo有同步和异步之分,同步fifo表示写入的控制时钟和读取的控制时钟为相同的时钟信号;异步fifo表示写入的控制时钟和读取的控制时钟为不同的时钟信号;同步fifo就可以将其作为一个在相同时钟域下缓存器;异步fifo可以用来进行跨时钟域数据信号处理。
异步fifo读写时钟不同,在调动fifo时需要选择双时钟设置,设置完成后,ip核fifo会在后面设置中分别有空满信号,会产生写的空满信号,也会产生读的空满信号,同时也会有观察信号usedw,其中usedw的值表示当前fifo中有多少个数据。
正文
一、IP核FIFO调用及验证(2)
1.项目需求
异步FIFO(先进先出)读写验证实验的主要目的是确保异步FIFO在实际应用中的可靠性和功能正确性。实验需要验证FIFO的基本功能,包括数据的正确存取和FIFO指针的更新。在读写操作中,数据必须能够按照预期的顺序被正确地存入和取出,FIFO的内部指针也需在操作后正确地更新。
需要确保数据在读写过程中没有丢失、重复或被篡改。评估FIFO的时序特性,这包括验证其在不同频率下的时钟边沿对操作的影响。边界条件测试也是关键部分,实验需要检查FIFO在极限状态下(如接近满或空的情况下)的表现,以确保其在各种极端条件下依然能够稳定运行。验证FIFO在处理来自不同时钟域的数据时,是否能够正确地同步和传输数据。
2.技术介绍
异步FIFO(先进先出)的读写逻辑是为了在两个不同时钟域之间可靠地传输数据。由于写操作和读操作使用不同的时钟信号,因此需要特别的逻辑来处理这些异步时钟之间的数据传输。下面是异步FIFO的读写逻辑的基本工作原理:
写操作:当写使能信号有效(通常是高电平)时,写指针会在写时钟信号的上升沿或下降沿更新。数据会被写入FIFO存储区中由写指针指定的位置。写指针在写入数据后会递增,循环回到FIFO的起始位置(即实现循环缓冲)。
读操作:当读使能信号有效时,读指针会在读时钟信号的上升沿或下降沿更新。FIFO存储区中由读指针指定的位置的数据会被读取。读指针在读取数据后会递增,循环回到FIFO的起始位置。
由于FIFO的读写操作使用不同的时钟域,因此在设计中需要考虑时钟域交叉问题,确保数据在不同的时钟域之间能够安全地传输。设计需要保证在读取和写入数据的过程中,FIFO内部的数据不会被损坏,尤其是在时钟域之间的数据同步时。设计中通常会加入额外的同步机制,确保读写指针的同步,避免在不同时钟域中出现数据竞争或丢失问题。
异步FIFO的读写逻辑通过上述机制实现了在不同频率和不同来源的时钟域之间的数据可靠传输,是解决FPGA设计中时钟域交叉问题的一种有效方法。
调用异步FIFO需要勾选不同时钟域下进行读写,设置8位256深度。
不进行时钟同步控制
其他选项保持默认
其他设置保持默认,勾选生成inst.v文件。
3.顶层架构
通过锁相环产生不同时钟,不同时钟分别控制读写控制模块,读写控制模块读取当前FIFO的状态,进而确认其下个工作状态。
4.端口描述
clk | 系统时钟 |
rst_n | 复位按键(低电平有效) |
data[7:0] | 读出数据 |
二、代码验证
rd_fifo_ctrl模块:
module rd_fifo_ctrl(
input clk,
input rst_n,
input empty,//标志信号,指示FIFO是否为空。为空时,无法进行读操作。
input full,//标志信号,指示FIFO是否已满。FIFO满时,无法进行写操作。
output reg rdreq//读请求信号,表示请求从FIFO中读取数据。
);
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
rdreq <= 1'b0;
else
if(full == 1)//FIFO已满
rdreq <= 1'b1;//开始读
else
if(empty == 1)//FIFO为空
rdreq <= 1'b0;//停止读
else
rdreq <= rdreq;
end
endmodule
wr_fifo_ctrl模块:
module wr_fifo_ctrl(
input clk,
input rst_n,
input empty,//标志信号,指示FIFO在写操作时是否为空。
input full,//标志信号,指示FIFO在写操作时是否已满。
output reg wrreq,//写请求信号,表示请求将数据写入FIFO。
output reg [7:0] wr_data//输入信号,将要写入FIFO的数据。
);
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
wrreq <= 1'b0;
else
if(empty == 1)//FIFO为空
wrreq <= 1'b1;//开始写
else
if(full == 1)//FIFO已满
wrreq <= 1'b0;//停止写
else
wrreq <= wrreq;//未满,继续写
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
wr_data <= 8'd0;
else
if(wrreq == 1)//写入数据产生
wr_data <= wr_data + 8'd1;
else
wr_data <= 8'd0;
end
endmodule
asyn_fifo_test模块:
//异步FIFO
module asyn_fifo_test(
input clk,
input rst_n,
output [7:0] data
);
wire wr_clk;
wire rd_clk;
wire locked;
wire wrempty;
wire wrfull;
wire wrreq;
wire [7:0] wr_data;
wire rdempty;
wire rdfull;
wire rdreq;
wire [8:0] wrusedw;
wire [8:0] rdusedw;
my_pll my_pll_inst (
.areset ( ~rst_n ),
.inclk0 ( clk ),
.c0 ( wr_clk ),
.c1 ( rd_clk ),
.locked ( locked )
);
wr_fifo_ctrl wr_fifo_ctrl_inst(
.clk(wr_clk),
.rst_n(locked),
.empty(wrempty),
.full(wrfull),
.wrreq(wrreq),
.wr_data(wr_data)
);
rd_fifo_ctrl rd_fifo_ctrl_inst(
.clk(rd_clk),
.rst_n(locked),
.empty(rdempty),
.full(rdfull),
.rdreq(rdreq)
);
asyn_fifo asyn_fifo_inst (
.data ( wr_data ),//输入信号,将要写入FIFO的数据。
.rdclk ( rd_clk ),//读操作的时钟信号,控制FIFO读操作的时序。
.rdreq ( rdreq ),//读请求信号,表示请求从FIFO中读取数据。
.wrclk ( wr_clk ),//写操作的时钟信号,控制FIFO写操作的时序。
.wrreq ( wrreq ),//写请求信号,表示请求将数据写入FIFO。
.q ( data ),//输出信号,从FIFO中读取的数据。
.rdempty ( rdempty ),//标志信号,指示FIFO是否为空。为空时,无法进行读操作。
.rdfull ( rdfull ),//标志信号,指示FIFO是否已满。FIFO满时,无法进行写操作。
.rdusedw ( rdusedw ),//信号,指示当前FIFO中已被使用的单元数量。
.wrempty ( wrempty ),//标志信号,指示FIFO在写操作时是否为空。
.wrfull ( wrfull ),//标志信号,指示FIFO在写操作时是否已满。
.wrusedw ( wrusedw )//信号,指示当前FIFO中已被写入的单元数量。
);
endmodule
仿真代码
`timescale 1ns/1ps
module fifo_test;
reg clk;
reg rst_n;
wire [7:0] data;
asyn_fifo_test syn_fifo_test_inst(
.clk(clk),
.rst_n(rst_n),
.data(data)
);
initial clk = 1;
always #10 clk = ~clk;
initial begin
rst_n = 0;
#200
rst_n = 1;
#15000
$stop;
end
endmodule
三、仿真验证
开始仿真,观察仿真波形图,有读出数据,调出中间信号进行分析:
先观察写逻辑:empty为FIFO产出信号,为高时表示FIFO写操作时为空,FIFO为空,写逻辑模块产生写使能与写入数据。
在地址写到256时,FIFO写满wrfull拉高,写逻辑模块检测到wrfull拉高后写使能拉低。写操作完毕。此时FIFO以满,rdfull输出为高电平,读读逻辑模块检测到rdfull为高电平后,将读使能拉高,数据读出,先入先出。
数据读完,FIFO为空,拉高empty信号,驱动写逻辑模块进行下次写入操作。