FPGA_学习_12_IP核_FIFO

FIFO(Frist Input Frist Output),即先入先出,也是一种存储器,一般做数据缓冲。FIFO和 RAM的共同点在于都能存储数据、都有控制写和读的信号;不同点在于 FIFO 没有地址,所以不能任意指定读取某一个数据,数据只能按照数据输入的顺序输出,即先入先出,并且读写可以同时进行。如果数据把 FIFO 的深度写满了,数据将不能再进去,也不会覆盖原有的数据;读 FIFO 的数据也只能读一遍,读完一遍 FIFO 就空了,需要再往 FIFO 写数据才能读出新数据,否则读出的数据一直是最后一次读 FIFO 时的数据。

FIFO一般用在跨时钟域,和高速输入数据的缓冲。异步 FIFO 有两个时钟信号,读和写接口分别采用不同时钟,这两个时钟可能时钟频率不同,也可能时钟相位不同,可能是同源时钟,也可能是不同源时钟。 在现代逻辑设计中,随着设计规模的不断扩大,一个系统中往往含有数个时钟,多时钟域带来的一个问题就是,如何设计异步时钟之间的接口电路。异步 FIFO 是这个问题的一种简便、快捷的解决方案,使用异步 FIFO 可以在两个不同时钟系统之间快速而方便地传输实时数据。

本文主要实现:复位过后,打开FIFO的写使能,往FIFO里面写数据,写了一段时间,或者说写了一定的数据量后,我们开启读使能。然后观察信号的变化。

1 FIFO IP核原理

下图所示的是一个简单的异步 fifo 的示意图。在写端口有写时钟、写使能和待写入的数据。在读端口有读时钟,读使能和读出的数据。此外还有 fifo 的empty/full信号,当fifo 中写满数据的时候,full 信号拉高,当fifo 中数据全部读出后empty信号拉高。

FPGA_学习_12_IP核_FIFO_第1张图片

 写时序

FPGA_学习_12_IP核_FIFO_第2张图片

 读时序1

FPGA_学习_12_IP核_FIFO_第3张图片

标准模式-输出信号会延迟一个周期

读时序2

FPGA_学习_12_IP核_FIFO_第4张图片

 First-word Fall-Through模式不会延迟一个周期

2 FIFO IP核配置步骤

(Vivado 赛灵思)

截图warning

FPGA_学习_12_IP核_FIFO_第5张图片

FPGA_学习_12_IP核_FIFO_第6张图片 

FPGA_学习_12_IP核_FIFO_第7张图片 

FPGA_学习_12_IP核_FIFO_第8张图片 

FPGA_学习_12_IP核_FIFO_第9张图片 

FPGA_学习_12_IP核_FIFO_第10张图片 

FPGA_学习_12_IP核_FIFO_第11张图片 

FPGA_学习_12_IP核_FIFO_第12张图片 

3 测试代码 

`timescale 1ns / 1ps

module fifo_test(
        input   wire            clk     ,
        input   wire            rst_n   ,
        output  wire     [7:0]  data_out
);


//==================================================================
//                        Parameter define
//==================================================================

parameter       MAX             = 256 - 1;
parameter       RD_START        = 128 - 1;


//==================================================================
//                        Internal Signals
//==================================================================
reg     [7:0]   din             ;
reg             wr_en           ;
reg             wr_flag         ;
reg             rd_en           ;
wire    [7:0]   dout            ;
wire            full, empty     ;
reg     [7:0]   wr_cnt          ;
reg             rd_start        ;

assign data_out = dout;

IP_FIFO inst_FIFO (
        .wr_clk(clk),           // input wire wr_clk
        .rd_clk(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_flag -----------------------------
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                wr_flag <= 1'b1;
        end
        else if (wr_cnt == MAX && wr_flag == 1'b1) begin
                wr_flag <= 1'b0;
        end
        else if (empty == 1'b1) begin
                wr_flag <= 1'b1;
        end
        else begin
                wr_flag <= wr_flag;
        end
end

//----------------------------- wr_en -----------------------------
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                wr_en <= 1'b0;               
        end
        else begin
                wr_en <= wr_flag;  
        end
end

//----------------------------- wr_cnt -----------------------------
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                wr_cnt  <= 'd0;             
        end
        else if (wr_en == 1'b1) begin
                if (wr_cnt == MAX) begin
                        wr_cnt <= 'd0;
                end
                else begin
                        wr_cnt <= wr_cnt + 1'b1;
                end
        end
        else begin
                wr_cnt <= 'd0;
        end
end

//----------------------------- din -----------------------------
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                din  <= 'd0;             
        end
        else begin
                din <= wr_cnt;
        end
end

//----------------------------- rd_start -----------------------------
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                rd_start <= 1'b0;
        end
        else if (wr_cnt == RD_START) begin
                rd_start <= 1'b1;
        end
        else begin
                rd_start <= 1'b0;
        end
end

//----------------------------- rd_en -----------------------------
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                rd_en <= 1'b0;             
        end
        else if (rd_start == 1'b1) begin
                rd_en <= 1'b1;
        end
        else if (empty == 1'b1) begin
                rd_en <= 1'b0;
        end
        else begin
                rd_en <= rd_en;
        end
end
endmodule

4 仿真代码

`timescale 1ns/1ps

module tb_fifo_test (); /* this is automatically generated */

    parameter      MAX = 256 - 1;
    parameter RD_START = 128 - 1;

    // clock
    reg clk;
    reg rst_n;
    wire [7:0] data_out;

    initial begin
        clk <= 1'b1;
        forever #(10) clk = ~clk;
    end

    // asynchronous reset
  
    initial begin
        rst_n <= 1'b0;
        #200
        rst_n <= 1'b1;
    end



    fifo_test #(.MAX(MAX), .RD_START(RD_START)) inst_fifo_test (.clk(clk), .rst_n(rst_n), .data_out(data_out));

endmodule

5 仿真结果

(使劲双击可以看得清楚大图)

FPGA_学习_12_IP核_FIFO_第13张图片

 

仿真结果有个小小的疑问,就是rd_en拉高后,过了两个时钟周期输出数据才变成1,说明读了2个0。 前面写数据的时候,由于din = 滞后一个周期的wr_cnt ,所以写了两个0进去。

FPGA_学习_12_IP核_FIFO_第14张图片

我本以为是IP核的读模式不小心选成了标准模式,结果原因是din相比于wr_en滞后了一个时钟周期,因此我往fifo里是写了两个0进去的。 如果我把IP例化时候din的位置用wr_cnt替换

IP_FIFO inst_FIFO (
        .wr_clk(clk),           // input wire wr_clk
        .rd_clk(clk),           // input wire rd_clk
        .din(wr_cnt),           // 这一行改了
        .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
);

则输出dout在rd_en拉高后,将不会读出两个0。 

FPGA_学习_12_IP核_FIFO_第15张图片

 

 

 

你可能感兴趣的:(FPGA,fpga开发,学习)