> 技术文档 > Xilinx零基础学习FPGA笔记——第四课,FIFO IP核的使用

Xilinx零基础学习FPGA笔记——第四课,FIFO IP核的使用

实验需求:

配置异步FIFO IP核,可以缓存31字节的数据,并仿真

目录

锁相环配置

异步FIFO配置

顶层模块设计

仿真测试

仿真实验


锁相环配置

在FPGA设计中,锁相环(PLL)用于生成不同频率的时钟信号。假设使用Xilinx FPGA,可以通过Vivado中的Clock Wizard IP核来配置PLL,生成50MHz和35MHz的时钟信号。

  1. 打开Vivado,创建一个新的工程。
  2. 在IP Catalog中搜索并添加“Clock Wizard” IP核。
  3. 配置Clock Wizard,设置输入时钟为50MHz,输出时钟为50MHz和35MHz。
  4. 生成IP核并实例化到顶层模块中。

异步FIFO配置

异步FIFO用于在不同时钟域之间传递数据。在Vivado中,可以使用FIFO Generator IP核来配置异步FIFO。

  1. 在IP Catalog中搜索并添加“FIFO Generator” IP核。
  2. 设置异步FIFO。

生成IP核并实例化到顶层模块中。

设置位宽和位深

生成IP核并实例化到顶层模块中。

顶层模块设计

在顶层模块中,实例化PLL和FIFO IP核,并连接相关信号。

`timescale 1ns / 1ps//////////////////////////////////////////////////////////////////////////////////// Company: // Engineer: // // Create Date: 2025/05/09 23:10:25// Design Name: // Module Name: Class_Top_fifo// Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision:// Revision 0.01 - File Created// Additional Comments:// //////////////////////////////////////////////////////////////////////////////////module Class_Top_dcfifo( input  clk_in1, input  reset_n, input  wr_en, input  rd_en, input [7 : 0] din, output [7 : 0] dout, output [31 : 0] wr_data_gcount, output [31 : 0] rd_data_gcount, output  full, output  empty, output  locked, output  clk_wr, output  clk_rd ); // reg din_T ; reg [7 : 0] din_T ; wire clk_out_75MHZ; wire clk_out_35MHZ; wire  wr_rst_busy; wire  rd_rst_busy; //wire wr_en_T;//wire rd_en_T;//assign wr_en_T=wr_en;//assign rd_en_T=rd_en; reg  wr_en_T ; reg  rd_en_T ; clk_35M_75M_ip u1_clk_35M_75M_ip ( // Clock out ports .clk_out1(clk_out_75MHZ), // output clk_out1 .clk_out2(clk_out_35MHZ), // output clk_out2 // Status and control signals .resetn(reset_n), // input resetn .locked(locked), // output locked // Clock in ports .clk_in1(clk_in1)); // input clk_in1 dcfifo_std8x31_ip u1_dcfifo ( .rst((!reset_n&locked)),// input wire rst .wr_clk(clk_wr), // input wire wr_clk .rd_clk(clk_rd), // input wire rd_clk .din(din_T),// input wire [7 : 0] din .wr_en(wr_en_T),  // input wire wr_en .rd_en(rd_en_T),  // 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_busyassign clk_wr=clk_out_75MHZ;assign clk_rd=clk_out_75MHZ;/////////下降沿给数据///////////always @(negedge clk_wr or negedge reset_n)begin if(!reset_n) din_T<=0; else din_T<=din;end/////////下降沿给使能信号///////////////////////always @(negedge clk_rd or negedge reset_n)begin if(!reset_n) rd_en_T<=0; else rd_en_T<=rd_en;end/////////下降沿给使能信号///////////////////////always @(negedge clk_wr or negedge reset_n)begin if(!reset_n) wr_en_T<=0; else wr_en_T<=wr_en;end gen_wr_fifo wr_u1(  . rst_n((reset_n&locked)), . clk_wr(clk_wr), . wr_en(wr_en_T), . full(full), . wr_real_counts(wr_data_gcount) );gen_rd_fifo rd_u1( . rst_n((reset_n&locked)), . clk_rd(clk_rd), .rd_en(rd_en_T), . empty(empty), . rd_real_counts(rd_data_gcount));endmodule

仿真测试

编写测试平台(Testbench)对设计进行仿真测试,验证FIFO在不同时钟域下的读写操作。

`timescale 1ns / 1ps//////////////////////////////////////////////////////////////////////////////////// Company: // Engineer: // // Create Date: 2025/05/09 23:30:45// Design Name: // Module Name: Test_dcfifo_tb// Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision:// Revision 0.01 - File Created// Additional Comments:// //////////////////////////////////////////////////////////////////////////////////module Test_dcfifo_tb( ); parameter TEST_DATA_SIZE = 1024; // 测试数据量 reg clk_50MHz_in; reg rst_n; reg wr_en; reg rd_en; wire locked; wire full; wire empty;  reg [7:0] test_data [0:TEST_DATA_SIZE-1]; // 测试数据池 wire clk_wr; wire clk_rd; wire [31:0] wr_counts;//已经写入的总个数 wire [7:0] gdin; wire [31:0] rd_counts;//读出的总个数 wire [7:0] data_out;//读取的实时数据 reg [7:0] expected_data[0:TEST_DATA_SIZE];//读取的数据 integer i=0; reg [7:0] userData; Class_Top_dcfifo U1( .clk_in1(clk_50MHz_in), .reset_n(rst_n), .wr_en(wr_en), .rd_en(rd_en), .din(gdin), .dout(data_out), .wr_data_gcount(wr_counts), .rd_data_gcount(rd_counts), .full(full), .empty(empty), .locked(locked), .clk_wr(clk_wr), .clk_rd(clk_rd)); // 测试过程 initial begin clk_50MHz_in=0; rst_n = 0; // 复位 wr_en = 0; rd_en = 0; // 生成随机数据 generate_test_data; #300 ; userData=8\'b0; rst_n = 1; @(posedge locked);  #30;////////////////////////////写满设计//////////////////////////// for(i=0;i<35;i=i+1) begin  @(negedge clk_wr); userData= test_data[i]; wr_en = 1; end #100 ; @(posedge clk_wr); wr_en = 0; #200 ;////////////////////////读空设计//////////////////////////////// for(i=0;i<35;i=i+1)begin @(negedge clk_rd); rd_en = 1; #10; end #200 ; @(posedge clk_rd); #10; rd_en = 0; #200 ; $stop ;  end  assign gdin= userData;//always @(posedge clk_rd)// begin// if(data_out!= expected_data) begin // 同步值一致性检查// $display(\"Time=%0t: 第%d个数据data_out读取结果为:0x%h,与实际写入值 expected_data:0x%h不匹配\", $time, rd_counts,data_out,expected_data);// end//end // 生成50MHz时钟always #10 clk_50MHz_in = ~clk_50MHz_in; // 20ns周期,50MHztask generate_test_data; for( i=0; i<TEST_DATA_SIZE; i=i+1) begin test_data[i] = (i+11)%255; // 生成随机测试数据 endendtaskendmodule

仿真实验

实验1、连续读写,写35MHZ

测试方案1:

写时钟75MHZ,读时钟35HZ,持续读写

仿真波形:

现象:因为写的速度比较快,所以在写入数量与读取数量差值在31的时候,full为高电平;

测试方案2:

写时钟35MHZ,读时钟75HZ,持续读写

仿真波形:

结论:通过仿真数据分析,写入值和读出值数据能一一对应上!因为读比写快,所以会有empty信号为低电平输出

测试方案3:

写时钟75MHZ,读时钟75MHZ,持续读写

结论:通过仿真数据分析,写入值和读出值数据能一一对应上!因为用的时同一个时钟信号,所以无full 和empty 信号输出。

测试方案4:

写时钟75MHZ,读时钟75MHZ,先写满溢出设计,后读空

仿真波形:写满波形

读空波形:         

读取的第一个数据就是写入的第一个数据,至此,FIFO实验测试成功!

 注意事项

通常,读FIFO和写FIFO是两个不同的模块。也不确定FIFO里面有多少个数据,这时就需要判断FIFO里的可读数据寄存器的计数,或者empty信号(组合逻辑使用),通常是等计数大于等于某个设定值n,再读n个时钟,才能可靠

task rd_fifo;begin @(negedge rd_clk); while(n=4\'d8)begin  dcfifo_rden = 1;  n=n+8;  #320; end else begin  dcfifo_rden = 0;  @(negedge rd_clk); //#40 读数据不对,测试 end  end dcfifo_rden = 0; endendtask