同步FIFO

参考网上资料,整理了同步fifo的两种设计方法。


一、FIFO是什么?

        fifo是一种先入先出的存储机制,同步fifo指读写数据共用一个时钟。

        本文是基于双端口RAM设计的同步fifo,深度为16,宽度为8。

二、同步FIFO

(一)双端口RAM设计

 1. 思路

设计一个深度16,宽度8的双端口RAM。

Verilog代码如下:

//设计一个伪双端口的RAM,宽度8bit,深度16
//具有独立的读写时钟
module dp_RAM(
    input wr_clk,
    input rd_clk,
    input wr_allow,
    input rd_allow,
    input      [3:0] wr_addr,
    input      [3:0] rd_addr,
    input      [7:0] wr_data,
    output reg [7:0] rd_data
    );
       
    reg [7:0] memory[15:0]; //16个8位的存储器
    
    always@(posedge wr_clk) begin
        if(wr_allow)
            memory[wr_addr] <= wr_data;
    end
    
    always@(posedge rd_clk) begin
        if(rd_allow)
            rd_data <= memory[rd_addr];
    end
   
endmodule

(二)同步FIFO设计 

  • 方法一:利用计数器对fifo中数据计数,确定空满标志。 

 Verilog代码如下:

`timescale 1ns / 1ps

 Company: 
 Engineer: 
 
 Create Date: 2022/04/24 15:56:38
 Design Name: 
 Module Name: sync_fifo
 Project Name: 
 Target Devices: 
 Tool Versions: 
 Description: fifo深度为16,宽度为8
 
 Dependencies: 
 
 Revision:
 Revision 0.01 - File Created
 Additional Comments:
 



module sync_fifo(
    input  clk,             // 同步时钟
    input  rst_n,
    input  rd_en,
    input  wr_en,
    input  [7:0] wr_data,
    output [7:0] rd_data,
    output reg full,            // fifo满信号
    output reg empty,           // fifo空信号
    output reg [3:0] fifo_cnt         // 对fifo中的数据计数,深度为16---地址总线4
    );                                // 每向fifo写入一个数据,fifo_cnt就加1;每读出数据就减1
    
    reg [3:0] rd_addr;
    reg [3:0] wr_addr;
    
    wire rd_allow;          // 双端口RAM的读控制信号
    wire wr_allow;
    assign rd_allow = (rd_en && !empty);    // 空不能读(当外部fifo有读使能信号且非空标志,双端口RAM读允许)
    assign wr_allow = (wr_en && !full);     // 满不能写
    
    dp_RAM dp_RAM_inst(     // 例化
     .wr_clk  (clk),        // 设计的fifo是同步时钟
     .rd_clk  (clk),
     .wr_allow(wr_allow),
     .rd_allow(rd_allow),
     .wr_addr (wr_addr),
     .rd_addr (rd_addr),
     .wr_data (wr_data),
     .rd_data (rd_data)
    );
    
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            empty <= 0;
        else
            empty <= (!wr_en && fifo_cnt==0) || (!wr_en && fifo_cnt==4'd1 && rd_en);
            // (没有写&&计数值为0) || (没有写&&计数值为1&&读信号) 
            // 已空 或 将空,两种情况fifo为空
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            full <= 0;
        else
            full <= (!rd_en && fifo_cnt==15) || (!rd_en && fifo_cnt==14 && wr_en);
            // 已满 或 将满
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            rd_addr <= 0;
        end
        else if(rd_en & !empty) 
            rd_addr <= rd_addr + 1;
        else rd_addr <= rd_addr;
    end
    
        always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            wr_addr <= 0;
        end
        else if(wr_en & !full) 
            wr_addr <= wr_addr + 1;
        else wr_addr <= wr_addr;
    end
    
//    always@(posedge clk or negedge rst_n) begin     // 没有考虑同时读写
//        if(!rst_n)
//            fifo_cnt <= 0;
//        else if((!rd_allow && wr_allow) || (rd_allow && !wr_allow)) begin
//            if(wr_allow) fifo_cnt <= fifo_cnt + 1;
//            else fifo_cnt <= fifo_cnt - 1;
//        end
//    end

    always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            fifo_cnt <= 0;
        else begin
            case({rd_en,wr_en})
                2'b00: fifo_cnt <= fifo_cnt;
                2'b01: if(!full)
                            fifo_cnt <= fifo_cnt + 1;
                       else
                            fifo_cnt <= fifo_cnt;
                2'b10: if(!empty)
                            fifo_cnt <= fifo_cnt - 1;
                        else
                            fifo_cnt <= fifo_cnt;
                2'b11: fifo_cnt <= fifo_cnt;
            endcase
        end
    end
    
endmodule

  • 方法二: 将原fifo地址位扩展一位,用最高位判断空满标志。

Verilog代码如下:

module sync_fifo #(
    parameter data_addr  = 4,       // 参数赋值,模块内参数视为本地参数
    parameter data_width = 8,
    parameter fifo_depth = 16
)
(
    input clk,
    input rst_n,
    input wr_en,
    input  [data_width-1:0]wr_data,
//    output  [fifo_addr-1 :0]wr_addr,
    input rd_en,
    output reg [data_width-1:0]rd_data,
//    output  [fifo_addr -1:0]rd_addr,
    output full,
    output empty
    );

//================定义读写指针,多一位来控制空满==================================
    reg [data_addr:0] wr_addr_ptr;
    reg [data_addr:0] rd_addr_ptr;
    
    wire [data_addr-1 : 0] wr_addr;     // fifo的读写地址,遵循先入先出原则
    wire [data_addr-1 : 0] rd_addr;
    assign wr_addr = wr_addr_ptr[data_addr-1 : 0];
    assign rd_addr = rd_addr_ptr[data_addr-1 : 0];
    
//=============读写指针模块====================================================
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            wr_addr_ptr <= 0;
        else if(wr_en && (~full))
            wr_addr_ptr <= wr_addr_ptr + 1;
        else                                                    // 防止产生锁存器
            wr_addr_ptr <= wr_addr_ptr;
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            rd_addr_ptr <= 0;
        else if(rd_en && (~empty))
            rd_addr_ptr <= rd_addr_ptr + 1;         
        else 
            rd_addr_ptr <= rd_addr_ptr;
    end
    
    
//==============双端口RAM=======================
    integer i;
    reg [data_width-1:0] memory[fifo_depth-1:0];    //定义fifo_depth个大小为data_width宽度的内存
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            for(i=0;i<fifo_depth;i=i+1) begin
                memory[i] <= 0;
            end
        else if(wr_en && (!full))
            memory[wr_addr] <= wr_data;
        else
            memory[wr_addr] <= memory[wr_addr]; 
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
                rd_data <= 0;                       // 复位,读出数据为0
        else if(rd_en && (!empty))
            rd_data <= memory[rd_addr];
        else 
            rd_data <= rd_data;
    end
    
//================空满标志信号输出==================
//=======读写指针相同(读指针第一次追上写指针),输出空标志=======
//===============读写指针最高位不同,其他位相同,输出满标志=======
    assign empty = (wr_addr_ptr == rd_addr_ptr) ? 1'b1 : 1'b0;
    assign full  = ({!(wr_addr_ptr[data_addr]),wr_addr} == rd_addr_ptr) ? 1'b1 : 1'b0;
       
endmodule

仿真testbench文件:

`timescale 1ns / 1ps
`define clk_period 20


module sync_fifo_tb();

    reg clk;     
    reg rst_n;   
    reg rd_en;   
    reg wr_en;   
    reg [7:0] wr_data; 

    
    wire full;    
    wire empty;
    wire [7:0] rd_data;   
    
    always #10 clk = ~clk;
    
    initial begin
        clk = 0;
        rst_n = 0;
        wr_en = 0;
        rd_en = 0;
        wr_data = 0;
        #20;
        rst_n = 1;
        write;              // 写任务
        read;               // 读任务
    end
    
    task write;
        integer i;
        begin
            for(i=0;i<16;i=i+1) begin
                wr_en = 1;
                @(posedge clk);
                wr_data = {$random}%256;    // 产生0-255范围的随机数
            end
            wr_en = 0;    
        end
    endtask
    
    task read;
        integer i;
        begin
            for(i=0;i<16;i=i+1) begin
                rd_en = 1;
                @(posedge clk);
            end
            rd_en = 0;    
        end
    endtask
    
    sync_fifo inst(
    .clk     (clk),             // 同步时钟
    .rst_n   (rst_n),
    .rd_en   (rd_en),
    .wr_en   (wr_en),
    .wr_data (wr_data),
    .rd_data (rd_data),
    .full    (full),            
    .empty   (empty)              
    );
    

   
endmodule


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值