diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index a6ebe193e..64c25e494 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -3235,8 +3235,7 @@ public: AstNodeModule* classOrPackagep() const { AstNode* foundp = m_classOrPackageNodep; while (auto* const anodep = VN_CAST(foundp, Typedef)) foundp = anodep->subDTypep(); - while (auto* const anodep = VN_CAST(foundp, ClassRefDType)) - foundp = anodep->classOrPackagep(); + if (auto* const anodep = VN_CAST(foundp, ClassRefDType)) foundp = anodep->classp(); return VN_CAST(foundp, NodeModule); } AstPackage* packagep() const { return VN_CAST(classOrPackageNodep(), Package); } diff --git a/src/V3Common.cpp b/src/V3Common.cpp index ad4ecaf6e..6f983809c 100644 --- a/src/V3Common.cpp +++ b/src/V3Common.cpp @@ -86,7 +86,7 @@ static void makeToStringMiddle(AstClass* nodep) { } } } - if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) { + if (nodep->extendsp()) { string stmt = "out += "; if (!comma.empty()) stmt += "\", \"+ "; // comma = ", "; // Nothing further so not needed diff --git a/src/V3Const.cpp b/src/V3Const.cpp index fc4555304..fb90cf866 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2379,6 +2379,7 @@ private: iterateChildren(nodep); } } + virtual void visit(AstClassOrPackageRef* nodep) override { iterateChildren(nodep); } virtual void visit(AstPin* nodep) override { iterateChildren(nodep); } void replaceLogEq(AstLogEq* nodep) { diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index ec0da2f09..03703c2c8 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -471,9 +471,6 @@ private: } } - // Accelerate the recursion - // Must do statements to support Generates, math though... - virtual void visit(AstNodeMath*) override {} virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } // METHODS diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 45e5a2038..f5dc1b59e 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1988,6 +1988,16 @@ private: } } + bool isParamedClassRef(const AstNode* nodep) { + if (const auto* classRefp = VN_CAST(nodep, ClassOrPackageRef)) { + if (classRefp->paramsp()) return true; + const auto* classp = classRefp->classOrPackageNodep(); + while (const auto* typedefp = VN_CAST(classp, Typedef)) classp = typedefp->subDTypep(); + return VN_IS(classp, ClassRefDType) && VN_AS(classp, ClassRefDType)->paramsp(); + } + return false; + } + // VISITs virtual void visit(AstNetlist* nodep) override { // Recurse..., backward as must do packages before using packages @@ -2172,11 +2182,17 @@ private: // if (!start) { nodep->lhsp()->v3error("Package reference may not be embedded in // dotted reference"); m_ds.m_dotErr=true; } m_ds.m_dotPos = DP_PACKAGE; + iterateAndNextNull(nodep->lhsp()); } else { m_ds.m_dotPos = DP_SCOPE; iterateAndNextNull(nodep->lhsp()); // if (debug() >= 9) nodep->dumpTree("-dot-lho: "); } + if (m_statep->forPrimary() && isParamedClassRef(nodep->lhsp())) { + // Dots of paramed classes will be linked after deparametrization + m_ds.m_dotPos = DP_NONE; + return; + } if (m_ds.m_unresolved && (VN_IS(nodep->lhsp(), CellRef) || VN_IS(nodep->lhsp(), CellArrayRef))) { m_ds.m_unlinkedScopep = nodep->lhsp(); @@ -2517,13 +2533,24 @@ private: if (start) m_ds = lastStates; } virtual void visit(AstClassOrPackageRef* nodep) override { - UINFO(9, " linkClassOrPackageRef " << m_ds.ascii() << " n=" << nodep << endl); - if (m_ds.m_dotPos == DP_PACKAGE) { - // Already under dot, so this is {ClassOrPackage} Dot {ClassOrPackage} - // m_ds.m_dotText communicates the cell prefix between stages - m_ds.m_dotPos = DP_PACKAGE; + // Class: Recurse inside or cleanup not founds + // checkNoDot not appropriate, can be under a dot + AstNode::user5ClearTree(); + UASSERT_OBJ(m_statep->forPrimary() || nodep->classOrPackagep(), nodep, + "ClassRef has unlinked class"); + UASSERT_OBJ(m_statep->forPrimary() || !nodep->paramsp(), nodep, + "class reference parameter not removed by V3Param"); + VL_RESTORER(m_ds); + VL_RESTORER(m_pinSymp); + { + // ClassRef's have pins, so track + if (nodep->classOrPackagep()) { + m_pinSymp = m_statep->getNodeSym(nodep->classOrPackagep()); + } + m_ds.init(m_curSymp); + UINFO(4, "(Backto) Link ClassOrPackageRef: " << nodep << endl); + iterateChildren(nodep); } - // TODO we don't iterate pins yet, as class parameters are not supported } virtual void visit(AstVarRef* nodep) override { diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 62aa66541..8c1595194 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -557,15 +557,16 @@ class ParamProcessor final { if ((newmodp->level() - srcModp->level()) >= (v3Global.opt.moduleRecursionDepth() - 2)) { cellp->v3error("Exceeded maximum --module-recursion-depth of " << v3Global.opt.moduleRecursionDepth()); + return; } // Keep tree sorted by level. Note: Different parametrizations of the same recursive module // end up with the same level, which we will need to fix up at the end, as we do not know // up front how recursive modules are expanded, and a later expansion might re-use an // earlier expansion (see t_recursive_module_bug_2). - AstNodeModule* insertp = srcModp; - while (insertp->nextp() + AstNode* insertp = srcModp; + while (VN_IS(insertp->nextp(), NodeModule) && VN_AS(insertp->nextp(), NodeModule)->level() <= newmodp->level()) { - insertp = VN_AS(insertp->nextp(), NodeModule); + insertp = insertp->nextp(); } insertp->addNextHere(newmodp); @@ -699,9 +700,9 @@ class ParamProcessor final { } } - void cellInterfaceCleanup(AstCell* nodep, AstNodeModule* srcModp, string& longnamer, + void cellInterfaceCleanup(AstPin* pinsp, AstNodeModule* srcModp, string& longnamer, bool& any_overridesr, IfaceRefRefs& ifaceRefRefs) { - for (AstPin* pinp = nodep->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) { + for (AstPin* pinp = pinsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) { const AstVar* const modvarp = pinp->modVarp(); if (modvarp->isIfaceRef()) { AstIfaceRefDType* portIrefp = VN_CAST(modvarp->subDTypep(), IfaceRefDType); @@ -761,8 +762,73 @@ class ParamProcessor final { } } + bool nodeDeparamCommon(AstNode* nodep, AstNodeModule*& srcModpr, AstPin* paramsp, + AstPin* pinsp, bool any_overrides) { + // Make sure constification worked + // Must be a separate loop, as constant conversion may have changed some pointers. + // if (debug()) nodep->dumpTree(cout, "-cel2: "); + string longname = srcModpr->name() + "_"; + if (debug() > 8 && paramsp) paramsp->dumpTreeAndNext(cout, "-cellparams: "); + + if (srcModpr->hierBlock()) { + longname = parameterizedHierBlockName(srcModpr, paramsp); + any_overrides = longname != srcModpr->name(); + } else { + for (AstPin* pinp = paramsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) { + cellPinCleanup(nodep, pinp, srcModpr, longname /*ref*/, any_overrides /*ref*/); + } + } + IfaceRefRefs ifaceRefRefs; + cellInterfaceCleanup(pinsp, srcModpr, longname /*ref*/, any_overrides /*ref*/, + ifaceRefRefs /*ref*/); + + if (!any_overrides) { + UINFO(8, "Cell parameters all match original values, skipping expansion.\n"); + } else if (AstNodeModule* const paramedModp + = m_hierBlocks.findByParams(srcModpr->name(), paramsp, m_modp)) { + paramedModp->dead(false); + // We need to relink the pins to the new module + relinkPinsByName(pinsp, paramedModp); + srcModpr = paramedModp; + } else { + const string newname + = srcModpr->hierBlock() ? longname : moduleCalcName(srcModpr, longname); + const ModInfo* const modInfop + = moduleFindOrClone(srcModpr, nodep, paramsp, newname, ifaceRefRefs); + // We need to relink the pins to the new module + relinkPinsByName(pinsp, modInfop->m_modp); + UINFO(8, " Done with " << modInfop->m_modp << endl); + srcModpr = modInfop->m_modp; + } + + // Delete the parameters from the cell; they're not relevant any longer. + if (paramsp) paramsp->unlinkFrBackWithNext()->deleteTree(); + return any_overrides; + } + + void cellDeparam(AstCell* nodep, AstNodeModule*& srcModpr) { + // Must always clone __Vrcm (recursive modules) + if (nodeDeparamCommon(nodep, srcModpr, nodep->paramsp(), nodep->pinsp(), + nodep->recursive())) { + nodep->modp(srcModpr); + nodep->modName(srcModpr->name()); + } + nodep->recursive(false); + } + + void classRefDeparam(AstClassOrPackageRef* nodep, AstNodeModule*& srcModpr) { + if (nodeDeparamCommon(nodep, srcModpr, nodep->paramsp(), nullptr, false)) + nodep->classOrPackagep(srcModpr); + } + + void classRefDeparam(AstClassRefDType* nodep, AstNodeModule*& srcModpr) { + if (nodeDeparamCommon(nodep, srcModpr, nodep->paramsp(), nullptr, false)) + nodep->classp(VN_AS(srcModpr, Class)); + } + public: - void cellDeparam(AstCell* nodep, AstNodeModule* modp, const string& someInstanceName) { + void nodeDeparam(AstNode* nodep, AstNodeModule*& srcModpr, AstNodeModule* modp, + const string& someInstanceName) { m_modp = modp; // Cell: Check for parameters in the instantiation. // We always run this, even if no parameters, as need to look for interfaces, @@ -772,57 +838,18 @@ public: if (debug() >= 10) nodep->dumpTree(cout, "-cell: "); // Evaluate all module constants V3Const::constifyParamsEdit(nodep); - AstNodeModule* const srcModp = nodep->modp(); - srcModp->someInstanceName(someInstanceName + "." + nodep->name()); + srcModpr->someInstanceName(someInstanceName + "." + nodep->name()); - // Make sure constification worked - // Must be a separate loop, as constant conversion may have changed some pointers. - // if (debug()) nodep->dumpTree(cout, "-cel2: "); - string longname = srcModp->name() + "_"; - bool any_overrides = false; - // Must always clone __Vrcm (recursive modules) - if (nodep->recursive()) any_overrides = true; - if (debug() > 8 && nodep->paramsp()) - nodep->paramsp()->dumpTreeAndNext(cout, "-cellparams: "); - - if (srcModp->hierBlock()) { - longname = parameterizedHierBlockName(srcModp, nodep->paramsp()); - any_overrides = longname != srcModp->name(); + if (auto* cellp = VN_CAST(nodep, Cell)) { + cellDeparam(cellp, srcModpr); + } else if (auto* classRefp = VN_CAST(nodep, ClassRefDType)) { + classRefDeparam(classRefp, srcModpr); + } else if (auto* classRefp = VN_CAST(nodep, ClassOrPackageRef)) { + classRefDeparam(classRefp, srcModpr); } else { - for (AstPin* pinp = nodep->paramsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) { - cellPinCleanup(nodep, pinp, srcModp, longname /*ref*/, any_overrides /*ref*/); - } - } - IfaceRefRefs ifaceRefRefs; - cellInterfaceCleanup(nodep, srcModp, longname /*ref*/, any_overrides /*ref*/, - ifaceRefRefs /*ref*/); - - if (!any_overrides) { - UINFO(8, "Cell parameters all match original values, skipping expansion.\n"); - } else if (AstNodeModule* const paramedModp - = m_hierBlocks.findByParams(srcModp->name(), nodep->paramsp(), m_modp)) { - nodep->modp(paramedModp); - nodep->modName(paramedModp->name()); - paramedModp->dead(false); - // We need to relink the pins to the new module - relinkPinsByName(nodep->pinsp(), paramedModp); - } else { - const string newname - = srcModp->hierBlock() ? longname : moduleCalcName(srcModp, longname); - const ModInfo* const modInfop - = moduleFindOrClone(srcModp, nodep, nodep->paramsp(), newname, ifaceRefRefs); - // Have child use this module instead. - nodep->modp(modInfop->m_modp); - nodep->modName(newname); - // We need to relink the pins to the new module - relinkPinsByName(nodep->pinsp(), modInfop->m_modp); - UINFO(8, " Done with " << modInfop->m_modp << endl); + nodep->v3fatalSrc("Expected module parametrization"); } - nodep->recursive(false); - - // Delete the parameters from the cell; they're not relevant any longer. - if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree(); UINFO(8, " Done with " << nodep << endl); // if (debug() >= 10) // v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param-out.tree")); @@ -854,7 +881,8 @@ class ParamVisitor final : public VNVisitor { bool m_iterateModule = false; // Iterating module body string m_generateHierName; // Generate portion of hierarchy name string m_unlinkedTxt; // Text for AstUnlinkedRef - std::deque m_cellps; // Cells left to process (in current module) + std::multimap m_cellps; // Cells left to process (in current module) + std::multimap m_workQueue; // Modules left to process // Map from AstNodeModule to set of all AstNodeModules that instantiates it. std::unordered_map> m_parentps; @@ -887,32 +915,40 @@ class ParamVisitor final : public VNVisitor { // Process interface cells, then non-interface cells, which may reference an interface // cell. - for (bool doInterface : {true, false}) { - for (AstCell* const cellp : m_cellps) { - if (doInterface != VN_IS(cellp->modp(), Iface)) continue; + while (!m_cellps.empty()) { + const auto itm = m_cellps.cbegin(); + AstNode* const cellp = itm->second; + m_cellps.erase(itm); - // Visit parameters in the instantiation. - iterateChildren(cellp); - - // Update path - string someInstanceName(modp->someInstanceName()); - if (const string* const genHierNamep = cellp->user5u().to()) { - someInstanceName += *genHierNamep; - cellp->user5p(nullptr); - VL_DO_DANGLING(delete genHierNamep, genHierNamep); - } - - // Apply parameter specialization - m_processor.cellDeparam(cellp, modp, someInstanceName); - - // Add the (now potentially specialized) child module to the work queue - workQueue.emplace(cellp->modp()->level(), cellp->modp()); - - // Add to the hierarchy registry - m_parentps[cellp->modp()].insert(modp); + AstNodeModule* srcModp = nullptr; + if (const auto* modCellp = VN_CAST(cellp, Cell)) { + srcModp = modCellp->modp(); + } else if (const auto* classRefp = VN_CAST(cellp, ClassOrPackageRef)) { + srcModp = classRefp->classOrPackagep(); + } else if (const auto* classRefp = VN_CAST(cellp, ClassRefDType)) { + srcModp = classRefp->classp(); + } else { + cellp->v3fatalSrc("Expected module parametrization"); } + + // Update path + string someInstanceName(modp->someInstanceName()); + if (const string* const genHierNamep = cellp->user5u().to()) { + someInstanceName += *genHierNamep; + cellp->user5p(nullptr); + VL_DO_DANGLING(delete genHierNamep, genHierNamep); + } + + // Apply parameter specialization + m_processor.nodeDeparam(cellp, srcModp /* ref */, modp, someInstanceName); + + // Add the (now potentially specialized) child module to the work queue + workQueue.emplace(srcModp->level(), srcModp); + + // Add to the hierarchy registry + m_parentps[srcModp].insert(modp); } - m_cellps.clear(); + if (workQueue.empty()) std::swap(workQueue, m_workQueue); } while (!workQueue.empty()); m_iterateModule = false; @@ -930,25 +966,24 @@ class ParamVisitor final : public VNVisitor { if (modp->level() <= maxParentLevel) modp->level(maxParentLevel + 1); } + // A generic visitor for cells and class refs + void visitCellOrClassRef(AstNode* nodep, bool isIface) { + // Must do ifaces first, so push to list and do in proper order + string* const genHierNamep = new string{m_generateHierName}; + nodep->user5p(genHierNamep); + // Visit parameters in the instantiation. + iterateChildren(nodep); + m_cellps.emplace(!isIface, nodep); + } + // VISITORS virtual void visit(AstNodeModule* nodep) override { if (nodep->recursiveClone()) nodep->dead(true); // Fake, made for recursive elimination if (nodep->dead()) return; // Marked by LinkDot (and above) - // Warn on unsupported parametrised class - if (VN_IS(nodep, Class)) { - for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { - if (const AstVar* const varp = VN_CAST(stmtp, Var)) { - if (varp->isParam()) { - varp->v3warn(E_UNSUPPORTED, "Unsupported: class parameters"); - } - } - } - } - if (m_iterateModule) { // Iterating body UINFO(4, " MOD-under-MOD. " << nodep << endl); - iterateChildren(nodep); + m_workQueue.emplace(nodep->level(), nodep); // Delay until current module is done return; } @@ -961,19 +996,10 @@ class ParamVisitor final : public VNVisitor { } virtual void visit(AstCell* nodep) override { - // Must do ifaces first, so push to list and do in proper order - string* const genHierNamep = new string(m_generateHierName); - nodep->user5p(genHierNamep); - m_cellps.push_back(nodep); - } - - virtual void visit(AstClassRefDType* nodep) override { - if (nodep->paramsp()) { - nodep->paramsp()->v3warn(E_UNSUPPORTED, "Unsupported: parameterized classes"); - pushDeletep(nodep->paramsp()->unlinkFrBackWithNext()); - } - iterateChildren(nodep); + visitCellOrClassRef(nodep, VN_IS(nodep->modp(), Iface)); } + virtual void visit(AstClassRefDType* nodep) override { visitCellOrClassRef(nodep, false); } + virtual void visit(AstClassOrPackageRef* nodep) override { visitCellOrClassRef(nodep, false); } // Make sure all parameters are constantified virtual void visit(AstVar* nodep) override { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 824d66beb..a9f784e06 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2438,6 +2438,17 @@ private: // though causes problems with t_class_forward.v, so for now avoided // userIterateChildren(nodep->classp(), nullptr); } + virtual void visit(AstClassOrPackageRef* nodep) override { + if (nodep->didWidthAndSet()) return; + userIterateChildren(nodep, nullptr); + } + virtual void visit(AstDot* nodep) override { + // We can only reach this from constify called during V3Param (so before linkDotParam) + // ... #(Cls#(...)::...) ... + // ^^~~~ this is our DOT + nodep->v3warn(E_UNSUPPORTED, "dotted expressions in parameters\n" + << nodep->warnMore() << "... Suggest use a typedef"); + } virtual void visit(AstClassExtends* nodep) override { if (nodep->didWidthAndSet()) return; if (VN_IS(nodep->childDTypep(), ClassRefDType)) { diff --git a/test_regress/t/t_class_param.out b/test_regress/t/t_class_param.out deleted file mode 100644 index 442cfc01e..000000000 --- a/test_regress/t/t_class_param.out +++ /dev/null @@ -1,30 +0,0 @@ -%Error-UNSUPPORTED: t/t_class_param.v:40:11: Unsupported: parameterized classes - : ... In instance t - 40 | Cls #(.PBASE(4)) c4; - | ^~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_class_param.v:42:12: Unsupported: parameterized classes - : ... In instance t - 42 | Wrap #(.P(16)) w16; - | ^ -%Error-UNSUPPORTED: t/t_class_param.v:13:24: Unsupported: class parameters - : ... In instance t - 13 | class Wrap #(parameter P = 13); - | ^ -%Error-UNSUPPORTED: t/t_class_param.v:21:15: Unsupported: class parameters - : ... In instance t - 21 | localparam PMINUS1 = P - 1; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_class_param.v:20:17: Unsupported: parameterized classes - : ... In instance t - 20 | Cls#(PMINUS1 + 1) c1; - | ^ -%Error-UNSUPPORTED: t/t_class_param.v:24:23: Unsupported: class parameters - : ... In instance t - 24 | class Cls #(parameter PBASE = 12); - | ^~~~~ -%Error-UNSUPPORTED: t/t_class_param.v:35:14: Unsupported: parameterized classes - : ... In instance t - 35 | typedef Cls#(8) Cls8_t; - | ^ -%Error: Exiting due to diff --git a/test_regress/t/t_class_param.pl b/test_regress/t/t_class_param.pl index 2ad4a887d..aabcde63e 100755 --- a/test_regress/t/t_class_param.pl +++ b/test_regress/t/t_class_param.pl @@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - fails => $Self->{vlt_all}, - expect_filename => $Self->{golden_filename}, ); execute( check_finished => 1, - ) if !$Self->{vlt_all}; + ); ok(1); 1; diff --git a/test_regress/t/t_class_param.v b/test_regress/t/t_class_param.v index 0fc61cb76..99740abb7 100644 --- a/test_regress/t/t_class_param.v +++ b/test_regress/t/t_class_param.v @@ -21,6 +21,17 @@ class Wrap #(parameter P = 13); localparam PMINUS1 = P - 1; // Checking works when last endclass +class Wrap2 #(parameter P = 35); + function int get_p; + return c1.get_p(); + endfunction + function new; + c1 = new; + endfunction + Wrap#(PMINUS1 + 1) c1; + localparam PMINUS1 = P - 1; // Checking works when last +endclass + class Cls #(parameter PBASE = 12); bit [PBASE-1:0] member; function bit [PBASE-1:0] get_member; @@ -40,11 +51,13 @@ module t (/*AUTOARG*/); Cls #(.PBASE(4)) c4; Cls8_t c8; Wrap #(.P(16)) w16; + Wrap2 #(.P(32)) w32; initial begin c12 = new; c4 = new; c8 = new; w16 = new; + w32 = new; if (Cls#()::PBASE != 12) $stop; if (Cls#(4)::PBASE != 4) $stop; if (Cls8_t::PBASE != 8) $stop; @@ -65,6 +78,7 @@ module t (/*AUTOARG*/); if (c4.get_p() != 4) $stop; if (c8.get_p() != 8) $stop; if (w16.get_p() != 16) $stop; + if (w32.get_p() != 32) $stop; // verilator lint_off WIDTH c12.member = 32'haaaaaaaa; diff --git a/test_regress/t/t_class_param_circ_bad.out b/test_regress/t/t_class_param_circ_bad.out new file mode 100644 index 000000000..35b6ce09a --- /dev/null +++ b/test_regress/t/t_class_param_circ_bad.out @@ -0,0 +1,5 @@ +%Error: t/t_class_param_circ_bad.v:14:4: Exceeded maximum --module-recursion-depth of 100 + : ... In instance t + 14 | ClsA #(PARAM+1) a; + | ^~~~ +%Error: Internal Error: ../V3Param.cpp:#: should find just-made module diff --git a/test_regress/t/t_class_param_circ_bad.pl b/test_regress/t/t_class_param_circ_bad.pl new file mode 100755 index 000000000..c35c8bc93 --- /dev/null +++ b/test_regress/t/t_class_param_circ_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 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); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_param_circ_bad.v b/test_regress/t/t_class_param_circ_bad.v new file mode 100644 index 000000000..3ebe10df8 --- /dev/null +++ b/test_regress/t/t_class_param_circ_bad.v @@ -0,0 +1,21 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +typedef class ClsB; + +class ClsA #(parameter PARAM = 12); + ClsB #(PARAM+1) b; +endclass + +class ClsB #(parameter PARAM = 12); + ClsA #(PARAM+1) a; +endclass + +module t (/*AUTOARG*/); + + ClsA #(.PARAM(15)) c; // Bad param name + +endmodule diff --git a/test_regress/t/t_class_param_extends.pl b/test_regress/t/t_class_param_extends.pl new file mode 100755 index 000000000..1aa73f80a --- /dev/null +++ b/test_regress/t/t_class_param_extends.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 2022 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_class_param_extends.v b/test_regress/t/t_class_param_extends.v new file mode 100644 index 000000000..1c1078ea7 --- /dev/null +++ b/test_regress/t/t_class_param_extends.v @@ -0,0 +1,52 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +// Code your testbench here +// or browse Examples +class Base #(parameter PBASE = 12); + bit [PBASE-1:0] member; + function bit [PBASE-1:0] get_member; + return member; + endfunction + function int get_p; + return PBASE; + endfunction +endclass + +class Cls #(parameter P = 13) extends Base #(P); +endclass + +typedef Cls#(8) Cls8_t; + +// See also t_class_param_mod.v + +module t (/*AUTOARG*/); + + Cls #(.P(4)) c4; + Cls8_t c8; + + initial begin + c4 = new; + c8 = new; + if (c4.PBASE != 4) $stop; + if (c8.PBASE != 8) $stop; + if (c4.get_p() != 4) $stop; + if (c8.get_p() != 8) $stop; + // verilator lint_off WIDTH + c4.member = 32'haaaaaaaa; + c8.member = 32'haaaaaaaa; + // verilator lint_on WIDTH + if (c4.member != 4'ha) $stop; + if (c4.get_member() != 4'ha) $stop; + if (c8.member != 8'haa) $stop; + if (c8.get_member() != 8'haa) $stop; + $display("c4 = %s", $sformatf("%p", c4)); + if ($sformatf("%p", c4) != "'{member:'ha}") $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_param_mod.pl b/test_regress/t/t_class_param_mod.pl index a2ed99042..aabcde63e 100755 --- a/test_regress/t/t_class_param_mod.pl +++ b/test_regress/t/t_class_param_mod.pl @@ -11,12 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - fails => $Self->{vlt_all}, ); execute( check_finished => 1, - ) if !$Self->{vlt_all}; + ); ok(1); 1; diff --git a/test_regress/t/t_class_param_mod.v b/test_regress/t/t_class_param_mod.v index 67065e2d7..a57ba7316 100644 --- a/test_regress/t/t_class_param_mod.v +++ b/test_regress/t/t_class_param_mod.v @@ -32,17 +32,30 @@ class Wrap #(parameter P = 13); localparam PMINUS1 = P - 1; // Checking works when last endclass +class Wrap2 #(parameter P = 35); + function int get_p; + return c1.get_p(); + endfunction + function new; + c1 = new; + endfunction + Wrap#(PMINUS1 + 1) c1; + localparam PMINUS1 = P - 1; // Checking works when last +endclass + typedef Cls#(8) Cls8_t; Cls c12; Cls #(.PBASE(4)) c4; Cls8_t c8; Wrap #(.P(16)) w16; + Wrap2 #(.P(32)) w32; initial begin c12 = new; c4 = new; c8 = new; w16 = new; + w32 = new; if (Cls#()::PBASE != 12) $stop; if (Cls#(4)::PBASE != 4) $stop; if (Cls8_t::PBASE != 8) $stop; @@ -63,6 +76,7 @@ endclass if (c4.get_p() != 4) $stop; if (c8.get_p() != 8) $stop; if (w16.get_p() != 16) $stop; + if (w32.get_p() != 32) $stop; // verilator lint_off WIDTH c12.member = 32'haaaaaaaa; diff --git a/test_regress/t/t_class_param_nconst_bad.out b/test_regress/t/t_class_param_nconst_bad.out index 100f930d8..812399530 100644 --- a/test_regress/t/t_class_param_nconst_bad.out +++ b/test_regress/t/t_class_param_nconst_bad.out @@ -1,10 +1,9 @@ -%Error-UNSUPPORTED: t/t_class_param_nconst_bad.v:12:11: Unsupported: parameterized classes - : ... In instance t +%Error: t/t_class_param_nconst_bad.v:12:17: Expecting expression to be constant, but can't convert a RAND to constant. + : ... In instance t + 12 | Cls #(.PARAM($random)) c; + | ^~~~~~~ +%Error: t/t_class_param_nconst_bad.v:12:11: Can't convert defparam value to constant: Param 'PARAM' of 'Cls' + : ... In instance t 12 | Cls #(.PARAM($random)) c; | ^~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_class_param_nconst_bad.v:7:23: Unsupported: class parameters - : ... In instance t - 7 | class Cls #(parameter PARAM = 12); - | ^~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_class_param_nested_bad.out b/test_regress/t/t_class_param_nested_bad.out new file mode 100644 index 000000000..ce4a30bc0 --- /dev/null +++ b/test_regress/t/t_class_param_nested_bad.out @@ -0,0 +1,10 @@ +%Error-UNSUPPORTED: t/t_class_param_nested_bad.v:51:23: dotted expressions in parameters + : ... In instance t + : ... Suggest use a typedef + 51 | Wrap2 #(Wrap#(19)::PBASE * 2) w38; + | ^~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Internal Error: t/t_class_param_nested_bad.v:51:29: ../V3Width.cpp:#: Node has no type + : ... In instance t + 51 | Wrap2 #(Wrap#(19)::PBASE * 2) w38; + | ^ diff --git a/test_regress/t/t_class_param_nested_bad.pl b/test_regress/t/t_class_param_nested_bad.pl new file mode 100755 index 000000000..c35c8bc93 --- /dev/null +++ b/test_regress/t/t_class_param_nested_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 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); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_param_nested_bad.v b/test_regress/t/t_class_param_nested_bad.v new file mode 100644 index 000000000..ec65b3e09 --- /dev/null +++ b/test_regress/t/t_class_param_nested_bad.v @@ -0,0 +1,64 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Arkadiusz Kozdra. +// SPDX-License-Identifier: CC0-1.0 + +typedef class Cls; + +class Wrap #(parameter P = 13); + function int get_p; + return c1.get_p(); + endfunction + function new; + c1 = new; + endfunction + Cls#(PMINUS1 + 1) c1; + localparam PMINUS1 = P - 1; // Checking works when last +endclass + +class Wrap2 #(parameter P = 35); + function int get_p; + return c1.get_p(); + endfunction + function new; + c1 = new; + endfunction + Wrap#(PMINUS1 + 1) c1; + localparam PMINUS1 = P - 1; // Checking works when last +endclass + +class Cls #(parameter PBASE = 12); + bit [PBASE-1:0] member; + function bit [PBASE-1:0] get_member; + return member; + endfunction + static function int get_p; + return PBASE; + endfunction + typedef enum { E_PBASE = PBASE } enum_t; +endclass + +typedef Cls#(8) Cls8_t; + +module t (/*AUTOARG*/); + + Cls c12; + Cls #(.PBASE(4)) c4; + Cls8_t c8; + Wrap #(.P(16)) w16; + Wrap2 #(.P(32)) w32; + Wrap2 #(Wrap#(19)::PBASE * 2) w38; + initial begin + c12 = new; + c4 = new; + c8 = new; + w16 = new; + w32 = new; + w38 = new; + if (w38.get_p() != 38) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_param_pkg.out b/test_regress/t/t_class_param_pkg.out deleted file mode 100644 index a01b5aed6..000000000 --- a/test_regress/t/t_class_param_pkg.out +++ /dev/null @@ -1,30 +0,0 @@ -%Error-UNSUPPORTED: t/t_class_param_pkg.v:43:16: Unsupported: parameterized classes - : ... In instance t - 43 | Pkg::Cls #(.PBASE(4)) c4; - | ^~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_class_param_pkg.v:45:17: Unsupported: parameterized classes - : ... In instance t - 45 | Pkg::Wrap #(.P(16)) w16; - | ^ -%Error-UNSUPPORTED: t/t_class_param_pkg.v:14:27: Unsupported: class parameters - : ... In instance t - 14 | class Wrap #(parameter P = 13); - | ^ -%Error-UNSUPPORTED: t/t_class_param_pkg.v:22:18: Unsupported: class parameters - : ... In instance t - 22 | localparam PMINUS1 = P - 1; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_class_param_pkg.v:21:20: Unsupported: parameterized classes - : ... In instance t - 21 | Cls#(PMINUS1 + 1) c1; - | ^ -%Error-UNSUPPORTED: t/t_class_param_pkg.v:25:26: Unsupported: class parameters - : ... In instance t - 25 | class Cls #(parameter PBASE = 12); - | ^~~~~ -%Error-UNSUPPORTED: t/t_class_param_pkg.v:36:22: Unsupported: parameterized classes - : ... In instance t - 36 | typedef Pkg::Cls#(8) Cls8_t; - | ^ -%Error: Exiting due to diff --git a/test_regress/t/t_class_param_pkg.pl b/test_regress/t/t_class_param_pkg.pl index 2ad4a887d..aabcde63e 100755 --- a/test_regress/t/t_class_param_pkg.pl +++ b/test_regress/t/t_class_param_pkg.pl @@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - fails => $Self->{vlt_all}, - expect_filename => $Self->{golden_filename}, ); execute( check_finished => 1, - ) if !$Self->{vlt_all}; + ); ok(1); 1; diff --git a/test_regress/t/t_class_param_pkg.v b/test_regress/t/t_class_param_pkg.v index 4c53244f8..e311756c2 100644 --- a/test_regress/t/t_class_param_pkg.v +++ b/test_regress/t/t_class_param_pkg.v @@ -22,6 +22,17 @@ package Pkg; localparam PMINUS1 = P - 1; // Checking works when last endclass + class Wrap2 #(parameter P = 35); + function int get_p; + return c1.get_p(); + endfunction + function new; + c1 = new; + endfunction + Wrap#(PMINUS1 + 1) c1; + localparam PMINUS1 = P - 1; // Checking works when last + endclass + class Cls #(parameter PBASE = 12); bit [PBASE-1:0] member; function bit [PBASE-1:0] get_member; @@ -43,11 +54,13 @@ module t (/*AUTOARG*/); Pkg::Cls #(.PBASE(4)) c4; Pkg::Cls8_t c8; Pkg::Wrap #(.P(16)) w16; + Pkg::Wrap2 #(.P(32)) w32; initial begin c12 = new; c4 = new; c8 = new; w16 = new; + w32 = new; if (Pkg::Cls#()::PBASE != 12) $stop; if (Pkg::Cls#(4)::PBASE != 4) $stop; if (Pkg::Cls8_t::PBASE != 8) $stop; @@ -68,6 +81,7 @@ module t (/*AUTOARG*/); if (c4.get_p() != 4) $stop; if (c8.get_p() != 8) $stop; if (w16.get_p() != 16) $stop; + if (w32.get_p() != 32) $stop; // verilator lint_off WIDTH c12.member = 32'haaaaaaaa; diff --git a/test_regress/t/t_class_vparam.out b/test_regress/t/t_class_vparam.out deleted file mode 100644 index 7c4a4ba3d..000000000 --- a/test_regress/t/t_class_vparam.out +++ /dev/null @@ -1,18 +0,0 @@ -%Error-UNSUPPORTED: t/t_class_vparam.v:11:26: Unsupported: parameterized classes - : ... In instance t - 11 | typedef paramed_class_t#(real, 1) paramed_class_double_t; - | ^~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_class_vparam.v:13:56: Unsupported: class parameters - : ... In instance t - 13 | virtual class vclass #(type CTYPE_t = arg_class_t, int I = 0); - | ^ -%Error-UNSUPPORTED: t/t_class_vparam.v:14:58: Unsupported: parameterized classes - : ... In instance t - 14 | pure virtual function void funcname(paramed_class_t #(CTYPE_t) v); - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_class_vparam.v:17:46: Unsupported: class parameters - : ... In instance t - 17 | class paramed_class_t #(type TYPE = int, int I = 0); - | ^ -%Error: Exiting due to diff --git a/test_regress/t/t_class_vparam.pl b/test_regress/t/t_class_vparam.pl index c6e10f70b..c81275cdf 100755 --- a/test_regress/t/t_class_vparam.pl +++ b/test_regress/t/t_class_vparam.pl @@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - fails => $Self->{vlt_all}, - expect_filename => $Self->{golden_filename}, ); execute( check_finished => 1, - ) if !$Self->{vlt_all}; + ); ok(1); 1; diff --git a/test_regress/t/t_class_vparam.v b/test_regress/t/t_class_vparam.v index 9f19c609a..660dbb5fa 100644 --- a/test_regress/t/t_class_vparam.v +++ b/test_regress/t/t_class_vparam.v @@ -8,16 +8,18 @@ typedef class paramed_class_t; typedef class arg_class_t; -typedef paramed_class_t#(real, 1) paramed_class_double_t; +typedef paramed_class_t#(logic[3:0], 1) paramed_class_logic4_t; virtual class vclass #(type CTYPE_t = arg_class_t, int I = 0); pure virtual function void funcname(paramed_class_t #(CTYPE_t) v); endclass class paramed_class_t #(type TYPE = int, int I = 0); + TYPE memb; endclass class arg_class_t; + int ifield; endclass module t (/*AUTOARG*/ @@ -26,7 +28,15 @@ module t (/*AUTOARG*/ ); input clk; - always @ (posedge clk) begin + vclass vir; + paramed_class_t#(arg_class_t) argu; + + initial begin + argu = new; + argu.memb = new; + argu.memb.ifield = 1234; + // vir.funcname(argu); + if (argu.memb.ifield != 1234) $stop; $write("*-* All Finished *-*\n"); $finish; end