八、IP核——FIFO实验

IP核——FIFO实验

一、实验任务

本节的实验任务是使用 Vivado 生成 FIFO IP 核,并实现以下功能:当 FIFO 为空时,向 FIFO 中写入数据,写入的数据量和 FIFO 深度一致,即 FIFO 被写满;然后从 FIFO 中读出数据,直到 FIFO 被读空为止,以此向大家详细介绍一下 FIFO IP 核的使用方法。

二、程序设计

前置知识

  • FIFO 的英文全称是 First In First Out,即先进先出。FPGA 使用的FIFO 一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存,或者高速异步数据的交互也即所谓的跨时钟域信号传递。它与 FPGA 内部的 RAM 和 ROM 的区别是没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,使用起来简单方便。
  • FIFO在开发板上也是一个RAM资源。
  • 在这里插入图片描述
  • 注意这里的“即将空”和“即将满”的信号,在“空”或“满”的前一拍拉高,需要一个边沿检测来抓取上升沿。
  • 使用状态机来实现读、写状态的跳转。
  • 当满或空时,不能立即进行读或写操作,因为可能会出现数据丢失的问题,所以一般在读使能或写使能拉高之前,延迟几个时钟周期。

设计文件

//顶层文件
module ip_fifo (
    input sys_clk,
    input sys_rst_n
);

    wire       almost_full;
    wire       almost_empty;
    wire       fifo_wr_en;
    wire [7:0] fifo_wr_data;

    wire [7:0] dout;
    wire       fifo_rd_en;

    wire rd_data_count;
    wire wr_data_count;
    wire full;
    wire empty;

    fifo_wr u_fifo_wr (
        .sys_clk     (sys_clk     ),
        .sys_rst_n   (sys_rst_n   ),
        .almost_full (almost_full ),
        .almost_empty(almost_empty),
        .fifo_wr_en  (fifo_wr_en  ),
        .fifo_wr_data(fifo_wr_data)
    );

    fifo_rd u_fifo_rd (
        .sys_clk     (sys_clk     ),
        .sys_rst_n   (sys_rst_n   ),
        .almost_full (almost_full ),
        .almost_empty(almost_empty),
        .fifo_rd_data(fifo_rd_data),
        .fifo_rd_en  (fifo_rd_en  )
    );

    fifo_generator_0 u_fifo_generator_0 (
        .wr_clk       (sys_clk      ), // input wire wr_clk
        .rd_clk       (sys_clk      ), // input wire rd_clk
        .din          (fifo_wr_data ), // input wire [7 : 0] din
        .wr_en        (fifo_wr_en   ), // input wire wr_en
        .rd_en        (fifo_rd_en   ), // input wire rd_en
        .dout         (dout         ), // output wire [7 : 0] dout
        .full         (full         ), // output wire full
        .almost_full  (almost_full  ), // output wire almost_full
        .empty        (empty        ), // output wire empty
        .almost_empty (almost_empty ), // output wire almost_empty
        .rd_data_count(rd_data_count), // output wire [7 : 0] rd_data_count
        .wr_data_count(wr_data_count)  // output wire [7 : 0] wr_data_count
    );

