Support structure initial values (#6130).

Fixes #6130.
This commit is contained in:
Wilson Snyder 2026-02-01 13:44:20 -05:00
parent d3f608058f
commit a05cbd8382
9 changed files with 120 additions and 40 deletions

View File

@ -18,6 +18,7 @@ Verilator 5.045 devel
* Add parsing of solve-before inside foreach (#6934). [Pawel Kojma, Antmicro Ltd.]
* Add error when accessing a non-static class field from a static function (#6948). [Artur Bieniek, Antmicro Ltd.]
* Add VerilatedContext::useNumaAssign and set on threads() call (#6954). [Yangyu Chen]
* Support structure initial values (#6130).
* Support vpi_put/vpi_get forcing of signals (#5933) (#6704). [Christian Hecken]
* Support detailed failure info for constraint violations (#6617) (#6883). [Yilou Wang]
* Support `unique` constraints (on 1D static arrays) (#6810) (#6878). [Srinivasan Venkataramanan]

View File

@ -542,6 +542,8 @@ void EmitCFunc::emitVarReset(AstVar* varp, bool constructing) {
// If an ARRAYINIT we initialize it using an initial block similar to a signal
// puts("// parameter "+varp->nameProtect()+" = "+varp->valuep()->name()+"\n");
} else if (const AstInitArray* const initarp = VN_CAST(varp->valuep(), InitArray)) {
// TODO this code probably better handled as initp argument to emitVarResetRecurse
// TODO merge this functionality with V3EmitCConstInit.h visitors
if (VN_IS(dtypep, AssocArrayDType)) {
if (initarp->defaultp()) {
emitSetVarConstant(varNameProtected + ".atDefault()",
@ -583,13 +585,14 @@ void EmitCFunc::emitVarReset(AstVar* varp, bool constructing) {
varp->v3fatalSrc("InitArray under non-arrayed var");
}
} else {
putns(varp, emitVarResetRecurse(varp, constructing, varNameProtected, dtypep, 0, ""));
putns(varp, emitVarResetRecurse(varp, constructing, varNameProtected, dtypep, 0, "",
varp->valuep()));
}
}
string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
const string& varNameProtected, AstNodeDType* dtypep,
int depth, const string& suffix) {
int depth, const string& suffix, const AstNode* valuep) {
dtypep = dtypep->skipRefp();
AstBasicDType* const basicp = dtypep->basicp();
// Returns string to do resetting, empty to do nothing (which caller should handle)
@ -599,14 +602,14 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
return pre
+ emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
depth + 1, suffix + ".atDefault()" + cvtarray);
depth + 1, suffix + ".atDefault()" + cvtarray, nullptr);
} else if (AstWildcardArrayDType* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
// Access std::array as C array
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
return pre
+ emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
depth + 1, suffix + ".atDefault()" + cvtarray);
depth + 1, suffix + ".atDefault()" + cvtarray, nullptr);
} else if (VN_IS(dtypep, CDType)) {
return ""; // Constructor does it
} else if (VN_IS(dtypep, ClassRefDType)) {
@ -619,14 +622,14 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
return pre
+ emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
depth + 1, suffix + ".atDefault()" + cvtarray);
depth + 1, suffix + ".atDefault()" + cvtarray, nullptr);
} else if (const AstQueueDType* const adtypep = VN_CAST(dtypep, QueueDType)) {
// Access std::array as C array
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
return pre
+ emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
depth + 1, suffix + ".atDefault()" + cvtarray);
depth + 1, suffix + ".atDefault()" + cvtarray, nullptr);
} else if (VN_IS(dtypep, SampleQueueDType)) {
return "";
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
@ -637,17 +640,17 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
+ cvtToStr(adtypep->elementsConst()) + "; ++" + ivar + ") {\n");
const string below
= emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
depth + 1, suffix + "[" + ivar + "]");
depth + 1, suffix + "[" + ivar + "]", nullptr);
const string post = "}\n";
return below.empty() ? "" : pre + below + post;
} else if (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, NodeUOrStructDType)->packed()) {
const auto* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
const AstNodeUOrStructDType* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
string literal;
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
const std::string line = emitVarResetRecurse(
varp, constructing, varNameProtected + suffix + "." + itemp->nameProtect(),
itemp->dtypep(), depth + 1, "");
itemp->dtypep(), depth + 1, "", itemp->valuep());
if (!line.empty()) literal += line;
}
return literal;
@ -682,10 +685,10 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
splitSizeInc(1);
if (dtypep->isWide()) { // Handle unpacked; not basicp->isWide
string out;
if (varp->valuep()) {
const AstConst* const constp = VN_AS(varp->valuep(), Const);
if (valuep) {
const AstConst* const constp = VN_AS(valuep, Const);
UASSERT_OBJ(constp, varp, "non-const initializer for variable");
for (int w = 0; w < varp->widthWords(); ++w) {
for (int w = 0; w < dtypep->widthWords(); ++w) {
out += varNameProtected + suffix + "[" + cvtToStr(w) + "] = ";
out += cvtToStr(constp->num().edataWord(w)) + "U;\n";
}
@ -709,7 +712,15 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
return out;
} else {
string out = varNameProtected + suffix;
if (zeroit) {
if (valuep) {
out += " = ";
// TODO cleanup code shared between here, V3EmitCConstInit.h,
// EmitCFunc::emitVarReset, EmitCFunc::emitConstant
const AstConst* const constp = VN_AS(valuep, Const);
UASSERT_OBJ(constp, varp, "non-const initializer for variable");
out += cvtToStr(constp->num().edataWord(0)) + "U;\n";
out += ";\n";
} else if (zeroit) {
out += " = 0;\n";
} else {
emitVarResetScopeHash();

View File

@ -215,7 +215,7 @@ public:
void emitVarReset(AstVar* varp, bool constructing);
string emitVarResetRecurse(const AstVar* varp, bool constructing,
const string& varNameProtected, AstNodeDType* dtypep, int depth,
const string& suffix);
const string& suffix, const AstNode* valuep);
void emitVarResetScopeHash();
void emitChangeDet();
void emitConstInit(AstNode* initp) { iterateConst(initp); }

View File

@ -3270,10 +3270,6 @@ class WidthVisitor final : public VNVisitor {
itemp->v3error("Initial values not allowed in packed struct/union"
" (IEEE 1800-2023 7.2.2)");
pushDeletep(itemp->valuep()->unlinkFrBack());
} else if (itemp->valuep()) {
itemp->valuep()->v3warn(E_UNSUPPORTED,
"Unsupported: Initial values in struct/union members");
pushDeletep(itemp->valuep()->unlinkFrBack());
}
}
const bool isHardPackedUnion
@ -5146,6 +5142,11 @@ class WidthVisitor final : public VNVisitor {
// default_value for any unmatched member yet
return defaultp->cloneTree(false);
}
if (memp->valuep()) {
return new AstPatMember{nodep->fileline(),
VN_AS(memp->valuep()->cloneTree(false), NodeExpr),
new AstText{nodep->fileline(), memp->name()}, nullptr};
}
if (!VN_IS(memp_vdtypep, UnionDType)) {
nodep->v3error("Assignment pattern missed initializing elements: "
<< memp->virtRefDTypep()->prettyDTypeNameQ() << " "

View File

@ -1,6 +0,0 @@
%Error-UNSUPPORTED: t/t_struct_unpacked_init.v:12:24: Unsupported: Initial values in struct/union members
: ... note: In instance 't'
12 | bit [3:0] m_lo = P;
| ^
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -4,13 +4,15 @@
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2024 Wilson Snyder
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('linter')
test.scenarios('simulator')
test.lint(fails=True, expect_filename=test.golden_filename)
test.compile()
test.execute()
test.passes()

View File

@ -1,25 +1,40 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2023 Wilson Snyder
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv, expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t;
parameter P = 4'h5;
parameter P = 4'h5;
struct { // Can't legally be packed
bit [3:0] m_lo = P;
bit [3:0] m_hi;
} s;
typedef struct { // Must be unpacked -- existing error check
// Update ctor_var_reset to check instead of making a constructor
bit [3:0] m_lo = P;
bit [93:0] m_mid = '1; // Wide
bit [3:0] m_hi;
} s_t;
s_t s;
initial begin
s.m_hi = 4'ha;
if (s.m_lo != 4'h5) $stop;
if (s.m_hi != 4'ha) $stop;
initial begin
$display("%p", s);
`checkh(s.m_lo, 4'h5);
`checkh(s.m_mid, ~94'h0);
`checkh(s.m_hi, 4'h0);
s.m_mid = 94'ha;
s.m_hi = 4'hb;
$display("%p", s);
`checkh(s.m_lo, 4'h5);
`checkh(s.m_mid, 94'ha);
`checkh(s.m_hi, 4'hb);
$write("*-* All Finished *-*\n");
$finish;
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,38 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv, expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t;
parameter P = 4'h5;
typedef struct { // Must be unpacked -- existing error check
bit [3:0] m_lo = P;
bit [93:0] m_mid = '1; // Wide
bit [3:0] m_hi;
} s_t;
localparam s_t S = '{m_hi: 6}; // Not setting m_mid/m_hi
localparam S_LO = S.m_lo;
localparam S_HI = S.m_hi;
initial begin
`checkh(S.m_hi, 4'h6);
`checkh(S_HI, 4'h6);
`checkh(S.m_lo, 4'h5);
`checkh(S_LO, 4'h5);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule