数字设计---同步fifo

同步FIFO原理

FIFO

FIFO (First-In-First-Out) 是一种先进先出的数据交互方式,在数字ASIC设计中常常被使用。

FIFO 与普通存储器 RAM 的区别是没有外部读写地址线,使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加 1 完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。 FIFO 本质上是由 RAM (或者寄存器)加读写控制逻辑构成的一种先进先出的数据缓冲器。

FIFO按工作时钟域的不同又可以分为:同步FIFO异步FIFO

同步FIFO与异步FIFO的区别

同步FIFO的写时钟和读时钟为同一个时钟,FIFO内部所有逻辑都是同步逻辑,常常用于交互数据缓冲。

异步FIFO的写时钟和读时钟为异步时钟,FIFO内部的写逻辑和读逻辑的交互需要异步处理,异步FIFO常用于跨时钟域交互。

同步FIFO的端口

  • FIFO 的宽度:即 FIFO 一次读写操作的数据位;
  • FIFO 的深度:指的是 FIFO 可以存储多少个 N 位的数据(如果宽度为 N)。
  • 满标志:FIFO 已满或将要满时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的写操作继续向FIFO 中写数据而造成溢出(overflow)。
  • 空标志:FIFO 已空或将要空时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的读操作继续从FIFO 中读出数据而造成无效数据的读出(underflow)。
  • 读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。
  • 写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。
  • 将满标志(almost full):FIFO将要满时由FIFO的状态电路送出的一个信号。
  • 将空信号(almost empty):FIFO将要空时由FIFO的状态电路送出的一个信号。

同步FIFO的结构

读写指针

img

从这个图上我们可以看出,写指针总是指向下一个时钟要写的地址,读指针总是指向下一个时钟要读的地址。读指针等于写指针的时候有可能为空,有可能为满。(当然,读指针也可以指向当前正在读地址,但相应的根据地址读取数据的逻辑会有所不同,前面读指针指向下一个时钟要读的地址是用时序逻辑去读,指向当前正在读的地址用组合逻辑去读。)

img

当fifo没有写满并且写使能拉高时,写指针加一,当fifo没有读空并且读使能拉高时,读指针加一。由于fifo的读写地址变化是循环的,即fifo的写地址写到存储的最大地址时,写地址会回到0地址处再次按地址递增的顺序进行写操作,同理当fifo的读地址读到存储的最大地址时,读地址会回到0地址处再次按地址递增的顺序进行读操作。所以在对读写指针进行加一操作时还需要考虑到指针会回到0地址这一问题。

当然有可能在fifo既没有写满也没有读空的情况下,写使能和读使能同时拉高,这是将写指针和读指针同时加一,那会不会同时读写会对数据产生影响呢?是不会的,出现问题的情况是对同一地址进行读写,但当同时读写对同一个数据地址进行操作时,这时意味着fifo要么已经满要么已经空,这时对fifo进行读或者进行写都是违法的,所以写fifo逻辑部分要避免在fifo空的时候进行读操作,在fifo满的时候对fifo进行写操作。但上面提到的是读写fifo逻辑部分需要考虑的问题,这里考虑的是fifo内部的问题,在前面已经提到,同时读写是在fifo既没有写满又没有读空的条件下进行的,所以对于数据的读写没有影响。

空满检测

对于空满检测,前面我们提过,当读写指针地址相同时,fifo可能已经写满也可能已经读空,但是由于地址会在fifo最大深度处回到0地址,所以直接将读写地址进行比较来判断fifo的空满状态好像不是那么可行。

可以通过在fifo内部设立一个计数器用来计数fifo内的数据量,当fifo没有写满且写使能拉高时(或者写指针加一时),计数器加一,当fifo没有读空且读使能拉高时(或者读指针加一时),计数器减一,当fifo既没有读空有没有写满,且读写使能同时拉高有效时,这是的计数器不增也不减(由于我们讨论的时同步fifo,读写都在同一个时钟下,在相同时间内,如果读写使能同时有效,那么读的数据量和写的数据量相等,所以计数器不增也不减)。

