Verilog同步FIFO

关键:本文简单说明了FIFO的实现方式,同步FIFO,判断full或者empty方法使用FIFO内容计数器,设置一个变量fifo_cnt记录存储器中数据个数:

        //判断空满
assign buf_empty = (fifo_cnt == 0);  //buf_empty若是reg类型则错,不能使用assign持续赋值
assign buf_full  = (fifo_cnt == `BUF_SIZE);


Implementing a FIFO using Verilog

    FIFO uses a dual port memory and there will be two pointers to point read and write addresses. Here is a generalized block diagram of FIFO. 简单来说,FIFO就是一个双口RAM加上两个读写指针。


Generally fifos are implementedusing rotating pointers. We can call write and read pointers of a FIFO as headand tail of data area. Initially read and write pointers of the FIFO will pointto the same location

一般FIFO使用循环指针(计数溢出自动归零)。一般可以称写指针为头head,读指针为尾tail。初始化时,读写指针指向同一数据地址。

Here is an example to explain howFIFO uses the memory. This is a fifo of length 8, WP and RP are the locationswhere write pointer and read pointer points. Shaded area in the diagram isfilled with data.

下图可见,FIFO初始化时,WPRP指针指向同一数据单元。WP指向下一个将要写入的数据单元,RP指向将要读出的数据单元


When ever FIFO counter becomes zeroor BUF_LENGTH, empty or full flags will beset.

使用fifo_counter记录FIFO RAM中的数据个数,等于0时,给出empty信号,等于BUF_LENGTH时,给出full信号

fifo_counter is incremented ifwrite takes place and buffer is not full and will be decremented id read takesplace and buffer is not empty. If both read and write takes place, counter willremain the same.

fifo_counter写而未满时增加1,读而未空时减1。同时发生读写操作时,fifo_counter不变。

rd_ptr and wr_ptr are read andwrite pointers. Since we selected the bits in these registers same as addresswidth of buffer, when buffer overflows, values will overflow and become 0.

读写指针宽度与地址宽度相当,地址增加而溢出后,自动变成0。循环指针,此处地质变化:0-7-0-7-0

程序代码:

`define BUF_WIDTH   4     //地址宽度为3+1,
`define BUF_SIZE    8    //数据个数,FIFO深度

module fifo_counter( clk,rst_n,buf_in,buf_out,wr_en,rd_en,buf_empty,buf_full,fifo_cnt);
	input clk,rst_n;
	input wr_en,rd_en;
	input [7:0] buf_in;             // data input to be pushed to buffer
	output reg [7:0] buf_out;       // port to output the data using pop.
	output wire buf_empty,buf_full;  // buffer empty and full indication 
	output reg [`BUF_WIDTH-1:0] fifo_cnt;  // number of data pushed in to buffer  
	                                       //写入数据等于8时,满
	
	reg [`BUF_WIDTH-2:0] rd_ptr,wr_ptr;  //这个很重要,数据指针3位宽度,0-7索引,8个数据深度,循环指针0-7-0-7
	reg [7:0] buf_mem[0:`BUF_SIZE-1];
	
	//判断空满
	assign buf_empty = (fifo_cnt == 0);  //buf_empty若是reg类型则错,不能使用assign持续赋值
	assign buf_full  = (fifo_cnt == `BUF_SIZE);
	
	always @(posedge clk or negedge rst_n)begin
		if(!rst_n)
			fifo_cnt <= 0;
		else if((!buf_full&&wr_en)&&(!buf_empty&&rd_en)) //同时读写,数量不变
			fifo_cnt <= fifo_cnt;
		else if(!buf_full && wr_en)          //写数据
			fifo_cnt <= fifo_cnt + 1;
		else if(!buf_empty && rd_en)         //读数据
			fifo_cnt <= fifo_cnt-1;
		else 
			fifo_cnt <= fifo_cnt;
	end
	
	always @(posedge clk or negedge rst_n) begin   //读数据
		if(!rst_n)
			buf_out <= 0;
		if(rd_en && !buf_empty)
			buf_out <= buf_mem[rd_ptr];
	end
	
	always @(posedge clk) begin
		if(wr_en && !buf_full)
			buf_mem[wr_ptr] <= buf_in;
	end
	
	always @(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			wr_ptr <= 0;
			rd_ptr <= 0;
		end
		else begin
			if(!buf_full && wr_en)
				wr_ptr <= wr_ptr + 1;
			if(!buf_empty && rd_en)
				rd_ptr <= rd_ptr + 1;
		end
	end
	
endmodule 

使用Modelsim仿真的程序:

`define BUF_WIDTH   4     //地址宽度为3+1,
`define BUF_SIZE    (8)    //数据个数,FIFO深度

module tb_fifo_counter;
	reg clk,rst_n;
	reg wr_en,rd_en;
	reg [7:0] buf_in;             // data input to be pushed to buffer
	wire [7:0] buf_out;       // port to output the data using pop.
    wire buf_empty,buf_full;  // buffer empty and full indication 
	wire [`BUF_WIDTH-1:0] fifo_cnt;  // number of data pushed in to buffer 
	
	fifo_counter dut(clk,rst_n,buf_in,buf_out,wr_en,rd_en,buf_empty,buf_full,fifo_cnt);
	
	always #10 clk = ~clk;
	
	reg [7:0] tempdata = 0;
	initial begin
		clk = 0;
		rst_n = 0;
		wr_en = 0;
		rd_en = 0;
		buf_in = 0;
		#15;
		rst_n = 1;
		
		push(1);
        fork
           push(2);
           pop(tempdata);
        join              //push and pop together   
        push(10);
        push(20);
        push(30);
        push(40);
        push(50);
        push(60);
        push(70);
        push(80);
        push(90);
        push(100);
        push(110);
        push(120);
        push(130);

        pop(tempdata);
        push(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
		push(140);
        pop(tempdata);
        push(tempdata);//
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        pop(tempdata);
        push(5);
        pop(tempdata);
		
	end
	
	task push (input [7:0] data);
		if(buf_full)
			$display("---Cannot push %d: Buffer Full---",data);
		else begin
			$display("Push",,data);
			buf_in = data;
			wr_en = 1;
			@(posedge clk);
			#5 wr_en = 0;
		end
	endtask
	
	task pop(output[7:0] data);
		if(buf_empty)
			$display("---Cannot Pop: Buffer Empty---");
		else begin
			rd_en = 1;
			@(posedge clk);
			#3 rd_en = 0;
			data = buf_out;
			$display("------Poped:",,data);
		end		
	endtask
	
	
endmodule 

程序仿真打印结果:

# Push   1
# Push   2
# ------Poped:   1
# Push  10
# Push  20
# Push  30
# Push  40
# Push  50
# Push  60
# Push  70
# ---Cannot push  80: Buffer Full---
# ---Cannot push  90: Buffer Full---
# ---Cannot push 100: Buffer Full---
# ---Cannot push 110: Buffer Full---
# ---Cannot push 120: Buffer Full---
# ---Cannot push 130: Buffer Full---
# ------Poped:   2
# Push   2
# ------Poped:  10
# ------Poped:  20
# ------Poped:  30
# ------Poped:  40
# Push 140
# ------Poped:  50
# Push  50
# ------Poped:  60
# ------Poped:  70
# ------Poped:   2
# ------Poped: 140
# ------Poped:  50
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# ---Cannot Pop: Buffer Empty---
# Push   5
# ------Poped:   5


程序仿真图:






同步FIFO是一种常用的电子电路,用于在两个或多个时钟域之间传输数据。它具有先入先出的性质,可以保证数据传输的顺序不变。 Verilog语言中,同步FIFO可以使用一组寄存器实现。下面是一个简单的同步FIFOVerilog代码: ``` module sync_fifo ( input clk_w, // 写时钟 input rst_w, // 写复位 input wr_en, // 写使能 input [7:0] wr_data, // 写数据 input clk_r, // 读时钟 input rst_r, // 读复位 input rd_en, // 读使能 output [7:0] rd_data, // 读数据 output full, // 满标志 output empty // 空标志 ); parameter WIDTH = 8; // 数据宽度 parameter DEPTH = 16; // 深度 reg [WIDTH-1:0] fifo [0:DEPTH-1]; // FIFO存储器 reg [WIDTH-1:0] rd_data_reg; // 读数据寄存器 reg [DEPTH-1:0] wr_ptr; // 写指针 reg [DEPTH-1:0] rd_ptr; // 读指针 wire [DEPTH-1:0] count; // 元素个数 // 写逻辑 always @(posedge clk_w or posedge rst_w) begin if (rst_w) begin wr_ptr <= 0; fifo <= 0; end else if (wr_en && ~full) begin fifo[wr_ptr] <= wr_data; wr_ptr <= wr_ptr + 1; end end // 读逻辑 always @(posedge clk_r or posedge rst_r) begin if (rst_r) begin rd_ptr <= 0; rd_data_reg <= 0; end else if (rd_en && ~empty) begin rd_data_reg <= fifo[rd_ptr]; rd_ptr <= rd_ptr + 1; end end // 元素个数计算 assign count = wr_ptr - rd_ptr; // 满标志 assign full = (count == DEPTH); // 空标志 assign empty = (count == 0); // 读数据输出 assign rd_data = rd_data_reg; endmodule ``` 在这个Verilog代码中,FIFO存储器使用一组寄存器实现,写指针和读指针分别指向下一个写入位置和读取位置。当写使能信号wr_en有效且FIFO未满时,写入数据wr_data,并将写指针加1;当读使能信号rd_en有效且FIFO非空时,读取数据并将读指针加1。元素个数count可以通过写指针和读指针的差值计算得到,满标志full和空标志empty分别表示FIFO是否已满和是否为空。最后,读数据rd_data通过一个寄存器输出,以保证读数据的正确性。
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值