FPGA-FIFO (包含PLL锁相环模块内容)

        学习FPGA的笔记,根据正点原子视频进行学习

开发板AX7020中本身并没有FIFO模块,其FIFO模块其实是通过block memory generator产生的

FIFO与RAM模块不同的是FIFO好像是一个管道,并没有对应的地址,其遵循先进先出的原则。其可作用在同步或者异步之间传输数据。

一、实验内容

        创建FIFO模块并写入0-255再读出来

二、实验分析

        首先我们需要调用IP_FIFO模块,再通过一个写入驱动模块以及一个读出驱动模块对FIFO进行驱动,由于本次实验进行的是异步FIFO实验(读和写用的不是一个时钟),因此我们需要一个锁相环(PLL)模块对时钟进行分频。最后我们需要一个顶层模块对以上四个模块进行调用。

三、实验步骤

1.IP_FIFO模块调用配置

        直接在IP Catalog模块中搜索FIFO,其中有一个FIFO Generator,进入即为FIFO配置模块。Basic模块中可以选择不同的FIFO类型,前面几个带有Common的是同步FIFO,后面几个是异步的FIFO,具体不同点可以查看数据手册。

        我们这里选用第四个Block产生的异步FIFO。

        Native Ports中可以对FIFO的容量进行配置,其中包括宽度以及深度

        Status Flags中可以设置Almost Flag,这两个选项勾选上,它表明是加入FIFO快满信号(实际上是,FIFO还有一位就满了或者还有一位就空了的时候这个引脚会拉高,在后面是很有用的)。 

        其余配置可以根据数据手册按照自己的需求进行配置。

2.IP_PLL配置

        在IP Catalog中搜索clock,选择clock wzard选项进入配置

        clocking options中可以设置MMCM或者PLL类型,MMCM比PLL功能稍微强大一点,能用        PLL解决的MMCM也行,下面一些配置我们默认。

        output clock页面是配置输出时钟,可以设置输出时钟的条数,名称,要求的频率,相位,占空比,下面可以设置是否要reset,locked。

注意:FIFO、PLL的reset都是高电平有效即输入1复位在该页面可以修改,但AX7020的按键是输入0有效。

        后面的页面保持默认。

3.分析FIFO时序图

        以上是三个模块(FIFO)的输入输出,以及整个项目中四个模块之间的关系以及输入输出。

        下面对写、读模块进行分析:

256位FIFO实际容量是255,因此写入数据位0-254

写模块:

       clk_50m:时钟 50m

       rst_n:复位

       wr_rst_busy: 复位在忙信号,复位结束一段时间后拉低

       empty:FIFO是否为空,通常写入几个数据之后,空信号才会由高变低

       empty_d0 d1:打拍,(避免亚稳态的产生,常常采用打两拍的方法,类似于消抖)

       fifo_wr_en: 写使能,在复位繁忙结束后即可进行写使能的拉高

       fifo_wr_data:FIFO写入数据,注意定义256位的FIFO实际只有255位可以存储数据

       almost_full:当还差一位满的时候进行拉高,通常用于停止写入

         

读模块:

        clk_100m:时钟

        rst_n:复位

        full:满

        fifo_rd_en:读使能信号

        fifo_rd_data:读取数据、

        almost_empty:几乎空

这里解释一下我们为什么需要“几乎满”、“几乎空了这两个信号”

        我们使用满信号/几乎满信号来判断是否停止写操作(如果满了之后仍然进行写操作是无效的,而且会触发溢出标志)。如果我们使用满信号,则在写完254的周期的下一个时钟上升沿拉上满信号,因此在254下一个周期判断full是否是1,由于非阻塞赋值的原因,254周期full信号仍然是0,因此在下一个周期内检测到的full仍然是0。这样会导致有一位溢出。

        如果使用快满信号,则在写入254位的周期时候快满信号拉高,虽然在本周期内无法检测到快满拉高,但下一周期就不会继续写入。

      3.代码编写

        3.1 写入模块



