Xilinx零基础学习FPGA笔记——第四课,FIFO IP核的使用
实验需求:
配置异步FIFO IP核,可以缓存31字节的数据,并仿真
目录
锁相环配置
异步FIFO配置
顶层模块设计
仿真测试
仿真实验
锁相环配置
在FPGA设计中,锁相环(PLL)用于生成不同频率的时钟信号。假设使用Xilinx FPGA,可以通过Vivado中的Clock Wizard IP核来配置PLL,生成50MHz和35MHz的时钟信号。
- 打开Vivado,创建一个新的工程。
- 在IP Catalog中搜索并添加“Clock Wizard” IP核。
- 配置Clock Wizard,设置输入时钟为50MHz,输出时钟为50MHz和35MHz。
- 生成IP核并实例化到顶层模块中。
异步FIFO配置
异步FIFO用于在不同时钟域之间传递数据。在Vivado中,可以使用FIFO Generator IP核来配置异步FIFO。
- 在IP Catalog中搜索并添加“FIFO Generator” IP核。
- 设置异步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