ZYNQ-PL实践课堂(五)IP核之FIFO
ZYNQ-PL实践课堂(四)IP核之FIFO)
- 1 概述
- 2 程序
- 2.1 FIFO IP核
- 2.2 写FIFO模块
- 2.3 读FIFO模块
- 2.4 顶层例化模块
- 3 仿真
- 总结
1 概述
FIFO在fpga应用过程相当于一个先进先出的缓冲器,跨时钟域传输信号传递,采用顺序写入数据并顺序独处数据。
根据FIFO工作的时钟域可以分为同步FIFO和异步FIFO。
选择同步 FIFO: 只使用 wr_clk,所有的输入输出信号都同步于 wr_clk 信号;
选择异步 FIFO: 写端口同步于写时钟 wr_clk和读端口同步于读时钟 rd_clk。
FIFO的参数如下:
FIFO 宽度 : 一次读写操作的数据位 N;
FIFO 深度 : 存储多少个宽度为 N 位的数据;
将空标志 : 即将被读空;
空标志 : 已空时由 FIFO 的状态电路送出的一个信号;
将满标志 : 即将被写满。
满标志 : 已满或将要写满时由 FIFO 的状态电路送出的一个信号;
读时钟 : 读 FIFO 时所遵循的时钟,在每个时钟的上升沿触发。
写时钟 : 写 FIFO 时所遵循的时钟,在每个时钟的上升沿触发。
2 程序
2.1 FIFO IP核
选择IP Catalog添加FIFO,
Basic 设置异步FIFO,
设置写FIFO深度、写FIFO宽度、读FIFO宽度如下,
设定专用的输入口, 使用“即将写满”和“即将读空”这两个信号,
“Data Counts”设置 FIFO 内数据计数的输出信号,
Summary如下,
2.2 写FIFO模块
module fifo_wr(
//mudule clock
input clk , // 时钟信号
input rst_n , // 复位信号
//FIFO interface
input almost_empty, // FIFO将空信号
input almost_full , // FIFO将满信号
output reg fifo_wr_en , // FIFO写使能
output reg [7:0] fifo_wr_data // 写入FIFO的数据
);
//reg define
reg [1:0] state ; //动作状态
reg almost_empty_d0 ; //almost_empty 延迟一拍
reg almost_empty_syn ; //almost_empty 延迟两拍
reg [3:0] dly_cnt ; //延迟计数器
//*****************************************************
//** main code
//*****************************************************
//因为 almost_empty 信号是属于FIFO读时钟域的
//所以要将其同步到写时钟域中
always@( posedge clk ) begin
if( !rst_n ) begin
almost_empty_d0 <= 1'b0 ;
almost_empty_syn <= 1'b0 ;
end
else begin
almost_empty_d0 <= almost_empty ;
almost_empty_syn <= almost_empty_d0 ;
end
end
//向FIFO中写入数据
always @(posedge clk ) begin
if(!rst_n) begin
fifo_wr_en <= 1'b0;
fifo_wr_data <= 8'd0;
state <= 2'd0;
dly_cnt <= 4'd0;
end
else begin
case(state)
2'd0: begin
if(almost_empty_syn) begin //如果检测到FIFO将被读空
state <= 2'd1; //就进入延时状态
end
else
state <= state;
end
2'd1: begin
if(dly_cnt == 4'd10) begin //延时10拍
//原因是FIFO IP核内部状态信号的更新存在延时
//延迟10拍以等待状态信号更新完毕
dly_cnt <= 4'd0;
state <= 2'd2; //开始写操作
fifo_wr_en <= 1'b1; //打开写使能
end
else
dly_cnt <= dly_cnt + 4'd1;
end
2'd2: begin
if(almost_full) begin //等待FIFO将被写满
fifo_wr_en <= 1'b0; //关闭写使能
fifo_wr_data <= 8'd0;
state <= 2'd0; //回到第一个状态
end
else begin //如果FIFO没有被写满
fifo_wr_en <= 1'b1; //则持续打开写使能
fifo_wr_data <= fifo_wr_data + 1'd1; //且写数据值持续累加
end
end
default : state <= 2'd0;
endcase
end
end
endmodule
2.3 读FIFO模块
module fifo_rd(
//system clock
input clk , // 时钟信号
input rst_n , // 复位信号
//FIFO interface
input [7:0] fifo_dout , // 从FIFO读出的数据
input almost_full ,// FIFO将满信号
input almost_empty,// FIFO将空信号
output reg fifo_rd_en // FIFO读使能
);
//reg define
reg [1:0] state ; // 动作状态
reg almost_full_d0 ; // fifo_full 延迟一拍
reg almost_full_syn ; // fifo_full 延迟两拍
reg [3:0] dly_cnt ; //延迟计数器
//*****************************************************
//** main code
//*****************************************************
//因为 fifo_full 信号是属于FIFO写时钟域的
//所以要将其同步到读时钟域中
always@( posedge clk ) begin
if( !rst_n ) begin
almost_full_d0 <= 1'b0 ;
almost_full_syn <= 1'b0 ;
end
else begin
almost_full_d0 <= almost_full ;
almost_full_syn <= almost_full_d0 ;
end
end
//读出FIFO的数据
always @(posedge clk ) begin
if(!rst_n) begin
fifo_rd_en <= 1'b0;
state <= 2'd0;
dly_cnt <= 4'd0;
end
else begin
case(state)
2'd0: begin
if(almost_full_syn) //如果检测到FIFO将被写满
state <= 2'd1; //就进入延时状态
else
state <= state;
end
2'd1: begin
if(dly_cnt == 4'd10) begin //延时10拍
//原因是FIFO IP核内部状态信号的更新存在延时
//延迟10拍以等待状态信号更新完毕
dly_cnt <= 4'd0;
state <= 2'd2; //开始读操作
end
else
dly_cnt <= dly_cnt + 4'd1;
end
2'd2: begin
if(almost_empty) begin //等待FIFO将被读空
fifo_rd_en <= 1'b0; //关闭读使能
state <= 2'd0; //回到第一个状态
end
else //如果FIFO没有被读空
fifo_rd_en <= 1'b1; //则持续打开读使能
end
default : state <= 2'd0;
endcase
end
end
2.4 顶层例化模块
创建 源文件 ip_fifo.v,作为顶层模块,实现前三个模块信息交互。
module ip_fifo(
input sys_clk , // 时钟信号
input sys_rst_n // 复位信号
);
//wire define
wire fifo_wr_en ; // FIFO写使能信号
wire fifo_rd_en ; // FIFO读使能信号
wire [7:0] fifo_din ; // 写入到FIFO的数据
wire [7:0] fifo_dout ; // 从FIFO读出的数据
wire almost_full ; // FIFO将满信号
wire almost_empty ; // FIFO将空信号
wire fifo_full ; // FIFO满信号
wire fifo_empty ; // FIFO空信号
wire [7:0] fifo_wr_data_count ; // FIFO写时钟域的数据计数
wire [7:0] fifo_rd_data_count ; // FIFO读时钟域的数据计数
//例化FIFO IP核
fifo_generator_0 fifo_generator_0 (
.wr_clk ( sys_clk ), // input wire wr_clk
.rd_clk ( sys_clk ), // input wire rd_clk
.wr_en ( fifo_wr_en ), // input wire wr_en
.rd_en ( fifo_rd_en ), // input wire rd_en
.din ( fifo_din ), // input wire [7 : 0] din
.dout ( fifo_dout ), // output wire [7 : 0] dout
.almost_full (almost_full ), // output wire almost_full
.almost_empty (almost_empty ), // output wire almost_empty
.full ( fifo_full ), // output wire full
.empty ( fifo_empty ), // output wire empty
.wr_data_count ( fifo_wr_data_count ), // output wire [7 : 0] wr_data_count
.rd_data_count ( fifo_rd_data_count ) // output wire [7 : 0] rd_data_count
);
//例化 - 写FIFO模块
fifo_wr u_fifo_wr(
.clk ( sys_clk ), // 写时钟
.rst_n ( sys_rst_n ), // 复位信号
.fifo_wr_en ( fifo_wr_en ) , // fifo写请求
.fifo_wr_data ( fifo_din ) , // 写入FIFO的数据
.almost_empty ( almost_empty ), // fifo空信号
.almost_full ( almost_full ) // fifo满信号
);
//例化 - 读FIFO模块
fifo_rd u_fifo_rd(
.clk ( sys_clk ), // 读时钟
.rst_n ( sys_rst_n ), // 复位信号
.fifo_rd_en ( fifo_rd_en ), // fifo读请求
.fifo_dout ( fifo_dout ), // 从FIFO输出的数据
.almost_empty ( almost_empty ), // fifo空信号
.almost_full ( almost_full ) // fifo满信号
);
//例化 ILA IP核
ila_0 ila_0 (
.clk ( sys_clk ), // input wire clk
.probe0 ( fifo_wr_en ), // input wire [0:0] probe0
.probe1 ( fifo_rd_en ), // input wire [0:0] probe1
.probe2 ( fifo_din ), // input wire [7:0] probe2
.probe3 ( fifo_dout ), // input wire [7:0] probe3
.probe4 ( fifo_empty ), // input wire [0:0] probe4
.probe5 ( almost_empty ), // input wire [0:0] probe5
.probe6 ( fifo_full ), // input wire [0:0] probe6
.probe7 ( almost_full ), // input wire [0:0] probe7
.probe8 ( fifo_wr_data_count ), // input wire [7:0] probe8
.probe9( fifo_rd_data_count ) // input wire [7:0] probe9
);
endmodule
3 仿真
TestBench 中只要送出时钟的复位信号。
`timescale 1ns / 1ps
module tb_ip_fifo( );
// Inputs
reg sys_clk;
reg sys_rst_n;
// Instantiate the Unit Under Test (UUT)
ip_fifo u_ip_fifo (
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n)
);
//Genarate the clk
parameter PERIOD = 20;
always begin
sys_clk = 1'b0;
#(PERIOD/2) sys_clk = 1'b1;
#(PERIOD/2);
end
initial begin
// Initialize Inputs
sys_rst_n = 0;
// Wait 100 ns for global reset to finish
#100 ;
sys_rst_n = 1;
// Add stimulus here
end
endmodule
总结
本文介绍FIFO IP核及读写应用。
感谢阅读,祝君成功!
-by aiziyou