1,FIFO
是一种先进的数据缓
存器,一般分两种同步和异步,先说同步,在设计中同步FIFO是只有一个时钟信号,所以要考虑到的是读空,和写满两个状态的判断。
常见参数:
FIFO的宽度,一次读写操的数据位
FIFO的深度,可以存储多少个位宽的数据
满标志,已满或者将要满是FIFO会发送一个信号,防止继续写,造成数据的溢出
空标志,已空或者将要空时FIFO会发送一个信号,防止继续读,造成无效数据读出
读写时钟(同步是一个时钟)
2.2
FIFO的设计的关键:
产生可靠的FIFO读写地址并且生成FIFO“空/满”状态标志。
当读写地址相同时,表明FIFO为空,这种情况发生在复位,或者读指针读出最后一个数据,追上了写指针。
当读写指针再次相同时,表明FIFO为满,这种情况发生在写指针转了一圈,回来追上了读指针。
2.1计数器方法
构建一个计数器,该计数器fifo_cnt用来指示当前FIFO中的数据的个数:
复位时:该计数器为0,FIFO中的数据为0
当读写使能信号都有效时,说明又读又写,计数器不变,FIFO中的数据个数没有变化
当写使能有效且fifo非满,则fifo_cnt+1,表示写操作,fifo中的数据个数加1
当读使能有效且FIFO非空,则fifo_cnt- 1 ,表示读操作,FIFO中的数据个数减1
FIFO为空 empty = 1,fifo_cnt = 0, fifo为满 full fifo_cnt为FIFO深度最大值
2.2verilog实现
module sync_fifo_cnt
#(
parameter DATA_WIDTH = 'd8 ,
parameter DATA_DEPTH = 'd16 ,
parameter DATA_ADDR = 'd4
)
(
input clk ,
input rst_n ,
input [DATA_WIDTH - 1 : 0] data_in ,
input rd_en ,
input wr_en ,
output reg [DATA_WIDTH-1 :0] data_out ,
output empty ,
output full ,
output reg [DATA_ADDR :0 ] fifo_cnt ,
);
reg [DATA_WIDTH-1 :0 ] fifo_buff [DATA_DEPTH - 1 :0] ;
reg [DATA_ADDR - 1 :0] wr_addr ;
reg [DATA_ADDR - 1 :0] rd_addr ;
//读操作
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
rd_addr <= 1'b0 ;
else if (!empty && rd_en)begin
rd_addr <= rd_addr+1'b1;
data_out <= fifo_buff[rd_addr] ;
end
end
//写操作
always@(posedge clk or negedge rst_n)begin
if(!rst_n )
wr_addr <= 1'b0;
else if(!full&&wr_en)begin
wr_addr <= wr_addr +1'b1;
fifo_buff[wr_addr] <= data_in ;
end
end
//计数器
always@(posedge clk or negedge rst_n )begin
if(!rst_n)
fifo_cnt <= 1'b0;
else begin
case ({wr_en,rd_en})
2'b00 :fifo_cnt <= fifo_cnt ;
2'b01 :
if(fifo_cnt!= 0)
fifo_cnt <= fifo_cnt -1'b1;
2'b10:
if(fifo_cnt != DATA_DEPTH)
fifo_cnt <= fifo_cnt +1'b1;
2'b11:
fifo_cnt <= fifo_cnt ;
endcase
end
end
assign full = (fifo_cnt == DATA_DEPTH) ? 1'b1 :1'b0;
assign empty = fifo_cnt == 0 ? 1'b1 :1'b0;
endmodule
2.2.1测试仿真
例化一个深度为8 ,位宽为8 的同步FIFO
先对FIFO 进行写操作,知道写满,写书数据随机
然后对FIFO读操作,知道读空
然后在对FIFO写入4 个数据,同时读写操作
`timescal 1ns/1ns
module tb_sync_fifo_cnt (
);
parameter DATA_WIDTH = 8 ;
parameter DATA_DEPTH = 8 ;
reg clk ;
reg rst_n ;
reg [DATA_WIDTH - 1 :0] data_in ;
reg rd_en ;
reg wr_en ;
wire [DATA_WIDTH - 1: 0] data_out;
wire empty ;
wire full;
wire [4:0] fifo_cnt ;
sync_fifo_cnt
#(
.DATA_WIDTH(DATA_WIDTH),
.DATA_DEPTH(DATA_DEPTH)
)
sync_fifo_cnt_inst(
.clk(clk),
.rst_n(rst_n),
.data_in(data_in),
.rd_en(rd_en),
.wr_en(wr_en),
.data_out(data_out),
.empty(empty),
.full(full),
.fifo_cnt(fifo_cnt)
);
initial begin
clk = 1'b0; //初始时钟为0
rst_n <= 1'b0; //初始复位
data_in <= 'd0;
wr_en <= 1'b0;
rd_en <= 1'b0;
//重复8次写操作,让FIFO写满
repeat(8) begin
@(negedge clk)begin
rst_n <= 1'b1;
wr_en <= 1'b1;
data_in <= $random; //生成8位随机数
end
end
//重复8次读操作,让FIFO读空
repeat(8) begin
@(negedge clk)begin
wr_en <= 1'b0;
rd_en <= 1'd1;
end
end
//重复4次写操作,写入4个随机数据
repeat(4) begin
@(negedge clk)begin
wr_en <= 1'b1;
data_in <= $random; //生成8位随机数
rd_en <= 1'b0;
end
end
//持续同时对FIFO读写,写入数据为随机数据
forever begin
@(negedge clk)begin
wr_en <= 1'b1;
data_in <= $random; //生成8位随机数
rd_en <= 1'b1;
end
end
end
//------------<设置时钟>----------------------------------------------
always #10 clk = ~clk; //系统时钟周期20ns
endmodule
3,高位扩展法
例如3bit的地址,我们扩展成4bit 地址区间从3‘b000-3’b111变成4‘b0000-4'b1111,假设不看最高位的话,地址区间任然是3’b000-3'b111,所以,最高位就可以看作是指示位
总结:
当最高位不同,且其他位相同,则写指针多跑了一圈,表示写满了
当最高位相同,且其他位也相同,说明读指针追上了写指针,表示读空了。
3.1Verilog实现
module sync_fifo_ptr
# (
parameter DATA_WIDTH = 'd8 ,
parameter DATA_DEPTH = 'd16
)
(
input clk ,
input rst_n ,
input [DATA_WIDTH - 1 : 0] data_in ,
input rd_en ,
input wr_en,
output reg [DATA_WIDTH-1 :0] data_out ,
output empty ,
output full
);
reg [DATA_WIDTH - 1 : 0] fifo_buffer [DATA_DEPTH - 1 :0];
reg [4:0] wr_ptr ;
reg [4:0] rd_ptr ;
wire [3:0] wr_ptrt_true ;
wire [3:0] rd_ptr_true ;
wire wr_ptr_msb ;
wire rd_ptr_msb ;
assign {wr_ptr_msb,wr_ptrt_true} = wr_ptr;
assign {rd_ptr_msb,rd_ptr_true } = rd_ptr ;
//read
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
rd_ptr<= 1'b0;
else if(rd_en && !empty)begin
data_out <= fifo_buffer[rd_ptr];
rd_ptr <= rd_ptr +1'b1;
end
end
//write
always@(posedge clk or negedge rst_n )begin
if(!rst_n )
wr_ptr<= 1'b0;
else if(wr_en && !full)begin
wr_ptr <= wr_ptr +1'b1;
fifo_buffer[wr_ptr ] <= data_in ;
end
end
//
assign empty =(wr_ptr == rd_ptr )? 1'b1:1'b0;
assign full = (wr_ptr_msb != rd_ptr_msb) && (wr_ptrt_true == rd_ptr_true) ? 1'b1 :1'b0 ;
endmodule
测试与上面经测试一样,这边就不再演示了。