同步fifo的三种实现方法

同步fifo

框图

image-20220511212922479

下图为深度为4的fifo写示意图。当wr_ptr = rd_ptr时,要么fifo为空(最开始状态),要么fifo为满(最后状态)

tmpA1E0

判断空满方法一

使用计数器(counter)记录读写操作,当(wr_en & !rd_end) == 1时,只写不读,计数器加1;当(!wr_en & rd_end) == 1时,只读不写,计数器加1;其他情况下,计数器的值不变。

判断fifo_full

当 counter 的值等于fifo的深度时,fifo为满。

判断fifo_empty

当 counter的值等于0时,fifo为空。

Verilog实现

module syn_fifo_1#(parameter WIDTH = 8, DEPTH = 8)(
  input clk,
  input rst_n,
  input wr_en,
  input [WIDTH-1 : 0] wr_data,
  input rd_en,
  output reg [WIDTH-1 : 0] rd_data,
  output fifo_full,
  output fifo_empty
);
  
  reg [WIDTH-1 : 0] buffer [0 : DEPTH -1];

  reg [$clog2(WIDTH)-1 : 0] rd_ptr;
  reg [$clog2(WIDTH)-1 : 0] wr_ptr;
  reg [$clog2(WIDTH) : 0] counter;

  //keep track of counter 
  always @(posedge clk) begin
    if(!rst_n)
      counter <= 0;
    else begin
      if(wr_en && !rd_en)
        counter <= counter + 1;
      else begin
        if(rd_en && !wr_en)
          counter <= counter - 1; 
        else 
          counter <= counter;
      end
    end
  end

  //keep track of wr_ptr
  always @(posedge clk) begin
    if(!rst_n)
      wr_ptr <= 0;
    else begin
      if(wr_en && !fifo_full) begin
        if(wr_ptr == DEPTH-1)
          wr_ptr <= 0;
        else
          wr_ptr <= wr_ptr + 1;
      end
      else 
        wr_ptr <= wr_ptr;
    end
  end

  //keep track of rd_ptr
  always @(posedge clk) begin
    if(!rst_n)
      rd_ptr <= 0;
    else begin
      if(rd_en && !fifo_empty) begin
        if(rd_ptr == DEPTH-1)
          rd_ptr <= 0;
        else
          rd_ptr <= rd_ptr + 1;
      end
      else
        rd_ptr <= rd_ptr;
    end
  end

  // write data into buffer when wr_en asserted at the posedege of clk
  always @(posedge clk) begin
    if(wr_en)
      buffer[wr_ptr] <= wr_data;
  end
  //read data when rd_en asserted
  always @(posedge clk) begin
    if(rd_en)
      rd_data <= buffer[rd_ptr];
    else
      rd_data <= 'hz;
  end
  //assign rd_data = rd_en? buffer[rd_ptr]:'hz;

  assign fifo_full = (counter == DEPTH)? 1 : 0;

  assign fifo_empty = (counter == 0)?1:0;

endmodule


TB实现

module tb();

	parameter WIDTH = 8;
  parameter DEPTH = 8;
  
    reg clk;
    reg rst_n;
    
    //write port
    reg wr_en;
    reg [WIDTH - 1 : 0] wr_data;
    wire fifo_full;
    //read port
    reg rd_en;
    wire [WIDTH - 1 : 0] rd_data;
    wire fifo_empty;


    initial begin
    	clk = 0;
    	forever begin
    		#5 clk = ~clk;
    	end
    end

    initial begin
    	rst_n = 1;
    	wr_en = 0;
    	rd_en = 0;

    	@(negedge clk) rst_n = 0;
    	@(negedge clk) rst_n = 1;
      //write 4 data to fifo
    	@(negedge clk) wr_en = 1;
    	wr_data = $random;

    	repeat(3) begin
    		@(negedge clk)
    		wr_data = $random;	
    	end
    	
    	@(negedge clk)
    	wr_en = 0;
    	rd_en = 1;
  //read 3 data from fifo
    	repeat(3) begin
    		@(negedge clk);	
    	end
  //write 7 data to fifo
    	@(negedge clk)
    	rd_en = 0;
    	wr_en = 1;
    	wr_data = $random;

    	repeat(7) begin   		
    		@(negedge clk)
    		wr_data = $random;
    	end
    	#20 $finish;
    end
  initial begin
    $fsdbDumpvars();
  end

	syn_fifo_1 #(.WIDTH(WIDTH),.DEPTH(DEPTH))
		DUT(.clk    (clk),
			.rst_n    (rst_n),
			.wr_en    (wr_en),
			.wr_data  (wr_data),
			.fifo_full  (fifo_full),
			.rd_en    (rd_en),
			.rd_data  (rd_data),
			.fifo_empty (fifo_empty)
		);

endmodule

波形

tmpA533

判断空满方法二

判断fifo_full

当为写操作,且写指针下一个时钟指向的地址等于目前的读指针时,fifo_full拉高

if(wr_en &&  (rd_ptr == wr_ptr +1))
	fifo_full = 1

判断fifo_empty

当为读操作,且读指针下一个时钟指向的地址等于目前的写指针时,fifo_full拉高

if(rd_en &&  (wr_ptr == rd_ptr +1))
    fifo_empty = 1

Verilog实现

module syn_fifo_2#(parameter WIDTH = 8, DEPTH = 8)(
  input clk,
  input rst_n,
  input wr_en,
  input [WIDTH-1 : 0] wr_data,
  input rd_en,
  output reg [WIDTH-1 : 0] rd_data,
  output reg fifo_full,
  output reg fifo_empty
);
  
  reg [WIDTH-1 : 0] buffer [0 : DEPTH -1];

  reg [$clog2(WIDTH)-1 : 0] rd_ptr;
  reg [$clog2(WIDTH)-1 : 0] wr_ptr;

  //keep track of wr_ptr
  always @(posedge clk) begin
    if(!rst_n)
      wr_ptr <= 0;
    else begin
      if(wr_en && !fifo_full) begin
        if(wr_ptr == DEPTH-1)
          wr_ptr <= 0;
        else
          wr_ptr <= wr_ptr + 1;
      end
      else 
        wr_ptr <= wr_ptr;
    end
  end

  //keep track of rd_ptr
  always @(posedge clk) begin
    if(!rst_n)
      rd_ptr <= 0;
    else begin
      if(rd_en && !fifo_empty) begin
        if(rd_ptr == DEPTH-1)
          rd_ptr <= 0;
        else
          rd_ptr <= rd_ptr + 1;
      end
      else
        rd_ptr <= rd_ptr;
    end
  end

  // write data into buffer when wr_en asserted at the posedege of clk
  always @(posedge clk) begin
    if(wr_en)
      buffer[wr_ptr] <= wr_data;
  end
  //read data when rd_en asserted
  always @(posedge clk) begin
    if(rd_en)
      rd_data <= buffer[rd_ptr];
    else
      rd_data <= 'hz;
  end
  //assign rd_data = rd_en? buffer[rd_ptr]:'hz;

  //assign fifo_full = (wr_en && (rd_ptr == wr_ptr +1))? 1 : 0;
  always @(posedge clk) begin
    if(!rst_n)
      fifo_full <= 0;
    else begin
      if(wr_en && (rd_ptr == wr_ptr +1))
        fifo_full <= 1;
      else
        fifo_full <= 0;
    end
  end

  //assign fifo_empty = (rd_en && (wr_ptr == rd_ptr +1))?1:0;
  always @(posedge clk) begin
    if(!rst_n)
      fifo_empty <= 0;
    else
      if(rd_en && (wr_ptr == rd_ptr +1))
        fifo_empty<= 1;
      else
        fifo_empty <= 0;
  end

endmodule

TB实现

