赞
踩
对于 FIFO 需要了解一些常见参数:
对应的 XDC 约束语句如下所示:
- 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]
根据实验任务要求和模块化设计的思想,我们需要如下 4 个模块:fifo IP 核、写 fifo 模块、读 fifo 模块以及顶层例化模块实现前三个模块的信号交互。由于 FIFO 多用于跨时钟域信号的处理,所以本实验我们使用异步 FIFO 来向大家详细介绍双时钟 FIFO IP 核的创建和使用。为了方便大家理解,这里我们将读/写时钟都用系统时钟来驱动。系统的功能框图如下图所示:
在“IP Catalog”窗口中,在搜索栏中输入“fifo”关键字,这时 Vivado 会自动查找出与关键字匹配的 IP 核名称,我们双击“FIFO Generator”,如下图所示。
弹出“Customize IP”窗口,如下图所示。
下来就是配置 IP 核的时钟参数的过程。
接下来是“Native Ports”选项卡,用于设置 FIFO 端口的参数。“Read Mode”选项用于设置读 FIFO时的读模式,这里我们选择默认的“Standard FIFO”。“Data Port Parameters”一栏用于设置读写端口的数据总线的宽度以及 FIFO 的深度,写宽度“Write Width”我们设置为 8 位,写深度“Write Depth”我们设置为 256,注意此时 FIFO IP 核所能实现的实际深度却是 255;虽然读宽度“Read Width”能够设置成和写宽度不一样的位宽,且此时读深度“Read Depth”会根据上面三个参数被动地自动设置成相应的值;但是我们还是将读宽度“Read Width”设置成和写宽度“Write Width”一样的位宽,这也是在实际应用中最常用的情况。由于我们只是观察 FIFO 的读写,所以最下面的“Reset Pin”选项我们可以不使用,把它取消勾选。其他设置保持默认即可,如下图所示。
“Status Flags”选项卡,用于设置用户自定义接口或者用于设定专用的输入口。这里我们使用“即将写满”和“即将读空”这两个信号,所以我们把它们勾选上,其他保持默认即可,如下图所示。
“Data Counts”选项卡用于设置 FIFO 内数据计数的输出信号,此信号表示当前在 FIFO 内存在多少个有效数据。为了更加方便地观察读/写过程,这里我们把读/写端口的数据计数都打开,且计数值总线的位宽设置为满位宽,即 8 位,如下图所示。
最后的“Summary”选项卡是对前面所有配置的一个总结,在这里我们直接点击“OK”按钮即可,如 下图所示。
接着就弹出了“Genarate Output Products”窗口,我们直接点击“Generate”即可,如下图所示。
之后我们就可以在“Design Run”窗口的“Out-of-Context Module Runs”一栏中出现了该 IP 核对应的run“fifo_generator_0_synth_1”,其综合过程独立于顶层设计的综合,所以在我们可以看到其正在综合,如下图所示。
在其 Out-of-Context 综合的过程中,我们就可以进行 RTL 编码了。首先打开 IP 核的例化模板,在“Source” 窗口中的“IP Sources”选项卡中,依次用鼠标单击展开“IP”-“fifo_generator _0”-“Instantitation Template”,我们可以看到“fifo_generator_0.veo”文件,它是由 IP 核自动生成的只读的 verilog 例化模板文件,双击就可以打开它,如下图所示。
我们创建一个 verilog 源文件,其名称为 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读时钟域的数据计数
-
- //*****************************************************
- //** main code
- //*****************************************************
-
- //例化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

- 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

- 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
- endmodule

- 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

由波形图可知,当写满 255 个数据后,fifo_full 满信号就会拉高。经过延时之后,fifo_rd_en 写使能信号拉高,经过一拍之后就开始将 fifo 中的数据送到 fifo_dout 端口上。写满后转为读的仿真波形图如下图所示:
由波形图可知,当读完 255 个数据后,fifo_empty 空信号就会拉高。经过延时之后,fifo_wr_en 写使能信号拉高,经过一拍之后就开始向 fifo 中继续写入数据。
将有关探针信号添加到波形窗口中,这里我们已经完成信号的添加,方法是点击“hw_ila_1”Dashboard窗口左上角的“+”。同时我们在窗口右下角将“fifo_rd_en”信号添加到触发窗口中且设置为上升沿触发,单击左上角的触发按钮,如下图所示:
最后就看到了 ILA 捕获得到的数据,展开波形图如下图所示:
从捕获得到的波形图中可以看出,其逻辑行为与仿真波形图中的一致,证明我们的代码正确地实现了预期的功能。
ip_fifo
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。