IC基础(二):同步FIFO

在IC基础(二)中我们已经介绍了异步FIFO,本章介绍的同步FIFO会比上一章简单许多。

一、什么是同步FIFO?
这部是很简单的问题吗?同步FIFO就是读写时钟域都是同一个时钟的FIFO。也就是说同步FIFO不用考虑跨时钟域的问题。同步FIFO和异步FIFO的根本区别就是读写时钟的不一样。

二、同步FIFO的常见参数

clk:读写时钟
rst_n:同步复位信号
w_en:写使能信号
r_en:读使能信号
wdata:输入数据信号
rdata:读出数据信号
wfull:满信号
rempty:空信号

三、同步FIFO建模

同步FIFO的模型可比异步FIFO的模型简单多了。同步FIFO的模型一般如下所示。(懒得画图了,直接用Xilinx的模型)
在这里插入图片描述
可以看到我们设计的端口信号和XIlinx的同步FIFO的模型的端口信号是一致的。FIFO的读写行为如下:

当需要写入数据时:需要判断两个信号,一个是wfull信号,另一个是写使能信号w_en。只有FIFO不满并且写使能有效的时候才可以写入数据。用逻辑来表达就是:
(!wfull&&w_en==1’b1),并且此时的数据要和写使能对齐。用代码来表示就是:

always@(*) begin
	if(rst_n==1'b0) 
		wdata = 0;
	else if(!wfull&&w_en==1)
		wdata = i;
	else
		wdata <= 0;
end 

如果不满足这个条件,那么写入数据就会有错误。

当需要读出数据的时候:需要判断两个信号,一个是rempty信号,另一个是r_en信号。只有当FIFO不空,并且读使能有效的时候才可以读取数据。用逻辑来表示就是:

(!rempty&&r_en==1'b1)

读时能没有那么讲究了,只要不空随时可以读。

always@(posedge clk or negedge rst_n) begin
	if(rst_n==1'b0)
		r_en =1'b0;
	else if(!rempty)
		r_en = 1'b1;
	else
		r_en = 1'b0;
end

四、地址跳变
FIFO设计最难的地方就在于如何设计读写指针,反正我在调试的时候遇到了很多问题。
总之记住:
FIFO每读一个数据,读指针就加一。FIFO每写一个数,写指针就加一。
FIFO的读和写指针永远都是指向下一个即将要读或者写的单元。

五、判空和判满
同步FIFO的判空和判满也是和异步FIFO一样的,区别在于异步FIFO在各自的时钟域使用格雷码进行判空和判满;而同步FIFO只有一个时钟域,判空和判满是使用的自然二进制地址。

FIFO的读指针和写指针相同时不是空就是满,同步FIFO同样需要一个额外的位来区别是空还是满。

判空:assign rempty_val = (rbinnext==wbinnext);

判满:assign wfull_val = (rbinnext=={~wbinnext[ADDR_SIZE],wbinnext[ADDR_SIZE-1:0]});

注意:这里使用的是rbinnext和wbinnext进行判空和判满。因为我们说过了,FIFO的指针总是指向下一个即将要写入或者读取的数据,因此使用的是对应的next信号。而把真正需要送到RAM的地址用:

    assign raddr = rbinnext[ADDR_SIZE-1:0];
    assign waddr = wbinnext[ADDR_SIZE-1:0];

六、代码实现

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2019/03/25 10:00:51
// Design Name: 
// Module Name: async_fifo
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module async_fifo#(
	parameter		DATA_SIZE = 8,
	parameter		ADDR_SIZE = 4
	)(
	input		clk,
	input		rst_n,
	input		w_en,
	input		r_en,
	input		[DATA_SIZE-1:0] wdata,
	output	reg	[DATA_SIZE-1:0] rdata,
	output	reg	wfull,
	output	reg	rempty
    );
    
    //开辟一个RAM存储空间
    localparam	DEPTH = 1<<(ADDR_SIZE);
    reg	[DATA_SIZE-1:0] mem [0:DEPTH-1];
    //定义读指针和写指针
    reg	[ADDR_SIZE:0] rbin,wbin;
    wire	[ADDR_SIZE:0] rbinnext,wbinnext;
    wire	[ADDR_SIZE-1:0] raddr,waddr;
    
    wire	wfull_val;
    wire	rempty_val;
    
    //写地址变化
    always@(posedge clk or negedge rst_n) begin
    	if(!rst_n) begin
			rbin <= 0;
    	end
    	else
    		rbin <= rbinnext;
    end
    
        //读地址变化
    always@(posedge clk or negedge rst_n) begin
    	if(!rst_n) begin
			wbin <= 0;
    	end
    	else
    		wbin <= wbinnext;
    end
    //满标志
    always@(posedge clk or negedge rst_n) begin
    	if(!rst_n) 
    		wfull <= 1'b0;
    	else
    		wfull <= wfull_val;
    end
    
        //空标志
    always@(posedge clk or negedge rst_n) begin
    	if(!rst_n) 
    		rempty <= 1'b1;
    	else
    		rempty <= rempty_val;
    end
    
    //读数据
    always@(*)begin
    	if(!rst_n) 
    		rdata <= 0;
    	else if(r_en==1'b1)
    		rdata <= mem[raddr];
    	else
    		rdata <= rdata;
    end
//	assign rdata = mem[raddr];
    
        //写数据
    always@(posedge clk)begin
		if(w_en==1'b1)
			mem[waddr] <= wdata;
		else
			mem[waddr] <= mem[waddr];
    end

    //逻辑设计
    assign wbinnext = !wfull?(wbin + w_en):wbin;
    assign rbinnext = !rempty?(rbin + r_en):rbin;
    assign raddr = rbinnext[ADDR_SIZE-1:0];
    assign waddr = wbinnext[ADDR_SIZE-1:0];
    assign wfull_val = (rbinnext=={~wbinnext[ADDR_SIZE],wbinnext[ADDR_SIZE-1:0]});
    assign rempty_val = (rbinnext==wbinnext);
endmodule

七、测试文件

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2019/03/25 10:30:43
// Design Name: 
// Module Name: TB_async_fifo
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module TB_async_fifo;

    parameter		DATA_SIZE = 8;
    parameter		ADDR_SIZE = 4;
    parameter		CLK_PERIOD = 10;
    integer			i 		  = 3;
    
    reg		clk;
    reg		rst_n;
    reg		w_en;
    reg		r_en;
    reg		[DATA_SIZE-1:0] wdata;
    
    wire	[DATA_SIZE-1:0] rdata;
    wire	wfull;
    wire	rempty;
    
    initial begin
    	clk = 0;
    end
    always#(CLK_PERIOD/2) clk = ~clk;
    
    initial begin;
    	rst_n = 1'b0;
    	#15;
    	rst_n = 1'b1;
    end

//	always@(rst_n or wfull) begin
//		if(rst_n==1'b0)
//			w_en = 1'b0;
//		else if(!wfull) 
//			w_en = 1'b1;
//		else
//			w_en = 1'b0;	
//	end

	always@(posedge clk or negedge rst_n) begin
		if(rst_n==1'b0)
			w_en = 1'b0;
		else if(!wfull) 
			w_en = 1'b1;
		else
			w_en = 1'b0;	
	end
	
//	always@(rst_n or rempty) begin
//		if(rst_n==1'b0)
//			r_en =1'b0;
//		else if(!rempty)
//			r_en = 1'b1;
//		else
//			r_en = 1'b0;
//	end
	
always@(posedge clk or negedge rst_n) begin
	if(rst_n==1'b0)
		r_en =1'b0;
	else if(!rempty)
		r_en = 1'b1;
	else
		r_en = 1'b0;
end

	always@(posedge clk or negedge rst_n) begin
		if(rst_n==1'b0)
			i <= 3;
		else if(!wfull)
			i <= i+1;
		else
			i <= i;
	end 
	
	always@(*) begin
		if(rst_n==1'b0) 
			wdata = 0;
		else if(!wfull&&w_en==1)
			wdata = i;
		else
			wdata <= 0;
	end   
    
    
    
		async_fifo#(
    	.DATA_SIZE(DATA_SIZE),
    	.ADDR_SIZE(ADDR_SIZE)
    	)
    	uut(
    	.clk(clk),
    	.rst_n(rst_n),
    	.w_en(w_en),
    	.r_en(r_en),
    	.wdata(wdata),
    	.rdata(rdata),
    	.wfull(wfull),
    	.rempty(rempty)
        );
endmodule

八、测试波形
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/MaoChuangAn/article/details/88796292