题解 | 异步FIFO

代碼思路:觀察兩個模塊接口,以判斷本模塊需要完成哪些功能

RAM每次斷電后會全部清空,再次通電所有信號都歸零

1、時鐘:直接相連

2、讀寫使能:讀時處於非空狀態,寫時處於非滿狀態;

2.1、【讀空】和【寫滿】判斷

3、讀寫地址:在讀寫使能時,自加1即可;(先進先出要求讀追趕寫,所以是同向)

4、讀寫數據:直接相連(因爲RAM裏面亦含有數據傳輸條件,即2步驟產生的使能信號)

二、以上難點在於步驟2:具體解決方案見下圖。

易錯點:轉爲格雷碼后打拍在各自的時鐘下,而後續消除亞穩態的兩拍是在對方時鐘下;

因爲讀/寫操作處在不同的時鐘下。

判斷是否讀空,需要將讀地址與寫地址作比較,而寫地址是由寫時鐘產生的,在讀時鐘下獲取寫地址便需要將寫地址在讀時鐘下打兩拍;判斷是否寫滿亦然。

三、代碼實現:

`timescale 1ns/1ns
 
/***************************************RAM*****************************************/
module dual_port_RAM #(parameter DEPTH = 16,
                       parameter WIDTH = 8)(
     input wclk
    ,input wenc
    ,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
    ,input [WIDTH-1:0] wdata        //数据写入
    ,input rclk
    ,input renc
    ,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
    ,output reg [WIDTH-1:0] rdata       //数据输出
);
 
reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
 
always @(posedge wclk) begin
    if(wenc)
        RAM_MEM[waddr] <= wdata;
end 
 
always @(posedge rclk) begin
    if(renc)
        rdata <= RAM_MEM[raddr];
end 
 
endmodule  

/***************************************AFIFO*****************************************/
module asyn_fifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					wclk	, 
	input 					rclk	,   
	input 					wrstn	, //
	input					rrstn	, //
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output wire				wfull	, //
	output wire				rempty	, //
	output wire [WIDTH-1:0]	rdata
);


//讀寫使能
wire wenc,renc;
assign wenc = winc & !wfull;
assign renc = rinc & !rempty;

//讀寫地址邏輯
parameter addr_len = $clog2(DEPTH);

reg [addr_len:0] waddr;
reg [addr_len:0] raddr;

    always @(posedge wclk or negedge wrstn)         //////////  讀和寫均從最底端開始
        begin                                       //   ↑  //  每個數據通過wdata總綫寫入 rdata讀取 
            if(!wrstn)                              //   |  //  寫/讀到頂,如果再寫就又從底端開始,往復循環 
               waddr <='d0;                         //   |  //  1.讀寫何時可以執行?      
            else if(wenc)                 //   |  //       讀時非空,寫時未滿即可            
               waddr <= waddr + 1'b1;               //   |  //  2.如何判斷空/滿?
        end                                         //   |  //       判斷full/empty:循環讀/寫時發現讀寫地址相衝
                                                    //////////  3.跨時域需減輕亞穩態!

    always @(posedge rclk or negedge rrstn)   
        begin                                
            if(!rrstn)                       
               raddr <='d0;                     
            else if(renc)           
               raddr <= raddr + 1'b1;                                   
        end

//消除亞穩態; 
//A.格雷碼【addr⊕(addr>>1)】相鄰只變換一個位,可有效防止繼存存捕獲中間態。
//如011-100,變了三位,格雷碼 011-010,只變了一位
//B.常規打二拍
wire [addr_len:0] waddr_gray,raddr_gray;
reg  [addr_len:0] waddr_gray_pat,raddr_gray_pat,waddr_pat1,waddr_pat2,raddr_pat1,raddr_pat2;

    assign waddr_gray = waddr ^ (waddr >> 1 );  //**************重點***************// 
    assign raddr_gray = raddr ^ (raddr >> 1 );  //**************重點**************// 

    always @(posedge wclk or negedge wrstn)   
        begin                                
            if(~wrstn) begin
               waddr_gray_pat <='d0;                     
            end                     
            else begin
               waddr_gray_pat <= waddr_gray;    
            end                                    
        end

    always @(posedge rclk or negedge rrstn)   
        begin                                
            if(~rrstn) begin 
               raddr_gray_pat <='d0;                    
            end                     
            else begin   
               raddr_gray_pat <= raddr_gray; 
            end                                    
        end

    always @(posedge wclk or negedge wrstn)   
        begin                                
            if(~wrstn) begin                    //**************易錯點**************//
               raddr_pat1 <='d0;
               raddr_pat2 <='d0;
            end                     
            else begin    
               raddr_pat1 <= raddr_gray_pat;
               raddr_pat2 <= raddr_pat1;
            end                                    
        end

    always @(posedge rclk or negedge rrstn)   
        begin                                
            if(~rrstn) begin                     //**************易錯點**************//
               waddr_pat1 <='d0;
               waddr_pat2 <='d0;
            end                     
            else begin   
               waddr_pat1 <= waddr_gray_pat;
               waddr_pat2 <= waddr_pat1;
            end                                    
        end

//滿/空判斷
//  要求實時性反應,采用組合邏輯
                                                 //**************重點**************//
assign wfull = waddr_gray_pat == {~raddr_pat2[addr_len:addr_len-1],raddr_pat2[addr_len-2:0]};
assign rempty = raddr_gray_pat == waddr_pat2;    //**************重點**************//

//連綫
wire [addr_len-1:0] waddr_f,raddr_f;
assign waddr_f = waddr[addr_len-1:0];
assign raddr_f = raddr[addr_len-1:0];

dual_port_RAM #(
    .DEPTH(DEPTH),
    .WIDTH(WIDTH)
    )
dual_port_RAM0(
       .wclk    (wclk),
       .wenc    (wenc),
       .waddr   (waddr_f), //
       .wdata   (wdata),       	
       .rclk    (rclk),
       .renc    (renc),
       .raddr   (raddr_f), //
       .rdata 	(rdata)   
         );
endmodule

全部评论

相关推荐

不愿透露姓名的神秘牛友
01-17 17:00
点赞 评论 收藏
分享
牛客539033066号:放心吧,这里面一大半都不会去面试的,剩下一半面过了最后还是回拒,实际上免笔试的那些bg的人,没多少愿意去这些岗位,薪资水平在那里
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务