FIFO 很重要,之前參加的各類電子公司的邏輯設計的筆試幾乎都會考到。
FIFO是英文First In First Out 的縮寫,是一種先進先出的數據緩存器,他與普通存儲器的區別是沒有外部讀寫地址線,這樣使用起來非常簡單,但缺點就是只能順序寫入數據,順序的讀出數據, 其數據地址由內部讀寫指針自動加1完成,不能像普通存儲器那樣可以由地址線決定讀取或寫入某個指定的地址。
?FIFO一般用于不同時鐘域之間的數據傳輸,比如FIFO的一端是AD數據采集, 另一端是計算機的PCI總線,假設其AD采集的速率為16位 100K SPS,那么每秒的數據量為100K×16bit=1.6Mbps,而PCI總線的速度為33MHz,總線寬度32bit,其最大傳輸速率為 1056Mbps,在兩個不同的時鐘域間就可以采用FIFO來作為數據緩沖。另外對于不同寬度的數據接口也可以用FIFO,例如單片機位8位數據輸出,而 DSP可能是16位數據輸入,在單片機與DSP連接時就可以使用FIFO來達到數據匹配的目的。
?
FIFO的分類根均FIFO工作的時鐘域,可以將FIFO分為同步FIFO和異步FIFO。同步FIFO是指讀時鐘和寫時鐘為同一個時鐘。在時鐘沿來臨時同時發生讀寫操作。異步FIFO是指讀寫時鐘不一致,讀寫時鐘是互相獨立的。
?
FIFO設計的難點 FIFO設計的難點在于怎樣判斷FIFO的空/滿狀態。為了保證數據正確的寫入或讀出,而不發生益處或讀空的狀態出現,必須保證FIFO在滿的情況下,不 能進行寫操作。在空的狀態下不能進行讀操作。怎樣判斷FIFO的滿/空就成了FIFO設計的核心問題。
.........................................................................................................................................
同步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 outassign dataout = mem[rp];// memory write inalways@(posedge clk) begin??? if(wr && ~full_in) mem[wp]<=datain;end// memory write pointer incrementalways@(posedge clk or negedge rst) begin??? if(!rst) wp<=0;??? else begin????? if(wr && ~full_in) wp<= wp+1'b1;??? endend// memory read pointer incrementalways@(posedge clk or negedge rst)begin??? if(!rst) rp <= 0;??? else begin????? if(rd && ~empty_in) rp <= rp + 1'b1;??? endend// Full signal generatealways@(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;??? endend// Empty signal generatealways@(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;??? endendendmodule
...........................................................................................................................
同步FIFO的Verilog代碼 之二
這一種設計的FIFO,是基于觸發器的。寬度,深度的擴展更加方便,結構化跟強。以下代碼在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];????????????????????????????? endcaseendmodule
?
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代碼,個人認為代碼結構簡單易懂,非常適合于考試中填寫。記得10月份參加威盛的筆試的時候,就考過異步FIFO的實現。想當初要是早點復習,可能就可以通過威盛的筆試了。
與之前的用RAM實現的同步FIFO的程序相比,異步更為復雜。增加了讀寫控制信號的跨時鐘域的同步。此外,判空與判滿的也稍有不同。
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<
..........................................................................................................................
異步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 、01、 11、 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,當存在多個跨時鐘域的控制信號時,我們必須特別注意這些信號,保證這些控制信號到達新的時鐘域仍然能夠保持正確的順序。
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));endmodulemodule fifomem2 (rdata, wdata, waddr, raddr, wclken, wclk);parameter DATASIZE = 8; // Memory data word widthparameter ADDRSIZE = 4; // Number of memory address bitsparameter DEPTH = 1<
.............................................................
評論
查看更多