參考如下帖子:html
https://blog.csdn.net/hengzo/article/details/49683707算法
https://blog.csdn.net/Times_poem/article/details/51917648express
http://www.javashuo.com/article/p-eudfpkmw-dz.html緩存
https://www.cnblogs.com/ylsm-kb/p/9068449.htmldom
https://blog.csdn.net/Pieces_thinking/article/details/78026326異步
FIFO是英文First In First Out 的縮寫,是一種先進先出的數據緩存器,他與普通存儲器的區別是沒有外部讀寫地址線,這樣使用起來很是簡單,但缺點就是隻能順序寫入數據,順序的讀出數據, 其數據地址由內部讀寫指針自動加1完成,不能像普通存儲器那樣能夠由地址線決定讀取或寫入某個指定的地址。async
FIFO通常用於不一樣時鐘域之間的數據傳輸,好比FIFO的一端是AD數據採集, 另外一端是計算機的PCI總線,假設其AD採集的速率爲16位 100K SPS,那麼每秒的數據量爲100K×16bit=1.6Mbps,而PCI總線的速度爲33MHz,總線寬度32bit,其最大傳輸速率爲 1056Mbps,在兩個不一樣的時鐘域間就能夠採用FIFO來做爲數據緩衝。另外對於不一樣寬度的數據接口也能夠用FIFO,例如單片機位8位數據輸出,而 DSP多是16位數據輸入,在單片機與DSP鏈接時就可使用FIFO來達到數據匹配的目的。ide
FIFO的分類根據FIFO工做的時鐘域,能夠將FIFO分爲同步FIFO和異步FIFO。同步FIFO是指讀時鐘和寫時鐘爲同一個時鐘。在時鐘沿來臨時同時發生讀寫操做。異步FIFO是指讀寫時鐘不一致,讀寫時鐘是互相獨立的。工具
FIFO設計的難點在於怎樣判斷FIFO的空/滿狀態。爲了保證數據正確的寫入或讀出,而不發生溢出或讀空的狀態出現,必須保證FIFO在滿的狀況下,不能進行寫操做。在空的狀態下不能進行讀操做。怎樣判斷FIFO的滿/空就成了FIFO設計的核心問題。性能
同步FIFO的意思是說FIFO的讀寫時鐘頻率相同,不一樣於異步FIFO,異步FIFO的讀寫時鐘頻率是不一樣的。同步FIFO的對外接口包括時鐘,清零,讀請求,寫請求,數據輸入總線,數據輸出總線,空以及滿信號。下面分別對同步FIFO的對外接口信號做一描述:
下面的框圖主要描述同步FIFO的內部結構 :
verilog 代碼和波形文件以下:
fifo_sync.v
/*use a extra counter to calcuate current fifo filled number its bit is fifo width + 1, such as fifo depth is 8, then it is 0,...,7, we use 4 bits counter, if counter = 8, then express full, counter is 0, then it is empty */ module fifo_sync #( parameter FIFO_WIDTH = 32, //every fifo unit 's bit number, default is 32bits, a dword parameter ADDR_WIDTH = 3, //2^3=FIFO_DEPTH, so addr with is 3 bits if depth=8 parameter FIFO_DEPTH = 8 //fifo depth, first in, first out. ) ( input clk, input rst_n, input [FIFO_WIDTH-1:0] wr_data, input rq, //read request input wq, //write request output reg [FIFO_WIDTH-1:0] rd_data, output full, output empty ); //internal signal reg[FIFO_WIDTH-1:0] fifo_mem[FIFO_DEPTH-1:0]; reg[ADDR_WIDTH:0] counter; //extra one bit for counter reg[ADDR_WIDTH-1:0] rd_ptr; reg[ADDR_WIDTH-1:0] wr_ptr; //set full and empty assign full=(counter==FIFO_DEPTH); assign empty=(counter==0); //set current fifo counter value always @(posedge clk or negedge rst_n) begin if(!rst_n) counter<=0; else if((wq && !full)&&(rq && !empty)) // read and write meanwhile,counter keep no change counter <= counter; else if(rq&&!empty) counter <= counter - 1; else if(wq&&!full) counter <= counter + 1; else counter <= counter; //no read, no write, keep no change end //read data if no empty and read enable always @(posedge clk or negedge rst_n ) begin if(!rst_n) begin rd_data <= 0; end if(rq && !empty) rd_data <= fifo_mem[rd_ptr]; end //write data if no full and write enable always @(posedge clk) begin if(wq && !full) fifo_mem[wr_ptr] <= wr_data; end //update read and write ptr always @(posedge clk or negedge rst_n) begin if(!rst_n) begin wr_ptr <= 0; rd_ptr <= 0; end else begin if(!full && wq) begin wr_ptr <= wr_ptr + 1; // we can omit these two lines, for it will change to // 0 if overflow. //if(wr_ptr==(FIFO_DEPTH-1)) // wr_ptr<=0; end if(!empty && rq) begin rd_ptr <= rd_ptr + 1; //if(rd_ptr==(FIFO_DEPTH-1)) // rd_ptr<=0; end end end endmodule
fifo_sync_tb.v
module fifo_sync_tb; reg clk,rst_n; reg wq,rq; reg [7:0] wr_data; // data input to be pushed to buffer wire [7:0] rd_data; // port to output the data using pop. wire empty,full; // buffer empty and full indication fifo_sync #(.FIFO_WIDTH(8), .ADDR_WIDTH(3), .FIFO_DEPTH(8)) fifo_inst(.clk(clk),.rst_n(rst_n),.wr_data(wr_data),.rq(rq),.wq(wq),.rd_data(rd_data),.full(full),.empty(empty)); always #10 clk = ~clk; reg [7:0] tempdata = 0; initial begin clk = 0; rst_n = 0; wq = 0; rq = 0; wr_data = 0; #15; rst_n = 1; push(1); fork push(2); pop(tempdata); join //push and pop together push(10); push(20); push(30); push(40); push(50); push(60); push(70); push(80); push(90); push(100); push(110); push(120); push(130); pop(tempdata); push(tempdata); pop(tempdata); pop(tempdata); pop(tempdata); pop(tempdata); push(140); pop(tempdata); push(tempdata); pop(tempdata); pop(tempdata); pop(tempdata); pop(tempdata); pop(tempdata); pop(tempdata); pop(tempdata); pop(tempdata); pop(tempdata); pop(tempdata); pop(tempdata); push(5); pop(tempdata); #1000 $finish; end initial begin $dumpfile("dump.vcd"); $dumpvars; $fsdbDumpfile("dump.fsdb"); $fsdbDumpvars("+all"); end task push (input [7:0] data); if(full) $display("---Cannot push %d: Buffer Full---",data); else begin $display("Push",,data); wr_data = data; wq = 1; @(posedge clk); #5 wq = 0; end endtask task pop(output[7:0] data); if(empty) $display("---Cannot Pop: Buffer Empty---"); else begin rq = 1; @(posedge clk); #3 rq = 0; data = rd_data; $display("------Poped:",,data); end endtask endmodule
Makefile
# VCS flags, if want to use dump fsdb in verilog file, need to add args -fsdb, otherwise will be compiled fail VCS_FLAGS = -sverilog -full64 -fsdb -debug_all +v2k -timescale=1ns/1ns # Source files SRC_FILES = fifo_sync.v \ fifo_sync_tb.v # Source directories INCDIR = +incdir+./ all: vcs $(VCS_FLAGS) $(INCDIR) $(SRC_FILES) clean: rm -rf ./csrc *.daidir ./csrc *.log novas.* *.vpd *.vdb simv* *.key *race.out* *vcd *fsdb debug: verdi -sv -ssf dump.fsdb -f verdi.f &
verdi.f
fifo_sync.v fifo_sync_tb.v
從波形中能夠看出執行結果是正確的。
FIFO (先進先出隊列)是一種在電子系統獲得普遍應用的器件,一般用於數據的緩存和用於容納異步信號的頻率或相位的差別。FIFO的實現一般是利用雙口RAM和讀寫地址產生模塊來實現的。FIFO的接口信號包括異步的寫時鐘(wr_clk)和讀時鐘(rd_clk)、與寫時鐘同步的寫有效(wren)和寫數據(wr_data)、與讀時鐘同步的讀有效(rden)和讀數據(rd_data)。爲了實現正確的讀寫和避免FIFO的上溢或下溢,一般還應該給出與讀時鐘和寫時鐘同步的FIFO的空標誌(empty)和滿標誌(full)以禁止讀寫操做。
1 異步FIFO功能描述
下圖給出了FIFO的接口信號和內部模塊圖。
由圖能夠看出,寫地址產生模塊根據寫時鐘和寫有效信號產生遞增的寫地址,讀地址產生模塊根據讀時鐘和讀有效信號產生遞增的讀地址。FIFO的操做以下:在寫時鐘wr_clk的上升沿,當wren有效時,將wr_data寫入雙口RAM中寫地址對應的位置中;始終將讀地址對應的雙口RAM中的數據輸出到讀數據總線上。這樣就實現了先進先出的功能。
這裏寫圖片描述
寫地址產生模塊還根據讀地址和寫地址關係產生FIFO的滿標誌。當wren有效時,若寫地址+2=讀地址時,full爲1;當wren無效時,若寫地址+ 1=讀地址時,full爲1。讀地址產生模塊還根據讀地址和寫地址的差產生FIFO的空標誌。當rden有效時,若寫地址-1=讀地址時,empty爲 1;當rden無效時,若寫地址=讀地址時,empty爲1。按照以上方式產生標誌信號是爲了提早一個時鐘週期產生對應的標誌信號。
因爲空標誌和滿標誌控制了FIFO的操做,所以標誌錯誤會引發操做的錯誤。如上所述,標誌的產生是經過對讀寫地址的比較產生的,當讀寫時鐘徹底異步時,對讀寫地址進行比較時,可能得出錯誤的結果。例如,在讀地址變化過程當中,因爲讀地址的各位變化並不一樣步,計算讀寫地址的差值,可能產生錯誤的差值,致使產生錯誤的滿標誌信號。若將未滿標誌置爲滿標誌時,可能下降了應用的性能,下降寫數據速率;而將滿置標誌置爲未滿時,執行一次寫操做,則可能產生溢出錯誤,這對於實際應用來講是絕對應該避免的。空標誌信號的產生也可能產生相似的錯誤。
2 異步FIFO的改進設計
從以上分析中能夠看出,異步FIFO之因此會發生錯誤是由於在地址變化時,因爲多位地址各位變化時間不一樣,異步時鐘對其進行採樣時數值可能爲不一樣於地址變化後數值的其它值,異步產生錯誤的空標誌和滿標誌,以至於產生FIFO的操做錯誤。
格雷碼是一種在相鄰計數值之間只有一位發生變化的編碼方式。能夠看出,若讀寫地址採用格雷碼編碼方式,就能夠解決上面的問題。
爲了應用的靈活,還增長了兩個標誌信號,將滿(almosf_full)標誌和空(almost_empty)標誌分別定義以下:當寫地址與讀地址的距離小於某個預先定義數值時,almost_full爲1;當讀地址與寫地址的距離小於這個預先定義的數值時,almost_empty爲1。
格雷碼細節:https://www.cnblogs.com/mikewolf2002/
3 異步FIFO的Verilog
…………………………………………………………………………………………………………………………………………………………………………………………………………………………….
異步FIFO的Verilog代碼 之一,與以前的用RAM實現的同步FIFO的程序相比,異步更爲複雜。增長了讀寫控制信號的跨時鐘域的同步。此外,判空與判滿的也稍有不一樣。滿空判斷以及格雷碼生成的細節在https://www.cnblogs.com/mikewolf2002/
fifo_async.v
module fifomem #( parameter DATASIZE = 8, // Memory data word width parameter ADDRSIZE = 4 // depth is 2^(ADDRSIZE-1),extra one bit is to judge empty or full ) // Number of mem address bits ( output [DATASIZE-1:0] read_data, input [DATASIZE-1:0] write_data, input [ADDRSIZE-1:0] waddr, raddr, input wq, wfull, wclk ); localparam DEPTH = 1<<ADDRSIZE; //depth is 2^4 reg [DATASIZE-1:0] mem [0:DEPTH-1]; //fifo mem assign read_data = mem[raddr]; always @(posedge wclk) if (wq && !wfull) mem[waddr] <= write_data; endmodule module sync_r2w #( parameter ADDRSIZE = 4 ) ( output reg [ADDRSIZE:0] wq2_rptr, //read ptr sync to write clock domain input [ADDRSIZE:0] rptr, //gray code pointer 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 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_wptr <= wptr; rq2_wptr <= rq1_wptr; end endmodule module rptr_empty #( parameter ADDRSIZE = 4 ) ( output reg rempty, output [ADDRSIZE-1:0] raddr, //binary ptr output reg [ADDRSIZE :0] rptr, //gray code ptr input [ADDRSIZE :0] rq2_wptr, //pointer after sync input rq, rclk, rrst_n ); reg [ADDRSIZE:0] rbin; wire [ADDRSIZE:0] rgraynext, rbinnext; // GRAYSTYLE2 pointer // sync graycode read pointer with binary read pointer always @(posedge rclk or negedge rrst_n) if (!rrst_n) begin rbin <= 0; rptr <= 0; end else begin rbin<=rbinnext; rptr<=rgraynext; //output to sync_r2w.v end // Memory read-address pointer (okay to use binary to address memory) assign raddr = rbin[ADDRSIZE-1:0]; // assign rbinnext = rbin + (rq & ~rempty); //not empty and read request, then plus 1 assign rgraynext = (rbinnext>>1) ^ rbinnext; //graycode // FIFO empty when the next rptr == synchronized wptr or on reset assign rempty_val = (rgraynext == rq2_wptr); //empty when read graycode equal write prt after sync always @(posedge rclk or negedge rrst_n) if (!rrst_n) rempty <= 1'b1; else rempty <= rempty_val; endmodule 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 wq, 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 + (wq & ~wfull); assign wgraynext = (wbinnext>>1) ^ wbinnext; // //----------------------------------------------------------------- //msb not equal msb -1 bit, and other bits are same, then full 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 module fifo_async #( parameter DSIZE = 8, parameter ASIZE = 4 ) ( input rclk, rrst_n,rq, input wclk, wrst_n,wq, input [DSIZE-1:0] write_data, output [DSIZE-1:0] read_data, output wfull, output rempty ); 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 ( .read_data(read_data), .write_data(write_data), .waddr(waddr), .raddr(raddr), .wq(wq), .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), .rq(rq), .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), .wq(wq), .wclk(wclk), .wrst_n(wrst_n) ); endmodule
fifo_async_tb.v
module fifo_async_tb; reg wclk,wrst_n; reg rclk,rrst_n; reg wq,rq; reg [7:0] write_data; // data input to be pushed to buffer wire [7:0] read_data; // port to output the data using pop. wire rempty,wfull; // buffer empty and full indication fifo_async #(.DSIZE(8), .ASIZE(4)) fifo_inst( .rclk(rclk),.rrst_n(rrst_n),.rq(rq), .wclk(wclk),.wrst_n(wrst_n),.wq(wq), .write_data(write_data),.read_data(read_data), .wfull(wfull),.rempty(rempty)); always #10 wclk = ~wclk; always #20 rclk = ~rclk; reg [7:0] tempdata = 0; initial begin rclk = 0; wclk = 0; #5000 $finish; end initial begin wrst_n = 1; #2; wrst_n = 0; #60; wrst_n = 1; end initial begin rrst_n = 1; #2; rrst_n = 0; #120; rrst_n = 1; end always @(posedge wclk or negedge wrst_n) begin if(wrst_n==1'b0) begin wq <= 0; rq <= 0; end else begin wq <= $random; end end always @(posedge rclk or negedge rrst_n) if(rrst_n==1'b0) rq <= 0; else rq <= $random; always@(*) if(wq == 1) begin write_data= $random ; end else write_data = 0; always @(write_data,read_data) begin $monitor($time, "write data:%d, read data:%d",write_data,read_data); end initial begin //$dumpfile("dump.vcd"); //$dumpvars; $fsdbDumpfile("dump.fsdb"); $fsdbDumpvars("+all"); end endmodule
Makefile
# VCS flags, if want to use dump fsdb in verilog file, need to add args -fsdb, otherwise will be compiled fail VCS_FLAGS = -sverilog -full64 -fsdb -debug_all +v2k -timescale=1ns/1ns # Source files SRC_FILES = fifo_async.v \ fifo_async_tb.v # Source directories INCDIR = +incdir+./ all: vcs $(VCS_FLAGS) $(INCDIR) $(SRC_FILES) clean: rm -rf ./csrc *.daidir ./csrc novas.* *.log *.vpd *.vdb simv* *.key *race.out* *vcd *fsdb debug: verdi -sv -ssf dump.fsdb -f verdi.f &
verdi.f
fifo_async.v fifo_async_tb.v
下面的fifo_async.v中,全部的代碼在一個module中,接口和功能和前面同樣。
module fifo_async #( parameter DSIZE = 8, parameter ASIZE = 4 ) ( input wclk,wrst_n,rclk,rrst_n,rq,wq, input [DSIZE-1:0] write_data, output [DSIZE-1:0] read_data, output reg wfull,rempty ); reg [ASIZE:0] wptr, rptr, wq2_rptr, rq2_wptr, wq1_rptr,rq1_wptr; reg [ASIZE:0] rbin, wbin; reg [DSIZE-1:0] mem[0:(1<<ASIZE)-1]; wire [ASIZE-1:0] waddr, raddr; wire [ASIZE:0] rgraynext, rbinnext,wgraynext,wbinnext; wire rempty_val,wfull_val; //double port ram assign read_data=mem[raddr]; always@(posedge wclk) if (wq && !wfull) mem[waddr] <= write_data; //rprt sync to write clock domain always @(posedge wclk or negedge wrst_n) if (!wrst_n) {wq2_rptr,wq1_rptr} <= 0; else {wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr}; //wptr sync to read clock domain always @(posedge rclk or negedge rrst_n) if (!rrst_n) {rq2_wptr,rq1_wptr} <= 0; else {rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr}; //rempty and rptr generate always @(posedge rclk or negedge rrst_n) // GRAYSTYLE2 pointer begin if (!rrst_n) {rbin, rptr} <= 0; else {rbin, rptr} <= {rbinnext, rgraynext}; end // Memory read-address pointer (okay to use binary to address memory) assign raddr = rbin[ASIZE-1:0]; assign rbinnext = rbin + (rq & ~rempty); 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) begin if (!rrst_n) rempty <= 1'b1; else rempty <= rempty_val; end //---------------wfull and wprt generate always @(posedge wclk or negedge wrst_n) // GRAYSTYLE2 pointer 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[ASIZE-1:0]; assign wbinnext = wbin + (wq & ~wfull); assign wgraynext = (wbinnext>>1) ^ wbinnext; assign wfull_val = (wgraynext=={~wq2_rptr[ASIZE:ASIZE-1], wq2_rptr[ASIZE-2:0]}); //:ASIZE-1] always @(posedge wclk or negedge wrst_n) if (!wrst_n) wfull <= 1'b0; else wfull <= wfull_val; endmodule
異步FIFO的Verilog代碼 之二
與前一段異步FIFO代碼的主要區別在於,空/滿狀態標誌的不一樣算法。
第一個算法:Clifford E. Cummings的文章中提到的STYLE #1,構造一個指針寬度爲N+1,深度爲2^N字節的FIFO(爲便方比較將格雷碼指針轉換爲二進制指針)。當指針的二進制碼中最高位不一致而其它N位都 相等時,FIFO爲滿(在Clifford E. Cummings的文章中以格雷碼錶示是前兩位均不相同,然後兩位LSB相同爲滿,這與換成二進制表示的MSB不一樣其餘相同爲盡是同樣的)。當指針徹底相 等時,FIFO爲空。
這種方法思路很是明瞭,爲了比較不一樣時鐘產生的指針,須要把不一樣時鐘域的信號同步到本時鐘域中來,而使用Gray碼的目的就是使這個異步同步化的過 程發生亞穩態的機率最小,而爲何要構造一個N+1的指針,Clifford E. Cummings也闡述的很明白,有興趣的讀者能夠看下做者原文是怎麼論述的,Clifford E. Cummings的這篇文章有Rev1.1 \ Rev1.2兩個版本,二者在比較Gray碼指針時的方法略有不一樣,個Rev1.2版更爲精簡。
第二種算法:Clifford E. Cummings的文章中提到的STYLE #2。它將FIFO地址分紅了4部分,每部分分別用高兩位的MSB 00 、0一、 十一、 10決定FIFO是否爲going full 或going empty (即將滿或空)。若是寫指針的高兩位MSB小於讀指針的高兩位MSB則FIFO爲「幾乎滿」,若寫指針的高兩位MSB大於讀指針的高兩位MSB則FIFO 爲「幾乎空」。
它是利用將地址空間分紅4個象限(也就是四個等大小的區域),而後觀察兩個指針的相對位置,若是寫指針落後讀指針一個象限(25%的距離,呵呵), 則證實極可能要寫滿,反之則極可能要讀空,這個時候分別設置兩個標誌位dirset和dirrst,而後在地址徹底相等的狀況下,若是dirset有效就 是寫滿,若是dirrst有效就是讀空。
這種方法對深度爲2^N字節的FIFO只需N位的指針便可,處理的速度也較第一種方法快。
這段是說明的原話,算法一,還好理解。算法二,彷佛沒有說清楚,不太明白。有興趣的能夠查查論文,詳細研究下。
總之,第二種寫法是推薦的寫法。由於異步的多時鐘設計應按如下幾個原則進行設計:
1,儘量的將多時鐘的邏輯電路(非同步器)分割爲多個單時鐘的模塊,這樣有利於靜態時序分析工具來進行時序驗證。
2,同步器的實現應使得全部輸入來自同一個時鐘域,而使用另外一個時鐘域的異步時鐘信號採樣數據。
3,面向時鐘信號的命名方式能夠幫助咱們肯定那些在不一樣異步時鐘域間須要處理的信號。
4,當存在多個跨時鐘域的控制信號時,咱們必須特別注意這些信號,保證這些控制信號到達新的時鐘域仍然可以保持正確的順序。
第二種異步fifo實現方式代碼以下,testbench文件和Makefile文件和前面的第一種方法相同。
fifo_async.v
module fifo_async (read_data, wfull, rempty, write_data, wq, wclk, wrst_n, rq, rclk, rrst_n); parameter DSIZE = 8; parameter ASIZE = 4; output [DSIZE-1:0] read_data; output wfull; output rempty; input [DSIZE-1:0] write_data; input wq, wclk, wrst_n; input rq, rclk, rrst_n; wire [ASIZE-1:0] wptr, rptr; wire [ASIZE-1:0] waddr, raddr; async_cmp #(ASIZE) async_cmp(.aempty_n(aempty_n), .afull_n(afull_n), .wptr(wptr), .rptr(rptr), .wrst_n(wrst_n)); fifomem2 #(DSIZE, ASIZE) fifomem2(.read_data(read_data), .write_data(write_data), .waddr(wptr), .raddr(rptr), .wq(wq), .wclk(wclk)); rptr_empty2 #(ASIZE) rptr_empty2(.rempty(rempty), .rptr(rptr), .aempty_n(aempty_n), .rq(rq), .rclk(rclk), .rrst_n(rrst_n)); wptr_full2 #(ASIZE) wptr_full2(.wfull(wfull), .wptr(wptr), .afull_n(afull_n), .wq(wq), .wclk(wclk), .wrst_n(wrst_n)); endmodule module fifomem2 #( parameter DATASIZE = 8, // Memory data word width parameter ADDRSIZE = 4 // depth is 2^(ADDRSIZE-1),extra one bit is to judge empty or full ) // Number of mem address bits ( output [DATASIZE-1:0] read_data, input [DATASIZE-1:0] write_data, input [ADDRSIZE-1:0] waddr, raddr, input wq, wclk ); localparam DEPTH = 1<<ADDRSIZE; // DEPTH = 2**ADDRSIZE reg [DATASIZE-1:0] MEM [0:DEPTH-1]; assign read_data = MEM[raddr]; always @(posedge wclk) if (wq) MEM[waddr] <= write_data; endmodule module async_cmp #( parameter ADDRSIZE = 4 // depth is 2^(ADDRSIZE-1),extra one bit is to judge empty or full ) ( input [ADDRSIZE-1:0] wptr, rptr, input wrst_n, output aempty_n, afull_n ); localparam N = ADDRSIZE-1; //internal signals reg direction; wire high = 1'b1; wire dirset_n = ~( (wptr[N]^rptr[N-1]) & ~(wptr[N-1]^rptr[N])); wire dirclr_n = ~((~(wptr[N]^rptr[N-1]) & (wptr[N-1]^rptr[N]))| ~wrst_n); always @(posedge high or negedge dirset_n or negedge dirclr_n) if (!dirclr_n) direction <= 1'b0; else if(!dirset_n) direction <= 1'b1; else direction <= high; assign aempty_n = ~((wptr == rptr) && !direction); assign afull_n = ~((wptr == rptr) && direction); endmodule module rptr_empty2 #( parameter ADDRSIZE = 4 // depth is 2^(ADDRSIZE-1),extra one bit is to judge empty or full ) ( input aempty_n, input rq,rclk,rrst_n, output reg rempty, output reg [ADDRSIZE-1:0] rptr ); reg [ADDRSIZE-1:0] rbin; reg rempty2; wire [ADDRSIZE-1:0] rgnext, rbnext; //--------------------------------------------------------------- // GRAYSTYLE2 pointer //--------------------------------------------------------------- always @(posedge rclk or negedge rrst_n) if (!rrst_n) begin rbin <= 0; rptr <= 0; end else begin rbin <= rbnext; rptr <= rgnext; end //--------------------------------------------------------------- // increment the binary count if not empty //--------------------------------------------------------------- assign rbnext = !rempty ? rbin + rq : rbin; assign rgnext = (rbnext>>1) ^ rbnext; // binary-to-gray conversion always @(posedge rclk or negedge aempty_n) if (!aempty_n) {rempty,rempty2} <= 2'b11; else {rempty,rempty2} <= {rempty2,~aempty_n}; endmodule module wptr_full2 #( parameter ADDRSIZE = 4 // depth is 2^(ADDRSIZE-1),extra one bit is to judge empty or full ) ( input afull_n, wq, wclk,wrst_n, output reg wfull, output reg [ADDRSIZE-1:0] wptr ); reg [ADDRSIZE-1:0] wbin; reg wfull2; wire [ADDRSIZE-1:0] wgnext, wbnext; //--------------------------------------------------------------- // GRAYSTYLE2 pointer //--------------------------------------------------------------- always @(posedge wclk or negedge wrst_n) if (!wrst_n) begin wbin <= 0; wptr <= 0; end else begin wbin <= wbnext; wptr <= wgnext; end //--------------------------------------------------------------- // increment the binary count if not full assign wbnext = !wfull ? wbin + wq : wbin; assign wgnext = (wbnext>>1) ^ wbnext; // binary-to-gray conversion always @(posedge wclk or negedge wrst_n or negedge afull_n) if (!wrst_n ) {wfull,wfull2} <= 2'b00; else if (!afull_n) {wfull,wfull2} <= 2'b11; else {wfull,wfull2} <= {wfull2,~afull_n}; endmodule