如果fifo内部的计数器计数为0,表示fifo内没有数据,则空信号拉高,当fifo内部的计数器计数等于fifo的最大深度,表示fifo已经写满,则满信号拉高。

当然除了在fifo内部设立一个计数器,我们也可以采用在读写地址前增加一位的策略,如果fifo的深度为16,则地址需要4位二进制来表示,那么我们在表示fifo地址时用5位二进制数来表示。

img

当读写指针完全相同时,表明fifo已经读空了

img

当有数据进入时,写指针继续增大,当写指针为15时,继续增大来到了0地址处,这时第五位置为1,继续增大,当读指针与写指针低四位相同,最高为相反时,表示fifo已经写满。(最高为可以理解为写指针将读指针套圈的标志位,当最高位不同时,代表着写指针已经将读指针套圈,当低位完全相同时,表示写指针套圈后追上读指针,这时fifo已经写满)

我们也可以通过读写指针前后关系判断FIFO的空满,如果读指针落后于写指针一个地址,当读指针加一时,这时FIFO为空,当写指针落后于读指针一个地址时,当写指针加一时,这时FIFO为满。

读写数据

读数据可以通过组合逻辑根据读指针读取fifo中的数据,也可以根据时序逻辑根据读指针读取数据,采用时序逻辑读时序可能会错后一个时钟。

同步FIFO的verilog实现

采用计数器进行空满检测

sync_fifo_1.v