module tb();

	parameter WIDTH = 8;
  parameter DEPTH = 8;
  
    reg clk;
    reg rst_n;
    
    //write port
    reg wr_en;
    reg [WIDTH - 1 : 0] wr_data;
    wire fifo_full;
    //read port
    reg rd_en;
    wire [WIDTH - 1 : 0] rd_data;
    wire fifo_empty;


    initial begin
    	clk = 0;
    	forever begin
    		#5 clk = ~clk;
    	end
    end

    initial begin
    	rst_n = 1;
    	wr_en = 0;
    	rd_en = 0;

    	@(negedge clk) rst_n = 0;
    	@(negedge clk) rst_n = 1;
      //write 4 data to fifo
    	@(negedge clk) wr_en = 1;
    	wr_data = $random;

    	repeat(3) begin
    		@(negedge clk)
    		wr_data = $random;	
    	end
    	
    	@(negedge clk)
    	wr_en = 0;
    	rd_en = 1;
  //read 3 data from fifo
    	repeat(3) begin
    		@(negedge clk);	
    	end
  //write 7 data to fifo
    	@(negedge clk)
    	rd_en = 0;
    	wr_en = 1;
    	wr_data = $random;

    	repeat(7) begin   		
    		@(negedge clk)
    		wr_data = $random;
    	end
    	#20 $finish;
    end
  initial begin
    $fsdbDumpvars();
  end

	syn_fifo_2 #(.WIDTH(WIDTH),.DEPTH(DEPTH))
		DUT(.clk    (clk),
			.rst_n    (rst_n),
			.wr_en    (wr_en),
			.wr_data  (wr_data),
			.fifo_full  (fifo_full),
			.rd_en    (rd_en),
			.rd_data  (rd_data),
			.fifo_empty (fifo_empty)
		);

endmodule

波形

tmp67F

判断空满方法三

拓展一位读写指针的位宽

判断fifo_full

当读写指针的最高位不同,其余为相同时,fifo_full为高。

判断fifo_empty

当读写指针的所有位都相同时,fifo_empty为高。

Verilog实现

module syn_fifo_3#(parameter WIDTH = 8, DEPTH = 8)(
  input clk,
  input rst_n,
  input wr_en,
  input [WIDTH-1 : 0] wr_data,
  input rd_en,
  output reg [WIDTH-1 : 0] rd_data,
  output fifo_full,
  output fifo_empty
);
  
  reg [WIDTH-1 : 0] buffer [0 : DEPTH -1];
  parameter P_WIDTH = $clog2(WIDTH); 

  reg [P_WIDTH : 0] rd_ptr;
  reg [P_WIDTH : 0] wr_ptr;

  //keep track of wr_ptr
  always @(posedge clk) begin
    if(!rst_n)
      wr_ptr <= 0;
    else begin
      if(wr_en && !fifo_full) begin
          wr_ptr <= wr_ptr + 1;
      end
      else 
        wr_ptr <= wr_ptr;
    end
  end

  //keep track of rd_ptr
  always @(posedge clk) begin
    if(!rst_n)
      rd_ptr <= 0;
    else begin
      if(rd_en && !fifo_empty) begin
          rd_ptr <= rd_ptr + 1;
      end
      else
        rd_ptr <= rd_ptr;
    end
  end

  // write data into buffer when wr_en asserted at the posedege of clk
  always @(posedge clk) begin
    if(wr_en)
      buffer[wr_ptr] <= wr_data;
  end
  //read data when rd_en asserted
  always @(posedge clk) begin
    if(rd_en)
      rd_data <= buffer[rd_ptr];
    else
      rd_data <= 'hz;
  end
  //assign rd_data = rd_en? buffer[rd_ptr]:'hz;

  assign fifo_full = ((rd_ptr[P_WIDTH] ^ wr_ptr[P_WIDTH]) && (rd_ptr[P_WIDTH-1 :0 ] == wr_ptr[P_WIDTH-1 : 0]))? 1 : 0;

  assign fifo_empty = (rd_ptr == wr_ptr)? 1 : 0;

