FPGA知识基础之--FIFO ip核的使用以及实例化clocking wizard ip产生一个异步FIFO,附RTL和仿真代码


一、FIFO简介

1.定义

FIFO是一种先进先出的数据缓存队列,主要特点是数据顺序写入,再按照同样的顺序输出数据,即先进去的数据先被取出来。

2.特点

  • 无需外部读写地址线:FIFO的使用非常简单,因为它不需要外部读写地址线,数据会自动按照先进先出的顺序进行存取。
  • 数据顺序性:FIFO保证了数据的顺序性,即先写入的数据会先被读取出来。
  • 缓存作用:FIFO可以作为数据缓存,当数据源和数据接收端的速度不一致时,FIFO可以起到缓冲数据的作用,避免数据丢失或溢出。

3.分类

根据FIFO输入时钟的区别,可以分为同步FIFO和异步FIFO:

同步FIFO:只有一个独立的时钟端口clock,所有的输入输出信号都同步于clock信号。
异步FIFO:有两个时钟,写端口和读端口分别有独立的时钟,所有写相关的信号都属于写时钟,所有与读相关的信号都属于读时钟。

4.FIFO在FPGA中的应用

FIFO在FPGA开发中的应用非常广泛,主要包括以下几个方面:

  • 数据传输同步:在数据传输过程中,由于不同的设备或处理器的时钟频率可能不同,因此需要进行同步处理。FIFO作为一种缓冲元件,可以将异步数据进行存储,并在同步时按照先进先出的顺序输出,确保数据的正确传输。
  • 网络协议转换:在计算机网络中,不同的协议可能具有不同的数据包格式和传输速率。使用FIFO可以将一种协议的数据包转换为另一种协议的数据包,实现协议转换。
  • 数据压缩与信号处理:在处理大量数据时,FIFO可以用于存储待压缩的数据,并控制数据的压缩顺序。在信号处理中,FIFO可以用于缓存信号数据,并对数据进行预处理和后处理。
  • 并行数据处理:在并行数据处理中,多个处理器或线程同时对数据进行处理。FIFO可以用于协调不同处理器之间的数据传输,并确保数据的顺序和完整性。

二、实验任务

生成一个异步FIFO,当FIFO为空时,向FIFO写入数据,直到将FIFO写满后停止写操作,当FIFO为满时,从FIFO中读数据,直到FIFO被读空后停止读操作。将FIFO的深度和宽度分别设置为256和8进行读写测试

三、FIFO IP核

笔者参考了xlinx的pg_057参考文档,针对于本博客需要的知识来向读者进行分享,详细说明请参考xlinx提供的官方文档。

1.接口

在这里插入图片描述
xlinx提供的IP核有如下接口,笔者标注了每个接口信号的基本功能

2.写时序

在这里插入图片描述

3.读时序

需要注意的是,在FIFO中读有两种模式,Standard模式和**First-Word Fall-Through(FWFT)**模式,关于这两种模式的区分,xlinx文档已经给出了很详尽的解释

在这里插入图片描述
笔者将通过时序图来向大家简单介绍一下这两种模式的区分

1.Standara

在这里插入图片描述

2 .FWFT

在这里插入图片描述
学习完上诉知识后,我们来正式开始今天的实验任务,设计一个异步FIFO

四、vivado设置

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
其余保持默认即可

五、程序设计

1.模块

顶层模块
在这里插入图片描述
写模块
在这里插入图片描述
读模块
在这里插入图片描述

关于各个模块的接口,建议读者首先根据自身的任务要求,其次可参考vivado ip核中的例化模板,可知道ip核的输入输出端口,即可做好模块的划分
在这里插入图片描述

2.时序

写模块时序
在这里插入图片描述
读模块时序
在这里插入图片描述

3.异步信号传输

  • 对于单bit信号,我们一般采用打拍的方法,通过打两拍来将异步的信号变为同步,例如在本任务中,full信号是写模块输出的,其时钟和读模块时钟不一致,因此需要对full进行打两拍,来使得full信号与读模块时序一致,同理empty信号也是一样
  • 对于多bit信号,我们则用FIFO来进行信号的传输,哈哈哈哈哈,突然感觉知识产生了闭环对吧,挺有意思

4.RTL代码

写模块

module fifo_wr (
input                    wr_clk,  //写时钟
input                    rst_n,   //复位信号  
input                    wr_rst_busy, //写忙信号  
input                    almost_full,   //满前一个  
input                    empty,     //空信号
input                    wr_data_count, //计数信号


output  reg              wr_en, //使能信号
output  reg [7:0]        wr_data   //写数据
);

//打拍
reg     empty_d0;
reg     empty_d1;

//打两拍,使得异步单比特信号变成同步
always @(posedge wr_clk or negedge rst_n) begin
    if(!rst_n) begin
        empty_d0   <= 1'b0;        
        empty_d1   <= 1'b0;        
    end
    else begin
        empty_d0   <= empty;
        empty_d1   <= empty_d0;
    end
end

//使能信号赋值
always @(posedge wr_clk or negedge rst_n) begin
    if(!rst_n) 
       wr_en <= 1'b0;
    else if(!wr_rst_busy) begin
        if(empty_d1)
           wr_en <= 1'b1;
        else if(almost_full)
           wr_en <= 1'b0;
        
    end
    else
       wr_en <= 1'b0;
end  

