一种简单的、真实的基于循环序列的FIFO缓存设计

内容
fifo缓存是介于两个子系统之间的弹性存储器,其概念图如图1所示。它有两个控制信号,wr和rd,用于读操作和写操作。当wr被插入时,输入的数据被写入缓存,此时读操作被忽视。fifo缓存的head一般情况下总是有效的,因此可在任意时间被读取。rd信号实际上就像“remove”信号;当其被插入的时候,fifo缓存的第一个项(即head)被移除,下一个项变为可用项。
图1 fifo缓存的概念框图
在许多应用中,fifo缓存是一种临界组件,其实现的优化相当复杂。在本节中,我们介绍一种简单的、真实的基于循环序列设计的fifo缓存。更有效的、基于指定器件实现的fifo缓存可在altera或xilinx的相关手册中找到。
基于循环队列的实现
一种实现fifo缓存的方法是给寄存器文件添加一个控制电路。寄存器文件通过两个指针像循环队列一样来排列寄存器。写指针(write poniter)指向队列的头(head);读指针(read poniter)指向队列的尾(tail)。每次读操作或写操作,指针都会前进一个位置。8-字循环队列的操作如图2所示。
图2 基于循环队列的fifo缓存
fifo缓存通常包括两个标志信号,full和empty,相应地来指示fifo满(即不可写)或fifo空(即不可读)。这两种情况发生在读指针和写指针相等的时候,如图2(a)、(f)和(i)所示的情况。控制器最难的设计任务是获取一种分辨这两种情形的机制。一种方案是使用触发器来跟踪empty和full标志。当系统被初始化时,触发器被设置为1和0;然后在每一个时钟周期根据wr和rd的值来修改。
代码 fifo缓存
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 module fifo
#(
  parameter b=8, // number of bits in a word
            w=3  // number of address bits
)
(
  // global clock and aysn reset
  input clk,
  input rst_n,
  // fifo interface
  //  fifo control signnal
  input rd,
  input wr,
  //  fifo status signal
  output empty,
  output full,
  // fifo data bus
  input [b-1:0] w_data,
  output [b-1:0] r_data
);
// signal declaration
reg [b-1:0] array_reg [2**w-1:0];  // register array
reg [w-1:0] w_ptr_reg, w_ptr_next, w_ptr_succ;
reg [w-1:0] r_ptr_reg, r_ptr_next, r_ptr_succ;
reg full_reg, empty_reg, full_next, empty_next;
wire wr_en;
// body
// register file write operation
always @(posedge clk)
  if (wr_en)
    array_reg[w_ptr_reg] <= w_data;
// register file read operation
assign r_data = array_reg[r_ptr_reg];
// write enabled only when fifo is not full
assign wr_en = wr & ~full_reg;
// fifo control logic
// register for read and write pointers
always @(posedge clk, negedge rst_n)
  if (!rst_n)
  begin
    w_ptr_reg <= 0;
    r_ptr_reg <= 0;
    full_reg <= 1'b0;
    empty_reg <= 1'b1;
  end
  else
  begin
    w_ptr_reg <= w_ptr_next;
    r_ptr_reg <= r_ptr_next;
    full_reg <= full_next;
    empty_reg <= empty_next;
  end
// next-state logic for read and write pointers
always @*
begin
  // successive pointer values
  w_ptr_succ = w_ptr_reg + 1;
  r_ptr_succ = r_ptr_reg + 1;
  // default: keep old values
  w_ptr_next = w_ptr_reg;
  r_ptr_next = r_ptr_reg;
  full_next = full_reg;
  empty_next = empty_reg;
  case ({wr, rd})
    // 2'b00:  no op
    2'b01: // read
      if (~empty_reg) // not empty
      begin
        r_ptr_next = r_ptr_succ;
        full_next = 1'b0;
        if (r_ptr_succ==w_ptr_reg)
          empty_next = 1'b1;
      end
    2'b10: // write
    if (~full_reg) // not full
    begin
      w_ptr_next = w_ptr_succ;
      empty_next = 1'b0;
      if (w_ptr_succ==r_ptr_reg)
        full_next = 1'b1;
    end
    2'b11: // write and read
    begin
      w_ptr_next = w_ptr_succ;
      r_ptr_next = r_ptr_succ;
    end
  endcase
