同步FIFO

fifo介绍

先入先出的数据缓存器,没有外部读写地址线,可同时读写。

  • fifo种类

    FIFO有同步fifo和异步fifo之分。同步fifo只有一个时钟,读写时钟一样;异步fifo读写时钟不同,可能不同频率,或者同频不同相。

  • fifo用途

    1. 缓存数据

    如果输入端burst一个数据,接收端不能立马全部接受,需要fifo存住这些数据。
    2. 跨时钟域数据传输

    同步数据。

同步fifo

同步FIFO就是读写时钟是一个,读写地址是同步的。

  • 注意

  1. 读写地址是下一次要进行读写的地址,是否进行读写需要判断最终读写有效(通过输入读写位和空满标志)
  2. 最终读写有效才进行读写
  3. 地址变换:最终读写有效才进行地址跳变,否则的话这个地址还没进行读写就跳过去了。
  • fifo空满判断

  1. 可以在读写地址上增加一位,用来判断是否空满。空满只通过比较地址,与读写有效输入端口无关。

空:读写地址的MSB相同,其他位也相同;满:读写地址的MSB不同,其他位相同

用组合逻辑实现,与时钟信号无关,没有延迟,直接根据当前的读写地址就可以判断
1
2
3
4
  // 空:读写地址相同    clog2()函数是求以2为底数的对数
assign full = (r_addr[clog2(Depth)-1:0]==w_addr[clog2(Depth)-1:0]) && (r_addr[clog2(Depth)] ^ w_addr[clog2(Depth)]);
// 满:读写地址的MSB不同,其他位相同
assign empty =(r_addr[clog2(Depth)-1:0]==w_addr[clog2(Depth)-1:0]) && (r_addr[clog2(Depth)] == w_addr[clog2(Depth)]);
  1. 也可以用一个计数器cnt来表示当前有多少个没有读走的数据,当写有效,+1,读有效-1,又读又写不变。但这需要额外的计数器,并且可能影响FIFO最终的速度

  2. 读写地址位数不变,通过地址和读写有效输入端口共同判断

    用时序逻辑实现,有一个时钟的延迟,需要根据读写有效输入位判断空满,判断的是下一个周期的空满状态。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
      /*
    rd,wr 读写有效输入端口
    rp,wp 读写地址
    */
    // 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)))
    // 下个周期只写不读,如果当前写地址落后读一个地址或者读地址是0000,写地址是1111,下个周期fifo满了。
    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
1
2
3
4
5
6
7
8

- #### 读写地址产生

1. FIFO的读写地址产生比较简单 当读使能有效时 在时钟作用下 读地址加1 当写使能有效时 写地址加1
2. 当FIFO深度较大时 同时FIFO的速度要求较高时 可以采用线性反馈移位计数器 LFSR它的速度非常快 但是要牺牲一个地址。

**线性反馈移位寄存器关键是要设计出2^n个状态。**

/************************************************************\

  • Generation of Read and Write address pointers. They use *
  • LFSR counters, which are very fast. Because of the *
  • nature of LFSR, one address is sacrificed. *
    • ************************************************************/
      wire read_linearfeedback, write_linearfeedback;
      assign read_linearfeedback = ! (read_addr[8] ^ read_addr[4]);
      assign write_linearfeedback = ! (write_addr[8] ^ write_addr[4]);
      // 读地址反馈
      always @(posedge clock or posedge fifo_gsr)
      if (fifo_gsr)
      read_addr <= 9’h0;
      else if (read_allow)
      read_addr <= { read_addr[7], read_addr[6], read_addr[5],
      read_addr[4], read_addr[3], read_addr[2],
      read_addr[1], read_addr[0], read_linearfeedback };
      // 写地址反馈
      always @(posedge clock or posedge fifo_gsr)
      if (fifo_gsr)
      write_addr <= 9’h0
      else if (write_allow)
      write_addr <= { write_addr[7], write_addr[6], write_addr[5],
      write_addr[4], write_addr[3], write_addr[2],
      write_addr[1], write_addr[0], write_linearfeedback };
1
2
3
- #### 同步fifo的verilog代码


// file name : fifo_1.v
// email : dyk1203@126.com
// author : dong yk

