FIFO的原则:
满不能写,空不能读。
设计的思考:如何产生full empty信号?
方法1:计数器cnt,当写一个数据时,cnt加1 ; 反之,减1。
方法2:地址位扩展一位,用最高位进行判断满空。
//写指针 读指针
//状态full empty 模块用一个fifo_cnt 计数器去控制。 同时这个计数器醉着读写状态的变化自增自减
// 写使能后每个周期写数据
//
module syn_fifo #(
parameter ADDR_width = 2,
parameter DATA_WIDTH = 8,
parameter depth = 2** ADDR_width //深度 4
)
(
input clk,
input rst_n,
//write
input wr_en,
input [DATA_WIDTH - 1: 0] wr_data,
//read
input rd_en,
output [DATA_WIDTH - 1: 0] rd_data,
output wr_full,
output rd_empty
);
//RAM
reg [DATA_WIDTH - 1: 0] mem[0: depth-1 ];
reg [ADDR_width :0] fifo_cnt ; //计数器多计数一位
reg [ADDR_width - 1 :0] wr_pointer ;
reg [ADDR_width - 1 :0] rd_pointer ;
always@(posedge clk)
begin
if(!rst_n) begin
fifo_cnt <= 0;
end
else begin
if(wr_en && !rd_en) begin //写
fifo_cnt <= fifo_cnt + 'b1;
end
else if(rd_en && !wr_en) begin //读
fifo_cnt <= fifo_cnt - 'b1;
end
end
end
//write or read pointer : if it arrives to depth,then it comes back to zero
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
wr_pointer <= 0;
else if(wr_en && !rd_empty)
begin
if( wr_pointer == depth - 1) //full ,then zero 深度和存储的深度一致 才可以正确存放
wr_pointer <= 'b0;
else // 使能读写
wr_pointer <= wr_pointer + 'b1;
end
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
rd_pointer <= 0;
else if(rd_en && !wr_full)
begin
if( rd_pointer == depth - 1) //full ,then zero 深度和存储的深度一致 才可以正确存放
rd_pointer <= 'b0;
else // 使能读写
rd_pointer <= rd_pointer + 'b1;
end
end
//data writes into fifo
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
mem[wr_pointer] <= 'b0 ;
end
else if(wr_en)
begin
mem[wr_pointer] <= wr_data ; //
end
end
// use FIFO_CNT to judge full or empty
assign rd_data = mem[rd_pointer]; // when rd_en is asserted, rd_pointer moves one
//way one: use counter to judge full or empty
//way two: use wr_pointer + 1 = rd_pointer to judge full
assign wr_full = (fifo_cnt == depth) ? 'b1: 'b0;
assign rd_empty= (fifo_cnt == 0) ? 1:0 ;
endmodule
module syn_fifo_tb(
);
parameter ADDR_width = 2;
parameter DATA_WIDTH = 8;
parameter depth = 2** ADDR_width; //深度 4
reg clk ;
reg rst_n ;
reg wr_en ;
reg [DATA_WIDTH - 1: 0] wr_data ;
reg rd_en ;
wire [DATA_WIDTH - 1: 0] rd_data ;
wire wr_full ;
wire rd_empty ;
always #5 clk = ~clk;
initial begin
clk = 0;
rst_n = 0 ;
wr_en = 0;
rd_en = 0;
@(negedge clk) rst_n = 1; // ! : negedge
@(negedge clk) wr_en = 1;
wr_data = $random;
repeat(4) begin
@(negedge clk)
wr_data = $random;
end
@(negedge clk)
wr_en = 0; // only w but not read
rd_en = 1;
repeat(3) begin
@(negedge clk);
end
@(negedge clk)
rd_en = 0;
wr_en = 1;
wr_data = $random;
repeat(7) begin
@(negedge clk)
wr_data = $random;
end
#20 $finish;
end
syn_fifo #(
.ADDR_width ( ADDR_width ),
.DATA_WIDTH ( DATA_WIDTH ),
.depth ( depth ) //深度 16
)
syn_fifo_tb(
. clk ( clk ) ,
. rst_n ( rst_n ) ,
. wr_en ( wr_en ) ,
. wr_data ( wr_data ) ,
. rd_en ( rd_en ) ,
. rd_data ( rd_data ) ,
. wr_full ( wr_full ) ,
. rd_empty ( rd_empty )
);
endmodule
这种方法:因为增加了计数器, 来产生满和空的条件,这就导师消耗资源,随着深度的增加,也会增加计数器的位宽,这样会降低最高频率。