Fix randomize() with skipping derived pre/post_randomize (#7799)
This commit is contained in:
parent
5712f9b614
commit
22b45f0fd7
|
|
@ -3105,6 +3105,8 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
std::map<AstClass*, AstVar*>
|
||||
m_staticConstraintModeVars; // Static constraint mode vars per class
|
||||
std::map<AstClass*, AstVar*> m_staticRandModeVars; // Static rand mode vars per class
|
||||
std::map<AstClass*, std::pair<bool, bool>>
|
||||
m_prePostWrap; // Per-handle-type pre/post virtual wrapper presence
|
||||
|
||||
// METHODS
|
||||
// Check if two nodes are semantically equivalent (not pointer equality):
|
||||
|
|
@ -3797,12 +3799,75 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
void addPrePostCall(AstClass* const classp, AstFunc* const funcp, const string& name) {
|
||||
void addPrePostCall(AstClass* const classp, AstNodeFTask* const funcp, const string& name) {
|
||||
if (AstTask* const userFuncp = findPrePostTask(classp, name)) {
|
||||
AstTaskRef* const callp = new AstTaskRef{userFuncp->fileline(), userFuncp};
|
||||
funcp->addStmtsp(callp->makeStmt());
|
||||
}
|
||||
}
|
||||
// Per-class virtual wrapper that invokes the class's effective
|
||||
// pre_randomize/post_randomize. IEEE 1800-2023 18.6.2: pre_randomize and
|
||||
// post_randomize "appear to behave as virtual methods" because randomize()
|
||||
// is virtual. The inline `randomize() with` path builds a non-virtual
|
||||
// function on the static handle type, so it dispatches pre/post through
|
||||
// this wrapper to reach the dynamic type's override.
|
||||
AstTask* getCreatePrePostCallback(AstClass* const classp, const string& which) {
|
||||
const string name = "__V" + which;
|
||||
if (AstTask* const existingp = VN_CAST(m_memberMap.findMember(classp, name), Task)) {
|
||||
return existingp;
|
||||
}
|
||||
AstTask* const taskp = new AstTask{classp->fileline(), name, nullptr};
|
||||
taskp->classMethod(true);
|
||||
taskp->isVirtual(classp->isExtended());
|
||||
classp->addMembersp(taskp);
|
||||
m_memberMap.insert(classp, taskp);
|
||||
addPrePostCall(classp, taskp, which);
|
||||
return taskp;
|
||||
}
|
||||
// Build the virtual pre/post wrappers across classp's whole hierarchy so a
|
||||
// `randomize() with` through a base handle dispatches to a derived
|
||||
// override. Returns whether a pre/post wrapper exists anywhere in the
|
||||
// hierarchy (cached per static handle type).
|
||||
std::pair<bool, bool> buildPrePostVirtualWrappers(AstClass* const classp) {
|
||||
const auto cachedIt = m_prePostWrap.find(classp);
|
||||
if (cachedIt != m_prePostWrap.end()) return cachedIt->second;
|
||||
std::vector<AstClass*> hierp{classp};
|
||||
v3Global.rootp()->foreach([&](AstClass* subp) {
|
||||
if (subp != classp && AstClass::isClassExtendedFrom(subp, classp))
|
||||
hierp.push_back(subp);
|
||||
});
|
||||
bool hasPre = false;
|
||||
bool hasPost = false;
|
||||
for (AstClass* const cp : hierp) {
|
||||
if (findPrePostTask(cp, "pre_randomize")) {
|
||||
getCreatePrePostCallback(cp, "pre_randomize");
|
||||
hasPre = true;
|
||||
}
|
||||
if (findPrePostTask(cp, "post_randomize")) {
|
||||
getCreatePrePostCallback(cp, "post_randomize");
|
||||
hasPost = true;
|
||||
}
|
||||
}
|
||||
// Ensure the static handle type owns the slot whenever a subclass
|
||||
// overrides, so the virtual call resolves on a base handle.
|
||||
if (hasPre) getCreatePrePostCallback(classp, "pre_randomize");
|
||||
if (hasPost) getCreatePrePostCallback(classp, "post_randomize");
|
||||
const std::pair<bool, bool> result{hasPre, hasPost};
|
||||
m_prePostWrap.emplace(classp, result);
|
||||
return result;
|
||||
}
|
||||
void addVirtualPrePostCall(AstFunc* const randomizeFuncp, AstClass* const classp,
|
||||
const string& which) {
|
||||
FileLine* const fl = classp->fileline();
|
||||
AstTask* const wrapperp = getCreatePrePostCallback(classp, which);
|
||||
AstClassRefDType* const refDTypep = new AstClassRefDType{fl, classp, nullptr};
|
||||
v3Global.rootp()->typeTablep()->addTypesp(refDTypep);
|
||||
AstMethodCall* const callp
|
||||
= new AstMethodCall{fl, new AstThisRef{fl, refDTypep}, wrapperp->name(), nullptr};
|
||||
callp->taskp(wrapperp);
|
||||
callp->dtypeSetVoid();
|
||||
randomizeFuncp->addStmtsp(callp->makeStmt());
|
||||
}
|
||||
// Check if a class (including inherited members) has any rand class-type members
|
||||
bool classHasRandClassMembers(AstClass* classp) {
|
||||
return classp->existsMember([](const AstClass*, const AstVar* varp) {
|
||||
|
|
@ -5228,7 +5293,17 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
AstFunc* const randomizeFuncp = V3Randomize::newRandomizeFunc(
|
||||
m_memberMap, classp, m_inlineUniqueNames.get(nodep), false);
|
||||
|
||||
addPrePostCall(classp, randomizeFuncp, "pre_randomize");
|
||||
// A base-handle `randomize() with` must still reach a derived
|
||||
// pre/post_randomize. Route them through per-class virtual wrappers
|
||||
// when the static handle type participates in inheritance.
|
||||
const std::pair<bool, bool> prePostWrap = classp->isExtended()
|
||||
? buildPrePostVirtualWrappers(classp)
|
||||
: std::pair<bool, bool>{false, false};
|
||||
if (prePostWrap.first) {
|
||||
addVirtualPrePostCall(randomizeFuncp, classp, "pre_randomize");
|
||||
} else {
|
||||
addPrePostCall(classp, randomizeFuncp, "pre_randomize");
|
||||
}
|
||||
|
||||
// Call nested pre_randomize on rand class-type members (IEEE 18.4.1)
|
||||
if (classHasRandClassMembers(classp)) {
|
||||
|
|
@ -5390,7 +5465,11 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
randomizeFuncp->addStmtsp((new AstTaskRef{nodep->fileline(), postTaskp})->makeStmt());
|
||||
}
|
||||
|
||||
addPrePostCall(classp, randomizeFuncp, "post_randomize");
|
||||
if (prePostWrap.second) {
|
||||
addVirtualPrePostCall(randomizeFuncp, classp, "post_randomize");
|
||||
} else {
|
||||
addPrePostCall(classp, randomizeFuncp, "post_randomize");
|
||||
}
|
||||
|
||||
// Replace the node with a call to that function
|
||||
nodep->name(randomizeFuncp->name());
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
// 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
|
||||
|
||||
class Base;
|
||||
rand bit [7:0] a;
|
||||
bit [7:0] m_pre;
|
||||
bit [7:0] m_post;
|
||||
endclass
|
||||
|
||||
class Derived extends Base;
|
||||
function void pre_randomize;
|
||||
`checkd(m_pre, 8'd0);
|
||||
m_pre = 8'd10;
|
||||
endfunction
|
||||
function void post_randomize;
|
||||
`checkd(m_pre, 8'd10);
|
||||
m_post = a + 8'd1;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class Base2;
|
||||
rand bit [7:0] b;
|
||||
bit [7:0] bp;
|
||||
bit [7:0] bq;
|
||||
function void pre_randomize;
|
||||
bp = 8'd1;
|
||||
endfunction
|
||||
function void post_randomize;
|
||||
bq = b;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class Derived2 extends Base2;
|
||||
bit [7:0] dp;
|
||||
bit [7:0] dq;
|
||||
function void pre_randomize;
|
||||
dp = 8'd2;
|
||||
super.pre_randomize();
|
||||
endfunction
|
||||
function void post_randomize;
|
||||
dq = b + 8'd1;
|
||||
super.post_randomize();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
Base b;
|
||||
Derived d;
|
||||
Base2 b2;
|
||||
Derived2 d2;
|
||||
int ok;
|
||||
|
||||
// Plain randomize through a base handle already dispatches pre/post
|
||||
d = new;
|
||||
b = d;
|
||||
ok = b.randomize();
|
||||
`checkd(ok, 1);
|
||||
`checkd(d.m_pre, 8'd10);
|
||||
`checkd(d.m_post, d.a + 8'd1);
|
||||
|
||||
// randomize() with through a base handle whose static type lacks pre/post
|
||||
d = new;
|
||||
b = d;
|
||||
ok = b.randomize() with {a == 8'h3c;};
|
||||
`checkd(ok, 1);
|
||||
`checkd(b.a, 8'h3c);
|
||||
`checkd(d.m_pre, 8'd10);
|
||||
`checkd(d.m_post, 8'h3d);
|
||||
|
||||
// randomize() with through a base handle that DOES define pre/post,
|
||||
// overridden by the derived class with super chaining
|
||||
d2 = new;
|
||||
b2 = d2;
|
||||
ok = b2.randomize() with {b == 8'h11;};
|
||||
`checkd(ok, 1);
|
||||
`checkd(d2.b, 8'h11);
|
||||
`checkd(d2.dp, 8'd2);
|
||||
`checkd(d2.bp, 8'd1);
|
||||
`checkd(d2.dq, 8'h12);
|
||||
`checkd(d2.bq, 8'h11);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue