verilog教程(3) —— Verilog 数值表示和数据类型-Anlogic-安路社区-FPGA CPLD-ChipDebug

verilog教程(3) —— Verilog 数值表示和数据类型

在数字逻辑系统中,只存在高电平和低电平,因此用其表示数字只有整数形式,并存在 3 种表示方法,即:原码表示法(符号加绝对值)、反码表示法(符号加反码)和补码表示法 (符号加补码)。这三种在 FPGA 开发中都有着广泛的应用,下面分别讨论。

1.原码表示法原码表示法是机器数的一种简单的表示法,采用符号位级联绝对值的方法来表示数字。

2.反码表示法反码可由原码得到。如果数字是正数,则其反码与原码一样;如果数字是负数,则其反码是对它的原码(符号位除外)各位取反而得到的(除符号位外,所有的 0 改为 1,所有的 1 改为 0)。

3.补码表示法补码表示法是实际中应用最广泛的数字表示法,其表示规则如下:若是正数,补码、反码和原码的表示是一样的;若是负数,补码、反码和原码的表示都是不一样的。

原码的优点就是乘除运算方便,不论正负数,乘除运算都一样,并以符号位决定结果的正负号;若做加法则需要判断两数符号是否相同;若作减法,还需要判断两数绝对值的大小, 以使大数减小数。 补码的优点是,加法运算方便,不论数的正负都可直接相加,而符号位同样参加运算, 如果符号位发生进位,把进位的 1 去掉,余下的即为结果。 不过我们不用去管底层的数值表示方法,编译工具会去处理。

数值类型

Verilog HDL 有下列四种基本的值来表示硬件电路中的电平逻辑:

  • 0:逻辑 0 或 “假”
  • 1:逻辑 1 或 “真”
  • x 或 X:未知
  • z 或 Z:高阻

x 意味着信号数值的不确定,即在实际电路里,信号可能为 1,也可能为 0。

z 意味着信号处于高阻状态,常见于信号(input, reg)没有驱动时的逻辑结果。例如一个 pad 的 input 呈现高阻状态时,其逻辑值和上下拉的状态有关系。上拉则逻辑值为 1,下拉则为 0 。

注意这四种值的解释都内置于语言中。此外, x 值和z 值都是不分大小写的,也就是说,值0x1z 与值0X1Z 相同。

整数表示方法

数字声明时,合法的基数格式有 4 中,包括:十进制(‘d 或 ‘D),十六进制(‘h 或 ‘H),二进制(’b 或 ‘B),八进制(’o 或 ‘O)。数值可指明位宽,也可不指明位宽。

指明位宽:

值和上下拉的状态有关系。上拉则逻辑值为 1,下拉则为 0 。

4'b1011         // 4bit 数值
32'h3022_c0de   // 32bit 的数值

下划线 _ 可以用来分隔数值起到提高易读性的作用;唯一的限制是下划线符号不能用作为首字符。

不指明位宽:

一般直接写数字时,默认为十进制表示,例如下面的 3 种写法是等效的:

counter = 'd100 ; //一般会根据编译器自动分频位宽,常见的为32bit
counter = 100 ;
counter = 32'h64 ;

负数表示

通常在表示位宽的数字前面加一个减号来表示负数。例如:

-6'd15  
-15

-15 在 5 位二进制中的形式为 5’b10001, 在 6 位二进制中的形式为 6’b11_0001。

实数表示方法

实数表示方法主要有两种方式:

十进制:

30.123
6.0
3.0
0.001

科学计数法:

1.2e4         //大小为12000
1_0001e4      //大小为100010000
1E-3          //大小为0.001

字符串表示方法

字符串是由双引号包起来的字符队列。字符串不能多行书写,即字符串中不能包含回车符。Verilog 将字符串当做一系列的单字节 ASCII 字符队列。

reg [0: 14*8-1]       str ;
initial begin
    str = "chipdebug.com";
end

Verilog 数据类型

Verilog 最常用的 2 种数据类型就是线网(wire)与寄存器(reg)。

线网(wire)

在Verilog中,线网(net)是用于连接模块内部元素(如门、寄存器等)的基本元素之一。线网可以承载信号和数据,并且可以是单个位(bit)或者多位(vector)。

Verilog中的线网类型包括:

  1. wire:最常见的线网类型,用于连接模块内的元素。wire可以连接多个驱动器,并且具有默认的连续赋值行为。

  2. tri:三态线网,用于连接多个驱动器,但是只允许其中一个驱动器处于活动状态。通常与电路中的三态门相关。

  3. triand:三态与门线网,用于连接多个驱动器,并且当所有驱动器都处于活动状态时输出为1,否则输出为Z。

  4. trior:三态或门线网,用于连接多个驱动器,并且当任何一个驱动器处于活动状态时输出为1,否则输出为Z。

  5. trireg:三态寄存器线网,类似于tri,但通常用于存储状态而不是连接信号。

这些线网类型在Verilog中被用来描述模块内的信号连接和数据传输。根据设计的需要,可以选择合适的线网类型来实现所需的功能。

一般主要用到wire类型,注意以下几点:

  • wire 类型代表的是物理连接线,因此它不存贮逻辑值。必须由器件所驱动。通常由assign进行赋值。如 assign A = B ^ C
  • 如果没有硬件驱动连接到 wire 型变量,缺省值一般为 “Z”。
  • 可以通过[a:b] 的形式为其指定位宽,a、b只能是常数。
  • 信号没有定义数据类型时,缺省为 wire 类型。
wire flag1; 

wire [7:0] flag2; //声明8位宽度的wire,即总线

