0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

FPGA学习笔记:FIFO IP核的使用方法

CHANBAEK 来源:小小研究生 作者: xxyjs 2023-09-07 18:30 次阅读

理论知识

FIFO(First In First Out, 先入先出 ),是一种数据缓冲器,用来实现数据先入先出的读写方式。数据按顺序写入 FIFO,先被写入的数据同样在读取的时候先被读出,所以 FIFO存储器没有地址线,有一个写端口和一个读端口。

FIFO 存储器主要是作为缓存,应用在同步时钟系统和异步时钟系统中,分为 SCFIFO(同步 FIFO)和 DCFIFO(异步 FIFO)。后面实例中如:多比特数据做跨时钟域的转换、前后带宽不同步等都用到了FIFO。

同步FIFO-SCFIFO

SCFIFO IP核配置

图片

图片

图片

full:写满标志位,有效表示 FIFO 已经存储满了,此时禁止再往FIFO中写入数据,防止数据溢出丢失。当写入数据量达到FIFO设置的最大空间时,时钟上升沿写入最后一个数据同时full拉高;读取数据时随时钟上升沿触发同时拉低。

empty:读空标志位,有效表示 FIFO 中已经没有数据了,此时禁止FIFO继续再读出数据,否则读出的将是无效数据。写入数据同时拉低;读到最后一个数据同时拉高。

usedw:显示当前FIFO中已存数据个数,写第一个数据时就置1,空或满时值为0(满是因为寄存器溢出)。

almost full:几乎满标志信号,我们可以控制FIFO快要被写满的时候和full信号的作用一样。

almost empty:几乎空标志信号,我们可以控制FIFO快要被读空的时候和empty信号的作用一样。

Asynchronous clear:异步复位信号,用于清空FIFO。

Synchronous clear:同步复位信号,用于清空FIFO。

后面三个没有使用

图片

图片

图片

图片

SCFIFO IP核调用

我们需要写一个顶层模块,并通过testbench定义激励来观察信号变化,验证SCFIFO IP核。我们可以看到生成的模块文件中有以下几个端口,顶层模块需要进行实例化

图片

输入信号有:sys_clk、输入256个8bit的数据pi_data(值为十进制0~255),输入数据有效的标志信号pi_flag,写请求信号rdreq。输出信号有:读取的数据po_data、空标志信号empty、满标志信号full、指示FIFO中存在数据个数的信号usedw。

编写代码

module fifo(
input wire sys_clk , 
input wire [7:0] pi_data ,
input wire pi_flag , 
input wire rdreq , 
output wire [7:0] po_data ,
 output wire empty , 
 output wire full , 
 output wire [7:0] usedw 
 );


 scfifo_256x8 scfifo_256x8_inst(
 .clock (sys_clk ), 
 .data (pi_data ), 
 .rdreq (rdreq ), 
 .wrreq (pi_flag ),
 .empty (empty ), 
 .full (full ), 
 .q (po_data ), 
 .usedw (usedw ) 
 );
 endmodule

图片

编写testbench

