ZYNQ之FPGA学习----FIFO IP核使用实验
Posted 鲁棒最小二乘支持向量机
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZYNQ之FPGA学习----FIFO IP核使用实验相关的知识,希望对你有一定的参考价值。
1 FIFO IP核介绍
FIFO 的英文全称是 First In First Out, 即先进先出
。与 FPGA 内部的 RAM 和 ROM 的区别是没有外部读写地址线, 采取顺序写入数据, 顺序读出数据的方式
,使用起来简单方便,缺点就是不能像 RAM 和 ROM 那样可以由地址线决定读取或写入某个指定的地址
根据 FIFO 工作的时钟域,可以将 FIFO 分为同步 FIFO 和异步 FIFO:
- 同步 FIFO 是指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作
- 异步 FIFO 是指读写时钟不一致,读写时钟是互相独立的
FIFO常用参数:
- FIFO 的宽度,FIFO 一次读写操作的数据位 N
- FIFO 的深度,FIFO 可以存储多少个宽度为 N 位的数据
- 将空标志(almost_empty),FIFO 即将被读空
- 空标志(empty),FIFO 已空时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的读操作继续从 FIFO中读出数据而造成无效数据的读出
- 将满标志(almost_full),FIFO 即将被写满
- 满标志(full),FIFO 已满时由 FIFO 的状态电路送出的一个号,以阻止 FIFO 的写操作继续向 FIFO 中写数据而造成溢出
- 读时钟,读 FIFO 时所遵循的时钟,在每个时钟的上升沿触发
- 写时钟,写 FIFO 时所遵循的时钟,在每个时钟的上升沿触发
2 实验任务
使用 Vivado 生成 FIFO IP 核,并实现以下功能:当 FIFO 为空时,向 FIFO 中写入数据,写入的数据量和 FIFO 深度一致,即 FIFO 被写满;然后从 FIFO 中读出数据,直到 FIFO 被读空为止,系统框图如下:
图片来自《领航者ZYNQ之FPGA开发指南》
3 实验设计
3.1 创建工程
新建工程,操作如图所示:
输入工程名和工程路径,如图所示:
选择创建RTL工程,如图所示:
直接点击Next:
继续点击Next:
添加芯片型号,操作如图所示:
完成工程创建:
3.2 设计输入
点击IP Catalog,搜索fifo,如图所示:
双击打开后,弹出窗口如下,Interface Type 选项用于选择 FIFO 接口的类型,选择默认的 Native;Fifo Implementation 选项用于选择是同步 FIFO 还是异步 FIFO 以及使用哪种资源实现 FIFO,选择 Independent Clocks Block RAM,即使用块 RAM 来实现的异步 FIFO:
Read Mode 选项用于设置读 FIFO时的读模式,选择默认的Standard FIFO。Data Port Parameters 选型用于设置读写端口的数据总线的宽度以及 FIFO 的深度,写宽度 Write Width 设置为 8 位,写深度 Write Depth 设置为 256。Reset Pin 不使用,取消勾选:
Status Flags窗口,用于设置用户自定义接口或者用于设定专用的输入口,勾选 即将写满 和 即将读空 这两个信号:
Data Counts 窗口用于设置 FIFO 内数据计数的输出信号,此信号表示当前在 FIFO 内存在多少个有效数据。为了更加方便地观察读/写过程,设置如图所示:
设置完成:
弹出如下窗口,直接点击Generate:
点击OK:
继续添加ILA(集成逻辑分析器) IP核,即在线逻辑分析仪的功能,操作如图所示:
进行如下设置,Number of Probes 用于设置所需的探针数量;Sample Data Depth 用于设置采样深度:
如下进行设置每个探针的参数:
设置完成,点击OK:
创建工程顶层文件,操作如图所示:
创建文件ip_fifo,作为顶层模块:
创建完成:
双击打开,输入代码如下:
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
如图所示:
继续创建fifo_wr文件,即写FIFO模块,如图所示:
双击打开,输入代码如下:
module fifo_wr(
input clk , // 时钟信号
input rst_n , // 复位信号
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 ; //延迟计数器
//因为 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
如图所示:
继续创建fifo_rd文件,即读FIFO模块,如图所示:
双击打开,输入代码如下:
module fifo_rd(
input clk , // 时钟信号
input rst_n , // 复位信号
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 ; //almost_full 延迟一拍
reg almost_full_syn ; //almost_full 延迟两拍
reg [3:0] dly_cnt ; //延迟计数器
//因为 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
endmodule
如图所示:
3.3 分析与综合
对设计进行分析,操作如图所示:
分析后的设计,Vivado自动生成顶层原理图,如图所示:
对设计进行综合,操作如图所示:
综合完成后,弹出窗口如下,直接关闭:
3.4 约束输入
创建约束文件,操作如图所示:
创建约束文件,输入文件名:
双击打开,输入约束代码:
create_clock -period 20.000 -name clk [get_ports sys_clk]
set_property -dict PACKAGE_PIN U18 iosTANDARD LVCMOS33 [get_ports sys_clk]
set_property -dict PACKAGE_PIN J15 IOSTANDARD LVCMOS33 [get_ports sys_rst_n]
如图所示:
3.5 设计实现
点击 Flow Navigator 窗口中的 Run Implementation,如图所示:
点击OK:
完成后,关闭即可:
3.6 功能仿真
创建TestBench,操作如图所示:
创建激励文件,输入文件名:
创建完成:
双击打开,输入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
如图所示:
开始进行仿真,操作如下:
选择HDL仿真对象:
点击Restart,波形窗口中的当前仿真时刻点回归到0ns:
写满数据后,fifo_full信号拉高:
读完数据后,fifo_empty信号拉高:
3.7 下载验证
由于疫情,一直无法去实验室,故ZYNQ开发板不在身边,该步骤内容待更新
致谢领航者ZYNQ开发板,开启FPGA学习之路!
希望本文对大家有帮助,上文若有不妥之处,欢迎指正
分享决定高度,学习拉开差距
以上是关于ZYNQ之FPGA学习----FIFO IP核使用实验的主要内容,如果未能解决你的问题,请参考以下文章
ZYNQ之FPGA学习----MMCM/PLL IP核使用实验
ZYNQ从入门到秃头08 FPGA片内异步FIFO读写测试实验
ZYNQ从入门到秃头08 FPGA片内异步FIFO读写测试实验
ZYNQ从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)