题解 | 异步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