赞
踩
FIFO是一种先进先出的数据缓存队列,主要特点是数据顺序写入,再按照同样的顺序输出数据,即先进去的数据先被取出来。
根据FIFO输入时钟的区别,可以分为同步FIFO和异步FIFO:
同步FIFO:只有一个独立的时钟端口clock,所有的输入输出信号都同步于clock信号。
异步FIFO:有两个时钟,写端口和读端口分别有独立的时钟,所有写相关的信号都属于写时钟,所有与读相关的信号都属于读时钟。
FIFO在FPGA开发中的应用非常广泛,主要包括以下几个方面:
生成一个异步FIFO,当FIFO为空时,向FIFO写入数据,直到将FIFO写满后停止写操作,当FIFO为满时,从FIFO中读数据,直到FIFO被读空后停止读操作。将FIFO的深度和宽度分别设置为256和8进行读写测试
笔者参考了xlinx的pg_057参考文档,针对于本博客需要的知识来向读者进行分享,详细说明请参考xlinx提供的官方文档。
xlinx提供的IP核有如下接口,笔者标注了每个接口信号的基本功能
需要注意的是,在FIFO中读有两种模式,Standard模式和**First-Word Fall-Through(FWFT)**模式,关于这两种模式的区分,xlinx文档已经给出了很详尽的解释
笔者将通过时序图来向大家简单介绍一下这两种模式的区分
学习完上诉知识后,我们来正式开始今天的实验任务,设计一个异步FIFO
其余保持默认即可
顶层模块
写模块
读模块
关于各个模块的接口,建议读者首先根据自身的任务要求,其次可参考vivado ip核中的例化模板,可知道ip核的输入输出端口,即可做好模块的划分
写模块时序
读模块时序
写模块
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线网类型,对各个变量进行提前说明即可,这是笔者的一些小技巧。
`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
因为顶层模块只有两个激励,所以仿真测试代码还是很好写的
笔者是利用vivado和modelsim来进行联合仿真
写模块
读模块
仿真验证正确
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。