数字IC设计系列----单端口RAM、双端口RAM、同步FIFO、异步FIFO

目录​​​​​​​

一、单端口RAM原理及实现

1.1、原理

1.2、Verilog实现

1.3、优缺点分析

2、双端口RAM原理及实现

2.1、原理

2.2、Verilog实现

 2.3、优缺点分析

3、 同步FIFO

3.1、原理

3.2、Verilog实现(1)

3.3、Verilog实现(2)

3.4、优缺点分析

利用factor来记数当前FIFO中的数据量来判断空满

利用位置指针多加1bit来判断空满

4、异步FIFO

4.1、原理

4.2、格雷码

4.3、Verilog代码实现

4.4 、优缺点分析

总结


一、单端口RAM原理及实现

1.1、原理

在内存空间中开辟出一段固定大小的内存用于存储数据,每一个数据所占的bit位称之为位宽,这段内存空间中数据的总数称之为深度。例如reg [7:0] mem [255:0],这段内存空间中每一个数据的位宽为8bit,深度为256。

在这段内存空间中,每个数据分配给一个地址,如上例深度为256,可以用8bit的地址来表示所有的数据,0000_0000则表示第0个数据,1111_1111则表示第255个数据。

外部信号通过固定的时钟节拍,通过使能信号及地址信号来读取RAM中特定位置的数据或者向RAM中特定位置写入数据。

1.2、Verilog实现

module Single_Port_RAM
(
	//system input
	 input 		clk
	,input		rst_n
	
	//data input
	,input 						enable_wr		//allow the user to read or write the data
														//when enable_wr == 0,it means to write the data from RAM
														//when enable_wr == 1,it means to read the data from RAM
	,input		 	[7:0]		data_write
	,input			[7:0]		address
	
	//data output
	,output	reg 	[7:0]		data_read
);

//setup RAM which has the width of 8 and depth of 256
localparam 	mem_width = 7;
localparam	mem_depth = 255;
reg	[mem_width:0]		mem	[mem_depth:0];

always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
		data_read <= 8'd0;
	else if (!enable_wr)
		mem[address] <= data_write;
	else if (enable_wr)
		data_read <= mem[address];
end
endmodule

1.3、优缺点分析

缺点:单一时刻只能进行读操作或者写操作,而不能同时进行;地址处的数据为0时也会读取,可能会发生错误。

优点:简单,不需要协调读写操作;

2、双端口RAM原理及实现

2.1、原理

参考如上的单端口RAM,双端口RAM在单端口RAM的基础上,将使能信号分为读使能信号和写使能信号,将地址信号分为读地址信号和写地址信号。

2.2、Verilog实现

module Dual_Port_RAM
(
	//system input
	 input			clk
	,input			rst_n
	
	//signal of read
	,input						read_en				//when read_en == 1,read the data from mem according to read_addr
	,input			[7:0]		read_addr
	,output	reg 	[7:0]		read_data
	
	//signal of write
	,input						write_en				//when write_en ==1,write the data to mem according to write_addr
	,input			[7:0]		write_addr
	,input			[7:0]		write_data
);

//setup RAM which has the width of 8 and depth of 256
localparam 	mem_width = 7;
localparam	mem_depth = 255;
reg	[mem_width:0]		mem	[mem_depth:0];
reg	[7:0]		i;

always @ (posedge clk or negedge rst_n)
begin
	if (~rst_n)
		for (i=0;i<=(mem_depth);i=i+1)
			mem[i] <= 8'd0;
	else if (write_en)
		mem[write_addr] <= write_data;
end

always @ (posedge clk or negedge rst_n)
begin
	if (~rst_n)
		read_data <= 8'd0;		
	else if (read_en)									
		read_data <= mem[read_addr];
	else
		read_data <= read_data;				//when read_en is disabled, read_data maintains the data before 	
end
endmodule

 2.3、优缺点分析

优点:可以同时对RAM进行读写操作

3、 同步FIFO

3.1、原理

在双端口RAM的基础上增加了一个限制项:即最先写进去的数据被最先读出来,此时不再对RAM的固定地址进行读写操作,而是按照写入顺序进行读出。

