diff --git a/include/verilated.cpp b/include/verilated.cpp index db9b32a01..09f3c4d03 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1180,28 +1180,23 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc, if (fmt != 'p' && fmt != 'x') fmt = 's'; // Override } else if (formatAttr == VL_VFORMATATTR_ENUM || formatAttr == VL_VFORMATATTR_ENUM_SIGNED) { + // Always <= VL_QUADSIZE; emit uses non-ENUM format for wider enums const int numericAttr = formatAttr == VL_VFORMATATTR_ENUM_SIGNED ? VL_VFORMATATTR_SIGNED : VL_VFORMATATTR_UNSIGNED; lbits = va_arg(ap, int); - if (lbits <= VL_QUADSIZE) { - ld = VL_VA_ARG_Q_(ap, lbits); - strwide.resize(2); - WDataOutP strwidep = WDataOutP::external(strwide.data()); - VL_SET_WQ(strwidep, ld); - lwp = strwidep; - } else { - lwp = WDataInP::external(va_arg(ap, const EData*)); - ld = VL_SET_QW(lwp); - } + ld = VL_VA_ARG_Q_(ap, lbits); + strwide.resize(2); + WDataOutP strwidep = WDataOutP::external(strwide.data()); + VL_SET_WQ(strwidep, ld); + lwp = strwidep; lsb = lbits - 1; ++argn; // Enum value is followed by the generated name string argument static_cast(va_arg(ap, int)); // VL_VFORMATATTR_STRING enump = va_arg(ap, std::string*); - if (fmt != 'p' && fmt != 's') { - formatAttr = numericAttr; - } else if (enump && !enump->empty()) { + if (enump && !enump->empty()) { formatAttr = (fmt == 'p') ? VL_VFORMATATTR_COMPLEX : VL_VFORMATATTR_STRING; + if (fmt == 'd') formatAttr = numericAttr; thingp = const_cast(enump); } else if (fmt == 'p' && widthSet && width == 0) { output += "'h"; diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 057eb243e..ac931b6c0 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -2404,7 +2404,6 @@ class AstSFormatArg final : public AstNodeExpr { // used to pass to (potentially) runtime decoding of format arguments // PARENT: SFormatF (or next list of expressions) // @astgen op1 := exprp : AstNodeExpr - // @astgen op2 := namep : Optional[AstNodeExpr] // Runtime enum name lookup VFormatAttr m_formatAttr; // How to format expression public: @@ -2427,7 +2426,6 @@ public: bool cleanOut() const override { return true; } const char* broken() const override { BROKEN_RTN(!VN_IS(backp(), SFormatF) && firstAbovep()); // In list under SFormatF - BROKEN_RTN(formatAttr().isEnum() != static_cast(namep())); return nullptr; } VFormatAttr formatAttr() const { return m_formatAttr; } diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index d6423b447..afd7d3f14 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -282,11 +282,6 @@ class CleanVisitor final : public VNVisitor { iterateChildren(nodep); ensureClean(nodep->condp()); } - void visit(AstSFormatArg* nodep) override { - iterateChildren(nodep); - ensureCleanAndNext(nodep->exprp()); - setClean(nodep, true); - } void visit(AstSFormatF* nodep) override { iterateChildren(nodep); ensureCleanAndNext(nodep->exprsp()); diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 9d63f93a7..e800a221f 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -3969,12 +3969,7 @@ class ConstVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); return true; } - void visit(AstSFormatArg* nodep) override { - // Skip namep(): requiring its runtime lookup to be constant can reject valid - // enum-valued constant-function calls in parameters. displayedEnum() resolves - // the name from the folded exprp() value and enum dtype instead. - iterateAndNextNull(nodep->exprp()); - } + void visit(AstSFormatArg* nodep) override { iterateChildren(nodep); } void visit(AstSFormatF* nodep) override { // Substitute constants into displays. The main point of this is to // simplify assertion methodologies which call functions with display's. @@ -4038,9 +4033,7 @@ class ConstVisitor final : public VNVisitor { : VFormatAttr{}; if (VN_IS(subargp, Const)) { // Convert it const string out - = formatAttr.isEnum() - ? constNumV(subargp).displayedEnum(fargp, fmt) - : constNumV(subargp).displayed(nodep, fmt, formatAttr); + = constNumV(subargp).displayed(nodep, fmt, formatAttr); UINFO(9, " DispConst: " << fmt << " -> " << out << " for " << subargp); // fmt = out w/ replace % with %% as it must later when diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index 52cdbfe6c..a203a04bf 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -447,11 +447,6 @@ class DeadVisitor final : public VNVisitor { checkDType(nodep); checkAll(nodep); } - void visit(AstEnumDType* nodep) override { - // Keep cached lookup tables until V3WidthCommit releases the cache. - for (const auto& entry : nodep->tableMap()) entry.second->user1Inc(); - visit(static_cast(nodep)); - } void visit(AstEnumItemRef* nodep) override { iterateChildren(nodep); checkAll(nodep); diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index cf936bf8e..1dfc033db 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -304,11 +304,7 @@ void EmitCFunc::displayNode(AstNode* nodep, AstSFormatF* fmtp, // fmtp is nullp int argc = 0; if (needsScope) ++argc; if (needsTimescale) ++argc; - for (AstNode* argp = exprsp; argp; argp = argp->nextp()) { - ++argc; - const AstSFormatArg* const fargp = VN_CAST(argp, SFormatArg); - if (fargp && fargp->formatAttr().isEnum()) ++argc; // Additional name argument - } + for (AstNode* argp = exprsp; argp; argp = argp->nextp()) ++argc; ofp()->puts("," + std::to_string(argc)); if (needsScope) { @@ -349,9 +345,7 @@ void EmitCFunc::displayNode(AstNode* nodep, AstSFormatF* fmtp, // fmtp is nullp if (formatAttr.isSigned() || formatAttr.isUnsigned() || formatAttr.isEnum()) puts("," + cvtToStr(subargp->widthMin())); const bool addrof = isScan || formatAttr.isString() || formatAttr.isComplex(); - const bool wideEnum = formatAttr.isEnum() && subargp->isWide(); puts(","); - if (wideEnum) puts("static_cast("); if (addrof) puts("&("); if (VN_IS(subargp, StreamR)) emitStreamR( @@ -360,12 +354,6 @@ void EmitCFunc::displayNode(AstNode* nodep, AstSFormatF* fmtp, // fmtp is nullp else { iterateConst(subargp); } if (addrof) puts(")"); if (!addrof) emitDatap(argp); - if (wideEnum) puts(")"); - if (formatAttr.isEnum()) { - puts(", '"s + VFormatAttr{VFormatAttr::STRING}.ascii() + "', &("); - iterateConst(fargp->namep()); - puts(")"); - } ofp()->indentDec(); } diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 7fff42aed..096609363 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -645,41 +645,6 @@ string V3Number::displayPad(size_t fmtsize, char pad, bool left, const string& i return left ? (in + padding) : (padding + in); } -string V3Number::displayedEnumName(const AstEnumItem* itemp) { - const string name = VIdProtect::protect(itemp->prettyName()); - return VString::isIdentifier(name) && !std::isdigit(static_cast(name[0])) - ? name - : "\\" + name + " "; -} - -string V3Number::displayedEnumName(const AstEnumDType* dtypep) const VL_MT_STABLE { - V3Number value{this, dtypep->width()}; - value.opAssign(*this); - for (const AstEnumItem* itemp = dtypep->itemsp(); itemp; - itemp = VN_AS(itemp->nextp(), EnumItem)) { - const AstConst* const constp = VN_AS(itemp->valuep(), Const); - if (!constp->num().isAnyXZ() && value.isCaseEq(constp->num())) - return displayedEnumName(itemp); - } - return ""; -} - -string V3Number::displayedEnum(const AstSFormatArg* argp, - const string& vformat) const VL_MT_STABLE { - const AstEnumDType* const dtypep = VN_AS(argp->dtypep()->skipRefToEnump(), EnumDType); - V3Number value{this, dtypep->width()}; - value.opAssign(*this); - const VFormatAttr numericAttr - = dtypep->isSigned() ? VFormatAttr::SIGNED : VFormatAttr::UNSIGNED; - const char fmt = std::tolower(vformat.back()); - if (fmt != 'p' && fmt != 's') return value.displayed(argp, vformat, numericAttr); - string name = value.displayedEnumName(dtypep); - if (name.empty()) name = value.displayed(argp, fmt == 'p' ? vformat : "%0d", numericAttr); - V3Number nameNum{this}; - nameNum.setString(name); - return nameNum.displayed(argp, vformat, VFormatAttr::COMPLEX); -} - string V3Number::displayed(const AstNode* nodep, const string& vformat, const VFormatAttr& formatAttr) const VL_MT_STABLE { return displayed(nodep->fileline(), vformat, formatAttr); diff --git a/src/V3Number.h b/src/V3Number.h index 23e5308f3..404add9e7 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -30,8 +30,6 @@ #include #include -class AstEnumDType; -class AstEnumItem; class AstNode; class AstNodeDType; class AstSFormatArg; @@ -654,9 +652,6 @@ public: const VFormatAttr& formatAttr = VFormatAttr::UNSIGNED) const VL_MT_STABLE; string displayed(FileLine* fl, const string& vformat, const VFormatAttr& formatAttr = VFormatAttr::UNSIGNED) const VL_MT_STABLE; - string displayedEnum(const AstSFormatArg* argp, const string& vformat) const VL_MT_STABLE; - string displayedEnumName(const AstEnumDType* dtypep) const VL_MT_STABLE; - static string displayedEnumName(const AstEnumItem* itemp); static bool displayedFmtHasArg(char format, bool isScan); string emitC() const VL_MT_STABLE; int width() const VL_MT_SAFE { return m_data.width(); } diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index bec4538b5..82a3b0864 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -415,33 +415,12 @@ class PremitVisitor final : public VNVisitor { // Any strings sent to a display must be var of string data type, // to avoid passing a pointer to a temporary. AstNodeExpr* exprsp = nodep->exprsp(); - if (nodep->exprFormat()) { - exprsp = VN_AS(exprsp->nextp(), NodeExpr); - for (AstNode* argp = exprsp; argp; argp = argp->nextp()) { - const AstSFormatArg* const fargp = VN_CAST(argp, SFormatArg); - if (fargp && fargp->formatAttr().isEnum()) { - // Evaluate the format before materializing enum arguments. - AstVar* const varp = createTemp(nodep->exprsp()); - varp->noSubst(true); - break; - } - } - } + if (nodep->exprFormat()) exprsp = VN_AS(exprsp->nextp(), NodeExpr); for (AstNodeExpr *argp = exprsp, *nextp; argp; argp = nextp) { nextp = VN_AS(argp->nextp(), NodeExpr); AstSFormatArg* const fargp = VN_CAST(argp, SFormatArg); AstNodeExpr* const subargp = fargp ? fargp->exprp() : argp; - if (fargp && fargp->formatAttr().isEnum()) { - // The name lookup must see the same value as the numeric argument. - AstVar* const valueVarp = createTemp(subargp); - valueVarp->noSubst(true); - if (!VN_IS(fargp->namep(), VarRef)) { - AstVar* const nameVarp = createTemp(fargp->namep()); - nameVarp->noSubst(true); - } - continue; - } // Must avoid taking address of rvalue, so even Const needs a temp if (subargp->isString() && !VN_IS(subargp, VarRef)) { AstVar* const varp = createTemp(subargp); diff --git a/src/V3Simulate.h b/src/V3Simulate.h index f15cb174d..d4b7fde4d 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -1261,8 +1261,7 @@ private: void visit(AstSFormatArg* nodep) override { checkNodeInfo(nodep); - // Constant enum names come from the dtype, not the runtime lookup. - iterateAndNextConstNull(nodep->exprp()); + iterateChildrenConst(nodep); } void visit(AstSFormatF* nodep) override { if (jumpingOver()) return; @@ -1306,9 +1305,7 @@ private: break; } const string pformat = "%"s + width + pos[0]; - result += formatAttr.isEnum() - ? constp->num().displayedEnum(fargp, pformat) - : constp->num().displayed(nodep, pformat, formatAttr); + result += constp->num().displayed(nodep, pformat, formatAttr); } else { switch (std::tolower(pos[0])) { case '%': result += "%"; break; diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 79c680d3b..113c6e4d2 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -221,7 +221,6 @@ class WidthVisitor final : public VNVisitor { using DTypeMap = std::map; // STATE - V3UniqueNames m_enumTempNames; // Captured enum expressions V3UniqueNames m_insideTempNames; // For generating unique temporary variable names for // `inside` expressions VMemberMap m_memberMap; // Member names cached for fast lookup @@ -6728,10 +6727,6 @@ class WidthVisitor final : public VNVisitor { while (AstNodeExpr* argp = oldExprsp) { oldExprsp = VN_AS(oldExprsp->nextp(), NodeExpr); if (oldExprsp) oldExprsp->unlinkFrBackWithNext(); - if (VN_IS(argp, SFormatArg)) { - nodep->addExprsp(argp); - continue; - } // Need to record formatAttr's at elaboration time, as later optimizations // may change an argument's data type. Plus need them for runtime formats VFormatAttr formatAttr = VFormatAttr::UNSIGNED; @@ -6764,14 +6759,21 @@ class WidthVisitor final : public VNVisitor { argp = newp; } else if (nodep->exprFormat()) { if (AstEnumDType* const enumDtp = formatEnumDType(argp)) { - nodep->addExprsp(newFormatEnumArg(argp, enumDtp)); + const VFormatAttr attr + = enumDtp->isSigned() ? VFormatAttr::ENUM_SIGNED : VFormatAttr::ENUM; + nodep->addExprsp(new AstSFormatArg{argp->fileline(), attr, argp}); + AstNodeExpr* const namep + = enumSelect(argp->cloneTreePure(false), enumDtp, VAttrType::ENUM_NAME); + nodep->addExprsp( + new AstSFormatArg{namep->fileline(), VFormatAttr::STRING, namep}); continue; } } if (formatAttr.isUnsigned() && dtypep->isSigned()) { formatAttr = VFormatAttr::SIGNED; } - if (formatAttr.isUnsigned()) { // Save Ast space and imply the AstSFormatArg + if (VN_IS(argp, SFormatArg) // Already done + || formatAttr.isUnsigned()) { // Save Ast space and imply the AstSFormatArg nodep->addExprsp(argp); } else { nodep->addExprsp(new AstSFormatArg{argp->fileline(), formatAttr, argp}); @@ -8014,7 +8016,6 @@ class WidthVisitor final : public VNVisitor { } void visit(AstNodeModule* nodep) override { assertAtStatement(nodep); - VL_RESTORER_COPY(m_enumTempNames); VL_RESTORER_COPY(m_insideTempNames); if (AstClass* const classp = VN_CAST(nodep, Class)) { visitClass(classp); @@ -8699,13 +8700,16 @@ class WidthVisitor final : public VNVisitor { // For sformatf's with constant format, iterate/check arguments UASSERT_OBJ(!nodep->exprFormat(), nodep, "Assumes constant format"); bool inPct = false; + string fmtMods; AstNodeExpr* argp = nodep->exprsp(); string newFormat; for (char ch : nodep->text()) { if (!inPct && ch == '%') { inPct = true; + fmtMods = ""; newFormat += ch; } else if (inPct && (std::isdigit(ch) || ch == '.' || ch == '-')) { + fmtMods += ch; newFormat += ch; } else if (!inPct) { // Normal text newFormat += ch; @@ -8772,11 +8776,33 @@ class WidthVisitor final : public VNVisitor { case 'p': // FALLTHRU case 's': // As with enum.name(): valid values print the mnemonic, else numeric - if (subargp && !fargp) { + if (subargp) { if (AstEnumDType* const enumDtp = formatEnumDType(subargp)) { - VNRelinker relinker; - subargp->unlinkFrBack(&relinker); - relinker.relink(newFormatEnumArg(subargp, enumDtp)); + string fallbackFormat = "%0d"; + if (ch == 'p') { + bool widthSet = false; + size_t width = 0; + for (const char mod : fmtMods) { + if (!std::isdigit(mod)) continue; + widthSet = true; + width = width * 10 + (mod - '0'); + } + if (widthSet && width == 0) fallbackFormat = "'h%0h"; + } + AstNodeExpr* fallbackp = subargp->cloneTreePure(false); + if (enumDtp->isSigned()) { + fallbackp = new AstSFormatArg{subargp->fileline(), + VFormatAttr::SIGNED, fallbackp}; + } + AstNodeExpr* const newp + = new AstCond{subargp->fileline(), enumTestValid(subargp, enumDtp), + enumSelect(subargp->cloneTreePure(false), enumDtp, + VAttrType::ENUM_NAME), + new AstSFormatF{subargp->fileline(), fallbackFormat, + true, fallbackp}}; + subargp->replaceWith(new AstSFormatArg{subargp->fileline(), + VFormatAttr::COMPLEX, newp}); + VL_DO_DANGLING(pushDeletep(subargp), subargp); } } argp = nextp; @@ -8798,47 +8824,11 @@ class WidthVisitor final : public VNVisitor { enumDtp = VN_CAST(varrefp->varp()->dtypep()->skipRefToEnump(), EnumDType); } } + // Enums > 64 bits have no name table (see enumMaxValue); format as plain numbers + if (enumDtp && enumDtp->width() > VL_QUADSIZE) return nullptr; return enumDtp; } - AstExprStmt* newEnumCapture(AstNodeExpr* valuep, AstEnumDType* dtypep) { - FileLine* const flp = valuep->fileline(); - AstVar* const varp = new AstVar{flp, VVarType::XTEMP, m_enumTempNames.get(valuep), dtypep}; - varp->noSubst(true); - if (m_ftaskp) { - varp->funcLocal(true); - varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); - m_ftaskp->addStmtsp(varp); - } else { - UASSERT_OBJ(m_modep, valuep, "Enum expression without a containing module"); - m_modep->addStmtsp(varp); - } - iterate(varp); - AstExprStmt* const exprp = new AstExprStmt{ - flp, new AstAssign{flp, new AstVarRef{flp, varp, VAccess::WRITE}, valuep}, - new AstVarRef{flp, varp, VAccess::READ}}; - iterate(exprp); - return exprp; - } - - AstSFormatArg* newFormatEnumArg(AstNodeExpr* valuep, AstEnumDType* dtypep) { - if (valuep->isPure()) valuep = V3Const::constifyEdit(valuep); - AstNodeExpr* keyp; - if (VN_IS(valuep, Const) || VN_IS(valuep, VarRef) || (!m_modep && !m_ftaskp)) { - // Standalone parameter expressions are checked for constness by V3Const. - keyp = valuep->cloneTree(false); - } else { - AstExprStmt* const capturep = newEnumCapture(valuep, dtypep); - valuep = capturep; - keyp = capturep->resultp()->cloneTreePure(false); - } - const VFormatAttr attr = dtypep->isSigned() ? VFormatAttr::ENUM_SIGNED : VFormatAttr::ENUM; - AstSFormatArg* const argp = new AstSFormatArg{valuep->fileline(), attr, valuep}; - argp->dtypep(dtypep); - argp->namep(enumSelect(keyp, dtypep, VAttrType::ENUM_NAME)); - return argp; - } - //---------------------------------------------------------------------- // LOWER LEVEL WIDTH METHODS (none iterate) @@ -9981,7 +9971,7 @@ class WidthVisitor final : public VNVisitor { } if (adtypep->itemsp()->width() > 64) { errNodep->v3warn(E_UNSUPPORTED, - "Unsupported: enum next/prev method on enum with > 64 bits"); + "Unsupported: enum next/prev/name method on enum with > 64 bits"); return 64; } return maxval; @@ -10041,7 +10031,6 @@ class WidthVisitor final : public VNVisitor { UASSERT_OBJ(nodep->itemsp(), nodep, "enum without items"); std::map values; { - uint64_t ordinal = 0; AstEnumItem* const firstp = nodep->itemsp(); const AstEnumItem* prevp = firstp; // Prev must start with last item while (prevp->nextp()) prevp = VN_AS(prevp->nextp(), EnumItem); @@ -10050,10 +10039,10 @@ class WidthVisitor final : public VNVisitor { const AstConst* const vconstp = VN_AS(itemp->valuep(), Const); UASSERT_OBJ(vconstp, nodep, "Enum item without constified value"); if (!vconstp->num().isAnyXZ()) { // Can 2-state runtime decode - const uint64_t i = nodep->isWide() ? ++ordinal : vconstp->toUQuad(); + const uint64_t i = vconstp->toUQuad(); if (attrType == VAttrType::ENUM_NAME) { values[i] = new AstConst{nodep->fileline(), AstConst::String{}, - V3Number::displayedEnumName(itemp)}; + itemp->name()}; } else if (attrType == VAttrType::ENUM_NEXT) { values[i] = (nextp ? nextp : firstp)->valuep()->cloneTree(false); // A const @@ -10083,44 +10072,9 @@ class WidthVisitor final : public VNVisitor { return pair.first->second; } - AstNodeExpr* enumSelect(AstNodeExpr* nodep, AstEnumDType* adtypep, VAttrType attrType) { + static AstNodeExpr* enumSelect(AstNodeExpr* nodep, AstEnumDType* adtypep, VAttrType attrType) { // Return expression to get given attrType information from a enum's value (nodep) - if (attrType == VAttrType::ENUM_NAME) { - if (nodep->isPure()) nodep = V3Const::constifyEdit(nodep); - if (const AstConst* const constp = VN_CAST(nodep, Const)) { - AstNodeExpr* const newp = new AstConst{nodep->fileline(), AstConst::String{}, - constp->num().displayedEnumName(adtypep)}; - VL_DO_DANGLING(pushDeletep(nodep), nodep); - return newp; - } - } - if (attrType == VAttrType::ENUM_NAME && !nodep->isPure() && (m_modep || m_ftaskp)) { - AstExprStmt* const capturep = newEnumCapture(nodep, adtypep); - AstNodeExpr* const valuep = capturep->resultp()->unlinkFrBack(); - capturep->resultp(enumSelect(valuep, adtypep, attrType)); - capturep->dtypeSetString(); - return capturep; - } - if (attrType == VAttrType::ENUM_NAME && adtypep->isWide()) { - // Wide values select an ordinal in the shared name table. - AstNodeExpr* indexp = new AstConst{nodep->fileline(), 0}; - uint32_t ordinal = 0; - for (const AstEnumItem* itemp = adtypep->itemsp(); itemp; - itemp = VN_AS(itemp->nextp(), EnumItem)) { - const AstConst* const constp = VN_AS(itemp->valuep(), Const); - if (constp->num().isAnyXZ()) continue; - indexp = new AstCond{nodep->fileline(), - new AstEq{nodep->fileline(), nodep->cloneTree(false), - itemp->valuep()->cloneTree(false)}, - new AstConst{nodep->fileline(), ++ordinal}, indexp}; - } - AstVar* const varp = enumVarp(adtypep, attrType, false, ordinal); - AstNodeExpr* const newp - = new AstArraySel{nodep->fileline(), newVarRefDollarUnit(varp), indexp}; - newp->dtypeSetString(); - VL_DO_DANGLING(pushDeletep(nodep), nodep); - return newp; - } + // Need a runtime lookup table. Yuk. const uint64_t msbdim = enumMaxValue(nodep, adtypep); const bool assoc = msbdim > ENUM_LOOKUP_BITS; AstNodeExpr* newp; @@ -10130,24 +10084,12 @@ class WidthVisitor final : public VNVisitor { } else { const int selwidth = V3Number::log2b(msbdim) + 1; // Width to address a bit AstVar* const varp = enumVarp(adtypep, attrType, false, (1ULL << selwidth) - 1); - AstNodeExpr* const boundp - = attrType == VAttrType::ENUM_NAME - && (adtypep->width() > selwidth || msbdim != (1ULL << selwidth) - 1) - ? new AstGt{nodep->fileline(), nodep->cloneTree(false), - new AstConst{nodep->fileline(), - V3Number{nodep, adtypep->width(), - static_cast(msbdim)}}} - : nullptr; newp = new AstArraySel{ nodep->fileline(), newVarRefDollarUnit(varp), // Select in case widths are off due to msblen!=width // We return "random" values if outside the range, which is fine // as next/previous on illegal values just need something good out new AstSel{nodep->fileline(), nodep, 0, selwidth}}; - if (boundp) { - newp = new AstCond{nodep->fileline(), boundp, - new AstConst{nodep->fileline(), AstConst::String{}, ""}, newp}; - } } if (attrType == VAttrType::ENUM_NAME) { newp->dtypeSetString(); @@ -10441,8 +10383,7 @@ public: WidthVisitor(bool paramsOnly, // [in] TRUE if we are considering parameters only. bool doGenerate) // [in] TRUE if we are inside a generate statement and // // don't wish to trigger errors - : m_enumTempNames{"__Venum"} - , m_insideTempNames{"__VInside"} + : m_insideTempNames{"__VInside"} , m_paramsOnly{paramsOnly} , m_doGenerate{doGenerate} {} AstNode* mainAcceptEdit(AstNode* nodep) { diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 0a8e08bd9..013bce73a 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -52,10 +52,6 @@ module Vt_debug_emitv_t; if ((ZERO == 'sh0)) begin $write(""); end - $display("%p", $_EXPRSTMT( - __Venum_h4d326b97__0 = (in); - , __Venum_h4d326b97__0); - ); if ($value$plusargs("TEST=%d", i1)) begin $display("value was %d", i1); end @@ -1027,10 +1023,6 @@ module Vt_debug_emitv_t; cg_trans_inst = new(); Vt_debug_emitv_cg_cross cg_cross_instVt_debug_emitv_cg_cross; cg_cross_inst = new(); - enum logic [2:0] { - ZERO = 3'h0, - ONE = 3'h1 - } __Venum_h4d326b97__0; endmodule package Vt_debug_emitv___024unit; class Vt_debug_emitv_Cls; @@ -1162,7 +1154,6 @@ package Vt_debug_emitv___024unit; disable label3; end endfunction - string __Venumtab_enum_name17[1:0]; endpackage package Vt_debug_emitv_std; class Vt_debug_emitv_semaphore; diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index b138c2e9c..0fe96dbbf 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -96,7 +96,6 @@ module t (/*AUTOARG*/ if ($test$plusargs("HELLO")) $display("Hello argument found."); if (Pkg::FOO == 0) $write(""); if (ZERO == 0) $write(""); - $display("%p", e_t'(in)); if ($value$plusargs("TEST=%d", i1)) $display("value was %d", i1); else diff --git a/test_regress/t/t_display_enum_format.v b/test_regress/t/t_display_enum_format.v index b956ae9da..4281a9380 100644 --- a/test_regress/t/t_display_enum_format.v +++ b/test_regress/t/t_display_enum_format.v @@ -12,7 +12,7 @@ // verilog_format: on module t ( - input no_opt + input string empty_no_opt ); typedef enum logic [1:0] { E0 = 0, @@ -24,28 +24,13 @@ module t ( W64A = 64'h1, W64B = 64'h0000_0001_0000_0001 } wide64_e; + // Enums > 64 bits are beyond enum.name() support, so %p/%s format numerically typedef enum logic [95:0] { W96A = 96'h1, W96B = 96'hA_0000_0000_0000_0001 } wide96_e; typedef logic signed [4095:0] uvm_bitstream_t; - function automatic wide64_e const_enum(input bit high); - wide64_e value; - if (high) value = W64B; - else value = W64A; - return value; - endfunction - - localparam string CONST_ENUM_TEXT = $sformatf("%p/%s", const_enum(1'b1), const_enum(1'b1)); - - // IEEE 1800-2023 21.2.1.6 permits implementation-specific %0p output. -`ifdef QUESTA - localparam string COMPACT_NUM_PREFIX = ""; -`else - localparam string COMPACT_NUM_PREFIX = "'h"; -`endif - my_e e; wide64_e e64; wide96_e e96; @@ -54,11 +39,6 @@ module t ( initial begin string fmt; - string formatted; - string empty_no_opt; - // Keep formats nonconstant using an input supported by the generated testbench. - empty_no_opt = (no_opt === 1'b1) ? "unexpected" : ""; - `checks(CONST_ENUM_TEXT, "W64B/W64B"); begin my_e it; string names_p; @@ -73,222 +53,134 @@ module t ( names_s = {names_s, ","}; vals_d = {vals_d, ","}; end - names_p = {names_p, $sformatf("%p/%p", it, it)}; - names_s = {names_s, $sformatf("%s/%s", it, it)}; - vals_d = {vals_d, $sformatf("%0d/%0d", it, it)}; + names_p = {names_p, $sformatf("%p", it)}; + names_s = {names_s, $sformatf("%s", it)}; + vals_d = {vals_d, $sformatf("%0d", it)}; if (it == it.last) break; end - `checks(names_p, "E0/E0,E1/E1,E2/E2"); - `checks(names_s, "E0/E0,E1/E1,E2/E2"); - `checks(vals_d, "0/0,1/1,2/2"); + `checks(names_p, "E0,E1,E2"); + `checks(names_s, "E0,E1,E2"); + `checks(vals_d, "0,1,2"); end // Valid enum values print mnemonic for %p/%s. e = E0; - formatted = $sformatf("%p/%p", e, e); - `checks(formatted, "E0/E0"); - formatted = $sformatf("%s/%s", e, e); - `checks(formatted, "E0/E0"); + `checks($sformatf("%p", e), "E0"); + `checks($sformatf("%s", e), "E0"); e = E1; - formatted = $sformatf("%p/%p", e, e); - `checks(formatted, "E1/E1"); - formatted = $sformatf("%P/%P", e, e); - `checks(formatted, "E1/E1"); - formatted = $sformatf("%0p/%0p", e, e); - `checks(formatted, "E1/E1"); - formatted = $sformatf("%s/%s", e, e); - `checks(formatted, "E1/E1"); - formatted = $sformatf("%S/%S", e, e); - `checks(formatted, "E1/E1"); - formatted = $sformatf("%d/%d", e, e); - `checks(formatted, "1/1"); - formatted = $sformatf("%0d/%0d", e, e); - `checks(formatted, "1/1"); - formatted = $sformatf("%h/%h", e, e); - `checks(formatted, "1/1"); - formatted = $sformatf("%0h/%0h", e, e); - `checks(formatted, "1/1"); - formatted = $sformatf("%b/%b", e, e); - `checks(formatted, "01/01"); - formatted = $sformatf("%0b/%0b", e, e); - `checks(formatted, "1/1"); - formatted = $sformatf("%o/%o", e, e); - `checks(formatted, "1/1"); - formatted = $sformatf("%0o/%0o", e, e); - `checks(formatted, "1/1"); - formatted = $sformatf("%x/%x", e, e); - `checks(formatted, "1/1"); - formatted = $sformatf("%0x/%0x", e, e); - `checks(formatted, "1/1"); + `checks($sformatf("%p", e), "E1"); + `checks($sformatf("%P", e), "E1"); + `checks($sformatf("%0p", e), "E1"); + `checks($sformatf("%s", e), "E1"); + `checks($sformatf("%S", e), "E1"); + `checks($sformatf("%d", e), "1"); + `checks($sformatf("%0d", e), "1"); + `checks($sformatf("%h", e), "1"); + `checks($sformatf("%0h", e), "1"); + `checks($sformatf("%b", e), "01"); + `checks($sformatf("%0b", e), "1"); + `checks($sformatf("%o", e), "1"); + `checks($sformatf("%0o", e), "1"); + `checks($sformatf("%x", e), "1"); + `checks($sformatf("%0x", e), "1"); e = E2; - formatted = $sformatf("%p/%p", e, e); - `checks(formatted, "E2/E2"); - formatted = $sformatf("%s/%s", e, e); - `checks(formatted, "E2/E2"); + `checks($sformatf("%p", e), "E2"); + `checks($sformatf("%s", e), "E2"); `checks($sformatf("%s|%p", e, e), "E2|E2"); - // IEEE 1800-2023 21.2.1.6 specifies %p/%0p, not nonzero %p field widths. - formatted = $sformatf("%4p/%-4p", e, e); -`ifdef QUESTA - `checks(formatted, "00E2/E2 "); -`else - `checks(formatted, "E2/E2"); -`endif - formatted = $sformatf("%d/%d", e, e); - `checks(formatted, "2/2"); - formatted = $sformatf("%h/%h", e, e); - `checks(formatted, "2/2"); - formatted = $sformatf("%b/%b", e, e); - `checks(formatted, "10/10"); - formatted = $sformatf("%0b/%0b", e, e); - `checks(formatted, "10/10"); - formatted = $sformatf("%o/%o", e, e); - `checks(formatted, "2/2"); - formatted = $sformatf("%x/%x", e, e); - `checks(formatted, "2/2"); - formatted = $sformatf("%4d/%4d", e, e); - `checks(formatted, " 2/ 2"); - formatted = $sformatf("%04d/%04d", e, e); -`ifdef QUESTA - `checks(formatted, " 2/ 2"); -`else - `checks(formatted, "0002/0002"); -`endif - formatted = $sformatf("%4h/%4h", e, e); - `checks(formatted, "0002/0002"); - formatted = $sformatf("%-4s/%-4s", e, e); - `checks(formatted, "E2 /E2 "); - formatted = $sformatf("%4s/%4s", e, e); - `checks(formatted, " E2/ E2"); + `checks($sformatf("%4p", e), "E2"); + `checks($sformatf("%-4p", e), "E2"); + `checks($sformatf("%d", e), "2"); + `checks($sformatf("%h", e), "2"); + `checks($sformatf("%b", e), "10"); + `checks($sformatf("%0b", e), "10"); + `checks($sformatf("%o", e), "2"); + `checks($sformatf("%x", e), "2"); + `checks($sformatf("%4d", e), " 2"); + `checks($sformatf("%04d", e), "0002"); + `checks($sformatf("%4h", e), "0002"); + `checks($sformatf("%-4s", e), "E2 "); + `checks($sformatf("%4s", e), " E2"); // `%p`/`%s` in non-terminal positions with mixed formatters. `checks($sformatf("%0d:%s:%0d", 9, e, 7), "9:E2:7"); `checks($sformatf("%s %h %p", e, 4'hA, e), "E2 a E2"); - formatted = $sformatf("pre %% %s/%s post", e, e); - `checks(formatted, "pre % E2/E2 post"); + `checks($sformatf("pre %% %s post", e), "pre % E2 post"); // Complex enum expressions (non-var-ref) in format args. - formatted = $sformatf("%s/%s", (1'b1 ? E2 : E0), (1'b1 ? E2 : E0)); - `checks(formatted, "E2/E2"); + `checks($sformatf("%s", (1'b1 ? E2 : E0)), "E2"); // 64-bit enums should preserve bits above 32 in both named and numeric cases. e64 = W64B; - formatted = $sformatf("%p/%p", e64, e64); - `checks(formatted, "W64B/W64B"); - formatted = $sformatf("%s/%s", e64, e64); - `checks(formatted, "W64B/W64B"); + `checks($sformatf("%p", e64), "W64B"); + `checks($sformatf("%s", e64), "W64B"); e64 = wide64_e'(64'h0000_0002_0000_0001); - formatted = $sformatf("%p/%p", e64, e64); - `checks(formatted, "8589934593/8589934593"); - formatted = $sformatf("%s/%s", e64, e64); - `checks(formatted, "8589934593/8589934593"); + `checks($sformatf("%p", e64), "8589934593"); + `checks($sformatf("%s", e64), "8589934593"); n64 = 64'h0000_0000_0000_0001; - formatted = $sformatf("%0p/%0p", n64, n64); - `checks(formatted, {COMPACT_NUM_PREFIX, "1/", COMPACT_NUM_PREFIX, "1"}); - // Wide enums use names for %p/%s without changing explicit numeric formats. + `checks($sformatf("%0p", n64), "'h1"); + // > 64-bit enums print numerically for %p (no name table support) e96 = W96B; // 10 * 2**64 + 1 if (empty_no_opt != "") e96 = W96A; // Defeat constant folding - formatted = $sformatf("%p/%p", e96, e96); - `checks(formatted, "W96B/W96B"); - formatted = $sformatf("%s/%s", e96, e96); - `checks(formatted, "W96B/W96B"); - formatted = $sformatf("%0p/%0p", e96, e96); - `checks(formatted, "W96B/W96B"); - formatted = $sformatf("%0d/%0d", e96, e96); - `checks(formatted, "184467440737095516161/184467440737095516161"); - formatted = $sformatf("%0h/%0h", e96, e96); - `checks(formatted, "a0000000000000001/a0000000000000001"); + `checks($sformatf("%p", e96), "184467440737095516161"); + `checks($sformatf("%0p", e96), "'ha0000000000000001"); + `checks($sformatf("%0d", e96), "184467440737095516161"); + `checks($sformatf("%0h", e96), "a0000000000000001"); // Exercise display/write-family formatting path in addition to $sformatf checks. $display("display-valid:%s:%0d:%p", e, 7, e); $write("write-valid:%s:%0d:%p\n", e, 8, e); // Invalid enum values fall back to numeric formatting for %p/%s. e = my_e'(3); - formatted = $sformatf("%p/%p", e, e); - `checks(formatted, "3/3"); - formatted = $sformatf("%P/%P", e, e); - `checks(formatted, "3/3"); - formatted = $sformatf("%0p/%0p", e, e); - `checks(formatted, {COMPACT_NUM_PREFIX, "3/", COMPACT_NUM_PREFIX, "3"}); - formatted = $sformatf("%s/%s", e, e); - `checks(formatted, "3/3"); - formatted = $sformatf("%S/%S", e, e); - `checks(formatted, "3/3"); - formatted = $sformatf("%4p/%4p", e, e); -`ifdef QUESTA - `checks(formatted, "0003/0003"); -`else - `checks(formatted, "3/3"); -`endif - formatted = $sformatf("%4s/%4s", e, e); - `checks(formatted, " 3/ 3"); - formatted = $sformatf("%d/%d", e, e); - `checks(formatted, "3/3"); - formatted = $sformatf("%0d/%0d", e, e); - `checks(formatted, "3/3"); - formatted = $sformatf("%h/%h", e, e); - `checks(formatted, "3/3"); - formatted = $sformatf("%0h/%0h", e, e); - `checks(formatted, "3/3"); - formatted = $sformatf("%b/%b", e, e); - `checks(formatted, "11/11"); - formatted = $sformatf("%0b/%0b", e, e); - `checks(formatted, "11/11"); - formatted = $sformatf("%o/%o", e, e); - `checks(formatted, "3/3"); - formatted = $sformatf("%x/%x", e, e); - `checks(formatted, "3/3"); + `checks($sformatf("%p", e), "3"); + `checks($sformatf("%P", e), "3"); + `checks($sformatf("%0p", e), "'h3"); + `checks($sformatf("%s", e), "3"); + `checks($sformatf("%S", e), "3"); + `checks($sformatf("%4p", e), "3"); + `checks($sformatf("%4s", e), " 3"); + `checks($sformatf("%d", e), "3"); + `checks($sformatf("%0d", e), "3"); + `checks($sformatf("%h", e), "3"); + `checks($sformatf("%0h", e), "3"); + `checks($sformatf("%b", e), "11"); + `checks($sformatf("%0b", e), "11"); + `checks($sformatf("%o", e), "3"); + `checks($sformatf("%x", e), "3"); // Non-terminal invalid-value fallback with mixed formatters. `checks($sformatf("%0d:%p:%0d", 9, e, 7), "9:3:7"); `checks($sformatf("%s %h %p", e, 4'hA, e), "3 a 3"); - formatted = $sformatf("pre %% %s/%s post", e, e); - `checks(formatted, "pre % 3/3 post"); + `checks($sformatf("pre %% %s post", e), "pre % 3 post"); `checks($sformatf("%s|%p", e, e), "3|3"); - formatted = $sformatf("%s/%s", (1'b1 ? my_e'(3) : E0), (1'b1 ? my_e'(3) : E0)); - `checks(formatted, "3/3"); - formatted = $sformatf("%p/%p", (1'b0 ? E0 : my_e'(3)), (1'b0 ? E0 : my_e'(3))); - `checks(formatted, "3/3"); + `checks($sformatf("%s", (1'b1 ? my_e'(3) : E0)), "3"); + `checks($sformatf("%p", (1'b0 ? E0 : my_e'(3))), "3"); $display("display-invalid:%s:%0d:%p", e, 7, e); $write("write-invalid:%s:%0d:%p\n", e, 8, e); // Runtime-computed $sformatf formats should preserve enum mnemonic/fallback behavior. e = E2; - fmt = {"%", "s/%s", empty_no_opt}; - formatted = $sformatf(fmt, e, e); - `checks(formatted, "E2/E2"); - fmt = {"%", "p/%p", empty_no_opt}; - formatted = $sformatf(fmt, e, e); - `checks(formatted, "E2/E2"); - fmt = {"%0h/%0h", empty_no_opt}; - formatted = $sformatf(fmt, e, e); - `checks(formatted, "2/2"); + fmt = {"%", "s", empty_no_opt}; + `checks($sformatf(fmt, e), "E2"); + fmt = {"%", "p", empty_no_opt}; + `checks($sformatf(fmt, e), "E2"); fmt = {"%0d:%", "s", ":%0d", empty_no_opt}; `checks($sformatf(fmt, 9, e, 7), "9:E2:7"); fmt = {"%", "s", " %h %", "p", empty_no_opt}; `checks($sformatf(fmt, e, 4'hA, e), "E2 a E2"); e = my_e'(3); - fmt = {"%0b/%0b", empty_no_opt}; - formatted = $sformatf(fmt, e, e); - `checks(formatted, "11/11"); - fmt = {"%", "s/%s", empty_no_opt}; - formatted = $sformatf(fmt, e, e); - `checks(formatted, "3/3"); - fmt = {"%", "p/%p", empty_no_opt}; - formatted = $sformatf(fmt, e, e); - `checks(formatted, "3/3"); - fmt = {"%0", "p/%0p", empty_no_opt}; - formatted = $sformatf(fmt, e, e); - `checks(formatted, {COMPACT_NUM_PREFIX, "3/", COMPACT_NUM_PREFIX, "3"}); + fmt = {"%", "s", empty_no_opt}; + `checks($sformatf(fmt, e), "3"); + fmt = {"%", "p", empty_no_opt}; + `checks($sformatf(fmt, e), "3"); + fmt = {"%0", "p", empty_no_opt}; + `checks($sformatf(fmt, e), "'h3"); fmt = {"%0d:%", "s", ":%0d", empty_no_opt}; `checks($sformatf(fmt, 9, e, 7), "9:3:7"); fmt = {"%", "s", " %h %", "p", empty_no_opt}; `checks($sformatf(fmt, e, 4'hA, e), "3 a 3"); - fmt = {"%", "p/%p", empty_no_opt}; - formatted = $sformatf(fmt, e64, e64); - `checks(formatted, "8589934593/8589934593"); - // Runtime formats must also preserve the wide enum's name. - fmt = {"%", "p/%p", empty_no_opt}; - formatted = $sformatf(fmt, e96, e96); - `checks(formatted, "W96B/W96B"); - fmt = {"%0d/%0d", empty_no_opt}; - formatted = $sformatf(fmt, e96, e96); - `checks(formatted, "184467440737095516161/184467440737095516161"); + fmt = {"%", "p", empty_no_opt}; + `checks($sformatf(fmt, e64), "8589934593"); + // > 64-bit enums use the non-ENUM format in runtime formats too + fmt = {"%", "p", empty_no_opt}; + `checks($sformatf(fmt, e96), "184467440737095516161"); + fmt = {"%0d", empty_no_opt}; + `checks($sformatf(fmt, e96), "184467440737095516161"); bitstream_value = 30; `checks($sformatf("%0s%0t", "", bitstream_value), "30"); bitstream_value = '0; @@ -299,8 +191,7 @@ module t ( `checks($sformatf("%0s%0t", "", bitstream_value), "4294967297"); bitstream_value[7:0] = "A"; // verilator lint_off WIDTHTRUNC - formatted = $sformatf("%c/%c", bitstream_value, bitstream_value); - `checks(formatted, "A/A"); + `checks($sformatf("%c", bitstream_value), "A"); // verilator lint_on WIDTHTRUNC $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_display_enum_nolift.py b/test_regress/t/t_display_enum_nolift.py deleted file mode 100755 index e311e226e..000000000 --- a/test_regress/t/t_display_enum_nolift.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/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('vlt') -test.top_filename = 't/t_display_pattern_format.v' - -test.compile(verilator_flags2=['--binary', '-fno-lift-expr']) - -test.execute() - -test.passes() diff --git a/test_regress/t/t_display_enum_protect_ids.py b/test_regress/t/t_display_enum_protect_ids.py deleted file mode 100755 index 9ef6f9db5..000000000 --- a/test_regress/t/t_display_enum_protect_ids.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/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('vlt') -test.top_filename = 't/t_display_pattern_format.v' - -test.compile(verilator_flags2=[ - '--binary', '--protect-ids', '--protect-key ENUM_KEY', '-Wno-INSECURE', '+define+TEST_PROTECT' -]) - -test.execute() - -test.passes() diff --git a/test_regress/t/t_display_pattern_format.v b/test_regress/t/t_display_pattern_format.v index f0398c2d1..b93d7ed16 100644 --- a/test_regress/t/t_display_pattern_format.v +++ b/test_regress/t/t_display_pattern_format.v @@ -6,7 +6,6 @@ // verilog_format: off `define stop $stop -`define checkd(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); `define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); // verilog_format: on @@ -22,10 +21,6 @@ module t; SECOND = 7'd65 } enum_t; typedef enum_t enum_alias_t; - typedef enum logic [6:0] { - SMALL_FIRST = 7'd0, - SMALL_SECOND = 7'd1 - } small_t; typedef enum logic signed [6:0] { SIGNED7_NEG = -7'sd3, SIGNED7_POS = 7'sd7 @@ -39,54 +34,6 @@ module t; SIGNED65_NEG = -65'sd18446744073709551615, SIGNED65_POS = 65'sd7 } signed65_t; - typedef enum logic signed [94:0] { - SIGNED95_NEG = -95'sd19807040628566084398385987583, - SIGNED95_POS = 95'sd1 - } signed95_t; - typedef enum logic signed [128:0] { - SIGNED129_LOW = 129'sd1, - SIGNED129_HIGH = 129'sd18446744073709551617, - SIGNED129_NEG = -129'sd340282366920938463463374607431768211455 - } signed129_t; - typedef signed129_t signed129_alias_t; - typedef enum logic [64:0] { - UNSIGNED65_LOW = 65'h1, - UNSIGNED65_HIGH = 65'h10000000000000001, - \escaped.wide = 65'h3, - UNSIGNED65_X = 65'bx - } unsigned65_t; - typedef unsigned65_t unsigned65_alias_t; - - int enum_calls = 0; - int format_calls = 0; - unsigned65_t format_value; - function automatic unsigned65_t next_enum(input bit high); - enum_calls++; - return high ? UNSIGNED65_HIGH : UNSIGNED65_LOW; - endfunction - - function automatic string format_enum(input unsigned65_t value); - return $sformatf("%p/%s", value, value); - endfunction - - function automatic string format_next_enum(input bit high); - return $sformatf("%p", next_enum(high)); - endfunction - - function automatic enum_t next_narrow_enum(input bit high); - enum_calls++; - return high ? SECOND : FIRST; - endfunction - - function automatic string format_narrow_enum(input enum_t value); - return $sformatf("%p/%s", value, value); - endfunction - - function automatic string select_format(input bit high, input bit string_format); - format_calls++; - format_value = high ? UNSIGNED65_HIGH : UNSIGNED65_LOW; - return string_format ? "%s" : "%p"; - endfunction localparam text_t TEXT_PARAM = "quote=\" slash=\\ bell=\a form=\f vert=\v ctrl=\001"; localparam string ESCAPED_PARAM_STRING = $sformatf("%p", TEXT_PARAM); @@ -100,78 +47,8 @@ module t; localparam string WIDE_SECOND_TEXT = $sformatf("%p", WIDE_SECOND); localparam string REAL_FIRST_TEXT = $sformatf("%p", 1.25); localparam string REAL_SECOND_TEXT = $sformatf("%p", 0.5); - localparam string ENUM_FIRST_TEXT = $sformatf("%p", FIRST); - localparam string ENUM_SECOND_TEXT = $sformatf("%s", SECOND); - localparam string ENUM7_NEG_TEXT = $sformatf("%p", SIGNED7_NEG); - localparam string ENUM33_NEG_TEXT = $sformatf("%p", SIGNED33_NEG); - localparam string NARROW_FUNC_TEXT = format_narrow_enum(FIRST); - localparam string NARROW_EXPR_TEXT = $sformatf("%p", enum_t'(7'd1 + 7'd2)); - localparam string SMALL_INVALID_TEXT = $sformatf("%p", small_t'(7'd2)); - localparam string ENUM_LOW_TEXT = $sformatf("%p", UNSIGNED65_LOW); - localparam string ENUM_HIGH_TEXT = $sformatf("%s", UNSIGNED65_HIGH); - localparam unsigned65_t ENUM_METHOD_VALUE = UNSIGNED65_HIGH; - localparam string ENUM_METHOD_TEXT = ENUM_METHOD_VALUE.name(); - localparam string WIDE_EXPR_TEXT = $sformatf("%p", unsigned65_t'((65'd1 << 64) | 65'd1)); - localparam string ENUM_NEG_TEXT = $sformatf("%p", SIGNED65_NEG); - localparam string ENUM95_NEG_TEXT = $sformatf("%p", SIGNED95_NEG); - localparam string ENUM95_POS_TEXT = $sformatf("%s", SIGNED95_POS); - localparam string ENUM129_LOW_TEXT = $sformatf("%p", SIGNED129_LOW); - localparam string ENUM129_HIGH_TEXT = $sformatf("%s", SIGNED129_HIGH); - localparam string ENUM129_NEG_TEXT = $sformatf("%p", SIGNED129_NEG); - localparam string ENUM_ESCAPED_TEXT = $sformatf("%p", \escaped.wide ); - localparam string ENUM_FUNC_TEXT = format_enum(UNSIGNED65_HIGH); - localparam unsigned65_t ENUM_UNKNOWN = unsigned65_t'(65'h10000000000000002); - localparam string ENUM_UNKNOWN_TEXT = $sformatf("%p", ENUM_UNKNOWN); - localparam string ENUM_UNKNOWN_STRING = $sformatf("%s", ENUM_UNKNOWN); - localparam string ENUM_UNKNOWN_COMPACT = $sformatf("%0p", ENUM_UNKNOWN); - localparam string ENUM_UNKNOWN_FUNC_TEXT = format_enum(ENUM_UNKNOWN); - localparam string ENUM_SIGNED_UNKNOWN_TEXT = $sformatf("%p", signed65_t'(-65'sd2)); initial begin - string formatted; - string fmt; -`ifdef TEST_PROTECT - formatted = ENUM_HIGH_TEXT.substr(0, 1); - `checks(formatted, "PS"); - formatted = ENUM129_NEG_TEXT.substr(0, 1); - `checks(formatted, "PS"); -`else - `checks(ENUM_FIRST_TEXT, "FIRST"); - `checks(ENUM_SECOND_TEXT, "SECOND"); - `checks(ENUM7_NEG_TEXT, "SIGNED7_NEG"); - `checks(ENUM33_NEG_TEXT, "SIGNED33_NEG"); - `checks(NARROW_FUNC_TEXT, "FIRST/FIRST"); - `checks(NARROW_EXPR_TEXT, "FIRST"); - `checks(ENUM_LOW_TEXT, "UNSIGNED65_LOW"); - `checks(ENUM_HIGH_TEXT, "UNSIGNED65_HIGH"); - `checks(WIDE_EXPR_TEXT, "UNSIGNED65_HIGH"); - `checks(ENUM_NEG_TEXT, "SIGNED65_NEG"); - `checks(ENUM95_NEG_TEXT, "SIGNED95_NEG"); - `checks(ENUM95_POS_TEXT, "SIGNED95_POS"); - `checks(ENUM129_LOW_TEXT, "SIGNED129_LOW"); - `checks(ENUM129_HIGH_TEXT, "SIGNED129_HIGH"); - `checks(ENUM129_NEG_TEXT, "SIGNED129_NEG"); - `checks(ENUM_ESCAPED_TEXT, "\\escaped.wide "); - `checks(ENUM_FUNC_TEXT, "UNSIGNED65_HIGH/UNSIGNED65_HIGH"); -`endif - `checks(SMALL_INVALID_TEXT, "2"); - `checks(ENUM_UNKNOWN_STRING, ENUM_UNKNOWN_TEXT); - `checks(ENUM_METHOD_TEXT, ENUM_HIGH_TEXT); - `checks(ENUM_UNKNOWN_FUNC_TEXT, {ENUM_UNKNOWN_TEXT, "/", ENUM_UNKNOWN_TEXT}); - `checks(ENUM_SIGNED_UNKNOWN_TEXT, "-2"); -`ifdef QUESTA - `checks(ENUM_UNKNOWN_TEXT, "2"); - `checks(ENUM_UNKNOWN_COMPACT, "2"); -`else - `checks(ENUM_UNKNOWN_TEXT, "18446744073709551618"); - `checks(ENUM_UNKNOWN_COMPACT, "'h10000000000000002"); -`endif - fmt = "%0h"; - formatted = $sformatf(fmt, UNSIGNED65_HIGH); - `checks(formatted, "10000000000000001"); - fmt = "%p"; - formatted = $sformatf(fmt, FIRST); - `checks(formatted, ENUM_FIRST_TEXT); `ifdef QUESTA // Questa 2025.2 does not escape strings as required by IEEE 1800-2012 21.2.1.7. `checks(ESCAPED_PARAM_STRING, {"\"", TEXT_PARAM, "\""}); @@ -187,7 +64,6 @@ module t; string escaped_expected; string fmt; enum_alias_t enum_value; - small_t small_value; narrow_array_t narrow; wide_array_t wide; real real_value; @@ -198,12 +74,7 @@ module t; signed7_alias_t signed7_value; signed33_t signed33_value; signed65_t signed65_value; - signed95_t signed95_value; - signed129_alias_t signed129_value; - unsigned65_alias_t unsigned65_value; - logic [64:0] unsigned65_bits; string signed_expected; - string unsigned_expected; plain = $sformatf("round %0d", cyc); escaped = {"quote=\" slash=\\ line=\n cr=\r tab=\t bell=\a form=\f vert=\v ctrl=\001 ", plain}; @@ -251,23 +122,11 @@ module t; if (cyc[0]) enum_text = $sformatf(fmt, enum_value); else enum_text = $sformatf(fmt, enum_value); - `checks(enum_text, cyc[0] ? ENUM_SECOND_TEXT : ENUM_FIRST_TEXT); + `checks(enum_text, cyc[0] ? "SECOND" : "FIRST"); formatted = $sformatf("%p", enum_value); `checks(formatted, enum_text); formatted = $sformatf("%s", enum_value); `checks(formatted, enum_text); - formatted = enum_value.name(); - `checks(formatted, enum_text); - formatted = format_narrow_enum(enum_value); - `checks(formatted, {enum_text, "/", enum_text}); - small_value = small_t'(cyc + 2); - formatted = small_value.name(); - `checks(formatted, ""); - unsigned_expected = $sformatf("%0d", cyc + 2); - formatted = $sformatf("%p", small_value); - `checks(formatted, unsigned_expected); - formatted = $sformatf(fmt, small_value); - `checks(formatted, unsigned_expected); real_value = cyc[0] ? 0.5 : 1.25; real_expected = cyc[0] ? REAL_SECOND_TEXT : REAL_FIRST_TEXT; @@ -294,7 +153,7 @@ module t; formatted = $sformatf(fmt, signed65_value); `checks(formatted, signed_expected); - signed_expected = cyc[0] ? ENUM7_NEG_TEXT : "-2"; + signed_expected = cyc[0] ? "SIGNED7_NEG" : "-2"; `ifdef QUESTA // Questa 2025.2 zero-extends unnamed enums narrower than 32 bits for %p/%s. if (!cyc[0]) signed_expected = "126"; @@ -307,7 +166,7 @@ module t; formatted = $sformatf(fmt, signed7_value); `checks(formatted, signed_expected); - signed_expected = cyc[0] ? ENUM33_NEG_TEXT : "-2"; + signed_expected = cyc[0] ? "SIGNED33_NEG" : "-2"; formatted = $sformatf("%p", signed33_value); `checks(formatted, signed_expected); formatted = $sformatf("%s", signed33_value); @@ -315,175 +174,6 @@ module t; formatted = $sformatf(fmt, signed33_value); `checks(formatted, signed_expected); - // The named values differ only above bit 63. - unsigned65_value = cyc[0] ? UNSIGNED65_HIGH : UNSIGNED65_LOW; - unsigned_expected = cyc[0] ? ENUM_HIGH_TEXT : ENUM_LOW_TEXT; - formatted = $sformatf("%p", unsigned65_value); - `checks(formatted, unsigned_expected); - formatted = $sformatf("%s", unsigned65_value); - `checks(formatted, unsigned_expected); - formatted = unsigned65_value.name(); - `checks(formatted, unsigned_expected); - formatted = $sformatf("%0p", unsigned65_value); - `checks(formatted, unsigned_expected); - formatted = $sformatf(fmt, unsigned65_value); - `checks(formatted, unsigned_expected); - if (!$value$plusargs("enum_complement=%h", unsigned65_bits)) - unsigned65_bits = cyc[0] ? 65'h0fffffffffffffffe : 65'h1fffffffffffffffe; - formatted = $sformatf("%p", unsigned65_t'(~unsigned65_bits)); - `checks(formatted, unsigned_expected); - formatted = $sformatf(fmt, unsigned65_t'(~unsigned65_bits)); - `checks(formatted, unsigned_expected); - formatted = format_enum(unsigned65_value); - `checks(formatted, {unsigned_expected, "/", unsigned_expected}); - formatted = $sformatf("%0d:%p:%s:%0d", 9, unsigned65_value, signed65_value, 7); - signed_expected = cyc[0] ? "-2" : ENUM_NEG_TEXT; - `checks(formatted, {"9:", unsigned_expected, ":", signed_expected, ":7"}); - formatted = $sformatf("%p", signed65_value); - `checks(formatted, signed_expected); - formatted = $sformatf("%s", signed65_value); - `checks(formatted, signed_expected); - formatted = signed65_value.name(); - `checks(formatted, cyc[0] ? "" : ENUM_NEG_TEXT); - formatted = $sformatf(fmt, signed65_value); - `checks(formatted, signed_expected); - - fmt = cyc[0] ? "%0d:%p:%s:%0d" : "%0d:%P:%S:%0d"; - formatted = $sformatf(fmt, 9, unsigned65_value, signed65_value, 7); - `checks(formatted, {"9:", unsigned_expected, ":", signed_expected, ":7"}); - fmt = cyc[1] ? "%p" : "%s"; - enum_calls = 0; - formatted = $sformatf("%p", next_enum(cyc[0])); - `checks(formatted, unsigned_expected); - `checkd(enum_calls, 1); - formatted = $sformatf("%s", next_enum(cyc[0])); - `checks(formatted, unsigned_expected); - `checkd(enum_calls, 2); - formatted = $sformatf(fmt, next_enum(cyc[0])); - `checks(formatted, unsigned_expected); - `checkd(enum_calls, 3); - $sformat(formatted, "%p", next_enum(cyc[0])); - `checks(formatted, unsigned_expected); - `checkd(enum_calls, 4); - $display("wide-enum: %p", next_enum(cyc[0])); - `checkd(enum_calls, 5); - formatted = $sformatf("%p", next_narrow_enum(cyc[0])); - `checks(formatted, enum_text); - `checkd(enum_calls, 6); - formatted = $sformatf(fmt, next_narrow_enum(cyc[0])); - `checks(formatted, enum_text); - `checkd(enum_calls, 7); - formatted = next_enum(cyc[0]).name(); - `checks(formatted, unsigned_expected); - `checkd(enum_calls, 8); - formatted = next_narrow_enum(cyc[0]).name(); - `checks(formatted, enum_text); - `checkd(enum_calls, 9); - formatted = $sformatf(cyc[1] ? "%p/%p" : "%s/%s", next_enum(cyc[0]), next_enum(!cyc[0])); - `checks(formatted, {unsigned_expected, "/", cyc[0] ? ENUM_LOW_TEXT : ENUM_HIGH_TEXT}); - `checkd(enum_calls, 11); - formatted = format_next_enum(cyc[0]); - `checks(formatted, unsigned_expected); - `checkd(enum_calls, 12); - - format_value = cyc[0] ? UNSIGNED65_LOW : UNSIGNED65_HIGH; - formatted = $sformatf(select_format(cyc[0], cyc[1]), format_value); - `checks(formatted, unsigned_expected); - `checkd(format_calls, 2 * cyc + 1); - format_value = cyc[0] ? UNSIGNED65_LOW : UNSIGNED65_HIGH; - $sformat(formatted, select_format(cyc[0], !cyc[1]), format_value); - `checks(formatted, unsigned_expected); - `checkd(format_calls, 2 * cyc + 2); - - unsigned65_value = cyc[0] ? UNSIGNED65_HIGH : \escaped.wide ; - unsigned_expected = cyc[0] ? ENUM_HIGH_TEXT : ENUM_ESCAPED_TEXT; - formatted = $sformatf("%p", unsigned65_value); - `checks(formatted, unsigned_expected); - formatted = $sformatf("%s", unsigned65_value); - `checks(formatted, unsigned_expected); - formatted = unsigned65_value.name(); - `checks(formatted, unsigned_expected); - formatted = $sformatf(fmt, unsigned65_value); - `checks(formatted, unsigned_expected); - fmt = cyc[0] ? "%20s" : "%-20s"; - enum_text = $sformatf(fmt, unsigned_expected); - formatted = $sformatf(fmt, unsigned65_value); - `checks(formatted, enum_text); - - unsigned65_bits = unsigned65_value; - unsigned_expected = $sformatf("%0h", unsigned65_bits); - fmt = cyc[0] ? "%0h" : "%0H"; - formatted = $sformatf(fmt, unsigned65_value); - `checks(formatted, unsigned_expected); - unsigned_expected = $sformatf("%0d", unsigned65_bits); - fmt = cyc[0] ? "%0d" : "%0D"; - formatted = $sformatf(fmt, unsigned65_value); - `checks(formatted, unsigned_expected); - - unsigned65_value = unsigned65_t'(65'h10000000000000002 + 65'(cyc)); - unsigned65_bits = unsigned65_value; - unsigned_expected = $sformatf("%0d", unsigned65_bits); -`ifdef QUESTA - // Questa 2025.2 truncates unnamed wide enums to 64 bits for %p/%s. - unsigned_expected = $sformatf("%0d", unsigned65_bits[63:0]); -`endif - formatted = $sformatf("%p", unsigned65_value); - `checks(formatted, unsigned_expected); - formatted = $sformatf("%s", unsigned65_value); - `checks(formatted, unsigned_expected); - fmt = cyc[0] ? "%p" : "%s"; - formatted = $sformatf(fmt, unsigned65_value); - `checks(formatted, unsigned_expected); - unsigned_expected = $sformatf("%0h", unsigned65_bits); - fmt = cyc[0] ? "%0h" : "%0H"; - formatted = $sformatf(fmt, unsigned65_value); - `checks(formatted, unsigned_expected); - formatted = $sformatf("%0p", unsigned65_value); -`ifdef QUESTA - unsigned_expected = $sformatf("%0d", unsigned65_bits[63:0]); - `checks(formatted, unsigned_expected); -`else - `checks(formatted, {"'h", unsigned_expected}); -`endif - - // Signed values sharing their low 64 bits must retain distinct names. - signed95_value = cyc[0] ? SIGNED95_NEG : SIGNED95_POS; - signed_expected = cyc[0] ? ENUM95_NEG_TEXT : ENUM95_POS_TEXT; - formatted = $sformatf("%p", signed95_value); - `checks(formatted, signed_expected); - formatted = $sformatf("%s", signed95_value); - `checks(formatted, signed_expected); - formatted = signed95_value.name(); - `checks(formatted, signed_expected); - fmt = cyc[0] ? "%p" : "%s"; - formatted = $sformatf(fmt, signed95_value); - `checks(formatted, signed_expected); - signed_expected = cyc[0] ? "-19807040628566084398385987583" : "1"; - formatted = $sformatf("%0d", signed95_value); - `checks(formatted, signed_expected); - fmt = cyc[0] ? "%0d" : "%0D"; - formatted = $sformatf(fmt, signed95_value); - `checks(formatted, signed_expected); - - signed129_value = cyc[1] ? SIGNED129_NEG : (cyc[0] ? SIGNED129_HIGH : SIGNED129_LOW); - signed_expected = cyc[1] ? ENUM129_NEG_TEXT : (cyc[0] ? ENUM129_HIGH_TEXT : ENUM129_LOW_TEXT); - formatted = $sformatf("%p", signed129_value); - `checks(formatted, signed_expected); - formatted = $sformatf("%s", signed129_value); - `checks(formatted, signed_expected); - formatted = signed129_value.name(); - `checks(formatted, signed_expected); - fmt = cyc[0] ? "%p" : "%s"; - formatted = $sformatf(fmt, signed129_value); - `checks(formatted, signed_expected); - signed_expected = cyc[1] ? "-340282366920938463463374607431768211455" : - (cyc[0] ? "18446744073709551617" : "1"); - formatted = $sformatf("%0d", signed129_value); - `checks(formatted, signed_expected); - fmt = cyc[0] ? "%0d" : "%0D"; - formatted = $sformatf(fmt, signed129_value); - `checks(formatted, signed_expected); - cyc <= cyc + 1; if (cyc == 3) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_enum_format_param_bad.out b/test_regress/t/t_enum_format_param_bad.out deleted file mode 100644 index d376e6fb3..000000000 --- a/test_regress/t/t_enum_format_param_bad.out +++ /dev/null @@ -1,10 +0,0 @@ -%Error: t/t_enum_format_param_bad.v:13:50: Expecting expression to be constant, but variable isn't const: 'small_value' - : ... note: In instance 't' - 13 | localparam string SMALL_TEXT = $sformatf("%p", small_value); - | ^~~~~~~~~~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_enum_format_param_bad.v:14:50: Expecting expression to be constant, but variable isn't const: 'large_value' - : ... note: In instance 't' - 14 | localparam string LARGE_TEXT = $sformatf("%p", large_value); - | ^~~~~~~~~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_enum_format_param_bad.py b/test_regress/t/t_enum_format_param_bad.py deleted file mode 100755 index 344a4e20a..000000000 --- a/test_regress/t/t_enum_format_param_bad.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/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('vlt') - -test.lint(fails=True, expect_filename=test.golden_filename) - -test.passes() diff --git a/test_regress/t/t_enum_format_param_bad.v b/test_regress/t/t_enum_format_param_bad.v deleted file mode 100644 index 0ba6c59e3..000000000 --- a/test_regress/t/t_enum_format_param_bad.v +++ /dev/null @@ -1,17 +0,0 @@ -// 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 - -module t; - typedef enum logic [6:0] {SMALL = 7'd1} small_t; - typedef enum logic signed [94:0] {LARGE = -95'sd3} large_t; - small_t small_value; - large_t large_value; - - localparam string SMALL_TEXT = $sformatf("%p", small_value); - localparam string LARGE_TEXT = $sformatf("%p", large_value); - - initial $display("%s:%s", SMALL_TEXT, LARGE_TEXT); -endmodule diff --git a/test_regress/t/t_enum_huge_methods_bad.out b/test_regress/t/t_enum_huge_methods_bad.out index a21de182d..e3a0d576c 100644 --- a/test_regress/t/t_enum_huge_methods_bad.out +++ b/test_regress/t/t_enum_huge_methods_bad.out @@ -2,8 +2,8 @@ 13 | ELARGE = 160'h1234_4567_abcd_1234_4567_abcd | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error-UNSUPPORTED: t/t_enum_huge_methods_bad.v:28:12: Unsupported: enum next/prev method on enum with > 64 bits - 28 | e <= e.next(); - | ^ +%Error-UNSUPPORTED: t/t_enum_huge_methods_bad.v:28:16: Unsupported: enum next/prev/name method on enum with > 64 bits + 28 | $display(e.name); + | ^ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest %Error: Exiting due to diff --git a/test_regress/t/t_enum_huge_methods_bad.v b/test_regress/t/t_enum_huge_methods_bad.v index 551cd54bc..898bc8e92 100644 --- a/test_regress/t/t_enum_huge_methods_bad.v +++ b/test_regress/t/t_enum_huge_methods_bad.v @@ -25,7 +25,7 @@ module t ( e <= E01; end else if (cyc == 1) begin - e <= e.next(); + $display(e.name); e <= ELARGE; end else if (cyc == 99) begin