FPGA学习笔记——FIFO读写

这篇具有很好参考价值的文章主要介绍了FPGA学习笔记——FIFO读写。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【实验任务】向FIFO中以50MHz的频率按顺序写入0~254,再从FIFO中以5Hz的频率按顺序读出0~254,输出到LED中点亮。

一、FIFO简介

FIFO全称为“First-In-First-Out”,是FPGA内部的存储数据的缓冲器,读写数据具有先入先出的特点,与数据结构中的“队列”有异曲同工之妙。

本博客例化紫光同创官方提供的FIFO ip核,实现数据的写入读出,烧录到PGL50H开发板,以LED灯指示数据的一次写入读出是否已完成。

fifo write,fpga开发,Powered by 金山文档

例化FIFO ip核各参数如上。有几个特别重要的选项要注意一下:

① FIFO Type:可选择是同步FIFO(SYN_FIFO,读写时钟相同、读写复位引脚相同)还是异步FIFO(ASYN_FIFO,读写时钟不同、读写复位引脚不同)

② Address Width:地址宽度,即可以存多少个数据(图中设为8,即可以存放1111_1111(b) = 255(d)个数据)

Data Width:数据宽度,即数据最大是二进制的几位(图中设为8,即每个数据最大只能255)

如果勾选了Write/Read Port Use Same Data Width,那么写模块读模块将保持相同的Address Width和Data Width。

③ Almost Full Water Level:当写到某某位时,almost full信号拉高,提示“FIFO将被写满”

紫光同创的ip核标明了地址宽度设为8时,写入第252个数据会拉高almost full,仿真结果如下:

fifo write,fpga开发,Powered by 金山文档

程序从0开始写起,每次自增1,所以当写入251(即第252位)时,almost_full拉高,还剩下252 253 254 没写。如果程序设定almost_full拉高的下一个时钟上升沿停止写入(250 -> 251 -> 0),那要延迟3个时钟。

④ Almost Empty Water Level:当写到某位时,almost empty信号拉高,提示“FIFO将被读空”

紫光同创的ip核表示地址宽度设为8时,读出倒数第5位(0 1 2 3 4,所以4是倒数第5位)数据时会拉高almost empty,仿真结果如下:

fifo write,fpga开发,Powered by 金山文档

程序从0开始读起,每次自增1,所以当读出250时,还剩下251 252 253 254 没读,如果程序设定almost_empty拉高的下一个时钟上升沿停止写入(249 -> 250 -> 0),那要延迟4个时钟。

二、系统框架

fifo write,fpga开发,Powered by 金山文档

如上图,虽然例化的ip核是“异步FIFO”,但是读时钟与写时钟均为系统时钟CLK,所以本实验最终结果仍然是“同步FIFO”。

实际上“Write_water_level”、“Write_full”、“Read_water_level”、“Read_empty”一般只发挥调试作用,不会应用到,所以这些信号不需要引入到两个控制模块中。

我们知道写模块以FIFO读完的 almost_empty 为写入的起始条件,读模块以FIFO写完的标志almost_empty为读出的起始条件,那我们要怎么知道FIFO已经读完或者写完了呢?

答:在“Write_almost_full”、“Read_almost_empty”信号拉高后,延时10拍。

防止FIFO 内部信号更新比实际的数据读/写操作有所延时而导致的时序紊乱,即等待到 FIFO 的空/满状态信号、数据计数信号等信号的更新完毕之后再进行 FIFO 写/读操作。

那如果写模块与读模块的时间频率不同,频率较快的打完10拍后,频率较慢的模块仍然没写完or读完怎么办呢?

答:将频率较慢的读/写模块打拍之后的almost_empty_sync / almost_full_sync信号输入到另一个模块当中去。

三、FIFO写控制模块设计

// 写在开头:以50MHz的频率写入FIFO
module fifo_write(
    input wire       clk,
    input wire       rst,
    input wire       almost_full,
    input wire       almost_empty,

    output reg       write_en,
    output reg [7:0] data_write
);

