当前位置:   article > 正文

Verilog Tutorial(2)数据类型和数组简介_verilog 数组

verilog 数组

写在前面

在自己准备写verilog教程之前,参考了许多资料----FPGA Tutorial网站的这套verilog教程即是其一。这套教程写得不错,只是没有中文,在下只好斗胆翻译过来(加了自己的理解)分享给大家。

这是网站原文:https://fpgatutorial.com/verilog/

这是系列导航:Verilog教程系列文章导航


在这篇文章将讨论 verilog 中最常用的数据类型,包括对数据表示,线网类型、变量类型,向量类型和数组的讨论。

尽管 verilog 被认为是一种弱类型语言(loosely typed),但设计者仍必须在 Verilog 设计中为每个端口或信号声明一个数据类型,被指定的类型用于定义数据的特征。例如,设计者可以使数据完全解释为逻辑值的类型,或者也可以将数据解释为数值的类型。

在大多数情况下,挡在verilog 中将数据赋值给信号时,数据都会被隐式地转换为正确的类型。因此,通常不需要在 verilog 中显式地执行类型转换。

1、Verilog 中的数值表示

编写 verilog 时,经常需要在代码中表示数据值。为此可以将此数据表示为2进制,8进制,10进制或者16进制值。

与其他编程语言不同,verilog中还需要定义数据的位宽。这是因为verilog 本质上是在描述硬件电路。

下面的代码片段显示了在 verilog 中表示数字数据的一般语法。

<bits>'<representation><value>
  • <bits> :指定数据的位宽

  • <representation> :指定数据用何种进制来描述。可以是2进制(b或B)、8进制(o或O)、10进制(d或D)、16进制(h或H)

  • <value> :指定数据的具体数值

下面的代码片段展示了使用不同方法来描述10进制值数字 8 。

  1. 4'b1000; //10进制数字8的位宽4的2进制表示
  2. 4'o10; //10进制数字8的位宽4的8进制表示
  3. 4'd8; //10进制数字8的位宽4的10进制表示
  4. 4'h8; //10进制数字8的位宽4的16进制表示

2、Verilog 中的基本数据类型

从广义上讲,verilog 中的基本数据类型可以分为两大类--线网类型(net)和变量类型(variable )。这两个不同的类别可以被用来建模数字电路的不同元素。

net类型用来对数字电路中的连接建模。它们无法自行存储数值,必须由数据驱动

variable类型用来对寄存器或触发器建模。它们可以存储数据,有点类似于其他编程语言(例如 C)中的变量。

无论开发者使用的确切类型是什么,都可以将4个有效值赋给数据中的各个位。这4个不同的值如下表所示。

0

逻辑 0 或 "假"

1

逻辑 1 或 "真"

z

高阻

x

未知

同样可以用相同的语法在 verilog 中声明变量,而不用管确切的类型。下面的代码片段展示了这种一般语法。

<type_name> <size> <variable_name> = <value>;

<type_name> 用来声明变量类型。

下面的 verilog 代码声明了一个名为example的integer (整型)变量并为其赋值 100。

integer example = 100;

2.1、Verilog 中的net类型

verilog 中的net数据类型可以用来描述设计中不同组件之间的物理连接。因此,net类型本身不能用于存储数据或驱动数据。

在上面的电路中,net类型将多路复用器的输出连接到触发器的输入。

通常使用连续赋值(continuous assignment)语句来将数据驱动到线型(wire)上。为此,必须使用 assign 关键字,如下面的代码片段所示。

assign a = 1'b0;  //给a赋值为0

2.1.1、wire类型

verilog 中最常用的net类型就是wire类型。使用wire类型来声明设计中基本的点对点连接信号。顾名思义,它们大致相当于传统电路中的电线(wire英文原意)。

下面的 verilog 代码展示了如何将连wire类型与 assign 关键字一起使用。

  1. wire a; //声明一个1位宽wire类型信号a
  2. assign a = c; //使用assign关键字来赋值
  3. assign b = d; //使用assign关键字来赋值

2.1.2、wand & wor类型

除了最常用的net数据类型wire类型外,还有几种其他的net类型。

wand 和 wor 类型用于将基本逻辑门插入到电路中。wand 相当于插入一个与门,wor 类型则相当于插入一个或门(顾名思义)。

当使用 wand 和 wor 类型时,必须对信号进行多次赋值,这样做是因为每次赋值都代表底层逻辑门的一个输入。

