FPGA零基础学习:IP CORE 之 FIFO设计
本系列将带来FPGA的系统性学习,从最基本的数字电路基础开始,最详细操作步骤,最直白的言语描述,手把手的“傻瓜式”讲解,让电子、信息、通信类专业学生、初入职场小白及打算进阶提升的职业开发者都可以有系统性学习的机会。
系统性的掌握技术开发以及相关要求,对个人就业以及职业发展都有着潜在的帮助,希望对大家有所帮助。后续会陆续更新 Xilinx 的 Vivado、ISE 及相关操作软件的开发的相关内容,学习FPGA设计方法及设计思想的同时,实操结合各类操作软件,会让你在技术学习道路上无比的顺畅,告别技术学习小BUG卡破脑壳,告别目前忽悠性的培训诱导,真正的去学习去实战应用。话不多说,上货。
IP CORE 之 FIFO设计
作者:郝旭帅 校对:陆辉
本篇实现基于叁芯智能科技的SANXIN -B01 FPGA开发板,以下为配套的教程,如有入手开发板,可以登录官方淘宝店购买,还有配套的学习视频。
FIFO(first input first output或者first in first out),先入先出队列,是一种数字电路中常用的缓冲器,先进入的数据或者命令会先出来,后进入的数据或者命令会后出来,不改变数据的先后顺序。FIFO的工作方式就像超市购物结账时的通道,先进入的顾客先结账,然后出超市;当先进入的顾客没有结算完成时,或进入的顾客只能进行等待;进入的顺序和走出超市的顺序是相同的。
在大多数的逻辑接口设计时,都会留有一定的缓冲区(FIFO),避免数据没有及时发送(接收)。
无论多大的缓冲区都可能会被装满。当装满后,再次进行载入时,就会出现错误(覆盖或者丢失),所以缓冲区会给予外部标志信号,表明自己的状态。
FIFO的输入和输出的速率可以是不相同的,这就为我们解决多bit数据线跨时钟域的问题提供了方法。
对于输入端口来说,只要FIFO中还有空余位置,就可以写入数据;对于输出端口来说,只要FIFO中还有数据,就可以读出数据。
写一侧的所有信号都同步于写时钟,读一侧的所有信号都同步于读时钟。
设计要求
设计宽度为8、缓冲深度为256、输入速率为100MHz、输出速率为50MHz和各类标志信号的FIFO。
设计原理
FPGA内部没有FIFO的电路,实现原理为利用FPGA内部的SRAM和可编程逻辑实现。
quartus软件中提供了FIFO的ip core,设计者不需要自己设计可编程逻辑和SRAM组成FIFO。
设计者也可以自己设计FIFO。
本节讲述调用quartus中的FIFO ip core。
架构设计和信号说明
此模块命名为fifo_test,fifo_my为调用的ip core。
由于FIFO的深度为256,所以两侧的使用量信号最大值可以为256,所以位宽为9。
调用FIFO
建立工程,并在qprj中,建立ipcore文件夹,在ipcore文件夹中建立fifo_my文件夹。
打开tools -> ip catalog。
双击打开FIFO。
选择verilog,选择路径为ipcore->fifo_my->,命名为fifo_my。点击OK。
选择深度为256,宽度为8,选择独立的读写时钟。点击Next。
此界面保持默认,下一步。
将所有的读写标志信号线全部选中,并且在usedw最前面加上一位。默认当fifo的深度为256时,usedw的宽度为8,当fifo满了时,usedw的值为0。当添加上一位后,fifo满时,usedw的值为256。点击Next。
此界面保持默认,点击Next。
此界面保持默认,点击Next。
此界面保持默认,点击Next。
选择上fifo_my_inst.v,选择finish。
将生成的fifo_my添加进工程,点击Yes。
顶层设计
顶层负责调用fifo_my,例化文件在ip core -> fifo_my -> fifo_my_inst.v中。
设计代码为:
代码语言:javascript复制module fifo_test (
input wire wrclk,
input wire wren,
input wire [7:0] wrdata,
output wire wrempty,
output wire wrfull,
output wire [8:0] wrusedw,
input wire rdclk,
input wire rden,
output wire [7:0] rdata,
output wire rdempty,
output wire rdfull,
output wire [8:0] rdusedw
);
fifo_my fifo_my_inst (
.data ( wrdata ),
.rdclk ( rdclk ),
.rdreq ( rden ),
.wrclk ( wrclk ),
.wrreq ( wren ),
.q ( rdata ),
.rdempty ( rdempty ),
.rdfull ( rdfull ),
.rdusedw ( rdusedw ),
.wrempty ( wrempty ),
.wrfull ( wrfull ),
.wrusedw ( wrusedw )
);
endmodule
RTL仿真
在应用时,只要检测到wrfull不为高时,就可以写入数据;检测到rdempty不为高时,就可以读出数据;在仿真时,我们做简单测试,将随机的256个数据,写入fifo中;然后将256个数据读出。
设计代码为:
代码语言:javascript复制`timescale 1ns/1ps
module fifo_test_tb;
reg wrclk;
reg wren;
reg [7:0] wrdata;
wire wrempty;
wire wrfull;
wire [8:0] wrusedw;
reg rdclk;
reg rden;
wire [7:0] rdata;
wire rdempty;
wire rdfull;
wire [8:0] rdusedw;
fifo_test fifo_test_inst(
.wrclk (wrclk),
.wren (wren),
.wrdata (wrdata),
.wrempty (wrempty),
.wrfull (wrfull),
.wrusedw (wrusedw),
.rdclk (rdclk),
.rden (rden),
.rdata (rdata),
.rdempty (rdempty),
.rdfull (rdfull),
.rdusedw (rdusedw)
);
initial wrclk = 1'b0;
always # 5 wrclk = ~wrclk;
initial rdclk = 1'b0;
always # 10 rdclk = ~rdclk;
initial begin
wren = 1'b0;
wrdata = 8'd0;
rden = 1'b0;
# 200
repeat (256) begin
@ (posedge wrclk);
# 2;
wren = 1'b1;
wrdata = {$random} % 256;
end
@ (posedge wrclk);
# 2;
wrdata = 8'd0;
wren = 1'b0;
# 200
repeat (256) begin
@ (posedge rdclk);
# 2;
rden = 1'b1;
end
@ (posedge rdclk);
# 2;
rden = 1'b0;
# 200;
$stop;
end
endmodule
wrclk的时钟速率为100MHz,rdclk的时钟速率为50MHz。
通过RTL波形可以看出写一侧的信号同步于wrclk,读一侧的信号同步于rdclk。
在没有数据写入时,wrempty为高电平,当写入第一个数据后,wrempty信号拉低;wrusedw延迟一拍输出统计个数。此时读一侧也会有一定的输出,不过会有略微延迟。输入的前五个数据为36,129,9,99,13。
在输入最后一个数据107时,wrfull立刻拉高。wrusedw信号延迟一拍后,输出256。
读数据时,输出数据和输入的数据是相同的。延迟一拍后,数据输出并且rdfull拉低,再延迟一拍后,统计量输出。此时写一侧的信号也会有变化,但是会有略微延迟。
读出最后一个数据107后,rdempty拉高。