小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

異步FIFO介紹

 依米大人 2023-02-16 發(fā)布于北京

??異步FIFO的寫時(shí)鐘和讀時(shí)鐘為異步時(shí)鐘,F(xiàn)IFO內(nèi)部的寫邏輯和讀邏輯的交互需要異步處理,異步FIFO常用于跨時(shí)鐘域交互。異步FIFO的設(shè)計(jì)重點(diǎn)有兩個(gè),第一個(gè)就是跨時(shí)鐘域的轉(zhuǎn)換及同步;第二個(gè)就是狀態(tài)信號的產(chǎn)生采用相對保守的方式。

異步FIFO接口

異步 FIFO的輸入/輸出 ,如下圖所示:
在這里插入圖片描述
異步FIFO端口說明:

  • wclk : 該時(shí)鐘為異步FIFO寫操作的工作時(shí)鐘。

  • w_rst_ : 該信號為異步FIFO的寫時(shí)鐘域復(fù)位信號,低電平有效。

  • wren : 該信號為異步FIFO的寫使能。

  • full : 該信號為FIFO已滿標(biāo)志。如果 FIFO 為滿狀態(tài),則禁止再寫數(shù)據(jù)。

  • wdata : 該總線為寫數(shù)據(jù)總線。

  • rclk : 該時(shí)鐘為異步FIFO讀操作的工作時(shí)鐘。

  • r_rst_: 該信號為異步FIFO的讀時(shí)鐘域復(fù)位信號,低電平有效。

  • rden: 該信號為異步FIFO的讀使能。

  • empty: 該信號為異步FIFO的空標(biāo)志。

  • rdata: 該總線為讀數(shù)據(jù)總線。

? ?FIFO還提供其他標(biāo)識,如almost_full和almost_empty,用于提供關(guān)于FIFO再寫人多少會滿以及再讀出多少會空的信息。

異步FIFO結(jié)構(gòu)

? ?異步FIFO基本上分為7個(gè)部分,分別是寫時(shí)鐘域的地址管理、讀時(shí)鐘域的地址管理、讀時(shí)鐘域讀地址到寫時(shí)鐘域的格雷碼方式同步、寫時(shí)鐘域?qū)懙刂返阶x時(shí)鐘域的格雷碼方式同步、寫時(shí)鐘域的滿和將滿信號的產(chǎn)生、讀時(shí)鐘域的空和將空信號產(chǎn)生以及FIFO Memory。
? ?
在這里插入圖片描述

異步FIFO設(shè)計(jì)思想

? ?復(fù)位后,讀和寫地址指針均指在0地址處,同時(shí)almost empty 和empty信號均有效。
? ?在寫時(shí)鐘域,當(dāng)寫請求wren有效時(shí),如果此時(shí)寫時(shí)鐘域的狀態(tài)正常(即還未滿),則RAM寫使能信號wr_ram有效,數(shù)據(jù)將會寫入RAM里,同時(shí)在下一個(gè)clk原來的寫地址加1,指向下一個(gè)寫地址。
? ?在讀時(shí)鐘域側(cè),當(dāng)讀請求rden有效時(shí),如果此時(shí)讀時(shí)鐘域的狀態(tài)正常(即還未空),則RAM讀使能信號rd_ram有效,數(shù)據(jù)將會從RAM里被讀出,同時(shí)在下一個(gè)clk原來的讀地址加1,指向下一個(gè)讀地址。
? ?在寫時(shí)鐘域FIFO狀態(tài)的判斷及產(chǎn)生是使用了格雷碼同步的方法實(shí)現(xiàn)多比特跨時(shí)鐘域的轉(zhuǎn)換、把讀地址從讀時(shí)鐘rd_clk同步到wr_clk,然后判斷寫地址和讀地址之間的差從而判斷FIFO是否被寫滿。
? ?在讀時(shí)鐘域FIFO狀態(tài)的判斷及產(chǎn)生也使用了格雷碼同步的方法實(shí)現(xiàn)多比特跨時(shí)鐘域的轉(zhuǎn)換、把寫地址從寫時(shí)鐘wr_clk到rd_clk的轉(zhuǎn)換,然后判斷寫地址和讀地址之差進(jìn)而得出FIFO是否為空的結(jié)論。

雙口RAM接口

? ?在 FIFO 中常用的RAM包括單口RAM、簡單雙口RAM、真雙口RAM、單口ROM、雙口ROM這5種類型的RAM,也可以使用寄存器來實(shí)現(xiàn)FIFO的存儲器。寄存器實(shí)現(xiàn)的方式比較適合深度和位寬較小的情況,一般在FPGA里還是推薦使用Block RAM來實(shí)現(xiàn)異步FIFO的存儲器。這里介紹簡單雙口RAM來做FIFO內(nèi)部的RAM,雙口RAM的接口如下圖所示。
在這里插入圖片描述
雙口RAM端口說明:

  1. wclk : 該時(shí)鐘為雙口RAM寫操作的工作時(shí)鐘。

  2. wren : 該信號為雙口RAM的寫使能。

  3. waddr : 該總線為雙口RAM的寫地址總線。

  4. wdata : 該總線為雙口RAM的寫數(shù)據(jù)總線。

  5. rclk : 該時(shí)鐘為雙口RAM讀操作的工作時(shí)鐘。

  6. rden : 該信號為雙口RAM的讀使能。

  7. raddr : 該總線為雙口RAM的讀地址總線。

  8. rdata: 該總線為雙口RAM的讀數(shù)據(jù)總線。

