FPGA学习笔记(十二)IP核之FIFO的学习总结

该系列文章详细介绍了FPGA的学习过程,从入门背景、Verilog语法基础,到流水灯设计、测试平台搭建,再到FPGA的时钟约束、Modelsim仿真和IP核如PLL、RAM、FIFO的使用。特别是FIFO部分,讲解了其工作原理、类型、参数以及如何进行程序设计和IP核创建,并提供了相关模块的示例代码。文章还涵盖了跨时钟域数据传递的应用和不同时钟频率下的读写行为分析。
摘要由CSDN通过智能技术生成

系列文章目录

一、FPGA学习笔记(一)入门背景、软件及时钟约束

二、FPGA学习笔记(二)Verilog语法初步学习(语法篇1)

三、FPGA学习笔记(三) 流水灯入门FPGA设计流程

四、FPGA学习笔记(四)通过数码管学习顶层模块和例化的编写

五、FPGA学习笔记(五)Testbench(测试平台)文件编写进行Modelsim仿真

六、FPGA学习笔记(六)Modelsim单独仿真和Quartus联合仿真

七、FPGA学习笔记(七)verilog的深入学习之任务与函数(语法篇3)

八、FPGA学习笔记(八)同步/异步信号的打拍分析及处理

九、FPGA学习笔记(九)SPI学习总结及stm32的HAL库下SPI配置

十、FPGA学习笔记(十)IP核之PLL锁相环的学习总结

十一、FPGA学习笔记(十一)IP核之RAM的学习总结


参考正点原子开拓者FPGA开发指南

FIFO简介

FIFO( First In First Out):先进先出
常用在数据缓存或者跨时钟域的信号传递(高速异步数据的交互)

类比前面学习的ROM,只是没有地址线,采取顺序写入数据,顺序读出数据的方式。

  • FIFO分类:
    SCFIFO(single clock FIFO):单时钟FIFO。
    DCFIFO(double clock FIFO):双时钟FIFO。
    DCFIFO_MIXED_WIDTHS:混合宽度双时钟FIFO。
    同步FIFO:读时钟和写时钟的频率相同(单时钟)
    异步FIFO:读时钟和写时钟的频率不同(双时钟)。
    在这里插入图片描述
    双时钟将clock分为wrclk和rdclk

  • 常见FIFO参数:
    FIFO宽度(N):一次读写操作的N位数据
    FIFO深度(M):存储M个宽度为N位的数据
    将空标志:almost_empty(FIFO即将被清空)
    空标志:empty。FIFO 已空时送出的一个信号,阻止读操作继续从 FIFO中读出数据而造成无效数据的读出。
    将满标志:almost_full。FIFO 即将被写满。
    满标志:full。FIFO 已满或将要写满时的一个信号,阻止写操作继续向 FIFO 中写数据而造成溢出。
    读时钟:读 FIFO 时所遵循的时钟,在每个时钟的上升沿触发。
    写时钟:写 FIFO 时所遵循的时钟,在每个时钟的上升沿触发。
    wrreq/rdreq:请求(request)

  • 应用实例:
    跨时钟域:存储器A和B的时钟不一样,传递数据的时候可以用FIFO作为两者的衔接。
    带宽不同步:两个存储器的数据宽度不一样(比如一个8位,一个12位),传递数据的时候可以用FIFO作为两者的衔接。

FIFO的程序设计

需要四个模块:FIFO模块、写测试模块、读测试模块、顶层模块

FIFO IP核创建

quartus18下(quartus13还是在那个MegaWizard Plug In Manager里面)
在这里插入图片描述
指定好路径和IP核的名字:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上图选择我们想要的优化:

  • Lowest latency but requires synchronized clocks(最低延迟,但要求同步时钟): 此选项使用一个同步阶段,没有亚稳态保护,适用于同步时钟。它是最小尺寸,提供良好的 Fmax 。
  • TYPE_Cmal setting for unsynchronized clocks (异步时钟时的最小设置):这个选项使用两个同步阶段,具有良好的亚稳态保护。它是中等尺寸,提供良好的 Fmax 。
  • Best metastability protection, best fmax and unsynchronized clocks
    (异步时钟时最好的亚稳态保护,最好的 Fmax ,不同步):这个选项使用三个或更多的同步阶段,具有最好的亚稳态保护。它是最大尺寸,给出了最好的 Fmax 。

