Add ENUMITEMWIDTH error, and apply to X-extended and ranged values.

This commit is contained in:
Wilson Snyder 2025-07-12 14:14:17 -04:00
parent cefe1845df
commit 2f199f20cf
9 changed files with 77 additions and 20 deletions

View File

@ -13,6 +13,7 @@ Verilator 5.039 devel
**Other:** **Other:**
* Add ENUMITEMWIDTH error, and apply to X-extended and ranged values.
* Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang]
* Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.]
* Support randomize() on class member selects (#6161). [Igor Zaworski, Antmicro Ltd.] * Support randomize() on class member selects (#6161). [Igor Zaworski, Antmicro Ltd.]

View File

@ -709,6 +709,28 @@ List Of Warnings
missing." missing."
.. option:: ENUMITEMWIDTH
An error that an enum item value is being assigned from a value which
would be truncated (similar to :option:`WIDTHTRUNC`), or from a sized
literal constant with a different bit width (similar to
:option:`WIDTHTRUNC` or :option:`WIDTHEXPAND`). IEEE requires this
error, but it may be disabled.
Faulty example:
.. code-block:: sv
:linenos:
:emphasize-lines: 2
typedef enum [3:0] {
WRONG_WIDTH = 33'h3 //<--- Warning
} enum_t;
To repair, correct the size of the item's value directly, or use a cast,
so the resulting width matches the enum's width.
.. option:: ENUMVALUE .. option:: ENUMVALUE
An error that an enum data type value is being assigned from another data An error that an enum data type value is being assigned from another data

View File

@ -99,6 +99,7 @@ public:
DEPRECATED, // Feature will be deprecated DEPRECATED, // Feature will be deprecated
ENCAPSULATED, // Error: local/protected violation ENCAPSULATED, // Error: local/protected violation
ENDLABEL, // End lable name mismatch ENDLABEL, // End lable name mismatch
ENUMITEMWIDTH, // Error: enum item width mismatch
ENUMVALUE, // Error: enum type needs explicit cast ENUMVALUE, // Error: enum type needs explicit cast
EOFNEWLINE, // End-of-file missing newline EOFNEWLINE, // End-of-file missing newline
GENCLK, // Generated Clock. Historical, never issued. GENCLK, // Generated Clock. Historical, never issued.
@ -204,8 +205,8 @@ public:
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA", "CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA",
"CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "COVERIGN", "CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "COVERIGN",
"DECLFILENAME", "DEFOVERRIDE", "DEFPARAM", "DEPRECATED", "DECLFILENAME", "DEFOVERRIDE", "DEFPARAM", "DEPRECATED",
"ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "ENCAPSULATED", "ENDLABEL", "ENUMITEMWIDTH", "ENUMVALUE", "EOFNEWLINE",
"GENUNNAMED", "HIERBLOCK", "GENCLK", "GENUNNAMED", "HIERBLOCK",
"IFDEPTH", "IGNOREDRETURN", "IFDEPTH", "IGNOREDRETURN",
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", "IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
@ -219,7 +220,8 @@ public:
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",
"UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDLOOP" ,"UNUSEDPARAM", "UNUSEDSIGNAL", "UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDLOOP" ,"UNUSEDPARAM", "UNUSEDSIGNAL",
"USERERROR", "USERFATAL", "USERINFO", "USERWARN", "USERERROR", "USERFATAL", "USERINFO", "USERWARN",
"VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND",
"ZERODLY", "ZEROREPL",
" MAX" " MAX"
}; };
// clang-format on // clang-format on
@ -249,9 +251,9 @@ public:
bool pretendError() const VL_MT_SAFE { bool pretendError() const VL_MT_SAFE {
return (m_e == ASSIGNIN || m_e == BADSTDPRAGMA || m_e == BADVLTPRAGMA || m_e == BLKANDNBLK return (m_e == ASSIGNIN || m_e == BADSTDPRAGMA || m_e == BADVLTPRAGMA || m_e == BLKANDNBLK
|| m_e == BLKLOOPINIT || m_e == CONTASSREG || m_e == ENCAPSULATED || m_e == BLKLOOPINIT || m_e == CONTASSREG || m_e == ENCAPSULATED
|| m_e == ENDLABEL || m_e == ENUMVALUE || m_e == IMPURE || m_e == MODMISSING || m_e == ENDLABEL || m_e == ENUMITEMWIDTH || m_e == ENUMVALUE || m_e == IMPURE
|| m_e == PINNOTFOUND || m_e == PKGNODECL || m_e == PROCASSWIRE || m_e == MODMISSING || m_e == PINNOTFOUND || m_e == PKGNODECL
|| m_e == ZEROREPL // Says IEEE || m_e == PROCASSWIRE || m_e == ZEROREPL // Says IEEE
); );
} }
// Warnings to mention manual // Warnings to mention manual

View File

@ -369,6 +369,10 @@ public:
&& m_lastLineno == rhs.m_lastLineno && m_lastColumn == rhs.m_lastColumn && m_lastLineno == rhs.m_lastLineno && m_lastColumn == rhs.m_lastColumn
&& m_filenameno == rhs.m_filenameno && m_msgEnIdx == rhs.m_msgEnIdx); && m_filenameno == rhs.m_filenameno && m_msgEnIdx == rhs.m_msgEnIdx);
} }
bool equalFirstLineCol(const FileLine& rhs) const {
return (m_filenameno == rhs.m_filenameno && m_firstLineno == rhs.m_firstLineno
&& m_firstColumn == rhs.m_firstColumn);
}
// Returns -1 if (*this) should come before rhs after sorted. 1 for the opposite case. 0 for // Returns -1 if (*this) should come before rhs after sorted. 1 for the opposite case. 0 for
// equivalent. // equivalent.
int operatorCompare(const FileLine& rhs) const { int operatorCompare(const FileLine& rhs) const {

View File

@ -267,16 +267,17 @@ class LinkParseVisitor final : public VNVisitor {
const int left = nodep->rangep()->leftConst(); const int left = nodep->rangep()->leftConst();
const int right = nodep->rangep()->rightConst(); const int right = nodep->rangep()->rightConst();
const int increment = (left > right) ? -1 : 1; const int increment = (left > right) ? -1 : 1;
int offset_from_init = 0; uint32_t offset_from_init = 0;
AstEnumItem* addp = nullptr; AstEnumItem* addp = nullptr;
FileLine* const flp = nodep->fileline(); FileLine* const flp = nodep->fileline();
for (int i = left; i != (right + increment); i += increment, offset_from_init++) { for (int i = left; i != (right + increment); i += increment, ++offset_from_init) {
const string name = nodep->name() + cvtToStr(i); const string name = nodep->name() + cvtToStr(i);
AstNodeExpr* valuep = nullptr; AstNodeExpr* valuep = nullptr;
if (nodep->valuep()) { if (nodep->valuep()) {
// V3Width looks for Adds with same fileline as the EnumItem
valuep valuep
= new AstAdd{flp, nodep->valuep()->cloneTree(true), = new AstAdd{flp, nodep->valuep()->cloneTree(true),
new AstConst(flp, AstConst::Unsized32{}, offset_from_init)}; new AstConst{flp, AstConst::Unsized32{}, offset_from_init}};
} }
addp = AstNode::addNext(addp, new AstEnumItem{flp, name, nullptr, valuep}); addp = AstNode::addNext(addp, new AstEnumItem{flp, name, nullptr, valuep});
} }

View File

@ -1188,7 +1188,7 @@ uint32_t V3Number::countOnes() const {
uint32_t V3Number::mostSetBitP1() const { uint32_t V3Number::mostSetBitP1() const {
for (int bit = width() - 1; bit >= 0; bit--) { for (int bit = width() - 1; bit >= 0; bit--) {
if (bitIs1(bit)) return bit + 1; if (!bitIs0(bit)) return bit + 1;
} }
return 0; return 0;
} }

View File

@ -2553,9 +2553,16 @@ class WidthVisitor final : public VNVisitor {
// Default type is int, but common to assign narrower values, so minwidth from value // Default type is int, but common to assign narrower values, so minwidth from value
userIterateAndNext(nodep->valuep(), WidthVP{CONTEXT_DET, PRELIM}.p()); userIterateAndNext(nodep->valuep(), WidthVP{CONTEXT_DET, PRELIM}.p());
bool warnOn = true; bool warnOn = true;
if (const AstConst* const constp = VN_CAST(nodep->valuep(), Const)) { AstNodeExpr* valuep = nodep->valuep();
if (const AstAdd* const anodep = VN_CAST(valuep, Add)) {
// If constructed by V3LinkParse due to "enumitem[N_REPEATS] value"
if (anodep->fileline()->equalFirstLineCol(*(nodep->fileline())))
valuep = anodep->lhsp();
}
if (const AstConst* const constp = VN_CAST(valuep, Const)) {
if (static_cast<int>(constp->num().mostSetBitP1()) > nodep->width()) { if (static_cast<int>(constp->num().mostSetBitP1()) > nodep->width()) {
constp->v3error("Enum value exceeds width of enum type (IEEE 1800-2023 6.19)"); constp->v3warn(ENUMITEMWIDTH,
"Enum value exceeds width of enum type (IEEE 1800-2023 6.19)");
warnOn = false; // Prevent normal WIDTHTRUNC warnOn = false; // Prevent normal WIDTHTRUNC
} }
} }

View File

@ -1,6 +1,20 @@
%Error: t/t_enum_bad_value.v:10:19: Enum value exceeds width of enum type (IEEE 1800-2023 6.19) %Error-ENUMITEMWIDTH: t/t_enum_bad_value.v:12:37: Enum value exceeds width of enum type (IEEE 1800-2023 6.19)
: ... note: In instance 't' : ... note: In instance 't'
10 | VALUE_BAD = 8 12 | typedef enum [2:0] { VALUE_BAD1 = 8 } enum_t;
| ^ | ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. ... For error description see https://verilator.org/warn/ENUMITEMWIDTH?v=latest
%Error-ENUMITEMWIDTH: t/t_enum_bad_value.v:14:29: Enum value exceeds width of enum type (IEEE 1800-2023 6.19)
: ... note: In instance 't'
14 | enum bit [4:0] {BAD2[4] = 100} bad2;
| ^~~
%Warning-WIDTHTRUNC: t/t_enum_bad_value.v:14:19: Operator ADD expects 5 bits on the LHS, but LHS's CONST '?32?sh64' generates 32 or 7 bits.
: ... note: In instance 't'
14 | enum bit [4:0] {BAD2[4] = 100} bad2;
| ^~~~
... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest
... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message.
%Error-ENUMITEMWIDTH: t/t_enum_bad_value.v:16:28: Enum value exceeds width of enum type (IEEE 1800-2023 6.19)
: ... note: In instance 't'
16 | enum logic [3:0] {BAD3 = 5'bxxxxx} bad3;
| ^~~~~~~~
%Error: Exiting due to %Error: Exiting due to

View File

@ -6,8 +6,14 @@
module t(); module t();
typedef enum [2:0] { enum bit signed [3:0] {OK1 = -1} ok1_t; // As is signed, loss of 1 bits is ok per IEEE
VALUE_BAD = 8 enum bit signed [3:0] {OK2 = 3} ok2_t;
} enum_t;
typedef enum [2:0] { VALUE_BAD1 = 8 } enum_t;
enum bit [4:0] {BAD2[4] = 100} bad2;
enum logic [3:0] {BAD3 = 5'bxxxxx} bad3;
initial $stop;
endmodule endmodule