实验需求:
配置异步FIFO IP核,可以缓存31字节的数据,并仿真
锁相环配置
在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深度为32字节。
- 生成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_busy
assign 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周期,50MHz
task generate_test_data;
for( i=0; i<TEST_DATA_SIZE; i=i+1) begin
test_data[i] = (i+11)%255; // 生成随机测试数据
end
endtask
endmodule
仿真实验
实验1、连续读写,写35MHZ
测试方案1:
写时钟75MHZ,读时钟35HZ,持续读写
仿真波形:
现象:因为写的速度比较快,所以在写入数量与读取数量差值在31的时候,full为高电平;
测试方案2:
写时钟35MHZ,读时钟75HZ,持续读写
仿真波形:
结论:通过仿真数据分析,写入值和读出值数据能一一对应上!因为读比写快,所以会有empty信号为低电平输出
测试方案3:
写时钟75MHZ,读时钟75MHZ,持续读写
结论:通过仿真数据分析,写入值和读出值数据能一一对应上!因为用的时同一个时钟信号,所以无full 和empty 信号输出。
测试方案4:
写时钟75MHZ,读时钟75MHZ,先写满溢出设计,后读空
仿真波形:写满波形
读空波形:
读取的第一个数据就是写入的第一个数据,至此,FIFO实验测试成功!