SystemVerilog 的 union 成员输出大写 I 是什么原因?
大写 I 不是 SystemVerilog 为这里的整数输出规定的特殊状态。仅凭这段日志,无法确定它在所用仿真器中的具体含义。建议先排查仿真器对 unpacked union 的读取或格式化实现。
代码中的 union 没有声明为 packed,不能直接假定各成员按最低位对齐。如果需要通过多个成员稳定地查看同一组位,建议使用明确的位切片,或重新设计为成员总位宽一致的 union packed。
为什么这些输出值得怀疑?
c 的声明是:
bit [1:0] c;
它是一个无符号的 2 位、2-state 值,只能表示 0~3。因此:
%0d输出的I不能解释为正常的整数值。- 十六进制输出的
9超出了该成员能表示的范围。 - 八进制输出的
9也不是有效的八进制数字。
这些现象提示实现或显示可能存在异常,但还不足以确定具体缺陷。尤其是 $display("A.c = %0d", A.c) 也输出了 I,所以问题不能只归因于 %p 的聚合显示。
unpacked union 不保证预期的位映射
原声明中的三个成员分别是 8、16 和 2 位:
typedef union {
bit [7:0] a;
bit [15:0] b;
bit [1:0] c;
} u_t;
成员使用 packed 位向量,并不意味着外层 union 也是 packed。IEEE 1800-2023 第 7.3 节说明,默认 union 是 unpacked,其成员的存储表示没有规定。因此,不能把这里的 A.c 当作有可移植保证的 A.a[1:0]。IEEE 1800-2023,第 7.3 节
下面的赋值会把右侧值转换为目标成员的 8 位宽度:
A.a = 16'd25;
25 能用 8 位表示,所以这里不会丢失有效位,也不能用它解释异常字符。写成 8'd25 会更清楚。
只需要查看不同位宽时,使用位切片
如果要保存一个 16 位值,并分别读取低 8 位和低 2 位,可以直接写成:
module union_ex;
bit [15:0] value;
initial begin
value = 16'd25;
$display("a = %0d", value[7:0]);
$display("b = %0d", value);
$display("c = %0d", value[1:0]);
$display("c binary = %b", value[1:0]);
end
endmodule
按位切片规则,预期结果为:
a = 25
b = 25
c = 1
c binary = 01
这是修改后代码的预期结果,不能据此断言原 unpacked union 中的 c 必须等于 1。
需要保留 union 时,让位布局明确
普通的非 tagged union packed 要求所有成员的总位宽相同,不能只在原声明上添加 packed。可以用 struct packed 补齐位宽,并明确把目标字段放在低位:
module union_packed_ex;
typedef union packed {
bit [15:0] b;
struct packed {
bit [7:0] reserved;
bit [7:0] a;
} a_view;
struct packed {
bit [13:0] reserved;
bit [1:0] c;
} c_view;
} u_t;
u_t A;
initial begin
A.b = '0;
A.a_view.a = 8'd25;
$display("A.a = %0d", A.a_view.a);
$display("A.b = %0d", A.b);
$display("A.c = %0d", A.c_view.c);
end
endmodule
这里三个 union 成员都是 16 位,a 和 c 的低位位置也已明确。先清零整个存储,再写入 a,可以让随后读取的完整 16 位值确定。成员等宽要求见 IEEE 1800-2023,第 7.3.1 节。
如何进一步定位仿真器问题?
保留原始最小示例,记录仿真器名称、版本、编译参数和完整输出,再做以下检查:
- 单独声明一个
bit [1:0]变量并赋值为2'b01,用%0d、%b、%h、%o显示,检查普通变量是否正常。 - 在原 union 示例中直接打印各成员,避免只依赖聚合输出。
- 使用另一款支持相应语法的仿真器对照;如果异常只在特定工具中出现,将最小示例提交给厂商确认。
%p 用于显示聚合值,%0p 是更紧凑、由实现决定的形式。标准规定 union 的 %p 输出只打印第一个声明的成员,因此日志中同时列出三个成员的形式不能当作通用格式。IEEE 1800-2023,第 21.2.1.6 节
在厂商说明或进一步验证之前,不应把 I 解释为 Infinity、Invalid、未知态或高阻态,也不应把更换显示进制视为已经修复了问题。
备注:内容仅供参考。
