同步fifo與異步fifo

參考如下帖子: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異步

1.定義

  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設計的核心問題性能

2.同步FIFO之Verilog實現

  同步FIFO的意思是說FIFO的讀寫時鐘頻率相同,不一樣於異步FIFO,異步FIFO的讀寫時鐘頻率是不一樣的。同步FIFO的對外接口包括時鐘,清零,讀請求,寫請求,數據輸入總線,數據輸出總線,空以及滿信號。下面分別對同步FIFO的對外接口信號做一描述:

1. 時鐘,輸入,用於同步FIFO的讀和寫,上升沿有效;
2. 清零,輸入,異步清零信號,低電平有效,該信號有效時,FIFO被清空;
3. 寫請求,輸入,高電平有效,該信號有效時,代表外部電路請求向FIFO寫入數據;
4. 讀請求,輸入,高電平有效,該信號有效時,代表外部電路請求從FIFO中讀取數據;
5. 數據輸入總線,輸入,當寫信號有效時,數據輸入總線上的數據被寫入到FIFO中;
6. 數據輸出總線,輸出,當讀信號有效時,數據從FIFO中被讀出並放到數據輸出總線上;
7. 空,輸出,高電平有效,當該信號有效時,代表FIFO中沒有任何數據,所有爲空;
8. 滿,輸出,高電平有效,當該信號有效時,代表FIFO已經滿了,沒有空間可用來存貯數據。

下面的框圖主要描述同步FIFO的內部結構 :


image

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
View Code

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
View Code

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 &
View Code

verdi.f

fifo_sync.v
fifo_sync_tb.v
View Code

從波形中能夠看出執行結果是正確的。

image

3.異步FIFO之Verilog實現

      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中的數據輸出到讀數據總線上。這樣就實現了先進先出的功能。
這裏寫圖片描述

image

  寫地址產生模塊還根據讀地址和寫地址關係產生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
View Code

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
View Code

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 &
View Code

verdi.f

fifo_async.v
fifo_async_tb.v
View Code


下面的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
View Code


異步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
View Code
相關文章
相關標籤/搜索