可以参考这篇文章,很详细:
https://blog.csdn.net/weixin_42151221/article/details/103410556
下面是我的FIFO IP核的配置情况:
1、选择异步FIFO
2、设置写数据和读数据的位宽以及深度
3、可以设置是否需要Almost Full Flag 或者 Almost Empty Flag
4、设置读写计数
该实例实现的功能是:分别以100MHZ和75MHZ的写读时钟进行写读FIFO,当然这里之所以设置读写频率不一样,是因为如果以上述频率进行操作,到后期会出现写满的情况,因为写数据的频率比读数据快,可以自行观察。
1、RTL代码
`timescale 1ns / 1ps module fifo_test ( input clk, //50MHz时钟 input rst_n //复位信号,低电平有效 ); reg [15:0] w_data ; //FIFO写数据 wire wr_en ; //FIFO写使能 wire rd_en ; //FIFO读使能 wire [15:0] r_data ; //FIFO读数据 wire full ; //FIFO满信号 wire empty ; //FIFO空信号 wire [8:0] rd_data_count ; //可读数据数量 wire [8:0] wr_data_count ; //已写入数据数量 wire clk_100M ; //PLL产生100MHz时钟 wire clk_75M ; //PLL产生75MHz时钟 wire locked ; //PLL lock信号,可作为系统复 //位信号,高电平表示lock住 wire fifo_rst_n ; //fifo复位信号, 低电平有效 wire wr_clk ; //写FIFO时钟 wire rd_clk ; //读FIFO时钟 reg [7:0] wcnt ; //写FIFO复位后等待计数器 reg [7:0] rcnt ; //读FIFO复位后等待计数器 //例化PLL,产生100MHz和75MHz时钟 clk_wiz_0 fifo_pll ( // Clock out ports .clk_out1(clk_100M), // output clk_out1 .clk_out2(clk_75M), // output clk_out2 // Status and control signals .reset(~rst_n), // input reset .locked(locked), // output locked // Clock in ports .clk_in1(clk) // input clk_in1 ); assign fifo_rst_n = locked ; //将PLL的LOCK信号赋值给fifo的复位信号 assign wr_clk = clk_100M ; //将100MHz时钟赋值给写时钟 assign rd_clk = clk_75M ; //将75MHz时钟赋值给读时钟 //*****************************************************************// /* 写FIFO状态机 */ localparam W_IDLE = 1 ; localparam W_FIFO = 2 ; reg[2:0] write_state; reg[2:0] next_write_state; always@(posedge wr_clk or negedge fifo_rst_n) begin if(!fifo_rst_n) write_state <= W_IDLE; else write_state <= next_write_state; end always@(*) begin case(write_state) W_IDLE: begin //复位后等待一定时间,safety circuit模式下的最慢时钟60个周期 if(wcnt == 8'd79) next_write_state <= W_FIFO; else next_write_state <= W_IDLE; end W_FIFO: next_write_state <= W_FIFO;//一直在写FIFO状态 default: next_write_state <= W_IDLE; endcase end //在IDLE状态下,也就是复位之后,计数器计数 always@(posedge wr_clk or negedge fifo_rst_n) begin if(!fifo_rst_n) wcnt <= 8'd0; else if (write_state == W_IDLE) wcnt <= wcnt + 1'b1 ; else wcnt <= 8'd0; end //在写FIFO状态下,如果不满就向FIFO中写数据 assign wr_en = (write_state == W_FIFO) ? ~full : 1'b0; //在写使能有效情况下,写数据值加1 always@(posedge wr_clk or negedge fifo_rst_n) begin if(!fifo_rst_n) w_data <= 16'd1; else if (wr_en) w_data <= w_data + 1'b1; end //*************************************************************// /* 读FIFO状态机 */ localparam R_IDLE = 1 ; localparam R_FIFO = 2 ; reg[2:0] read_state; reg[2:0] next_read_state; ///产生FIFO读的数据 always@(posedge rd_clk or negedge fifo_rst_n) begin if(!fifo_rst_n) read_state <= R_IDLE; else read_state <= next_read_state; end always@(*) begin case(read_state) R_IDLE: begin //复位后等待一定时间,safety circuit模式下的最慢时钟60个周期 if (rcnt == 8'd59) next_read_state <= R_FIFO; else next_read_state <= R_IDLE; end R_FIFO: next_read_state <= R_FIFO ;//一直在读FIFO状态 default: next_read_state <= R_IDLE; endcase end //在IDLE状态下,也就是复位之后,计数器计数 always@(posedge rd_clk or negedge fifo_rst_n) begin if(!fifo_rst_n) rcnt <= 8'd0; else if (write_state == W_IDLE) rcnt <= rcnt + 1'b1 ; else rcnt <= 8'd0; end //在读FIFO状态下,如果不空就从FIFO中读数据 assign rd_en = (read_state == R_FIFO) ? ~empty : 1'b0; //*************************************************************// //实例化FIFO fifo_ip fifo_ip_inst ( .rst (~fifo_rst_n ), // input rst .wr_clk (wr_clk ), // input wr_clk .rd_clk (rd_clk ), // input rd_clk .din (w_data ), // input [15 : 0] din .wr_en (wr_en ), // input wr_en .rd_en (rd_en ), // input rd_en .dout (r_data ), // output [15 : 0] dout .full (full ), // output full .empty (empty ), // output empty .rd_data_count (rd_data_count ), // output [8 : 0] rd_data_count .wr_data_count (wr_data_count ) // output [8 : 0] wr_data_count ); endmodule2、仿真程序
`timescale 1ns / 1ps module vtf_fifo_tb; // Inputs reg clk; reg rst_n; // Instantiate the Unit Under Test (UUT) fifo_test uut ( .clk (clk), .rst_n (rst_n) ); initial begin // Initialize Inputs clk = 0; rst_n = 0; // Wait 100 ns for global reset to finish #100; rst_n = 1; end always #10 clk = ~ clk; //20ns一个周期,产生50MHz时钟源 endmodule3、仿真结果