同步FIFO主要用于①同一时钟域但是速度不同的模块之间充当buffer的功能;②用于不同数据宽度的模块之间,作为数据匹配(如前一个模块位宽为8bit,后一个模块为16bit,中间加一个FIFO,则每次读出两个数据进入后一级)

按顺序写入和读出就会衍生出两个限制:RAM为空时不允许读出,RAM已经满后不允许写入。因此针对同步FIFO的难点主要在这儿。

设置两个位置指针,其中一个指向下一次要写入数据的位置write_addr,另外一个指向本次要读取数据的位置read_addr,每次读写一次数据write_addr和read_addr各增加一,当二者均等于RAM深度时,下一次置零,重新计数。(相当于两个人从一楼到顶楼,然后跳下来接着往上走。)

针对以上难点,出现了两种解决问题的方式:

①设定一个factor选项,每次写入数据,factor+1,;每次读出数据,factor-1。若此CLK有效边沿既要写入数据,又要读出数据,则Factor不变。则当factor的大小等于0时,表示RAM为空;当factor的大小等于RAM深度时,表示RAM为满;

3.2、Verilog实现(1)

//--==============================================================================
// THIS FILE DESCRIBE A SYNCHRONIZATION FIFO (FIRST IN FIRST OUT)
//	USING DATA COUNT TO DETECT FIFO FULL OR FIFO EMPTY 
//
// Project			:		Verilog_Learning
// File_name		:		Sync_FIFO_1.v
// Creator(s)		:		Moshang
// Date				:		2020/09/29
// Description		:		A synchronization FIFO control
//
// Modification	:
//		(1) Initial Design 2020/09/29
//
//
//--==============================================================================

module Sync_FIFO_1
(
	//System input
	 clk
	,rst_n
		
	//read control
	,fifo_read_en
	,fifo_read_data
	,fifo_read_err
	,fifo_empty
	
	//write control
	,fifo_write_en
	,fifo_write_data
	,fifo_write_err
	,fifo_full
	
	//data count in mem
	,fifo_data_cnt
);

// PARAMETER DECLARATION
parameter 	FIFO_DATA_WIDTH	=	8;
parameter	FIFO_ADDR_WIDTH	=	4;


// INPUT AND OUTPUT DECLARATION
input													clk;						//SYSTEM CLOCK INPUT 
input													rst_n;					//SYSTEM RESET (RESET:	0)				
input													fifo_read_en;			//FIFO READ ENABLE (ENABLE:	1)
output		reg	[FIFO_DATA_WIDTH-1:0]	fifo_read_data;		//DATA READ FROM FIFO
output		wire									fifo_read_err;			//FIFO IS EMPTY BUT READ IS ENABLED(ERR:	1)
output		wire									fifo_empty;				//FIFO IS EMPTY(EMPTY:	1)
input													fifo_write_en;			//FIFO WRITE ENABLE(ENABLE:	1)
input					[FIFO_DATA_WIDTH-1:0]	fifo_write_data;		//DATR WRITE TO FIFO
output		wire									fifo_write_err;		//FIFO IS FULL BUT WIRTE IS ENABLED(ERR:	1)
output		wire									fifo_full;				//FIFO IS FULL(FULL:	1)
output		reg	[FIFO_ADDR_WIDTH  :0]	fifo_data_cnt;			//FIFO DATA COUNT

// INNER DECLARATION
reg					[FIFO_ADDR_WIDTH-1:0]	fifo_read_addr;		//FIFO ADDR WHICH IS GOING TO WRITE DATA NEXT TIME
reg					[FIFO_ADDR_WIDTH-1:0]	fifo_write_addr;		//FIFO ADDR WHICH IS READ DATA FROM NEXT TIME

