FPGA手撕系列(1)——同步FIFO

目录

一、设计原理

时钟同步:

内部指针:

数据存储:

空满状态检测:

读写控制:

二、架构框图

三、参考代码

四、仿真结果

五、总结


一、设计原理

        FIFO是一种先进先出的数据缓存器,与普通存储器的区别在于没有外部读写地址线,只能顺序写入和读取数据。同步FIFO(First In First Out,先进先出)指的是读时钟和写时钟为同一个时钟,即读写操作在同一时钟沿发生。

时钟同步:

  • 同步FIFO的核心在于读写操作都在同一个时钟信号的控制下进行。这意味着读使能、写使能、数据输入、数据输出以及满/空状态信号等都在时钟的同一沿(如上升沿或下降沿)发生变化。

内部指针:

  • 同步FIFO内部通常包含两个指针:读指针(Read Pointer)和写指针(Write Pointer)。读指针指向下一个要读取的数据位置,写指针指向下一个要写入的数据位置。
  • 读写指针在时钟的驱动下自动递增,以追踪数据的读写位置。当写指针追上读指针时(考虑到FIFO的循环特性),表示FIFO已满;当读指针追上写指针时,表示FIFO为空。

数据储存:

  • FIFO通常使用双口RAM(Random Access Memory)或类似的存储结构来存储数据。双口RAM允许在同一时间内进行读写操作,适合FIFO的应用场景。

空满状态检测:

  • 设计同步FIFO时,需要解决的一个重要问题是如何准确检测FIFO的空和满状态。这通常通过比较读写指针的位置来实现。为了避免读写指针在FIFO满或空时重叠导致的混淆,有时会在指针设计中加入额外的位(如格雷码中的MSB位)来区分这种情况。

读写控制:

  • 写操作:当写使能有效且FIFO未满时,数据被写入由写指针指示的位置,随后写指针递增。
  • 读操作:当读使能有效且FIFO非空时,数据从由读指针指示的位置读出,随后读指针递增。

二、架构框图

 简单同步fifo,仅供参考

三、参考代码

top

// -----------------------------------------------------------------------------
// Item:
// -----------------------------------------------------------------------------
// Author : xiaohui_fpga
// File   : syn_fifo.v
// Create : 2024-07-15 15:30:59
// Revise : 2024-07-15 19:40:00
// Instruction : 
// -----------------------------------------------------------------------------
`timescale 1ns / 1ps

module syn_fifo#(
	parameter DATA_WIDTH = 8 ,
	parameter DATA_DEPTH = 4
)(
	input                                clk     ,
	input                                reset   ,
	input                                wr_en   ,
	input       [DATA_WIDTH-1:0]         din     ,
	input                                rd_en   ,
	output  reg [DATA_WIDTH-1:0]         dout    ,
	output      [$clog2(DATA_DEPTH)-1:0] dcount  ,
	output                               full    ,
	output                               empty 
    );


reg [DATA_WIDTH-1:0] fifo_ram [DATA_DEPTH-1:0];
reg [DATA_WIDTH-1:0] wr_ptr;
reg [DATA_WIDTH-1:0] rd_ptr;
reg [$clog2(DATA_DEPTH)-1:0] count;

assign full   = count == DATA_DEPTH -1;
assign empty  = count == 0 ;
assign dcount = DATA_DEPTH - 1 - count;//还剩空间数量

//写操作
always @(posedge clk) begin
    if (wr_en && ~full) begin
        fifo_ram[wr_ptr] <= din;
    end
    else begin
    	fifo_ram[wr_ptr] <= fifo_ram[wr_ptr];
    end
end
//更新写地址 
always@(posedge clk)begin
    if(reset)begin
        wr_ptr <= 0;
    end
    else if(wr_en && ~full)begin
        wr_ptr <= wr_ptr + 1;
    end
    else begin
    	wr_ptr <= 0;
    end
end

//读操作
always @(posedge clk) begin
    if (rd_en && ~empty) begin
        dout <= fifo_ram[rd_ptr];
    end
    else begin
    	dout <= dout;
    end
end

//更新读地址 
always@(posedge clk)begin
    if(reset)begin
        rd_ptr <= 0;
    end
    else if(rd_en && ~empty)begin
        rd_ptr <= rd_ptr + 1;
    end
    else begin
    	rd_ptr <= rd_ptr;
    end
end

//空间计数
always @(posedge clk) begin
    if (reset) begin
        count <= 0;
    end
    else begin
        case({wr_en,rd_en})
        	2'b00:begin
        		count <= count;
        	end
        	2'b01:begin
        		if (count > 0) begin
        			count <= count -1;
        		end
        	end
        	2'b10:begin
        		if (count < DATA_DEPTH-1) begin
        			count <= count +1;
        		end
        	end
        	default:count <= count;
        endcase
    end
end

endmodule

tb_top 

// -----------------------------------------------------------------------------
// Item:
// -----------------------------------------------------------------------------
// Author : xiaohui_fpga
// File   : tb_top.v
// Create : 2024-07-15 16:54:03
// Revise : 2024-07-15 19:49:06
// Instruction : 
// -----------------------------------------------------------------------------
`timescale 1ns / 1ps

module tb_top();

	parameter DATA_WIDTH = 8;
	parameter DATA_DEPTH = 21;

	reg                          clk;
	reg                          reset;
	reg                          wr_en;
	reg         [DATA_WIDTH-1:0] din;
	reg                          rd_en;

	wire         [DATA_WIDTH-1:0] dout;
	wire [$clog2(DATA_DEPTH)-1:0] dcount;
	wire                          full;
	wire                          empty;

//clock
initial begin
		clk = 0;
		forever #(10) clk = ~clk;
end

//
initial begin
	reset <= 1'b1;
	wr_en <= 0;
	rd_en <= 0;
	din   <= 0;
	#40
	reset <= 1'b0;
	#35
	wr_en <= 1'b1;
	#215
	rd_en <= 1'b1;
	/*观察empty时,注释47、48行,反注释50、51行*/
	//#200 
	//rd_en <= 0;
	/*观察full时,注释50、51行,反注释47、48行*/
	//#200
	//wr_en <= 0;
end

always @(posedge clk) begin
    if (wr_en) begin
        din <= din + 1;
    end
end


syn_fifo #(
			.DATA_WIDTH(DATA_WIDTH),
			.DATA_DEPTH(DATA_DEPTH)
		) inst_syn_fifo (
			.clk    (clk),
			.reset  (reset),
			.wr_en  (wr_en),
			.din    (din),
			.rd_en  (rd_en),
			.dout   (dout),
			.dcount (dcount),
			.full   (full),
			.empty  (empty)
		);

endmodule

四、仿真结果

        例如,检测empty时

五、总结

        同步fifo参数化设计,以上为简化版同步fifo架构实现,仅供参考。特别注意:若一次将要写入的数据数量大于该简化版同步fifo设定的深度,但读使能设计合理,避免了fifo的爆满,empty不会与读出最后一位相对应。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
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 是一种用于实现数据缓冲和转移的组件,在不同频率的数据传输中发挥了关键作用。它可以通过读写指针的同步移动来保证数据的可靠性和完整性,并且可根据需求和硬件资源进行灵活调整。但同时也需要注意处理同步和互斥问题,以确保数据的正确传输。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱看小黄鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值