parent
d3f608058f
commit
a05cbd8382
1
Changes
1
Changes
|
|
@ -18,6 +18,7 @@ Verilator 5.045 devel
|
||||||
* Add parsing of solve-before inside foreach (#6934). [Pawel Kojma, Antmicro Ltd.]
|
* 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 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]
|
* 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 vpi_put/vpi_get forcing of signals (#5933) (#6704). [Christian Hecken]
|
||||||
* Support detailed failure info for constraint violations (#6617) (#6883). [Yilou Wang]
|
* Support detailed failure info for constraint violations (#6617) (#6883). [Yilou Wang]
|
||||||
* Support `unique` constraints (on 1D static arrays) (#6810) (#6878). [Srinivasan Venkataramanan]
|
* Support `unique` constraints (on 1D static arrays) (#6810) (#6878). [Srinivasan Venkataramanan]
|
||||||
|
|
|
||||||
|
|
@ -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
|
// If an ARRAYINIT we initialize it using an initial block similar to a signal
|
||||||
// puts("// parameter "+varp->nameProtect()+" = "+varp->valuep()->name()+"\n");
|
// puts("// parameter "+varp->nameProtect()+" = "+varp->valuep()->name()+"\n");
|
||||||
} else if (const AstInitArray* const initarp = VN_CAST(varp->valuep(), InitArray)) {
|
} 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 (VN_IS(dtypep, AssocArrayDType)) {
|
||||||
if (initarp->defaultp()) {
|
if (initarp->defaultp()) {
|
||||||
emitSetVarConstant(varNameProtected + ".atDefault()",
|
emitSetVarConstant(varNameProtected + ".atDefault()",
|
||||||
|
|
@ -583,13 +585,14 @@ void EmitCFunc::emitVarReset(AstVar* varp, bool constructing) {
|
||||||
varp->v3fatalSrc("InitArray under non-arrayed var");
|
varp->v3fatalSrc("InitArray under non-arrayed var");
|
||||||
}
|
}
|
||||||
} else {
|
} 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,
|
string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
|
||||||
const string& varNameProtected, AstNodeDType* dtypep,
|
const string& varNameProtected, AstNodeDType* dtypep,
|
||||||
int depth, const string& suffix) {
|
int depth, const string& suffix, const AstNode* valuep) {
|
||||||
dtypep = dtypep->skipRefp();
|
dtypep = dtypep->skipRefp();
|
||||||
AstBasicDType* const basicp = dtypep->basicp();
|
AstBasicDType* const basicp = dtypep->basicp();
|
||||||
// Returns string to do resetting, empty to do nothing (which caller should handle)
|
// 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";
|
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
|
||||||
return pre
|
return pre
|
||||||
+ emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
|
+ 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)) {
|
} else if (AstWildcardArrayDType* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
|
||||||
// Access std::array as C array
|
// Access std::array as C array
|
||||||
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
||||||
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
|
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
|
||||||
return pre
|
return pre
|
||||||
+ emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
|
+ emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
|
||||||
depth + 1, suffix + ".atDefault()" + cvtarray);
|
depth + 1, suffix + ".atDefault()" + cvtarray, nullptr);
|
||||||
} else if (VN_IS(dtypep, CDType)) {
|
} else if (VN_IS(dtypep, CDType)) {
|
||||||
return ""; // Constructor does it
|
return ""; // Constructor does it
|
||||||
} else if (VN_IS(dtypep, ClassRefDType)) {
|
} 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";
|
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
|
||||||
return pre
|
return pre
|
||||||
+ emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
|
+ 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)) {
|
} else if (const AstQueueDType* const adtypep = VN_CAST(dtypep, QueueDType)) {
|
||||||
// Access std::array as C array
|
// Access std::array as C array
|
||||||
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
||||||
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
|
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
|
||||||
return pre
|
return pre
|
||||||
+ emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
|
+ emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
|
||||||
depth + 1, suffix + ".atDefault()" + cvtarray);
|
depth + 1, suffix + ".atDefault()" + cvtarray, nullptr);
|
||||||
} else if (VN_IS(dtypep, SampleQueueDType)) {
|
} else if (VN_IS(dtypep, SampleQueueDType)) {
|
||||||
return "";
|
return "";
|
||||||
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
} 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");
|
+ cvtToStr(adtypep->elementsConst()) + "; ++" + ivar + ") {\n");
|
||||||
const string below
|
const string below
|
||||||
= emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
|
= emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
|
||||||
depth + 1, suffix + "[" + ivar + "]");
|
depth + 1, suffix + "[" + ivar + "]", nullptr);
|
||||||
const string post = "}\n";
|
const string post = "}\n";
|
||||||
return below.empty() ? "" : pre + below + post;
|
return below.empty() ? "" : pre + below + post;
|
||||||
} else if (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, NodeUOrStructDType)->packed()) {
|
} 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;
|
string literal;
|
||||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||||
const std::string line = emitVarResetRecurse(
|
const std::string line = emitVarResetRecurse(
|
||||||
varp, constructing, varNameProtected + suffix + "." + itemp->nameProtect(),
|
varp, constructing, varNameProtected + suffix + "." + itemp->nameProtect(),
|
||||||
itemp->dtypep(), depth + 1, "");
|
itemp->dtypep(), depth + 1, "", itemp->valuep());
|
||||||
if (!line.empty()) literal += line;
|
if (!line.empty()) literal += line;
|
||||||
}
|
}
|
||||||
return literal;
|
return literal;
|
||||||
|
|
@ -682,10 +685,10 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
|
||||||
splitSizeInc(1);
|
splitSizeInc(1);
|
||||||
if (dtypep->isWide()) { // Handle unpacked; not basicp->isWide
|
if (dtypep->isWide()) { // Handle unpacked; not basicp->isWide
|
||||||
string out;
|
string out;
|
||||||
if (varp->valuep()) {
|
if (valuep) {
|
||||||
const AstConst* const constp = VN_AS(varp->valuep(), Const);
|
const AstConst* const constp = VN_AS(valuep, Const);
|
||||||
UASSERT_OBJ(constp, varp, "non-const initializer for variable");
|
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 += varNameProtected + suffix + "[" + cvtToStr(w) + "] = ";
|
||||||
out += cvtToStr(constp->num().edataWord(w)) + "U;\n";
|
out += cvtToStr(constp->num().edataWord(w)) + "U;\n";
|
||||||
}
|
}
|
||||||
|
|
@ -709,7 +712,15 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
|
||||||
return out;
|
return out;
|
||||||
} else {
|
} else {
|
||||||
string out = varNameProtected + suffix;
|
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";
|
out += " = 0;\n";
|
||||||
} else {
|
} else {
|
||||||
emitVarResetScopeHash();
|
emitVarResetScopeHash();
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ public:
|
||||||
void emitVarReset(AstVar* varp, bool constructing);
|
void emitVarReset(AstVar* varp, bool constructing);
|
||||||
string emitVarResetRecurse(const AstVar* varp, bool constructing,
|
string emitVarResetRecurse(const AstVar* varp, bool constructing,
|
||||||
const string& varNameProtected, AstNodeDType* dtypep, int depth,
|
const string& varNameProtected, AstNodeDType* dtypep, int depth,
|
||||||
const string& suffix);
|
const string& suffix, const AstNode* valuep);
|
||||||
void emitVarResetScopeHash();
|
void emitVarResetScopeHash();
|
||||||
void emitChangeDet();
|
void emitChangeDet();
|
||||||
void emitConstInit(AstNode* initp) { iterateConst(initp); }
|
void emitConstInit(AstNode* initp) { iterateConst(initp); }
|
||||||
|
|
|
||||||
|
|
@ -3270,10 +3270,6 @@ class WidthVisitor final : public VNVisitor {
|
||||||
itemp->v3error("Initial values not allowed in packed struct/union"
|
itemp->v3error("Initial values not allowed in packed struct/union"
|
||||||
" (IEEE 1800-2023 7.2.2)");
|
" (IEEE 1800-2023 7.2.2)");
|
||||||
pushDeletep(itemp->valuep()->unlinkFrBack());
|
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
|
const bool isHardPackedUnion
|
||||||
|
|
@ -5146,6 +5142,11 @@ class WidthVisitor final : public VNVisitor {
|
||||||
// default_value for any unmatched member yet
|
// default_value for any unmatched member yet
|
||||||
return defaultp->cloneTree(false);
|
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)) {
|
if (!VN_IS(memp_vdtypep, UnionDType)) {
|
||||||
nodep->v3error("Assignment pattern missed initializing elements: "
|
nodep->v3error("Assignment pattern missed initializing elements: "
|
||||||
<< memp->virtRefDTypep()->prettyDTypeNameQ() << " "
|
<< memp->virtRefDTypep()->prettyDTypeNameQ() << " "
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -4,13 +4,15 @@
|
||||||
# This program is free software; you can redistribute it and/or modify it
|
# 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
|
# under the terms of either the GNU Lesser General Public License Version 3
|
||||||
# or the Perl Artistic License Version 2.0.
|
# 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
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
import vltest_bootstrap
|
import vltest_bootstrap
|
||||||
|
|
||||||
test.scenarios('linter')
|
test.scenarios('simulator')
|
||||||
|
|
||||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
test.compile()
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
test.passes()
|
test.passes()
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,40 @@
|
||||||
// DESCRIPTION: Verilator: Verilog Test module
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
//
|
//
|
||||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
// 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
|
// 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;
|
module t;
|
||||||
|
|
||||||
parameter P = 4'h5;
|
parameter P = 4'h5;
|
||||||
|
|
||||||
struct { // Can't legally be packed
|
typedef struct { // Must be unpacked -- existing error check
|
||||||
bit [3:0] m_lo = P;
|
// Update ctor_var_reset to check instead of making a constructor
|
||||||
bit [3:0] m_hi;
|
bit [3:0] m_lo = P;
|
||||||
} s;
|
bit [93:0] m_mid = '1; // Wide
|
||||||
|
bit [3:0] m_hi;
|
||||||
|
} s_t;
|
||||||
|
s_t s;
|
||||||
|
|
||||||
initial begin
|
initial begin
|
||||||
s.m_hi = 4'ha;
|
$display("%p", s);
|
||||||
if (s.m_lo != 4'h5) $stop;
|
`checkh(s.m_lo, 4'h5);
|
||||||
if (s.m_hi != 4'ha) $stop;
|
`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");
|
$write("*-* All Finished *-*\n");
|
||||||
$finish;
|
$finish;
|
||||||
end
|
end
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -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
|
||||||
Loading…
Reference in New Issue