From ef259f63ca2575c9994a5c67e5d8ed0a867d2cc1 Mon Sep 17 00:00:00 2001 From: Arkadiusz Kozdra Date: Tue, 10 Sep 2024 15:10:36 +0200 Subject: [PATCH] Fix randomize treated as std::randomize in classes (#5436) --- src/V3LinkDot.cpp | 26 +++++++++++++++++++----- src/V3LinkResolve.cpp | 9 +------- src/V3Randomize.cpp | 17 ++++++++++------ src/V3Randomize.h | 3 ++- src/V3Width.cpp | 1 - test_regress/t/t_randomize_method_std.py | 18 ++++++++++++++++ test_regress/t/t_randomize_method_std.v | 22 ++++++++++++++++++++ 7 files changed, 75 insertions(+), 21 deletions(-) create mode 100755 test_regress/t/t_randomize_method_std.py create mode 100644 test_regress/t/t_randomize_method_std.v diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index b357e6186..cfaa34bc8 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -69,6 +69,7 @@ #include "V3Graph.h" #include "V3MemberMap.h" #include "V3Parse.h" +#include "V3Randomize.h" #include "V3String.h" #include "V3SymTable.h" @@ -1103,11 +1104,18 @@ class LinkDotFindVisitor final : public VNVisitor { VL_RESTORER(m_curSymp); VSymEnt* upSymp = m_curSymp; { - if (VN_IS(m_curSymp->nodep(), Class) - && VN_AS(m_curSymp->nodep(), Class)->isInterfaceClass() && !nodep->pureVirtual() - && !nodep->isConstructor()) { - nodep->v3error("Interface class functions must be pure virtual" - << " (IEEE 1800-2023 8.26): " << nodep->prettyNameQ()); + if (VN_IS(m_curSymp->nodep(), Class)) { + if (VN_AS(m_curSymp->nodep(), Class)->isInterfaceClass() && !nodep->pureVirtual() + && !nodep->isConstructor()) { + nodep->v3error("Interface class functions must be pure virtual" + << " (IEEE 1800-2023 8.26): " << nodep->prettyNameQ()); + } + if (m_statep->forPrimary() + && (nodep->name() == "randomize" || nodep->name() == "srandom")) { + nodep->v3error(nodep->prettyNameQ() + << " is a predefined class method; redefinition not allowed" + " (IEEE 1800-2023 18.6.3)"); + } } // Change to appropriate package if extern declaration (vs definition) if (nodep->classOrPackagep()) { @@ -3409,6 +3417,14 @@ class LinkDotResolveVisitor final : public VNVisitor { return; } } + if (first && nodep->name() == "randomize" && VN_IS(m_modp, Class)) { + // need special handling to avoid falling back to std::randomize + VMemberMap memberMap; + AstFunc* const randFuncp = V3Randomize::newRandomizeFunc( + memberMap, VN_AS(m_modp, Class), nodep->name(), true, true); + nodep->taskp(randFuncp); + m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), randFuncp, m_modp); + } VSymEnt* const foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot, first); AstNodeFTask* const taskp diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index ce67691ed..4ed741904 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -113,14 +113,7 @@ class LinkResolveVisitor final : public VNVisitor { // NodeTask: Remember its name for later resolution if (m_underGenerate) nodep->underGenerate(true); // Remember the existing symbol table scope - if (m_classp) { - if (nodep->name() == "randomize" || nodep->name() == "srandom") { - nodep->v3error(nodep->prettyNameQ() - << " is a predefined class method; redefinition not allowed" - " (IEEE 1800-2023 18.6.3)"); - } - nodep->classMethod(true); - } + if (m_classp) nodep->classMethod(true); // V3LinkDot moved the isExternDef into the class, the extern proto was // checked to exist, and now isn't needed nodep->isExternDef(false); diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 96f4a6953..63ce08f58 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1706,6 +1706,7 @@ class RandomizeVisitor final : public VNVisitor { iterateChildren(nodep); if (!nodep->user1()) return; // Doesn't need randomize, or already processed UINFO(9, "Define randomize() for " << nodep << endl); + nodep->baseMostClassp()->needRNG(true); AstFunc* const randomizep = V3Randomize::newRandomizeFunc(m_memberMap, nodep); AstVar* const fvarp = VN_AS(randomizep->fvarp(), Var); addPrePostCall(nodep, randomizep, "pre_randomize"); @@ -2027,26 +2028,30 @@ void V3Randomize::randomizeNetlist(AstNetlist* nodep) { } AstFunc* V3Randomize::newRandomizeFunc(VMemberMap& memberMap, AstClass* nodep, - const std::string& name, bool allowVirtual) { + const std::string& name, bool allowVirtual, + bool childDType) { AstFunc* funcp = VN_AS(memberMap.findMember(nodep, name), Func); if (!funcp) { v3Global.useRandomizeMethods(true); AstNodeDType* const dtypep - = nodep->findBitDType(32, 32, VSigning::SIGNED); // IEEE says int return of 0/1 - AstVar* const fvarp = new AstVar{nodep->fileline(), VVarType::MEMBER, name, dtypep}; + = childDType + ? new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT} + : nodep->findBitDType(32, 32, VSigning::SIGNED); // IEEE says int return of 0/1 + AstVar* const fvarp = childDType + ? new AstVar{nodep->fileline(), VVarType::MEMBER, name, + VFlagChildDType{}, dtypep} + : new AstVar{nodep->fileline(), VVarType::MEMBER, name, dtypep}; fvarp->lifetime(VLifetime::AUTOMATIC); fvarp->funcLocal(true); fvarp->funcReturn(true); fvarp->direction(VDirection::OUTPUT); nodep->addMembersp(funcp); funcp = new AstFunc{nodep->fileline(), name, nullptr, fvarp}; - funcp->dtypep(dtypep); + if (!childDType) funcp->dtypep(dtypep); funcp->classMethod(true); funcp->isVirtual(allowVirtual && nodep->isExtended()); nodep->addMembersp(funcp); memberMap.insert(nodep, funcp); - AstClass* const basep = nodep->baseMostClassp(); - basep->needRNG(true); } return funcp; } diff --git a/src/V3Randomize.h b/src/V3Randomize.h index 26e504c62..896c14895 100644 --- a/src/V3Randomize.h +++ b/src/V3Randomize.h @@ -32,7 +32,8 @@ public: static AstFunc* newRandomizeFunc(VMemberMap& memberMap, AstClass* nodep, const std::string& name = "randomize", - bool allowVirtual = true) VL_MT_DISABLED; + bool allowVirtual = true, + bool childDType = false) VL_MT_DISABLED; static AstFunc* newSRandomFunc(VMemberMap& memberMap, AstClass* nodep) VL_MT_DISABLED; }; diff --git a/src/V3Width.cpp b/src/V3Width.cpp index e7f21297f..c9a2c1d73 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -6025,7 +6025,6 @@ class WidthVisitor final : public VNVisitor { } UASSERT_OBJ(classp, nodep, "Should have failed in V3LinkDot"); if (nodep->name() == "randomize") { - nodep->taskp(V3Randomize::newRandomizeFunc(m_memberMap, classp)); AstClassRefDType* const adtypep = new AstClassRefDType{nodep->fileline(), classp, nullptr}; v3Global.rootp()->typeTablep()->addTypesp(adtypep); diff --git a/test_regress/t/t_randomize_method_std.py b/test_regress/t/t_randomize_method_std.py new file mode 100755 index 000000000..d4f986441 --- /dev/null +++ b/test_regress/t/t_randomize_method_std.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_method_std.v b/test_regress/t/t_randomize_method_std.v new file mode 100644 index 000000000..2e1d991cb --- /dev/null +++ b/test_regress/t/t_randomize_method_std.v @@ -0,0 +1,22 @@ +// DESCRIPTION: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +process p; // force importing std into top-level namespace + +class C; + function new; + if (randomize() != 1) $stop; + endfunction +endclass + +module t (/*AUTOARG*/); + initial begin + C c = new; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule