From 59d170c6f8d6dccdd6cf0a689bb75cf74874c5d2 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 11 Dec 2021 11:29:01 -0500 Subject: [PATCH] Support up to 64 bit enums for .next/.prev/.name (#3244). --- Changes | 1 + src/V3AstNodes.cpp | 55 +++++++---- src/V3AstNodes.h | 16 +++- src/V3EmitCConstInit.h | 59 ++++++++---- src/V3EmitCFunc.cpp | 13 ++- src/V3Hasher.cpp | 33 +++++-- src/V3Width.cpp | 103 +++++++++++++-------- test_regress/t/t_enum_huge_methods.pl | 21 +++++ test_regress/t/t_enum_huge_methods.v | 73 +++++++++++++++ test_regress/t/t_enum_huge_methods_bad.out | 2 +- 10 files changed, 288 insertions(+), 88 deletions(-) create mode 100755 test_regress/t/t_enum_huge_methods.pl create mode 100644 test_regress/t/t_enum_huge_methods.v diff --git a/Changes b/Changes index 9324fb681..8f0a8f126 100644 --- a/Changes +++ b/Changes @@ -19,6 +19,7 @@ Verilator 4.217 devel **Minor:** +* Support up to 64 bit enums for .next/.prev/.name (#3244). [Alexander Grobman] * Fix MSWIN compile error (#2681). [Unai Martinez-Corral] * Fix VL_STREAML_FAST_QQI with 64 bit left-hand-side (#3232) (#3235) * Fix $sformat of inputs/outputs (#3236). [Adrien Le Masle] diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index d64c9d81d..13df5edd9 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1017,21 +1017,41 @@ static bool sameInit(const AstInitArray* ap, const AstInitArray* bp) { // - the default/inititem children might be in different order yet still yield the same table // See note in AstInitArray::same about the same. This function instead compares by initializer // value, rather than by tree structure. - const AstUnpackArrayDType* const aDTypep = VN_AS(ap->dtypep(), UnpackArrayDType); - const AstUnpackArrayDType* const bDTypep = VN_AS(bp->dtypep(), UnpackArrayDType); - if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) { // Element types differ - return false; - } - if (!aDTypep->rangep()->sameTree(bDTypep->rangep())) { // Ranges differ - return false; - } - // Compare initializer arrays by value. Note this is only called when they hash the same, - // so they likely run at most once per call to 'AstConstPool::findTable'. - const uint32_t size = aDTypep->elementsConst(); - for (uint32_t n = 0; n < size; ++n) { - const AstNode* const valAp = ap->getIndexDefaultedValuep(n); - const AstNode* const valBp = bp->getIndexDefaultedValuep(n); - if (!valAp->sameTree(valBp)) { return false; } + if (const AstAssocArrayDType* const aDTypep = VN_CAST(ap->dtypep(), AssocArrayDType)) { + const AstAssocArrayDType* const bDTypep = VN_CAST(bp->dtypep(), AssocArrayDType); + if (!bDTypep) return false; + if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) return false; + if (!aDTypep->keyDTypep()->sameTree(bDTypep->keyDTypep())) return false; + UASSERT_OBJ(ap->defaultp(), ap, "Assoc InitArray should have a default"); + UASSERT_OBJ(bp->defaultp(), bp, "Assoc InitArray should have a default"); + if (!ap->defaultp()->sameTree(bp->defaultp())) return false; + // Compare initializer arrays by value. Note this is only called when they hash the same, + // so they likely run at most once per call to 'AstConstPool::findTable'. + // This assumes that the defaults are used in the same way. + // TODO when buinding the AstInitArray, remove any values matching the default + const auto& amapr = ap->map(); + const auto& bmapr = bp->map(); + const auto ait = amapr.cbegin(); + const auto bit = bmapr.cbegin(); + while (ait != amapr.cend() || bit != bmapr.cend()) { + if (ait == amapr.cend() || bit == bmapr.cend()) return false; // Different size + if (ait->first != bit->first) return false; // Different key + if (ait->second->sameTree(bit->second)) return false; // Different value + } + } else if (const AstUnpackArrayDType* const aDTypep + = VN_CAST(ap->dtypep(), UnpackArrayDType)) { + const AstUnpackArrayDType* const bDTypep = VN_CAST(bp->dtypep(), UnpackArrayDType); + if (!bDTypep) return false; + if (!aDTypep->subDTypep()->sameTree(bDTypep->subDTypep())) return false; + if (!aDTypep->rangep()->sameTree(bDTypep->rangep())) return false; + // Compare initializer arrays by value. Note this is only called when they hash the same, + // so they likely run at most once per call to 'AstConstPool::findTable'. + const vluint64_t size = aDTypep->elementsConst(); + for (vluint64_t n = 0; n < size; ++n) { + const AstNode* const valAp = ap->getIndexDefaultedValuep(n); + const AstNode* const valBp = bp->getIndexDefaultedValuep(n); + if (!valAp->sameTree(valBp)) return false; + } } return true; } @@ -1039,8 +1059,9 @@ static bool sameInit(const AstInitArray* ap, const AstInitArray* bp) { AstVarScope* AstConstPool::findTable(AstInitArray* initp) { const AstNode* const defaultp = initp->defaultp(); // Verify initializer is well formed - UASSERT_OBJ(VN_IS(initp->dtypep(), UnpackArrayDType), initp, - "Const pool table must have AstUnpackArrayDType dtype"); + UASSERT_OBJ(VN_IS(initp->dtypep(), AssocArrayDType) + || VN_IS(initp->dtypep(), UnpackArrayDType), + initp, "Const pool table must have array dtype"); UASSERT_OBJ(!defaultp || VN_IS(defaultp, Const), initp, "Const pool table default must be Const"); for (AstNode* nodep = initp->initsp(); nodep; nodep = nodep->nextp()) { diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index fb598bfaf..accdcb154 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -538,6 +538,12 @@ public: keyDTypep(nullptr); dtypep(nullptr); // V3Width will resolve } + AstAssocArrayDType(FileLine* fl, AstNodeDType* dtp, AstNodeDType* keyDtp) + : ASTGEN_SUPER_AssocArrayDType(fl) { + refDTypep(dtp); + keyDTypep(keyDtp); + dtypep(dtp); + } ASTNODE_NODE_FUNCS(AssocArrayDType) virtual const char* broken() const override { BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) @@ -4953,12 +4959,12 @@ class AstInitArray final : public AstNode { // Parents: ASTVAR::init() // Children: AstInitItem public: - using KeyItemMap = std::map; + using KeyItemMap = std::map; private: KeyItemMap m_map; // Node value for each array index public: - AstInitArray(FileLine* fl, AstNodeArrayDType* newDTypep, AstNode* defaultp) + AstInitArray(FileLine* fl, AstNodeDType* newDTypep, AstNode* defaultp) : ASTGEN_SUPER_InitArray(fl) { dtypep(newDTypep); addNOp1p(defaultp); @@ -4988,7 +4994,7 @@ public: AstNode* initsp() const { return op2p(); } // op2 = Initial value expressions void addValuep(AstNode* newp) { addIndexValuep(m_map.size(), newp); } const KeyItemMap& map() const { return m_map; } - AstNode* addIndexValuep(uint32_t index, AstNode* newp) { + AstNode* addIndexValuep(vluint64_t index, AstNode* newp) { // Returns old value, caller must garbage collect AstNode* oldp = nullptr; const auto it = m_map.find(index); @@ -5002,7 +5008,7 @@ public: } return oldp; } - AstNode* getIndexValuep(uint32_t index) const { + AstNode* getIndexValuep(vluint64_t index) const { const auto it = m_map.find(index); if (it == m_map.end()) { return nullptr; @@ -5010,7 +5016,7 @@ public: return it->second->valuep(); } } - AstNode* getIndexDefaultedValuep(uint32_t index) const { + AstNode* getIndexDefaultedValuep(vluint64_t index) const { AstNode* valuep = getIndexValuep(index); if (!valuep) valuep = defaultp(); return valuep; diff --git a/src/V3EmitCConstInit.h b/src/V3EmitCConstInit.h index d865bfd09..fa973eec4 100644 --- a/src/V3EmitCConstInit.h +++ b/src/V3EmitCConstInit.h @@ -46,27 +46,52 @@ class EmitCConstInit VL_NOT_FINAL : public EmitCBaseVisitor { protected: // VISITORS virtual void visit(AstInitArray* nodep) override { - const AstUnpackArrayDType* const dtypep - = VN_AS(nodep->dtypep()->skipRefp(), UnpackArrayDType); - UASSERT_OBJ(dtypep, nodep, "Array initializer has non-array dtype"); - const uint32_t size = dtypep->elementsConst(); - const uint32_t tabMod = tabModulus(dtypep->subDTypep()); VL_RESTORER(m_inUnpacked); VL_RESTORER(m_unpackedWord); m_inUnpacked = true; - // Note the double {{ initializer. The first { starts the initializer of the VlUnpacked, - // and the second starts the initializer of m_storage within the VlUnpacked. - puts("{"); - ofp()->putsNoTracking("{"); - puts("\n"); - for (uint32_t n = 0; n < size; ++n) { - m_unpackedWord = n; - if (n) puts((n % tabMod) ? ", " : ",\n"); - iterate(nodep->getIndexDefaultedValuep(n)); + if (const AstAssocArrayDType* const dtypep + = VN_CAST(nodep->dtypep()->skipRefp(), AssocArrayDType)) { + // Note the double {{ initializer. The first { starts the initializer of the + // VlUnpacked, and the second starts the initializer of m_storage within the + // VlUnpacked. + puts("{"); + ofp()->putsNoTracking("{"); + puts("\n"); + int comma = 0; + const auto& mapr = nodep->map(); + for (const auto& itr : mapr) { + if (comma++) putbs(",\n"); + puts(cvtToStr(itr.first)); + ofp()->printf("%" PRIx64 "ULL", itr.first); + ofp()->putsNoTracking(":"); + ofp()->putsNoTracking("{"); + iterate(nodep->getIndexValuep(itr.first)); + ofp()->putsNoTracking("}"); + } + puts("\n"); + puts("}"); + ofp()->putsNoTracking("}"); + } else if (const AstUnpackArrayDType* const dtypep + = VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { + const vluint64_t size = dtypep->elementsConst(); + const uint32_t tabMod = tabModulus(dtypep->subDTypep()); + // Note the double {{ initializer. The first { starts the initializer of the + // VlUnpacked, and the second starts the initializer of m_storage within the + // VlUnpacked. + puts("{"); + ofp()->putsNoTracking("{"); + puts("\n"); + for (vluint64_t n = 0; n < size; ++n) { + m_unpackedWord = n; + if (n) puts((n % tabMod) ? ", " : ",\n"); + iterate(nodep->getIndexDefaultedValuep(n)); + } + puts("\n"); + puts("}"); + ofp()->putsNoTracking("}"); + } else { + nodep->v3fatalSrc("Array initializer has non-array dtype"); } - puts("\n"); - puts("}"); - ofp()->putsNoTracking("}"); } virtual void visit(AstInitItem* nodep) override { // LCOV_EXCL_START diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index ec73f0593..24f79e2a0 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -622,7 +622,18 @@ void EmitCFunc::emitVarReset(AstVar* varp) { // If an ARRAYINIT we initialize it using an initial block similar to a signal // puts("// parameter "+varp->nameProtect()+" = "+varp->valuep()->name()+"\n"); } else if (const AstInitArray* const initarp = VN_CAST(varp->valuep(), InitArray)) { - if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) { + if (AstAssocArrayDType* const adtypep = VN_CAST(dtypep, AssocArrayDType)) { + if (initarp->defaultp()) { + emitSetVarConstant(varNameProtected + ".atDefault()", + VN_AS(initarp->defaultp(), Const)); + } + const auto& mapr = initarp->map(); + for (const auto& itr : mapr) { + AstNode* const valuep = itr.second->valuep(); + emitSetVarConstant(varNameProtected + ".at(" + cvtToStr(itr.first) + ")", + VN_AS(valuep, Const)); + } + } else if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) { if (initarp->defaultp()) { puts("for (int __Vi=0; __Vi<" + cvtToStr(adtypep->elementsConst())); puts("; ++__Vi) {\n"); diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp index f520c4a0d..bffd8dbf7 100644 --- a/src/V3Hasher.cpp +++ b/src/V3Hasher.cpp @@ -351,17 +351,30 @@ private: m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() {}); } virtual void visit(AstInitArray* nodep) override { - // Hash unpacked array initializers by value, as the order of initializer nodes does not - // matter, and we want semantically equivalent initializers to map to the same hash. - const AstUnpackArrayDType* const dtypep = VN_CAST(nodep->dtypep(), UnpackArrayDType); - m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, /* hashChildren: */ !dtypep, [=]() { - if (dtypep) { - const uint32_t size = dtypep->elementsConst(); - for (uint32_t n = 0; n < size; ++n) { // - iterateNull(nodep->getIndexDefaultedValuep(n)); - } + if (const AstAssocArrayDType* const dtypep = VN_CAST(nodep->dtypep(), AssocArrayDType)) { + if (nodep->defaultp()) { + m_hash + += hashNodeAndIterate(nodep->defaultp(), HASH_DTYPE, HASH_CHILDREN, [=]() {}); } - }); + const auto& mapr = nodep->map(); + for (const auto& itr : mapr) { // mapr is sorted, so hash should get stable results + m_hash += itr.first; + m_hash += hashNodeAndIterate(itr.second, HASH_DTYPE, HASH_CHILDREN, [=]() {}); + } + } else if (const AstUnpackArrayDType* const dtypep + = VN_CAST(nodep->dtypep(), UnpackArrayDType)) { + // Hash unpacked array initializers by value, as the order of initializer nodes does + // not matter, and we want semantically equivalent initializers to map to the same + // hash. + m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, /* hashChildren: */ !dtypep, [=]() { + if (dtypep) { + const uint32_t size = dtypep->elementsConst(); + for (uint32_t n = 0; n < size; ++n) { // + iterateNull(nodep->getIndexDefaultedValuep(n)); + } + } + }); + } } virtual void visit(AstPragma* nodep) override { m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { // diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 27197839d..32741ec96 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1675,20 +1675,30 @@ private: auto* const enumDtp = VN_AS(toDtp, EnumDType); UASSERT_OBJ(enumDtp, nodep, "$cast determined as enum, but not enum type"); const uint64_t maxval = enumMaxValue(nodep, enumDtp); - const int selwidth = V3Number::log2b(maxval) + 1; // Width to address a bit - AstVar* const varp - = enumVarp(enumDtp, AstAttrType::ENUM_VALID, (1ULL << selwidth) - 1); - AstVarRef* const varrefp = new AstVarRef(fl, varp, VAccess::READ); - varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp()); - FileLine* const fl_nowarn = new FileLine(fl); - fl_nowarn->warnOff(V3ErrorCode::WIDTH, true); - auto* const testp = new AstCond{ - fl, - new AstGt{fl_nowarn, nodep->fromp()->cloneTree(false), - new AstConst{fl_nowarn, AstConst::Unsized64{}, maxval}}, - new AstConst{fl, AstConst::BitFalse{}}, - new AstArraySel{fl, varrefp, - new AstSel{fl, nodep->fromp()->cloneTree(false), 0, selwidth}}}; + const bool assoc = maxval > ENUM_LOOKUP_BITS; + AstNode* testp = nullptr; + if (assoc) { + AstVar* const varp = enumVarp(enumDtp, AstAttrType::ENUM_VALID, true, 0); + AstVarRef* const varrefp = new AstVarRef{fl, varp, VAccess::READ}; + varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp()); + testp = new AstAssocSel{fl, varrefp, nodep->fromp()->cloneTree(false)}; + } else { + const int selwidth = V3Number::log2b(maxval) + 1; // Width to address a bit + AstVar* const varp + = enumVarp(enumDtp, AstAttrType::ENUM_VALID, false, (1ULL << selwidth) - 1); + AstVarRef* const varrefp = new AstVarRef(fl, varp, VAccess::READ); + varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp()); + FileLine* const fl_nowarn = new FileLine(fl); + fl_nowarn->warnOff(V3ErrorCode::WIDTH, true); + testp = new AstCond{ + fl, + new AstGt{fl_nowarn, nodep->fromp()->cloneTree(false), + new AstConst{fl_nowarn, AstConst::Unsized64{}, maxval}}, + new AstConst{fl, AstConst::BitFalse{}}, + new AstArraySel{ + fl, varrefp, + new AstSel{fl, nodep->fromp()->cloneTree(false), 0, selwidth}}}; + } newp = new AstCond{fl, testp, new AstExprStmt{fl, new AstAssign{fl, nodep->top()->unlinkFrBack(), @@ -2224,8 +2234,8 @@ private: AstNodeDType* const vdtypep = m_vup->dtypeNullp(); UASSERT_OBJ(vdtypep, nodep, "InitArray type not assigned by AstPattern/Var visitor"); nodep->dtypep(vdtypep); - if (const AstNodeArrayDType* const arrayp - = VN_CAST(vdtypep->skipRefp(), NodeArrayDType)) { + const AstNodeDType* const arrayp = vdtypep->skipRefp(); + if (VN_IS(arrayp, NodeArrayDType) || VN_IS(arrayp, AssocArrayDType)) { userIterateChildren(nodep, WidthVP(arrayp->subDTypep(), BOTH).p()); } else { UINFO(1, "dtype object " << vdtypep->skipRefp() << endl); @@ -2635,17 +2645,27 @@ private: } // Need a runtime lookup table. Yuk. const uint64_t msbdim = enumMaxValue(nodep, adtypep); - const int selwidth = V3Number::log2b(msbdim) + 1; // Width to address a bit - AstVar* const varp = enumVarp(adtypep, attrType, (1ULL << selwidth) - 1); - AstVarRef* const varrefp = new AstVarRef(nodep->fileline(), varp, VAccess::READ); - varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp()); - AstNode* const newp = new AstArraySel( - nodep->fileline(), varrefp, - // 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->fromp()->unlinkFrBack(), 0, selwidth)); - nodep->replaceWith(newp); + const bool assoc = msbdim > ENUM_LOOKUP_BITS; + if (assoc) { + AstVar* const varp = enumVarp(adtypep, attrType, true, 0); + AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), varp, VAccess::READ}; + varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp()); + AstNode* const newp + = new AstAssocSel{nodep->fileline(), varrefp, nodep->fromp()->unlinkFrBack()}; + nodep->replaceWith(newp); + } else { + const int selwidth = V3Number::log2b(msbdim) + 1; // Width to address a bit + AstVar* const varp = enumVarp(adtypep, attrType, false, (1ULL << selwidth) - 1); + AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), varp, VAccess::READ}; + varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp()); + AstNode* const newp = new AstArraySel( + nodep->fileline(), varrefp, + // 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->fromp()->unlinkFrBack(), 0, selwidth)); + nodep->replaceWith(newp); + } VL_DO_DANGLING(nodep->deleteTree(), nodep); } else { nodep->v3error("Unknown built-in enum method " << nodep->prettyNameQ()); @@ -5912,19 +5932,19 @@ private: UASSERT_OBJ(vconstp, errNodep, "Enum item without constified value"); if (vconstp->toUQuad() >= maxval) maxval = vconstp->toUQuad(); } - if (adtypep->itemsp()->width() > 64 || maxval >= (1 << ENUM_LOOKUP_BITS)) { + if (adtypep->itemsp()->width() > 64) { errNodep->v3warn(E_UNSUPPORTED, - "Unsupported: enum next/prev method on enum with > 10 bits"); - return ENUM_LOOKUP_BITS; + "Unsupported: enum next/prev/name method on enum with > 64 bits"); + return 64; } return maxval; } - AstVar* enumVarp(AstEnumDType* nodep, AstAttrType attrType, uint32_t msbdim) { + AstVar* enumVarp(AstEnumDType* nodep, AstAttrType attrType, bool assoc, uint32_t msbdim) { // Return a variable table which has specified dimension properties for this variable const auto pos = m_tableMap.find(std::make_pair(nodep, attrType)); if (pos != m_tableMap.end()) return pos->second; - UINFO(9, "Construct Venumtab attr=" << attrType.ascii() << " max=" << msbdim << " for " - << nodep << endl); + UINFO(9, "Construct Venumtab attr=" << attrType.ascii() << " assoc=" << assoc + << " max=" << msbdim << " for " << nodep << endl); AstNodeDType* basep; if (attrType == AstAttrType::ENUM_NAME) { basep = nodep->findStringDType(); @@ -5937,8 +5957,13 @@ private: } else { basep = nodep->dtypep(); } - AstNodeArrayDType* const vardtypep = new AstUnpackArrayDType( - nodep->fileline(), basep, new AstRange(nodep->fileline(), msbdim, 0)); + AstNodeDType* vardtypep; + if (assoc) { + vardtypep = new AstAssocArrayDType{nodep->fileline(), basep, nodep}; + } else { + vardtypep = new AstUnpackArrayDType{nodep->fileline(), basep, + new AstRange(nodep->fileline(), msbdim, 0)}; + } AstInitArray* const initp = new AstInitArray(nodep->fileline(), vardtypep, nullptr); v3Global.rootp()->typeTablep()->addTypesp(vardtypep); AstVar* const varp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP, @@ -5990,8 +6015,12 @@ private: } } // Add all specified values to table - for (vluint64_t i = 0; i < (msbdim + 1); ++i) { - if (values[i]) initp->addIndexValuep(i, values[i]); + if (assoc) { + for (const auto& itr : values) initp->addIndexValuep(itr.first, itr.second); + } else { + for (vluint64_t i = 0; i < (msbdim + 1); ++i) { + if (values[i]) initp->addIndexValuep(i, values[i]); + } } userIterate(varp, nullptr); // May have already done $unit so must do this var m_tableMap.emplace(std::make_pair(nodep, attrType), varp); diff --git a/test_regress/t/t_enum_huge_methods.pl b/test_regress/t/t_enum_huge_methods.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_enum_huge_methods.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_enum_huge_methods.v b/test_regress/t/t_enum_huge_methods.v new file mode 100644 index 000000000..499af6fde --- /dev/null +++ b/test_regress/t/t_enum_huge_methods.v @@ -0,0 +1,73 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2014 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`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); +`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); + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + typedef enum logic [59:0] { + E01 = 60'h1, + ELARGE = 60'h1234_4567_abcd + } my_t; + + integer cyc = 0; + my_t e; + + string all; + int i_cast; + + // Check runtime + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc == 0) begin + // Setup + e <= E01; + end + else if (cyc == 1) begin + `checks(e.name, "E01"); + `checkh(e.next, ELARGE); + e <= ELARGE; + end + else if (cyc == 3) begin + `checks(e.name, "ELARGE"); + `checkh(e.next, E01); + `checkh(e.prev, E01); + e <= E01; + end + // + else if (cyc == 10) begin + i_cast <= $cast(e, 60'h1234); + end + else if (cyc == 11) begin + `checkh(i_cast, 0); + i_cast <= $cast(e, 60'h1); + end + else if (cyc == 12) begin + `checkh(i_cast, 1); + i_cast <= $cast(e, 60'h1234_4567_abcd); + end + else if (cyc == 13) begin + `checkh(i_cast, 1); + end + // + else if (cyc == 20) begin + e <= 'h11; // Unknown + end + else if (cyc == 21) begin + `checks(e.name, ""); // Unknown + end + else if (cyc == 99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_enum_huge_methods_bad.out b/test_regress/t/t_enum_huge_methods_bad.out index cee01c470..885a3cd7f 100644 --- a/test_regress/t/t_enum_huge_methods_bad.out +++ b/test_regress/t/t_enum_huge_methods_bad.out @@ -1,7 +1,7 @@ %Error: t/t_enum_huge_methods_bad.v:15:11: Value too wide for 64-bits expected in this context 160'h12344567abcd12344567abcd 15 | ELARGE = 160'h1234_4567_abcd_1234_4567_abcd | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_enum_huge_methods_bad.v:30:14: Unsupported: enum next/prev method on enum with > 10 bits +%Error-UNSUPPORTED: t/t_enum_huge_methods_bad.v:30:14: Unsupported: enum next/prev/name method on enum with > 64 bits : ... In instance t 30 | $display(e.name); | ^~~~