From 5bf1d060c951e552bbd29dcd0f8056b1130e54f7 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Tue, 10 Mar 2026 23:03:18 +0000 Subject: [PATCH] Fix internal error when derived class calls this.randomize() with inherited rand members (#7229) (#7234) --- src/V3Randomize.cpp | 11 +++- test_regress/t/t_randomize_derived_this.py | 21 ++++++ test_regress/t/t_randomize_derived_this.v | 76 ++++++++++++++++++++++ 3 files changed, 106 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_randomize_derived_this.py create mode 100644 test_regress/t/t_randomize_derived_this.v diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 194de5785..2ec2a97cf 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -730,6 +730,8 @@ class ConstraintExprVisitor final : public VNVisitor { AstClass* const m_classp; AstNodeFTask* const m_inlineInitTaskp; // Method to add write_var calls to // (may be null, then new() is used) + AstNodeFTask* const m_memberselInitTaskp; // Fallback for membersel write_var when + // classp has no randomize() (inherited members) AstVar* const m_genp; // VlRandomizer variable of the class AstVar* m_randModeVarp; // Relevant randmode state variable bool m_wantSingle = false; // Whether to merge constraint expressions with LOGAND @@ -1248,6 +1250,9 @@ class ConstraintExprVisitor final : public VNVisitor { varp->user3(true); if (membersel) { initTaskp = VN_AS(m_memberMap.findMember(classp, "randomize"), NodeFTask); + // Inherited rand members may belong to a base class + // that has no randomize(); use the caller's function + if (!initTaskp) initTaskp = m_memberselInitTaskp; UASSERT_OBJ(initTaskp, classp, "No randomize() in class"); } else { initTaskp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask); @@ -2388,9 +2393,11 @@ public: // CONSTRUCTORS explicit ConstraintExprVisitor(AstClass* classp, VMemberMap& memberMap, AstNode* nodep, AstNodeFTask* inlineInitTaskp, AstVar* genp, - AstVar* randModeVarp, std::set& writtenVars) + AstVar* randModeVarp, std::set& writtenVars, + AstNodeFTask* memberselInitTaskp = nullptr) : m_classp{classp} , m_inlineInitTaskp{inlineInitTaskp} + , m_memberselInitTaskp{memberselInitTaskp} , m_genp{genp} , m_randModeVarp{randModeVarp} , m_memberMap{memberMap} @@ -3979,7 +3986,7 @@ class RandomizeVisitor final : public VNVisitor { if (constrp->itemsp()) expandUniqueElementList(constrp->itemsp()); if (constrp->itemsp()) lowerDistConstraints(taskp, constrp->itemsp()); ConstraintExprVisitor{classp, m_memberMap, constrp->itemsp(), nullptr, - genp, randModeVarp, m_writtenVars}; + genp, randModeVarp, m_writtenVars, randomizep}; if (constrp->itemsp()) { taskp->addStmtsp(wrapIfConstraintMode( nodep, constrp, constrp->itemsp()->unlinkFrBackWithNext())); diff --git a/test_regress/t/t_randomize_derived_this.py b/test_regress/t/t_randomize_derived_this.py new file mode 100755 index 000000000..db1adb3f9 --- /dev/null +++ b/test_regress/t/t_randomize_derived_this.py @@ -0,0 +1,21 @@ +#!/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('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_randomize_derived_this.v b/test_regress/t/t_randomize_derived_this.v new file mode 100644 index 000000000..3326a56ba --- /dev/null +++ b/test_regress/t/t_randomize_derived_this.v @@ -0,0 +1,76 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 PlanV GmbH +// SPDX-License-Identifier: CC0-1.0 + +// 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); +// verilog_format: on + +// Test: this.randomize() called in derived class with inherited rand members +// and constraints from base class. Verifies IS_RANDOMIZED propagation and +// membersel write_var fallback for ancestor classes. + +class sub_cfg_c; + rand bit enabled; + constraint defaults { + soft enabled == 1'b0; + } +endclass + +class base_c; + rand sub_cfg_c cfg; + rand int unsigned watchdog; + + constraint override_cons { + cfg.enabled == 1'b1; + } + + constraint watchdog_range { + watchdog inside {[32'd50:32'd200]}; + } + + function new(); + cfg = new(); + endfunction +endclass + +// Derived class: no additional rand members, calls this.randomize() +class derived_c extends base_c; + function int do_randomize(); + return this.randomize(); + endfunction +endclass + +// Deep inheritance: grandchild with no rand members +class grandchild_c extends derived_c; + function int do_rand_deep(); + return this.randomize(); + endfunction +endclass + +module t; + initial begin + automatic derived_c d = new(); + automatic grandchild_c g = new(); + + // Test derived class this.randomize() + repeat (20) begin + `checkd(d.do_randomize(), 1) + `checkd(d.cfg.enabled, 1) + `checkd(d.watchdog >= 32'd50 && d.watchdog <= 32'd200, 1) + end + + // Test deep inheritance this.randomize() + repeat (20) begin + `checkd(g.do_rand_deep(), 1) + `checkd(g.cfg.enabled, 1) + `checkd(g.watchdog >= 32'd50 && g.watchdog <= 32'd200, 1) + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule