一、分析
由於是異步FIFO的設計,讀寫時鍾不一樣,在產生讀空信號和寫滿信號時,會涉及到跨時鍾域的問題,如何解決?
跨時鍾域的問題:由於讀指針是屬於讀時鍾域的,寫指針是屬於寫時鍾域的,而異步FIFO的讀寫時鍾域不同,是異步的,要是將讀時鍾域的讀指針與寫時鍾域的寫指針不做任何處理直接比較肯定是錯誤的,因此我們需要進行同步處理以后仔進行比較
解決方法:
加兩級寄存器同步 + 格雷碼(目的都是消除亞穩態)
1.使用異步信號進行使用的時候,好的設計都會對異步信號進行同步處理,同步一般采用多級D觸發器級聯處理,如下圖。這種模型大部分資料都說的是第一級寄存器產生亞穩態后,第二級寄存器穩定輸出概率為90%,第三極寄存器穩定輸出的概率為99%,如果亞穩態跟隨電路一直傳遞下去,那就會另自我修護能力較弱的系統直接崩潰。

2.將一個二進制的計數值從一個時鍾域同步到另一個時鍾域的時候很容易出現問題,因為采用二進制計數器時所有位都可能同時變化,在同一個時鍾沿同步多個信號的變化會產生亞穩態問題。而使用格雷碼只有一位變化,因此在兩個時鍾域間同步多個位不會產生問題。所以需要一個二進制到gray碼的轉換電路,將地址值轉換為相應的gray碼,然后將該gray碼同步到另一個時鍾域進行對比,作為空滿狀態的檢測。
那么,多位二進制碼如何轉化為格雷碼?
換一種描述方法:
verilog代碼實現就一句:assign gray_code = (bin_code>>1) ^ bin_code;
使用gray碼解決了一個問題,但同時也帶來另一個問題,即在格雷碼域如何判斷空與滿。
這里直接給出結論:
判斷讀空時:需要讀時鍾域的格雷碼rgray_next和被同步到讀時鍾域的寫指針rd2_wp每一位完全相同;
判斷寫滿時:需要寫時鍾域的格雷碼wgray_next和被同步到寫時鍾域的讀指針wr2_rp高兩位不相同,其余各位完全相同;
二、Verilog實現
module fifo #( parameter WSIZE = 8; parameter DSIZE = 32; ) ( input wr_clk, input rst, input wr_en, input [WSIZE-1 : 0]din, input rd_clk, input rd_en, output [WSIZE-1 : 0]dout, output reg rempty, output reg wfull ); //定義變量 reg [WSIZE-1 :0] mem [DSIZE-1 : 0]; reg [WSIZE-1 : 0] waddr,raddr; reg [WSIZE : 0] wbin,rbin,wbin_next,rbin_next; reg [WSIZE : 0] wgray_next,rgray_next; reg [WSIZE : 0] wp,rp; reg [WSIZE : 0] wr1_rp,wr2_rp,rd1_wp,rd2_wp; wire rempty_val,wfull_val; //輸出數據 assign dout = mem[raddr]; //輸入數據 always@(posedge wr_clk) if(wr_en && !wfull) mem[waddr] <= din; //1.產生存儲實體的讀地址raddr; 2.將普通二進制轉化為格雷碼,並賦給讀指針rp always@(posedge rd_clk or negedge rst_n) if(!rst_n) {rbin,rp} <= 0; else {rbin,rp} <= {rbin_next,rgray_next}; assign raddr = rbin[WSIZE-1 : 0]; assign rbin_next = rbin + (rd_en & ~rempty); assign rgray_next = rbin_next ^ (rbin_next >> 1); //1.產生存儲實體的寫地址waddr; 2.將普通二進制轉化為格雷碼,並賦給寫指針wp always@(posedge wr_clk or negedge rst_n) if(!rst_n) {wbin,wp} <= 0; else {wbin,wp} <= {wbin_next,wgray_next}; assign waddr = wbin[WSIZE-1 : 0]; assign wbin_next = wbin + (wr_en & ~wfull); assign wgray_next = wbin_next ^ (wbin_next >> 1); //將讀指針rp同步到寫時鍾域 always@(posedge wr_clk or negedge rst_n) if(!rst_n) {wr2_rp,wr1_rp} <= 0; else {wr2_rp,wr1_rp} <= {wr1_rp,rp}; //將寫指針wp同步到讀時鍾域 always@(posedge rd_clk or negedge rst_n) if(!rst_n) {rd2_wp,rd1_wp} <= 0; else {rd2_wp,rd1_wp} <= {rd1_wp,wp}; //產生讀空信號rempty assign rempty_val = (rd2_wp == rgray_next); always@(posedge rd_clk or negedge rst_n) if(rst_n) rempty <= 1'b1; else rempty <= rempty_val; //產生寫滿信號wfull assign wfull_val = ((~(wr2_rp[WSIZE : WSIZE-1]),wr2_rp[WSIZE-2 : 0]) == wgray_next); always@(posedge wr_clk or negedge rst_n) if(!rst_n) wfull <= 1'b0; else wfull <= wfull_val; endmodule