標(biāo)題: 同步FIFO和異步FIFO的Verilog實現(xiàn) [打印本頁]

作者: 51黑黑黑    時間: 2016-2-23 01:19
標(biāo)題: 同步FIFO和異步FIFO的Verilog實現(xiàn)
FIFO是英文First In First Out  的縮寫,是一種先進(jìn)先出的數(shù)據(jù)緩存器,他與普通存儲器的區(qū)別是沒有外部讀寫地址線,這樣使用起來非常簡單,但缺點就是只能順序?qū)懭霐?shù)據(jù),順序的讀出數(shù)據(jù),  其數(shù)據(jù)地址由內(nèi)部讀寫指針自動加1完成,不能像普通存儲器那樣可以由地址線決定讀取或?qū)懭肽硞指定的地址。
  FIFO般用于不同時鐘域之間的數(shù)據(jù)傳輸,比如FIFO的一端是AD數(shù)據(jù)采集,  另一端是計算機(jī)的PCI總線,假設(shè)其AD采集的速率為16位 100K  SPS,那么每秒的數(shù)據(jù)量為100K×16bit=1.6Mbps,而PCI總線的速度為33MHz,總線寬度32bit,其最大傳輸速率為  1056Mbps,在兩個不同的時鐘域間就可以采用FIFO來作為數(shù)據(jù)緩沖。另外對于不同寬度的數(shù)據(jù)接口也可以用FIFO,例如單片機(jī)位8位數(shù)據(jù)輸出,而  DSP可能是16位數(shù)據(jù)輸入,在單片機(jī)與DSP連接時就可以使用FIFO來達(dá)到數(shù)據(jù)匹配的目的。

FIFO的分類根均FIFO工作的時鐘域,可以將FIFO分為同步FIFO和異步FIFO。同步FIFO是指讀時鐘和寫時鐘為同一個時鐘。在時鐘沿來臨時同時發(fā)生讀寫操作。異步FIFO是指讀寫時鐘不一致,讀寫時鐘是互相獨立的。

FIFO設(shè)計的難點  FIFO設(shè)計的難點在于怎樣判斷FIFO的空/滿狀態(tài)。為了保證數(shù)據(jù)正確的寫入或讀出,而不發(fā)生益處或讀空的狀態(tài)出現(xiàn),必須保證FIFO在滿的情況下,不  能進(jìn)行寫操作。在空的狀態(tài)下不能進(jìn)行讀操作。怎樣判斷FIFO的滿/空就成了FIFO設(shè)計的核心問題。
.........................................................................................................................................
同步FIFO的Verilog代碼  之一  在modlesim中驗證過。
/******************************************************
A fifo  controller verilog  description.
******************************************************/
module  fifo(datain, rd, wr, rst, clk, dataout, full, empty);
input [7:0]  datain;
input rd, wr, rst, clk;
output [7:0] dataout;
output full,  empty;
wire [7:0] dataout;
reg full_in, empty_in;
reg [7:0] mem  [15:0];
reg [3:0] rp, wp;
assign full = full_in;
assign empty =  empty_in;
// memory read out
assign dataout = mem[rp];
// memory write  in
always@(posedge clk) begin
    if(wr && ~full_in)  mem[wp]<=datain;
end
// memory write pointer  increment
always@(posedge clk or negedge rst) begin
    if(!rst)  wp<=0;
    else begin
      if(wr && ~full_in) wp<=  wp+1'b1;
    end
end
// memory read pointer  increment
always@(posedge clk or negedge rst)begin
    if(!rst) rp <=  0;
    else begin
      if(rd && ~empty_in) rp <= rp +  1'b1;
    end
end
// Full signal generate
always@(posedge clk or  negedge rst) begin
    if(!rst) full_in <= 1'b0;
    else  begin
      if( (~rd &&  wr)&&((wp==rp-1)||(rp==4'h0&&wp==4'hf)))
          full_in  <= 1'b1;
      else if(full_in && rd) full_in <= 1'b0;
     end
end
// Empty signal generate
always@(posedge clk or negedge rst)  begin
    if(!rst) empty_in <= 1'b1;
    else begin
       if((rd&&~wr)&&(rp==wp-1 ||  (rp==4'hf&&wp==4'h0)))
        empty_in<=1'b1;
      else  if(empty_in && wr) empty_in<=1'b0;
     end
