diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 6b4ecb82d..2398d9795 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1689,7 +1689,7 @@ class LinkDotFindVisitor final : public VNVisitor { "'import::*' in $unit scope may pollute global namespace"); } } - } else { + } else if (!nodep->resolvedClassp()) { VSymEnt* const impp = srcp->findIdFlat(nodep->name()); if (!impp) { nodep->v3error("Import object not found: " << nodep->prettyPkgNameQ()); diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 65842af76..b070261f9 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -280,6 +280,9 @@ class ParamProcessor final { // Default parameter values of hierarchical blocks std::map m_defaultParameterValues; VNDeleter m_deleter; // Used to delay deletion of nodes + // Class default paramater dependencies + std::vector> m_classParams; + std::unordered_map m_paramIndex; // METHODS @@ -929,6 +932,102 @@ class ParamProcessor final { UASSERT(paramspMap.empty(), "Not every generic interface implicit param is used"); } + void resolveDefaultParams(AstNode* nodep) { + AstClassOrPackageRef* classOrPackageRef = VN_CAST(nodep, ClassOrPackageRef); + AstClassRefDType* classRefDType = VN_CAST(nodep, ClassRefDType); + AstClass* classp = nullptr; + AstPin* paramsp = nullptr; + + if (classOrPackageRef) { + classp = VN_CAST(classOrPackageRef->classOrPackageSkipp(), Class); + if (!classp) return; // No parameters in packages + paramsp = classOrPackageRef->paramsp(); + } else if (classRefDType) { + classp = classRefDType->classp(); + paramsp = classRefDType->paramsp(); + } else { + nodep->v3fatalSrc("resolveDefaultParams called on node which is not a Class ref"); + } + + UASSERT_OBJ(classp, nodep, "Class or interface ref has no classp/ifacep"); + + // Get the parameter list for this class + m_classParams.clear(); + m_paramIndex.clear(); + for (AstNode* stmtp = classp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + if (AstParamTypeDType* paramTypep = VN_CAST(stmtp, ParamTypeDType)) { + m_paramIndex.emplace(paramTypep, m_classParams.size()); + m_classParams.emplace_back(paramTypep, -1); + } + } + + // For each parameter, detect either a dependent default (copy from previous param) + // or a direct type default (for ex. int). Store dependency index in + // m_classParams[i].second, default type in defaultTypeNodes[i]. + std::vector defaultTypeNodes(m_classParams.size(), nullptr); + for (size_t i = 0; i < m_classParams.size(); ++i) { + AstParamTypeDType* const paramTypep = m_classParams[i].first; + // Parser places defaults/constraints under childDTypep as AstRequireDType + AstRequireDType* const reqDtp = VN_CAST(paramTypep->getChildDTypep(), RequireDType); + if (!reqDtp) continue; + + // If default is a reference to another param type, record dependency + if (AstRefDType* const refDtp = VN_CAST(reqDtp->subDTypep(), RefDType)) { + if (AstParamTypeDType* const sourceParamp + = VN_CAST(refDtp->refDTypep(), ParamTypeDType)) { + auto it = m_paramIndex.find(sourceParamp); + if (it != m_paramIndex.end()) { m_classParams[i].second = it->second; } + continue; // dependency handled + } + } + + // If default is a direct type (for ex. int) + // also record dependency + if (AstNodeDType* const dtp = VN_CAST(reqDtp->lhsp(), NodeDType)) { + defaultTypeNodes[i] = dtp; + } + } + + // Count existing pins and capture them by index for easy lookup + std::vector pinsByIndex; + for (AstPin* pinp = paramsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) { + pinsByIndex.push_back(pinp); + } + + // For each missing parameter, get its pin from dependency or direct default + for (size_t paramIdx = pinsByIndex.size(); paramIdx < m_classParams.size(); paramIdx++) { + const int sourceParamIdx = m_classParams[paramIdx].second; + + AstPin* newPin = nullptr; + + // Case 1: Dependent default -> clone the source pin's type + if (sourceParamIdx >= 0) { newPin = pinsByIndex[sourceParamIdx]->cloneTree(false); } + + // Case 2: Direct default type (e.g., int), create a new pin with that dtype + if (!newPin && defaultTypeNodes[paramIdx]) { + AstNodeDType* const dtypep = defaultTypeNodes[paramIdx]; + newPin = new AstPin{dtypep->fileline(), static_cast(paramIdx) + 1, + "__paramNumber" + cvtToStr(paramIdx + 1), + dtypep->cloneTree(false)}; + } + + if (newPin) { + newPin->name("__paramNumber" + cvtToStr(paramIdx + 1)); + newPin->param(true); + newPin->modPTypep(m_classParams[paramIdx].first); + if (classOrPackageRef) { + classOrPackageRef->addParamsp(newPin); + } else if (classRefDType) { + classRefDType->addParamsp(newPin); + } + // Update local tracking so future dependent defaults can find it + pinsByIndex.resize(paramIdx + 1, nullptr); + pinsByIndex[paramIdx] = newPin; + if (!paramsp) paramsp = newPin; + } + } + } + bool nodeDeparamCommon(AstNode* nodep, AstNodeModule*& srcModpr, AstPin* paramsp, AstPin* pinsp, bool any_overrides) { // Make sure constification worked @@ -949,6 +1048,16 @@ class ParamProcessor final { cellInterfaceCleanup(pinsp, srcModpr, longname /*ref*/, any_overrides /*ref*/, ifaceRefRefs /*ref*/); + // Default params are resolved as overrides + if (!any_overrides) { + for (AstPin* pinp = paramsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) { + if (pinp->modPTypep()) { + any_overrides = true; + break; + } + } + } + if (m_hierBlocks.hierSubRun() && m_hierBlocks.isHierBlock(srcModpr->origName())) { AstNodeModule* const paramedModp = m_hierBlocks.findByParams(srcModpr->origName(), paramsp, m_modp); @@ -1021,11 +1130,13 @@ class ParamProcessor final { } void classRefDeparam(AstClassOrPackageRef* nodep, AstNodeModule*& srcModpr) { + resolveDefaultParams(nodep); if (nodeDeparamCommon(nodep, srcModpr, nodep->paramsp(), nullptr, false)) nodep->classOrPackagep(srcModpr); } void classRefDeparam(AstClassRefDType* nodep, AstNodeModule*& srcModpr) { + resolveDefaultParams(nodep); if (nodeDeparamCommon(nodep, srcModpr, nodep->paramsp(), nullptr, false)) { AstClass* const classp = VN_AS(srcModpr, Class); nodep->classp(classp); diff --git a/test_regress/t/t_class_defaultparams.py b/test_regress/t/t_class_defaultparams.py new file mode 100755 index 000000000..319c0ff4a --- /dev/null +++ b/test_regress/t/t_class_defaultparams.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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 + +import vltest_bootstrap + +test.scenarios('vlt') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_class_defaultparams.v b/test_regress/t/t_class_defaultparams.v new file mode 100644 index 000000000..76cfd7cb8 --- /dev/null +++ b/test_regress/t/t_class_defaultparams.v @@ -0,0 +1,58 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class c0 #(type T= real); + static function T f(); + endfunction +endclass +class c2 #(type REQ=int, type RSP= int, type IMP=int); + function new (IMP imp); + endfunction +endclass +class c3 #(type REQ, type RSP, type IMP=RSP); + function new (IMP imp); + endfunction +endclass + +class c1 #(type REQ= int, RSP=REQ); + typedef c1 #( REQ , RSP) this_type; + typedef c0 #(this_type) type_id; + c2 #(REQ, RSP, this_type) c2inst; + function new (string name, int parent); + c2inst = new (this); + endfunction + + c3 #(REQ, this_type) c3inst; +endclass + +`define test \ +c1 #(real) c1inst1;\ +c1 #(real, real) c1inst2;\ +c1 #(real, int) c1inst3;\ +c1 #() c1inst4;\ +c1 c1inst5; + +`test +interface interf; + // `test +endinterface +module t; + // `test + interf interf_inst(); +endmodule +class topc; + // `test +endclass + +class paramcl; +endclass: paramcl +class c5; +c1 #(paramcl) seq; +function void f(); + seq = c1 #(paramcl)::type_id::f(); +endfunction: f +endclass +c5 c5inst; diff --git a/test_regress/t/uvm/uvm_pkg_todo.svh b/test_regress/t/uvm/uvm_pkg_todo.svh index a79288b9e..6ffd9b8ee 100644 --- a/test_regress/t/uvm/uvm_pkg_todo.svh +++ b/test_regress/t/uvm/uvm_pkg_todo.svh @@ -19744,9 +19744,7 @@ endclass typedef uvm_sequencer #(uvm_sequence_item) uvm_virtual_sequencer; function uvm_sequencer::new (string name, uvm_component parent=null); super.new(name, parent); -//TODO issue #4497 - Fix uvm_sequencer wrong reference type -//TODO %Error: t/t_uvm_pkg_todo.vh:19869:21: Function Argument expects a CLASSREFDTYPE 'uvm_sequencer__Tz97_TBz97', got CLASSREFDTYPE 'uvm_sequencer__Tz97' -//TODO seq_item_export = new ("seq_item_export", this); + seq_item_export = new ("seq_item_export", this); endfunction function void uvm_sequencer::stop_sequences(); REQ t;