`timescale 1ns / 1ps

module sync_fifo_1#(
    parameter
        DATA_WIDTH = 8,
        FIFO_DEPTH = 8,
        AFULL_DEPTH = 7,
        AEMPTY_DEPTH = 1,
        ADDR_WIDTH = 3,
        RDATA_MODE = 0
)
(
    input   clk,
    input   rst_n,
    
    input   wr_en,
    input [DATA_WIDTH-1:0]  wr_data,
    output  full,
    output almost_full,
    
    input   rd_en,
    output reg [DATA_WIDTH-1:0] rd_data,
    output  empty,
    output  almost_empty,
    
    output reg overflow,
    output reg underflow
    );
    
 reg [ADDR_WIDTH-1:0] wr_ptr;
 reg [ADDR_WIDTH-1:0] rd_ptr;
 reg [ADDR_WIDTH:0] fifo_cnt;
 reg [DATA_WIDTH-1:0] buf_mem[0:FIFO_DEPTH-1];
 
 integer II;
 
 //- - - - -fifo_cnt- - - - - - - 
 always@(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            fifo_cnt <= {(ADDR_WIDTH+1){1'b0}};
        else
            begin
                if(wr_en && ~full && rd_en && ~empty)
                    fifo_cnt <= fifo_cnt;
                else if(wr_en && ~full)
                    fifo_cnt <= fifo_cnt + 1'b1;
                else if(rd_en && ~empty)
                    fifo_cnt <= fifo_cnt - 1'b1;
            end
    end   
 
//- - - - - -wr_ptr- - - - - - - - -
always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            wr_ptr <= {(ADDR_WIDTH+1){1'b0}};
         else 
            begin
                if(wr_ptr == FIFO_DEPTH-1)//注意这里,当写到fifo地址的最大值时需要从0开始写
                    wr_ptr <= {(ADDR_WIDTH+1){1'b0}};
                 else if(wr_en && ~full)
                    wr_ptr <= wr_ptr + 1'b1;
            end
    end 
    
//- - - - - -rd_ptr- - - - - - - - - - -
always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            rd_ptr <= {(ADDR_WIDTH+1){1'b0}};
        else
            begin
                if(rd_ptr == FIFO_DEPTH-1)
                    rd_ptr <= {(ADDR_WIDTH+1){1'b0}};
                else if(rd_en && ~empty)
                    rd_ptr <= rd_ptr + 1'b1;
            end
    end    
  
//- - - - - - buf_mem- - - - - - - - - -  
always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            for(II=0;II<FIFO_DEPTH;II=II+1)//初始化
                buf_mem[II]<={(DATA_WIDTH){1'b0}};
        else if(wr_en && ~full)
            buf_mem[wr_ptr] <= wr_data;
    end
    
//- - - - - - - rd_data- - - - - - - - -
generate
    if(RDATA_MODE == 1'b0)
        begin
            always @(*)
                rd_data = buf_mem[rd_ptr];
        end
    else
        begin
            always @(posedge clk or negedge rst_n)
                begin
                    if(!rst_n)
                        rd_data <= {DATA_WIDTH{1'b0}};
                     else
                        rd_data <= buf_mem[rd_ptr];
                end
        end
endgenerate

//- - - - - - - - overflow,underflow- - - - - - -
always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            begin
                overflow <= 1'b0;
                underflow <= 1'b0;
            end
        else if(wr_en && full)
            begin
                overflow <= 1'b1;
            end
        else if(rd_en && empty)
            begin
                underflow <= 1'b1;
            end
    end

assign full = (fifo_cnt == FIFO_DEPTH) ? 1'b1:1'b0;
assign empty = (fifo_cnt == {(ADDR_WIDTH+1){1'b0}}) ? 1'b1:1'b0;
assign almost_full = (fifo_cnt >= AFULL_DEPTH) ? 1'b1:1'b0;
assign almost_empty = (fifo_cnt <= AEMPTY_DEPTH) ? 1'b1:1'b0;

endmodule

testbench

sync_fifo_test_1.v

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2022/03/11 21:59:47
// Design Name: 
// Module Name: sync_fifo_test_1
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module sync_fifo_test_1(

    );
parameter DATA_WIDTH = 8;
parameter FIFO_DEPTH = 8;
parameter AFULL_DEPTH = FIFO_DEPTH -1 ;
parameter AEMPTY_DEPTH = 1;
parameter ADDR_WIDTH = 3;
parameter RDATA_MODE = 0;


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 full;
wire almost_full;
wire empty;
wire almost_empty;
wire overflow;
wire underflow;

integer II;

sync_fifo_1
#(
    .DATA_WIDTH(DATA_WIDTH),
    .FIFO_DEPTH(FIFO_DEPTH),
    .ADDR_WIDTH(ADDR_WIDTH),
    .RDATA_MODE(RDATA_MODE)
)    
inst_sync_fio(
    .clk(clk),
    .rst_n(rst_n),
    .wr_en(wr_en),
    .wr_data(wr_data),
    .rd_en(rd_en),
    .rd_data(rd_data),
    .full(full),
    .almost_full(almost_full),
    .empty(empty),
    .almost_empty(almost_empty),
    .overflow(overflow),
    .underflow(underflow)
);    

initial
    begin
        #0;
        clk = 0;
        rst_n = 0;
        #10;
        rst_n = 1;
    end
always #5 clk = ~clk;

initial
    begin
        #0;
        wr_en = 0;
        wr_data = 0;
        rd_en = 0;
    end

initial
    begin
        #20;
        send_wr;
    end
initial
    begin
        #100;
        send_rd;
        #1000;
        $finish;
    end

task send_wr;
    begin
        for(II=0;II<8;II=II+1)
            begin
                @(posedge clk)           
                    begin
                        wr_en <= 1'b1;
                        wr_data <= II+1;
                    end
            end
        @(posedge clk)
            begin
                wr_en <= 1'b0;
                wr_data <= 8'h0;
            end
            repeat(10) @(posedge clk);
    end
endtask

task send_rd;
    begin
        for(II=0;II<8;II=II+1)
            begin
                @(posedge clk)
                    begin
                        rd_en <= 1'b1;
                    end
            end
        @(posedge clk)
            begin
                rd_en <= 1'b0;
            end
    end
endtask


endmodule

采用在最高位相同与否检测空满

sync_fifo_2.v

module sync_fifo_2#(
	parameter DEPTH = 16,		//FIFO深度
	parameter WIDTH = 8,		//FIFO内数据位宽
	parameter P_WIDTH = 5		//读写指针位宽
)
(
	 input rst_n
	,input clk
	,input [WIDTH-1:0] Data_Write  //写入的数据值
	,input Write_Sig				//写入数据使能
	,input Read_Sig					//读取数据使能
	,output wire [WIDTH-1:0] Data_Read  //读取的数据值
	,output wire Full_Sig		//FIFO为空信号
	,output wire Empty_Sig		//FIFO为满信号
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
reg [P_WIDTH-1:0] Read_pointer;
reg [P_WIDTH-1:0] Write_pointer;
reg [WIDTH-1:0] rData_Read;



assign Full_Sig = ((Read_pointer[P_WIDTH-2:0]==Write_pointer[P_WIDTH-2:0])
		&&(Read_pointer[P_WIDTH-1] != Write_pointer[P_WIDTH-1]))? 1'd1:1'd0;
assign Empty_Sig = (Read_pointer[P_WIDTH-1:0]==Write_pointer[P_WIDTH-1:0])? 1'd1:1'd0;

always @(negedge rst_n or posedge clk) begin
	if (!rst_n)
		begin	
			rData_Read <= 0;
			Read_pointer <= 0;
			Write_pointer <= 0;
		end
	else begin
		if (~Full_Sig && ~Empty_Sig && Read_Sig && Write_Sig ) //非空非满时同时读写
			begin
				rData_Read <= RAM_MEM[Read_pointer[P_WIDTH-2:0]];
				RAM_MEM[Write_pointer[P_WIDTH-2:0]] <= Data_Write;
				Read_pointer <= Read_pointer + 1'd1;
				Write_pointer <= Write_pointer + 1'd1;
			end // 
		else if (~Empty_Sig && Read_Sig )	//非空时读数据
			begin
				rData_Read <= RAM_MEM[Read_pointer[P_WIDTH-2:0]];
				Read_pointer <= Read_pointer + 1'd1;
			end
		else if (~Full_Sig && Write_Sig)	//非满时写数据
			begin
				RAM_MEM[Write_pointer[P_WIDTH-2:0]] <= Data_Write;
				Write_pointer <= Write_pointer + 1'd1;
			end
	end // else
end // always

assign Data_Read = rData_Read;

endmodule 

testbench

sync_fifo_test_2.v

module sync_fifo_test_2(

    );
parameter DEPTH = 16;
    parameter WIDTH = 8;
    parameter P_WIDTH = 5;
    
    reg clk;
    reg rst_n;
    reg Write_Sig;
    reg Read_Sig;
    reg [7:0] Data_Write;
    wire [7:0] Data_Read;
    integer i;
    
    wire Full_Sig;
    wire Empty_Sig;
    
    sync_fifo_2 #(
            .DEPTH(DEPTH),
            .WIDTH(WIDTH),
            .P_WIDTH(P_WIDTH)
        ) inst_Syn_FIFO (
            .rst_n      (rst_n),
            .clk        (clk),
            .Data_Write (Data_Write),
            .Write_Sig  (Write_Sig),
            .Read_Sig   (Read_Sig),
            .Data_Read  (Data_Read),
            .Full_Sig   (Full_Sig),
            .Empty_Sig  (Empty_Sig)
        );
    
    always #5
        clk = ~clk;
    
    initial begin
        clk = 1'b0;
        rst_n = 1'b1;
        @(posedge clk)
        #5 rst_n = 1'b0;
        @(posedge clk)
        #5 rst_n = 1'b1;
    end // initial
    
    
    initial begin    
        i = 4'd0;
        Write_Sig = 1'd0;
        Read_Sig = 1'd0;
        Data_Write = 8'd0;
    
        repeat(5) @(posedge clk);
    
        for(i=0; i<19; i=i+1) 
            Write_Data(1'd1, 12*(i+1));
        for(i=0; i<19; i=i+1)
            Read_Data(1'd1);
    
        for(i=0; i<19; i=i+1) 
            Write_Data(1'd1, 12*(i+1));
        for(i=0; i<8; i=i+1)
            Read_Data(1'd1);
        @(posedge clk)        
            Read_Sig <= 1'b0;
        for(i=0; i<19; i=i+1) 
            Write_Data(1'd1, 12*(i+1));
    
        for(i=0; i<19; i=i+1)
            Read_Data(1'd1);
    
        @(posedge clk)
        Write_Sig <= 1'b1;
        Data_Write <= 8'd3;
        @(posedge clk)
        Write_Sig <= 1'b1;
        Data_Write <= 8'd5;    
    
        @(posedge clk)
        Write_Sig <= 1'b1;
        Data_Write <= 8'd7;    
        Read_Sig <= 1'b1;
    
        @(posedge clk)
        Write_Sig <= 1'b0;
        Data_Write <= 8'd0;    
        Read_Sig <= 1'b1;
    
        @(posedge clk)        
        Read_Sig <= 1'b1;
    
        @(posedge clk)        
        Read_Sig <= 1'b1;
    
        @(posedge clk)        
        Read_Sig <= 1'b0;
    
        #30 $stop;
    end // initial
    
    task Write_Data(input reg Write_en,input reg [7:0] Data_in);
        begin
            @(posedge clk)
            if(Full_Sig) begin 
                Write_Sig <= 1'b0;
                Data_Write <= 8'd0;
            end
            else begin 
                Write_Sig <= Write_en;
                Data_Write <= Data_in;
            end
        end
    endtask
    
    task Read_Data(input reg Read_en);
        begin
            @(posedge clk)
            if(Empty_Sig) 
                Read_Sig <= 1'b0;
            else Read_Sig <= Read_en;
        end
    endtask
    
    endmodule

数据丢失问题

img

在运行上面最后一个例子的时候会发现如果根据FIFO的满信号来中止数据传输,就会存在数据丢失的问题,上面在传输192的时候就已经写满了,这时候写满信号拉高,但是对于数据输入端来说,这时又传输了一个204,只有下一个时钟,发送端才采样到写满信号,才终止输出的传输,所以传输的204就丢失了。

对于这种情况,数据发送端可以采用将满信号(almost_flag)来控制数据的传输。

贴一个解决办法

sync_fifo.v

module sync_fifo_z
#(
    parameter
        DATA_WIDTH = 8,
        FIFO_DEPTH = 16,
        ALMOST_FULL_GAP = 15,
        ALMOST_EMPTY_GAP = 1,
        ADDR_WIDTH = $clog2(FIFO_DEPTH)
)
(
    input clk,
    input rst_n,
    input wr_en,
    input rd_en,
    input [DATA_WIDTH-1:0] wr_data,
    
    output full_flag,
    output empty_flag,
    output almost_full_flag,
    output almost_empty_flag,
    output [DATA_WIDTH-1:0] rd_data
    );
    
    reg [ADDR_WIDTH-1:0] wr_ptr;
    reg [ADDR_WIDTH-1:0] rd_ptr;
    reg [ADDR_WIDTH:0] cnt;
    reg [DATA_WIDTH-1:0] mem [FIFO_DEPTH-1:0];
    
    // cnt
    always @(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
                begin
                    cnt <= {(ADDR_WIDTH+1){1'b0}};
                end
            else
                begin
                    if(wr_en && !full_flag && rd_en && !empty_flag)
                        begin
                            cnt <= cnt;
                        end
                    else if(wr_en && !full_flag)
                        begin
                            cnt <= cnt + 1'b1;
                        end
                    else if(rd_en && !empty_flag)
                        begin
                            cnt <= cnt - 1'b1;
                        end
                    else
                        begin
                            cnt <= cnt;
                        end 
                end
        end
        
   //wr_ptr rd_ptr
   always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            begin
                wr_ptr <= {ADDR_WIDTH{1'b0}};
            end
        else
            begin
                if(wr_en && !full_flag)
                    begin
                        if(wr_ptr == FIFO_DEPTH-1)
                            wr_ptr <= {ADDR_WIDTH{1'b0}};
                        else
                            wr_ptr <= wr_ptr + 1'b1;
                    end
                else
                    wr_ptr <= wr_ptr; 
            end
    end     
    
   always @(posedge clk or negedge rst_n)
     begin
         if(!rst_n)
             begin
                 rd_ptr <= {ADDR_WIDTH{1'b0}};
             end
         else
             begin
                 if(rd_en && !empty_flag)
                     begin
                         if(rd_ptr == FIFO_DEPTH-1)
                             rd_ptr <= {ADDR_WIDTH{1'b0}};
                         else
                             rd_ptr <= rd_ptr + 1'b1;
                     end
                 else
                     rd_ptr <= rd_ptr; 
             end
     end 
    
  //read write
  integer i;
  always @(posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            begin
                for(i=0;i<FIFO_DEPTH;i=i+1)
                    begin
                        mem[i] = {DATA_WIDTH{1'b0}};
                    end
            end
        else
            begin
                if(wr_en && !full_flag)
                    mem[wr_ptr] <= wr_data;
            end
    end   
    
   assign rd_data = mem[rd_ptr];
    
  //full empty
  assign full_flag = (cnt == FIFO_DEPTH) ? 1'b1:1'b0;
  assign empty_flag = (cnt == {(ADDR_WIDTH+1){1'b0}})?1'b1:1'b0;
  assign almost_full_flag = (cnt >=ALMOST_FULL_GAP) ? 1'b1:1'b0;
  assign almost_empty_flag = (cnt <=ALMOST_EMPTY_GAP)?1'b1:1'b0;
    
endmodule

testbench

module sync_fifo_test_z(

    );
    parameter   
        DATA_WIDTH = 8,
        FIFO_DEPTH = 16,
        ALMOST_FULL_GAP = 15,
        ALMOST_EMPTY_GAP = 1,
        ADDR_WIDTH = $clog2(FIFO_DEPTH);
        
    reg wr_en;
    reg rd_en;
    reg [DATA_WIDTH-1:0] wr_data;
    wire [DATA_WIDTH-1:0] rd_data;
    wire full_flag;
    wire empty_flag;
    wire almost_full_flag;
    wire almost_empty_flag;
    
    reg clk;
    reg rst_n;
    
    sync_fifo_z 
    #(
        .DATA_WIDTH(DATA_WIDTH),
        .FIFO_DEPTH(FIFO_DEPTH)
        )
    inst1(
        .clk(clk),
        .rst_n(rst_n),
        .wr_en(wr_en),
        .rd_en(rd_en),
        .wr_data(wr_data),
        .full_flag(full_flag),
        .empty_flag(empty_flag),
        .almost_full_flag(almost_full_flag),
        .almost_empty_flag(almost_empty_flag),
        .rd_data(rd_data)
    );
    
    
    
    initial
        begin
            clk =0;
            forever #5 clk = ~clk;
        end 
    initial
        begin
            rst_n = 1'b1;
            #20 rst_n = 1'b0;
            #10 rst_n = 1'b1;
        end
    always@(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
                begin
                    wr_en <= 1'b0;
                    wr_data <= {DATA_WIDTH{1'b0}};
                end
            else
                begin
                    if(!almost_full_flag )
                        begin
                            wr_en <= 1'b1;
                            wr_data <= wr_data + 1'b1;
                        end
                    else
                        begin
                            wr_en <= 1'b0;
                            wr_data <= wr_data;
                        end
                end
        end 
        
    always @(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
                rd_en <= 1'b1;
            else
                begin
                    if(almost_full_flag && !almost_empty_flag)
                        rd_en <= 1'b1;
                    else if(almost_empty_flag)
                        rd_en <= 1'b0;
                    else
                        rd_en <= rd_en;
                end
        end           
endmodule

img

从图上可以看出,采用almost_full标志位可以解决数据丢失的位置,数据从17开始没有丢失。


小富哥(一个很牛逼的人)代码

module sync_fifo(
    input wire clk,
    input wire rst_n,
    input wire [7:0] data_in,
    input wire wr_en,
    input wire rd_en,
    output wire full,
    output wire empty,
    output reg [7:0] data_out
);

reg [7:0] fifo_mem [15:0];
wire [3:0] wr_addr;
wire [3:0] rd_addr;
reg [4:0] wr_addr_ptr;//写指针
reg [4:0] rd_addr_ptr;//读指针

assign wr_addr = wr_addr_ptr[3:0];
assign rd_addr = rd_addr_ptr[3:0];

always @(posedge clk)
begin
    if(wr_en && !full)
        fifo_mem[wr_addr] <= data_in;
    else
        fifo_mem[wr_addr] <= fifo_mem[wr_addr];
end

always @(posedge clk)
begin
    if(rd_en && !empty)
        data_out <= fifo_mem[rd_addr];
    else
        data_out <= 8'd0;
end


always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
    begin
        wr_addr_ptr <= 5'd0;
    end
    else
    begin
        if(wr_en && !full)
           wr_addr_ptr <= wr_addr_ptr + 1'b1;
        else
           wr_addr_ptr <= wr_addr_ptr;
    end
end


always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
    begin
        rd_addr_ptr <= 5'd0;
    end
    else
    begin
        if(rd_en && !empty)
           rd_addr_ptr <= rd_addr_ptr + 1'b1;
        else
           rd_addr_ptr <= rd_addr_ptr;
    end
end


assign full = ({!wr_addr_ptr[4],wr_addr_ptr[3:0]}==rd_addr_ptr) ? 1'b1 : 1'b0;
assign empty = (wr_addr_ptr == rd_addr_ptr) ? 1'b1 : 1'b0;


endmodule

testbench.v


module sync_fifo_sim();

reg clk;
reg rst_n;
reg [7:0] data_in;
reg wr_en;
reg rd_en;
wire full;
wire empty;
wire [7:0] data_out;

sync_fifo sync_fifo(
    .clk(clk),
    .rst_n(rst_n),
    .data_in(data_in),
    .wr_en(wr_en),
    .rd_en(rd_en),
    .full(full),
    .empty(empty),
    .data_out(data_out)
);

initial begin
    clk = 1'b0;
          wr_en = 1'b0;
          rd_en = 1'b0;
          rst_n = 1'b1;
          #20 rst_n = 1'b0;
          #20 rst_n = 1'b1;
          data_in = 8'd1;
          #20 data_in = 8'd2;
          #40 wr_en = 1'b1;
          #20 data_in = 8'd3;
          #20 data_in = 8'd2;
          #20 data_in = 8'd2;
          #260 wr_en = 1'b0;
          #10 rd_en = 1'b1;
end

always #10 clk = ~clk;
endmodule


参考

同步FIFO学习 - IC新手 - 博客园 (cnblogs.com)
(13条消息) FPGA基础知识极简教程(3)从FIFO设计讲起之同步FIFO篇_Reborn Lee-CSDN博客_fifo缓冲区作用
同步FIFO的实现(从verilog代码到波形) - 知乎 (zhihu.com)
手写同步FIFO - 知乎 (zhihu.com)
Register based FIFO in VHDL (nandland.com)//vhdl版本
同步FIFO笔记 - 知乎 (zhihu.com)

posted @ 2024-07-06 18:40  孤独野猪骑士  阅读(150)  评论(0编辑  收藏  举报