parameter IDLE  = 2'd0;     // 等待FIFO数据"将要全部被读出"的状态
parameter WAIT  = 2'd1;     // 等待到FIFO数据"全部被读出"的状态
parameter WRITE = 2'd2;     // 向FIFO发送数据的状态

reg     [1:0]   state;
reg     [1:0]   next_state;
reg     [3:0]   pause_count;
reg             pause_flag;
//  延迟"将满"信号为 第252位 的打拍寄存器
reg             almost_full_delay0;
reg             almost_full_sync;

    always @(posedge clk) begin
        if(!rst) begin
            almost_full_delay0 <= 1'b0;
            almost_full_sync   <= 1'b0;
        end
        else begin
            almost_full_delay0 <= almost_full;
            almost_full_sync   <= almost_full_delay0;     // 将满信号打两拍
        end
    end

// 以下为FIFO写控制状态机,收到"将空信号"后等待10拍完全读空FIFO(有延迟)
// 完全读空FIFO后,开始写入FIFO
// 收到"将满信号"后,不用管FIFO写入延迟,直接进入初始状态等待"将空信号"
always @(posedge clk) begin
    if(!rst)
        state <= IDLE;
    else
        state <= next_state;
end

always @(*) begin
    if(!rst)
        next_state <= 1'b0;
    else
        case (state)
            IDLE    : next_state <= (almost_empty)?     WAIT  : IDLE;
            WAIT    : next_state <= (pause_flag)?       WRITE : WAIT;
            WRITE   : next_state <= (almost_full_sync)? IDLE  : WRITE;
            default : next_state <= IDLE;    
        endcase
end

// 各个状态下输出信号的变化
// 收到“将满信号”之后停止写入,直到收到“将空信号”
always @(posedge clk) begin
    if(!rst) begin
        data_write <= 8'd0;
        write_en   <= 1'b0;
        pause_count<= 4'd0;
        pause_flag <= 1'd0;
    end
    else if(state == IDLE) begin
        write_en   <= 1'b0;
        data_write <= 8'd0;
    end
    else if(state == WAIT) begin
        if(pause_count  >= 4'd9) begin
            pause_count <= 4'd0;
            pause_flag  <= 1'b1;
            write_en    <= 1'b1;        //write_en在data_write+1之前拉高,从而保证data_write第0位可以顺利发出去
        end
        else begin
            pause_count <= pause_count + 1'b1;
            pause_flag  <= 1'b0;
        end
    end
    else if(state == WRITE) begin
        write_en   <= 1'b1;
        data_write <= data_write + 1'b1;
    end
    else begin
        data_write <= 8'd0;
        write_en   <= 1'b0;
        pause_count<= 4'd0;
        pause_flag <= 1'd0;
    end
end

endmodule

四、FIFO读控制模块设计

// 写在开头:以5Hz的频率读出FIFO,仿真暂时使用5MHz
module fifo_read(
    input  wire       clk,
    input  wire       rst,
    input  wire       almost_full,
    input  wire       almost_empty,
    input  wire [7:0] data_read,

    output reg        read_en,
    output wire       almost_empty_output,
    output wire [7:0] led_flag
);

parameter IDLE  = 2'd0;     // 等待FIFO数据"将要全部被读出"的状态
parameter WAIT  = 2'd1;     // 等待到FIFO数据"全部被读出"的状态
parameter READ = 2'd2;      // 向FIFO发送数据的状态

reg     [1:0]   state;
reg     [1:0]   next_state;
reg     [3:0]   pause_count;
reg             pause_flag;
//  延迟"将空"信号在第4位发出 的打拍寄存器
reg             almost_empty_delay0;
reg             almost_empty_delay1;
reg             almost_empty_sync;
reg             almost_empty_output_delay;

    always @(posedge clk) begin
        if(!rst) begin
            almost_empty_delay0       <= 1'b0;
            almost_empty_delay1       <= 1'b0;
            almost_empty_sync         <= 1'b0;
            almost_empty_output_delay <= 1'b0;
        end
        else begin
            almost_empty_delay0       <= almost_empty;
            almost_empty_delay1       <= almost_empty_delay0;
            almost_empty_sync         <= almost_empty_delay1;    // 将空信号打五拍

            almost_empty_output_delay <= almost_empty_sync;      // 再延时一拍,就可以在输出最后一位的同时告诉写模块“已经全部读完了”
        end
    end

// 以下为FIFO读控制状态机,收到"将满信号"后等待10拍完全写满FIFO(有延迟)
// 完全写满FIFO后,开始读出FIFO
// 收到"将空信号"后,不用管FIFO读出延迟,直接进入初始状态等待"将满信号"
always @(posedge clk) begin
    if(!rst)
        state <= IDLE;
    else
        state <= next_state;
end

always @(*) begin
    if(!rst)
        next_state <= 1'b0;
    else
        case (state)
            IDLE    : next_state <= (almost_full)?          WAIT  : IDLE;
            WAIT    : next_state <= (pause_flag)?           READ  : WAIT;
            READ    : next_state <= (almost_empty_sync)?    IDLE  : READ;
            default : next_state <= IDLE;    
        endcase
end

// 各个状态下输出信号的变化
// 收到“将空信号”之后停止读出,直到收到“将满信号”
always @(posedge clk) begin
    if(!rst) begin
        read_en    <= 1'b0;
        pause_count<= 4'd0;
        pause_flag <= 1'b0;
    end
    else if(state == IDLE) begin
        read_en    <= 1'b0;
    end
    else if(state == WAIT) begin
        if(pause_count  >= 4'd9) begin
            pause_count <= 4'd0;
            pause_flag  <= 1'b1;
        end
        else begin
            pause_count <= pause_count + 1'b1;
            pause_flag  <= 1'b0;
        end
    end
    else if(state == READ) begin 
        read_en   <= 1'b1;
    end
    else begin
        read_en    <= 1'b0;
        pause_count<= 4'd0;
        pause_flag <= 1'b0;
    end
end

assign led_flag            = data_read;
assign almost_empty_output = almost_empty_output_delay;

endmodule

五、FIFO读写TOP模块设计

TOP模块起着例化各子模块的作用

// 写在开头:1、写入FIFO时钟频率 50MHz ; 读出FIFO时钟频率 5Hz
//          2、模块fifo_write开始写的标志是:“FIFO空了”
//             模块fifo_read 开始读的标志是:“FIFO满了”
//          3、写模块在almost_empty到来时,延迟10拍再开始写入,
//             是因为almost_empty在剩下4个数时便拉高,延迟10拍后基本读完了可以开始写;读模块延时也是一样的道理
//             但是如果两个模块clk频率不一样呢?那么延时的工作就要交给频率低的模块执行。
//          4、根据2和3,如果读模块频率慢,那要将延时后的almost_empty输入到fifo_write
//                      如果写模块频率慢,那要将延时后的almost_full 输入到fifo_read
module top(
    input  wire       clk,
    input  wire       rstn_out,
    output wire [7:0] led_flag  //指示灯,输出FIFO里面读出的东西
);

reg     [23:0]  count_5hz;
reg             clk_5hz;
//  FIFO信号
wire            fifo_write_enable;
wire            fifo_almost_full;
wire    [7:0]   fifo_data_write;
wire            fifo_full;
wire    [8:0]   fifo_write_count;
wire            fifo_read_enable;
wire            fifo_almost_empty;
wire    [7:0]   fifo_data_read;
wire            fifo_empty;
wire    [8:0]   fifo_read_count;
//  频率低的模块,延时后输出“将空/将满信号”
wire            fifo_delay_almost;


// 分频器:50MHz --> 5Hz,仿真暂时使用5MHz
    always @(posedge clk) begin
        if(!rstn_out) begin
            count_5hz <= 24'd0;
            clk_5hz   <= 1'b0;
        end
        // else if(count_5hz >= 24'd10_000_000) begin
        //     clk_5hz   <= ~clk_5hz;
        //     count_5hz <= 24'd0;
        // end
        else if(count_5hz >= 24'd10) begin
            clk_5hz   <= ~clk_5hz;
            count_5hz <= 24'd0;
        end
        else
            count_5hz <= count_5hz + 1'b1;
    end

fifo_generator_0 instance1 (
    .wr_clk             (clk ),                 // input
    .wr_rst             (~rstn_out ),           // 高电平复位
    .wr_en              (fifo_write_enable ),   // input
    .wr_data            (fifo_data_write ),     // input [7:0]
    .wr_full            (fifo_full ),           // output
    .wr_water_level     (fifo_write_count),     // output [8:0]
    .almost_full        (fifo_almost_full ),    // output
    .rd_clk             (clk_5hz ),             // input
    .rd_rst             (~rstn_out ),           // 高电平复位
    .rd_en              (fifo_read_enable ),    // input
    .rd_data            (fifo_data_read ),      // output [7:0]
    .rd_empty           (fifo_empty ),          // output
    .rd_water_level     (fifo_read_count),      // output [8:0]
    .almost_empty       (fifo_almost_empty )    // output
);

fifo_write  instance2(
    .clk                (clk),
    .rst                (rstn_out),
    .data_write         (fifo_data_write),
    .write_en           (fifo_write_enable),
    .almost_full        (fifo_almost_full),
    .almost_empty       (fifo_delay_almost)
);

fifo_read   instacne3(
    .clk                (clk_5hz),
    .rst                (rstn_out),
    .data_read          (fifo_data_read),
    .read_en            (fifo_read_enable),
    .almost_empty       (fifo_almost_empty),
    .almost_full        (fifo_almost_full),
    .almost_empty_output(fifo_delay_almost),
    .led_flag           (led_flag)
);

endmodule

六、仿真分析

fifo write,fpga开发,Powered by 金山文档

首先,以50MHz写入FIFO,将满信号延迟两拍得到almost_full_sync信号,进入IDLE状态机(state == 2'h0)的下一个时钟上升沿,掐断fifo_write_enable信号。

fifo write,fpga开发,Powered by 金山文档

首先,以5MHz读出FIFO,将空信号延迟两拍得到almost_empty_sync信号,进入IDLE状态机(state == 2'h0)的下一个时钟上升沿,掐断fifo_read_enable信号。

综合上面的仿真图形我们可以看到,data变为0和enable拉低是在同一个时钟沿进行的。所以什么时候不想写了就得马上拉低fifo_write_enable信号,什么时候不想读了就得马上拉低fifo_read_enable信号。文章来源地址https://www.toymoban.com/news/detail-558765.html

到了这里,关于FPGA学习笔记——FIFO读写的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA原理与结构(12)——FIFO IP核原理学习

    系列文章目录:FPGA原理与结构(0)——目录与传送门         FIFO是英文First-In-First-Out的缩写,是一种先入先出的数据缓冲器,与一般的存储器的区别在于没有地址线, 使用起来简单,缺点是只能顺序读写数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器

    2024年02月08日
    浏览(36)
  • FPGA之FIFO详解,初识FIFO

           在篇博客里引入FIFO IP核的概念,FIFO是FPGA中最常用的IP核,经常用在接口模块、串并转换、协议处理、数据缓存等很多场合,所以活学活用这个IP核对于后期项目开发很重要,并且灵活掌握FIFO,也是一名合格的FPGA工程师的一项基本功。        FIFO顾名思义就是First

    2024年01月20日
    浏览(36)
  • FPGA FIFO基本介绍(FIFO Geneerator(13.2))

    软件:vivado 2020.2  参考文献: 文档xilinx官网pg057-fifo-generator文档下载链接官网https://docs.xilinx.com/v/u/en-US/pg057-fifo-generatorhttps://docs.xilinx.com/v/u/en-US/pg057-fifo-generator https://docs.xilinx.com/v/u/en-US/pg057-fifo-generator 原官网免费文档已被上传为付费文档,笔者上传的免费文档未过审核。

    2024年01月25日
    浏览(33)
  • FPGA中FIFO的应用(二)——异步FIFO设计

    ⭐️作者简介: 小瑞同学 ,一个努力精进的 FPGA 和通信学习者。 🍎个人主页:小瑞同学的博客主页 🌻个人信条:越努力,越幸运! ⏰日期:2023.12.3 🔍来源:自学经历 📖文章内容概述:介绍了 异步FIFO 的基本工作原理和深度计算,通过仿真观察了其读写过程。 连载系列

    2024年02月03日
    浏览(31)
  • FPGA——FIFO

    FIFO FIFO(First In First Out,即先入先出),是一种数据缓冲器,用来实现数据先入先出的读写方式。FIFO 根据读写时钟是否相同,分为 SCFIFO(single-clock FIFO)和 DCFIFO(duabl-clock FIFO),SCFIFO 的读写为同一时钟,应用在同步时钟系统中;DCFIFO 的读写时钟不同,应用在异步时钟系统

    2024年02月05日
    浏览(27)
  • 【LabVIEW FPGA入门】FIFO

            要在 FPGA VI 的不同部分之间或 FPGA 目标中的 VI 之间传输缓冲数据,可使用 FIFO。FIFO 是一种先进先出的缓冲器,第一个写入内存的数据项就是第一个从内存中读取和删除的数据项,类似于队列。         FPGA FIFO 的功能类似于一个固定长度的队列,可将多个数据

    2024年03月27日
    浏览(36)
  • FPGA中FIFO的应用(三)——Vivado FIFO IP核的调用

    ⭐️作者简介: 小瑞同学 ,一个努力精进的 FPGA 和通信学习者。 🍎个人主页:小瑞同学的博客主页 🌻个人信条:越努力,越幸运! ⏰日期:2023.12.6 🔍来源:自学经历 📖文章内容概述:简单介绍了 FIFO IP核 常用参数的配置,通过仿真分析了异步IP的 读写数据 过程。 连载

    2024年01月18日
    浏览(32)
  • 【FPGA】UART串口通信---基于FIFO

    我们在上一章完成了UART串口通信的收发模块,这一章我们将FIFO引入进来,使用FIFO进行缓存数据,来连接串口通信的收发模块 FIFO即First In First Out,是一种先进先出数据存储、缓冲器,我们知道一般的存储器是用外部的读写地址来进行读写,而FIFO这种存储器的结构并不需要外

    2023年04月14日
    浏览(73)
  • 【FPGA IP系列】FIFO深度计算详解

    FIFO(First In First Out)是一种先进先出的存储结构,经常被用来在FPGA设计中进行数据缓存或者匹配传输速率。 FIFO的一个关键参数是其深度,也就是FIFO能够存储的数据条数,深度设计的合理,可以防止数据溢出,也可以节省FPGA资源的消耗。 影响FIFO深度计算的主要因素包括: FIF

    2024年02月06日
    浏览(43)
  • <FPGA>异步FIFO的Verilg实现方法

            在上篇文章:同步FIFO的两种Verilog设计方法(计数器法、高位扩展法)中我们介绍了FIFO的基本概念,并对同步FIFO的两种实现方法进行了仿真验证。而异步FIFO因为读写时钟不一致,显然无法直接套用同步FIFO的实现方法,所以在本文我们将用Verilog实现异步FIFO的设计。

    2024年02月07日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包