end
endmodule
...........................................................................................................................
同步FIFO的Verilog代碼  之二  這一種設(shè)計的FIFO,是基于觸發(fā)器的。寬度,深度的擴(kuò)展更加方便,結(jié)構(gòu)化跟強(qiáng)。以下代碼在modelsim中驗證過。
module fifo_cell (sys_clk, sys_rst_n,  read_fifo, write_fifo, fifo_input_data,
                         next_cell_data, next_cell_full, last_cell_full, cell_data_out,  cell_full);
                        parameter WIDTH  =8;
                        parameter D = 2;
                        input  sys_clk;
                        input sys_rst_n;
                         input read_fifo, write_fifo;
                        input [WIDTH-1:0]  fifo_input_data;
                        input [WIDTH-1:0]  next_cell_data;
                        input next_cell_full,  last_cell_full;
                        output [WIDTH-1:0]  cell_data_out;
                        output  cell_full;
                        reg [WIDTH-1:0]  cell_data_reg_array;
                        reg [WIDTH-1:0]  cell_data_ld;
                        reg  cell_data_ld_en;
                        reg  cell_full;
                        reg  cell_full_next;
                        assign  cell_data_out=cell_data_reg_array;
                        always @(posedge  sys_clk or negedge sys_rst_n)
                           if  (!sys_rst_n)
                              cell_full <= #D  0;
                           else if (read_fifo ||  write_fifo)
                              cell_full <= #D  cell_full_next;
                        always @(write_fifo or read_fifo or  next_cell_full or last_cell_full or cell_full)
                            casex ({read_fifo, write_fifo})
                               2'b00:  cell_full_next = cell_full;
                               2'b01:  cell_full_next = next_cell_full;
                               2'b10:  cell_full_next = last_cell_full;
                               2'b11:  cell_full_next = cell_full;
                            endcase
                         always @(posedge sys_clk or negedge  sys_rst_n)
                              if  (!sys_rst_n)
                                 cell_data_reg_array [WIDTH-1:0]  <= #D 0;
                              else if  (cell_data_ld_en)
                                 cell_data_reg_array  [WIDTH-1:0] <= #D cell_data_ld [WIDTH-1:0];
                          always @(write_fifo or read_fifo or cell_full or  last_cell_full)   
                              casex  ({write_fifo,read_fifo,cell_full,last_cell_full})
                                   4'bx1_xx: cell_data_ld_en = 1'b1;
                                  4'b10_01:  cell_data_ld_en = 1'b1;
                                  default:  cell_data_ld_en =1'b0;
                               endcase
                         always @(write_fifo or read_fifo or  next_cell_full or cell_full or last_cell_full or fifo_input_data or  next_cell_data)
                              casex ({write_fifo, read_fifo,  next_cell_full, cell_full, last_cell_full})
                                  5'b10_x01: cell_data_ld[WIDTH-1:0] =  fifo_input_data[WIDTH-1:0];
                                 5'b11_01x:  cell_data_ld[WIDTH-1:0] =  fifo_input_data[WIDTH-1:0];
                                 default:  cell_data_ld[WIDTH-1:0] =  next_cell_data[WIDTH-1:0];
                               endcase
endmodule