module fifo_wr(
 (*mark_debug = "true"*)   input                clk_50M     ,
 (*mark_debug = "true"*)   input                rst         ,
  (*mark_debug = "true"*)  input                locked      ,
  (*mark_debug = "true"*)  input                empty       ,
   (*mark_debug = "true"*) input                almost_full ,
   (*mark_debug = "true"*) input                wr_rst_busy ,
   (*mark_debug = "true"*) output    reg        fifo_wr_en  ,
   (*mark_debug = "true"*) output    reg [7:0]  fifo_wr_data
);
  (*mark_debug = "true"*)  reg     empty_1;
  (*mark_debug = "true"*)  reg     empty_2;   //实际上使用的是empty_2,相当于消抖

//要对异步信号进行打两拍的操作
always @(posedge clk_50M or negedge rst) begin
    if(rst == 1'b0)  begin
       empty_1 <= 1'b0; 
       empty_2 <= 1'b0; 
    end
       
    else begin
       empty_1 <= empty  ; 
       empty_2 <= empty_1; 
    end
end


//fifo_wr_en
always @(posedge clk_50M or negedge rst) begin
    if(rst == 1'b0)
        fifo_wr_en <= 1'b0;
    else if (rst == 1'b1 && locked == 1'b1 && wr_rst_busy == 1'b0 )
        if( empty_2 == 1'b1 ) 
            fifo_wr_en <= 1'b1;
        else  begin
            fifo_wr_en <= fifo_wr_en;
            if(almost_full == 1'b1)
                fifo_wr_en <= 1'b0;
        end
//        if( almost_full == 1'b1)
//            fifo_wr_en <= 1'b0;
    else 
        fifo_wr_en <= 1'b0;

end


//fifo_wr_data
always @(posedge clk_50M or negedge rst) begin
    if (rst == 1'b0)
        fifo_wr_data <= 8'b0;
    else if( fifo_wr_en == 1'b1)
        fifo_wr_data <= fifo_wr_data + 8'b1;

end








endmodule

        (*mark_debug=="true"*)是后面调试的代码,可以忽略

需要注意的就是打两拍的这段代码是第一层接触,其实其意思也就是使用两个周期后的异步信号的值,防止不同时钟频率之间不稳定的情况。

        3.2读出模块


module fifo_rd(
  (*mark_debug = "true"*)  input               clk_100M    ,
 (*mark_debug = "true"*)   input               rst         ,
  (*mark_debug = "true"*)  output     reg      fifo_rd_en  ,
   (*mark_debug = "true"*) input               almost_empty,
  (*mark_debug = "true"*)  input               full        ,
  (*mark_debug = "true"*)  input               rd_rst_busy ,
   (*mark_debug = "true"*) input        [7:0]  fifo_rd_data,
  (*mark_debug = "true"*)  input               locked      
);

(*mark_debug = "true"*) reg             full_1;
(*mark_debug = "true"*) reg             full_2;


always @(posedge clk_100M or negedge rst) begin
    if(rst == 1'b0)  begin
       full_1 <= 1'b0; 
       full_2 <= 1'b0; 
       end
       
    else begin
       full_1 <= full  ; 
       full_2 <= full_1; 
        end
end




//fifo_rd_en
always @(posedge clk_100M or negedge rst) begin 
    if (rst == 1'b0)
        fifo_rd_en <= 1'b0;
    else if (rst == 1'b1 && locked == 1'b1 && rd_rst_busy == 1'b0)
        if (full_2 == 1'b1) 
            fifo_rd_en <= 1'b1;
        else begin
            fifo_rd_en <= fifo_rd_en;
            if(almost_empty == 1'b1)
                fifo_rd_en <= 1'b0;
         end

    else 
        fifo_rd_en <= 1'b0 ;
end



endmodule

同样需要打两排的模块

        3.3顶层封装模块



module ip_fifo(
    input           sys_clk,
    input           sys_rst

);
    
    wire    clk_50M  ;
    wire    clk_100M ;
    wire    locked   ;
    wire  [7:0]  fifo_wr_data;
    wire    fifo_wr_en   ;
    wire    fifo_rd_en   ;
    wire [7:0]   fifo_rd_data ;
    wire    full         ;
    wire    almost_full  ;
    wire    empty        ;
    wire    almost_empty ;
    wire  [7:0]  rd_data_count;
    wire  [7:0]  wr_data_count;
    wire    wr_rst_busy  ;
    wire    rd_rst_busy  ;
   

//pll
clk_wiz_0 u_clk_wiz_0
(
    // Clock out ports
    .clk_50M          (clk_50M)       ,     // output clk_50M
    .clk_100M         (clk_100M)      ,     // output clk_100M
    // Status and control signals
    .reset            (~sys_rst)       , // input reset
    .locked           (locked)        ,       // output locked
   // Clock in ports
    .clk_in1          (sys_clk)
);    
//fifo
fifo_generator_0 u_fifo_generator_0 (
    .rst              (~sys_rst                 ),                      // input wire rst   fifo 高电平有效
    .wr_clk           (clk_50M                 ),                // input wire wr_clk
    .rd_clk           (clk_100M                ),                // input wire rd_clk
    .din              (fifo_wr_data            ),                      // input wire [7 : 0] din
    .wr_en            (fifo_wr_en              ),                  // input wire wr_en
    .rd_en            (fifo_rd_en              ),                  // input wire rd_en
    .dout             (fifo_rd_data            ),                    // output wire [7 : 0] dout
    .full             (full                    ),                    // output wire full
    .almost_full      (almost_full             ),      // output wire almost_full
    .empty            (empty                   ),                  // output wire empty
    .almost_empty     (almost_empty            ),    // output wire almost_empty
    .rd_data_count    (rd_data_count           ),  // output wire [7 : 0] rd_data_count
    .wr_data_count    (wr_data_count           ),  // output wire [7 : 0] wr_data_count
    .wr_rst_busy      (wr_rst_busy             ),      // output wire wr_rst_busy
    .rd_rst_busy      (rd_rst_busy             )      // output wire rd_rst_busy
);


fifo_wr   u_fifo_wr (
    .clk_50M         (    clk_50M              )  ,
    .rst             (    sys_rst              )  ,
    .locked          (    locked               )  ,
    .empty           (    empty                )  ,
    .almost_full     (    almost_full          )  ,
    .wr_rst_busy     (    wr_rst_busy          )  ,
    .fifo_wr_en      (    fifo_wr_en           )  ,
    .fifo_wr_data    (    fifo_wr_data         )  
 
);
fifo_rd   u_fifo_rd(
    .clk_100M         (      clk_100M                  )  ,
    .rst              (      sys_rst                       )  ,
    .fifo_rd_en       (      fifo_rd_en                )  ,
    .almost_empty     (      almost_empty              )  ,
    .full             (      full                      )  ,
    .rd_rst_busy      (      rd_rst_busy               )  ,
    .fifo_rd_data     (      fifo_rd_data              )  ,
    .locked           (      locked                    )  
);




endmodule 

对四个子模块调用,注意引脚别错了就行。

4.仿真验证

        需要单独写仿真模块代码,本项目中对于整体(顶层模块)来说只有系统时钟、系统复位两个输入,因此仿真只需要对这两个信号进行模拟输入即可。

`timescale    1ns/1ns

module   tb_ip_2port_ram();

parameter clk_period = 20;

reg sys_clk;
reg sys_rst;
//初始化复位
initial begin 
    sys_clk <= 1'b0;
    sys_rst <= 1'b0;
    #200
    sys_rst <= 1'b1;
end

//初始化时钟
always #(clk_period/2) sys_clk =~sys_clk;

ip_fifo u_ip_fifo (
    .sys_clk       (sys_clk),
    .sys_rst       (sys_rst)
);


endmodule

(50M的时钟周期20ns)

直接在vivado中进行仿真即可

可以看到正常写入数据

254写入后停止写入数据

正常读出是数据

四、总结

        1.FIFO的使用

        2.PLL使用

        3.异步模块之间需要打两排

        4.PLL FIFO默认情况下1为复位信号(可修改)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值