一、同步FIFO
1、代碼
1 //************************************************************************** 2 // *** 名稱 : sFIFO.v 3 // *** 作者 : xianyu_FPGA 4 // *** 博客 : https://www.cnblogs.com/xianyufpga/ 5 // *** 日期 : 2020年6月 6 // *** 描述 : 同步FIFO,讀寫位寬相同,默認寫先讀后 7 //************************************************************************** 8 module sFIFO 9 //========================< 參數 >========================================== 10 #( 11 parameter DATA_W = 8 , //數據位寬 12 parameter ADDR_W = 4 //地址位寬 13 ) 14 //========================< 端口 >========================================== 15 ( 16 input clk , //時鍾 17 input rst_n , //復位 18 input wr_en , //寫使能 19 input [DATA_W-1:0] wr_data , //寫數據 20 input rd_en , //讀使能 21 output reg [DATA_W-1:0] rd_data , //讀數據 22 output full , //寫滿 23 output empty //讀空 24 ); 25 //========================< 信號 >========================================== 26 reg [ADDR_W-1:0] wr_addr ; //寫地址 27 reg [ADDR_W-1:0] rd_addr ; //讀地址 28 reg [DATA_W-1:0] ram[2**ADDR_W-1:0] ; //ram,地址位寬4,共16個 29 reg [ADDR_W :0] cnt ; //計數器 30 //========================================================================== 31 //== 讀寫地址 32 //========================================================================== 33 //-- 寫地址 34 //--------------------------------------------------- 35 always @(posedge clk or negedge rst_n) begin 36 if(!rst_n) begin 37 wr_addr <= 0; 38 end 39 else if(wr_en & (!full)) begin 40 wr_addr <= wr_addr + 1; 41 end 42 end 43 44 //-- 讀地址 45 //--------------------------------------------------- 46 always @(posedge clk or negedge rst_n) begin 47 if (!rst_n) begin 48 rd_addr <= 0; 49 end 50 else if(rd_en & (!empty)) begin 51 rd_addr <= rd_addr + 1; 52 end 53 end 54 //========================================================================== 55 //== 讀寫數據 56 //========================================================================== 57 //-- 寫數據 58 //--------------------------------------------------- 59 integer i; 60 always @(posedge clk or negedge rst_n) begin 61 if(!rst_n) begin 62 for(i=0; i<16; i=i+1) 63 ram[i] <= 0; 64 end 65 else if(wr_en) begin 66 ram[wr_addr] <= wr_data; 67 end 68 end 69 70 //-- 讀數據 71 //--------------------------------------------------- 72 always @(posedge clk or negedge rst_n) begin 73 if (!rst_n) begin 74 rd_data<=0; 75 end 76 else if (rd_en) begin 77 rd_data <= ram[rd_addr]; 78 end 79 end 80 //========================================================================== 81 //== 空滿標志 82 //========================================================================== 83 //-- 輔助計數 84 //--------------------------------------------------- 85 always @(posedge clk or negedge rst_n) begin 86 if (!rst_n) begin 87 cnt <= 0; 88 end 89 else if(wr_en && (!rd_en) && (cnt!=16)) begin 90 cnt <= cnt + 1; 91 end 92 else if(rd_en && (!wr_en) && (cnt!=0)) begin 93 cnt <= cnt - 1; 94 end 95 end 96 97 //-- 寫滿、讀空 98 //--------------------------------------------------- 99 assign full = (cnt==16); 100 assign empty = (cnt==0 ); 101 102 103 endmodule
2、仿真
1 `timescale 1ns/1ns 2 module sFIFO_tb; 3 //========================< 參數 >========================================== 4 parameter DATA_W = 8 ; //數據位寬 5 parameter ADDR_W = 4 ; //地址位寬 6 //========================< 信號 >========================================== 7 reg clk ; 8 reg rst_n ; 9 reg wr_en ; 10 reg [DATA_W-1:0] wr_data ; 11 reg rd_en ; 12 //========================================================================== 13 //== 例化 14 //========================================================================== 15 sFIFO 16 #( 17 .DATA_W (DATA_W ), 18 .ADDR_W (ADDR_W ) 19 ) 20 u_sFIFO 21 ( 22 .clk (clk ), 23 .rst_n (rst_n ), 24 .wr_en (wr_en ), 25 .wr_data (wr_data ), 26 .rd_en (rd_en ), 27 .rd_data ( ), 28 .full ( ), 29 .empty ( ) 30 ); 31 //========================================================================== 32 //== 時鍾 33 //========================================================================== 34 always #10 clk=~clk; 35 //========================================================================== 36 //== 設計 37 //========================================================================== 38 initial begin 39 clk = 1; 40 rst_n = 0; 41 wr_en = 0; 42 wr_data = 0; 43 rd_en = 0; 44 45 #101; 46 rst_n = 1; 47 48 #20; 49 gen_data; 50 51 @(posedge clk); 52 rd_en=1; 53 54 repeat(16)@(posedge clk); 55 rd_en=0; 56 end 57 58 task gen_data; 59 integer i; 60 begin 61 for(i=0; i<16; i=i+1) begin 62 wr_en = 1; 63 wr_data = i; 64 #20; 65 end 66 wr_en = 0; 67 wr_data = 0; 68 end 69 endtask 70 71 72 endmodule
二、異步FIFO
1、分析
(1)格雷碼
比較空滿時,需要讀寫地址進行判斷,二者屬於跨時鍾域,需要進行打拍的同步處理,未避免亞穩態,采用格雷碼,因為格雷碼相鄰只有一位變化,這樣同步多位時更不容易產生問題。
格雷碼公式:gray = (binary>>1) ^ binary;
(2)讀空判斷
默認是先寫后讀,讀追上了寫,之后就是讀空了。因此讀空標志為【讀寫地址相同】。
(3)寫滿判斷
默認是先寫后讀,寫在前面,超過了一輪地址后,又追上了讀,之后就是寫滿了。因此寫滿標志也是【讀寫地址相同】嗎?
答案錯誤!顯然,這樣的思維會導致寫滿和讀空的標志相同,無法確定【讀寫地址相同】時到底是寫滿還是讀空,因此可以設置一個寫指針 wr_addr_ptr 和 讀指針 rd_addr_ptr,其位寬比讀寫地址多1位,整個指針的長度是地址的 2 倍。假設前半段為 A,后半段為 B。
- 讀在 A,最高位為0,剩余位為100;
- 寫在 B,最高位為1,剩余位為100;
我們便可以判定,這時寫越過了一輪,又到了讀的位置,這便是真正的寫滿標志。提煉一下就是:“讀寫的最高位不同,其余位相同”時,處於寫滿狀態。
以上是當地址編碼為普通二進制碼時的分析,但是格雷碼是不一樣的,他的排列有些不同,前半段 A 和后半段 B 是鏡像對稱的,如下圖所示:
通過觀察格雷碼的特點,我們可以這樣判斷:“讀寫的最高2位不同,其余位相同”時,處於寫滿狀態。
2、代碼
1 //************************************************************************** 2 // *** 名稱 : asFIFO.v 3 // *** 作者 : xianyu_FPGA 4 // *** 博客 : https://www.cnblogs.com/xianyufpga/ 5 // *** 日期 : 2020年6月 6 // *** 描述 : 異步FIFO,讀寫位寬相同,默認寫先讀后 7 //************************************************************************** 8 module asFIFO 9 //========================< 參數 >========================================== 10 #( 11 parameter DATA_W = 8 , //數據位寬 12 parameter ADDR_W = 4 //地址位寬 13 ) 14 //========================< 端口 >========================================== 15 ( 16 input rst_n , //復位 17 //FIFO寫 ---------------------------------------- 18 input wr_clk , //寫時鍾 19 input wr_en , //寫使能 20 input [DATA_W-1:0] wr_data , //寫數據 21 output wr_full , //寫滿 22 //FIFO讀 ---------------------------------------- 23 input rd_clk , //讀時鍾 24 input rd_en , //讀使能 25 output reg [DATA_W-1:0] rd_data , //讀數據 26 output rd_empty //讀空 27 ); 28 //========================< 信號 >========================================== 29 reg [ADDR_W :0] wr_addr_ptr ; //寫指針,多1位 30 reg [ADDR_W :0] rd_addr_ptr ; //讀指針,多1位 31 wire [ADDR_W :0] wr_addr_gray ; //寫地址_格雷碼 32 reg [ADDR_W :0] wr_addr_gray_r ; //寫地址打拍 33 reg [ADDR_W :0] wr_addr_gray_rr ; //寫地址打拍 34 wire [ADDR_W :0] rd_addr_gray ; //讀地址_格雷碼 35 reg [ADDR_W :0] rd_addr_gray_r ; //讀地址打拍 36 reg [ADDR_W :0] rd_addr_gray_rr ; //讀地址打拍 37 //----------------------------------------------- 38 wire [ADDR_W-1:0] wr_addr ; //寫地址 39 wire [ADDR_W-1:0] rd_addr ; //讀地址 40 reg [DATA_W-1:0] ram[2**ADDR_W-1:0] ; //ram,地址位寬4,共16個 41 //========================================================================== 42 //== 地址指針 43 //========================================================================== 44 always @(posedge wr_clk or negedge rst_n) begin 45 if(!rst_n) begin 46 wr_addr_ptr <= 0; 47 end 48 else if(wr_en & (!wr_full)) begin 49 wr_addr_ptr <= wr_addr_ptr + 1; 50 end 51 end 52 53 always @(posedge rd_clk or negedge rst_n) begin 54 if(!rst_n) begin 55 rd_addr_ptr <= 0; 56 end 57 else if(rd_en & (!rd_empty)) begin 58 rd_addr_ptr <= rd_addr_ptr + 1; 59 end 60 end 61 //========================================================================== 62 //== 空滿信號 63 //========================================================================== 64 //-- 格雷碼轉換 65 //--------------------------------------------------- 66 assign wr_addr_gray = (wr_addr_ptr>>1) ^ wr_addr_ptr; 67 assign rd_addr_gray = (rd_addr_ptr>>1) ^ rd_addr_ptr; 68 69 //-- 跨時鍾域,打兩拍 70 //--------------------------------------------------- 71 always @(posedge wr_clk) begin 72 rd_addr_gray_r <= rd_addr_gray; 73 rd_addr_gray_rr <= rd_addr_gray_r; 74 end 75 76 always @(posedge rd_clk) begin 77 wr_addr_gray_r <= wr_addr_gray; 78 wr_addr_gray_rr <= wr_addr_gray_r; 79 end 80 81 //-- 寫滿標志:高2位不同,其余位相同 82 //--------------------------------------------------- 83 assign wr_full = (wr_addr_gray == ({~rd_addr_gray_rr[ADDR_W-:2],rd_addr_gray_rr[ADDR_W-2:0]})); 84 85 //-- 讀空標志:讀寫地址相同 86 //--------------------------------------------------- 87 assign rd_empty = (rd_addr_gray == wr_addr_gray_rr); 88 //========================================================================== 89 //== ram讀寫 90 //========================================================================== 91 //-- 讀寫地址 92 //--------------------------------------------------- 93 assign wr_addr = wr_addr_ptr[ADDR_W-1:0]; 94 assign rd_addr = rd_addr_ptr[ADDR_W-1:0]; 95 96 //-- 寫數據 97 //--------------------------------------------------- 98 integer i; 99 always @(posedge wr_clk or negedge rst_n) begin 100 if(!rst_n) begin 101 for(i=0; i<2**ADDR_W; i=i+1) 102 ram[i] <= 0; 103 end 104 else if(wr_en & (!wr_full)) begin 105 ram[wr_addr] <= wr_data; 106 end 107 end 108 109 //-- 讀數據 110 //--------------------------------------------------- 111 always @(posedge rd_clk or negedge rst_n) begin 112 if(!rst_n) begin 113 rd_data <= 0; 114 end 115 else if(rd_en & (!rd_empty)) begin 116 rd_data <= ram[rd_addr]; 117 end 118 end 119 120 121 endmodule
3、仿真
1 `timescale 1ns/1ns 2 module asFIFO_tb; 3 //========================< 參數 >========================================== 4 parameter DATA_W = 8 ; //數據位寬 5 parameter ADDR_W = 4 ; //地址位寬 6 //========================< 信號 >========================================== 7 reg wr_clk ; 8 reg rd_clk ; 9 reg rst_n ; 10 reg wr_en ; 11 reg [DATA_W-1:0] wr_data ; 12 reg rd_en ; 13 //========================================================================== 14 //== 例化 15 //========================================================================== 16 asFIFO 17 #( 18 .DATA_W (DATA_W ), 19 .ADDR_W (ADDR_W ) 20 ) 21 u_asFIFO 22 ( 23 .wr_clk (wr_clk ), 24 .rd_clk (rd_clk ), 25 .rst_n (rst_n ), 26 .wr_en (wr_en ), 27 .wr_data (wr_data ), 28 .rd_en (rd_en ), 29 .rd_data ( ), 30 .wr_full ( ), 31 .rd_empty ( ) 32 ); 33 //========================================================================== 34 //== 時鍾 35 //========================================================================== 36 always #10 wr_clk=~wr_clk; 37 always #5 rd_clk=~rd_clk; 38 //========================================================================== 39 //== 數據 40 //========================================================================== 41 initial begin 42 wr_clk = 1; 43 rd_clk = 0; 44 rst_n = 0; 45 wr_en = 0; 46 wr_data = 0; 47 rd_en = 0; 48 49 #101; 50 rst_n = 1; 51 52 #20; 53 gen_data; 54 55 @(posedge rd_clk); 56 rd_en = 1; 57 58 repeat(16)@(posedge rd_clk); 59 rd_en=0; 60 end 61 62 task gen_data; 63 integer i; 64 begin 65 for(i=0; i<16; i=i+1) begin 66 wr_en = 1; 67 wr_data = i; 68 #20; 69 end 70 wr_en = 0; 71 wr_data = 0; 72 end 73 endtask 74 75 76 endmodule