当前位置: 首页 > news >正文

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

相关文章:

  • unity pico开发 五 UI交互
  • PyTorch GPU显存管理与大规模张量操作
  • 虚拟网络IP设置
  • Focal Loss (聚焦损失) :解决类别不平衡与难易样本的利器,让模型学会“重点学习”
  • LINUX网络基础 - 网络编程套接字,UDP与TCP
  • Tauri跨平台开发问题及解决方案深度解析(React版)
  • 学生管理信息系统的需求分析与设计
  • 基于提示驱动的潜在领域泛化的医学图像分类方法(Python实现代码和数据分析)
  • 新一代信息技术:从技术范畴到未来趋势的全景洞察
  • 千峰React:组件与逻辑封装(下)
  • 神经网络 - 激活函数(Maxout 单元)
  • 【华为OD机试真题29.9¥】(E卷,100分) - IPv4地址转换成整数(Java Python JS C++ C )
  • 苹果iPhone 17 Pro系列将配备12GB内存,AI功能成升级关键
  • 2025机械考研复试面试问题汇总篇(含13门科目),考研机械复试专业面试常见重点问题总结!考研机械复试专业面试准备看这一篇就够了!
  • ArcGIS Pro高级应用:高效生成TIN地形模型
  • linux磁盘满了怎么安全删除文件
  • C++编程:常见内置算法
  • 详解Nginx no live upstreams while connecting to upstream
  • c++中的vector
  • 【GPU使用】如何在物理机和Docker中指定GPU进行推理和训练
  • 缅甸发生5.0级地震
  • 陈刚:推动良好政治生态和美好自然生态共生共优相得益彰
  • 国际金价下跌,中概股多数上涨,穆迪下调美国主权信用评级
  • 总奖金池百万!澎湃与七猫非虚构写作与现实题材征文大赛征稿启动
  • 贵州仁怀通报“正新鸡排鸡腿里全是蛆”:已对同类产品封存送检
  • A股三大股指低收:汽车股领涨,大金融走弱,两市成交近1.1万亿元