module fifo #( parameter DSIZE = 8, parameter ASIZE = 4 ) ( output [DSIZE-1:0] rdata, output wfull, output rempty, input [DSIZE-1:0] wdata, input winc, wclk, wrst_n, input rinc, rclk, rrst_n ); wire [ASIZE-1:0] waddr, raddr; wire [ASIZE:0] wptr, rptr, wq2_rptr, rq2_wptr; // synchronize the read pointer into the write-clock domain sync_r2w sync_r2w ( .wq2_rptr (wq2_rptr), .rptr (rptr ), .wclk (wclk ), .wrst_n (wrst_n ) ); // synchronize the write pointer into the read-clock domain sync_w2r sync_w2r ( .rq2_wptr(rq2_wptr), .wptr(wptr), .rclk(rclk), .rrst_n(rrst_n) ); //this is the FIFO memory buffer that is accessed by both the write and read clock domains. //This buffer is most likely an instantiated, synchronous dual-port RAM. //Other memory styles can be adapted to function as the FIFO buffer. fifomem #(DSIZE, ASIZE) fifomem ( .rdata(rdata), .wdata(wdata), .waddr(waddr), .raddr(raddr), .wclken(winc), .wfull(wfull), .wclk(wclk) ); //this module is completely synchronous to the read-clock domain and contains the FIFO read pointer and empty-flag logic. rptr_empty #(ASIZE) rptr_empty ( .rempty(rempty), .raddr(raddr), .rptr(rptr), .rq2_wptr(rq2_wptr), .rinc(rinc), .rclk(rclk), .rrst_n(rrst_n) ); //this module is completely synchronous to the write-clock domain and contains the FIFO write pointer and full-flag logic wptr_full #(ASIZE) wptr_full ( .wfull(wfull), .waddr(waddr), .wptr(wptr), .wq2_rptr(wq2_rptr), .winc(winc), .wclk(wclk), .wrst_n(wrst_n) ); endmodule
二、fifomem.v 生成存儲實體,FIFO 的本質是RAM,所以在設計存儲實體的時候有兩種方法:用數組存儲數據或者調用RAM的IP核 html
module fifomem #( parameter DATASIZE = 8, // Memory data word width parameter ADDRSIZE = 4 // 深度爲8即地址爲3位便可,這裏多定義一位的緣由是用來判斷是空仍是滿,詳細在後文講到 ) // Number of mem address bits ( output [DATASIZE-1:0] rdata, input [DATASIZE-1:0] wdata, input [ADDRSIZE-1:0] waddr, raddr, input wclken, wfull, wclk ); `ifdef RAM //能夠調用一個RAM IP核 // instantiation of a vendor's dual-port RAM my_ram mem ( .dout(rdata), .din(wdata), .waddr(waddr), .raddr(raddr), .wclken(wclken), .wclken_n(wfull), .clk(wclk) ); `else //用數組生成存儲體 // RTL Verilog memory model localparam DEPTH = 1<<ADDRSIZE; // 左移至關於乘法,2^4 reg [DATASIZE-1:0] mem [0:DEPTH-1]; //生成2^4個位寬位8的數組 assign rdata = mem[raddr]; always @(posedge wclk) //當寫使能有效且還未寫滿的時候將數據寫入存儲實體中,注意這裏是與wclk同步的 if (wclken && !wfull) mem[waddr] <= wdata; `endif endmodule
三、sync_r2w.v 將 rclk 時鐘域的格雷碼形式的讀指針同步到 wclk 時鐘域,簡單來說就是用兩級寄存器同步,即打兩拍 數組
module sync_r2w #( parameter ADDRSIZE = 4 ) ( output reg [ADDRSIZE:0] wq2_rptr, //讀指針同步到寫時鐘域 input [ADDRSIZE:0] rptr, // 格雷碼形式的讀指針,格雷碼的好處後面會細說 input wclk, wrst_n ); reg [ADDRSIZE:0] wq1_rptr; always @(posedge wclk or negedge wrst_n) if (!wrst_n) begin wq1_rptr <= 0; wq2_rptr <= 0; end else begin wq1_rptr<= rptr; wq2_rptr<=wq1_rptr; end endmodule
四、sync_w2r.v 將 wclk 時鐘域的格雷碼形式的寫指針同步到 rclk 時鐘域緩存
module sync_w2r #(parameter ADDRSIZE = 4) ( output reg [ADDRSIZE:0] rq2_wptr, //寫指針同步到讀時鐘域 input [ADDRSIZE:0] wptr, //格雷碼形式的寫指針 input rclk, rrst_n ); reg [ADDRSIZE:0] rq1_wptr; always @(posedge rclk or negedge rrst_n) if (!rrst_n)begin rq1_wptr <= 0; rq2_wptr <= 0; end else begin rq1_wpt <= wptr; rq2_wptr <= rq1_wptr; end endmodule
五、rptr_empty.v 將 sync_w2r.v 同步後的寫指針與 rclk 時鐘域的讀指針進行比較生成都空信號dom
module rptr_empty #( parameter ADDRSIZE = 4 ) ( output reg rempty, output [ADDRSIZE-1:0] raddr, //二進制形式的讀指針 output reg [ADDRSIZE :0] rptr, //格雷碼形式的讀指針 input [ADDRSIZE :0] rq2_wptr, //同步後的寫指針 input rinc, rclk, rrst_n ); reg [ADDRSIZE:0] rbin; wire [ADDRSIZE:0] rgraynext, rbinnext; // GRAYSTYLE2 pointer //將二進制的讀指針與格雷碼進制的讀指針同步 always @(posedge rclk or negedge rrst_n) if (!rrst_n) begin rbin <= 0; rptr <= 0; end else begin rbin<=rbinnext; //直接做爲存儲實體的地址 rptr<=rgraynext;//輸出到 sync_r2w.v模塊,被同步到 wrclk 時鐘域 end // Memory read-address pointer (okay to use binary to address memory) assign raddr = rbin[ADDRSIZE-1:0]; //直接做爲存儲實體的地址,好比鏈接到RAM存儲實體的讀地址端。 assign rbinnext = rbin + (rinc & ~rempty); //不空且有讀請求的時候讀指針加1 assign rgraynext = (rbinnext>>1) ^ rbinnext; //將二進制的讀指針轉爲格雷碼 // FIFO empty when the next rptr == synchronized wptr or on reset assign rempty_val = (rgraynext == rq2_wptr); //當讀指針等於同步後的寫指針,則爲空。 always @(posedge rclk or negedge rrst_n) if (!rrst_n) rempty <= 1'b1; else rempty <= rempty_val; endmodule
六、wptr_full.v 將 sync_r2w.v 同步後的讀指針與wclk 時鐘域的寫指針進行比較生成寫滿信號異步
module wptr_full #( parameter ADDRSIZE = 4 ) ( output reg wfull, output [ADDRSIZE-1:0] waddr, output reg [ADDRSIZE :0] wptr, input [ADDRSIZE :0] wq2_rptr, input winc, wclk, wrst_n ); reg [ADDRSIZE:0] wbin; wire [ADDRSIZE:0] wgraynext, wbinnext; // GRAYSTYLE2 pointer always @(posedge wclk or negedge wrst_n) if (!wrst_n) {wbin, wptr} <= 0; else {wbin, wptr} <= {wbinnext, wgraynext}; // Memory write-address pointer (okay to use binary to address memory) assign waddr = wbin[ADDRSIZE-1:0]; assign wbinnext = wbin + (winc & ~wfull); assign wgraynext = (wbinnext>>1) ^ wbinnext; //二進制轉爲格雷碼 //----------------------------------------------------------------- assign wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],wq2_rptr[ADDRSIZE-2:0]}); //當最高位和次高位不一樣其他位相同時則寫指針超前於讀指針一圈,即寫滿。後面會詳細解釋。 always @(posedge wclk or negedge wrst_n) if (!wrst_n) wfull <= 1'b0; else wfull <= wfull_val; endmodule
七、測試文件ide
`timescale 1ns /1ns module test(); reg [7:0] wdata; reg winc, wclk, wrst_n; reg rinc, rclk, rrst_n; wire [7:0] rdata; wire wfull; wire rempty; fifo u_fifo ( .rdata(rdata), .wfull(wfull), .rempty(rempty), .wdata (wdata), .winc (winc), .wclk (wclk), .wrst_n(wrst_n), .rinc(rinc), .rclk(rclk), .rrst_n(rrst_n) ); localparam CYCLE = 20; localparam CYCLE1 = 40; //時鐘週期,單位爲ns,可在此修改時鐘週期。 //生成本地時鐘50M initial begin wclk = 0; forever #(CYCLE/2) wclk=~wclk; end initial begin rclk = 0; forever #(CYCLE1/2) rclk=~rclk; end //產生復位信號 initial begin wrst_n = 1; #2; wrst_n = 0; #(CYCLE*3); wrst_n = 1; end initial begin rrst_n = 1; #2; rrst_n = 0; #(CYCLE*3); rrst_n = 1; end always @(posedge wclk or negedge wrst_n)begin if(wrst_n==1'b0)begin winc <= 0; rinc <= 0; end else begin winc <= $random; rinc <= $random; end end always @(posedge rclk or negedge rrst_n)begin if(rrst_n==1'b0)begin rinc <= 0; end else begin rinc <= $random; end end always@(*)begin if(winc == 1) wdata= $random ; else wdata = 0; end endmodule
八、仿真結果性能
因爲截圖篇幅的限制請本身驗證仿真。學習