可能是最简单的同步fifo 的设计

可能是最简单的同步fifo 设计

什么是FIFO

FIFO的英文全称为First In First Out ,是我们在FPGA工程设计开发中经常使用的一种先进先出的数据缓存器。它区别于其他普通存储器(比如rom ,ram)的是没有外部读写地址线。设计开发者使用它的时候不需要对地址进行管理,只需要给写使能.写数据,就能把对应的数据写入存储器保存;如果数据区域不空,给出读使能就能把有效数据读出来。虽然说这样使用对于开发者设计电路结构来说方便简单一些,但也存在很大的局限性:就是在存储或者读取数据时只能顺序进行,而不像使用普通存储器那样可以自由的通过控制地址线来对对应存储区间的数据进行操作。

FIFO的分类

  1. 同步FIFO:读写时钟为同一个时钟
  2. 异步FIFO:读写时钟为两个没有关系的独立独立时钟

设计FIFO的关键

不管是设计同步FIFO,还是设计异步FIFO,关键点之一是能产生正确的FIFO空满(empty/full)信号。

FIFO空满判断方法

FIFO空满判断方法有两种:

1.增加拓展位的方法
2.设置数据计数器的方法 (此种方法比较简单)

设置一个数据计数器count。 当写使能有效时每写入一个数据,数据计数器count同时加1;读操作时,每读出一个有效数据,数据计数器count同时减1。当数据计数器count等于0的时刻,fifo为空,empty拉高。当数据计数器count等于fifo深度值时,表示fifo被写满,full拉高。此种方法虽然简单,不易出错,但是由于计数器会占用额外的资源,当fifo的深度较大时,可能会降低fifo的读写速度。

本次同步FIFO设计的基本参数

  • FIFO深度:FIFO能存储的数据的个数
  • FIFO宽度:写入数据,读出数据的宽度(此处的读写数据位宽保持同样值)。
  • 满标志:拉高表示此时FIFO内部已经写满,不能再写入数据。如果仍然进行写数据操作,那么数据会丢失,不会被写入数据存储区
  • 空标志:拉高表示此时FIFO内部没有数据,数据已经全部被读走或者没有写入过数据。此时仍然可以进行读数据操作,读出来的数据是无效数据
  • 读写时钟:对FIFO进行读写操作的基准时钟
  • 读出数据有效标志:此信号为高电平表示此时从FIFO读出的数据是有效的。

在这里插入图片描述

具体代码实现过程

写入读出数据位宽,FIFO深度通过参数声明。直接修改参数即可实现其它位宽和深度的FIFO设计。
下面的工程是把上述设计的FIFO作为一个IP核调用,外接一些外设接口在某开发板的测试工程.测试结果表明设计没有问题.

设计源文件部分代码如下:

module syn_fifo
#(
    parameter   DATA_WIDTH = 3'd4  ,
    parameter   FIFO_DEPTH = 5'd16 ,
    parameter   CNT_WIDTH  = 3'd5 
)
(
    input                           rst_n           ,
    input                           clk             ,
    input       [DATA_WIDTH-1:0]    data_in         ,
    input                           wr_en           ,
    input                           rd_en           ,
    output  reg [DATA_WIDTH-1:0]    data_out =0     ,
    output  reg                     valid    =0     ,
    output  reg [CNT_WIDTH-1 :0]    count	 = 0    ,
    output                          full            ,
    output                          empty
);
//------------------------------------------------------------------
//  Variable definition

    reg     [DATA_WIDTH-1:0]    ram         [FIFO_DEPTH-1:0]    ;
    reg     [CNT_WIDTH-2 :0]    read_cnt    = 0	                ;
    reg     [CNT_WIDTH-2 :0]    write_cnt   = 0	                ;
    

//------------------------------------------------------------------
//  Read and write pointer,number of data in fifo
always @(posedge clk)
    begin
        if(rst_n == 1'b0)
        begin
            read_cnt    <= 0          ;
            write_cnt   <= 0          ;
            count       <= 0          ;
            data_out    <= 0          ;
            valid       <= 0          ; 
        end
        else
        begin
            case({wr_en,rd_en})
            
                2'b00: begin
                       count        <=count                                   ;
                       valid        <= 0                                      ;
                       end      
                2'b01:      
                    begin       
                        valid   <=  (  count >0  )  ?   rd_en:0               ;
                        data_out<=ram[read_cnt]                               ; 
                        read_cnt<=  (  count >0  )  ?(read_cnt+1 ):read_cnt   ;
                        if (  count >0  )       
                            count   <=  count-1                               ;
                        else count  <=   0                                    ;
                    end
                2'b10:
                    begin
                        ram[write_cnt]  <= ( count<FIFO_DEPTH )   ?   data_in:ram[write_cnt]  ;
                        count           <= ( count<FIFO_DEPTH )   ?   count+1:count           ;
                        write_cnt       <= ( count<FIFO_DEPTH )   ?   write_cnt+1 :write_cnt  ; 
                        valid           <= 0                                                  ; 
                    end
                2'b11:
                    begin       
                        ram[write_cnt]  <=      data_in          ;
                        data_out        <=      ram[read_cnt]    ;
                        write_cnt       <=      write_cnt+1      ;
                        read_cnt        <=      read_cnt+1       ;
                        count           <=      count            ; 
                        valid           <=      (count>0) ? rd_en :0  ;
                    end 
            
                default : ;
            endcase
        end
    end

//------------------------------------------------------------------
//  fifo flag output
assign  full  = (count == FIFO_DEPTH);
assign  empty = (count == 0);
endmodule

testbench文件代码如下:

`timescale 1ns/100ps
`define half_cycle 5

module syn_fifo_tb ;

    parameter   DATA_WIDTH = 3'd4  ;
    parameter   FIFO_DEPTH = 5'd16 ;
    parameter   CNT_WIDTH  = 3'd5  ;

    reg                         rst_n       ;
    reg                         clk         ;
    reg  [DATA_WIDTH-1:0]       data_in     ;  
    reg                         wr_en       ;
    reg                         rd_en       ; 
    wire [DATA_WIDTH-1:0]       data_out    ;
    wire                        valid       ;
    wire                        full        ;
    wire                        empty       ;
    
    syn_fifo
	   #(
        .DATA_WIDTH (   DATA_WIDTH  ),
        .FIFO_DEPTH (   FIFO_DEPTH  ),
        .CNT_WIDTH  (   CNT_WIDTH   )
      )
    syn_fifo_inst
        (
           .rst_n     ( rst_n    )  ,
           .clk       ( clk      )  ,
           .data_in   ( data_in  )  ,
           .wr_en     ( wr_en    )  ,
           .rd_en     ( rd_en    )  ,
           .data_out  ( data_out )  ,
           .valid     ( valid    )  ,
           .full      ( full     )  ,
           .empty     ( empty    )
        );
    
    always #(`half_cycle) clk=~clk;
    
    initial
        begin
            clk=0;
            data_in=0;
            rst_n=0;
            wr_en =0 ;
            rd_en = 0 ;
            #50 ;
		    rst_n=1;         
            wr_en=1;rd_en=0;
            # 500;
            wr_en=0;rd_en=1;
            #500 ;
            wr_en=1;rd_en=0;
            #100 ;
			wr_en=0;rd_en=1;
            #300 ;
			wr_en=1;rd_en=0;
		    #300 ;
            wr_en=0;rd_en=1;
            #100 ;
            wr_en=0;rd_en=0; 
		    #100 ;
            wr_en=1;rd_en=0;
            #100 ;
            wr_en=0;rd_en=0; 
		    #300 ;
            wr_en=0;rd_en=1; 
            #100 ;
            wr_en=0;rd_en=0;   
            #100 ;     
            wr_en=1;rd_en=0;
            #100 ;                
			wr_en=1;rd_en=1;
			#320 ;
            wr_en=1;rd_en=1; 
			#100 ;
            wr_en=0;rd_en=0;
            #100 ;
            wr_en=1;rd_en=0;   
            #100 ;
            wr_en=1;rd_en=0;
            #600 ;
		    $stop;
        
        end

    always@(posedge clk)
    
        data_in=data_in+1;
        
endmodule

仿真结果

  • 写入数据测试
    在这里插入图片描述
    仿真测试结果:向深度为16的FIFO中写入16个数据之后,FIFO的full信号被拉高,表明此时已经写满,不能再写入数据。

  • 读出数据测试
    在这里插入图片描述
    仿真测试结果: 再连续写入16个数据之后。进行连续读操作,可以把写入的数据正确的读出来(伴随的读数据有效信号为高电平)。注意红框中的信息。 在正确读出最后一个数据的时候,empty拉高,表明目前读出来的数据已经是FIFO里边存储的最后一个数据。之后如果没有新的数据写入,继续进行读操作,那么读出来的数据是无效数据,valid信号应该为低电平。

分享不易,看完点个赞呗。

同步FIFO是一种常用的电子电路,用于在两个或多个时钟域之间传输数据。它具有先入先出的性质,可以保证数据传输的顺序不变。 Verilog语言中,同步FIFO可以使用一组寄存器实现。下面是一个简单同步FIFO的Verilog代码: ``` module sync_fifo ( input clk_w, // 写时钟 input rst_w, // 写复位 input wr_en, // 写使能 input [7:0] wr_data, // 写数据 input clk_r, // 读时钟 input rst_r, // 读复位 input rd_en, // 读使能 output [7:0] rd_data, // 读数据 output full, // 满标志 output empty // 空标志 ); parameter WIDTH = 8; // 数据宽度 parameter DEPTH = 16; // 深度 reg [WIDTH-1:0] fifo [0:DEPTH-1]; // FIFO存储器 reg [WIDTH-1:0] rd_data_reg; // 读数据寄存器 reg [DEPTH-1:0] wr_ptr; // 写指针 reg [DEPTH-1:0] rd_ptr; // 读指针 wire [DEPTH-1:0] count; // 元素个数 // 写逻辑 always @(posedge clk_w or posedge rst_w) begin if (rst_w) begin wr_ptr <= 0; fifo <= 0; end else if (wr_en && ~full) begin fifo[wr_ptr] <= wr_data; wr_ptr <= wr_ptr + 1; end end // 读逻辑 always @(posedge clk_r or posedge rst_r) begin if (rst_r) begin rd_ptr <= 0; rd_data_reg <= 0; end else if (rd_en && ~empty) begin rd_data_reg <= fifo[rd_ptr]; rd_ptr <= rd_ptr + 1; end end // 元素个数计算 assign count = wr_ptr - rd_ptr; // 满标志 assign full = (count == DEPTH); // 空标志 assign empty = (count == 0); // 读数据输出 assign rd_data = rd_data_reg; endmodule ``` 在这个Verilog代码中,FIFO存储器使用一组寄存器实现,写指针和读指针分别指向下一个写入位置和读取位置。当写使能信号wr_en有效且FIFO未满时,写入数据wr_data,并将写指针加1;当读使能信号rd_en有效且FIFO非空时,读取数据并将读指针加1。元素个数count可以通过写指针和读指针的差值计算得到,满标志full和空标志empty分别表示FIFO是否已满和是否为空。最后,读数据rd_data通过一个寄存器输出,以保证读数据的正确性。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值