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:**
* Add ENUMITEMWIDTH error, and apply to X-extended and ranged values.
* Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang]
* Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.]
* Support randomize() on class member selects (#6161). [Igor Zaworski, Antmicro Ltd.]

View File

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

View File

@ -369,6 +369,10 @@ public:
&& m_lastLineno == rhs.m_lastLineno && m_lastColumn == rhs.m_lastColumn
&& 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
// equivalent.
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 right = nodep->rangep()->rightConst();
const int increment = (left > right) ? -1 : 1;
int offset_from_init = 0;
uint32_t offset_from_init = 0;
AstEnumItem* addp = nullptr;
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);
AstNodeExpr* valuep = nullptr;
if (nodep->valuep()) {
// V3Width looks for Adds with same fileline as the EnumItem
valuep
= 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});
}

View File

@ -1188,7 +1188,7 @@ uint32_t V3Number::countOnes() const {
uint32_t V3Number::mostSetBitP1() const {
for (int bit = width() - 1; bit >= 0; bit--) {
if (bitIs1(bit)) return bit + 1;
if (!bitIs0(bit)) return bit + 1;
}
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
userIterateAndNext(nodep->valuep(), WidthVP{CONTEXT_DET, PRELIM}.p());
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()) {
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
}
}

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)
: ... note: In instance 't'
10 | VALUE_BAD = 8
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%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'
12 | typedef enum [2:0] { VALUE_BAD1 = 8 } enum_t;
| ^
... 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

View File

@ -6,8 +6,14 @@
module t();
typedef enum [2:0] {
VALUE_BAD = 8
} enum_t;
enum bit signed [3:0] {OK1 = -1} ok1_t; // As is signed, loss of 1 bits is ok per IEEE
enum bit signed [3:0] {OK2 = 3} ok2_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