本系列将带来FPGA的系统性学习,从最基本的数字电路基础开始,最详细操作步骤,最直白的言语描述,手把手的“傻瓜式”讲解,让电子、信息、通信类专业学生、初入职场小白及打算进阶提升的职业开发者都可以有系统性学习的机会。
系统性的掌握技术开发以及相关要求,对个人就业以及职业发展都有着潜在的帮助,希望对大家有所帮助。后续会陆续更新 Xilinx 的 Vivado、ISE 及相关操作软件的开发的相关内容,学习FPGA设计方法及设计思想的同时,实操结合各类操作软件,会让你在技术学习道路上无比的顺畅,告别技术学习小BUG卡破脑壳,告别目前忽悠性的培训诱导,真正的去学习去实战应用,这种快乐试试你就会懂的。话不多说,上货。
IP CORE 之 FIFO 设计 - ISE操作工具
作者:李西锐 校对:陆辉
本篇实现基于叁芯智能科技的SANXIN -B02 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和可编程逻辑实现。
ISE软件中提供了FIFO的ip core,设计者不需要自己设计可编程逻辑和SRAM组成FIFO。
设计者也可以自己设计FIFO。
本节讲述调用ISE中的FIFO ip core。
- 架构设计和信号说明
此模块命名为fifo_test,my_fifo为调用的ip core。
由于FIFO的深度为256,所以两侧的使用量信号最大值可以为256,所以位宽为9。
- 调用FIFO
建立工程,右键点击顶层,选择New Source。新建IP核。
搜索FIFO,选择FIFO Generator。点击Next。
保持默认设置,点击Next。
此界面是设置FIFO读写时钟方式:
Block RAM: 指的是FPGA内部硬件已经存在的RAM,是硬件资源。在FPGA芯片内部都有这种已经设置好的芯片资源;
Distribute RAM: 分布式RAM,使用FPGA内部的寄存器和查找表搭建起来的RAM,当深度要求小于32的时候可以使用;
Shift Register: 使用FIFO产生一个移位寄存器;
Common clock: 指的就是同步FIFO;
Independent clock: 指的就是异步FIFO。
本页需要设置数据参数,包括数据位宽以及数据深度。
这一页的选项就是一些标志位,用来标记我们是否出现了读写溢出之类的。
Almost Full flag:就是我们可以设置一个位置,当写指针写道这个位置的时候,就会提醒说近乎满了,常用来设计警告之类的;
Almost Empty Flag :类似上面的作用;
overflow:就是写指针追到了读指针,然后还继续写就会overflow。
这里需要我们进行初始化设置,复位管脚我们不需要,所以不需要添加。保持默认,点击Next。
点击Next。
设置完成之后,点击Generate。
- 顶层设计
顶层负责调用my_fifo,例化文件在ipcore_dir -> my_fifo.veo 中。
设计代码为:
代码语言:javascript复制module fifo_test(
input wr_clk,
input rd_clk,
input [7:0] wrdata,
input wren,
input rden,
output full,
output empty,
output [7:0] rdata
);
my_fifo my_fifo_inst (
.wr_clk(wr_clk),
.rd_clk(rd_clk),
.din(wrdata),
.wr_en(wren),
.rd_en(rden),
.dout(rdata),
.full(full),
.empty(empty)
);
endmodule
- RTL仿真
在应用时,只要检测到wrfull不为高时,就可以写入数据;检测到rdempty不为高时,就可以读出数据;在仿真时,我们做简单测试,将随机的256个数据,写入fifo中;然后将256个数据读出。
设计代码为:
代码语言:javascript复制`timescale 1ns/1ps
module fifo_test_tb;
reg wr_clk;
reg rd_clk;
reg [7:0] wrdata;
reg wren;
reg rden;
wire full;
wire empty;
// Outputs
wire [7:0] rdata;
// Instantiate the Unit Under Test (UUT)
fifo_test fifo_test_inst (
.wr_clk(wr_clk),
.rd_clk(rd_clk),
.wrdata(wrdata),
.wren(wren),
.rden(rden),
.full(full),
.empty(empty),
.rdata(rdata)
);
initial wr_clk = 1'b0;
always # 5 wr_clk = ~wr_clk;
initial rd_clk = 1'b0;
always # 10 rd_clk = ~rd_clk;
initial begin
wren = 1'b0;
wrdata = 8'd0;
rden = 1'b0;
# 200
repeat (1100) begin
@ (posedge wr_clk);
# 2;
wren = 1'b1;
wrdata = {$random} % 256;
end
@ (posedge wr_clk);
# 2;
wrdata = 8'd0;
wren = 1'b0;
# 200
repeat (1100) begin
@ (posedge rd_clk);
# 2;
rden = 1'b1;
end
@ (posedge rd_clk);
# 2;
rden = 1'b0;
# 200;
$stop;
end
endmodule
wrclk的时钟速率为100MHz,rdclk的时钟速率为50MHz。
通过RTL波形可以看出写一侧的信号同步于wr_clk,读一侧的信号同步于rd_clk。
在没有数据写入时,empty为高电平,当写入第一个数据后,empty信号拉低。
在输入最后一个数据68时,full立刻拉高。
读数据时,输出数据和输入的数据是相同的。延迟一拍后,数据输出并且full拉低,再延迟一拍后,统计量输出。此时写一侧的信号也会有变化,但是会有略微延迟。
读出最后一个数据47后,empty拉高。
- End -