SystemVerilog学习【一】数据类型详解
📖 扩展学习资源:
1. SystemVerilog——数据类型
1.1 内建数据类型(四值逻辑和二值逻辑)
SV相对与Verilog引入二值逻辑的期望是将硬件的世界与软件的世界分离开,四值逻辑属于硬件世界即硬件设计,软件世界即验证环境更多的是二值逻辑。
1.1.1 逻辑类型
按照逻辑类型进行划分,将常见的变量类型划分为:
四值逻辑,可以表示0、1、X、Z四种状态,默认初始值为x;
二值逻辑,只可以表示0和1两种状态,默认初始值为0。
注:将四值变量赋值给二值变量,X和Z状态会转变为0;可使用系统函数$isunknown(变量名)检查未知值的传播,若表达式中出现存在X或Z时返回1。
四值逻辑类型:
- wire:(Verilog中)主要用在assign语句中,起连接作用。(SystemVerilog中)用在一个信号存在多个驱动时,例如双向总线建模;
- reg:(Verilog中)主要用在initial和always语句中,对线路建模。(SystemVerilog中)常用在寄存器类型;
- logic:(SV增补)logic是对Verilog中reg类型的增强,不仅可以作为变量,还可以被连续赋值语句、门单元和模块驱动。可用在assign、initial、always语句中,但不能多驱动(多驱动时需要定义成线网类型);
典型的四值变量除了上述三种类型外,还有integer、time两种类型。
二值逻辑类型:
- bit:用户自定义位宽大小,无符号变量;
- byte:8位宽,有符号变量,取值范围:-128~127;
- shortint:16位宽,有符号变量,取值范围:-215~215-1;
- int:32位宽,有符号变量,取值范围:-231~231-1;
- longint:64位宽,有符号变量,取值范围:-263~263-1。
module logic_types_demo;
// 四值逻辑类型
logic [7:0] logic_var; // 默认初始值为x
reg [7:0] reg_var; // 默认初始值为x
wire [7:0] wire_var; // 默认初始值为z
integer int_var; // 默认初始值为x
// 二值逻辑类型
bit [7:0] bit_var; // 默认初始值为0
byte byte_var; // 默认初始值为0
shortint short_var; // 默认初始值为0
int int2_var; // 默认初始值为0
longint long_var; // 默认初始值为0
initial begin
$display("四值逻辑类型初始值:");
$display("logic_var = %b", logic_var);
$display("reg_var = %b", reg_var);
$display("int_var = %d", int_var);
$display("\n二值逻辑类型初始值:");
$display("bit_var = %b", bit_var);
$display("byte_var = %d", byte_var);
$display("short_var = %d", short_var);
$display("int2_var = %d", int2_var);
$display("long_var = %d", long_var);
// 测试未知值检查
logic_var = 8'bxxxx_zzzz;
if ($isunknown(logic_var))
$display("logic_var包含未知值");
end
endmodule
1.1.2 符号类型
SystemVerilog中的数据类型可以分为有符号和无符号两种:
有符号类型:
- byte, shortint, int, longint:默认为有符号
- integer:有符号类型
- 可以使用signed关键字声明有符号类型
无符号类型:
- bit, logic, reg, wire:默认为无符号
- 可以使用unsigned关键字声明无符号类型
module signed_types_demo;
// 有符号类型
byte signed_byte = -50; // 8位有符号
shortint signed_short = -1000; // 16位有符号
int signed_int = -100000; // 32位有符号
longint signed_long = -1000000; // 64位有符号
// 无符号类型
bit [7:0] unsigned_bit = 200; // 8位无符号
logic [15:0] unsigned_logic = 50000; // 16位无符号
// 显式声明符号性
logic signed [7:0] signed_logic = -100;
bit unsigned [7:0] unsigned_bit2 = 255;
initial begin
$display("有符号类型:");
$display("signed_byte = %d", signed_byte);
$display("signed_short = %d", signed_short);
$display("signed_int = %d", signed_int);
$display("signed_long = %d", signed_long);
$display("\n无符号类型:");
$display("unsigned_bit = %d", unsigned_bit);
$display("unsigned_logic = %d", unsigned_logic);
$display("\n显式声明:");
$display("signed_logic = %d", signed_logic);
$display("unsigned_bit2 = %d", unsigned_bit2);
// 符号扩展示例
logic [3:0] small = 4'b1111; // -1 (如果是有符号)
logic [7:0] extended;
extended = small; // 零扩展:00001111
$display("零扩展结果: %b", extended);
logic signed [3:0] small_signed = 4'b1111; // -1
logic signed [7:0] extended_signed;
extended_signed = small_signed; // 符号扩展:11111111
$display("符号扩展结果: %b", extended_signed);
end
endmodule
1.1.3 类型转换
SystemVerilog提供了多种类型转换方式:
隐式转换:
- 自动进行位宽调整
- 符号性可能改变
- 可能丢失精度
显式转换:
- 使用$cast()系统函数
- 使用’()强制转换
- 更安全的转换方式
module type_conversion_demo;
bit [7:0] bit8;
bit [15:0] bit16;
int signed_int;
byte signed_byte;
real real_var;
initial begin
// 隐式转换
bit8 = 8'hFF;
bit16 = bit8; // 零扩展:0x00FF
$display("隐式转换 - bit8: 0x%h, bit16: 0x%h", bit8, bit16);
bit16 = 16'h1234;
bit8 = bit16; // 截断:0x34
$display("截断转换 - bit16: 0x%h, bit8: 0x%h", bit16, bit8);
// 符号转换
signed_int = -100;
bit16 = signed_int; // 有符号到无符号
$display("符号转换 - signed_int: %d, bit16: 0x%h", signed_int, bit16);
// 显式转换使用$cast
real_var = 3.14;
if ($cast(signed_int, real_var))
$display("$cast成功 - real: %f, int: %d", real_var, signed_int);
else
$display("$cast失败");
// 强制转换
signed_byte = byte'(signed_int);
$display("强制转换 - int: %d, byte: %d", signed_int, signed_byte);
// 类型转换函数
bit16 = 16'h8000;
$display("$signed(bit16) = %d", $signed(bit16)); // 转为有符号
$display("$unsigned(bit16) = %d", $unsigned(bit16)); // 转为无符号
end
endmodule
1.1.4 流操作符
流操作符用于在不同数据类型之间进行打包和解包操作:
打包操作符 {>>}:
- 将数据流打包成向量
- 支持不同的打包方式
解包操作符 {<<}:
- 将向量解包成数据流
- 支持不同的解包方式
module streaming_operators_demo;
bit [31:0] word;
bit [7:0] bytes[4];
bit [3:0] nibbles[8];
initial begin
// 初始化数据
bytes[0] = 8'hAA;
bytes[1] = 8'hBB;
bytes[2] = 8'hCC;
bytes[3] = 8'hDD;
// 打包操作 - 将字节数组打包成字
word = {>>byte{bytes}};
$display("打包结果: word = 0x%h", word);
// 解包操作 - 将字解包成字节数组
word = 32'h12345678;
{>>byte{bytes}} = word;
$display("解包结果:");
for (int i = 0; i < 4; i++)
$display(" bytes[%0d] = 0x%h", i, bytes[i]);
// 不同粒度的流操作
word = 32'hABCDEF01;
// 按半字节解包
{>>4{nibbles}} = word;
$display("\n按半字节解包:");
for (int i = 0; i < 8; i++)
$display(" nibbles[%0d] = 0x%h", i, nibbles[i]);
// 反向流操作
{<<byte{bytes}} = word;
$display("\n反向解包:");
for (int i = 0; i < 4; i++)
$display(" bytes[%0d] = 0x%h", i, bytes[i]);
// 带大小的流操作
bit [15:0] data_stream = 16'h1234;
bit [3:0] four_bits[4];
{>>4{four_bits}} = data_stream;
$display("\n流操作结果:");
for (int i = 0; i < 4; i++)
$display(" four_bits[%0d] = 0x%h", i, four_bits[i]);
end
endmodule
1.2 定宽数组
1.2.1 一维定宽数组
定宽数组是在编译时确定大小的数组,类似于Verilog中的数组,但SystemVerilog提供了更多的功能和语法糖。
module fixed_array_demo;
// 一维定宽数组声明
int fixed_array[10]; // 10个整数的数组
bit [7:0] byte_array[0:15]; // 16个字节的数组
logic [31:0] word_array[1:8]; // 8个字的数组,索引从1开始
// 数组初始化
int init_array[5] = '{1, 2, 3, 4, 5}; // 列表初始化
bit [3:0] pattern[4] = '{4'h1, 4'h2, 4'h3, 4'h4};
initial begin
// 数组赋值
for (int i = 0; i < 10; i++)
fixed_array[i] = i * i; // 平方数
// 数组访问
$display("定宽数组内容:");
for (int i = 0; i < 10; i++)
$display(" fixed_array[%0d] = %0d", i, fixed_array[i]);
// 初始化数组显示
$display("\n初始化数组:");
for (int i = 0; i < 5; i++)
$display(" init_array[%0d] = %0d", i, init_array[i]);
// 数组切片操作
byte_array[0:3] = '{8'hAA, 8'hBB, 8'hCC, 8'hDD};
$display("\n数组切片:");
for (int i = 0; i < 4; i++)
$display(" byte_array[%0d] = 0x%h", i, byte_array[i]);
end
endmodule
1.2.2 常量数组
常量数组在声明时初始化,之后不能修改:
module const_array_demo;
// 常量数组声明
const int const_array[5] = '{10, 20, 30, 40, 50};
const bit [7:0] lookup_table[16] = '{
8'h00, 8'h01, 8'h04, 8'h09, 8'h10, 8'h19, 8'h24, 8'h31,
8'h40, 8'h51, 8'h64, 8'h79, 8'h90, 8'hA9, 8'hC4, 8'hE1
};
// 参数化常量数组
parameter int COEFF[4] = '{1, 2, 4, 8};
initial begin
$display("常量数组内容:");
for (int i = 0; i < 5; i++)
$display(" const_array[%0d] = %0d", i, const_array[i]);
$display("\n查找表:");
for (int i = 0; i < 16; i++)
$display(" lookup_table[%0d] = 0x%h", i, lookup_table[i]);
$display("\n参数数组:");
for (int i = 0; i < 4; i++)
$display(" COEFF[%0d] = %0d", i, COEFF[i]);
// 常量数组不能修改
// const_array[0] = 100; // 编译错误
end
endmodule
1.2.3 多维数组
SystemVerilog支持多维数组,可以创建矩阵和更复杂的数据结构:
module multidim_array_demo;
// 多维数组声明
int matrix[4][4]; // 4x4矩阵
bit [7:0] image[32][32]; // 32x32图像
logic [15:0] cube[8][8][8]; // 8x8x8立方体
// 初始化多维数组
int identity[3][3] = '{
'{1, 0, 0},
'{0, 1, 0},
'{0, 0, 1}
};
initial begin
// 初始化矩阵
for (int i = 0; i < 4; i++) begin
for (int j = 0; j < 4; j++) begin
matrix[i][j] = i * 4 + j;
end
end
// 显示矩阵
$display("4x4矩阵:");
for (int i = 0; i < 4; i++) begin
$write(" ");
for (int j = 0; j < 4; j++) begin
$write("%3d ", matrix[i][j]);
end
$display("");
end
// 显示单位矩阵
$display("\n单位矩阵:");
for (int i = 0; i < 3; i++) begin
$write(" ");
for (int j = 0; j < 3; j++) begin
$write("%d ", identity[i][j]);
end
$display("");
end
// 数组切片
matrix[0] = '{10, 11, 12, 13}; // 设置第一行
$display("\n修改后的第一行:");
for (int j = 0; j < 4; j++)
$display(" matrix[0][%0d] = %0d", j, matrix[0][j]);
end
endmodule
1.2.4 数组的基本操作for和foreach
SystemVerilog提供了for循环和foreach循环来遍历数组:
module array_iteration_demo;
int array1[10];
int matrix[3][4];
bit [7:0] bytes[8];
initial begin
// 使用for循环初始化
for (int i = 0; i < 10; i++)
array1[i] = i * 2;
// 使用foreach循环显示(推荐方式)
$display("使用foreach遍历一维数组:");
foreach (array1[i])
$display(" array1[%0d] = %0d", i, array1[i]);
// 多维数组的foreach
foreach (matrix[i, j])
matrix[i][j] = i * 10 + j;
$display("\n使用foreach遍历二维数组:");
foreach (matrix[i]) begin
$write(" ");
foreach (matrix[i][j])
$write("%3d ", matrix[i][j]);
$display("");
end
// 嵌套foreach
$display("\n使用嵌套foreach:");
foreach (matrix[i]) begin
foreach (matrix[i][j]) begin
$display(" matrix[%0d][%0d] = %0d", i, j, matrix[i][j]);
end
end
// foreach的优势:自动处理数组边界
bytes = '{8'h01, 8'h02, 8'h04, 8'h08, 8'h10, 8'h20, 8'h40, 8'h80};
$display("\nforeach自动处理边界:");
foreach (bytes[idx])
$display(" bytes[%0d] = 0x%h", idx, bytes[idx]);
end
endmodule
1.3 动态数组
动态数组可以在运行时改变大小,提供了更灵活的内存管理:
module dynamic_array_demo;
// 动态数组声明
int dyn_array[]; // 动态整数数组
bit [7:0] byte_dyn[]; // 动态字节数组
string str_array[]; // 动态字符串数组
initial begin
// 分配内存
dyn_array = new[10]; // 分配10个元素
byte_dyn = new[5]; // 分配5个元素
// 初始化
foreach (dyn_array[i])
dyn_array[i] = i * i;
$display("初始动态数组:");
foreach (dyn_array[i])
$display(" dyn_array[%0d] = %0d", i, dyn_array[i]);
// 调整大小(保持原有数据)
dyn_array = new[15](dyn_array);
// 初始化新元素
for (int i = 10; i < 15; i++)
dyn_array[i] = i * 10;
$display("\n扩展后的动态数组:");
foreach (dyn_array[i])
$display(" dyn_array[%0d] = %0d", i, dyn_array[i]);
// 数组方法
$display("\n数组大小: %0d", dyn_array.size());
// 释放内存
dyn_array.delete();
$display("删除后数组大小: %0d", dyn_array.size());
// 字符串动态数组
str_array = new[3];
str_array[0] = "Hello";
str_array[1] = "World";
str_array[2] = "SystemVerilog";
$display("\n字符串数组:");
foreach (str_array[i])
$display(" str_array[%0d] = %s", i, str_array[i]);
end
endmodule
1.4 队列
队列是一种动态数据结构,支持在两端进行插入和删除操作:
module queue_demo;
// 队列声明
int queue_unbounded[$]; // 无界队列
bit [7:0] queue_bounded[$:15]; // 有界队列,最大16个元素
string str_queue[$];
initial begin
// 队列操作
queue_unbounded.push_back(10); // 尾部插入
queue_unbounded.push_back(20);
queue_unbounded.push_back(30);
queue_unbounded.push_front(5); // 头部插入
$display("初始队列:");
foreach (queue_unbounded[i])
$display(" queue[%0d] = %0d", i, queue_unbounded[i]);
// 队列访问
$display("\n队列信息:");
$display(" 大小: %0d", queue_unbounded.size());
$display(" 第一个元素: %0d", queue_unbounded[0]);
$display(" 最后一个元素: %0d", queue_unbounded[$]);
// 弹出操作
int front_val = queue_unbounded.pop_front();
int back_val = queue_unbounded.pop_back();
$display("\n弹出操作:");
$display(" 从前端弹出: %0d", front_val);
$display(" 从后端弹出: %0d", back_val);
$display("\n弹出后的队列:");
foreach (queue_unbounded[i])
$display(" queue[%0d] = %0d", i, queue_unbounded[i]);
// 插入到指定位置
queue_unbounded.insert(1, 15); // 在索引1处插入15
$display("\n插入后的队列:");
foreach (queue_unbounded[i])
$display(" queue[%0d] = %0d", i, queue_unbounded[i]);
// 删除指定位置
queue_unbounded.delete(0); // 删除索引0的元素
$display("\n删除后的队列:");
foreach (queue_unbounded[i])
$display(" queue[%0d] = %0d", i, queue_unbounded[i]);
// 字符串队列示例
str_queue.push_back("First");
str_queue.push_back("Second");
str_queue.push_back("Third");
$display("\n字符串队列:");
foreach (str_queue[i])
$display(" str_queue[%0d] = %s", i, str_queue[i]);
end
endmodule
1.5 关联数组
关联数组使用任意类型作为索引,适用于稀疏数据和查找表:
module associative_array_demo;
// 关联数组声明
int assoc_int[string]; // 字符串索引的整数数组
bit [31:0] sparse_array[int]; // 整数索引的稀疏数组
string name_table[int]; // 整数索引的字符串表
// 自定义类型作为索引
typedef struct {
int x, y;
} point_t;
int point_values[point_t]; // 结构体索引的数组
initial begin
// 字符串索引数组
assoc_int["apple"] = 100;
assoc_int["banana"] = 200;
assoc_int["cherry"] = 300;
assoc_int["date"] = 400;
$display("字符串索引数组:");
foreach (assoc_int[key])
$display(" %s = %0d", key, assoc_int[key]);
// 稀疏数组 - 只存储非零值
sparse_array[1000] = 32'hDEAD;
sparse_array[5000] = 32'hBEEF;
sparse_array[10000] = 32'hCAFE;
sparse_array[50000] = 32'hBABE;
$display("\n稀疏数组:");
foreach (sparse_array[idx])
$display(" [%0d] = 0x%h", idx, sparse_array[idx]);
// 关联数组方法
$display("\n数组信息:");
$display(" 字符串数组大小: %0d", assoc_int.size());
$display(" 稀疏数组大小: %0d", sparse_array.size());
// 检查键是否存在
if (assoc_int.exists("apple"))
$display(" 'apple'存在,值为: %0d", assoc_int["apple"]);
if (!assoc_int.exists("grape"))
$display(" 'grape'不存在");
// 删除元素
assoc_int.delete("banana");
$display("\n删除'banana'后:");
foreach (assoc_int[key])
$display(" %s = %0d", key, assoc_int[key]);
// 结构体索引示例
point_t p1 = '{x: 10, y: 20};
point_t p2 = '{x: 30, y: 40};
point_values[p1] = 100;
point_values[p2] = 200;
$display("\n结构体索引数组:");
foreach (point_values[pt])
$display(" (%0d,%0d) = %0d", pt.x, pt.y, point_values[pt]);
end
endmodule
1.6 数组的方法
1.6.1 数组直接赋值
SystemVerilog提供了多种数组赋值方法:
module array_assignment_demo;
int array1[5];
int array2[5];
int dyn_array[];
int queue_data[$];
initial begin
// 列表赋值
array1 = '{1, 2, 3, 4, 5};
$display("列表赋值:");
foreach (array1[i])
$display(" array1[%0d] = %0d", i, array1[i]);
// 重复赋值
array2 = '{5{10}}; // 所有元素都是10
$display("\n重复赋值:");
foreach (array2[i])
$display(" array2[%0d] = %0d", i, array2[i]);
// 混合赋值
array1 = '{1, 2, default: 0}; // 前两个元素为1,2,其余为0
$display("\n混合赋值:");
foreach (array1[i])
$display(" array1[%0d] = %0d", i, array1[i]);
// 索引赋值
array2 = '{0: 100, 2: 200, 4: 300, default: -1};
$display("\n索引赋值:");
foreach (array2[i])
$display(" array2[%0d] = %0d", i, array2[i]);
// 动态数组赋值
dyn_array = '{10, 20, 30, 40};
$display("\n动态数组赋值:");
foreach (dyn_array[i])
$display(" dyn_array[%0d] = %0d", i, dyn_array[i]);
// 队列赋值
queue_data = '{100, 200, 300};
$display("\n队列赋值:");
foreach (queue_data[i])
$display(" queue_data[%0d] = %0d", i, queue_data[i]);
// 数组复制
array1 = array2;
$display("\n数组复制后array1:");
foreach (array1[i])
$display(" array1[%0d] = %0d", i, array1[i]);
end
endmodule
1.6.2 数组大小返回
不同类型的数组提供了不同的大小查询方法:
module array_size_demo;
int fixed_array[10];
int dyn_array[];
int queue_data[$];
int assoc_array[string];
initial begin
// 固定数组大小
$display("固定数组大小: %0d", $size(fixed_array));
$display("固定数组维度: %0d", $dimensions(fixed_array));
$display("固定数组左边界: %0d", $left(fixed_array));
$display("固定数组右边界: %0d", $right(fixed_array));
$display("固定数组低位: %0d", $low(fixed_array));
$display("固定数组高位: %0d", $high(fixed_array));
// 动态数组
dyn_array = new[15];
$display("\n动态数组大小: %0d", dyn_array.size());
// 队列
queue_data = '{1, 2, 3, 4, 5, 6};
$display("\n队列大小: %0d", queue_data.size());
// 关联数组
assoc_array["key1"] = 100;
assoc_array["key2"] = 200;
assoc_array["key3"] = 300;
$display("\n关联数组大小: %0d", assoc_array.size());
// 多维数组
int matrix[3][4];
$display("\n多维数组信息:");
$display(" 总大小: %0d", $size(matrix));
$display(" 第一维大小: %0d", $size(matrix, 1));
$display(" 第二维大小: %0d", $size(matrix, 2));
// 数组为空检查
int empty_queue[$];
if (empty_queue.size() == 0)
$display("\n队列为空");
// 动态调整大小
dyn_array = new[20](dyn_array);
$display("\n调整后动态数组大小: %0d", dyn_array.size());
end
endmodule
1.6.3 数组运算方法
SystemVerilog提供了丰富的数组运算方法:
module array_operations_demo;
int numbers[] = '{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int even_numbers[$];
int sum_result;
initial begin
// 数组求和
sum_result = numbers.sum();
$display("数组求和: %0d", sum_result);
// 数组乘积
int product = numbers.product();
$display("数组乘积: %0d", product);
// 数组异或
int xor_result = numbers.xor();
$display("数组异或: %0d", xor_result);
// 数组与运算
int and_result = numbers.and();
$display("数组与运算: %0d", and_result);
// 数组或运算
int or_result = numbers.or();
$display("数组或运算: %0d", or_result);
// 查找方法
even_numbers = numbers.find(x) with (x % 2 == 0);
$display("\n偶数查找结果:");
foreach (even_numbers[i])
$display(" even_numbers[%0d] = %0d", i, even_numbers[i]);
// 查找索引
int indices[$] = numbers.find_index(x) with (x > 5);
$display("\n大于5的元素索引:");
foreach (indices[i])
$display(" index[%0d] = %0d, value = %0d", i, indices[i], numbers[indices[i]]);
// 查找第一个匹配
int first_even[$] = numbers.find_first(x) with (x % 2 == 0);
if (first_even.size() > 0)
$display("\n第一个偶数: %0d", first_even[0]);
// 查找最后一个匹配
int last_even[$] = numbers.find_last(x) with (x % 2 == 0);
if (last_even.size() > 0)
$display("最后一个偶数: %0d", last_even[0]);
// 最小值和最大值
int min_val[$] = numbers.min();
int max_val[$] = numbers.max();
$display("\n最小值: %0d", min_val[0]);
$display("最大值: %0d", max_val[0]);
// 唯一值
int duplicates[] = '{1, 2, 2, 3, 3, 3, 4, 5};
int unique_vals[$] = duplicates.unique();
$display("\n去重后的数组:");
foreach (unique_vals[i])
$display(" unique[%0d] = %0d", i, unique_vals[i]);
end
endmodule
1.6.4 数组排序方法
SystemVerilog提供了多种排序方法:
module array_sorting_demo;
int unsorted[] = '{5, 2, 8, 1, 9, 3, 7, 4, 6};
int sorted_array[];
// 自定义结构体用于排序
typedef struct {
string name;
int age;
real salary;
} person_t;
person_t people[] = '{
'{"Alice", 30, 50000.0},
'{"Bob", 25, 45000.0},
'{"Charlie", 35, 60000.0},
'{"David", 28, 48000.0}
};
initial begin
$display("原始数组:");
foreach (unsorted[i])
$display(" [%0d] = %0d", i, unsorted[i]);
// 升序排序
sorted_array = unsorted;
sorted_array.sort();
$display("\n升序排序:");
foreach (sorted_array[i])
$display(" [%0d] = %0d", i, sorted_array[i]);
// 降序排序
sorted_array = unsorted;
sorted_array.rsort();
$display("\n降序排序:");
foreach (sorted_array[i])
$display(" [%0d] = %0d", i, sorted_array[i]);
// 随机排序
sorted_array = unsorted;
sorted_array.shuffle();
$display("\n随机排序:");
foreach (sorted_array[i])
$display(" [%0d] = %0d", i, sorted_array[i]);
// 反转数组
sorted_array = unsorted;
sorted_array.reverse();
$display("\n反转数组:");
foreach (sorted_array[i])
$display(" [%0d] = %0d", i, sorted_array[i]);
// 结构体排序 - 按年龄排序
$display("\n原始人员信息:");
foreach (people[i])
$display(" %s: age=%0d, salary=%.0f", people[i].name, people[i].age, people[i].salary);
people.sort(x) with (x.age);
$display("\n按年龄排序:");
foreach (people[i])
$display(" %s: age=%0d, salary=%.0f", people[i].name, people[i].age, people[i].salary);
// 按薪水降序排序
people.rsort(x) with (x.salary);
$display("\n按薪水降序排序:");
foreach (people[i])
$display(" %s: age=%0d, salary=%.0f", people[i].name, people[i].age, people[i].salary);
end
endmodule
1.6.5 数组定位方法
数组定位方法用于查找满足特定条件的元素:
module array_locator_demo;
int data[] = '{10, 25, 30, 45, 50, 65, 70, 85, 90};
string words[] = '{"apple", "banana", "cherry", "date", "elderberry"};
initial begin
$display("原始数据:");
foreach (data[i])
$display(" data[%0d] = %0d", i, data[i]);
// find - 查找所有匹配的元素
int multiples_of_5[$] = data.find(x) with (x % 5 == 0);
$display("\n5的倍数:");
foreach (multiples_of_5[i])
$display(" [%0d] = %0d", i, multiples_of_5[i]);
// find_index - 查找匹配元素的索引
int indices[$] = data.find_index(x) with (x > 50);
$display("\n大于50的元素索引:");
foreach (indices[i])
$display(" index %0d: data[%0d] = %0d", i, indices[i], data[indices[i]]);
// find_first - 查找第一个匹配的元素
int first_large[$] = data.find_first(x) with (x > 40);
if (first_large.size() > 0)
$display("\n第一个大于40的元素: %0d", first_large[0]);
// find_first_index - 查找第一个匹配元素的索引
int first_index[$] = data.find_first_index(x) with (x > 40);
if (first_index.size() > 0)
$display("第一个大于40的元素索引: %0d", first_index[0]);
// find_last - 查找最后一个匹配的元素
int last_even[$] = data.find_last(x) with (x % 2 == 0);
if (last_even.size() > 0)
$display("\n最后一个偶数: %0d", last_even[0]);
// find_last_index - 查找最后一个匹配元素的索引
int last_index[$] = data.find_last_index(x) with (x % 2 == 0);
if (last_index.size() > 0)
$display("最后一个偶数的索引: %0d", last_index[0]);
// 字符串数组查找
$display("\n字符串数组:");
foreach (words[i])
$display(" words[%0d] = %s", i, words[i]);
// 查找长度大于5的字符串
string long_words[$] = words.find(s) with (s.len() > 5);
$display("\n长度大于5的字符串:");
foreach (long_words[i])
$display(" [%0d] = %s (length: %0d)", i, long_words[i], long_words[i].len());
// 查找包含特定字符的字符串
string words_with_a[$] = words.find(s) with (s.substr(0, 0) == "a" ||
s.substr(0, 0) == "A");
$display("\n以'a'或'A'开头的字符串:");
foreach (words_with_a[i])
$display(" [%0d] = %s", i, words_with_a[i]);
end
endmodule
1.7 结构体
结构体允许将相关的数据组合在一起,提供更好的数据组织方式:
module struct_detailed_demo;
// 基本结构体定义
typedef struct {
logic [31:0] addr;
logic [31:0] data;
logic [3:0] be; // byte enable
logic valid;
} bus_transaction_t;
// 打包结构体 - 可以作为向量使用
typedef struct packed {
logic [7:0] opcode;
logic [4:0] rs1;
logic [4:0] rs2;
logic [4:0] rd;
logic [10:0] immediate;
} instruction_t;
// 嵌套结构体
typedef struct {
string name;
int id;
struct {
string street;
string city;
int zip;
} address;
} employee_t;
// 联合体
typedef union packed {
logic [31:0] word;
logic [15:0] half[2];
logic [7:0] byte[4];
struct packed {
logic [15:0] high;
logic [15:0] low;
} parts;
} word_union_t;
bus_transaction_t trans1, trans2;
instruction_t inst;
employee_t emp;
word_union_t word_data;
initial begin
// 结构体初始化
trans1 = '{addr: 32'h1000, data: 32'hDEADBEEF, be: 4'hF, valid: 1'b1};
// 成员访问
$display("总线事务:");
$display(" 地址: 0x%h", trans1.addr);
$display(" 数据: 0x%h", trans1.data);
$display(" 字节使能: 0x%h", trans1.be);
$display(" 有效: %b", trans1.valid);
// 结构体赋值
trans2 = trans1;
trans2.addr = 32'h2000;
trans2.data = 32'hCAFEBABE;
$display("\n复制并修改后的事务:");
$display(" 地址: 0x%h", trans2.addr);
$display(" 数据: 0x%h", trans2.data);
// 打包结构体
inst = '{opcode: 8'h12, rs1: 5'd1, rs2: 5'd2, rd: 5'd3, immediate: 11'h123};
$display("\n指令结构体:");
$display(" 操作码: 0x%h", inst.opcode);
$display(" 源寄存器1: %0d", inst.rs1);
$display(" 源寄存器2: %0d", inst.rs2);
$display(" 目标寄存器: %0d", inst.rd);
$display(" 立即数: 0x%h", inst.immediate);
$display(" 完整指令: 0x%h", inst); // 作为向量显示
// 嵌套结构体
emp.name = "John Doe";
emp.id = 12345;
emp.address.street = "123 Main St";
emp.address.city = "Anytown";
emp.address.zip = 12345;
$display("\n员工信息:");
$display(" 姓名: %s", emp.name);
$display(" ID: %0d", emp.id);
$display(" 地址: %s, %s %0d", emp.address.street, emp.address.city, emp.address.zip);
// 联合体使用
word_data.word = 32'h12345678;
$display("\n联合体数据:");
$display(" 字: 0x%h", word_data.word);
$display(" 高半字: 0x%h", word_data.parts.high);
$display(" 低半字: 0x%h", word_data.parts.low);
$display(" 字节0: 0x%h", word_data.byte[0]);
$display(" 字节1: 0x%h", word_data.byte[1]);
$display(" 字节2: 0x%h", word_data.byte[2]);
$display(" 字节3: 0x%h", word_data.byte[3]);
end
endmodule
1.8 枚举类型
1.8.1 定义枚举值
枚举类型提供了一种定义命名常量的方式,提高代码可读性:
module enum_definition_demo;
// 基本枚举定义
typedef enum {RED, GREEN, BLUE} color_t;
// 指定枚举值
typedef enum {IDLE = 0, READ = 1, WRITE = 2, ERROR = 15} state_t;
// 指定基础类型
typedef enum bit [1:0] {S0, S1, S2, S3} state2_t;
// 枚举范围
typedef enum {LOW = 0, MID = 5, HIGH = 10} priority_t;
// 字符串枚举(仅用于显示)
typedef enum {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY} day_t;
color_t my_color;
state_t current_state;
state2_t fsm_state;
priority_t task_priority;
day_t today;
initial begin
// 枚举赋值
my_color = RED;
current_state = IDLE;
fsm_state = S0;
task_priority = HIGH;
today = MONDAY;
$display("枚举值:");
$display(" 颜色: %s (值: %0d)", my_color.name(), my_color);
$display(" 状态: %s (值: %0d)", current_state.name(), current_state);
$display(" FSM状态: %s (值: %0d)", fsm_state.name(), fsm_state);
$display(" 优先级: %s (值: %0d)", task_priority.name(), task_priority);
$display(" 今天: %s (值: %0d)", today.name(), today);
// 枚举比较
if (my_color == RED)
$display("\n颜色是红色");
if (current_state != ERROR)
$display("状态正常");
// 枚举范围检查
if (task_priority >= MID)
$display("高优先级任务");
// 遍历枚举值
$display("\n所有颜色:");
my_color = my_color.first();
do begin
$display(" %s = %0d", my_color.name(), my_color);
my_color = my_color.next();
end while (my_color != my_color.first());
end
endmodule
1.8.2 枚举类型的子程序
枚举类型提供了多个内置方法:
module enum_methods_demo;
typedef enum {RESET, INIT, RUN, PAUSE, STOP, ERROR} state_t;
typedef enum bit [2:0] {S0, S1, S2, S3, S4} counter_state_t;
state_t current_state;
counter_state_t cnt_state;
initial begin
current_state = RESET;
$display("枚举方法演示:");
// first() - 获取第一个枚举值
$display(" 第一个状态: %s", current_state.first().name());
// last() - 获取最后一个枚举值
$display(" 最后一个状态: %s", current_state.last().name());
// next() - 获取下一个枚举值
current_state = RUN;
$display(" RUN的下一个状态: %s", current_state.next().name());
// prev() - 获取前一个枚举值
$display(" RUN的前一个状态: %s", current_state.prev().name());
// num() - 获取枚举值的数量
$display(" 状态总数: %0d", current_state.num());
// name() - 获取枚举值的名称
current_state = ERROR;
$display(" 当前状态名称: %s", current_state.name());
// 遍历所有枚举值
$display("\n所有状态:");
current_state = current_state.first();
repeat (current_state.num()) begin
$display(" %s = %0d", current_state.name(), current_state);
current_state = current_state.next();
end
// 计数器状态演示
cnt_state = S0;
$display("\n计数器状态方法:");
$display(" 当前: %s, 下一个: %s", cnt_state.name(), cnt_state.next().name());
$display(" 第一个: %s, 最后一个: %s", cnt_state.first().name(), cnt_state.last().name());
end
endmodule
1.8.3 枚举类型的转换
枚举类型可以与其他数据类型进行转换:
module enum_conversion_demo;
typedef enum bit [2:0] {IDLE, READ, WRITE, WAIT, DONE} state_t;
typedef enum {LOW, MEDIUM, HIGH} priority_t;
state_t current_state;
priority_t task_priority;
int state_value;
bit [2:0] state_bits;
initial begin
// 枚举到整数转换
current_state = READ;
state_value = current_state;
$display("枚举转换演示:");
$display(" 状态 %s 的值: %0d", current_state.name(), state_value);
// 整数到枚举转换
state_value = 3;
if ($cast(current_state, state_value))
$display(" 值 %0d 对应状态: %s", state_value, current_state.name());
else
$display(" 值 %0d 无效", state_value);
// 位向量转换
state_bits = 3'b010;
if ($cast(current_state, state_bits))
$display(" 位向量 %b 对应状态: %s", state_bits, current_state.name());
// 无效值转换
state_value = 7; // 超出枚举范围
if (!$cast(current_state, state_value))
$display(" 值 %0d 转换失败", state_value);
// 枚举比较
current_state = READ;
if (current_state > IDLE)
$display(" %s 状态值大于 %s", current_state.name(), IDLE.name());
// 枚举运算
task_priority = MEDIUM;
$display("\n优先级运算:");
$display(" 当前优先级: %s (值: %0d)", task_priority.name(), task_priority);
// 优先级递增
if (task_priority != task_priority.last()) begin
task_priority = task_priority.next();
$display(" 提升后优先级: %s (值: %0d)", task_priority.name(), task_priority);
end
end
endmodule
1.9 字符串变量
SystemVerilog提供了强大的字符串处理功能:
module string_demo;
string str1, str2, str3;
string str_array[5];
initial begin
// 字符串赋值
str1 = "Hello";
str2 = "World";
str3 = "SystemVerilog";
$display("字符串基本操作:");
$display(" str1 = %s", str1);
$display(" str2 = %s", str2);
$display(" str3 = %s", str3);
// 字符串连接
string greeting = {str1, " ", str2, "!"};
$display(" 连接结果: %s", greeting);
// 字符串长度
$display("\n字符串长度:");
$display(" str1长度: %0d", str1.len());
$display(" greeting长度: %0d", greeting.len());
// 字符串比较
$display("\n字符串比较:");
if (str1 == "Hello")
$display(" str1 等于 'Hello'");
if (str1 != str2)
$display(" str1 不等于 str2");
// 字符串方法
$display("\n字符串方法:");
$display(" str3转大写: %s", str3.toupper());
$display(" str3转小写: %s", str3.tolower());
// 子字符串
string sub = str3.substr(6, 11); // "Verilog"
$display(" 子字符串(6,11): %s", sub);
// 字符访问
$display(" str1第一个字符: %c (ASCII: %0d)", str1.getc(0), str1.getc(0));
// 字符串查找
int pos = greeting.search("World");
if (pos >= 0)
$display(" 'World'在greeting中的位置: %0d", pos);
// 字符串替换
string replaced = greeting;
replaced.putc(6, "w"); // 将'W'替换为'w'
$display(" 替换后: %s", replaced);
// 字符串数组
str_array[0] = "First";
str_array[1] = "Second";
str_array[2] = "Third";
str_array[3] = "Fourth";
str_array[4] = "Fifth";
$display("\n字符串数组:");
foreach (str_array[i])
$display(" str_array[%0d] = %s", i, str_array[i]);
// 格式化字符串
int value = 42;
real pi = 3.14159;
string formatted = $sformatf("整数: %0d, 实数: %.2f", value, pi);
$display("\n格式化字符串: %s", formatted);
// 字符串转换
string num_str = "123";
int converted_num = num_str.atoi();
$display("\n字符串转整数: '%s' -> %0d", num_str, converted_num);
string real_str = "3.14";
real converted_real = real_str.atoreal();
$display("字符串转实数: '%s' -> %f", real_str, converted_real);
end
endmodule
总结
本文档详细介绍了SystemVerilog的数据类型系统,包括:
- 内建数据类型:四值逻辑和二值逻辑的区别,符号类型,类型转换和流操作符
- 定宽数组:一维数组,常量数组,多维数组和遍历方法
- 动态数据结构:动态数组,队列和关联数组的使用
- 数组方法:赋值,大小查询,运算,排序和定位方法
- 结构化数据:结构体和联合体的定义和使用
- 枚举类型:枚举定义,内置方法和类型转换
- 字符串处理:字符串变量的各种操作和方法
SystemVerilog的丰富数据类型为验证环境提供了强大的建模能力,使得测试平台的开发更加高效和灵活。掌握这些数据类型的使用是学习SystemVerilog验证的重要基础。
6775

被折叠的 条评论
为什么被折叠?



