PAYATHON 2026

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 节。

如何进一步定位仿真器问题?

保留原始最小示例,记录仿真器名称、版本、编译参数和完整输出,再做以下检查:

  1. 单独声明一个 bit [1:0] 变量并赋值为 2'b01,用 %0d、%b、%h、%o 显示,检查普通变量是否正常。
  2. 在原 union 示例中直接打印各成员,避免只依赖聚合输出。
  3. 使用另一款支持相应语法的仿真器对照;如果异常只在特定工具中出现,将最小示例提交给厂商确认。

%p 用于显示聚合值,%0p 是更紧凑、由实现决定的形式。标准规定 union 的 %p 输出只打印第一个声明的成员,因此日志中同时列出三个成员的形式不能当作通用格式。IEEE 1800-2023,第 21.2.1.6 节

在厂商说明或进一步验证之前,不应把 I 解释为 Infinity、Invalid、未知态或高阻态,也不应把更换显示进制视为已经修复了问题。

备注:内容仅供参考。