参考:

【新提醒】FIFO学习(转) - chuanshaoke的日志 - EETOP 创芯网论坛 (原名:电子顶级开发网) -

芯动力——硬件加速设计方法_中国大学MOOC(慕课) (icourse163.org)

FIFO的基本概念

FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器。

FIFO实际上是做匹配速率的一个match。

假设数据通道1的速率比数据通道2速率大(即,速率A>速率B)

两个速率不同的数据通道相连,两个数据通道的速率不match。

数据的吞吐率就不够,即在数据通道2,跑不了数据通道1这么多数据,

这时,需要中间一个缓存起来。

数据通道1不是全实时的一个工作,所以建立一个中间的缓冲机制FIFO,让数据进行一个match。 

FIFO的结构

 读写指针宽度

  • 与地址宽度相当
  • 地址增加而溢出后,自动变成0
  • 循环指针

 FIFO的一些重要参数

  • FIFO的深度(FIFO depth):FIFO可以存储多少个N位的数据(如果宽度为N)
  • FIFO的宽度(FIFO width):FIFO一次读写操作的数据位
  • FIFO空标志:IFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)。
  • FIFO满标志:FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow)
  • 读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。
  • 写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。
  • 读指针(Read-pointer):指向下一个读出地址。读完后自动加1。
  • 写指针( Write-pointer):指向下一个要写入的地址的,写完自动加1。

注意:读写指针其实就是读写的地址,只不过这个地址不能任意选择,而是连续的。

FIFO分类

根均FIFO工作的时钟域,可以将FIFO分为:

  • 同步FIFO:数据写入FIFO的时钟和数据读出FIFO的时钟是同步的(synchronous)
  • 异步FIFO:数据写入FIFO的时钟和数据读出FIFO的时钟是异步的(asynchronous)

同步FIFO

同步FIFO:数据写入FIFO的时钟和数据读出FIFO的时钟是同步的(synchronous)

同步FIFO结构

  同步FIFO的一些重要参数

  • 时钟复位,clk,rst_n_i,读写应用同一个时钟;
  • FIFO宽度,用参数FIFO_data_size表示,也就是FIFO存储的数据宽度;
  • FIFO深度,用参数FIFO_addr_size表示,也就是地址的大小,也就是说能存储多少个数据;
  • 读使能,rd_en_i
  • 写使能,wr_en_i
  • 写地址指针wr_pointer,由自动加一生成,将数据写入该地址;
  • 读地址指针rd_pointer,由自动加一生成,将该地址上的数据读出;
  • 计数器fifo_counter,用计数器来进行空满标志的判断;
  • 满标志,full,当FIFO中的数据满了以后将不再能进行数据的写入;
  • 空标志empty,当FIFO为空的时候将不能进行数据的读出;

同步FIFO的空满判断:

使用计数器fifo_counter记录FIFO RAM中的数据个数:

  1. fifo_counter=0,给出empty信号
  2. fifo_counter=FIFO_addr_size,给出full信号

计数器fifo_counter计数:

  1. 写而未满时增加1
  2. 读而未空时减1
  3. 同时发生读写操作时,fifo_counter计数不变

同步FIFO的Verilog描述

存储数据的FIFO用双端口RAM表示:

reg [FIFO_data_size - 1:0] register_array [0:FIFO_addr_size-1];
//memory depth,有多少个单元  FIFO_addr_size个存储单元
//memory width,一个存储单元有多少位  FIFO_data_size位

判断空满标志:

//计数器计数count
always@(posedge clk or negedge rst_n_i)
  	if(!rst_n_i)
  		count<=0;
  	else if(wr_en_i && rd_en_i && !full_o && !empty_o)//同时读写不变
				count<=count;
		else if(wr_en_i && !rd_en_i && !full_o)//写而未满+1
				count<=count+1;
		else if(!wr_en_i && rd_en_i  && !empty_o)//读而未空-1
				count<=count-1;
				
 //generate flags
	assign full_o = (count == FIFO_addr_size);
	assign empty_o = (count == 0);

写数据:

//write pointer
  always@(posedge clk or negedge rst_n_i)
  if(!rst_n_i)
  	   wr_point<=0;
  else if(wr_en_i &&  !full_o)
				wr_point <= wr_point+1;
	else
		    wr_point <= wr_point;

//write data
interger i;
always@(posedge clk or negedge rst_n_i)
begin
  	if(!rst_n_i)
  	  begin
  		  for(i=0;i<addr;i=i+1)begin
  		  	register_array[i] <= 0;
  		  end	
  		end		
  	else if(wr_en_i && !full_o)//写使能,且存储单元没满
			register_array[wr_point] <= wr_data_i;
end

读数据:

//read pointer
	always@(posedge clk or negedge rst_n_i)
		if(!rst_n_i)
  		rd_point<=0;
    else if(rd_en_i &&  !empty_o)
				rd_point <= rd_point+1;
		else
		    rd_point <= rd_point;
//read data
	always@(posedge clk or negedge rst_n_i)
  	if(!rst_n_i)
  	  begin
  		  	rd_data_o<= 0;
  		end		
  	else if(rd_en_i && !empty_o)//读使能,且存储单元没空
			rd_data_o<=register_array[rd_point] ;

 完整的同步FIFO代码

module sync_fifo
#(parameter FIFO_data_size = 32, parameter ADDR_WIDTH=3)
(
//reset signal  input   negedge active
  input rst_n_i,
//clock
  input clk,
//write interface
  input wire                       wr_en_i,   //write enable
  input wire  [FIFO_data_size-1:0] wr_data_i, //write data
//read interface
  input wire          				rd_en_i,   //read enable
   output reg [FIFO_data_size-1:0] rd_data_o, //read data
//flags
   output full_o,
   output empty_o
);
 parameter FIFO_addr_size = 1<<ADDR_WIDTH; //1<<3=8

//memory definition   use register model
	reg [FIFO_data_size - 1:0] register_array [0:FIFO_addr_size- 1];
//memory depth,有多少个单元  FIFO_addr_size个存储单元
//memory width,一个存储单元有多少位  FIFO_data_size位

//write pointer and read pointer
 reg[ADDR_WIDTH-1:0] wr_point;//例如存储单元0-7,一共8个存储单元 [2:0]
 reg[ADDR_WIDTH-1:0] rd_point;//例如存储单元0-7,一共8个存储单元
 
 reg [ADDR_WIDTH:0] count;
 //计数有0,1,2,3,4,5,6,7,8
 //9个状态,要用[3:0]表示
 
//############################################
//write pointer
  always@(posedge clk or negedge rst_n_i)
  if(!rst_n_i)
  	   wr_point<=0;
  else if(wr_en_i &&  !full_o)
				wr_point <= wr_point+1;
	else
		    wr_point <= wr_point;
//read pointer
	always@(posedge clk or negedge rst_n_i)
		if(!rst_n_i)
  		rd_point<=0;
    else if(rd_en_i &&  !empty_o)
				rd_point <= rd_point+1;
		else
		    rd_point <= rd_point;

//##########################################
//element counter
  always@(posedge clk or negedge rst_n_i)
  	if(!rst_n_i)
  		count<=0;
  	else if(wr_en_i && rd_en_i && !full_o && !empty_o)//同时读写不变
				count<=count;
		else if(wr_en_i && !rd_en_i && !full_o)//写而未满+1
				count<=count+1;
		else if(!wr_en_i && rd_en_i  && !empty_o)//读而未空-1
				count<=count-1;
				
 //generate flags
	assign full_o = (count == addr);
	assign empty_o = (count == 0);
	
//############################################################33
//access arry
//	reg [n-1:0] register_array [0:addr-1];
//memory depth,有多少个单元  addr个存储单元
//memory width,一个存储单元有多少位  n位
//###############################################################
//write data
interger i;

always@(posedge clk or negedge rst_n_i)
begin
  	if(!rst_n_i)
  	  begin
  		  for(i=0;i<addr;i=i+1)begin
  		  	register_array[i] <= 0;
  		  end	
  		end		
  	else if(wr_en_i && !full_o)//写使能,且存储单元没满
			register_array[wr_point] <= wr_data_i;
end
//read data
	always@(posedge clk or negedge rst_n_i)
  	if(!rst_n_i)
  	  begin
  		  	rd_data_o<= 0;
  		end		
  	else if(rd_en_i && !empty_o)//读使能,且存储单元没空
			rd_data_o<=register_array[rd_point] ;
endmodule

参考:

【新提醒】FIFO学习(转) - chuanshaoke的日志 - EETOP 创芯网论坛 (原名:电子顶级开发网) -

芯动力——硬件加速设计方法_中国大学MOOC(慕课) (icourse163.org)


同步FIFO编码练习

练习网站:

 牛客网-在线编程-Verilog练习

 里面还有其他人总结的各种题解,可以自己总结下思路。写完代码可以在线运行,检查自己的代码哪里有问题,

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