實(shí)現(xiàn)代碼

module  dual_port_RAM #(
    parameter DEPTH = 16,
    parameter WIDTH = 8
)(
	input                           wclk ,   //寫數(shù)據(jù)時(shí)鐘
	input                           wenc ,   //寫使能
	input    [$clog2(DEPTH)-1:0]    waddr,   //寫地址
	input    [WIDTH-1:0]            wdata,   //輸入數(shù)據(jù)
	input                           rclk ,   //讀數(shù)據(jù)時(shí)鐘
	input                           renc ,   //讀使能
	input    [$clog2(DEPTH)-1:0]    raddr,   //讀地址
	output  reg    [WIDTH-1:0]      rdata 	 //輸出數(shù)據(jù)
);


    /********************參數(shù)定義********************/
    
    /*********************IO 說明********************/

    /********************** 內(nèi)部信號聲明 **********************/
    reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];  //RAM大小定義
	
    /*************************功能定義*************************/	
    //RAM寫操作
    always @(posedge wclk) 
    begin
    	if(wenc)
    		RAM_MEM[waddr] <= wdata;
    end 
  
	//RAM讀操作
    always @(posedge rclk) 
    begin
    	if(renc)
    		rdata <= RAM_MEM[raddr];
    end 
    
    endmodule

地址管理

寫時(shí)鐘域的地址管理

