[SystemVerilog] Struct
SystemVerilog Struct用法详解
SystemVerilog 的 struct
是一种复合数据类型,用于将多个不同类型的变量(成员)组织成一个单一的实体。struct
在硬件设计和验证中广泛使用,特别适合表示复杂的数据结构,如数据包、配置寄存器或状态信息。与联合体(union
)和类(class
)相比,struct
提供了静态、紧凑的数据组织方式,并且支持综合。本文将详细介绍 SystemVerilog 中 struct
的各种用法,包括基本定义、成员访问、嵌套结构、参数化、数组、以及在验证和设计中的应用,并提供示例代码和最佳实践。
1. Struct 概述
struct
是一种用户定义的复合数据类型,允许将多个不同类型的成员组合在一起,形成一个逻辑单元。每个成员占用独立的存储空间,访问时通过点号(.
)引用。与其他数据类型的对比:
- 与联合体(union):
struct
的成员独立存储,union
的成员共享存储空间。 - 与类(class):
struct
是静态类型,支持综合;class
是动态类型,仅用于验证。 - 与数组:
struct
支持异构成员,数组成员类型相同。
主要用途
- 硬件设计:表示寄存器组、数据包或配置信息。
- 验证:在测试环境中组织复杂数据(如 UVM 事务)。
- 模块化:提高代码的可读性和可维护性。
基本语法
struct [packed] [signed/unsigned] {type1 member1;type2 member2;...
} struct_name;
packed
:可选,指定结构体的紧凑存储,适合硬件综合。signed/unsigned
:可选,指定结构体成员的符号性(通常与packed
一起使用)。type1, type2
:成员的数据类型,如logic
、int
等。struct_name
:结构体的名称。
2. 基本 Struct 定义与使用
struct
的基本用法是定义一组相关成员,并在代码中访问这些成员。
示例:简单数据包结构体
module example;struct {int id;logic [7:0] data;bit valid;} packet;initial beginpacket.id = 1;packet.data = 8'hA5;packet.valid = 1;$display("Packet: ID=%d, Data=%h, Valid=%b", packet.id, packet.data, packet.valid);end
endmodule
说明:
packet
结构体包含三个成员:id
(32位整数)、data
(8位逻辑信号)和valid
(1位标志)。- 通过点号(
.
)访问成员,如packet.id
。 - 结构体成员独立存储,修改一个成员不影响其他成员。
注意:
- 结构体成员的默认初始值取决于其类型(
logic
为 X,int
为 0,bit
为 0)。 - 未使用
packed
时,结构体可能包含填充位,不适合直接映射到硬件。
3. Packed Struct
packed
结构体将所有成员紧凑存储,适合硬件设计中表示连续的位字段(如寄存器或数据包)。packed
结构体可以整体赋值或比较。
示例:紧凑寄存器结构体
module example;struct packed {logic [3:0] opcode;logic [7:0] address;logic valid;} reg_config;initial beginreg_config = 13'h1A5F; // 整体赋值(opcode=4'h1, address=8'hA5, valid=1)$display("Opcode: %h, Address: %h, Valid: %b", reg_config.opcode, reg_config.address, reg_config.valid);end
endmodule
说明:
packed
确保opcode
、address
和valid
紧凑排列为 13 位。- 整体赋值(如
reg_config = 13'h1A5F
)按照成员定义顺序分配位。 - 支持位操作和综合,适合寄存器建模。
注意:
packed
结构体中的成员必须是可综合类型(如logic
、bit
)。- 成员按声明顺序从高位到低位排列(除非明确指定位域)。
带符号的 Packed Struct
packed
结构体可以指定 signed
或 unsigned
,影响整体结构体的符号性。
module example;struct packed signed {logic [3:0] value;logic sign;} data;initial begindata = -5; // 带符号赋值$display("Value: %d, Sign: %b", data.value, data.sign);end
endmodule
说明:
signed
指定结构体整体为有符号类型。- 赋值时按有符号整数解释。
注意:
- 符号性仅影响整体赋值或比较,成员仍按其类型处理。
- 通常与
packed
一起使用。
4. 嵌套 Struct
struct
支持嵌套,允许在结构体中定义其他结构体,形成层次化数据结构。
示例:嵌套数据包
module example;struct {int id;struct {logic [7:0] payload;logic [3:0] crc;} body;bit valid;} packet;initial beginpacket.id = 1;packet.body.payload = 8'hA5;packet.body.crc = 4'hF;packet.valid = 1;$display("Packet: ID=%d, Payload=%h, CRC=%h, Valid=%b", packet.id, packet.body.payload, packet.body.crc, packet.valid);end
endmodule
说明:
body
是一个嵌套结构体,包含payload
和crc
。- 通过多级点号访问成员,如
packet.body.payload
。 - 嵌套结构体适合表示复杂数据层次。
注意:
- 嵌套结构体可能增加存储需求,未使用
packed
时可能包含填充位。 - 确保嵌套层次清晰,避免过深嵌套。
5. 参数化 Struct
struct
可以通过模块参数或局部参数(localparam
)实现参数化,动态配置成员的位宽或其他属性。
示例:参数化结构体
module example #(parameter DATA_WIDTH = 8);struct packed {logic [DATA_WIDTH-1:0] data;logic [3:0] opcode;} packet;initial beginpacket.data = 'hA5;packet.opcode = 4'hF;$display("Data: %h, Opcode: %h", packet.data, packet.opcode);end
endmodule
说明:
- 参数
DATA_WIDTH
控制data
成员的位宽。 packed
确保紧凑存储。- 参数化提高了结构体的复用性。
注意:
- 参数必须在编译时确定。
- 确保成员类型与参数值兼容。
6. Struct 数组
struct
可以作为数组元素,适合表示一组相似的数据结构,如数据包队列或寄存器组。
示例:结构体数组
module example;struct {int id;logic [7:0] data;} packet [0:2]; // 3个packet结构体initial beginpacket[0] = '{id: 1, data: 8'hA1}; // 使用结构体字面量赋值packet[1] = '{id: 2, data: 8'hA2};packet[2] = '{id: 3, data: 8'hA3};foreach (packet[i])$display("Packet[%0d]: ID=%d, Data=%h", i, packet[i].id, packet[i].data);end
endmodule
说明:
packet
是一个包含 3 个结构体的数组。- 使用结构体字面量
'{id: value, data: value}
赋值。 foreach
循环遍历数组。
注意:
- 结构体数组支持综合,适合寄存器文件或内存建模。
- 确保数组大小在编译时确定。
7. Struct 在验证中的应用
struct
在验证环境(如 UVM)中常用于定义事务(transaction)或数据包,组织测试数据。
示例:UVM 验证中的 Struct
import uvm_pkg::*;
`include "uvm_macros.svh"module example;struct {int id;logic [7:0] data;bit valid;} packet;initial beginpacket = '{id: 1, data: 8'hA5, valid: 1};`uvm_info("TEST", $sformatf("Packet: ID=%0d, Data=%h, Valid=%b", packet.id, packet.data, packet.valid), UVM_LOW)end
endmodule
说明:
packet
结构体表示一个简单的事务。- 使用 UVM 宏(
uvm_info
)打印结构体内容。 - 结构体适合定义测试用例中的数据结构。
注意:
- 验证中,
struct
常与class
结合,class
用于动态行为,struct
用于静态数据。 - 确保结构体成员类型支持仿真。
8. Struct 在硬件设计中的应用
struct
在硬件设计中常用于表示寄存器组、数据包或状态信息,支持综合。
示例:寄存器组
module reg_bank (input logic clk, rst_n, write_en,input logic [7:0] write_data,output logic [7:0] read_data);struct packed {logic [3:0] control;logic [3:0] status;} reg_file;always_ff @(posedge clk or negedge rst_n) beginif (!rst_n)reg_file = '0;else if (write_en)reg_file = write_data;endassign read_data = reg_file;
endmodule
说明:
reg_file
是一个packed
结构体,表示 8 位寄存器组。- 支持同步写操作,异步复位。
read_data
直接输出结构体内容。
注意:
- 使用
packed
确保紧凑存储和位对齐。 - 验证综合工具对
struct
的支持。
9. Struct 的高级用法
9.1 结构体字面量赋值
SystemVerilog 支持使用结构体字面量('{}
)为结构体赋值,简化初始化。
module example;struct {int id;logic [7:0] data;} packet;initial beginpacket = '{id: 1, data: 8'hA5}; // 字面量赋值$display("Packet: ID=%d, Data=%h", packet.id, packet.data);end
endmodule
说明:
'{id: value, data: value}
按成员名称赋值,顺序无关。- 提高代码可读性。
注意:
- 所有成员必须赋值,否则报错。
- 支持嵌套字面量。
9.2 默认值赋值
可以使用 '{default: value}
为结构体所有成员赋默认值。
module example;struct {int id;logic [7:0] data;bit valid;} packet;initial beginpacket = '{default: 0}; // 所有成员赋值为 0$display("Packet: ID=%d, Data=%h, Valid=%b", packet.id, packet.data, packet.valid);end
endmodule
说明:
'{default: 0}
将所有成员初始化为 0。- 适合快速清零或初始化。
注意:
- 默认值必须与成员类型兼容。
- 仅适用于简单初始化。
9.3 结构体比较
struct
支持整体比较(==
或 !=
),但需确保成员类型支持比较。
module example;struct {int id;logic [7:0] data;} pkt1, pkt2;initial beginpkt1 = '{id: 1, data: 8'hA5};pkt2 = '{id: 1, data: 8'hA5};if (pkt1 == pkt2)$display("Packets are equal");else$display("Packets are different");end
endmodule
说明:
- 比较逐个检查成员。
packed
结构体支持位级比较。
注意:
- 非综合类型(如
string
)可能导致比较不可靠。 - 确保比较逻辑明确。
10. 注意事项与最佳实践
-
类型选择:
- 硬件设计中使用
packed
结构体,确保紧凑存储。 - 验证中可以使用非
packed
结构体,灵活组织数据。
- 硬件设计中使用
-
综合支持:
- 确保
struct
成员为可综合类型(如logic
、bit
)。 - 验证综合工具对
packed
和嵌套结构体的支持。
- 确保
-
成员访问:
- 使用点号(
.
)访问成员,确保成员名称唯一。 - 避免深层嵌套,保持访问简洁。
- 使用点号(
-
初始化:
- 显式初始化结构体成员,避免 X 或未定义值。
- 使用结构体字面量或默认值简化初始化。
-
数组与结构体:
- 结构体数组适合表示寄存器组或数据队列。
- 确保数组大小在编译时确定(硬件设计)。
-
验证环境:
- 在 UVM 中,使用
struct
定义静态事务数据。 - 结合
class
实现动态行为。
- 在 UVM 中,使用
-
代码可读性:
- 为结构体和成员提供有意义的名称。
- 添加注释说明结构体用途和成员功能。
-
调试与验证:
- 检查结构体成员的初始值和赋值逻辑。
- 使用仿真工具验证结构体行为。
11. 总结
SystemVerilog 的 struct
是一种灵活的复合数据类型,适用于硬件设计和验证中的数据组织。通过基本定义、packed
结构体、嵌套结构、参数化、数组等功能,struct
支持复杂数据建模,如寄存器组、数据包和事务。在硬件设计中,packed
结构体确保紧凑存储和综合支持;在验证中,struct
提供静态数据组织,结合 class
增强测试灵活性。遵循最佳实践并根据应用场景选择合适的 struct
用法,能够显著提高代码的可读性、可维护性和设计效率。
12. 设计工具推荐
- SZ901:
SZ901 是一款基于XVC协议的FPGA网络下载器。- 最高支持53M
- 支持4路JTAG独立使用
- 支持端口合并
- 支持国产FLASH烧写
- 下载器无限扩展
- 配备专属程序固化软件,一键烧写,能大大减小程序固化时间!