This commit is contained in:
parent
554fcef627
commit
e41436bd4a
|
|
@ -2691,12 +2691,95 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
|
||||
return new AstRandRNG{fl, dtypep};
|
||||
}
|
||||
// Find pre_randomize/post_randomize task in class hierarchy (walks extendsp chain)
|
||||
AstTask* findPrePostTask(AstClass* classp, const string& name) {
|
||||
for (AstClass* cp = classp; cp; cp = cp->extendsp() ? cp->extendsp()->classp() : nullptr) {
|
||||
if (AstTask* const taskp = VN_CAST(m_memberMap.findMember(cp, name), Task)) {
|
||||
return taskp;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void addPrePostCall(AstClass* const classp, AstFunc* const funcp, const string& name) {
|
||||
if (AstTask* userFuncp = VN_CAST(m_memberMap.findMember(classp, name), Task)) {
|
||||
if (AstTask* const userFuncp = findPrePostTask(classp, name)) {
|
||||
AstTaskRef* const callp = new AstTaskRef{userFuncp->fileline(), userFuncp, nullptr};
|
||||
funcp->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) {
|
||||
if (!varp->rand().isRandomizable()) return false;
|
||||
const AstNodeDType* const dtypep = varp->dtypep()->skipRefp();
|
||||
return VN_IS(dtypep, ClassRefDType);
|
||||
});
|
||||
}
|
||||
// Get or create __VrandCb_pre/__VrandCb_post task for nested callbacks
|
||||
AstTask* getCreateNestedCallbackTask(AstClass* classp, const string& suffix) {
|
||||
const string name = "__VrandCb_" + suffix;
|
||||
AstTask* taskp = VN_CAST(m_memberMap.findMember(classp, name), Task);
|
||||
if (taskp) return taskp;
|
||||
taskp = new AstTask{classp->fileline(), name, nullptr};
|
||||
taskp->classMethod(true);
|
||||
classp->addMembersp(taskp);
|
||||
m_memberMap.insert(classp, taskp);
|
||||
return taskp;
|
||||
}
|
||||
// Populate nested callback task body: calls pre/post_randomize on nested rand class members
|
||||
void populateNestedCallbackTask(AstTask* const callbackTaskp, AstClass* const classp,
|
||||
const string& cbName) {
|
||||
FileLine* const fl = classp->fileline();
|
||||
classp->foreachMember([&](AstClass* ownerClassp, AstVar* memberVarp) {
|
||||
if (!memberVarp->rand().isRandomizable()) return;
|
||||
const AstNodeDType* const dtypep = memberVarp->dtypep()->skipRefp();
|
||||
const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType);
|
||||
if (!classRefp) return;
|
||||
AstClass* const memberClassp = classRefp->classp();
|
||||
if (memberClassp == classp) return; // Avoid self-reference
|
||||
|
||||
// Force-visit member class if not yet processed
|
||||
if (memberClassp->user1()) {
|
||||
iterate(memberClassp);
|
||||
m_writtenVars.clear();
|
||||
}
|
||||
|
||||
AstNode* stmtsp = nullptr;
|
||||
|
||||
// 1. Call member.pre/post_randomize() if exists in hierarchy
|
||||
if (AstTask* const userFuncp = findPrePostTask(memberClassp, cbName)) {
|
||||
AstMethodCall* const callp = new AstMethodCall{
|
||||
fl, new AstVarRef{fl, ownerClassp, memberVarp, VAccess::WRITE}, cbName,
|
||||
nullptr};
|
||||
callp->taskp(userFuncp);
|
||||
callp->dtypeSetVoid();
|
||||
stmtsp = AstNode::addNext(stmtsp, callp->makeStmt());
|
||||
}
|
||||
|
||||
// 2. Call member.__VrandCb_pre/post() for deeper recursion
|
||||
if (classHasRandClassMembers(memberClassp)) {
|
||||
const string suffix = (cbName == "pre_randomize") ? "pre" : "post";
|
||||
AstTask* const nestedTaskp = getCreateNestedCallbackTask(memberClassp, suffix);
|
||||
AstMethodCall* const recurseCallp = new AstMethodCall{
|
||||
fl, new AstVarRef{fl, ownerClassp, memberVarp, VAccess::WRITE},
|
||||
nestedTaskp->name(), nullptr};
|
||||
recurseCallp->taskp(nestedTaskp);
|
||||
recurseCallp->dtypeSetVoid();
|
||||
stmtsp = AstNode::addNext(stmtsp, recurseCallp->makeStmt());
|
||||
}
|
||||
|
||||
if (!stmtsp) return;
|
||||
|
||||
// Wrap in null check
|
||||
AstIf* const nullCheckp = new AstIf{
|
||||
fl,
|
||||
new AstNeq{fl, new AstVarRef{fl, ownerClassp, memberVarp, VAccess::READ},
|
||||
new AstConst{fl, AstConst::Null{}}},
|
||||
stmtsp};
|
||||
|
||||
// Wrap in rand_mode check
|
||||
callbackTaskp->addStmtsp(wrapIfRandMode(classp, memberVarp, nullCheckp));
|
||||
});
|
||||
}
|
||||
AstTask* newSetupConstraintTask(AstClass* const nodep, const std::string& name) {
|
||||
AstTask* const taskp = new AstTask{nodep->fileline(), name + "_setup_constraint", nullptr};
|
||||
taskp->classMethod(true);
|
||||
|
|
@ -3058,6 +3141,13 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
AstVar* const randModeVarp = getRandModeVar(nodep);
|
||||
addPrePostCall(nodep, randomizep, "pre_randomize");
|
||||
|
||||
// Call nested pre_randomize on rand class-type members (IEEE 18.4.1)
|
||||
if (classHasRandClassMembers(nodep)) {
|
||||
AstTask* const preTaskp = getCreateNestedCallbackTask(nodep, "pre");
|
||||
populateNestedCallbackTask(preTaskp, nodep, "pre_randomize");
|
||||
randomizep->addStmtsp((new AstTaskRef{fl, preTaskp, nullptr})->makeStmt());
|
||||
}
|
||||
|
||||
// Both IS_RANDOMIZED and IS_RANDOMIZED_GLOBAL classes need full constraint support
|
||||
// IS_RANDOMIZED_GLOBAL classes can be randomized independently
|
||||
AstNodeExpr* beginValp = nullptr;
|
||||
|
|
@ -3159,6 +3249,14 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
AstFuncRef* const basicRandomizeCallp = new AstFuncRef{fl, basicRandomizep, nullptr};
|
||||
randomizep->addStmtsp(new AstAssign{fl, fvarRefp->cloneTree(false),
|
||||
new AstAnd{fl, fvarRefReadp, basicRandomizeCallp}});
|
||||
|
||||
// Call nested post_randomize on rand class-type members (IEEE 18.4.1)
|
||||
if (classHasRandClassMembers(nodep)) {
|
||||
AstTask* const postTaskp = getCreateNestedCallbackTask(nodep, "post");
|
||||
populateNestedCallbackTask(postTaskp, nodep, "post_randomize");
|
||||
randomizep->addStmtsp((new AstTaskRef{fl, postTaskp, nullptr})->makeStmt());
|
||||
}
|
||||
|
||||
addPrePostCall(nodep, randomizep, "post_randomize");
|
||||
nodep->user1(false);
|
||||
}
|
||||
|
|
@ -3422,6 +3520,16 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
|
||||
addPrePostCall(classp, randomizeFuncp, "pre_randomize");
|
||||
|
||||
// Call nested pre_randomize on rand class-type members (IEEE 18.4.1)
|
||||
if (classHasRandClassMembers(classp)) {
|
||||
AstTask* const preTaskp = getCreateNestedCallbackTask(classp, "pre");
|
||||
if (!preTaskp->stmtsp()) {
|
||||
populateNestedCallbackTask(preTaskp, classp, "pre_randomize");
|
||||
}
|
||||
randomizeFuncp->addStmtsp(
|
||||
(new AstTaskRef{nodep->fileline(), preTaskp, nullptr})->makeStmt());
|
||||
}
|
||||
|
||||
// Detach the expression and prepare variable copies
|
||||
const CaptureVisitor captured{withp->exprp(), m_modp, classp};
|
||||
// Add function arguments
|
||||
|
|
@ -3489,6 +3597,16 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var), VAccess::WRITE},
|
||||
new AstAnd{nodep->fileline(), basicRandomizeFuncCallp, solverCallp}});
|
||||
|
||||
// Call nested post_randomize on rand class-type members (IEEE 18.4.1)
|
||||
if (classHasRandClassMembers(classp)) {
|
||||
AstTask* const postTaskp = getCreateNestedCallbackTask(classp, "post");
|
||||
if (!postTaskp->stmtsp()) {
|
||||
populateNestedCallbackTask(postTaskp, classp, "post_randomize");
|
||||
}
|
||||
randomizeFuncp->addStmtsp(
|
||||
(new AstTaskRef{nodep->fileline(), postTaskp, nullptr})->makeStmt());
|
||||
}
|
||||
|
||||
addPrePostCall(classp, randomizeFuncp, "post_randomize");
|
||||
|
||||
// Replace the node with a call to that function
|
||||
|
|
|
|||
|
|
@ -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,242 @@
|
|||
// 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
|
||||
|
||||
// Test: pre/post_randomize callbacks on nested rand class objects and inherited methods
|
||||
// Covers: IEEE 1800-2017 Section 18.4.1 recursive callback invocation
|
||||
|
||||
`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);
|
||||
|
||||
// --- Inherited callbacks (no override) ---
|
||||
|
||||
class BaseInherit;
|
||||
rand int x;
|
||||
int pre_count;
|
||||
int post_count;
|
||||
|
||||
function new();
|
||||
pre_count = 0;
|
||||
post_count = 0;
|
||||
endfunction
|
||||
|
||||
function void pre_randomize;
|
||||
pre_count++;
|
||||
endfunction
|
||||
|
||||
function void post_randomize;
|
||||
post_count++;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class DerivedNoOverride extends BaseInherit;
|
||||
rand int y;
|
||||
|
||||
function new();
|
||||
super.new();
|
||||
endfunction
|
||||
// Does NOT override pre/post_randomize
|
||||
endclass
|
||||
|
||||
class DerivedPartialOverride extends BaseInherit;
|
||||
rand int z;
|
||||
int derived_pre_count;
|
||||
|
||||
function new();
|
||||
super.new();
|
||||
derived_pre_count = 0;
|
||||
endfunction
|
||||
|
||||
function void pre_randomize;
|
||||
derived_pre_count++;
|
||||
super.pre_randomize();
|
||||
endfunction
|
||||
// Does NOT override post_randomize
|
||||
endclass
|
||||
|
||||
// Override both without calling super
|
||||
class DerivedOverrideBoth extends BaseInherit;
|
||||
rand int w;
|
||||
int derived_pre_count;
|
||||
int derived_post_count;
|
||||
|
||||
function new();
|
||||
super.new();
|
||||
derived_pre_count = 0;
|
||||
derived_post_count = 0;
|
||||
endfunction
|
||||
|
||||
function void pre_randomize;
|
||||
derived_pre_count++;
|
||||
endfunction
|
||||
|
||||
function void post_randomize;
|
||||
derived_post_count++;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// Override only post_randomize without calling super
|
||||
class DerivedOverridePostOnly extends BaseInherit;
|
||||
rand int v;
|
||||
int derived_post_count;
|
||||
|
||||
function new();
|
||||
super.new();
|
||||
derived_post_count = 0;
|
||||
endfunction
|
||||
|
||||
// Does NOT override pre_randomize -> should inherit BaseInherit's
|
||||
function void post_randomize;
|
||||
derived_post_count++;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// --- Nested rand class callbacks (3-level) ---
|
||||
|
||||
class Level3;
|
||||
rand bit [7:0] val;
|
||||
int pre_count;
|
||||
int post_count;
|
||||
|
||||
constraint c_val { val inside {[10:200]}; }
|
||||
|
||||
function new();
|
||||
pre_count = 0;
|
||||
post_count = 0;
|
||||
endfunction
|
||||
|
||||
function void pre_randomize;
|
||||
pre_count++;
|
||||
endfunction
|
||||
|
||||
function void post_randomize;
|
||||
post_count++;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class Level2;
|
||||
rand Level3 l3;
|
||||
rand bit [7:0] val;
|
||||
int pre_count;
|
||||
int post_count;
|
||||
|
||||
constraint c_val { val inside {[1:100]}; }
|
||||
|
||||
function new();
|
||||
l3 = new();
|
||||
pre_count = 0;
|
||||
post_count = 0;
|
||||
endfunction
|
||||
|
||||
function void pre_randomize;
|
||||
pre_count++;
|
||||
endfunction
|
||||
|
||||
function void post_randomize;
|
||||
post_count++;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class Level1;
|
||||
rand Level2 l2;
|
||||
rand bit [7:0] val;
|
||||
int pre_count;
|
||||
int post_count;
|
||||
|
||||
constraint c_val { val inside {[50:150]}; }
|
||||
|
||||
function new();
|
||||
l2 = new();
|
||||
pre_count = 0;
|
||||
post_count = 0;
|
||||
endfunction
|
||||
|
||||
function void pre_randomize;
|
||||
pre_count++;
|
||||
endfunction
|
||||
|
||||
function void post_randomize;
|
||||
post_count++;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
|
||||
initial begin
|
||||
automatic int r;
|
||||
|
||||
// Test 1: Inherited callbacks (no override)
|
||||
begin
|
||||
automatic DerivedNoOverride obj = new;
|
||||
r = obj.randomize();
|
||||
`checkd(r, 1);
|
||||
`checkd(obj.pre_count, 1);
|
||||
`checkd(obj.post_count, 1);
|
||||
end
|
||||
|
||||
// Test 2: Partial override (pre overridden, post inherited)
|
||||
begin
|
||||
automatic DerivedPartialOverride obj = new;
|
||||
r = obj.randomize();
|
||||
`checkd(r, 1);
|
||||
`checkd(obj.derived_pre_count, 1);
|
||||
`checkd(obj.pre_count, 1); // super.pre_randomize called
|
||||
`checkd(obj.post_count, 1); // inherited post_randomize
|
||||
end
|
||||
|
||||
// Test 3: Override both without super - base counts stay 0
|
||||
begin
|
||||
automatic DerivedOverrideBoth obj = new;
|
||||
r = obj.randomize();
|
||||
`checkd(r, 1);
|
||||
`checkd(obj.derived_pre_count, 1);
|
||||
`checkd(obj.derived_post_count, 1);
|
||||
`checkd(obj.pre_count, 0); // base NOT called (no super)
|
||||
`checkd(obj.post_count, 0); // base NOT called (no super)
|
||||
end
|
||||
|
||||
// Test 4: Override only post, inherit pre
|
||||
begin
|
||||
automatic DerivedOverridePostOnly obj = new;
|
||||
r = obj.randomize();
|
||||
`checkd(r, 1);
|
||||
`checkd(obj.pre_count, 1); // inherited pre_randomize called
|
||||
`checkd(obj.derived_post_count, 1); // overridden post called
|
||||
`checkd(obj.post_count, 0); // base post NOT called (no super)
|
||||
end
|
||||
|
||||
// Test 5: Nested callbacks (3-level)
|
||||
begin
|
||||
automatic Level1 l1 = new;
|
||||
r = l1.randomize();
|
||||
`checkd(r, 1);
|
||||
`checkd(l1.pre_count, 1);
|
||||
`checkd(l1.post_count, 1);
|
||||
`checkd(l1.l2.pre_count, 1);
|
||||
`checkd(l1.l2.post_count, 1);
|
||||
`checkd(l1.l2.l3.pre_count, 1);
|
||||
`checkd(l1.l2.l3.post_count, 1);
|
||||
end
|
||||
|
||||
// Test 6: Multiple randomizations
|
||||
begin
|
||||
automatic Level1 l1 = new;
|
||||
repeat(5) begin
|
||||
r = l1.randomize();
|
||||
`checkd(r, 1);
|
||||
end
|
||||
`checkd(l1.pre_count, 5);
|
||||
`checkd(l1.post_count, 5);
|
||||
`checkd(l1.l2.pre_count, 5);
|
||||
`checkd(l1.l2.post_count, 5);
|
||||
`checkd(l1.l2.l3.pre_count, 5);
|
||||
`checkd(l1.l2.l3.post_count, 5);
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue