Vivado fifo IP 完整例程(包括testbench)

38 篇文章 5 订阅
34 篇文章 9 订阅
本文详细介绍了如何使用Verilog实现异步FIFO,包括完整的例程和仿真分析。通过实例展示了FIFO的读写地址生成、空满标志检测,并提供了独立时钟的Block RAM实现。此外,还提供了自编与调用Xilinx IP核的FIFO测试平台,分析了不同模式下的FIFO行为。
摘要由CSDN通过智能技术生成

网上尽管有各种介绍fifo的文章,但大部分没有一个完整的例程来练习,这里给出能够完整演示的fifo例程和仿真分析。工程文件虽然部分基于xilinx的官方例程,但官方例程没有testbench,这部分是自己编写的,并对不同的配置进行了分析,完整的工程文件见
如果没有分数下载,认真看本篇,也能做出来。

第一部分参考以下文章,表示感谢!
调试成功的简单异步FIFO–verilog实现+testbench-布布扣-bubuko.com http://www.bubuko.com/infodetail-2720594.html

实验一 自编fifo主程序

module Asyn_FIFO
#(
     parameter WIDTH = 8,
     parameter DEPTH = 4
 )
(
     input  clk_wr,
     input  clk_rd,
     input  rst_n_rd,
     input  rst_n_wr,
     input  wr_en,
     input  rd_en,
     input  [WIDTH-1:0] data_wr,
     output [WIDTH-1:0] data_rd,
     output reg rd_empty,
     output reg wr_full
);
 
     //defination
     reg  [WIDTH-1 : 0] mem [0 : (1<<DEPTH)-1];        //2^DEPTH numbers
     reg  [DEPTH   : 0] wp, rp;
     reg  [DEPTH   : 0] wr1_rp, wr2_rp, rd1_wp, rd2_wp;
     reg  [DEPTH   : 0] wbin, rbin;
 
 
     wire [DEPTH-1 : 0] waddr, raddr;
     wire [DEPTH   : 0] wbin_next, rbin_next;        //bincode
     wire [DEPTH   : 0] wgray_next, rgray_next;        //graycode
     
     wire rd_empty_val, wr_full_val;
     
     //output
     assign data_rd = mem[raddr];
 
     //input
     always@(posedge clk_wr)
         if(wr_en && !wr_full)
            mem[waddr] <= data_wr;
 
 /*----------generate waddr and raddr-------------------------*/
     //gen raddr and read gray code
     always@(posedge clk_rd or negedge rst_n_rd)
         if(!rst_n_rd)
            {rbin, rp} <= 0;
         else
            {rbin, rp} <= {rbin_next, rgray_next};
 
     assign raddr = rbin[DEPTH-1 : 0];
     assign rbin_next = rbin + (rd_en & ~rd_empty);
     assign rgray_next = rbin_next ^ (rbin_next >> 1);
 
     //gen waddr and write gray code
     always@(posedge clk_wr or negedge rst_n_wr)
         if(!rst_n_wr)
             {wbin, wp} <= 0;
         else
             {wbin, wp} <= {wbin_next, wgray_next};
 
     assign waddr = wbin[DEPTH-1 : 0];
     assign wbin_next = wbin + (wr_en & ~wr_full);
     assign wgray_next = wbin_next ^ (wbin_next >> 1);
 
 /*---------------synchro rp and wp--------------------------*/
     //synchro rp
     always@(posedge clk_wr or negedge rst_n_wr)
         if(!rst_n_wr)
             {wr2_rp, wr1_rp} <= 0;
         else
             {wr2_rp, wr1_rp} <= {wr1_rp, rp}; //delay two clock
 
     //synchro wp
     always@(posedge clk_rd or negedge rst_n_rd)
         if(!rst_n_rd)
             {rd2_wp, rd1_wp} <= 0;
         else
             {rd2_wp, rd1_wp} <= {rd1_wp, wp};
 
 /*---------------empty and full flags--------------------------*/
     //gen rd_empty
     assign rd_empty_val = (rd2_wp == rgray_next);
     always@(posedge clk_rd or negedge rst_n_rd)
         if(!rst_n_rd)
             rd_empty <= 1'b1;
         else
             rd_empty <= rd_empty_val;
 
     //gen wr_full, two high bit do not equal
     assign wr_full_val = ({~wr2_rp[DEPTH : DEPTH-1], wr2_rp[DEPTH-2 : 0]} == wgray_next);
     always@(posedge clk_wr or negedge rst_n_wr)
         if(!rst_n_wr)
             wr_full <= 1'b0;
         else
             wr_full <= wr_full_val;
 
endmodule

tb程序

module Asyn_FIFO_tb;
 
     parameter WIDTH = 8;
 
     reg clk_wr;
     reg clk_rd;
     reg rst_n_rd;
     reg rst_n_wr;
 
     reg  [WIDTH-1:0] data_wr;
     reg  wr_en;
     wire wr_full;
 
     wire [WIDTH-1:0] data_rd;
     reg  rd_en;
     wire rd_empty;
 
 
     Asyn_FIFO fifo_inst(
     .clk_wr(clk_wr), 
     .rst_n_rd(rst_n_rd),
     .rst_n_wr(rst_n_wr),
     .wr_en(wr_en), 
     .data_wr(data_wr), 
     .clk_rd(clk_rd), 
     .rd_en(rd_en), 
     .data_rd(data_rd), 
     .rd_empty(rd_empty), 
     .wr_full(wr_full)
     );
     
     initial begin
         rst_n_rd = 0;
         rst_n_wr = 0;
         clk_wr = 0;
         clk_rd = 0;
         wr_en = 0;
         rd_en = 0;
         
         #20
         rst_n_rd = 1;
         rst_n_wr = 1;
         
         #80
         wr_en = 1;
         rd_en = 0;
         
         #335
         wr_en = 0;
         rd_en = 1;
     end
 
     always #10 clk_wr = ~clk_wr;
     always #20 clk_rd = ~clk_rd;
     
 /*     always @(posedge clk_rd)
         rd_en <= ($random) % 2;
 
     always @(posedge clk_wr)
         wr_en <= ($random) % 2; */
     
     always @(posedge clk_wr)
         data_wr <= ($random) % 256;
     
endmodule

将simulate.runtime时间改为1500ns
在这里插入图片描述

在这里插入图片描述

实验二 调用已IP库fifo, 编写testbench, 参考xilinx下面的官方例程,但自己编写testbench.

从地址下载
https://www.xilinx.com/support/documentation/sw_manuals/xilinx2018_3/ug939-design-files.zip,解压后用vivado打开lab_1实验
在这里插入图片描述
从Flow Navigator, 选择IP Catalog,输入fifo
在这里插入图片描述
在这里插入图片描述

双击FIFO下面的选项
在这里插入图片描述

在这里插入图片描述
Component Name输入char_fifo, Fifo Implementation选Independent Clocks Block RAM.
在这里插入图片描述
注意这里选的是
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
保持Leave Out of context per IP选择, 双击Generate button.
在这里插入图片描述
至此生成fifo IP.

下面制作testbench,

在这里插入图片描述
在这里插入图片描述

拷贝第57至63行到testbench tb_fifo.v例化

char_fifo your_instance_name (
.rst(rst), // input wire rst
.wr_clk(wr_clk), // input wire wr_clk
.rd_clk(rd_clk), // input wire rd_clk
.din(din), // input wire [7 : 0] din
.wr_en(wr_en), // input wire wr_en
.rd_en(rd_en), // input wire rd_en
.dout(dout), // output wire [7 : 0] dout
.full(full), // output wire full
.empty(empty), // output wire empty
.wr_rst_busy(wr_rst_busy), // output wire wr_rst_busy
.rd_rst_busy(rd_rst_busy) // output wire rd_rst_busy
);

从图上可以看到full信号拉低需要在复位信号410ns以后,重新修改testbench
在这里插入图片描述

`timescale 1ns/1ps


module tb_fifo1;

//***************************************************************************
// Parameter definitions
//***************************************************************************

  parameter         WIDTH        = 8;
  parameter         DEPTH        = 16;

  //inputs 
  reg rst;
  reg wr_clk;
  reg rd_clk;
  reg [7:0] din;
  reg wr_en;
  reg rd_en;

  //outputs 
  wire [7:0] dout;
  wire full;
  wire empty;
  wire [3:0] rd_data_count;
  wire [3:0] wr_data_count; 

  // Instantiate the Unit 
 
char_fifo your_instance_name (
  .rst(rst),                      // input wire rst
  .wr_clk(wr_clk),                // input wire wr_clk
  .rd_clk(rd_clk),                // input wire rd_clk
  .din(din),                      // input wire [7 : 0] din
  .wr_en(wr_en),                  // input wire wr_en
  .rd_en(rd_en),                  // input wire rd_en
  .dout(dout),                    // output wire [7 : 0] dout
  .full(full),                    // output wire full
  .empty(empty),                  // output wire empty
  .rd_data_count(rd_data_count),  // output wire [3 : 0] rd_data_count
  .wr_data_count(wr_data_count)  // output wire [3 : 0] wr_data_count
);

  always #10 wr_clk = ~wr_clk;                
  always #20 rd_clk = ~rd_clk;

  initial begin
      // Initialize Inputs 
      wr_clk = 0;                
      rd_clk = 0;
      rst = 1;
      din = 1'b1;
      wr_en = 0;
      rd_en = 0;

      // Wait 100 ns for global reset to finish 
      #100
      rst = 0;
      #500; 

	  repeat (18) begin 
		wr_en = 1;
		#20;
		wr_en = 0;
		#20;
		din = din + 1'b1;
      end 
	  
	  #290
	  repeat (18) begin 
		rd_en = 1;
		#40;
		rd_en = 0;
		#40;
      end	 
 
  end

endmodule

选Standard FIFO时,有效读写深度是15
写得时候,din是输入,所以不与rd_clk时钟对齐,读的时候是时钟打得出去,所以dout与时钟rd_clk对齐。
在这里插入图片描述
在这里插入图片描述

选First Word First Through, 有效深度度是17

即使没有使能,只要有读时钟,dout就会取第一个数据,第一个读使能和读时钟上升沿,dout就会取第二个数。
在这里插入图片描述
设计深度16,实际是17,上图与下图完全相符,达到深度17时full跳起,
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
注意下图,尽管没有使能rd_en, 但由于是First Word First Through模式,在empty降低后,仍将第一们数据推向输出总线,这样写的数量就减少了1,出现长7的情况。
在这里插入图片描述
2021. 5. 6 输入第一 个数据后的六个时钟周期,empty变为0,同时把第一个数据推向dout, 写计数器在第二个写时钟时将写数据计数器加1,与真实写入数据的时钟差一个时钟周期。读写计数并不保持严格的时间周期,可能错两三个时钟周期才计数
在这里插入图片描述
读取第17个数据后的下一个读时钟上升沿empty信号升起。
在这里插入图片描述

实验三 专门处理IP核流程

lab2 IP manager
在这里插入图片描述

在这里插入图片描述
以下过程类似lab1
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Component Name名字改为clk_core,同时改变下面的配置
• Primary Clock Input Frequency: 200
• Source: Differential clock capable pin
在这里插入图片描述

Port Name设置如下
• clk_out1: clk_rx, 200
• clk_out2: clk_tx, 166.667
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

你可以使用VHDL来调用FIFO(First-In-First-Out)核。Vivado是Xilinx提供的一款综合软件,用于FPGA设计和开发。 要使用FIFO核,你需要按照以下步骤进行操作: 1. 打开Vivado并创建一个新工程。 2. 在“Flow Navigator”面板中选择“IP Integrator”。 3. 点击“Create Block Design”创建一个新的Block Design。 4. 在Block Design中,点击右键并选择“Add IP”。 5. 在IP Catalog中搜索FIFO核并选择适合你需求的版本。 6. 添加FIFO核后,双击它以打开配置界面。 7. 在配置界面中,你可以设置FIFO的大小、数据宽度、时钟频率等参数。根据你的需求进行配置。 8. 配置完成后,点击“OK”以保存设置。 9. 在Block Design中,你可以添加其他IP核或自定义逻辑来与FIFO核进行交互。 10. 完成设计后,点击“Validate Design”确保没有错误。 11. 在“Flow Navigator”面板中选择“Generate Bitstream”以生成比特流文件。 12. 下载比特流文件到你的FPGA设备中。 在VHDL代码中调用FIFO核时,你需要实例化FIFO核,并根据核的接口定义来连接信号。以下是一个简单的示例: ```vhdl library ieee; use ieee.std_logic_1164.all; entity MyDesign is port ( -- 输入信号 input_data : in std_logic_vector(7 downto 0); input_valid : in std_logic; input_ready : out std_logic; -- 输出信号 output_data : out std_logic_vector(7 downto 0); output_valid : out std_logic; output_ready : in std_logic ); end entity MyDesign; architecture rtl of MyDesign is -- 实例化FIFO核 component fifo_core generic ( DATA_WIDTH : integer := 8; DEPTH : integer := 16 ); port ( clk : in std_logic; rst : in std_logic; din : in std_logic_vector(DATA_WIDTH-1 downto 0); wr_en : in std_logic; rd_en : in std_logic; dout : out std_logic_vector(DATA_WIDTH-1 downto 0); full : out std_logic; empty : out std_logic ); end component; -- 信号声明 signal fifo_clk : std_logic; signal fifo_rst : std_logic; signal fifo_din : std_logic_vector(7 downto 0); signal fifo_wr_en : std_logic; signal fifo_rd_en : std_logic; signal fifo_dout : std_logic_vector(7 downto 0); signal fifo_full : std_logic; signal fifo_empty : std_logic; begin -- 将输入信号连接到FIFO核的接口 fifo_clk <= clk; -- 将FIFO核的时钟连接到你的设计的时钟 fifo_rst <= rst; -- 将FIFO核的复位信号连接到你的设计的复位信号 fifo_din <= input_data; fifo_wr_en <= input_valid; input_ready <= not fifo_full; -- 将输出信号连接到FIFO核的接口 output_data <= fifo_dout; output_valid <= not fifo_empty; fifo_rd_en <= output_ready; -- 实例化FIFOfifo_inst : fifo_core generic map ( DATA_WIDTH => 8, DEPTH => 16 ) port map ( clk => fifo_clk, rst => fifo_rst, din => fifo_din, wr_en => fifo_wr_en, rd_en => fifo_rd_en, dout => fifo_dout, full => fifo_full, empty => fifo_empty ); end architecture rtl; ``` 以上示例展示了如何将输入信号和输出信号连接到FIFO核的接口。你需要根据你的设计需求进行适当的修改。 希望这些信息对你有所帮助!如果还有其他问题,请随时提问。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值