? ?主要在寫時(shí)鐘域中產(chǎn)生FIFO Memory寫地址和寫有效信號,當(dāng)寫使能有效且FIFO沒有寫滿時(shí),F(xiàn)IFO寫地址的遞增應(yīng)。實(shí)現(xiàn)代碼如下:

	always@(posedge wclk or negedge w_rst_)
	begin
	    if(!w_rst_)
		    w_bin_waddr <= {(ADDR_WIDTH+1){1'b0}};
		else if (wren && !full)
			w_bin_waddr <= w_bin_waddr + 1'd1;
		else
		    w_bin_waddr <= w_bin_waddr;
	end

讀時(shí)鐘域的地址管理

? ?讀控制邏輯主要產(chǎn)生FIFO Memory讀地址和讀有效信號,當(dāng)讀使能有效且FIFO沒有讀空時(shí),F(xiàn)IFO讀地址的遞增應(yīng)。實(shí)現(xiàn)代碼如下:

	always@(posedge rclk or negedge r_rst_)
	begin
	    if(!r_rst_)
		    r_bin_raddr <= {(ADDR_WIDTH+1){1'b0}};
		else if (rden && !empty)
			r_bin_raddr <= r_bin_raddr + 1'd1;
		else
		    r_bin_raddr <= r_bin_raddr;
	end

格雷碼方式同步

格雷碼

二進(jìn)制碼轉(zhuǎn)換為格雷編通用電路

? ? 二進(jìn)制碼轉(zhuǎn)換成二進(jìn)制格雷碼,其法則是保留二進(jìn)制碼的最高位作為格雷碼的最高位,而次高位格雷碼為二進(jìn)制碼的高位與次高位相異或,而格雷碼其余各位與次高位的求法相類似。N位二進(jìn)制碼到N位格雷碼的生成公式為:
{ G i = B i ? B i + 1     ( i = 0 , 1 , . . . , n ? 2 ) G n ? 1 = B n ? 1

{Gi=Bi?Bi+1   (i=0,1,...,n?2)Gn?1=Bn?1{Gi=Bi?Bi+1   (i=0,1,...,n?2)Gn?1=Bn?1
{Gi=Bi?Bi+1   (i=0,1,...,n?2)Gn?1=Bn?1
實(shí)現(xiàn)代碼如下:
module  binary_to_gray#(parameter	WIDTH = 8)
(
	input 	  [WIDTH-1:0] 	 binary_value,  //二進(jìn)制編碼數(shù)值

	output    [WIDTH-1:0]    gray_value     //格雷編碼數(shù)值
);

	/*************************功能定義*************************/	
	generate 
	    genvar i;
		for(i=0;i<(WIDTH-1);i=i+1)
		begin: gray
		    assign    gray_value[i] = binary_value[i]^binary_value[i+1];
		end
    endgenerate
	
	assign   gray_value[WIDTH-1] = binary_value[WIDTH-1];
	
endmodule

格雷編轉(zhuǎn)換為二進(jìn)制碼通用電路

? ?二進(jìn)制格雷碼轉(zhuǎn)換成二進(jìn)制碼,其法則是保留格雷碼的最高位作為自然二進(jìn)制碼的最高位,而次高位自然二進(jìn)制碼為高位自然二進(jìn)制碼與次高位格雷碼相異或,而自然二進(jìn)制碼的其余各位與次高位自然二進(jìn)制碼的求法相類似。
{ B n ? 1 = G n ? 1 B i = B i + 1 ? G i     ( i = n ? 2 , . . . , 1 )

{Bn?1=Gn?1Bi=Bi+1?Gi   (i=n?2,...,1){Bn?1=Gn?1Bi=Bi+1?Gi   (i=n?2,...,1)
{Bn?1=Gn?1Bi=Bi+1?Gi   (i=n?2,...,1)
實(shí)現(xiàn)代碼如下:
module  gray_to_binary #(parameter	WIDTH = 8)
(
	input 	  [WIDTH-1:0] 	 gray_value,     //格雷編碼數(shù)值

	output    [WIDTH-1:0]    binary_value    //二進(jìn)制編碼數(shù)值
);


	/*************************功能定義*************************/	
    genvar i;
	generate
		for(i=0;i<(WIDTH-1);i=i+1)
		begin: binary
		    assign    binary_value[i] = gray_value[i]^binary_value[i+1];
		end
    endgenerate 
	
	assign   binary_value[WIDTH-1] = gray_value[WIDTH-1];
	
endmodule

格雷碼方式同步原理

? ?由于異步FIFO讀寫時(shí)鐘非同一個(gè),那么寫地址計(jì)數(shù)值在從寫時(shí)鐘域往讀時(shí)鐘域和讀地址計(jì)數(shù)器在從讀時(shí)鐘域往寫時(shí)鐘域傳遞的過程中會存在重匯聚(re-convergence)的問題。異步FIFO的地址進(jìn)行跨時(shí)鐘域的電路示意圖如下:
? ?
在這里插入圖片描述
時(shí)序圖如下所示在這里插入圖片描述
? ?當(dāng)FIFO地址的多bit信號在源時(shí)鐘域同時(shí)翻轉(zhuǎn)時(shí),在夸時(shí)鐘域傳輸時(shí),由于線路延時(shí)的不同(Delay 0 和Delay 1 不同),造成各bit信號到達(dá)目的時(shí)鐘域寄存器的時(shí)間不一致,一旦目的時(shí)鐘正好在他們錯(cuò)開的時(shí)刻進(jìn)行采樣,就會出現(xiàn)錯(cuò)誤。如上圖中,本來傳遞過來的正確信號是11,但最后在目的時(shí)鐘域采樣成了10。
? ?而為了解決這樣的問題就需要在進(jìn)行跨時(shí)鐘域之前將信號變成格雷碼,但是必須保證進(jìn)行跨時(shí)鐘域之前的信號對應(yīng)的二進(jìn)制在源時(shí)鐘域有效沿每次只會加1,這樣其對應(yīng)的格雷碼才會保證每次只有1bit翻轉(zhuǎn)。格雷碼常用于總線地址信號,memory地址信號的跨時(shí)鐘域傳輸。
? ?下圖為格雷碼與二進(jìn)制編碼的對應(yīng)關(guān)系??梢钥吹蕉M(jìn)制編碼相鄰轉(zhuǎn)換會出現(xiàn)同時(shí)多bit翻轉(zhuǎn)的時(shí)候,比如從0001->0010,低兩位同時(shí)翻轉(zhuǎn);但是格雷碼相鄰狀態(tài)轉(zhuǎn)換每次只有一位發(fā)生翻轉(zhuǎn),就算因?yàn)檠訒r(shí)問題出現(xiàn)出現(xiàn)采用錯(cuò)誤,也只會是出現(xiàn)一種錯(cuò)誤。例如二進(jìn)碼從0001跳轉(zhuǎn)到0010,對應(yīng)的格雷碼是從0001跳變到0011,只有第二位發(fā)生了翻轉(zhuǎn),其在目的時(shí)鐘域(讀時(shí)鐘域)被采樣只有兩種可能,分別是0001,0011。如果被采到了0001也不會出錯(cuò),頂多就是在讀時(shí)鐘域覺得沒有新數(shù)據(jù)寫入進(jìn)去(因?yàn)閭鬟^來的寫地址沒有變),但后來這個(gè)新的地址總會正確到來。只是滿一點(diǎn)而已,不會造成異步FIFO的數(shù)據(jù)流錯(cuò)亂或數(shù)據(jù)丟失。
在這里插入圖片描述
? ?Gray編碼可以有效解決異步FIFO讀/寫地址采樣不穩(wěn)定的問題,提高系統(tǒng)的可靠性;缺點(diǎn)是有延遲(可提前判斷來補(bǔ)償),電路門數(shù)、復(fù)雜度增加。但是,用Gray編碼來解決該問題也是異步FIFO設(shè)計(jì)最常用的方法。

寫時(shí)鐘域?qū)懙刂返阶x時(shí)鐘域的格雷碼方式同步代碼實(shí)現(xiàn)

	binary_to_gray #(.WIDTH(ADDR_WIDTH+1))
	binary_to_gray_U1
	(
	    .binary_value    (w_bin_waddr),
	    .gray_value      (w_gray_waddr) 
	);
    //寫時(shí)鐘域寄存
	always@(posedge wclk or negedge w_rst_)
	begin
	    if(!w_rst_)
		    w_gray_waddr_reg <= {(ADDR_WIDTH+1){1'b0}} ;
		else
		    w_gray_waddr_reg <= w_gray_waddr;
	
	
	end
	//讀時(shí)鐘域兩級同步
	always@(posedge rclk or negedge r_rst_)
	begin
	    if(!r_rst_)
		begin
		    r_gray_waddr_sync1 <= {(ADDR_WIDTH+1){1'b0}} ;
			r_gray_waddr_sync2 <= {(ADDR_WIDTH+1){1'b0}} ;
		end
		else
		begin
		    r_gray_waddr_sync1 <= w_gray_waddr_reg ;
			r_gray_waddr_sync2 <= r_gray_waddr_sync1 ;
		end
	end

讀時(shí)鐘域讀地址到寫時(shí)鐘域的格雷碼方式同步代碼實(shí)現(xiàn)

//二進(jìn)制碼轉(zhuǎn)格雷碼
	binary_to_gray #(.WIDTH(ADDR_WIDTH+1))
	binary_to_gray_U2
	(
	    .binary_value    (r_bin_raddr),
	    .gray_value      (r_gray_raddr)
	);
    //讀時(shí)鐘域寄存
	always@(posedge rclk or negedge r_rst_)
	begin
	    if(!r_rst_)
		    r_gray_raddr_reg <= {(ADDR_WIDTH+1){1'b0}} ;
		else
		    r_gray_raddr_reg <= r_gray_raddr;
	
	
	end
	//寫時(shí)鐘域兩級同步
	always@(posedge wclk or negedge w_rst_)
	begin
	    if(!w_rst_)
		begin
		    w_gray_raddr_sync1 <= {(ADDR_WIDTH+1){1'b0}} ;
			w_gray_raddr_sync2 <= {(ADDR_WIDTH+1){1'b0}} ;
		end
		else
		begin
		    w_gray_raddr_sync1 <= r_gray_raddr_reg ;
			w_gray_raddr_sync2 <= w_gray_raddr_sync1 ;
		end
	end	

空滿標(biāo)志判斷

? ?當(dāng)讀寫地址指針在復(fù)位操作期間被重置為零時(shí),或者當(dāng)讀指針在從FIFO中讀取了最后一個(gè)字之后趕上寫指針時(shí),此時(shí)讀指針和寫指針相等代表著FIFO為讀空。而當(dāng)寫指針再次追趕上讀指針時(shí),此時(shí)讀指針和寫指針相等代表著FIFO為寫滿。也就是說當(dāng)讀寫指針相等時(shí),F(xiàn)IFO要么為空,要么為滿。
? ?因此將地址位擴(kuò)展一位,地址為二進(jìn)制編碼時(shí),則用最高位來判斷空滿,其余低位還是正常用于讀寫地址索引。當(dāng)寫指針遞增超過FIFO的最大地址時(shí),寫指針的MSB位將置為1,同時(shí)將其余低位設(shè)置回零。讀指針也是如此。如果讀指針和寫指針的MSB不同,則意味著寫指針比讀指針多繞了一次,表示FIFO寫滿。如果兩個(gè)指針的MSB相同,則表示兩個(gè)指針的回繞次數(shù)相同,表示FIFO讀空。采用二進(jìn)制碼時(shí)電路結(jié)構(gòu)如下所示:
在這里插入圖片描述
當(dāng)判斷almost_full(FIFO再寫人多少會滿)和almost_empty(FIFO再讀出多少會空)標(biāo)志時(shí),
當(dāng)未回卷時(shí)(即讀寫地址最高位相同):未寫入數(shù)據(jù)的地址數(shù)量 = FIFO深度 + radder - waddr
當(dāng)回卷時(shí)(即讀寫地址最高位不相同):未寫入數(shù)據(jù)的地址數(shù)量 = radder[N-2:0 ]- wadder[N-2:0],其中[N-2:0]代表除去最高位,剩余的低N-1位;
當(dāng)未回卷時(shí)(即讀寫地址最高位相同):寫入數(shù)據(jù)的地址數(shù)量 = wadder - raddr
當(dāng)回卷時(shí)時(shí)(即讀寫地址最高位不相同):寫入數(shù)據(jù)的地址數(shù)量 = FIFO深度 +wadder[N-2:0 ]- radder[N-2:0],其中[N-2:0]代表除去最高位,剩余的低N-1位;
在這里插入圖片描述

? ?若不產(chǎn)生almost_full(FIFO再寫人多少會滿)和almost_empty(FIFO再讀出多少會空)標(biāo)志。則可以在將地址擴(kuò)展一位后,用地址為格雷碼格雷碼編碼時(shí),當(dāng)寫地址和讀地址的格雷碼的最高位和次高位相反,其余低位相同時(shí),表示FIFO寫滿,當(dāng)寫地址和地讀地址的格雷碼的相同時(shí),表示FIFO讀空。采用格雷碼時(shí)電路結(jié)構(gòu)如下所示:
在這里插入圖片描述

空滿標(biāo)志判斷代碼:

這里采用讀寫地址的格雷碼進(jìn)行比較,判斷空滿標(biāo)志。實(shí)現(xiàn)代碼如下所示:

	/*寫滿標(biāo)志判斷*/
	assign full = ({~w_gray_waddr_reg[ADDR_WIDTH:ADDR_WIDTH-1],w_gray_waddr_reg[ADDR_WIDTH-2:0]} == w_gray_raddr_sync2)?1'b1:1'b0;
	
	/*讀空標(biāo)志判斷*/
	assign empty = (r_gray_raddr_reg == r_gray_waddr_sync2)? 1'b1:1'b0;

異步FIFO代碼

實(shí)現(xiàn)一

不產(chǎn)生almost_full(FIFO再寫人多少會滿)和almost_empty(FIFO再讀出多少會空)標(biāo)志采用讀寫地址的格雷碼進(jìn)行比較,判斷空滿標(biāo)志;

module  async_fifo #(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					wclk    , //寫時(shí)鐘
	input                   rclk    , //讀時(shí)鐘
	input 					w_rst_ 	, //寫時(shí)鐘域異步復(fù)位,低電平有效
	input 					r_rst_ 	, //寫時(shí)鐘域異步復(fù)位,低電平有效	
	input 					wren	, //寫使能
	input 			 		rden	, //寫使能
	input 		[WIDTH-1:0]	wdata	, //寫數(shù)據(jù)

	output wire				full	, //寫滿信號
	output wire				empty	, //讀空信號
	output wire [WIDTH-1:0]	rdata     //讀數(shù)據(jù)
);

    /********************參數(shù)定義********************/

    /*********************IO 說明********************/

	/********************** 內(nèi)部信號聲明 **********************/
	localparam    ADDR_WIDTH = $clog2(DEPTH);   //地址位寬
	
	wire                        wenc              ;       //雙端口RAM寫使能
	wire                        renc              ;  	  //雙端口RAM讀使能
	reg     [ADDR_WIDTH:0]      w_bin_waddr       ;       //寫地址(二進(jìn)制)
	reg     [ADDR_WIDTH:0]      r_bin_raddr       ;       //讀地址(二進(jìn)制)
    wire    [ADDR_WIDTH:0]      w_gray_waddr      ;       //寫地址(格雷碼)
    wire    [ADDR_WIDTH:0]      r_gray_raddr      ;       //讀地址(格雷碼)
	reg     [ADDR_WIDTH:0]      w_gray_waddr_reg  ;       //寫地址(格雷碼)寫時(shí)鐘域暫存寄存器
	reg     [ADDR_WIDTH:0]      r_gray_raddr_reg  ;       //讀地址(格雷碼)讀時(shí)鐘域暫存寄存器
	reg     [ADDR_WIDTH:0]      r_gray_waddr_sync1;       //寫地址(格雷碼)寫時(shí)鐘域到讀時(shí)鐘域第一級同步
	reg     [ADDR_WIDTH:0]      r_gray_waddr_sync2;       //寫地址(格雷碼)寫時(shí)鐘域到讀時(shí)鐘域第二級同步
	reg     [ADDR_WIDTH:0]      w_gray_raddr_sync1;       //讀地址(格雷碼)讀時(shí)鐘域到寫時(shí)鐘域第一級同步
	reg     [ADDR_WIDTH:0]      w_gray_raddr_sync2;       //讀地址(格雷碼)讀時(shí)鐘域到寫時(shí)鐘域第二級同步
	/*************************功能定義*************************/	
    
	assign    wenc = wren && !full;
	assign    renc = rden && !empty;
	
	/*雙端口RAM*/
    dual_port_RAM #(.DEPTH(DEPTH),.WIDTH(WIDTH))
	dual_port_RAM_U1
	(
	    .wclk     (wclk       ),   //寫數(shù)據(jù)時(shí)鐘
	    .wenc     (wenc       ),   //寫使能
	    .waddr    (w_bin_waddr),   //寫地址
	    .wdata    (wdata      ),   //輸入數(shù)據(jù)
	    .rclk     (rclk       ),   //讀數(shù)據(jù)時(shí)鐘
	    .renc     (renc       ),   //讀使能
	    .raddr    (r_bin_raddr),   //讀地址
	    .rdata    (rdata      )    //輸出數(shù)據(jù)
    );
   
   /*寫控制邏輯*/
	always@(posedge wclk or negedge w_rst_)
	begin
	    if(!w_rst_)
		    w_bin_waddr <= {(ADDR_WIDTH+1){1'b0}};
		else if (wren && !full)
			w_bin_waddr <= w_bin_waddr + 1'd1;
		else
		    w_bin_waddr <= w_bin_waddr;
	end
		
	/*讀控制邏輯*/
	always@(posedge rclk or negedge r_rst_)
	begin
	    if(!r_rst_)
		    r_bin_raddr <= {(ADDR_WIDTH+1){1'b0}};
		else if (rden && !empty)
			r_bin_raddr <= r_bin_raddr + 1'd1;
		else
		    r_bin_raddr <= r_bin_raddr;
	end
	
	/*寫地址從寫時(shí)鐘域到讀時(shí)鐘域的格雷碼方式同步*/
	//二進(jìn)制碼轉(zhuǎn)格雷碼
	binary_to_gray #(.WIDTH(ADDR_WIDTH+1))
	binary_to_gray_U1
	(
	    .binary_value    (w_bin_waddr),
	    .gray_value      (w_gray_waddr) 
	);
    //寫時(shí)鐘域寄存
	always@(posedge wclk or negedge w_rst_)
	begin
	    if(!w_rst_)
		    w_gray_waddr_reg <= {(ADDR_WIDTH+1){1'b0}} ;
		else
		    w_gray_waddr_reg <= w_gray_waddr;
	
	
	end
	//讀時(shí)鐘域兩級同步
	always@(posedge rclk or negedge r_rst_)
	begin
	    if(!r_rst_)
		begin
		    r_gray_waddr_sync1 <= {(ADDR_WIDTH+1){1'b0}} ;
			r_gray_waddr_sync2 <= {(ADDR_WIDTH+1){1'b0}} ;
		end
		else
		begin
		    r_gray_waddr_sync1 <= w_gray_waddr_reg ;
			r_gray_waddr_sync2 <= r_gray_waddr_sync1 ;
		end
	end
	
	/*讀時(shí)鐘域讀地址到寫時(shí)鐘域的格雷碼方式同步*/
	//二進(jìn)制碼轉(zhuǎn)格雷碼
	binary_to_gray #(.WIDTH(ADDR_WIDTH+1))
	binary_to_gray_U2
	(
	    .binary_value    (r_bin_raddr),
	    .gray_value      (r_gray_raddr)
	);
    //讀時(shí)鐘域寄存
	always@(posedge rclk or negedge r_rst_)
	begin
	    if(!r_rst_)
		    r_gray_raddr_reg <= {(ADDR_WIDTH+1){1'b0}} ;
		else
		    r_gray_raddr_reg <= r_gray_raddr;
	
	
	end
	//寫時(shí)鐘域兩級同步
	always@(posedge wclk or negedge w_rst_)
	begin
	    if(!w_rst_)
		begin
		    w_gray_raddr_sync1 <= {(ADDR_WIDTH+1){1'b0}} ;
			w_gray_raddr_sync2 <= {(ADDR_WIDTH+1){1'b0}} ;
		end
		else
		begin
		    w_gray_raddr_sync1 <= r_gray_raddr_reg ;
			w_gray_raddr_sync2 <= w_gray_raddr_sync1 ;
		end
	end
	
	/*寫滿標(biāo)志判斷*/
	assign full = ({~w_gray_waddr_reg[ADDR_WIDTH:ADDR_WIDTH-1],w_gray_waddr_reg[ADDR_WIDTH-2:0]} == w_gray_raddr_sync2)?1'b1:1'b0;
	
	/*讀空標(biāo)志判斷*/
	assign empty = (r_gray_raddr_reg == r_gray_waddr_sync2)? 1'b1:1'b0;
	
endmodule

實(shí)現(xiàn)二

產(chǎn)生almost_full(FIFO再寫人多少會滿)和almost_empty(FIFO再讀出多少會空)標(biāo)志采用讀寫地址的二進(jìn)制碼進(jìn)行比較,判斷空滿標(biāo)志;

module  async_fifo #(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16,
	parameter 	ALMOST_FULL_GAP = 3,  //離滿還有ALMOST_FULL_GAP時(shí),almost_full有效
	parameter 	ALMOST_EMPTY_GAP = 3  //離滿還有ALMOST_EMPTY_GAP時(shí),almost_empty有效
)(
	input 					wclk        , //寫時(shí)鐘
	input                   rclk        , //讀時(shí)鐘
	input 					w_rst_ 	    , //寫時(shí)鐘域異步復(fù)位,低電平有效
	input 					r_rst_ 	    , //寫時(shí)鐘域異步復(fù)位,低電平有效	
	input 					wren	    , //寫使能
	input 			 		rden	    , //寫使能
	input 		[WIDTH-1:0]	wdata	    , //寫數(shù)據(jù)
    
	output wire				almost_full	, //將滿信號
	output wire				almost_empty, //將空信號
	output wire				full	    , //寫滿信號
	output wire				empty	    , //讀空信號
	output wire [WIDTH-1:0]	rdata         //讀數(shù)據(jù)
);

    /********************參數(shù)定義********************/

    /*********************IO 說明********************/

	/********************** 內(nèi)部信號聲明 **********************/
	localparam    ADDR_WIDTH = $clog2(DEPTH);   //地址位寬
	
	wire                        wenc              ;       //雙端口RAM寫使能
	wire                        renc              ;  	  //雙端口RAM讀使能
	reg     [ADDR_WIDTH:0]      w_bin_waddr       ;       //寫地址(二進(jìn)制)
	reg     [ADDR_WIDTH:0]      r_bin_raddr       ;       //讀地址(二進(jìn)制)
    wire    [ADDR_WIDTH:0]      w_gray_waddr      ;       //寫地址(格雷碼)
    wire    [ADDR_WIDTH:0]      r_gray_raddr      ;       //讀地址(格雷碼)
	reg     [ADDR_WIDTH:0]      w_gray_waddr_reg  ;       //寫地址(格雷碼)寫時(shí)鐘域暫存寄存器
	reg     [ADDR_WIDTH:0]      r_gray_raddr_reg  ;       //讀地址(格雷碼)讀時(shí)鐘域暫存寄存器
	reg     [ADDR_WIDTH:0]      r_gray_waddr_sync1;       //寫地址(格雷碼)寫時(shí)鐘域到讀時(shí)鐘域第一級同步
	reg     [ADDR_WIDTH:0]      r_gray_waddr_sync2;       //寫地址(格雷碼)寫時(shí)鐘域到讀時(shí)鐘域第二級同步
	reg     [ADDR_WIDTH:0]      w_gray_raddr_sync1;       //讀地址(格雷碼)讀時(shí)鐘域到寫時(shí)鐘域第一級同步
	reg     [ADDR_WIDTH:0]      w_gray_raddr_sync2;       //讀地址(格雷碼)讀時(shí)鐘域到寫時(shí)鐘域第二級同步
	wire    [ADDR_WIDTH:0]      r_bin_waddr       ;       //讀時(shí)鐘域二進(jìn)制寫地址
	wire    [ADDR_WIDTH:0]      w_bin_raddr       ;       //寫時(shí)鐘域二進(jìn)制讀地址
	reg     [ADDR_WIDTH:0]      w_bin_raddr_reg   ;       //寫時(shí)鐘域讀地址(二進(jìn)制)暫存寄存器
	reg     [ADDR_WIDTH:0]      r_bin_waddr_reg   ;       //讀時(shí)鐘域?qū)懙刂?二進(jìn)制)暫存寄存器
    reg     [ADDR_WIDTH:0]      room_avail        ;       //FIFO內(nèi)剩余空間
	reg     [ADDR_WIDTH:0]      data_avail        ;       //FIFO內(nèi)已存入數(shù)據(jù)個(gè)數(shù)
	
	/*************************功能定義*************************/	
    /*RAM寫使能*/
	assign    wenc = wren && !full;
	/*RAM讀使能*/
	assign    renc = rden && !empty;
	
	/*雙端口RAM*/
    dual_port_RAM #(.DEPTH(DEPTH),.WIDTH(WIDTH))
	dual_port_RAM_U1
	(
	    .wclk     (wclk       ),   //寫數(shù)據(jù)時(shí)鐘
	    .wenc     (wenc       ),   //寫使能
	    .waddr    (w_bin_waddr),   //寫地址
	    .wdata    (wdata      ),   //輸入數(shù)據(jù)
	    .rclk     (rclk       ),   //讀數(shù)據(jù)時(shí)鐘
	    .renc     (renc       ),   //讀使能
	    .raddr    (r_bin_raddr),   //讀地址
	    .rdata    (rdata      )    //輸出數(shù)據(jù)
    );
   
   /*寫控制邏輯*/
	always@(posedge wclk or negedge w_rst_)
	begin
	    if(!w_rst_)
		    w_bin_waddr <= {(ADDR_WIDTH+1){1'b0}};
		else if (wren && !full)
			w_bin_waddr <= w_bin_waddr + 1'd1;
		else
		    w_bin_waddr <= w_bin_waddr;
	end
		
	/*讀控制邏輯*/
	always@(posedge rclk or negedge r_rst_)
	begin
	    if(!r_rst_)
		    r_bin_raddr <= {(ADDR_WIDTH+1){1'b0}};
		else if (rden && !empty)
			r_bin_raddr <= r_bin_raddr + 1'd1;
		else
		    r_bin_raddr <= r_bin_raddr;
	end
	
	/*寫地址從寫時(shí)鐘域到讀時(shí)鐘域的格雷碼方式同步*/
	//二進(jìn)制碼轉(zhuǎn)格雷碼
	binary_to_gray #(.WIDTH(ADDR_WIDTH+1))
	binary_to_gray_U1
	(
	    .binary_value    (w_bin_waddr),
	    .gray_value      (w_gray_waddr) 
	);
    //寫時(shí)鐘域?qū)懙刂罚ǜ窭状a)寄存
	always@(posedge wclk or negedge w_rst_)
	begin
	    if(!w_rst_)
		    w_gray_waddr_reg <= {(ADDR_WIDTH+1){1'b0}} ;
		else
		    w_gray_waddr_reg <= w_gray_waddr;
	end
	//讀時(shí)鐘域兩級同步
	always@(posedge rclk or negedge r_rst_)
	begin
	    if(!r_rst_)
		begin
		    r_gray_waddr_sync1 <= {(ADDR_WIDTH+1){1'b0}} ;
			r_gray_waddr_sync2 <= {(ADDR_WIDTH+1){1'b0}} ;
		end
		else
		begin
		    r_gray_waddr_sync1 <= w_gray_waddr_reg ;
			r_gray_waddr_sync2 <= r_gray_waddr_sync1 ;
		end
	end
	//格雷碼轉(zhuǎn)二進(jìn)制
	gray_to_binary #(.WIDTH(ADDR_WIDTH+1))
    gray_to_binary_U1
	(
	    .gray_value      (r_gray_waddr_sync2), 
	    .binary_value    (r_bin_waddr)

	);
	//讀時(shí)鐘域?qū)懙刂罚ǘM(jìn)制)寄存
	always@(posedge rclk or negedge r_rst_)
	begin
	    if(!r_rst_)
		    r_bin_waddr_reg <= {(ADDR_WIDTH+1){1'b0}} ;
		else
		    r_bin_waddr_reg <= r_bin_waddr;
	end
	
	/*讀時(shí)鐘域讀地址到寫時(shí)鐘域的格雷碼方式同步*/
	//二進(jìn)制碼轉(zhuǎn)格雷碼
	binary_to_gray #(.WIDTH(ADDR_WIDTH+1))
	binary_to_gray_U2
	(
	    .binary_value    (r_bin_raddr),
	    .gray_value      (r_gray_raddr)
	);
    //讀時(shí)鐘域寄存
	always@(posedge rclk or negedge r_rst_)
	begin
	    if(!r_rst_)
		    r_gray_raddr_reg <= {(ADDR_WIDTH+1){1'b0}} ;
		else
		    r_gray_raddr_reg <= r_gray_raddr;
	
	
	end
	//寫時(shí)鐘域兩級同步
	always@(posedge wclk or negedge w_rst_)
	begin
	    if(!w_rst_)
		begin
		    w_gray_raddr_sync1 <= {(ADDR_WIDTH+1){1'b0}} ;
			w_gray_raddr_sync2 <= {(ADDR_WIDTH+1){1'b0}} ;
		end
		else
		begin
		    w_gray_raddr_sync1 <= r_gray_raddr_reg ;
			w_gray_raddr_sync2 <= w_gray_raddr_sync1 ;
		end
	end
	//格雷碼轉(zhuǎn)二進(jìn)制
	gray_to_binary #(.WIDTH(ADDR_WIDTH+1))
    gray_to_binary_U2
	(
	    .gray_value      (w_gray_raddr_sync2), 
	    .binary_value    (w_bin_raddr)

	);
	//讀時(shí)鐘域?qū)懙刂罚ǘM(jìn)制)寄存
	always@(posedge rclk or negedge r_rst_)
	begin
	    if(!r_rst_)
		    w_bin_raddr_reg <= {(ADDR_WIDTH+1){1'b0}} ;
		else
		    w_bin_raddr_reg <= w_bin_raddr;
	end
	
	/*FIFO內(nèi)剩余空間計(jì)算*/
	always@(posedge wclk or negedge w_rst_)
	begin
	    if(!w_rst_)
		    room_avail <= {(ADDR_WIDTH+1){1'b0}};
		else if(~w_bin_waddr[ADDR_WIDTH]==w_bin_raddr_reg[ADDR_WIDTH])//回卷時(shí)
		    room_avail <= w_bin_raddr_reg[ADDR_WIDTH-1:0]- w_bin_waddr[ADDR_WIDTH-1:0];
		else                                                          //未回卷時(shí)
		    room_avail <= DEPTH + w_bin_raddr_reg - w_bin_waddr;		    	    
	end
	
    /*FIFO內(nèi)已存入數(shù)據(jù)個(gè)數(shù)計(jì)算*/
	always@(posedge wclk or negedge w_rst_)
	begin
	    if(!w_rst_)
		    data_avail <= {(ADDR_WIDTH+1){1'b0}};
		else if(~r_bin_raddr[ADDR_WIDTH]==r_bin_waddr_reg[ADDR_WIDTH])//回卷時(shí)
		    data_avail <= DEPTH + r_bin_waddr_reg[ADDR_WIDTH-1:0]- r_bin_raddr[ADDR_WIDTH-1:0];
		else                                                          //未回卷時(shí)
		    data_avail <= r_bin_waddr_reg - r_bin_raddr;		    	    
	end
	
	/*將滿信號判斷*/
	assign    almost_full = room_avail<ALMOST_FULL_GAP ? 1'b1:1'b0;
	
	/*將空信號判斷*/
	assign    almost_empty = data_avail<ALMOST_EMPTY_GAP ? 1'b1:1'b0;	
	
	/*寫滿標(biāo)志判斷*/
	assign full = ({~w_bin_waddr[ADDR_WIDTH],w_bin_waddr[ADDR_WIDTH-1:0]} == w_bin_raddr_reg)?1'b1:1'b0;
	
	/*讀空標(biāo)志判斷*/
	assign empty = (r_bin_raddr == r_bin_waddr_reg)? 1'b1:1'b0;
	
endmodule

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多