在这里插入图片描述
在这里插入图片描述
正常模式: 将 rdreq 看做读请求,在该端口信号为高电平进行读操作。(一般选正常模式)

前显模式: 将 rdreq 看做读确认,rdreq 信号置为高电平时,输出 FIFO 中的下一个数据字(如果存在)。(将会使设计性能下降)(应该意思就是超前读了一个数据字)

记忆块如下:
在这里插入图片描述

在这里插入图片描述
是否禁止上溢检测和下溢检测的保护电路。

上溢检测保护电路主要是用于在FIFO 满时,禁止 wrreq 端口
下溢检测保护电路主要是用于在 FIFO 空时,禁止 rdreq 端口

“Implement FIFO storage with logic cells only, even if the device contains memory blocks ?(选项使用逻辑单元实现 FIFO 存储器,即使器件拥有存储块)
选择使用存储块实现FIFO
在这里插入图片描述
仿真FIFO IP 核,需要用到“ altera_mf ”仿真库。

想要将此 FIFO IP 核用在其他的 EDA 工具上,可以通过选择“ Generate netlist ”来生成 IP_syn.v 文件,用于其他的 EDA 工具中。
在这里插入图片描述
FIFO_IP_inst.v选上后,可以打开,里面有直接例化好的(我感觉没啥用,选不选都无所谓)

FIFO读程序

//****************************************Copyright (c)***********************************//
// File name:           fifo_rd
// Last modified Date:  2021/4/7 9:30:00
// Last Version:        V1.1
// Descriptions:        FIFO读模块
// Created by:          正点原子
//****************************************************************************************//

module fifo_rd(
    input              clk,      //时钟信号
    input              rst_n,    //复位信号
    
    //fifo的读端口
    input              rd_full,  //读侧满信号
    input              rd_empty, //读侧空信号
    output             rd_req,   //读请求信号
    input   [7:0]      rd_data   //读出FIFO的数据
    );

//reg define
reg     rd_req_t;   

//*****************************************************
//**                    main code
//*****************************************************

//防止fifo读空后继续读出数据
assign  rd_req = rd_req_t & (~rd_empty);  

//rd_req_t信号赋值    
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        rd_req_t <= 1'b0;
    else if(rd_full)  
        rd_req_t <= 1'b1;
    else if(rd_empty)
        rd_req_t <= 1'b0;
end

endmodule 

FIFO写程序

//****************************************Copyright (c)***********************************//
// File name:           fifo_wr
// Last modified Date:  2021/4/7 9:30:00
// Last Version:        V1.1
// Descriptions:        FIFO写模块
// Created by:          正点原子
//****************************************************************************************//

module fifo_wr(
    input              clk,      //时钟信号
    input              rst_n,    //复位信号
                                 
    //fifo的写端口               
    input              wr_full,  //写侧满信号
    input              wr_empty, //写侧空信号
    output             wr_req,   //写请求信号
    output  reg [7:0]  wr_data   //写入FIFO的数据
    );

//reg define    
reg     wr_req_t;   

//*****************************************************
//**                    main code
//*****************************************************

//防止fifo写满后继续写入数据
assign  wr_req = wr_req_t & (~wr_full);  

//wr_req_t信号赋值    
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_req_t <= 1'b0;
    else if(wr_empty)  
        wr_req_t <= 1'b1;
    else if(wr_full)
        wr_req_t <= 1'b0;
end

//写数据信号赋值 
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        wr_data <= 8'd0;
    else if(wr_req)
        wr_data <= wr_data + 1'b1;
    else
        wr_data <= 8'd0;
end

endmodule    
    

FIFO顶层模块

//****************************************Copyright (c)***********************************//
//Copyright(C) 正点原子 2018-2028
// File name:           ip_fifo
// Last modified Date:  2021/4/7 9:30:00
// Last Version:        V1.1
// Descriptions:        IP核之FIFO实验
//****************************************************************************************//

module ip_fifo(
    input    sys_clk    ,      //时钟信号
    input    sys_rst_n         //复位信号
    );

//wire define
wire                clk_50m ;  //50Mhz时钟  
wire                clk_25m ;  //25Mhz时钟   
wire                locked  ;  //时钟稳定信号

wire                rst_n   ;  //复位信号   

wire     [7:0]      rd_usedw;  //读侧FIFO中的数据量
wire     [7:0]      wr_usedw;  //写侧FIFO中的数据量

