当前位置:   article > 正文

FPGA的设计艺术(18)如何使用Verilog中的数组对存储器进行建模?_fpga数组

fpga数组

前言

Verilog中的二维数组很有用,可以使用for以及generate for配合二维数组进行使用,可以代替大量寄存器的场合,其实大量同类寄存器可以使用存储器进行代替,Verilog中可以使用二维数组对存储器进行建模。

存储器由FPGA的什么资源来构成,可以由综合属性来设定,或者由综合工具来自动设置。这不是本文的重点,具体可见:Vivado 随笔(1) 综合属性之 ram_style & rom_style?

下面一起来看Verilog中的数组以及如何使用数组对存储器进行建模。

存储器

存储器是数字存储元件,可帮助在数字电路中存储数据和信息。FPGA中的RAM以及ROM等存储器的模型大概都是如下这样,我们可以设计不同的位宽和深度:
内存结构

在FPGA中,我们可以使用EDA工具提供的IP核对存储元件进行定制,当然也可以使用Verilog中的数组进行建模,下面介绍:

Verilog中的数组

我们还可以在verilog中创建和使用数组类型。这些在内存建模中特别有用。

为了在verilog中声明一个数组,我们只需要在变量名后添加一个额外的字段即可声明数组中有多少个元素。

下面的代码段显示了用于在Verilog中声明数组类型的常规语法。我们使用字段声明数组的大小。

// General syntax to declare an array type

<type> <size> <variable_name> <elements>;
  • 1
  • 2
  • 3

例如,假设我们要创建一个4位wire类型的数组。我们希望数组中总共有16个元素。下面的Verilog代码显示了如何创建此数组。

wire [3:0] example [15:0];
  • 1

我们可以使用方括号访问数组类型中的各个元素。例如,下面的verilog代码显示了如何将Fh的值分配给示例数组中的第一个元素。

assign example[0] = 4'hF;
  • 1

数组允许以Verilog为reg,wire,integer和real数据类型。

reg        y1 [11:0];        // y is an scalar reg array of depth=12, each 1-bit wide
wire [7:0] y2 [3:0]          // y is an 8-bit vector net with a depth of 4

  • 1
  • 2
  • 3

必须指定每个维的索引才能访问数组的特定元素,并且可以是其他变量的表达式。可以为Verilog支持的任何不同数据类型形成一个数组。

y1 = 0; 						// Illegal - All elements can't be assigned in a single go

y2[0] = 8'ha2; 			// Assign 0xa2 to index=0
y2[2] = 8'h1c; 			// Assign 0x1c to index=2

  • 1
  • 2
  • 3
  • 4
  • 5

多维数组

在verilog 1995标准中,我们只能创建一维数组,例如上一节中使用的数组。

但是,当我们使用verilog 2001标准时,我们还可以创建具有多个维度的数组。

为此,我们只需添加另一个字段即可定义所需元素的数量。

下面的代码段显示了用于在verilog中创建2D数组的常规语法。

// General syntax to declare an array type

<type> <size> <variable_name> <elements> <elements>;
  • 1
  • 2
  • 3

举个例子,让我们考虑一下我们想从前面的例子中修改数组大小的情况。

现在,我们要创建一个变量,该变量可以存储2个元素,两个元素都有16个4位wire元素。

为此,我们只需在声明的末尾添加一个额外的字段即可。下面的代码段显示了我们将如何执行此操作。

wire [3:0] example [15:0][1:0];

reg  [7:0] y3 [0:1][0:3];    // y is a 2D array rows=2,cols=4 each 8-bit wide
  • 1
  • 2
  • 3

我们也使用与一维数组相同的方法来对多维数组赋值。但是,我们现在使用一对方括号在数组的两个维度上定义元素。

例如,假设我们要为两个维度中的最后一个元素分配Ah的值。下面的verilog代码显示了如何将数据分配给数组中的该元素。

和上面数组一样的原则:

必须指定每个维的索引才能访问数组的特定元素,并且可以是其他变量的表达式。可以为Verilog支持的任何不同数据类型形成一个数组。

y3[1][2] = 8'hdd; 	// Assign 0xdd to rows=1 cols=2
y3[0][0] = 8'haa; 	// Assign 0xaa to rows=0 cols=0

example [15][1] = 4'ha;

example [15][0] = 4'ha;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

数组建模以及初始化示例

mem1是一个8位向量,mem2是一个深度为4(由范围[0:3]指定)的8位数组,mem3是一个具有4行2列的16位向量2D数组。这些变量被分配了不同的值并被打印。

module des ();
  reg [7:0]  mem1; 							// reg vector 8-bit wide
  reg [7:0]  mem2 [0:3]; 				// 8-bit wide vector array with depth=4
  reg [15:0] mem3 [0:3][0:1]; 	// 16-bit wide vector 2D array with rows=4,cols=2

  initial begin
    int i;

    mem1 = 8'ha9;
    $display ("mem1 = 0x%0h", mem1);

    mem2[0] = 8'haa;
    mem2[1] = 8'hbb;
    mem2[2] = 8'hcc;
    mem2[3] = 8'hdd;
    for(i = 0; i < 4; i = i+1) begin
      $display("mem2[%0d] = 0x%0h", i, mem2[i]);
    end

    for(int i = 0; i < 4; i += 1) begin
      for(int j = 0; j < 2; j += 1) begin
        mem3[i][j] = i + j;
        $display("mem3[%0d][%0d] = 0x%0h", i, j, mem3[i][j]);
      end
    end
  end
endmodule
  • 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

最终可以得到打印结果:

mem1 = 0xa9
mem2 [0] = 0xaa
mem2 [1] = 0xbb
mem2 [2] = 0xcc
mem2 [3] = 0xdd
mem3 [0] [0] = 0x0
mem3 [0] [1] = 0x1
mem3 [1] [0] = 0x1
mem3 [1] [1] = 0x2
mem3 [2] [0] = 0x2
mem3 [2] [1] = 0x3
mem3 [3] [0] = 0x3
mem3 [3] [1] = 0x4
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在给一个存储器逻辑设计的例子:

在此示例中,存储器是一个深度为4的数组,每个深度的宽度为16位。设计模块接受一个称为addr的附加输入信号,以访问存储器中的特定索引。

module des (    input           clk,
                input           rstn,
                input  [1:0]    addr,
                input           wr,
                input           sel,
                input [15:0]    wdata,
                output [15:0]   rdata);

reg [15:0] register [0:3];
integer i;

always @ (posedge clk) begin
    if (!rstn) begin
        for (i = 0; i < 4; i = i+1) begin
            register[i] <= 0;
        end
    end else begin
        if (sel & wr)
            register[addr] <= wdata;
        else
            register[addr] <= register[addr];
    end
end

assign rdata = (sel & ~wr) ? register[addr] : 0;
endmodule
  • 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

在硬件示意图中可以看到,存储器的每个索引都是一个16位触发器,并且输入地址用于访问一组特定的触发器。

综合图

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/494333
推荐阅读
相关标签
  

闽ICP备14008679号