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


上海得物信息集团有限公司公司福利 1200人发布