`timescale 1ns/100ps
module fifo #(parameter Width=8,
parameter Depth=8) //FIFO的深度
( input clk,
input rst,
input we,
input re,
input [Width-1:0] data_i,
output full,
output empty,
output[Width-1:0] data_o
);

function integer clog2; // 求2的对数 ,log2(x)。得到memory的地址宽度
input integer value;
begin
for(clog2=0;value>1;value=value>>1) begin
clog2=clog2+1;
end
end
endfunction
reg [Width-1:0] dout;
reg [clog2(Depth):0] r_addr; // 读地址
reg [clog2(Depth):0] w_addr; // 写地址
wire re_flag = (re==1’b1) && (empty==1’b0); // 在输入读有效,且fifo不为空时,读有效
wire we_flag = (we==1’b1) && (full == 1’b0);// 在输入写有效,且fifo不为满时,写有效

reg[Width-1:0] memory[0:Depth-1]; // 申请fifo内的存储空间
// 空:读写地址相同
assign full = (r_addr[clog2(Depth)-1:0]==w_addr[clog2(Depth)-1:0]) && (r_addr[clog2(Depth)] ^ w_addr[clog2(Depth)]);
// 满:读写地址的MSB不同,其他位相同
assign empty =(r_addr[clog2(Depth)-1:0]==w_addr[clog2(Depth)-1:0]) && (r_addr[clog2(Depth)] == w_addr[clog2(Depth)]);
assign data_o = dout;

always@(posedge clk or negedge rst) begin
if(!rst) begin
r_addr <= 0;
dout <= 0;
end else begin
r_addr <= re_flag?(r_addr+1) : r_addr ; // 读地址累加
dout <= re_flag?memory[r_addr[clog2(Depth)-1:0]]:dout; // 数据输出
end
end
always @(posedge clk or negedge rst) begin
if(!rst) begin
w_addr <= 0;
end else begin
w_addr <= we_flag?(w_addr+1) : w_addr; // 写地址累加
memory[w_addr[clog2(Depth)-1:0]] <= we_flag?data_i:memory[w_addr[clog2(Depth)-1:0]]; // 写入数据
end
end
endmodule

1
2
- #### 在fifo中增加一个表示当前剩余多少数据的值usedw

// file name : fifo_1.v
//
// author : dong yk
// description : 增加usedw 表示fifo中已经有了的数据个数
`timescale 1ns/100ps
module fifo #(parameter Width=8,
parameter Depth=8) //FIFO的深度
( input clk,
input rst,
input we,
input re,
input [Width-1:0] data_i,
output full,
output empty,
output[Width-1:0] data_o,
output [clog2(Depth)-1:0] usedw
);

function integer clog2; // 求2的对数 ,log2(x)。得到memory的地址宽度
input integer value;
begin
for(clog2=0;value>1;value=value>>1) begin
clog2=clog2+1;
end
end
endfunction
reg [Width-1:0] dout;
reg [clog2(Depth):0] r_addr; // 读地址
reg [clog2(Depth):0] w_addr; // 写地址
reg [clog2(Depth)-1:0] usedw_r;
wire re_flag = (re==1’b1) && (empty==1’b0); // 在输入读有效,且fifo不为空时,读有效
wire we_flag = (we==1’b1) && (full == 1’b0);// 在输入写有效,且fifo不为满时,写有效

reg[Width-1:0] memory[0:Depth-1]; // 申请fifo内的存储空间
// 空:读写地址相同
assign full = (r_addr[clog2(Depth)-1:0]==w_addr[clog2(Depth)-1:0]) && (r_addr[clog2(Depth)] ^ w_addr[clog2(Depth)]);
// 满:读写地址的MSB不同,其他位相同
assign empty =(r_addr[clog2(Depth)-1:0]==w_addr[clog2(Depth)-1:0]) && (r_addr[clog2(Depth)] == w_addr[clog2(Depth)]);
assign data_o = dout;

always@(posedge clk or negedge rst) begin
if(!rst) begin
r_addr <= 0;
dout <= 0;
end else begin
r_addr <= re_flag?(r_addr+1) : r_addr ; // 读地址累加
dout <= re_flag?memory[r_addr[clog2(Depth)-1:0]]:dout; // 数据输出
end
end
always @(posedge clk or negedge rst) begin
if(!rst) begin
w_addr <= 0;
end else begin
w_addr <= we_flag?(w_addr+1) : w_addr; // 写地址累加
memory[w_addr[clog2(Depth)-1:0]] <= we_flag?data_i:memory[w_addr[clog2(Depth)-1:0]]; // 写入数据
end
end
assign usedw = usedw_r;
always @(posedge clk or negedge rst) begin
if(!rst) begin
usedw_r <= 0;
end//if
else begin
case({we_flag,re_flag})
2’b00: begin
usedw_r <= usedw_r;
end//00
2’b01: begin
if(usedw_r==(Depth-1))
usedw_r <= usedw_r;
else
usedw_r <= usedw_r + 1’b1;
end//01
2’b10: begin
if(usedw_r==0)
usedw_r <= usedw_r;
else
usedw_r <= usedw_r - 1’b1;
end//10
2’b11: begin
usedw_r <= usedw_r;
end//11
default: usedw_r <= 0;
endcase
end//else
end//always

endmodule

1


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!