同步FIFO
FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器。FIFO实际上是做匹配速率的一个match。假设数据通道1的速率比数据通道2速率大(即,速率A>速率B)两个速率不同的数据通道相连,两个数据通道的速率不match。数据的吞吐率就不够,即在数据通道2,跑不了数据通道1这么多数据,这时,需要中间一个缓存起来。数据通道1不是全实时的一个工作,所以建立一个中间的缓冲机
参考:
【新提醒】FIFO学习(转) - chuanshaoke的日志 - EETOP 创芯网论坛 (原名:电子顶级开发网) -
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中的数据个数:
- fifo_counter=0,给出empty信号
- fifo_counter=FIFO_addr_size,给出full信号
计数器fifo_counter计数:
- 写而未满时增加1
- 读而未空时减1
- 同时发生读写操作时,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编码练习
练习网站:
里面还有其他人总结的各种题解,可以自己总结下思路。写完代码可以在线运行,检查自己的代码哪里有问题,
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)