//FIFO MEMORY INSTANCE
reg		[FIFO_DATA_WIDTH-1:0]	FIFO_MEM		[{(FIFO_ADDR_WIDTH){1'b1}}:0];
integer 	i;


//--=========================== MODULE SOURCE CODE ===========================--

//--===========================================--
// SRAM INSTANCE :
// FIFO Rdata & FIFO Wdata
//--===========================================--
always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
		fifo_read_data <= {(FIFO_DATA_WIDTH){1'b0}};
	else if (fifo_read_en && (!fifo_empty))
		fifo_read_data <= FIFO_MEM[fifo_read_addr];
	else 
		fifo_read_data <= fifo_read_data;
end

always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
		for (i=0;i<=({(FIFO_ADDR_WIDTH){1'b1}});i=i+1)
			begin
				FIFO_MEM[i] <= {(FIFO_DATA_WIDTH){1'b0}};
			end
	else if (fifo_write_en && (!fifo_full))
		FIFO_MEM[fifo_write_addr] <= fifo_write_data;
	else
		FIFO_MEM[fifo_write_addr] <= FIFO_MEM[fifo_write_addr];
end

//--===========================================--
// READ ADDR & WRITE ADDR CONTROL
//--===========================================--
always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
		fifo_read_addr <= {(FIFO_ADDR_WIDTH){1'b0}};
	else if (fifo_read_en && (!fifo_empty))
		fifo_read_addr <= fifo_read_addr + 1'b1;
	else
		fifo_read_addr <= fifo_read_addr;
end

always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
		fifo_write_addr <= {(FIFO_ADDR_WIDTH){1'b0}};
	else if (fifo_write_en && (!fifo_full))
		fifo_write_addr <= fifo_write_addr + 1'b1;
	else
		fifo_write_addr <= fifo_write_addr;
end

//--===========================================--
// FIFO DATA CNT
//	VALID WRITE ONLY, DATA COUNT INCREASE
// VALID READ ONLY , DATA COUNT DECREASE
//--===========================================--
always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
		fifo_data_cnt <= {(FIFO_ADDR_WIDTH + 1'b1){1'b0}};
	else if (fifo_read_en && (!fifo_empty) && (!(fifo_write_en && (!fifo_full))))			//FIFO VALID READ ONLY , COUINT DECREASE
		fifo_data_cnt <= fifo_data_cnt - 1'b1;
	else if (!(fifo_read_en && (!fifo_empty)) && (fifo_write_en && (!fifo_full)))			//FIFO VALID WRITE ONLY, COUNT INCREASE
		fifo_data_cnt <= fifo_data_cnt + 1'b1;
	else
		fifo_data_cnt <= fifo_data_cnt;
end

//--===========================================--
// FIFO DATA CNT
// EMPTY, FULL , READ_ERR,WRITE_ERR
// 因为fifo_data_cnt的增减是以空满信号为判断依据的,反过来,空满信号也是以fifo_data_cnt作为判断依据的,因此可以理解为互相驱动;
// 且二者之间必须是实时响应的,如果两个都用always块来进行赋值,则二者之间会出现一个时钟周期的延迟,最终导致逻辑错误
// 如果不想要这个延迟,则将其中一个作为assign类型来处理就可以
//--===========================================--
assign fifo_empty = 	(fifo_data_cnt == 0) & (rst_n);
assign fifo_full 	=	(fifo_data_cnt == {1'b1,{(FIFO_ADDR_WIDTH){1'b0}}}) & (rst_n);

assign fifo_read_err 	= (fifo_empty & fifo_read_en) & (rst_n); 
assign fifo_write_err 	= (fifo_full & fifo_write_en) & (rst_n);

endmodule

②在addr前面再加一bit标示位来标识是否已经转了一圈了来判决究竟是空还是满。

当write_addr == read_addr时,可能是已经读操作赶上了写操作,即当前RAM为空;也可能是写操作已经转了一圈了到达读操作的位置,即RAM当前已经满了。在addr前面再加一bit标示位来标识是否已经转了一圈了来判决究竟是空还是满,正常逻辑情况下,写操作永远领先于读操作。如果最高位不同,而其他位相同,则说明写操作已经领先于读操作一圈,则FIFO状态为满;如果最高位相同,其他位也相同,则说明二者处于同一圈的同一位置,则FIFO状态为空)

3.3、Verilog实现(2)

//--==============================================================================
// THIS FILE DESCRIBE A SYNCHRONIZATION FIFO (FIRST IN FIRST OUT)
//	USING EXTRA BIT OF WRITE_ADDR AND READ_ADDR TO DETECT FIFO FULL AND FIFO EMPTY
//
// Project			:		Verilog_Learning
// File_name		:		Sync_FIFO_2.v
// Creator(s)		:		Moshang
// Date				:		2020/09/29
// Description		:		A synchronization FIFO control
//
// Modification	:
//		(1) Initial Design 2020/09/29
//
//
//--==============================================================================


module Sync_FIFO_2
(
	//System input
	 clk
	,rst_n
		
	//read control
	,fifo_read_en
	,fifo_read_data
	,fifo_read_err
	,fifo_empty
	
	//write control
	,fifo_write_en
	,fifo_write_data
	,fifo_write_err
	,fifo_full	
);

// PARAMETER DECLARATION
parameter FIFO_DATA_WIDTH 	= 	8;
parameter FIFO_ADDR_WIDTH	= 	3;

// INPUT DECLARATION
input														clk;						// SYSTEM CLOCK INPUT 
input														rst_n;					// SYSTEM RESET (RESET:1)
input														fifo_read_en;			//	FIFO READ ENABLE (ENABLE:1)
input														fifo_write_en;			// FIFO WRITE ENABLE (ENABLE:1)
input			wire	[FIFO_DATA_WIDTH-1'b1 : 0]	fifo_write_data;		// DATA WRITE TO FIFO

// OUTPUT DECLARATION
output 		reg	[FIFO_DATA_WIDTH-1'b1 : 0]	fifo_read_data;		// DATA READ FROM FIFO
output													fifo_read_err;			// ERR CAUSED WHEN FIFO IS EMPTY BUT READ IS DESIRED
output													fifo_empty;				// FIFO EMPTY (EMPTY:1)
output													fifo_write_err;		//	ERROR CAUSED WHEN FIFO IS FULL BUT WIRTE IS DESIRED
output 													fifo_full;				// FIFO FULL (FULL:1)

// FIFO MEMORY DECLARATION
reg	[FIFO_DATA_WIDTH-1'b1:0]  	FIFO_MEM		[{(FIFO_ADDR_WIDTH){1'b1}} : 0];
integer i;

// WRITE ADDRESS AND READ ADDRESS DECLARATION
// WHERE FIFO_READ_ADDR AND FIFO_WRITE_ADDR IS ONE BIT MORE THAN FIFO_ADDR_WIDTH
reg 	[FIFO_ADDR_WIDTH : 0]	fifo_read_addr;
reg 	[FIFO_ADDR_WIDTH : 0]	fifo_write_addr;

//--========================== MODULE SOURCE CODE ============================--

//--==================================--
// SRAM INSTANCE 
// FIFO READ DATA & FIFO WRITE DATA
//--==================================--
always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
		fifo_read_data <= {(FIFO_DATA_WIDTH){1'b0}};
	else if (fifo_read_en && (!fifo_empty) )
		fifo_read_data <= FIFO_MEM[{fifo_read_addr[(FIFO_ADDR_WIDTH - 1'b1) : 0]}];
	else
		fifo_read_data <= fifo_read_data;
end
always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
		for (i=0;i<={(FIFO_ADDR_WIDTH){1'b1}};i=i+1)
			FIFO_MEM[i] <= {(FIFO_DATA_WIDTH){1'b0}};
		else if (fifo_write_en && (!fifo_full))
			FIFO_MEM[{fifo_write_addr[(FIFO_ADDR_WIDTH - 1'b1) : 0]}] <= fifo_write_data;
		else 
			FIFO_MEM[{fifo_write_addr[(FIFO_ADDR_WIDTH - 1'b1) : 0]}] <= FIFO_MEM[{fifo_write_addr[(FIFO_ADDR_WIDTH - 1'b1) : 0]}];
end


// FIFO CONTROL OF WRITE ADDRESS AND READ ADDRESS
always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
		fifo_read_addr <= {(FIFO_ADDR_WIDTH + 1'b1){1'b0}};
	else if (fifo_read_en && (!fifo_empty) )
		fifo_read_addr <= fifo_read_addr + 1'b1;
	else
		fifo_read_addr <= fifo_read_addr;
end
always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
		fifo_write_addr <= {(FIFO_ADDR_WIDTH + 1'b1){1'b0}};
	else if (fifo_write_en && (!fifo_full) )
		fifo_write_addr <= fifo_write_addr + 1'b1;
	else
		fifo_write_addr <= fifo_write_addr;
end

// DECTION OF FIFO EMPTY AND FIFO FULL
assign fifo_empty 		= ((fifo_read_addr == fifo_write_addr) && rst_n);
assign fifo_read_err 	= ((fifo_empty && fifo_read_en) && rst_n);

assign fifo_full		 	= (((fifo_write_addr - fifo_read_addr) == {1'b1,{(FIFO_ADDR_WIDTH){1'b0}}}) && rst_n);
assign fifo_write_err 	= ((fifo_full && fifo_write_en) && rst_n);
  
endmodule

 

3.4、优缺点分析

  • 利用factor来记数当前FIFO中的数据量来判断空满

优点:逻辑简单,便于理解,且能直接知道当前FIFO中的剩余数据量

缺点:使用寄存器资源较多,代码量增加

  • 利用位置指针多加1bit来判断空满

优点:代码量总体来说比上一个小,逻辑也较为简单;使用的寄存器资源较少

缺点:无法准确知道当前FIFO中一共有多少个数

4、异步FIFO

4.1、原理

由于读写时钟来自不同的时钟域,且复位信号也有可能来自另外一个时钟域,则有可能会产生以下问题

① 异步复位信号在读写时钟域分别产生亚稳态;

② 空满信号判断时产生亚稳态,如 判断空信号时,需要在“读时钟域”内采样写地址信号,并在此时钟域与读地址信号进行对比,判断是否为空;由于对写地址信号为跨时钟域采样,可能会产生亚稳态。满信号的判断也是如此;

③ 跨时钟域对读地址/写地址 进行采样时,可能会产生误判;如读地址正好处在从“0001”到"0010"的变化边沿上,其实此时有两个bit发生变化,而两个bit一般不会是同时变化的,总会有先后顺序,即有可能采集到“0001、0010、0000、0011”这四个状态中的某一个。从而使得读写地址产生误判;

 

针对以上三种问题,分别做出了处理方式:

① 异步复位产生亚稳态

          解决方式:对异步复位信号进行同步化处理,即分别在读时钟域和写时钟域内进行异步复位同步释放的操作;

② 空满信号产生亚稳态

          解决方式:在读时钟域内对写地址进行两级触发器采样,这样可以降低亚稳态发生的概率;(两级触发器采样实质上就是对异步信号进行同步化处理,降低亚稳态发生的概率;复位信号的异步复位同步释放的操作本质上也是这样)

③ 读写地址采样时产生误判

        解决方式:对读写地址采用格雷码编码,使得每次变化时只有1bit发生变化;如从“0001(0001的格雷码)”到“0011(0010的格雷码)”,此时只有1bit发生变化,采样时只有可能是0011或0001;如果采集错误(本来是0011,结果采集到了0001),也仅仅是慢一个时钟周期而已,因为两个数是相邻的,直接跳转到其他意料之外的地址,也不会产生重大的逻辑错误。

4.2、格雷码

格雷码:通常我们的计数采用8421码,如0000表示0,0001表示1,0010表示2;格雷码其特点是相邻编码之间只有1bit发生变化,如0000表示0,0001表示1,0011表示2等,从8421码转化为格雷码的方法如下:

方法1: 最高位(MSB)不变,其余每一位与其相邻的高一位进行亦或,从而得到格雷码;

方法2:8421码右移1bit,最高为补0,然后此新的代码与原来的8421码求亦或,从而得到格雷码(两种方法本质上是一样的)

4.3、Verilog代码实现

//--===================================================================--
// THIS FILE DESCRIBE A MODULE OF ASYNCHRONIZATION FIFO WHICH IS OFTEN USED IN 
// CLOCK DOMAIN CROSSING
// Project		:	Verilog_Learning 
// File_name	:	Async_FIFO.v
// Creator(s)	:	Moshang
// Date			:	2020/09/30
//	Description	:	a module of asynchronization FIFO
//	Modification:
//			(1)	Initial Design 2020/09/30
//
//
//--===================================================================--

module Async_FIFO
(
		 rst_n							//	[UNKNOW DOMAIN]
		,FIFO_read_clk 				// [READ DOMAIN]
		,FIFO_write_clk				//	[WRITE DOMAIN]
		
		,FIFO_read_en					// [READ DOMAIN]
		,FIFO_read_err					// [READ DOMAIN]
		,FIFO_read_data				// [READ DOMAIN]
		
		,FIFO_write_en					//	[WRITE DOMAIN]
		,FIFO_write_err				//	[WRITE DOMAIN]
		,FIFO_write_data				//	[WRITE DOMAIN]
		
		,FIFO_empty						//	[READ DOMAIN]
		,FIFO_full						//	[WRITE DOMAIN]
		
);	


// PARAMETER DECLARATION
parameter	FIFO_DATA_WIDTH	=	8;
parameter	FIFO_ADDR_WIDTH	=	3;


// FIFO MEMORY DECLARATION 
reg			[FIFO_DATA_WIDTH-1 : 0] 	FIFO_MEM		[{(FIFO_ADDR_WIDTH){1'b1}} : 0];
integer 		i;


// INPUT AND OUTPUT DECLARATION
input 												rst_n;				// SYSTEM RESET (RESET:0)
input													FIFO_read_clk;		//	READ CLOCK OF READ DOMAIN 
input													FIFO_write_clk;	// WRITE CLOCK OF WRITE DOMAIN
input													FIFO_read_en;		// READ ENABLE FROM READ DOMAIN (ENABLE: 1)
input													FIFO_write_en;		// WRITE ENABLE FROM WRITE DOMAIN (ENABLE: 1)
input				[FIFO_DATA_WIDTH-1 : 0]		FIFO_write_data;	// DATA WRITE TO FIFO

output												FIFO_read_err;		// ERROR OCCURED WHEN READ IS ENABLED BUT FIFO IS EMPTY (ERR:1)
output	reg	[FIFO_DATA_WIDTH-1 : 0]		FIFO_read_data;	// DATA READ FROM FIFO
output												FIFO_write_err;	// ERROR OCCURED WHEN WRITE IS ENABLED BUT FIFO IS FULL (ERR: 1)
output												FIFO_empty;			// FIFO EMPTY (EMPTY :1)
output												FIFO_full;			// FIFO FULL (FULL :1)


// READ ADDRESS AND WRITE ADDRESS DECLARATION
reg				[FIFO_ADDR_WIDTH : 0]		FIFO_read_addr;	//	[READ DOMAIN] READ ADDRESS WHEN EXTRA BIT IS USED TO DETECT EMPTY OR FULL
reg				[FIFO_ADDR_WIDTH : 0]		FIFO_write_addr;	//	[WRITE DOMAIN] WRITE ADDRESS WHEN EXTRA BIT IS USED TO DETECT EMPTY OR FULL


//	REG DECLARATION WHICH WILL BE USED WHEN TWO-LEVEL SYNCHRONIZATION
reg									read_rst_n_1;			// THE FIRST REG OUT WHEN SYNCHRONIZE RST_N TO READ DOMAIN
reg									read_rst_n_2;			// THE SECOND REG OUT WHEN SYNCHRONIZE RST_N TO READ DOMAIN
reg									write_rst_n_1;			// THE FIRST REG OUT WHEN SYNCHRONIZE RST_N TO WRITE DOMAIN
reg									write_rst_n_2;			// THE SECONDE REG OUT WHEN SYNCHRONIZE RST_N TO WRITE DOMAIN

wire				[FIFO_ADDR_WIDTH : 0]		FIFO_read_addr_grey_code;		// TRANSFER THE READ ADDRESS FROM 8421 TO GREY CODE
wire				[FIFO_ADDR_WIDTH : 0]		FIFO_write_addr_grey_code;		// TRANSFER THE WRITE ADDRESS FROM 8421 TO GREY CODE

reg				[FIFO_ADDR_WIDTH : 0]		FIFO_read_addr_to_write_1;		// THE FIRST REG OUT WHEN SYNCHRONIZE FIFO_read_addr TO WRITE DOMAIN
reg				[FIFO_ADDR_WIDTH : 0]		FIFO_read_addr_to_write_2;	// THE SECOND REG OUT WHEN SYNCHRONIZE FIFO_read_addr TO WRITE DOMAIN
reg				[FIFO_ADDR_WIDTH : 0]		FIFO_write_addr_to_read_1;		// THE FIRST REG OUT WHEN SYNCHRONIZE FIFO_write_addr TO READ DOMAIN
reg				[FIFO_ADDR_WIDTH : 0]		FIFO_write_addr_to_read_2;		// THE SECOND REG OUT WHEN SYNCHRONIZE FIFO_write_addr TO READ DOMAIN



//--========================== MODULE SOURCE CODE ==========================--

//Synchronize the rst_n to read clock domain and write clock domain
always @ (posedge FIFO_read_clk or negedge rst_n)
begin
	if (!rst_n)
		begin
			read_rst_n_1 <= 1'b0;
			read_rst_n_2 <= 1'b0;
		end
	else
		begin
			read_rst_n_1 <= 1'b1;
			read_rst_n_2 <= read_rst_n_1;
		end
end
always @ (posedge FIFO_write_clk or negedge rst_n)
begin
	if (!rst_n)
		begin
			write_rst_n_1 <= 1'b0;
			write_rst_n_2 <= 1'b0;
		end
	else
		begin
			write_rst_n_1 <= 1'b1;
			write_rst_n_2 <= write_rst_n_1;
		end
end

// READ DATA FROM FIFO MEMORY OR WRITE DATA TO FIFO MEMORY
always @ (posedge FIFO_read_clk)
begin
	if (!read_rst_n_2)
		FIFO_read_data <= {(FIFO_DATA_WIDTH){1'b0}};
	else if (FIFO_read_en && (!FIFO_empty))
		FIFO_read_data <= FIFO_MEM[FIFO_read_addr];
	else 
		FIFO_read_data <= FIFO_read_data;
end
always @ (posedge FIFO_write_clk)
begin
	if (!write_rst_n_2)
		for (i=0;i<=({(FIFO_ADDR_WIDTH){1'b1}});i=i+1)
			FIFO_MEM[i] <= {(FIFO_DATA_WIDTH){1'b0}};
	else if (FIFO_write_en && (!FIFO_full))
		FIFO_MEM[FIFO_write_addr] <= FIFO_write_data;
	else
		FIFO_MEM[FIFO_write_addr] <= FIFO_MEM[FIFO_write_addr];
end

// FIFO READ ADDR INCREASE WHEN DATA IS READ OUT FROM FIFO IN READ DOMAIN
// FIFO WRITE ADDR INCREASE WHEN DATA IS WRITE INTO FIFO IN WRITE DOMAIN
always @ (posedge FIFO_read_clk)
begin
	if (!read_rst_n_2)	
		FIFO_read_addr <= {(FIFO_ADDR_WIDTH+1){1'b0}};
	else if (FIFO_read_en && (!FIFO_empty))
		FIFO_read_addr <= FIFO_read_addr + 1;
	else
		FIFO_read_addr <= FIFO_read_addr;
end
always @ (posedge FIFO_write_clk)
begin
	if (!write_rst_n_2)
		FIFO_write_addr <= {(FIFO_ADDR_WIDTH+1){1'b0}};
	else if (FIFO_write_en && (!FIFO_full))
		FIFO_write_addr <= FIFO_write_addr + 1;
	else 
		FIFO_write_addr <= FIFO_write_addr;
end

// TRANSFER FIFO_write_addr FROM 8421 BIN CODE TO GREY CODE
// TRANSDER FIFO_read_addr FROM 8421 BIN CODE TO GREY CODE
// THE WAYS IS TO MOVE 8421 CODE TO RIGHT SIDE OF ONE BIT, AND MSB WILL BE FILL BY 0;
// THEN (^) WILL BE USED BETWEEN FORMER AND LATER TO GET GREY CODE 
assign 	FIFO_read_addr_grey_code 	= ((FIFO_read_addr >> 1) ^ FIFO_read_addr); 
assign 	FIFO_write_addr_grey_code 	= ((FIFO_write_addr >> 1) ^ FIFO_write_addr); 

// TRANSFER FIFO_write_addr_grey_code TO READ CLOCK DOMAIN THROUGH TWO LEVEL REG
// TRANSFER FIFO_read_addr_grey_code TO WRITE CLOCK DOMAIN THROUGH TWO LEVEL REG
always @ (posedge FIFO_read_clk)
begin
	if (!read_rst_n_2)
		begin
			FIFO_write_addr_to_read_1 <= {(FIFO_ADDR_WIDTH+1){1'b0}};
			FIFO_write_addr_to_read_2 <= {(FIFO_ADDR_WIDTH+1){1'b0}};
		end
	else 
		begin
			FIFO_write_addr_to_read_1 <= FIFO_write_addr_grey_code;
			FIFO_write_addr_to_read_2 <= FIFO_write_addr_to_read_1;
		end
end
always @ (posedge FIFO_write_clk)
begin
	if (!write_rst_n_2)
		begin
			FIFO_read_addr_to_write_1 <= {(FIFO_ADDR_WIDTH+1){1'b0}};
			FIFO_read_addr_to_write_2 <= {(FIFO_ADDR_WIDTH+1){1'b0}};
		end
	else 
		begin
			FIFO_read_addr_to_write_1 <= FIFO_read_addr_grey_code;
			FIFO_read_addr_to_write_2 <= FIFO_read_addr_to_write_1;
		end
end

// ACCORDING TO THE READ ADDRESS GREY CODE AND WRITE ADDRESS GREY CODE IN READ CLOCK DOMAIN TO DETERMIN FIFO ENPTY
// ACCORDING TO THE READ ADDRESS GREY CODE AND WRITE ADDRESS GREY CODE IN WRITE CLOCK DOMAIN TO DETERMIN FIFO FULL
// FIFO EMPTY WHEN READ ADDRESS GREY CODE IS THE SAME AS WRITE ADDRESS GREY CODE
// FIFO FULL WHEN THE MOST SIGNIFICENT TWO CODE OF WRITE AND READ ADDRESS GREY CODE ARE BOTH DIFFERENT AND THE OTHERS ARE THE SAME 
assign	FIFO_empty 	=	 	(FIFO_read_addr_grey_code == FIFO_write_addr_to_read_2);
assign 	FIFO_full 	=	 	((!(FIFO_write_addr_grey_code[FIFO_ADDR_WIDTH] 
								==     FIFO_read_addr_to_write_2[FIFO_ADDR_WIDTH])) 
							&&		 (!(FIFO_write_addr_grey_code[FIFO_ADDR_WIDTH-1] 
								==     FIFO_read_addr_to_write_2[FIFO_ADDR_WIDTH-1])) 
							&& 	(   FIFO_write_addr_grey_code[FIFO_ADDR_WIDTH -2:0] 
								==     FIFO_read_addr_to_write_2[FIFO_ADDR_WIDTH -2:0]));
							
assign  	FIFO_read_err = 		FIFO_empty && FIFO_read_en;
assign 	FIFO_write_err = 		FIFO_full && FIFO_write_en;
endmodule


4.4 、优缺点分析

优点:读写可以处于不同的时钟域,从而分割模块设计,之后通过FIFO连接起来就行

缺点:由于采用了两级寄存器来在不同时钟域之间做同步化处理,实际上地址信号的传递慢了两个时钟周期,对于低速信号尚可以接受,但是高速信号就不太好了。


总结

本文从单端口RAM、双端口RAM、同步FIFO、异步FIFO出发,分析各个模块的应用场景、代码实现及优缺点分析。

  • 12
    点赞
  • 120
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值