endmodule

TB实现

module tb();

  	parameter WIDTH = 8;
  	parameter DEPTH = 8;
  
    reg clk;
    reg rst_n;
    
    //write port
    reg wr_en;
    reg [WIDTH - 1 : 0] wr_data;
    wire fifo_full;
    //read port
    reg rd_en;
    wire [WIDTH - 1 : 0] rd_data;
    wire fifo_empty;

    initial begin
    	clk = 0;
    	forever begin
    		#5 clk = ~clk;
    	end
    end

    initial begin
    	rst_n = 1;
    	wr_en = 0;
    	rd_en = 0;

    	@(negedge clk) rst_n = 0;
    	@(negedge clk) rst_n = 1;
      //write 4 data to fifo
    	@(negedge clk) wr_en = 1;
    	wr_data = $random;

    	repeat(3) begin
    		@(negedge clk)
    		wr_data = $random;	
    	end
    	
    	@(negedge clk)
    	wr_en = 0;
    	rd_en = 1;
  //read 3 data from fifo
    	repeat(3) begin
    		@(negedge clk);	
    	end
  //write 7 data to fifo
    	@(negedge clk)
    	rd_en = 0;
    	wr_en = 1;
    	wr_data = $random;

    	repeat(7) begin   		
    		@(negedge clk)
    		wr_data = $random;
    	end
    	#20 $finish;
    end
 	initial begin
   	  $fsdbDumpvars();
  	end

	syn_fifo_3 #(.WIDTH(WIDTH),.DEPTH(DEPTH))
		DUT(.clk    (clk),
			.rst_n    (rst_n),
			.wr_en    (wr_en),
			.wr_data  (wr_data),
			.fifo_full  (fifo_full),
			.rd_en    (rd_en),
			.rd_data  (rd_data),
			.fifo_empty (fifo_empty)
		);

endmodule

波形

tmp9D76

参考

【Verilog】同步FIFO原理及verilog实现(参数化)_子墨祭的博客-CSDN博客_同步fifo原理

FPGA基础知识极简教程(3)从FIFO设计讲起之同步FIFO篇 - 云+社区 - 腾讯云 (tencent.com)

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
FPGA 同步 FIFO 是一种用于在 FPGA 设备中实现数据缓冲和数据转移的组件。它由一个读取指针和一个写入指针组成,可以实现读写操作的同步和互斥。 使用 FPGA 同步 FIFO 的一个常见场景是在不同频率的数据传输之间进行数据缓冲和同步。当输入以不同频率产生数据时,为了保证数据的可靠传输和处理,可以使用同步 FIFO 来缓冲输入数据,并在输出端以相同或不同的频率读取数据。 FPGA 同步 FIFO实现可以采用 FPGA 内部的存储单元,如 Block RAM 或 Distributed RAM。写入操作将数据写入 FIFO 的写入指针所指向的位置,并将写入指针前移。读取操作将数据从 FIFO 的读取指针所指向的位置读取出来,并将读取指针前移。读写指针的移动是同步的,以保证数据的正确性和完整性。 FPGA 同步 FIFO 的大小通常取决于数据传输的需求和 FPGA 设备的资源限制。可以根据数据产生和处理的速度来确定 FIFO 的大小,并且需要根据需要调整读写指针的顺序和移动策略,以满足数据的传输需求。 尽管 FPGA 同步 FIFO 在数据传输中起到了重要的作用,但同时也会增加设计的复杂性和资源消耗。在使用 FPGA 同步 FIFO 时,需要注意处理数据的同步和互斥问题,以及避免出现数据丢失、溢出等异常情况。 总之,FPGA 同步 FIFO 是一种用于实现数据缓冲和转移的组件,在不同频率的数据传输中发挥了关键作用。它可以通过读写指针的同步移动来保证数据的可靠性和完整性,并且可根据需求和硬件资源进行灵活调整。但同时也需要注意处理同步和互斥问题,以确保数据的正确传输。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值