Verilog设计FIFO

关注、星标公众号,精彩内容每日送达
来源:网络素材

FIFO缓存是介于两个子系统之间的弹性存储器,其概念图如图1所示。它有两个控制信号,wr和rd,用于读操作和写操作。当wr被插入时,输入的数据被写入缓存,此时读操作被忽视。FIFO缓存的head一般情况下总是有效的,因此可在任意时间被读取。rd信号实际上就像“remove”信号;当其被插入的时候,FIFO缓存的第一个项(即head)被移除,下一个项变为可用项。

 6c03c215af582d4e298302de97724859.png

图1 FIFO缓存的概念框图

在许多应用中,FIFO缓存是一种临界组件,其实现的优化相当复杂。在本节中,我们介绍一种简单的、真实的基于循环序列设计的FIFO缓存。更有效的、基于指定器件实现的FIFO缓存可在Altera或Xilinx的相关手册中找到。

基于循环队列的实现

一种实现FIFO缓存的方法是给寄存器文件添加一个控制电路。寄存器文件通过两个指针像循环队列一样来排列寄存器。写指针(write poniter)指向队列的头(head);读指针(read poniter)指向队列的尾(tail)。每次读操作或写操作,指针都会前进一个位置。8-字循环队列的操作如图2所示。

e1b3d1f19848a5e474cdd6e083ce53a0.jpeg

图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

8c1c10feac595dfffd959d8c233f8654.png

图3 RTL级仿真波形

参考

1 Pong P. Chu.FPGA Prototyping By Verilog Examples: Xilinx Spartan-3 Version.Wiley

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值