同步FIFO
fifo介绍
先入先出的数据缓存器,没有外部读写地址线,可同时读写。
fifo种类
FIFO有同步fifo和异步fifo之分。同步fifo只有一个时钟,读写时钟一样;异步fifo读写时钟不同,可能不同频率,或者同频不同相。
fifo用途
- 缓存数据
如果输入端burst一个数据,接收端不能立马全部接受,需要fifo存住这些数据。
2. 跨时钟域数据传输同步数据。
同步fifo
同步FIFO就是读写时钟是一个,读写地址是同步的。
- 读写地址是下一次要进行读写的地址,是否进行读写需要判断最终读写有效(通过输入读写位和空满标志)
- 最终读写有效才进行读写
- 地址变换:最终读写有效才进行地址跳变,否则的话这个地址还没进行读写就跳过去了。
- 可以在读写地址上增加一位,用来判断是否空满。空满只通过比较地址,与读写有效输入端口无关。
空:读写地址的MSB相同,其他位也相同;满:读写地址的MSB不同,其他位相同
用组合逻辑实现,与时钟信号无关,没有延迟,直接根据当前的读写地址就可以判断
1 |
|
也可以用一个计数器cnt来表示当前有多少个没有读走的数据,当写有效,+1,读有效-1,又读又写不变。但这需要额外的计数器,并且可能影响FIFO最终的速度
读写地址位数不变,通过地址和读写有效输入端口共同判断
用时序逻辑实现,有一个时钟的延迟,需要根据读写有效输入位判断空满,判断的是下一个周期的空满状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25/*
rd,wr 读写有效输入端口
rp,wp 读写地址
*/
// Full signal generate
always@(posedge clk or negedge rst) begin
if(!rst) full_in <= 1'b0;
else begin
if( (~rd && wr)&&((wp==rp-1)||(rp==4'h0&&wp==4'hf)))
// 下个周期只写不读,如果当前写地址落后读一个地址或者读地址是0000,写地址是1111,下个周期fifo满了。
full_in <= 1'b1;
// 如果这个周期已经满了,但是有个读信号,那么下个周期就不满了
else if(full_in && rd) full_in <= 1'b0;
end
end
// Empty signal generate
always@(posedge clk or negedge rst) begin
if(!rst) empty_in <= 1'b1;
else begin
// 空跟满类似
if((rd&&~wr)&&(rp==wp-1 || (rp==4'hf&&wp==4'h0)))
empty_in<=1'b1;
else if(empty_in && wr) empty_in<=1'b0;
end
end
1 |
|
/************************************************************\
- Generation of Read and Write address pointers. They use *
- LFSR counters, which are very fast. Because of the *
- nature of LFSR, one address is sacrificed. *
- ************************************************************/
wire read_linearfeedback, write_linearfeedback;
assign read_linearfeedback = ! (read_addr[8] ^ read_addr[4]);
assign write_linearfeedback = ! (write_addr[8] ^ write_addr[4]);
// 读地址反馈
always @(posedge clock or posedge fifo_gsr)
if (fifo_gsr)
read_addr <= 9’h0;
else if (read_allow)
read_addr <= { read_addr[7], read_addr[6], read_addr[5],
read_addr[4], read_addr[3], read_addr[2],
read_addr[1], read_addr[0], read_linearfeedback };
// 写地址反馈
always @(posedge clock or posedge fifo_gsr)
if (fifo_gsr)
write_addr <= 9’h0
else if (write_allow)
write_addr <= { write_addr[7], write_addr[6], write_addr[5],
write_addr[4], write_addr[3], write_addr[2],
write_addr[1], write_addr[0], write_linearfeedback };
- ************************************************************/
1 |
|
// file name : fifo_1.v
// email : dyk1203@126.com
// author : dong yk
`timescale 1ns/100ps
module fifo #(parameter Width=8,
parameter Depth=8) //FIFO的深度
( input clk,
input rst,
input we,
input re,
input [Width-1:0] data_i,
output full,
output empty,
output[Width-1:0] data_o
);
function integer clog2; // 求2的对数 ,log2(x)。得到memory的地址宽度
input integer value;
begin
for(clog2=0;value>1;value=value>>1) begin
clog2=clog2+1;
end
end
endfunction
reg [Width-1:0] dout;
reg [clog2(Depth):0] r_addr; // 读地址
reg [clog2(Depth):0] w_addr; // 写地址
wire re_flag = (re==1’b1) && (empty==1’b0); // 在输入读有效,且fifo不为空时,读有效
wire we_flag = (we==1’b1) && (full == 1’b0);// 在输入写有效,且fifo不为满时,写有效
reg[Width-1:0] memory[0:Depth-1]; // 申请fifo内的存储空间
// 空:读写地址相同
assign full = (r_addr[clog2(Depth)-1:0]==w_addr[clog2(Depth)-1:0]) && (r_addr[clog2(Depth)] ^ w_addr[clog2(Depth)]);
// 满:读写地址的MSB不同,其他位相同
assign empty =(r_addr[clog2(Depth)-1:0]==w_addr[clog2(Depth)-1:0]) && (r_addr[clog2(Depth)] == w_addr[clog2(Depth)]);
assign data_o = dout;
always@(posedge clk or negedge rst) begin
if(!rst) begin
r_addr <= 0;
dout <= 0;
end else begin
r_addr <= re_flag?(r_addr+1) : r_addr ; // 读地址累加
dout <= re_flag?memory[r_addr[clog2(Depth)-1:0]]:dout; // 数据输出
end
end
always @(posedge clk or negedge rst) begin
if(!rst) begin
w_addr <= 0;
end else begin
w_addr <= we_flag?(w_addr+1) : w_addr; // 写地址累加
memory[w_addr[clog2(Depth)-1:0]] <= we_flag?data_i:memory[w_addr[clog2(Depth)-1:0]]; // 写入数据
end
end
endmodule
1 |
|
// file name : fifo_1.v
//
// author : dong yk
// description : 增加usedw 表示fifo中已经有了的数据个数
`timescale 1ns/100ps
module fifo #(parameter Width=8,
parameter Depth=8) //FIFO的深度
( input clk,
input rst,
input we,
input re,
input [Width-1:0] data_i,
output full,
output empty,
output[Width-1:0] data_o,
output [clog2(Depth)-1:0] usedw
);
function integer clog2; // 求2的对数 ,log2(x)。得到memory的地址宽度
input integer value;
begin
for(clog2=0;value>1;value=value>>1) begin
clog2=clog2+1;
end
end
endfunction
reg [Width-1:0] dout;
reg [clog2(Depth):0] r_addr; // 读地址
reg [clog2(Depth):0] w_addr; // 写地址
reg [clog2(Depth)-1:0] usedw_r;
wire re_flag = (re==1’b1) && (empty==1’b0); // 在输入读有效,且fifo不为空时,读有效
wire we_flag = (we==1’b1) && (full == 1’b0);// 在输入写有效,且fifo不为满时,写有效
reg[Width-1:0] memory[0:Depth-1]; // 申请fifo内的存储空间
// 空:读写地址相同
assign full = (r_addr[clog2(Depth)-1:0]==w_addr[clog2(Depth)-1:0]) && (r_addr[clog2(Depth)] ^ w_addr[clog2(Depth)]);
// 满:读写地址的MSB不同,其他位相同
assign empty =(r_addr[clog2(Depth)-1:0]==w_addr[clog2(Depth)-1:0]) && (r_addr[clog2(Depth)] == w_addr[clog2(Depth)]);
assign data_o = dout;
always@(posedge clk or negedge rst) begin
if(!rst) begin
r_addr <= 0;
dout <= 0;
end else begin
r_addr <= re_flag?(r_addr+1) : r_addr ; // 读地址累加
dout <= re_flag?memory[r_addr[clog2(Depth)-1:0]]:dout; // 数据输出
end
end
always @(posedge clk or negedge rst) begin
if(!rst) begin
w_addr <= 0;
end else begin
w_addr <= we_flag?(w_addr+1) : w_addr; // 写地址累加
memory[w_addr[clog2(Depth)-1:0]] <= we_flag?data_i:memory[w_addr[clog2(Depth)-1:0]]; // 写入数据
end
end
assign usedw = usedw_r;
always @(posedge clk or negedge rst) begin
if(!rst) begin
usedw_r <= 0;
end//if
else begin
case({we_flag,re_flag})
2’b00: begin
usedw_r <= usedw_r;
end//00
2’b01: begin
if(usedw_r==(Depth-1))
usedw_r <= usedw_r;
else
usedw_r <= usedw_r + 1’b1;
end//01
2’b10: begin
if(usedw_r==0)
usedw_r <= usedw_r;
else
usedw_r <= usedw_r - 1’b1;
end//10
2’b11: begin
usedw_r <= usedw_r;
end//11
default: usedw_r <= 0;
endcase
end//else
end//always
endmodule
1
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!