两种同步FIFO的设计方法(计数器、扩展位)

一、同步FIFO介绍

  FIFO (First-In-First-Out) 是一种先进先出的数据交互方式,相当于一个RAM存储模块,在数字ASIC/SOC设计中常常被使用。FIFO按工作时钟域的不同又可以分为:同步FIFO和异步FIFO*。同步FIFO的写时钟和读时钟为同一个时钟,FIFO内部所有逻辑都是同步逻辑,常常用于交互数据缓冲。异步FIFO的写时钟和读时钟为异步时钟,FIFO内部的写逻辑和读逻辑的交互需要异步处理,异步FIFO常用于跨时钟域交互。

  同步FIFO通常由三大部分组成:
    (1)、 FIFO写逻辑控制——产生FIFO写地址、写有效信号,同时产生FIFO写满、写错等状态信号;
    (2)、 FIFO读逻辑控制——产生FIFO读地址、读有效信号,同时产生FIFO读空、读错等状态信号;
    (3)、 FIFO存储体(如Memory,reg等)。

其逻辑结构如下所示:
在这里插入图片描述
FIFO读写逻辑控制如下:
  1)、当FIFO初始化(复位)时fifo_write_addr与fifo_read_addr同指到0x0,此时FIFO处于空状态;
  2)、当FIFO进行写操作时,fifo_write_addr递增(增加到FIFO DEPTH时回绕),与fifo_read_addr错开,此时FIFO处于非空状态;
  3)、当FIFO进行读操作时,fifo_read_addr递增;

FIFO空满状态产生:
  为产生FIFO空满标志,引入FIFO Counter计数器,FIFO Counter寄数器用于指示FIFO内部存储数据个数;
  1)、当只有写操作时,FIFO Counter加1;只有读操作是,FIFO Counter减1;其他情况下,FIFO Counter保持;
  2)、当FIFO Counter为0时,说明FIFO为空,fifo_empty置位;
  3)、当FIFO Counter等于FIFO_DEPTH时,说明FIFO已满,fifo_full置位;

二、同步FIFO Code

2.1.计数器Counter判断空满

//1)、RAM design ,16×8 daul ram---存储模块设计
module dp_ram(                                  //verilog---1995
                         wr_clk,
                         wr_en,
                         wr_addr,
                         wr_data,
                         rd_clk,
                         rd_en,
                         rd_addr,
                         rd_data);
                         
       parameter  WIDTH = 8;
       parameter  DEPTH = 16;
       parameter  ADDR = 4;
        
       input             wr_clk;
       input             wr_en;
       input [ADDR-1:0]  wr_addr;
       input [WIDTH-1:0] wr_data;
       input             rd_clk;
       input             rd_en;
       input [ADDR-1:0]  rd_addr;   
       output[WIDTH-1:0] rd_data;

       reg   [WIDTH-1:0] rd_data; 
        
       reg  [WIDTH-1:0]  memory[DEPTH-1:0];      //memory为一个由16个8位寄存器组成的存储体

//read and write data
    always @(posedge wr_clk) begin               //写数据部分
      if(wr_en) begin
        memory[wr_addr] <= wr_data;              //时序逻辑,采用非阻塞赋值"<="
      end
    end

    always @(posedge rd_clk) begin               //读数据部分
      if(rd_en) begin
        rd_data <= memory[rd_addr];              //时序逻辑,采用非阻塞赋值"<="
      end
    end

endmodule

//2)、top module design---顶层模块设计,FIFO    
module sync_fifo                                
             #(parameter  F_WIDTH = 8,
               parameter  F_DEPTH = 16,
               parameter  F_ADDR = 4 )       
             (input               clk,             //verilog---2001
              input               rst,
              input               wr_en,
              input               rd_en,
              input [F_WIDTH-1:0] wr_data,
               
              output[F_WIDTH-1:0] rd_data,
              output              full,
              output              empty            //full 和empty都为wire类型,以便于使用assign状态设计
              );
               
              reg    [F_ADDR-1:0] rd_addr;
              reg    [F_ADDR-1:0] wr_addr;         //读写地址为中间信号
              reg    [F_ADDR:0]  counter;         //计数器最大数值需达到16,4位2进制数最大只能表示15,故不需减1

