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核配置)

ZYNQ从入门到秃头10 DAC FIFO实验(AXI-stream FIFO IP核配置)

fpgaip核最多