下面的 verilog 代码展示了如何将 wand 和 wor 类型与 assign 关键字一起使用。

  1. wor a; //声明wor类型
  2. wand b; //声明wand类型
  3. wire c, d, e, f; //逻辑门的输入
  4. //创建一个输入为c、d的或门
  5. assign a = c;
  6. assign a = d;
  7. //创建一个输入为e、f的与门
  8. assign b = e;
  9. assign b = f;

因为与门跟或门都可以轻松使用assign的组合逻辑实现,因此,并不推荐实践中使用 wor 和 wand 类型。

2.1.3、tri, triand & trior 类型

除了 wire、wand 和 wor 状态,还可以使用等效的 tri、triand 或 trior 类型。

使用这些类型的方式与 wire、wand 和 wor 类型完全相同。事实上,这些类型的功能是完全一样的。但是,设计者可以使用它们来更清楚地表达设计意图。

下面的代码片段显示了一个基本示例,其中 tri 类型被驱动为高阻。

  1. tri a; //声明一个tri类型信号
  2. assign a = 1'bz; //将其驱动为高阻

由于wire类型也可以取三态值,所以在实践中很少使用tri类型。trior 和 triand 类型也是如此,它们也可以使用wire类型轻松实现。

2.1.4、supply0 & supply1 类型

supply0 和 supply1 类型可以用来连接到一个2进制常量0或1上(即高电平与低电平)。这相当于直接连接到电路中的地ground或者电源VCC。

下面的代码片段展示了如何使用这些类型来创建连接到高电平或低电平的信号。

  1. supply0 a; //声明信号a连接到低电平
  2. supply1 b; //声明信号b连接到高电平

然而,在实践中很少需要将信号连接到高电平或低电平。哪怕真的需要这样做,使用wire类型也能很容易完成。因此,supply0 和 supply1 类型在实践中也很少使用。

2.2、Verilog 中的变量(Variable )类型

与net类型不同,在 verilog 中使用variable(变量)类型来存储数据。将一个值赋给一个变量类型后,它会一直保持这个值直到它被再次赋值。

variable类型通常比net类型更容易理解,因为它与C语言中的变量很类似。

在上面的电路中,将使用variable类型对触发器的输出进行建模,因为它可以有效存储一位数据。

variable类型必须在过程代码块中使用,例如always块,如下面的代码片段所示,它实现了一个D触发器。

  1. //D触发器
  2. always @(posedge clock)
  3. q <= d;
  4. end

2.2.1、Verilog 中的触发器(Reg)类型

verilog中最常用的variable类型是reg类型。当需要在设计中存储一个值时,可以使用这种类型。

虽然reg 类型常被用来建模触发器,但在某些情况下,reg 类型也可用于在 verilog 中对组合逻辑进行建模。

下面的 verilog 代码片段展示了如何使用 reg 类型来实现一个基本的D触发器。

  1. reg q; //声明一个reg变量q
  2. //D触发器
  3. always @(posedge clock)
  4. q <= d;
  5. end

2.2.2、数值变量(Numeric Variable)类型

到目前为止,所讨论到的类型都是与单个数据位一起使用的。设计者当然也可以在 verilog 中以数值方式来表示数据。

在verilog中,有两种常用的数值类型--integer(整数型)和real(实数型)。

①、integer类型

verilog 中最常用的数值数据类型是integer类型。但是,integer类型通常用于模块中的内部信号而不是端口。

默认情况下,integer是一个 32 位补码,这意味着其可以被用来表示 verilog 设计中的任何整数(当然是范围内)。

在使用integer类型时,需要为integer变量直接分配数值而不是2进制值。

因为 reg 类型也可以被直接赋予数值,因此通常将 reg 类型用作 verilog 中的常量或循环变量。