//reg define
reg sys_clk ;
reg [7:0] pi_data ;
reg pi_flag ;
reg rdreq ;
reg sys_rst_n ;
reg [1:0] cnt_baud ;


 //wire define
 wire [7:0] po_data ;
 wire empty ;
 wire full ;
 wire [7:0] usedw ;


 //初始化系统时钟、复位
 initial begin
 sys_clk = 1'b1;
 sys_rst_n <= 1'b0;
 #100;
 sys_rst_n <= 1'b1;
 end


 //sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50MHz
 always #10 sys_clk = ~sys_clk;


 //cnt_baud:计数从0到3的计数器,用于产生输入数据间的间隔
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_baud <= 2'b0;
 else if(&cnt_baud == 1'b1)
 cnt_baud <= 2'b0;
 else
 cnt_baud <= cnt_baud + 1'b1;


 //pi_flag:输入数据有效标志信号,也作为FIFO的写请求信号
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 pi_flag <= 1'b0;
 else if((cnt_baud == 2'd0) && (rdreq == 1'b0))
 pi_flag <= 1'b1;
 else
 pi_flag <= 1'b0;


 //pi_data:输入顶层模块的数据,要写入到FIFO中的数据
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 pi_data <= 8'b0;
 else if((pi_data == 8'd255) && (pi_flag == 1'b1))
 pi_data <= 8'b0;
 else if(pi_flag == 1'b1) 
 pi_data <= pi_data + 1'b1;


 //rdreq:FIFO读请求信号
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 rdreq <= 1'b0;
 else if(full == 1'b1) 
 rdreq <= 1'b1;
 else if(empty == 1'b1) 
 rdreq <= 1'b0;


 fifo fifo_inst(
 .sys_clk (sys_clk ), 
 .pi_data (pi_data ),
 .pi_flag (pi_flag ), 
 .rdreq (rdreq ),
 .po_data (po_data ), 
 .empty (empty ), 
 .full (full ), 
 .usedw (usedw ) 
 );


 endmodule

初始化:初始时钟为高电平,复位有效,延迟100ns后复位释放

模拟时钟:每隔10ns翻转,时钟周期20ns,频率50MHz

输入间隔计数器cnt_baud:从0-3计数,复位和溢出归0,其他情况+1,这里溢出判断条件(cnt_baud=11)用的是位与为1

输入有效标志信号pi_flag:也是写请求信号,变化条件是时钟上升沿和复位下降沿。复位有效时归0;计数0且没有读请求时拉高相当于四个时钟周期产生一次;其他情况归0

输入数据pi_data:是要写到FIFO中的数据,变化条件是时钟上升沿和复位下降沿。复位有效时归0;输入255且pi_flag有效时归0;其他情况+1,意味着输入数据从0-255循环

读请求rdreq:变化条件是时钟上升沿和复位下降沿。复位有效时归0;full信号拉高时也拉高说明存满该读了;empty拉高时就归0说明读空禁读了

实例化

波形变化

图片

我们选择的是普通模式,读出数据比读使能晚一拍图片

图片

对比一下"先出数据FIFO模式"的波形,就可以看出延迟不延迟一拍的区别,这里没有演示,实际上只需要在下面这一步时选择先出数据模式即可

图片

图片

异步FIFO-SCFIFO

DCFIFO IP核配置

命名为"dcfifo_256x8to128x16",我们调用的dcfifo是输入256个深度8位宽、输出128个深度16位宽

图片

图片

图片

图片

图片

图片

图片

DSCFIFO IP核调用

我们需要写一个顶层模块,并通过testbench定义激励来观察信号变化,验证DCFIFO IP核。我们可以看到生成的模块文件中有以下几个端口,顶层模块需要进行实例化

图片

输入信号 :50MHz写时钟wrclk,输入256个8bit的数据pi_data(值为十进制0~255),输入数据有效的标志信号pi_flag,25MHz的读时钟rdclk,写请求信号rdreq。 输出信号 :同步于wrclk的空标志信号wrempty,同步于wrclk的满标志信号wrfull,同步于wrclk指示FIFO中存在数据个数的信号wrusedw,从FIFO中读取的数据po_data,同步于rdclk的FIFO空标志信号rdempty,同步于rdclk 的FIFO满标志信号rdfull,同步于rdclk指示FIFO中存在数据个数的信号rdusedw。

编写代码

module fifo
(
//同步于FIFO写时钟
input wire wrclk , 
input wire [7:0] pi_data , 
input wire pi_flag , 
 //同步于FIFO读时钟
 input wire rdclk , 
 input wire rdreq , 
 //同步于FIFO写时钟
 output wire wrempty ,
 output wire wrfull , 
 output wire [7:0] wrusedw ,
 //同步于FIFO读时钟
 output wire [15:0] po_data ,
 output wire rdempty , 
 output wire rdfull , 
 output wire [6:0] rdusedw 
 );


 dcfifo_256x8to128x16 dcfifo_256x8to128x16_inst
 (
 .data (pi_data), //input [7:0] data
 .rdclk (rdclk ), //input rdclk
 .rdreq (rdreq ), //input rdreq
 .wrclk (wrclk ), //input wrclk
 .wrreq (pi_flag), //input wrreq
 .q (po_data), //output [15:0] q
 .rdempty(rdempty), //output rdempty
 .rdfull (rdfull ), //output rdfull
 .rdusedw(rdusedw), //output [6:0] rdusedw
 .wrempty(wrempty), //output wrempty
 .wrfull (wrfull ), //output wrfull
 .wrusedw(wrusedw) //output [7:0] wrusedw
 );


 endmodule

编写testbench

`timescale 1ns/1ns
module tb_fifo();


//reg define
reg wrclk ;
reg [7:0] pi_data ;
reg pi_flag ;
reg rdclk ;
reg rdreq ;
reg sys_rst_n ;
reg [1:0] cnt_baud ;
reg wrfull_reg0 ;
reg wrfull_reg1 ;


//wire define
wire wrempty ;
wire wrfull ;
wire [7:0] wrusedw ;
wire [15:0] po_data ;
wire rdempty ;
wire rdfull ;
wire [6:0] rdusedw ;


//初始化时钟、复位
initial begin
wrclk = 1'b1;
rdclk = 1'b1;
sys_rst_n <= 1'b0;
#100;
sys_rst_n <= 1'b1;
end


//wrclk:模拟FIFO的写时钟,每10ns电平翻转一次,周期为20ns,频率为50MHz
always #10 wrclk = ~wrclk;
//rdclk:模拟FIFO的读时钟,每20ns电平翻转一次,周期为40ns,频率为25MHz
always #20 rdclk = ~rdclk;


//cnt_baud:计数从0到3的计数器,用于产生输入数据间的间隔
always@(posedge wrclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_baud <= 2'b0;
else if(&cnt_baud == 1'b1)
cnt_baud <= 2'b0;
else
cnt_baud <= cnt_baud + 1'b1;


//pi_flag:输入数据有效标志信号,也作为FIFO的写请求信号
always@(posedge wrclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_flag <= 1'b0;
else if((cnt_baud == 2'd0) && (rdreq == 1'b0))
pi_flag <= 1'b1;
else
pi_flag <= 1'b0;


//pi_data:输入顶层模块的数据,要写入到FIFO中的数据
always@(posedge wrclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_data <= 8'b0;
else if((pi_data == 8'd255) && (pi_flag == 1'b1))
pi_data <= 8'b0;
else if(pi_flag == 1'b1) 
pi_data <= pi_data + 1'b1;


//将同步于rdclk时钟的写满标志信号wrfull在rdclk时钟下打两拍
always@(posedge rdclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
wrfull_reg0 <= 1'b0;
wrfull_reg1 <= 1'b0;
end
else
begin
wrfull_reg0 <= wrfull;
wrfull_reg1 <= wrfull_reg0;
end


//rdreq:FIFO读请求信号同步于rdclk时钟
always@(posedge rdclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rdreq <= 1'b0;


//如果wrfull信号有效就立刻读,则不会看到rd_full信号拉高,
//所以此处使用wrfull在rdclk时钟下打两拍后的信号
else if(wrfull_reg1 == 1'b1)
rdreq <= 1'b1;
else if(rdempty == 1'b1)//当FIFO中的数据被读空时停止读取FIFO中的数据
rdreq <= 1'b0;

 fifo fifo_inst(
 .wrclk (wrclk ), 
 .pi_data(pi_data),
 .pi_flag(pi_flag),
 .rdclk (rdclk ),
 .rdreq (rdreq ), 
 .wrempty(wrempty), 
 .wrfull (wrfull ), 
 .wrusedw(wrusedw), 
 .po_data(po_data), 
 .rdempty(rdempty), 
 .rdfull (rdfull ), 
 .rdusedw(rdusedw) 
 );


 endmodule

初始化:和同步fifo的区别在于读写时钟是异步的,需要初始化两个时钟

时钟模拟:写时钟50MHz每隔10ns翻转一次,读时钟25MHz每隔20ns翻转一次

cnt_baud:计数器是基于写时钟的,将变化条件中时钟上升沿改为写时钟的上升沿

pi_flag:输入数据有效标志信号,基于写时钟,同上

pi_data:输入数据,基于写时钟,同上

wrfull:写满标志信号,基于读时钟,变化条件是读时钟上升沿和复位下降沿。wrfull在读时钟下打两拍,(always块中的语句是顺序执行的,使用两个寄存器b,c,令b=输入a,c=b,并输出c,那么c相对于a而言波形不会产生变化,只是有两个时钟周期的延迟,这个就叫打拍,使用几个寄存器就是延迟几个周期就是打几拍),这里打两拍的原因是如果wrfull有效立刻就读,就看不到rd_full拉高

rdreq:读请求信号,基于读时钟,变化条件是读时钟上升沿和复位下降沿。复位时归0;写满信号打两拍之后的信号拉高就拉高表示写满需要读,读空信号拉高就拉低表示读空不能读

波形变化

图片

可以看到当pi_flag为高且pi_data为255的同时wrfull满标志信号先拉高了,延后一段时间rdfull满标志信号也拉高了,说明FIFO的存储空间已经满了。wrfull满标志信号和rdfull满标志信号同步于 不同的时钟 ,所以拉高的时间不同步。

还可以看到wrusedw信号是8位的计数到255,而rdusedw信号是7位的计数到127,因为输入是8btit的,输出是16bit的,8256=16128。wrusedw信号从255变成了0、rdusedw信号从127变成0的原因和SCFIFO中的情况一样,都是因为数据存储满了,FIFO内部的计数器溢出所导致的。

另外可以看出读出的16bit数据,是两次的输入8bit数据拼凑而成,先输入的在低位,后输入的在高位,例如输入00,01,02,03...,两次输入的数据拼凑为01_00,03_02进行输出。

如果不打两拍会发生什么呢?我们来看波形

图片

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1602

    文章

    21320

    浏览量

    593193
  • 存储器
    +关注

    关注

    38

    文章

    7148

    浏览量

    161985
  • 缓冲器
    +关注

    关注

    6

    文章

    1566

    浏览量

    44877
  • fifo
    +关注

    关注

    3

    文章

    369

    浏览量

    43069
  • IP核
    +关注

    关注

    4

    文章

    317

    浏览量

    49041
收藏 人收藏

    评论

    相关推荐

    #FPGA点拨 生成FIFOIP

    fpgaIP
    电子技术那些事儿
    发布于 :2022年10月12日 21:52:56

    #硬声创作季 #FPGA FPGA-27-04 Quartus中fifo IP介绍与仿真测试-1

    fpgafifoIP
    水管工
    发布于 :2022年10月29日 02:28:30

    #硬声创作季 #FPGA FPGA-27-04 Quartus中fifo IP介绍与仿真测试-2

    fpgafifoIP
    水管工
    发布于 :2022年10月29日 02:28:50

    #硬声创作季 #FPGA FPGA-27-04 Quartus中fifo IP介绍与仿真测试-3

    fpgafifoIP
    水管工
    发布于 :2022年10月29日 02:29:13

    #硬声创作季 #FPGA FPGA-27-04 Quartus中fifo IP介绍与仿真测试-4

    fpgafifoIP
    水管工
    发布于 :2022年10月29日 02:29:34

    xilinx FPGA的FFT IP的调用

    有没有大神可以提供xilinx FPGA的FFT IP的调用的verilog 的参考程序,最近在学习FFT的IP
    发表于 12-25 17:05

    LabVIEW FPGA CORDIC IP的arctan使用方法

    使用LabVIEW FPGA模块中的CORDIC IP,配置arctan(X/Y)算法,配置完成之后,IP只有一个输入。我参考网上VHD
    发表于 09-10 20:07

    【正点原子FPGA连载】第十三章IPFIFO实验-领航者ZYNQ之FPGA开发指南

    不能像RAM和ROM那样可以由地址线决定读取或写入某个指定的地址。本章我们将对Vivado软件生成的FIFO IP进行读写测试,来向大家介绍Xilinx FIFO
    发表于 09-23 17:27

    基于IPFPGA设计方法是什么?

    的分类和特点是什么?基于IPFPGA设计方法是什么?
    发表于 05-08 07:07

    FPGA零基础学习IP CORE 之 FIFO设计

    学习FPGA设计方法及设计思想的同时,实操结合各类操作软件,会让你在技术学习道路上无比的顺畅,告别技术学习小BUG卡破脑壳,告别目前忽悠性
    发表于 03-15 16:19

    FPGAIP学习的正确打开方式

    的情况时,总会遇到一些以前未曾接触过的新内容,这些新内容会让我们感到陌生和恐惧,不知道该如何下手。 那么今天以xilinx vivado 为例分享学习FPGAIP的正确打开方式
    发表于 11-17 11:09

    FPGA学习笔记:PLL IP核的使用方法

    IP(Intellectual Property)是知识产权的意思,半导体行业的IP是“用于ASIC或FPGA中的预先设计好的电路功能模块”。一些常用的复杂的功能模块(如FIFO、RA
    的头像 发表于 08-22 15:04 1901次阅读
    <b class='flag-5'>FPGA</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>:PLL <b class='flag-5'>IP</b>核的<b class='flag-5'>使用方法</b>

    FPGA学习笔记:ROM IP核的使用方法

    上一篇介绍了常用的锁相环IP,这一节将介绍一种较为常用的 存储类IP核 ——ROM的使用方法。ROM是 只读存储器 (Read-Only Memory),顾名思义,我们只能读出事先存放在固态中的数据
    的头像 发表于 08-22 15:06 2094次阅读
    <b class='flag-5'>FPGA</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>:ROM <b class='flag-5'>IP</b>核的<b class='flag-5'>使用方法</b>

    FPGA学习笔记:RAM IP核的使用方法

    我们知道除了只读存储器外还有随机存取存储器,这一篇将介绍另一种 存储类IP核 ——RAM的使用方法。RAM是 随机存取存储器 (Random Access Memory),是一个易失性存储器,断电丢失。RAM工作时可以随时从任何一个指定的地址写入或读出数据。
    的头像 发表于 08-29 16:46 1857次阅读
    <b class='flag-5'>FPGA</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>:RAM <b class='flag-5'>IP</b>核的<b class='flag-5'>使用方法</b>

    XILINX FPGA IPFIFO Generator例化仿真

    上文XILINX FPGA IPFIFO对XILINX FIFO Generator IP的特性和内部处理流程进行了简要的说明,本文通过实
    的头像 发表于 09-07 18:31 903次阅读
    XILINX <b class='flag-5'>FPGA</b> <b class='flag-5'>IP</b>之<b class='flag-5'>FIFO</b> Generator例化仿真