ZYNQ-PL学习实践(五)IP核之FIFO

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 程序

本文介绍一个先循环写入,延时10个时钟周期后,再读出FIFO数据,读完后再延时10个时钟周期继续写,进入下一次循环。

2.1 FIFO IP核

选择IP Catalog添加FIFO,
在这里插入图片描述
Basic 设置异步FIFO,
在这里插入图片描述
设置写FIFO深度、写FIFO宽度、读FIFO宽度如下,
在这里插入图片描述
设定专用的输入口, 使用“即将写满”和“即将读空”这两个信号,
在这里插入图片描述
“Data Counts”设置 FIFO 内数据计数的输出信号,
在这里插入图片描述
Summary如下,
在这里插入图片描述

2.2 写FIFO模块

FIFO写模块其核心思想是设计一个简单的状态机【0状态、1状态、2状态】内涵:

0 状态 : 将被读空状态,下一个跳转状态是 1状态;
1 状态 : 延时10 拍过程状态,下一个跳转状态是 2 状态;
2 状态 : 将被写满状态、写满状态,下一个跳转状态是 0状态;

fifo_wr.v代码如下:

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          ; //延迟计数器

//因为 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模块

FIFO读模块其核心思想是也是设计一个简单的状态机【0状态、1状态、2状态】内涵:

0 状态 : 将被写满状态,下一个跳转状态是 1状态;
1 状态 : 延时10 拍过程状态,下一个跳转状态是 2 状态;
2 状态 : 将被读空状态、读空状态,下一个跳转状态是 0状态;

fifo_rd.v代码如下:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jack.Jia

感谢打赏!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值