综合工具会自动缩减 reg 类型类型中未使用到的位。例如,如果声明一个值为 255 的整数常量,综合工具会将其缩减为 8 位(即8'b1111_1111)。

下面的代码片段展示了如何在 verilog 对integer类型进行声明和赋值。

integer a = 255;    //声明一个值为255的integer类型变量a

②、real 类型

real类型被用来存储非整数,即也有小数部分的数字。real类型通常在 verilog 中用64位浮点数来实现。因此,它不能被综合为实际电路,所以通常在testbench等测试脚本中使用。

real类型可以使用十进制或科学记数法来赋值。

下面的代码片段展示了如何声明一个real类型并为其赋值。

real a; //声明一个real变量a
a = 2.5; //十进制方式赋值
a = 1e-3; //科学记数法方法赋值

2.3、Verilog 中的向量(Vector )类型

除了Numeric类型之外,目前看到的其他类型均由单个位组成。然而,在数字电路中却经常使用数据总线来传输数据。

在 verilog 中可以使用Vector(向量)类型来创建数据总线,这意味着允许声明多于一位的信号。

下面的代码片段展示了在 verilog 中声明vector类型的一般语法。

//声明向量类型的一般语法
<type> <size> <variable_name>;

定义vector的大小时,必须指定最高有效位和最低有效位(MSB 和 LSB)。因此,<size> 要采用 [MSB:LSB] 的形式。

例如,如果要声明一个 4 位little endian(小字节序)类型vector,将使用结构 [3:0]。反之,如果要使用big endian(大字节序)类型vector,则使用结构 [0:3]。在实际应用中小字节序更为常见。

正如在本文前面所述,设计者可以使用2进制、8进制、10进制或16进制格式表示数据。当给vector赋值时,可以使用其中任何一种方式。

下面的 verilog 代码展示了如何声明一个 4 位宽的 reg 类型,并使用不同的方式将 1010b 赋值给变量。

  1. reg [3:0] a; //声明一个位宽为4的reg变量a
  2. a = 4'b1010; //2进制方法赋值
  3. a = 4'o12; //8进制方法赋值
  4. a = 4'd10; //10进制方法赋值
  5. a = 4'ha; //16进制方法赋值

2.4、Verilog 中的有符号(Signed)和无符号数据(Unsigned)

在Verilog-2001标准发布之前,所有net类型和variable类型都只能用于存储无符号(Unsigned)数据类型。同样,integer类型总是被解释为有符号值(Signed)

Verilog-2001 标准引入了signed 和 unsigned关键字。这改变了变量存储数据的方式。

在 verilog 中将类型声明为signed时,它会被解释为补码。这意味着可以为这些信号分配负数值。默认情况下,integer类型是有符号的,而 reg 和 wire 类型都是无符号的。如果希望修改此默认行为,只需要使用这些关键字(signed 和 unsigned)。

下面的 verilog 代码展示如何使用 reg、wire 和integer类型声明有符号和无符号数据。统一起见,声明的所有变量都是 32 位宽的。

  1. //声明无符号reg变量a、有符号reg变量b
  2. reg [31:0] a;
  3. reg signed [31:0] b;
  4. //声明无符号wire变量a、有符号wire变量b
  5. wire [31:0] a;
  6. wire signed [31:0] b;
  7. //声明无符号integer变量a、有符号integer变量b
  8. integer unsigned a;
  9. integer b;

3、Verilog 中的数组(Arrays

设计者可以在 verilog 中创建和使用Arrays(数组)类型。为了在 verilog 中声明一个数组,只需在变量名后添加一个额外的字段来声明数组中的元素个数。该声明采用与之前讨论过的向量大小字段相同的格式。

下面的代码片段展示了在 verilog 中声明数组类型的一般语法。<elements> 被用来声明数组的大小。

<type> <size> <variable_name> <elements>;

假设要创建一个 3 位 reg 类型的数组,该数组中总共有 8 个元素。下面的 verilog 代码展示了如何创建这个数组。

  1. //共有8个元素的数组example,每个元素的位宽为3位
  2. reg [2:0] example [7:0];

可以使用带有数组下标的方括号来访问数组中的单个元素。例如,下面的 verilog 代码显示了如何将 5h 的值分配给示例数组中的最后一个元素(下标为7,即从0开始的第8个元素)。

  1. //将5赋值给数组example中的最后一个元素
  2. example[7] = 3'h5;

多维数组

在Verilog-1995标准中,只能创建一维数组。而 verilog 2001 标准则可以创建多于一维的数组。为此,只需添加另一个字段来定义需要的元素数量。

下面的代码片段展示了在 verilog 中创建2维数组时使用的一般语法。

<type> <size> <variable_name> <elements> <elements>;

假设现在要创建一个变量,它可以存储 2 个元素,这2个元素都有 8 个 4 位 reg 类型元素。

为此,只需在声明的末尾添加一个额外的字段。下面的代码片段展示了如何做到这一点。

reg [3:0] example2d [7:0][1:0];

设计者还使用与赋值维数组相同的方法来赋值多维数组,但现在需要使用一对方括号来定义数组的两个维度中的元素。

假设要将 0xa 的值赋给两个维度中的最后一个元素,下面的 verilog 代码展示了如何将数据分配给数组中的这个元素。

  1. example2d [7][1] = 4'ha;
  2. example2d [7][0] = 4'ha;

推荐阅读
相关标签