Verilog 实现同步FIFO

这篇博文记录一下这两天折腾的同步FIFO,大多时候我们使用FIFO都是直接例化IP,最近刚好有位大佬级别的师兄要自己写一个异步FIFO。我想着我也玩玩吧,于是就有重最简单的同步FIFO开始写起,第一次写对于FIFO没有什么概念,仅仅是停留在First in first out 的理解。前几看来一篇关于FIFO深度计算的博文,让自己对FIFO有了更深刻的理解。相较于first in first out更深入的理解FIFO,可以这么看,FIFO是一个双端口RAM加上读写指针以及满、空的判断。如下图所示,本图来自博文:https://blog.csdn.net/hengzo/article/details/49683707
Verilog 实现同步FIFO_第1张图片简单谈一下设计同步FIFO的基本思路:
在开始之前我们先定义一些参量有利于后续的表述:以深度为16, 数据位宽为16bits为例进行说明。write为写信号,高电平有效;read为读信号,高电平有效;wp_reg为读指针,rp_reg为写指针,full为满,empty为空,cnt_reg为计算当前FIFO中含有的数据个数,[15:0]mem[15:0]采用寄存器搭建起来的memory ,fifo_din 需要写入FIFO 的数据,fifo_dout由FIFO输出的数据。
1、写数据:!full 和 write 同时为真时,写数据且wp_reg加1
2、读数据:!empty和 read同时为真时,写数据且rp_reg加1
3、full 和empty 则根据cnt_reg进行判断高低电平,cnt_reg = 16,full = 1,因为0~15刚好为16个数,对应于FIFO的深度。如果cnt_reg = 15 时, full = 1,那么意味着0-14,仅有15个数。cnt_reg = 15 时, empty = 1。
4、cnt_reg 写而满是加1,读而为空是减1
5、mem的读写。!full 和 write 同时为真时 mem[wp_reg] = fif_din,!empty和 read同时为真时,fifo_dout = mem[rp_reg]

下面是代码:

//========================================================
//  Module Name  : sync_fifo
//  Author   : Winson_c
//  Tool version : Quartus II 13.1
//  Revision  : v1.0
//  Description  : Synchronize FIFO 
//=========================================================
`define FIFO_DEPTH 16
`define DATA_WIDTH 16
module sync_fifo(
 input clk,
 input rst_n,
 input [`DATA_WIDTH - 1:0]fifo_din,
 input read,
 input write,
 
 output  empty,
 output  full,
 output reg [`DATA_WIDTH - 1:0]fifo_dout
 );
 
 //parameters define
 parameter FIFO_WIDH = 4;
 
 reg [FIFO_WIDH - 1:0] rp_reg;//0-15-0-15
 reg [FIFO_WIDH - 1:0] wp_reg;
 reg [FIFO_WIDH:0]   cnt_reg;//FIFO_DEPTH = 16
 reg [`DATA_WIDTH - 1:0] men[`FIFO_DEPTH - 1:0];
 
 //rp_reg
 always@(posedge clk or negedge rst_n) begin
  if(rst_n == 0)
   rp_reg <= 4'd0;
  else if(!empty && read == 1'b1)
   rp_reg <= rp_reg + 1'b1;
  else rp_reg <= rp_reg;
 
 end 
 
 //wp_reg
 always@(posedge clk or negedge rst_n) begin
  if(rst_n == 0)
   wp_reg <= 4'd0;
  else if(!full && write == 1'b1)
   wp_reg <= wp_reg + 1'b1;
  else wp_reg <= wp_reg;
 
 end 
 
 //cnt_reg
 always@(posedge clk or negedge rst_n) begin
  if(rst_n == 0)
   cnt_reg <= 4'd0;
  else if(!full && write == 1'b1 && !empty && read == 1'b1)
   cnt_reg <= cnt_reg;
  else if(!full && write == 1'b1)
   cnt_reg <= cnt_reg + 1'b1;
  else if(!empty && read == 1'b1)
   cnt_reg <= cnt_reg - 1'b1;
  else 
   cnt_reg <= cnt_reg;
 end 
 
 //men
 always@(posedge clk) begin
  if(!full && write == 1'b1)
   men[wp_reg] <= fifo_din;
  if(!empty && read == 1'b1)
   fifo_dout <= men[rp_reg];
 end 
 
 assign empty = (cnt_reg == 5'd0) ? 1'b1 : 1'b0;
 assign full = (cnt_reg == 5'd16) ? 1'b1 : 1'b0;//FIFO DEPTH 16
 
endmodule

下面是testbench:

`timescale 1ns/1ns
`define clk_period 20
module sync_fifo_tb;
 reg clk;
 reg rst_n;
 reg [15:0]fifo_din;
 reg read;
 reg write;
 
 wire empty;
 wire full;
 wire [15:0]fifo_dout;
 
 sync_fifo sync_fifo_inst(
   .clk(clk),
   .rst_n(rst_n),
   .fifo_din(fifo_din),
   .read(read),
   .write(write),
   
   .empty(empty),
   .full(full),
   .fifo_dout(fifo_dout)  
 );
 
 initial begin
  clk=1;
  write = 0;
  read = 0;
  rst_n=0;
  #45;
  rst_n=1;
  #320;
  write = 1;
  read = 0;
  #100;
  read = 1;
  #20;
  read = 0;
  #200;
  write = 0;
  read = 1;
  #200;
  write = 1;
  read = 1;
  #100;
  /*write = 1;
  read = 0;
  #200;
  write = 0;
  read = 1;
  #300;*/
  #300;
  $stop;
 end
 
 //clk 
 always #(`clk_period/2) clk=~clk;
 
 //fifo_din
 always@(posedge clk) begin
  fifo_din <= $random($time);
 end 
 
 //always #(`clk_period * 60) write = ~write;
 //always #(`clk_period *20) read = ~read;
 
 
endmodule 

仿真波形:
在这里插入图片描述

你可能感兴趣的:(Verilog 实现同步FIFO)