[SystemVerilog] Arrays
SystemVerilog Arrays用法详解
SystemVerilog 提供了丰富的数组类型,用于存储和操作一组数据,广泛应用于硬件设计和验证。数组类型包括固定大小数组、动态数组、关联数组和队列,每种类型针对不同场景提供了灵活性和效率。SystemVerilog 数组支持硬件建模(如寄存器文件、内存)和验证中的复杂数据处理(如测试用例、数据包队列)。本文将详细介绍 SystemVerilog 中数组的各种用法,包括定义、操作、方法、参数化、多维数组、以及在设计和验证中的应用,并提供示例代码和最佳实践。
1. Arrays 概述
SystemVerilog 的数组类型分为以下四类:
- 固定大小数组(Fixed-Size Arrays):在编译时确定大小,适合硬件建模。
- 动态数组(Dynamic Arrays):运行时分配大小,适合验证中的可变数据。
- 关联数组(Associative Arrays):使用非连续索引(如字符串或整数),适合稀疏数据。
- 队列(Queues):动态大小,支持高效的插入和删除操作,适合 FIFO 数据处理。
主要用途
- 硬件设计:实现寄存器组、存储器或数据缓冲区。
- 验证:存储测试数据、事务队列或配置信息。
- 模块化:提高代码的组织性和可维护性。
基本特性
- 支持多种数据类型(如
logic
、int
、struct
)。 - 提供内置方法(如
size()
、push_back()
)简化操作。 - 可结合参数化、多维数组等功能实现复杂数据结构。
2. 固定大小数组(Fixed-Size Arrays)
固定大小数组在编译时确定大小,适合硬件设计中的寄存器文件、内存或常量表。
2.1 定义与使用
语法:
type array_name [size];
示例:
module example;logic [7:0] mem [0:15]; // 16个8位元素initial beginmem[0] = 8'hAA;mem[1] = 8'hBB;$display("Mem[0]: %h, Mem[1]: %h", mem[0], mem[1]);end
endmodule
说明:
mem
是一个包含 16 个 8 位元素的数组,索引从 0 到 15。- 通过索引(如
mem[0]
)访问元素。 - 支持综合,适合硬件存储建模。
注意:
- 数组大小必须在编译时确定。
- 索引超出范围会导致仿真错误或综合失败。
2.2 数组初始化
固定大小数组支持使用字面量或默认值初始化。
示例:
module example;logic [3:0] data [0:3] = '{4'h1, 4'h2, 4'h3, 4'h4}; // 字面量初始化logic [7:0] mem [0:3] = '{default: 8'hFF}; // 默认值初始化initial begin$display("Data: %p", data);$display("Mem: %p", mem);end
endmodule
说明:
'{...}
指定每个元素的值。'{default: value}
为所有元素赋相同值。%p
格式化显示数组内容。
注意:
- 字面量初始化需匹配数组大小。
- 默认值必须与元素类型兼容。
3. 动态数组(Dynamic Arrays)
动态数组在运行时分配大小,适合验证中处理可变长度的数据。
3.1 定义与分配
语法:
type array_name [];
示例:
module example;int dyn_array [];initial begindyn_array = new[5]; // 分配5个元素dyn_array[0] = 10;dyn_array[4] = 50;$display("Size: %0d, Element[0]: %0d", dyn_array.size(), dyn_array[0]);end
endmodule
说明:
- 使用
new[size]
分配数组大小,元素初始化为默认值(int
为 0)。 size()
方法返回当前数组大小。- 支持运行时调整大小。
注意:
- 分配前数组为空,访问会导致错误。
- 动态数组不支持综合,仅用于验证。
3.2 动态数组操作
支持重新分配、复制和删除操作。
示例:
module example;int dyn_array [];initial begindyn_array = new[3]; // 分配3个元素dyn_array = '{1, 2, 3}; // 初始化$display("Initial: %p", dyn_array);dyn_array = new[5](dyn_array); // 扩展到5个元素,保留原有数据$display("Extended: %p", dyn_array);dyn_array.delete(); // 删除数组$display("Size after delete: %0d", dyn_array.size());end
endmodule
说明:
new[size](old_array)
扩展数组,复制原有数据,新元素初始化为默认值。delete()
清空数组,大小变为 0。- 动态调整适合验证中的数据处理。
注意:
- 重新分配会覆盖原有数据,除非使用复制构造。
- 删除后需重新分配才能使用。
4. 关联数组(Associative Arrays)
关联数组使用非连续索引(如字符串、整数),适合存储稀疏或键值对数据。
4.1 定义与使用
语法:
type array_name [index_type];
示例:
module example;int assoc_array [string];initial beginassoc_array["apple"] = 1;assoc_array["banana"] = 2;$display("Apple: %0d, Banana: %0d", assoc_array["apple"], assoc_array["banana"]);end
endmodule
说明:
- 索引类型为
string
,键为"apple"
和"banana"
。 - 适合键值对存储,如配置表。
注意:
- 关联数组不支持综合,仅用于验证。
- 未定义的索引访问会返回默认值(需检查存在性)。
4.2 关联数组方法
支持检查、遍历和删除操作。
示例:
module example;int assoc_array [string];initial beginassoc_array["x"] = 10;assoc_array["y"] = 20;if (assoc_array.exists("x"))$display("x exists: %0d", assoc_array["x"]);$display("Size: %0d", assoc_array.num());assoc_array.delete("x"); // 删除单个元素$display("Size after delete: %0d", assoc_array.num());end
endmodule
说明:
exists(index)
检查索引是否存在。num()
返回元素数量。delete(index)
删除指定元素,delete()
清空数组。
注意:
- 使用
exists()
避免访问无效索引。 - 遍历需使用
foreach
或first()
/next()
。
5. 队列(Queues)
队列是动态数组的扩展,支持在头部或尾部高效插入和删除,适合 FIFO 或列表操作。
5.1 定义与操作
语法:
type queue_name [$];
示例:
module example;int queue [$];initial beginqueue.push_back(10); // 尾部添加queue.push_front(20); // 头部添加$display("Queue: %p", queue);int val = queue.pop_front(); // 移除并返回头部$display("Popped: %0d, Queue: %p", val, queue);end
endmodule
说明:
- 方法包括
push_back
、push_front
、pop_back
、pop_front
。 - 队列大小动态调整,初始为空。
注意:
- 队列不支持综合,仅用于验证。
- 访问空队列(如
pop_front()
)会导致错误。
5.2 队列方法
支持插入、删除和查询操作。
示例:
module example;int queue [$];initial beginqueue = '{1, 2, 3}; // 初始化queue.insert(1, 10); // 在索引1插入10$display("After insert: %p", queue);queue.delete(2); // 删除索引2$display("After delete: %p", queue);$display("Size: %0d", queue.size());end
endmodule
说明:
insert(index, value)
在指定索引插入元素。delete(index)
删除指定索引的元素。size()
返回队列长度。
注意:
- 插入或删除操作会调整索引,需注意后续访问。
- 使用
foreach
遍历队列。
6. 参数化数组
数组可以通过参数动态配置大小或位宽,增强复用性。
示例:参数化固定大小数组
module example #(parameter SIZE = 4, WIDTH = 8);logic [WIDTH-1:0] mem [0:SIZE-1];initial beginmem[0] = 'hA5;$display("Mem[0]: %h", mem[0]);end
endmodule
说明:
- 参数
SIZE
控制数组大小,WIDTH
控制元素位宽。 - 适合可配置的存储器或寄存器组。
注意:
- 参数必须在编译时确定。
- 确保参数值与硬件需求匹配。
7. 多维数组
SystemVerilog 支持多维数组,适合表示矩阵、表格或复杂存储结构。
示例:二维数组
module example;logic [7:0] matrix [0:3][0:2]; // 4x3二维数组initial beginmatrix[0][0] = 8'h11;matrix[1][1] = 8'h22;$display("Matrix[0][0]: %h, Matrix[1][1]: %h", matrix[0][0], matrix[1][1]);end
endmodule
说明:
matrix
是一个 4x3 的二维数组,每个元素为 8 位。- 通过多级索引(如
matrix[0][0]
)访问元素。 - 适合图像数据或内存建模。
注意:
- 多维数组支持综合,但高维度可能增加复杂性。
- 确保索引范围有效。
示例:多维动态数组
module example;int dyn_matrix [][];initial begindyn_matrix = new[3]; // 分配3行foreach (dyn_matrix[i])dyn_matrix[i] = new[2]; // 每行分配2列dyn_matrix[0][0] = 10;dyn_matrix[1][1] = 20;$display("Matrix: %p", dyn_matrix);end
endmodule
说明:
- 动态分配二维数组,需为每行单独分配。
- 适合验证中的可变矩阵。
注意:
- 多维动态数组需逐级分配。
- 不支持综合,仅用于验证。
8. 数组在硬件设计中的应用
固定大小数组广泛用于硬件设计中的存储器、寄存器组或数据缓冲区。
示例:寄存器文件
module reg_file (input logic clk, rst_n, write_en,input logic [1:0] addr,input logic [7:0] write_data,output logic [7:0] read_data);logic [7:0] regs [0:3];always_ff @(posedge clk or negedge rst_n) beginif (!rst_n)regs = '{default: 0};else if (write_en)regs[addr] <= write_data;endassign read_data = regs[addr];
endmodule
说明:
regs
是一个 4 个 8 位寄存器的数组。- 支持同步写和异步读。
- 适合硬件存储建模。
注意:
- 确保数组大小和索引与硬件需求匹配。
- 验证综合工具支持。
9. 数组在验证中的应用
动态数组、关联数组和队列在验证环境(如 UVM)中用于存储测试数据、事务或配置。
示例:UVM 验证中的队列
import uvm_pkg::*;
`include "uvm_macros.svh"module example;int tx_queue [$];initial begintx_queue.push_back(100);tx_queue.push_back(200);`uvm_info("TEST", $sformatf("Queue: %p", tx_queue), UVM_LOW)tx_queue.pop_front();`uvm_info("TEST", $sformatf("After pop: %p", tx_queue), UVM_LOW)end
endmodule
说明:
tx_queue
存储事务数据。- 使用 UVM 宏打印队列内容。
- 队列适合事务处理或 FIFO 测试。
注意:
- 验证中,结合
struct
或class
增强数据组织。 - 确保队列操作(如
pop
)在非空时执行。
10. 数组的高级用法
10.1 数组切片
SystemVerilog 支持数组切片,提取或赋值部分元素。
module example;logic [7:0] data [0:5] = '{8'h1, 8'h2, 8'h3, 8'h4, 8'h5, 8'h6};initial beginlogic [7:0] slice [] = data[1:3]; // 提取索引1到3$display("Slice: %p", slice);end
endmodule
说明:
data[1:3]
提取{8'h2, 8'h3, 8'h4}
。- 切片返回动态数组。
注意:
- 切片不支持综合,仅用于验证。
- 确保切片范围有效。
10.2 数组方法综合
固定大小数组支持部分内置方法(如 sum
、min
),可用于验证。
module example;int values [0:3] = '{10, 20, 30, 40};initial begin$display("Sum: %0d", values.sum());$display("Min: %0d", values.min());end
endmodule
说明:
sum()
计算数组元素之和。min()
返回最小值。- 支持
max()
、product()
等。
注意:
- 这些方法不支持综合,仅用于验证。
- 确保数组非空。
11. 注意事项与最佳实践
-
类型选择:
- 硬件设计使用固定大小数组,支持综合。
- 验证中使用动态数组、关联数组或队列,处理可变数据。
-
综合支持:
- 仅固定大小数组支持综合,确保大小和索引确定。
- 验证综合工具对数组操作的支持。
-
初始化:
- 显式初始化数组,避免 X 或未定义值。
- 使用字面量或默认值简化初始化。
-
操作安全:
- 动态数组和队列需检查大小或存在性(如
exists()
、size()
)。 - 避免访问空数组或无效索引。
- 动态数组和队列需检查大小或存在性(如
-
参数化:
- 使用参数配置数组大小或位宽,增强复用性。
- 确保参数值与设计需求一致。
-
验证环境:
- 在 UVM 中,使用队列或关联数组存储事务。
- 结合
struct
或class
组织复杂数据。
-
代码可读性:
- 为数组提供有意义的名称和注释。
- 使用
foreach
或方法简化遍历和操作。
-
调试与验证:
- 检查数组的初始值和操作逻辑。
- 使用仿真工具验证数组行为。
12. 总结
SystemVerilog 的数组类型(固定大小数组、动态数组、关联数组和队列)为硬件设计和验证提供了灵活的数据存储和操作方式。固定大小数组适合硬件建模,如寄存器文件和存储器;动态数组、关联数组和队列适合验证中的可变数据处理,如事务队列和配置表。通过参数化、多维数组、切片和内置方法,数组支持复杂数据结构和高效操作。遵循最佳实践并根据应用场景选择合适的数组类型,能够显著提高代码质量和设计效率。
13. 设计工具推荐
- SZ901:
SZ901 是一款基于XVC协议的FPGA网络下载器。- 最高支持53M
- 支持4路JTAG独立使用
- 支持端口合并
- 支持国产FLASH烧写
- 下载器无限扩展
- 配备专属程序固化软件,一键烧写,能大大减小程序固化时间!