diff --git a/Changes b/Changes index 81e98b2a6..7e20b5412 100644 --- a/Changes +++ b/Changes @@ -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.] diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index d377d6236..b14612600 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -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 diff --git a/src/V3Error.h b/src/V3Error.h index d4d7a6656..307de72e8 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -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 diff --git a/src/V3FileLine.h b/src/V3FileLine.h index ead89fd63..e7e0cbb8e 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -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 { diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index c1128b796..77769d5a7 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -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}); } diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 7856c04c4..09933f352 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -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; } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 8dc92b97d..f35bcebb0 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -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(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 } } diff --git a/test_regress/t/t_enum_bad_value.out b/test_regress/t/t_enum_bad_value.out index d9920ebaa..a185699b5 100644 --- a/test_regress/t/t_enum_bad_value.out +++ b/test_regress/t/t_enum_bad_value.out @@ -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 diff --git a/test_regress/t/t_enum_bad_value.v b/test_regress/t/t_enum_bad_value.v index ed97f82a0..3834b382d 100644 --- a/test_regress/t/t_enum_bad_value.v +++ b/test_regress/t/t_enum_bad_value.v @@ -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