verilog 最常用的 2 种数据类型就是 网线(wire)
与 寄存器(reg)
。
其余类型可以理解为这两种数据的扩展或辅助。
1. 网线(wire)
wire
类型表示硬件元器件之间的物理连线,可以简单的理解为一根连接线。
wire 类型数据如果没有驱动,仿真时一般显示高阻态 z。
例如:
wire clk ;
wire rst_n ;
wire led = 1'b1 ;
wire [7:0] cnt = 8'b0 ;
2. 寄存器(reg)
寄存器(reg)
用来表示存储单元,可以简单理解为存储器。
例如:
reg clk ;
reg rst_n ;
reg [7:0] cnt ;
3. 向量
当位宽大于 1 时,wire 或 reg 即可声明为向量的形式。
例如:
wire [8:0] cnt ; /// 声明8bit位宽的网线cnt
reg [8:0] data ; /// 声明8bit位宽的寄存器data
3.1 verilog 支持可变的向量域选择。
例如:
reg [31:0] data1 ;
reg [7:0] data2 [3:0] ;
integer i ;
always @(*) begin
for(i=0; i<4; i=i+1) begin
/// 把data1[7:0],data[15:8],data1[23:16],data[31:24]的值
/// 依次赋给data2[0][7:0],data2[1][15:8],data2[2][23:16],
/// data2[3][31:24]
data2[i] = data[(i+1)*8-1:i*8]
end
end
3.2 verilog 还支持指定 bit 位后固定位宽的向量域选择访问
- [bit+: width]:从起始 bit 位开始递增,位宽为 width。
- [bit-: width]:从起始 bit 位开始递减,位宽为 width。
例如:
/// 下面2种赋值是等效的
A = data[31-:8] ;
A = data[31:24] ;
/// 下面2种赋值是等效的
A = data[0+:8] ;
A = data[7:0] ;
3.3 对信号重新组合成新的向量时,需要借助大括号
例如:
reg [7:0] data1 ;
reg [7:0] data2 ;
wire [31:0] cnt1 ;
wire [31:0] cnt2 ;
assign cnt1 = {data1[7:0],data2[7:0]} ; /// 数据拼接
assign cnt2 = {32'{1'b0}} ; /// 把32位宽的cnt2的每一位都赋值0
笔记
对于向量整个概念比较抽象,其实可以理解为多位宽的数值。
4. 整数,实数,时间寄存器变量
整数,实数,时间等数据类型也属于寄存器类型。
4.1 整数(integer)
整数类型用关键字 integer
来声明。
声明时不用指明位宽,位宽和编译器有关,一般为 32 bit。
reg 型变量为无符号数,而 integer 型变量为有符号数。
例如:
reg [31:0] data1 ;
reg [7:0] data2 [3:0] ;
integer i ; /// 整型变量,用来辅助生成数字电路
always @(*) begin
for(i=0; i<=3; i=i+1) begin
data2[i] = data1[(i+1)*8-1:i*8];
end
end
上面代码中,integer
信号 i 作为辅助信号,将 data 1 的数据依次赋值给数组 data 2。
综合后实际电路里并没有 i 这个信号,i 只是辅助生成相应的硬件电路。
4.2 实数(real)
实数用关键字 real
来声明。
实数声明不能带有范围,默认值为 0。
如果将一个实数赋值给一个整数,则自由实数的整数部分会赋值给整数。
例如:
real data1 ;
integer data2 ;
initial begin
data1 = 7.77 ;
end
initial begin
data2 = data1 ; /// data2的值为7
end
4.3 时间(time)
verilog 使用特殊的时间寄存器 time 型变量,对仿真时间进行保存。
其宽度一般为 64 bit,通常调用系统函数 $time
获取当前仿真时间。
例如:
time run_time ;
initial begin
# 100 ;
run_time = $time ; /// run_time的大小为100
end
4.4 数组
在 verilog 中,允许声明 reg、wire、integer、real、time 及其向量类型的数组。
数组维数没有限制。
线网数组也可以用于连接实力模块的端口。
数组中的每个元素都可以作为一个标量或者向量。
对于多维数组来讲,用户需要说明其每一数组的索引。
格式:
【数组名】【下标】
例如:
/// 8个整数组成的数组
integer data [7:0] ;
/// 8个8bit的reg组成的数组
reg [7:0] mem [7:0] ;
/// 8个8bit的wire组成的数组
wire [7:0] test [7:0] ;
/// 定义1 bit的wire类型的二维数组
reg data_bit [7:0] [7:0] ;
/// 声明4维的32bit数组
reg [31:0] data_in [2:0] [4:0] [8:0] [16:0]
下面是对数组元素的赋值操作:
/// 将data数组中第二个元素赋值为32bit的0
data[1] = 32'd0;
/// 等价于mem[3] [7:0],将数组中的第四个元素赋值为8'f
mem[3] = 8'f ;
/// 将数组中的第一个元素赋值为8'b0
test[0]= 8'b0 ;
/// 将data_bit的第二行第二列的元素赋值为1'b1,
/// 这时的第二个标号不能省略,不能写成data_bit[0] = 1'b1;
data_bit[1][1] = 1'b1 ;
/// 将数组data_in中标号为[0][0][0][0]的寄存器单元为15-0bit赋值为15'd3
data_in[0][0][0][0][15:0] = 15'd3 ;
4.5 参数
参数用来表示常量,只能赋值一次。
例如:
parameter IDEL = 1 ;
localparam DO = 2 ;
笔记
parameter 定义的常量,是全局定义,全模块都可以使用。
localparam 定义的常量,是局部定义,只在本模块使用。
5. 总结
本节的重点:
- wire 类型数据相当于网线。
- reg 类型数据相当于寄存器,起存储作用。
- 向量可以理解为 wire/reg 类型数据的多位宽形式。
- 不同数据类型的关键字不一样,代表的含义也不一样。
- 数组一般涉及到宽度和深度,宽度 m表示可以写 m位宽的数据,深度 n表示可以写 n 个 m 位宽的数据。
- 区别全局常量和局部常量。