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 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; endend
// 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; endend
// 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; endend
// 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; endend
endmodule
...........................................................................................................................
同步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_dataor 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< //-----------------双⼝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产⽣与raddr产⽣-------------------always @(posedge rclk or negedge rrst_n) // GRAYSTYLE2 pointerbegin 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 resetassign 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产⽣与waddr产⽣------------------------------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 <= 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 、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));endmodule module fifomem2 (rdata, wdata, waddr, raddr, wclken, wclk);parameter DATASIZE = 8; // Memory data word width parameter ADDRSIZE = 4; // Number of memory address bitsparameter DEPTH = 1< // instantiation of a vendor's dual-port RAMVENDOR_RAM MEM (.dout(rdata), .din(wdata),.waddr(waddr), .raddr(raddr),.wclken(wclken), .clk(wclk));`else `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) beginrbin <= 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 conversionalways @(posedge rclk or negedge aempty_n)if (!aempty_n) {rempty,rempty2} <= 2'b11; 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) beginwbin <= 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 conversionalways @(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
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- baoquwan.com 版权所有 湘ICP备2024080961号-7
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务