endmodule
//读模块
module fifo_rd (
    input            sys_clk,
    input            sys_rst_n,
    input            almost_full  ,  //写模块的即将满信号 在下一个周期满
    input            almost_empty , //读模块的即将空信号
    input  [7:0]     fifo_rd_data ,  //读数据
    output reg       fifo_rd_en    //读使能
);

    reg        almost_full_d0;
    reg        almost_full_d1;
    reg  [1:0] state;           //状态机的状态
    reg  [3:0] dly_cnt;         //延迟计数器
    wire       syn;

    assign syn = ~almost_full_d1 && almost_full_d0 ;
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if (!sys_rst_n) begin
            // reset
            almost_full_d0 <= 1'b0;
            almost_full_d1 <= 1'b0;
        end
        else begin
            almost_full_d0 <= almost_full;
            almost_full_d1 <= almost_full_d0;
        end
    end

    always @(posedge sys_clk or negedge sys_rst_n) begin
        if (!sys_rst_n) begin
            // reset
            fifo_rd_en   <= 1'b0;
            state        <= 2'b0;
            dly_cnt      <= 4'b0;
        end
        else case(state)
            2'd0 : begin
                if(syn)
                    state <= 2'd1;
                else begin
                    state <= state;
                end
            end
            2'd1 : begin
                if(dly_cnt == 4'd10) begin
                    dly_cnt    <= 4'd0;
                    state      <= 2'd2;
                end
                else begin
                    dly_cnt <= dly_cnt + 4'd1;
                end
            end
            2'd2 : begin
                if (almost_empty) begin
                    fifo_rd_en   <= 1'b0;
                    state        <= 2'd0;
                end
                else begin
                    fifo_rd_en   <= 1'b1;
                end
            end
            default : state <= 2'd0 ;
        endcase

    end

endmodule
//写模块
module fifo_wr (
    input            sys_clk,
    input            sys_rst_n,
    input            almost_full ,  //写模块的即将满信号 在下一个周期满
    input            almost_empty, //读模块的即将空信号
    output reg       fifo_wr_en  ,   //写使能
    output reg [7:0] fifo_wr_data  //写数据
);

    reg        almost_empty_d0;
    reg        almost_empty_d1;
    reg  [1:0] state;           //状态机的状态
    reg  [3:0] dly_cnt;         //延迟计数器
    wire       syn;

    assign syn = ~almost_empty_d1 && almost_empty_d0 ;
    //抓取即将空的上升沿
    //因为 almost_empty 信号是属于 FIFO 读时钟域的
    //所以要将其同步到写时钟域中
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if (!sys_rst_n) begin
            // reset
            almost_empty_d0 <= 1'b0;
            almost_empty_d1 <= 1'b0;
        end
        else begin
            almost_empty_d0 <= almost_empty;
            almost_empty_d1 <= almost_empty_d0;
        end
    end

    //状态机控制写:当即将为空时,为防止数据丢失不能立即写,而是延时10个周期
    //当写的时候,一直保持写使能为高电平,写满时写使能改为低电平并将状态机恢复初始状态
    //一个计数器dly_cnt记延迟的周期
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if (!sys_rst_n) begin
            // reset
            fifo_wr_en   <= 1'b0;
            fifo_wr_data <= 8'b0;
            state        <= 2'b0;
            dly_cnt      <= 4'b0;
        end
        else case(state)
            2'd0 : begin
                if(syn)
                    state <= 2'd1;
                else begin
                    state <= state;
                end
            end
            2'd1 : begin
                if(dly_cnt == 4'd10) begin
                    dly_cnt    <= 4'd0;
                    state      <= 2'd2;
                    fifo_wr_en <= 1'b1;
                end
                else begin
                    dly_cnt <= dly_cnt + 4'd1;
                end
            end
            2'd2 : begin
                if (almost_full) begin
                    fifo_wr_en   <= 1'b0;
                    fifo_wr_data <= 8'b0;
                    state        <= 2'd0;
                end
                else begin
                    fifo_wr_en   <= 1'b1;
                    fifo_wr_data <= fifo_wr_data + 1'd1;
                end

            end
            default : state <= 2'd0 ;
        endcase

    end

endmodule

tb文件

module tb_ip_fifo ();

    //激励信号一般定义为 reg型
    reg sys_clk;
    reg sys_rst_n;


    initial begin
        sys_clk         = 1'b0;
        sys_rst_n       = 1'b0;
        #200
            sys_rst_n = 1'b1;

    end

    always #10 sys_clk = ~sys_clk;

    ip_fifo u_ip_fifo (
        .sys_clk  (sys_clk  ),
        .sys_rst_n(sys_rst_n)
);

endmodule


仿真波形

在这里插入图片描述
在这里插入图片描述

上板验证

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值