wire                wr_full ;  //写侧满信号
wire                wr_empty;  //写侧空信号
wire                wr_req  ;  //写请求信号
wire     [7:0]      wr_data ;  //写入FIFO的数据

wire                rd_full ;  //读侧满信号
wire                rd_empty;  //读侧空信号
wire                rd_req  ;  //读请求信号
wire     [7:0]      rd_data ;  //读出FIFO的数据

//*****************************************************
//**                    main code
//*****************************************************

//待时钟输出稳定后,再拉高rst_n信号
assign rst_n = sys_rst_n & locked;

//例化锁相环模块    
pll_clk	u_pll_clk (
    .areset (~sys_rst_n ),
    .inclk0 (sys_clk ),
    .c0     (clk_50m ),
    .c1     (clk_25m ),
    .locked (locked )
    );    

//例化FIFO写模块
fifo_wr u_fifo_wr(
    .clk        (clk_50m),  
    .rst_n      (rst_n),

    .wr_full    (wr_full ),
    .wr_empty   (wr_empty),
    .wr_req     (wr_req  ),
    .wr_data    (wr_data )
    );

//例化异步FIFO模块    
async_fifo	u_async_fifo (
    .aclr       (~rst_n ),
    .data       (wr_data ),
    .rdclk      (clk_25m ),
    .rdreq      (rd_req ),
    .wrclk      (clk_50m ),
    .wrreq      (wr_req ),
    .q          (rd_data ),
    .rdempty    (rd_empty ),
    .rdfull     (rd_full ),
    .rdusedw    (rd_usedw ),
    .wrempty    (wr_empty ),
    .wrfull     (wr_full ),
    .wrusedw    (wr_usedw )
    );

//例化FIFO读模块
fifo_rd u_fifo_rd(
    .clk         (clk_25m),
    .rst_n       (rst_n),

    .rd_full     (rd_full ),
    .rd_empty    (rd_empty),
    .rd_req      (rd_req  ),
    .rd_data     (rd_data )
    );
  
endmodule    

modelsim的FIFO仿真文件

`timescale  1ns/1ns   //仿真的单位/仿真的精度

module ip_fifo_tb();

parameter T = 20;

reg          sys_clk;
reg          sys_rst_n;   

initial begin
    sys_clk = 1'b0;
    sys_rst_n = 1'b0;
    #(T+1)
    sys_rst_n = 1'b1;
end

always #(T/2) sys_clk = ~sys_clk; 

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

这个仿真库也要添加进去,在quartus的安装路径下:quartus/eda/sim_lib/altera_mf .v
在这里插入图片描述

结果

代码结构是当FIFO写满使开始读取,当全部读完的时候再开始写。

同一个时钟的读切换写:
在这里插入图片描述
FIFO数据全部读完的时候,读空信号 rdempty 先拉高,过两个时钟周期后写空信号 wrempty 才拉高,这是由FIFO 内部结构决定的,并且在写请求信号 wrempty 拉低后的第 3 个时钟周期 读空信号 rdempty 才拉低。

上面是读时钟和写时钟使用的是一个clock,下图是写始终50Mhz,读时钟25Mhz:
在这里插入图片描述
FIFO数据全部读完的时候,读空信号 rdempty 先拉高,过两个时钟周期后写空信号 wrempty 才拉高,这是由FIFO 内部结构决定的,并且在wrempty 拉低后的第 4 个时钟周期 读空信号 rdempty 才拉低。

同一个时钟的写切换读:
在这里插入图片描述
在写满信号 wrfull 拉高 2 个时钟周期后,读满信号 rdfull 才有效,并且rdfull拉低后的第3个时钟周期写满信号 wrfull 才拉低。

上面是读时钟和写时钟使用的是一个clock,下图是写始终50Mhz,读时钟25Mhz
在这里插入图片描述
因为时钟不一样,在写满信号 wrfull 拉高 2 .5个时钟(读时钟)周期后,读满信号 rdfull 才有效,并且在rdfull拉低后的第3个时钟(写时钟)周期写满信号 wrfull 才拉低。

通常情况下,向FIFO 中写数据时,当写使能(wr_req)为高,下一个写时钟的上升沿,数据就可以写入FIFO。但是需要特别注意的是,从FIFO 中读取数据时,当读使能为高,读时钟(rd_req)的第二个周期之后才有有效数据输出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值