//写数据
always @(posedge wr_clk or negedge rst_n) begin
    if(!rst_n)
        wr_data <= 8'd0;
    else if(wr_en == 1 && wr_data < 8'd254)
        wr_data <= wr_data + 8'd1;
    else 
        wr_data <= 8'd0;

end

    
endmodule

读模块

module fifo_rd(
input                    rd_clk,
input                    rst_n,
input                    full,
input                    rd_rst_busy,
input                    almost_empty,
input        [7:0]       rd_data,

output   reg             rd_en


);

reg         full_d0;
reg         full_d1;

//打拍
always @(posedge rd_clk or negedge rst_n) begin
    if(!rst_n) begin
        full_d0   <= 1'b0;        
        full_d1   <= 1'b0;        
    end
    else begin
        full_d0   <= full;
        full_d1   <= full_d0;
    end
end


//对rd_en进行赋值,FIFO写满之后开始读,读空之后停止读
always @(posedge rd_clk or negedge rst_n) begin
    if(!rst_n) 
        rd_en <= 1'b0;
    else if(!rd_rst_busy) begin
        if(full_d1)
           rd_en <= 1'b1;
        else if(almost_empty)
           rd_en <= 1'b0;
        
    end
    else
        rd_en <= 1'b0;
end



endmodule

顶层模块

module ip_fifo(
input       sys_clk,
input       sys_rst_n
);


wire         clk_50m       ;  // 50M时钟
wire         clk_100m      ;  // 100M时钟
wire         locked        ;  // 时钟锁定信号
wire         rst_n         ;  // 复位,低有效
wire         wr_rst_busy   ;  // 写复位忙信号
wire         rd_rst_busy   ;  // 读复位忙信号
wire         wr_en    ;  // FIFO写使能信号
wire         rd_en    ;  // FIFO读使能信号
wire  [7:0]  wr_data  ;  // 写入到FIFO的数据
wire  [7:0]  rd_data  ;  // 从FIFO读出的数据
wire         almost_full   ;  // FIFO将满信号
wire         almost_empty  ;  // FIFO将空信号
wire         full          ;  // FIFO满信号
wire         empty         ;  // FIFO空信号
wire  [7:0]  wr_data_count ;  // FIFO写时钟域的数据计数
wire  [7:0]  rd_data_count ;  // FIFO读时钟域的数据计数



assign rst_n = sys_rst_n & locked;


clk_wiz_0 u_clk_wiz_0
(
.clk_out1   (clk_50m), 
.clk_out2   (clk_100m), 

.locked     (locked),  
.clk_in1    (sys_clk) 
);


//例化写FIFO模块
fifo_wr     u_fifo_wr(
.wr_clk                       (clk_50m),// 写时钟
.rst_n                        (rst_n), // 复位信号
.wr_rst_busy                  (wr_rst_busy),// 写复位忙信号
.almost_full                  (almost_full ),
.empty                        (empty),
.wr_data_count                (wr_data_count ),
.wr_en                        (wr_en),
.wr_data                      (wr_data)
);

//例化读FIFO模块
fifo_rd  u_fifo_rd(
.rd_clk                       (clk_100m),
.rst_n                        (rst_n),
.full                         (full),
.rd_rst_busy                  (rd_rst_busy),
.almost_empty                  (almost_empty),
.rd_data                      (rd_data),
.rd_en                        (rd_en)
);

//例化FIFO IP核
fifo_generator_0  u_fifo_generator_0(
.rst                                 (~rst_n),   //比较奇怪                   
.wr_clk                              (clk_50m),   //写时钟            
.rd_clk                              (clk_100m),  //读时钟          
.din                                 (wr_data),                      
.wr_en                               (wr_en),                  
.rd_en                               (rd_en),                  
.dout                                (rd_data),                    
.full                                (full),                    
.almost_full                         (almost_full),      
.empty                               (empty),                  
.almost_empty                        (almost_empty),    
.rd_data_count                       (rd_data_count),  
.wr_data_count                       (wr_data_count),  
.wr_rst_busy                         (wr_rst_busy),      
.rd_rst_busy                         (rd_rst_busy)      
);                       





endmodule

FIFO ip核和 clocking wizard ip核在顶层模块中调用即可
需要注意的是:我们会发现,这个顶层模块也太难写了,第一眼看完全无从下手,这里笔者认为,根据顶层模块框图来对各个模块进行例化即可不那么模糊,至于前面变量的调用,缺啥补啥就可以了,都是wire线网类型,对各个变量进行提前说明即可,这是笔者的一些小技巧。

五、仿真

1.Testbench代码

`timescale	1ns/1ns //仿真的单位/仿真的精度s
module tb_ip_fifo();

parameter CLK_PERIOD = 20;

reg 		sys_clk;  //周期20ns
reg 		sys_rst_n; 


initial begin
	sys_clk <= 1'b0;
	sys_rst_n <=1'b0;
	#200 
	sys_rst_n <= 1'b1;
end

always #(CLK_PERIOD/2) sys_clk = ~sys_clk;
	


 
ip_fifo  u_ip_fifo (
    .sys_clk     (sys_clk),
	.sys_rst_n   (sys_rst_n) 
 );
 
 endmodule

因为顶层模块只有两个激励,所以仿真测试代码还是很好写的

2.波形

笔者是利用vivado和modelsim来进行联合仿真
写模块
在这里插入图片描述

在这里插入图片描述
读模块
在这里插入图片描述
仿真验证正确


  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值