线网型还有其他数据类型,包括 wandworwritriandtriortrireg 等。这些数据类型用在FPGA中几乎用不到,就不介绍了。

寄存器(reg)

寄存器(reg)具有存储功能,它会保持数据原有的值,直到被改写。可以通过[a:b] 的形式为其指定位宽,a、b只能是常数。

reg flag1; 

reg [7:0] flag2; //声明8位宽度的reg

例如在 always 块中,寄存器可能被综合成边沿触发器,在组合逻辑中可能被综合成 wire 型变量。寄存器不需要驱动源,也不一定需要时钟信号。在仿真时,寄存器的值可在任意时刻通过赋值操作进行改写。

reg rstn ;
initial begin
    rstn = 1'b0 ;
    #100 ;
    rstn = 1'b1 ;
end

Verillog 还支持指定 bit 位后固定位宽的向量域选择访问。

  • [bit+: width] : 从起始 bit 位开始递增,位宽为 width。
  • [bit-: width] : 从起始 bit 位开始递减,位宽为 width。
//下面 2 种赋值是等效的
A = flag1[31-: 8] ;
A = flag1[31:24] ;

//下面 2 种赋值是等效的
B = flag2[0+ : 8] ;
B = flag2[0:7] ;

对信号重新进行组合成新的向量时,需要借助大括号。

wire [31:0]    temp1, temp2 ;
assign temp1 = {byte1[0][7:0], data1[31:8]};  //数据拼接
assign temp2 = {32{1'b0}};  //赋值32位的数值0

整数,实数,时间寄存器变量

整数,实数,时间等数据类型实际也属于寄存器类型。

整数(integer)

整数类型用关键字 integer 来声明。声明时不用指明位宽,位宽和编译器有关,一般为32 bit。reg 型变量为无符号数,而 integer 型变量为有符号数。

reg [31:0]      data1 ;
reg [3:0]       byte1 [7:0]; //数组变量,后续介绍
integer j ;  //整型变量,用来辅助生成数字电路
always@* begin
    for (j=0; j<=3;j=j+1) begin
        byte1[j] = data1[(j+1)*8-1 : j*8]; 
        //把data1[7:0]…data1[31:24]依次赋值给byte1[0][7:0]…byte[3][7:0]
        end
end

此例中,integer 信号 j 作为辅助信号,将 data1 的数据依次赋值给数组 byte1。综合后实际电路里并没有 j 这个信号,j 只是辅助生成相应的硬件电路。

实数(real)

实数用关键字 real 来声明,可用十进制或科学计数法来表示。实数声明不能带有范围,默认值为 0。如果将一个实数赋值给一个整数,则只有实数的整数部分会赋值给整数。

real        data1 ;
integer     temp ;
initial begin
    data1 = 2e3 ;
    data1 = 3.75 ;
end
 
initial begin
    temp = data1 ; //temp 值的大小为3
end

时间(time)

Verilog 使用特殊的时间寄存器 time 型变量,对仿真时间进行保存。其宽度一般为 64 bit,通过调用系统函数 $time 获取当前仿真时间。

time       current_time ;
initial begin
       #100 ;
       current_time = $time ; //current_time 的大小为 100
end

数组

在 Verilog 中允许声明 reg, wire, integer, time, real 及其向量类型的数组。

数组维数没有限制。线网数组也可以用于连接实例模块的端口。数组中的每个元素都可以作为一个标量或者向量,以同样的方式来使用,形如:<数组名>[<下标>]。对于多维数组来讲,用户需要说明其每一维的索引。

integer          flag [7:0] ; //8个整数组成的数组
reg  [3:0]       counter [3:0] ; //由4个4bit计数器组成的数组
wire [7:0]       addr_bus [3:0] ; //由4个8bit wire型变量组成的数组
wire             data_bit[7:0][5:0] ; //声明1bit wire型变量的二维数组
reg [31:0]       data_4d[11:0][3:0][3:0][255:0] ; //声明4维的32bit数据变量数组

下面显示了对数组元素的赋值操作:

flag [1]   = 32'd0 ; //将flag数组中第二个元素赋值为32bit的0值
counter[3] = 4'hF ;  //将数组counter中第4个元素的值赋值为4bit 十六进制数F,等效于counter[3][3:0] = 4'hF,即可省略宽度; 
assign addr_bus[0]        = 8'b0 ; //将数组addr_bus中第一个元素的值赋值为0
assign data_bit[0][1]     = 1'b1;  //将数组data_bit的第1行第2列的元素赋值为1,这里不能省略第二个访问标号,即 assign data_bit[0] = 1'b1; 是非法的。
data_4d[0][0][0][0][15:0] = 15'd3 ;  //将数组data_4d中标号为[0][0][0][0]的寄存器单元的15~0bit赋值为3

存储器

存储器变量就是一种寄存器数组,可用来描述 RAM 或 ROM 的行为。

reg               membit[0:255] ;  //256bit的1bit存储器
reg  [7:0]        mem[0:1023] ;    //1Kbyte存储器,位宽8bit
mem[511] = 8'b0 ;                  //令第512个8bit的存储单元值为0

参数(parameter,localparam )

参数用来表示常量,用关键字 parameter 声明,只能赋值一次。

parameter      data_width = 10'd32 ;
parameter      i=1, j=2, k=3 ;
parameter      mem_size = data_width * 10 ;

但是,通过实例化的方式,可以更改参数在模块中的值。此部分以后会介绍。

局部参数用 localparam 来声明,其作用和用法与 parameter 相同,区别在于它的值不能被改变。所以当参数只在本模块中调用时,可用 localparam 来说明。

请登录后发表评论

    没有回复内容