//define full and empty status
              assign full = (counter == F_DEPTH)?1:0;
              assign empty = (counter == 'h0)?1:0;

//initialization,将顶层设计与存储体连接
     dp_ram   u_dp_ram(
              .wr_clk    (clk),
              .wr_en     (wr_en),
              .wr_addr   (wr_addr),
              .wr_data   (wr_data),
              .rd_clk    (clk),
              .rd_en     (rd_en),
              .rd_addr   (rd_addr),
              .rd_data   (rd_data)
                );

//generate  rd_addr, 产生读地址
      always@(posedge clk,negedge rst)begin
         if(!rst) begin
            rd_addr <= 'h0;
         end
         else if(!empty && rd_en) begin
            rd_addr <= rd_addr + 1'b1;
         end
      end

//generate  wr_addr, 产生写地址
      always@(posedge clk,negedge rst)begin
         if(!rst) begin
            wr_addr <= 'h0;
         end
         else if(!full && wr_en) begin
            wr_addr <= wr_addr + 1'b1;
         end
      end

//counter in fifo,计录存储体中的数据长度
      always@(posedge clk,negedge rst)begin
         if(!rst) 
            counter <= 'b0;
         else if(!full && wr_en && !rd_en)
            counter <= counter + 1'b1;
         else if(!empty && !wr_en && rd_en)
            counter <= counter - 1'b1;
         else
            counter <= counter;               

endmodule         

关于模块引用连接,采用名字映射时,相对应数据类型的规定如下图二所示:
在这里插入图片描述
在这里插入图片描述
代码参考:https://blog.csdn.net/u014070258/article/details/90052370

2.2.扩展地址最高位判断空满

在这里插入图片描述

module sync_fifo
       #(parameter   WIDTH = 8,
         parameter   DEPTH = 16,
         parameter   ADDR = 4)
       (input                clk, rst, wr_en, rd_en,
        input      [WIDTH-1:0]   wr_data,
        output reg [WIDTH-1:0]   rd_data,      //尽量将reg与output声明到一起避免提示变量重复声明
        output                   full,empty);

        wire   [ADDR-1:0]    rd_addr,wr_addr;   //中间信号不必在端口中声明
        reg    [WIDTH-1:0]   memory [DEPTH-1:0];
        reg    [ADDR:0]     rd_addr_1;       //扩展一位地址用于判断空满
        reg    [ADDR:0]     wr_addr_1;       //扩展一位地址用于判断空满
       //reg   [WIDTH-1:0]   rd_data;  // 有时将output与reg分开声明Questasim编译可能会报错
       
 assign wr_addr = wr_addr_1[ADDR-1:0];
 assign rd_addr = rd_addr_1[ADDR-1:0];

always @(posedge clk or negedge rst)       //写数据
  begin
    if(!rst)
      wr_addr_1 <= 'h0;
    else if(!full && wr_en)begin
      wr_addr_1 <= wr_addr_1 + 1'b1;
      memory[wr_addr] <= wr_data;
    end
  end

always @(posedge clk or negedge rst)       //读数据
  begin
    if(!rst)
      rd_addr_1 <= 'h0;
    else if(!empty && rd_en)begin
      rd_addr_1 <= rd_addr_1 + 1'b1;
      rd_data <= memory[rd_addr];
    end
  end

assign  empty = (w_addr_1==rd_addr_1)?1:0;   //地址判断
assign  full = ((w_addr_1[ADDR]!=rd_addr_1[ADDR]) && (w_addr_1[ADDR-1:0]==rd_addr_1[ADDR-1:0]))?1:0;

endmodule
发布了21 篇原创文章 · 获赞 10 · 访问量 1831

猜你喜欢

转载自blog.csdn.net/weixin_46022434/article/details/105095493