end
// output
assign full = full_reg;
assign empty = empty_reg;
endmodule
代码被分为寄存器文件和fifo控制器两部分。控制器由两个指针和两个标志触发器组成,它们的次态逻辑会检测wr和rd信号,以采取相应的动作。举例说,在“10”条件下,即暗示只发生写操作。先检查标志触发器,以确保缓存不为满。如果满足条件,我们将写指针前进一位,并清除空标志。再多存储一个字(偏移地址为1所对应的数据)可能使得fifo缓存满,即新的写指针赶上了读指针,我们使用w_ptr_succ==r_ptr_reg表达式来描述这一情况。
根据图2,我写了下面的testbench,其rtl仿真结果与图2一致。
代码 fifo缓存的testbench
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 `timescale 1ns/1ns
module fifo_tb;
localparam t=20; // clock period
// global clock and asyn reset
reg clk, rst_n;
// fifo interface
reg rd, wr;
wire empty, full;
reg [7:0] w_data;
wire [7:0] r_data;
// fifo instantiation
fifo #(.b(8), .w(3)) fifo_inst
(
  .clk(clk), .rst_n(rst_n),
  .rd(rd), .wr(wr),
  .empty(empty), .full(full),
  .w_data(w_data), .r_data(r_data)
);
// clcok
always
begin
  clk = 1'b0;
  #(t/2);
  clk = 1'b1;
  #(t/2);
end
// reset
initial
begin
  rst_n = 1'b0;
  #(t/2)
  rst_n = 1'b1;
end
// stimulus body
initial
begin
  // initial input; empty
  rd=0; wr=0; w_data=8'h00;
  @(posedge rst_n); // wait to deassert rst_n
  @(negedge clk); // wait for a clock
  // 1 write
  wr=1; w_data=8'h11;
  @(negedge clk); // wait to assert wr
  wr=0;
  @(negedge clk); // wait to deassert wr
  // 3 writes
  wr=1;
  repeat(3)
  begin
    w_data=w_data+8'h11;
    @(negedge clk);
  end
  wr=0;
  @(negedge clk);
  // 1 read
  rd=1;
  @(negedge clk); // wait to assert rd
  rd=0;
  @(negedge clk) // wait to deassert rd
  // 4 writes
  wr=1;
  repeat(4)
  begin
    w_data=w_data+8'h11;
    @(negedge clk);
  end
  wr=0;
  @(negedge clk);
  // 1 write; full
  wr=1; w_data=8'haa;
  @(negedge clk);
  wr=0;
  @(negedge clk);
  // 2 reads
  rd=1;
  repeat(2) @(negedge clk);
  rd=0;
  @(negedge clk);
  // 5 reads
  rd=1;
  repeat(5) @(negedge clk);
  rd=0;
  @(negedge clk);
  // 1 read; empty
  rd=1;
  @(negedge clk);
  rd=0;
  @(negedge clk);
  $stop;
end
endmodule
图3 rtl级仿真波形


小米集团与全国多家高校共建小米工作室
肌肉可以控制机器人吗
MIT的新研究,MIT科学家们将此技术称为内爆制造
亚光科技与赛迪集团将共同推动“中国芯应用创新中心”在地方落地,总投资约为10亿左右
高性能探测器(APDs)
一种简单的、真实的基于循环序列的FIFO缓存设计
PCB印制板外形加工的方法解析
Docker里玩转PetaLinux的上手教程
空中交工具是如何为全国的医院和诊所输送血液的
杭州19号线开通,赛思电子赋能“516公里线网”顺利贯通
电力表采集的参数有哪些
新能源汽车再迎重大利好_新能源汽车行情进入全面加速阶段
PCB设计软件分析
三维扫描检测仪外观质量控制
意法半导体副总裁:希望更多新行业参与到物联网中,并从中受益
小米即将进入2亿像素时代
工业智能网关搭建智能工厂解决方案
合见工软发布先进FPGA原型验证系统UniVista Advanced Prototyping System
怎样用arduino制作RGB照明系统
一体化扬尘自动监测设备的产品特点介绍
s