module fifo_4cell(sys_clk, sys_rst_n,  fifo_input_data, write_fifo, fifo_out_data,
                  read_fifo,  full_cell0, full_cell1, full_cell2, full_cell3);
                  parameter  WIDTH = 8;
                  parameter D = 2;
                  input  sys_clk;
                  input sys_rst_n;
                  input  [WIDTH-1:0] fifo_input_data;
                  output [WIDTH-1:0]  fifo_out_data;
                  input read_fifo,  write_fifo;
                  output full_cell0, full_cell1, full_cell2,  full_cell3;
                  wire [WIDTH-1:0] dara_out_cell0,  data_out_cell1, data_out_cell2,
                                    data_out_cell3, data_out_cell4;
                  wire  full_cell4;
                  fifo_cell #(WIDTH,D) cell0
                   ( .sys_clk (sys_clk),
                    .sys_rst_n  (sys_rst_n),
                    .fifo_input_data  (fifo_input_data[WIDTH-1:0]),
                    .write_fifo  (write_fifo),
                    .next_cell_data  (data_out_cell1[WIDTH-1:0]),
                    .next_cell_full  (full_cell1),
                    .last_cell_full  (1'b1),
                    .cell_data_out (fifo_out_data  [WIDTH-1:0]),
                    .read_fifo  (read_fifo),
                    .cell_full  (full_cell0)
                   );

                  fifo_cell #(WIDTH,D)  cell1
                  ( .sys_clk (sys_clk),
                     .sys_rst_n (sys_rst_n),
                    .fifo_input_data  (fifo_input_data[WIDTH-1:0]),
                    .write_fifo  (write_fifo),
                    .next_cell_data  (data_out_cell2[WIDTH-1:0]),
                    .next_cell_full  (full_cell2),
                    .last_cell_full  (full_cell0),
                    .cell_data_out  (data_out_cell1[WIDTH-1:0]),
                    .read_fifo  (read_fifo),
                    .cell_full  (full_cell1)
                   );                  
                   fifo_cell #(WIDTH,D) cell2
                  ( .sys_clk  (sys_clk),
                    .sys_rst_n (sys_rst_n),
                     .fifo_input_data (fifo_input_data[WIDTH-1:0]),
                     .write_fifo (write_fifo),
                    .next_cell_data  (data_out_cell3[WIDTH-1:0]),
                    .next_cell_full  (full_cell3),
                    .last_cell_full  (full_cell1),
                    .cell_data_out  (data_out_cell2[WIDTH-1:0]),
                    .read_fifo  (read_fifo),
                    .cell_full  (full_cell2)
                   );                  

                  fifo_cell #(WIDTH,D)  cell3
                  ( .sys_clk (sys_clk),
                     .sys_rst_n (sys_rst_n),
                    .fifo_input_data  (fifo_input_data[WIDTH-1:0]),
                    .write_fifo  (write_fifo),
                    .next_cell_data  (data_out_cell4[WIDTH-1:0]),
                    .next_cell_full  (full_cell4),
                    .last_cell_full  (full_cell2),
                    .cell_data_out  (data_out_cell3[WIDTH-1:0]),
                    .read_fifo  (read_fifo),
                    .cell_full  (full_cell3)
                   );     
                   assign  data_out_cell4[WIDTH-1:0] = {WIDTH{1'B0}};
                   assign  full_cell4 = 1'b0;
endmodule                              

..........................................................................................................................
異步FIFO的Verilog代碼  之一  這個是基于RAM的異步FIFO代碼,個人認(rèn)為代碼結(jié)構(gòu)簡單易懂,非常適合于考試中填寫。記得10月份參加威盛的筆試的時候,就考過異步FIFO的實現(xiàn)。想當(dāng)初要是早點復(fù)習(xí),可能就可以通過威盛的筆試了。
與之前的用RAM實現(xiàn)的同步FIFO的程序相比,異步更為復(fù)雜。增加了讀寫控制信號的跨時鐘域的同步。此外,判空與判滿的也稍有不同。
module fifo1(rdata, wfull, rempty, wdata,  winc, wclk, wrst_n,rinc, rclk, rrst_n);
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;
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;
//-----------------雙口RAM存儲器--------------------
assign  rdata=mem[raddr];
always@(posedge wclk)
if (winc && !wfull)  mem[waddr] <= wdata;
//-------------同步rptr  指針-------------------------
always @(posedge wclk or negedge wrst_n)
if  (!wrst_n) {wq2_rptr,wq1_rptr} <= 0;
else {wq2_rptr,wq1_rptr} <=  {wq1_rptr,rptr};
//-------------同步wptr指針---------------------------
always  @(posedge rclk or negedge rrst_n)
if (!rrst_n) {rq2_wptr,rq1_wptr} <=  0;
else {rq2_wptr,rq1_wptr} <=  {rq1_wptr,wptr};
//-------------rempty產(chǎn)生與raddr產(chǎn)生-------------------
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 +  (rinc & ~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產(chǎn)生與waddr產(chǎn)生------------------------------
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 + (winc & ~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代碼的主要區(qū)別在于,空/滿狀態(tài)標(biāo)志的不同算法。
第一個算法:Clifford E.  Cummings的文章中提到的STYLE  #1,構(gòu)造一個指針寬度為N+1,深度為2^N字節(jié)的FIFO(為便方比較將格雷碼指針轉(zhuǎn)換為二進(jìn)制指針)。當(dāng)指針的二進(jìn)制碼中最高位不一致而其它N位都  相等時,F(xiàn)IFO為滿(在Clifford E.  Cummings的文章中以格雷碼表示是前兩位均不相同,而后兩位LSB相同為滿,這與換成二進(jìn)制表示的MSB不同其他相同為滿是一樣的)。當(dāng)指針完全相  等時,F(xiàn)IFO為空。
這種方法思路非常明了,為了比較不同時鐘產(chǎn)生的指針,需要把不同時鐘域的信號同步到本時鐘域中來,而使用Gray碼的目的就是使這個異步同步化的過  程發(fā)生亞穩(wěn)態(tài)的機(jī)率最小,而為什么要構(gòu)造一個N+1的指針,Clifford E.  Cummings也闡述的很明白,有興趣的讀者可以看下作者原文是怎么論述的,Clifford E. Cummings的這篇文章有Rev1.1 \  Rev1.2兩個版本,兩者在比較Gray碼指針時的方法略有不同,個Rev1.2版更為精簡。
第二種算法:Clifford E.  Cummings的文章中提到的STYLE #2。它將FIFO地址分成了4部分,每部分分別用高兩位的MSB 00 、01、 11、  10決定FIFO是否為going full 或going empty  (即將滿或空)。如果寫指針的高兩位MSB小于讀指針的高兩位MSB則FIFO為“幾乎滿”,若寫指針的高兩位MSB大于讀指針的高兩位MSB則FIFO  為“幾乎空”。
它是利用將地址空間分成4個象限(也就是四個等大小的區(qū)域),然后觀察兩個指針的相對位置,如果寫指針落后讀指針一個象限(25%的距離,呵呵),  則證明很可能要寫滿,反之則很可能要讀空,這個時候分別設(shè)置兩個標(biāo)志位dirset和dirrst,然后在地址完全相等的情況下,如果dirset有效就  是寫滿,如果dirrst有效就是讀空。
這種方法對深度為2^N字節(jié)的FIFO只需N位的指針即可,處理的速度也較第一種方法快。  
這段是說明的原話,算法一,還好理解。算法二,似乎沒有說清楚,不太明白。有興趣的可以查查論文,詳細(xì)研究下。
總之,第二種寫法是推薦的寫法。因為異步的多時鐘設(shè)計應(yīng)按以下幾個原則進(jìn)行設(shè)計:
1,盡可能的將多時鐘的邏輯電路(非同步器)分割為多個單時鐘的模塊,這樣有利于靜態(tài)時序分析工具來進(jìn)行時序驗證。
2,同步器的實現(xiàn)應(yīng)使得所有輸入來自同一個時鐘域,而使用另一個時鐘域的異步時鐘信號采樣數(shù)據(jù)。
3,面向時鐘信號的命名方式可以幫助我們確定那些在不同異步時鐘域間需要處理的信號。
4,當(dāng)存在多個跨時鐘域的控制信號時,我們必須特別注意這些信號,保證這些控制信號到達(dá)新的時鐘域仍然能夠保持正確的順序。

module fifo2 (rdata, wfull, rempty,  wdata,
winc, wclk, wrst_n, rinc, rclk, rrst_n);
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] 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(.rdata(rdata),
.wdata(wdata),
.waddr(wptr),
.raddr(rptr),
.wclken(winc),
.wclk(wclk));
rptr_empty2  #(ASIZE)  rptr_empty2(.rempty(rempty),
.rptr(rptr),
.aempty_n(aempty_n),
.rinc(rinc),
.rclk(rclk),
.rrst_n(rrst_n));
wptr_full2  #(ASIZE)  wptr_full2(.wfull(wfull),
.wptr(wptr),
.afull_n(afull_n),
.winc(winc),
.wclk(wclk),
.wrst_n(wrst_n));
endmodule
module  fifomem2 (rdata, wdata, waddr, raddr, wclken, wclk);
parameter DATASIZE = 8;  // Memory data word width
parameter ADDRSIZE = 4; // Number of memory address  bits
parameter DEPTH = 1<<ADDRSIZE; // DEPTH = 2**ADDRSIZE
output  [DATASIZE-1:0] rdata;
input [DATASIZE-1:0] wdata;
input [ADDRSIZE-1:0]  waddr, raddr;
input wclken, wclk;
`ifdef VENDORRAM
// instantiation of  a vendor's dual-port RAM
VENDOR_RAM MEM (.dout(rdata),  .din(wdata),
.waddr(waddr), .raddr(raddr),
.wclken(wclken),  .clk(wclk));
`else
reg [DATASIZE-1:0] MEM [0:DEPTH-1];
assign rdata =  MEM[raddr];
always @(posedge wclk)
if (wclken) MEM[waddr] <=  wdata;
`endif
endmodule
module async_cmp (aempty_n, afull_n, wptr,  rptr, wrst_n);
parameter ADDRSIZE = 4;
parameter N = ADDRSIZE-1;
output  aempty_n, afull_n;
input [N:0] wptr, rptr;
input wrst_n;
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;
//always  @(negedge dirset_n or negedge dirclr_n)
//if (!dirclr_n) direction <=  1'b0;
//else direction <= 1'b1;
assign aempty_n = ~((wptr == rptr)  && !direction);
assign afull_n = ~((wptr == rptr) &&  direction);
endmodule
module rptr_empty2 (rempty, rptr, aempty_n, rinc,  rclk, rrst_n);
parameter ADDRSIZE = 4;
output rempty;
output  [ADDRSIZE-1:0] rptr;
input aempty_n;
input rinc, rclk, rrst_n;
reg  [ADDRSIZE-1:0] rptr, rbin;
reg rempty, 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 + rinc : 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  (wfull, wptr, afull_n, winc, wclk, wrst_n);
parameter ADDRSIZE = 4;
output  wfull;
output [ADDRSIZE-1:0] wptr;
input afull_n;
input winc, wclk,  wrst_n;
reg [ADDRSIZE-1:0] wptr, wbin;
reg wfull, 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 + winc : 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











歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1