目录
- 实验内容
- 实验原理
- FIFO IP 核
- 时序绘制
- HDL 代码
- 仿真
- 综合实现
- 上板测试
实验内容
- 理解 FIFO 原理
- 调用 FIFO IP 核完成数据读写
实验原理
- FIFO:First In First Out,先入先出式数据缓冲器,用来实现数据先入先出的读写方式。可分类为同步 FIFO 和异步 FIFO,读写时钟相同即为同步 FIFO,读写时钟不同即为异步 FIFO。
- FIFO 框图:
- FIFO 端口定义与说明:
写端口 | 说明 | 读端口 | 说明 |
---|---|---|---|
wr_clk | 写时钟 | rd_clk | 读时钟 |
wr_en | 写使能 | rd_en | 读使能 |
din | 写入的数据 | dout | 读出的数据 |
full | 满信号 | empty | 空信号 |
almost_full | 将满信号 | almost_empty | 将空信号 |
prog_full | 可配置满信号 | prog_empty | 可配置空信号 |
full:当 FIFO 已满时,满信号输出高电平,此时的写入操作失效。
almost_full:当 FIFO 差一个数据即满时,将满信号输出高电平,此时只可再进行一次写入操作。
prog_full:用户可自定义阈值,若 FIFO 存储的数据量超过阈值,可配置满信号输出高电平。
FIFO IP 核
- 添加 FIFO Generator IP 核:IP Catalog -> FIFO Generator
- Basic 界面
- Fifo Implementation:用来设置同步/异步,以及使用的资源,常用为 Independent Clocks Block RAM
- Synchronization Stages:跨时钟域逻辑的同步器级数,保持默认即可。数值 2 代表,empty 信号会在 FIFO 成功写入数据后的 2 个读时钟周期后拉低。
- Native Ports 界面
- Read Mode:Standard FIFO 标准模式,输出数据延迟读使能一拍,First Word Fall Through 预读模式,输出数据与读使能同步。
- Data Port Parameters:调整数据位宽和深度
- ECC:数据校验时开启,这里默认不开启
- Output Registers:输出寄存器,可以改善 FIFO 时序,但输出会延迟一拍
- Initialization:设置复位信号,Enable Reset Synchronization 启用同步复位
- Enable Safety Circuit:启用安全电路,复位信号至少要保持八个时钟周期(以慢时钟为准)的有效,且在复位后至少要经过六十个时钟周期(以慢时钟为准)后,才能对 FIFO 进行写数据操作。
- 其余保持默认,最后会统计出输出延迟 Read Latency
- Status Flags 界面:可配置标志位信号,有需求时启用
- Data Counts 界面:可配置读写数据计数,有需求时启用
时序绘制
- 实验按照异步 FIFO 进行设计,使用 100MHz 时钟信号作为写时钟,50MHz 时钟信号作为读时钟。
- 复位后,由于安全电路的存在,慢时钟(读时钟)计数六十拍后才可启动 FIFO,启动后将标志位 state 信号拉高。
- 启动后,若 FIFO 未满,即 full 信号为低电平时,则拉高 wr_en,开始写数据;启动后,若 FIFO 不空,即 empty 信号为低电平,则拉高 rd_en,开始读数据。
HDL 代码
`timescale 1ns / 1ps
module FIFO(input wire sys_clk_p,input wire sys_clk_n,input wire rst_n
);/**********************************************
*********** 例化PLL
**********************************************/wire sys_clk_100M;wire sys_clk_50M;wire locked;clk_wiz_0 inst_clk(// Clock out ports.clk_out1(sys_clk_100M), // output clk_out1.clk_out2(sys_clk_50M), // output clk_out2// Status and control signals.reset(1'b0), // input reset.locked(locked), // output locked// Clock in ports.clk_in1_p(sys_clk_p), // input clk_in1_p.clk_in1_n(sys_clk_n) // input clk_in1_n);/**********************************************
*********** 例化FIFO
**********************************************/reg [15:0] din;wire wr_en;wire rd_en;wire [15:0] dout;wire full;wire empty;wire wr_rst_busy;wire rd_rst_busy;fifo_0 inst_fifo (.rst(!rst_n), // input wire rst.wr_clk(sys_clk_100M), // input wire wr_clk.rd_clk(sys_clk_50M), // input wire rd_clk.din(din), // input wire [15 : 0] din.wr_en(wr_en), // input wire wr_en.rd_en(rd_en), // input wire rd_en.dout(dout), // output wire [15 : 0] dout.full(full), // output wire full.empty(empty), // output wire empty.wr_rst_busy(wr_rst_busy), // output wire wr_rst_busy.rd_rst_busy(rd_rst_busy) // output wire rd_rst_busy);/**********************************************
*********** 信号赋值
**********************************************/ // 赋值启动标志位,state为高时代表可以开始FIFO读写reg state;reg [7:0] start_cnt;always@(posedge sys_clk_50M) beginif(!rst_n) beginstart_cnt <= 8'b0;endelse if(state) beginstart_cnt <= 8'b0;endelse beginstart_cnt <= start_cnt + 8'b1;endendalways@(posedge sys_clk_50M) beginif(!rst_n) beginstate <= 1'b0;endelse if(start_cnt == 8'd60) beginstate <= 1'b1;endend// 组合逻辑赋值wr_en,当state为1时,wr_en的取值与full信号相反assign wr_en = (state == 1'b1) ? ~full : 1'b0;// 赋值dinalways@(posedge sys_clk_100M) beginif(!rst_n) begindin <= 16'b0;endelse if(wr_en) begindin <= din + 16'b1;endend// 组合逻辑赋值rd_en,当state为1时,rd_en的取值与empty信号相反assign rd_en = (state == 1'b1) ? ~empty : 1'b0;/**********************************************
*********** 例化ILA
**********************************************/ ila_0 inst_ila (.clk(sys_clk_100M), // input wire clk.probe0(state), // input wire [0:0] probe0 .probe1(wr_en), // input wire [0:0] probe1 .probe2(din), // input wire [15:0] probe2 .probe3(rd_en), // input wire [0:0] probe3 .probe4(dout) // input wire [15:0] probe4);endmodule
仿真
testbench 代码:
`timescale 1ns / 1ps
module tb_FIFO();/**********************************************
*********** 实例化模块
**********************************************/reg sys_clk_p;wire sys_clk_n;reg rst_n;FIFO tb_FIFO(.sys_clk_p(sys_clk_p),.sys_clk_n(sys_clk_n),.rst_n(rst_n));/**********************************************
*********** 初始化 clk、rst
**********************************************/// 初始化 clkassign sys_clk_n = ~sys_clk_p;initial beginsys_clk_p = 1;forever #2.5 sys_clk_p = ~sys_clk_p; end// 初始化 rstinitial beginrst_n = 0;#302.5;rst_n = 1;end
endmodule
仿真时序图:
- 当 start_cnt 计数到 60 后,state 标志位置一,开始启动 FIFO
- 当 wr_en 置高,FIFO 开始写入数据,当 rd_en 置高,FIFO 开始读出数据,输出的数据延迟 rd_en 一拍
- 当 FIFO 存满时,full 信号被拉高,等待数据被读取后, full 信号被拉低,即可再次写入数据
综合实现
添加管脚和时序约束后,run synthesis 以及 run implementation,具体步骤可参考:FPGA上板项目(一)——点灯熟悉完整开发流程、ILA在线调试
上板测试
上板测试结果如下: