FIFO 同步、異步以及Verilog代碼實(shí)現(xiàn)
FIFO 很重要,之前參加的各類電子公司的邏輯設(shè)計(jì)的筆試幾乎都會(huì)考到。
FIFO是英文First In First Out 的縮寫,是一種先進(jìn)先出的數(shù)據(jù)緩存器,他與普通存儲(chǔ)器的區(qū)別是沒有外部讀寫地址線,這樣使用起來(lái)非常簡(jiǎn)單,但缺點(diǎn)就是只能順序?qū)懭霐?shù)據(jù),順序的讀出數(shù)據(jù), 其數(shù)據(jù)地址由內(nèi)部讀寫指針自動(dòng)加1完成,不能像普通存儲(chǔ)器那樣可以由地址線決定讀取或?qū)懭肽硞€(gè)指定的地址。
FIFO一般用于不同時(shí)鐘域之間的數(shù)據(jù)傳輸,比如FIFO的一端是AD數(shù)據(jù)采集, 另一端是計(jì)算機(jī)的PCI總線,假設(shè)其AD采集的速率為16位 100K SPS,那么每秒的數(shù)據(jù)量為100K&TImes;16bit=1.6Mbps,而PCI總線的速度為33MHz,總線寬度32bit,其最大傳輸速率為 1056Mbps,在兩個(gè)不同的時(shí)鐘域間就可以采用FIFO來(lái)作為數(shù)據(jù)緩沖。另外對(duì)于不同寬度的數(shù)據(jù)接口也可以用FIFO,例如單片機(jī)位8位數(shù)據(jù)輸出,而 DSP可能是16位數(shù)據(jù)輸入,在單片機(jī)與DSP連接時(shí)就可以使用FIFO來(lái)達(dá)到數(shù)據(jù)匹配的目的。
FIFO的分類根均FIFO工作的時(shí)鐘域,可以將FIFO分為同步FIFO和異步FIFO。同步FIFO是指讀時(shí)鐘和寫時(shí)鐘為同一個(gè)時(shí)鐘。在時(shí)鐘沿來(lái)臨時(shí)同時(shí)發(fā)生讀寫操作。異步FIFO是指讀寫時(shí)鐘不一致,讀寫時(shí)鐘是互相獨(dú)立的。
FIFO設(shè)計(jì)的難點(diǎn) FIFO設(shè)計(jì)的難點(diǎn)在于怎樣判斷FIFO的空/滿狀態(tài)。為了保證數(shù)據(jù)正確的寫入或讀出,而不發(fā)生益處或讀空的狀態(tài)出現(xiàn),必須保證FIFO在滿的情況下,不 能進(jìn)行寫操作。在空的狀態(tài)下不能進(jìn)行讀操作。怎樣判斷FIFO的滿/空就成了FIFO設(shè)計(jì)的核心問題。
.........................................................................................................................................
同步FIFO的Verilog代碼 之一
在modlesim中驗(yàn)證過(guò)。
/******************************************************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+1b1; endend// memory read pointer incrementalways@(posedge clk or negedge rst)begin if(!rst) rp = 0; else begin if(rd ~empty_in) rp = rp + 1b1; endend// Full signal generatealways@(posedge clk or negedge rst) begin if(!rst) full_in = 1b0; else begin if( (~rd wr)((wp==rp-1)||(rp==4h0wp==4hf))) full_in = 1b1; else if(full_in rd) full_in = 1b0; endend// Empty signal generatealways@(posedge clk or negedge rst) begin if(!rst) empty_in = 1b1; else begin if((rd~wr)(rp==wp-1 || (rp==4hfwp==4h0))) empty_in=1b1; else if(empty_in wr) empty_in=1b0; endendendmodule
...........................................................................................................................
同步FIFO的Verilog代碼 之二
這一種設(shè)計(jì)的FIFO,是基于觸發(fā)器的。寬度,深度的擴(kuò)展更加方便,結(jié)構(gòu)化跟強(qiáng)。以下代碼在modelsim中驗(yàn)證過(guò)。
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}) 2b00: cell_full_next = cell_full; 2b01: cell_full_next = next_cell_full; 2b10: cell_full_next = last_cell_full; 2b11: 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}) 4bx1_xx: cell_data_ld_en = 1b1; 4b10_01: cell_data_ld_en = 1b1; default: cell_data_ld_en =1b0; 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}) 5b10_x01: cell_data_ld[WIDTH-1:0] = fifo_input_data[WIDTH-1:0]; 5b11_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 (1b1), .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{1B0}}; assign full_cell4 = 1b0;endmodule
..........................................................................................................................
異步FIFO的Verilog代碼 之一
這個(gè)是基于RAM的異步FIFO代碼,個(gè)人認(rèn)為代碼結(jié)構(gòu)簡(jiǎn)單易懂,非常適合于考試中填寫。記得10月份參加威盛的筆試的時(shí)候,就考過(guò)異步FIFO的實(shí)現(xiàn)。想當(dāng)初要是早點(diǎn)復(fù)習(xí),可能就可以通過(guò)威盛的筆試了。
與之前的用RAM實(shí)現(xiàn)的同步FIFO的程序相比,異步更為復(fù)雜。增加了讀寫控制信號(hào)的跨時(shí)鐘域的同步。此外,判空與判滿的也稍有不同。
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:(1ASIZE)-1];wire [ASIZE-1:0] waddr, raddr;wire [ASIZE:0] rgraynext, rbinnext,wgraynext,wbinnext;wire rempty_val,wfull_val;//-----------------雙口RAM存儲(chǔ)器--------------------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 pointerbeginif (!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 resetassign rempty_val = (rgraynext == rq2_wptr);always @(posedge rclk or negedge rrst_n)beginif (!rrst_n) rempty = 1b1;else rempty = rempty_val;end//---------------wfull產(chǎn)生與waddr產(chǎn)生------------------------------always @(posedge wclk or negedge wrst_n) // GRAYSTYLE2 pointerif (!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 = 1b0;else wfull = wfull_val;endmodule
..........................................................................................................................
異步FIFO的Verilog代碼 之二
與前一段異步FIFO代碼的主要區(qū)別在于,空/滿狀態(tài)標(biāo)志的不同算法。
第一個(gè)算法:Clifford E. Cummings的文章中提到的STYLE #1,構(gòu)造一個(gè)指針寬度為N+1,深度為2^N字節(jié)的FIFO(為便方比較將格雷碼指針轉(zhuǎn)換為二進(jìn)制指針)。當(dāng)指針的二進(jìn)制碼中最高位不一致而其它N位都 相等時(shí),F(xiàn)IFO為滿(在Clifford E. Cummings的文章中以格雷碼表示是前兩位均不相同,而后兩位LSB相同為滿,這與換成二進(jìn)制表示的MSB不同其他相同為滿是一樣的)。當(dāng)指針完全相 等時(shí),F(xiàn)IFO為空。
這種方法思路非常明了,為了比較不同時(shí)鐘產(chǎn)生的指針,需要把不同時(shí)鐘域的信號(hào)同步到本時(shí)鐘域中來(lái),而使用Gray碼的目的就是使這個(gè)異步同步化的過(guò) 程發(fā)生亞穩(wěn)態(tài)的機(jī)率最小,而為什么要構(gòu)造一個(gè)N+1的指針,Clifford E. Cummings也闡述的很明白,有興趣的讀者可以看下作者原文是怎么論述的,Clifford E. Cummings的這篇文章有Rev1.1 Rev1.2兩個(gè)版本,兩者在比較Gray碼指針時(shí)的方法略有不同,個(gè)Rev1.2版更為精簡(jiǎn)。
第二種算法:Clifford E. Cummings的文章中提到的STYLE #2。它將FIFO地址分成了4部分,每部分分別用高兩位的MSB 00 、01、 11、 10決定FIFO是否為going full 或going empty (即將滿或空)。如果寫指針的高兩位MSB小于讀指針的高兩位MSB則FIFO為“幾乎滿”,若寫指針的高兩位MSB大于讀指針的高兩位MSB則FIFO 為“幾乎空”。
它是利用將地址空間分成4個(gè)象限(也就是四個(gè)等大小的區(qū)域),然后觀察兩個(gè)指針的相對(duì)位置,如果寫指針落后讀指針一個(gè)象限(25%的距離,呵呵), 則證明很可能要寫滿,反之則很可能要讀空,這個(gè)時(shí)候分別設(shè)置兩個(gè)標(biāo)志位dirset和dirrst,然后在地址完全相等的情況下,如果dirset有效就 是寫滿,如果dirrst有效就是讀空。
這種方法對(duì)深度為2^N字節(jié)的FIFO只需N位的指針即可,處理的速度也較第一種方法快。
這段是說(shuō)明的原話,算法一,還好理解。算法二,似乎沒有說(shuō)清楚,不太明白。有興趣的可以查查論文,詳細(xì)研究下。
總之,第二種寫法是推薦的寫法。因?yàn)楫惒降亩鄷r(shí)鐘設(shè)計(jì)應(yīng)按以下幾個(gè)原則進(jìn)行設(shè)計(jì):1,盡可能的將多時(shí)鐘的邏輯電路(非同步器)分割為多個(gè)單時(shí)鐘的模塊,這樣有利于靜態(tài)時(shí)序分析工具來(lái)進(jìn)行時(shí)序驗(yàn)證。2,同步器的實(shí)現(xiàn)應(yīng)使得所有輸入來(lái)自同一個(gè)時(shí)鐘域,而使用另一個(gè)時(shí)鐘域的異步時(shí)鐘信號(hào)采樣數(shù)據(jù)。3,面向時(shí)鐘信號(hào)的命名方式可以幫助我們確定那些在不同異步時(shí)鐘域間需要處理的信號(hào)。4,當(dāng)存在多個(gè)跨時(shí)鐘域的控制信號(hào)時(shí),我們必須特別注意這些信號(hào),保證這些控制信號(hào)到達(dá)新的時(shí)鐘域仍然能夠保持正確的順序。
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 = 1ADDRSIZE; // DEPTH = 2**ADDRSIZEoutput [DATASIZE-1:0] rdata;input [DATASIZE-1:0] wdata;input [ADDRSIZE-1:0] waddr, raddr;input wclken, wclk;`ifdef VENDORRAM// instanTIaTIon of a vendors dual-port RAMVENDOR_RAM MEM (.dout(rdata), .din(wdata),.waddr(waddr), .raddr(raddr),.wclken(wclken), .clk(wclk));`elsereg [DATASIZE-1:0] MEM [0:DEPTH-1];assign rdata = MEM[raddr];always @(posedge wclk)if (wclken) MEM[waddr] = wdata;`endifendmodulemodule 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 = 1b1;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 = 1b0;else if (!dirset_n) direction = 1b1;else direction = high;//always @(negedge dirset_n or negedge dirclr_n)//if (!dirclr_n) direction = 1b0;//else direction = 1b1;assign aempty_n = ~((wptr == rptr) !direction);assign afull_n = ~((wptr == rptr) direction);endmodulemodule 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) beginrbin = 0;rptr = 0;endelse beginrbin = 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 conversionalways @(posedge rclk or negedge aempty_n)if (!aempty_n) {rempty,rempty2} = 2b11;else {rempty,rempty2} = {rempty2,~aempty_n};endmodulemodule 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) beginwbin = 0;wptr = 0;endelse beginwbin = 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 conversionalways @(posedge wclk or negedge wrst_n or negedge afull_n)if (!wrst_n ) {wfull,wfull2} = 2b00;else if (!afull_n) {wfull,wfull2} = 2b11;else {wfull,wfull2} = {wfull2,~afull_n};endmodule
.............................................................
評(píng)論