This commit is contained in:
parent
cfd22a95e2
commit
f097e8a34e
|
|
@ -898,17 +898,12 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
AstNodeExpr* parentAccess = membersel->fromp()->cloneTree(false);
|
||||
AstNodeModule* const varClassp = VN_AS(varp->user2p(), NodeModule);
|
||||
AstVar* const effectiveRandModeVarp = VN_AS(varClassp->user2p(), Var);
|
||||
if (effectiveRandModeVarp) {
|
||||
AstMemberSel* randModeSel
|
||||
= new AstMemberSel{varp->fileline(), parentAccess, effectiveRandModeVarp};
|
||||
randModeSel->dtypep(effectiveRandModeVarp->dtypep());
|
||||
randModeAccess = randModeSel;
|
||||
} else {
|
||||
UASSERT_OBJ(m_randModeVarp, nodep, "No m_randModeVarp");
|
||||
randModeAccess = new AstVarRef{varp->fileline(),
|
||||
VN_AS(m_randModeVarp->user2p(), NodeModule),
|
||||
m_randModeVarp, VAccess::READ};
|
||||
}
|
||||
UASSERT_OBJ(effectiveRandModeVarp, nodep,
|
||||
"Member-selected variable must have randmode in its class");
|
||||
AstMemberSel* randModeSel
|
||||
= new AstMemberSel{varp->fileline(), parentAccess, effectiveRandModeVarp};
|
||||
randModeSel->dtypep(effectiveRandModeVarp->dtypep());
|
||||
randModeAccess = randModeSel;
|
||||
} else {
|
||||
UASSERT_OBJ(m_randModeVarp, nodep, "No m_randModeVarp");
|
||||
randModeAccess
|
||||
|
|
@ -958,11 +953,21 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
dimension = 1;
|
||||
}
|
||||
methodp->dtypeSetVoid();
|
||||
AstNodeModule* const classp = membersel ? VN_AS(membersel->user2p(), NodeModule)
|
||||
: VN_AS(varp->user2p(), NodeModule);
|
||||
AstNodeModule* classp;
|
||||
if (membersel) {
|
||||
// For membersel, find the root varref to get the class where randomize() is called
|
||||
AstNode* rootNode = membersel->fromp();
|
||||
while (AstMemberSel* nestedMemberSel = VN_CAST(rootNode, MemberSel)) {
|
||||
rootNode = nestedMemberSel->fromp();
|
||||
}
|
||||
if (AstNodeVarRef* rootVarRef = VN_CAST(rootNode, NodeVarRef)) {
|
||||
classp = VN_AS(rootVarRef->varp()->user2p(), NodeModule);
|
||||
} else {
|
||||
classp = VN_AS(membersel->user2p(), NodeModule);
|
||||
}
|
||||
methodp->addPinsp(membersel);
|
||||
} else {
|
||||
classp = VN_AS(varp->user2p(), NodeModule);
|
||||
AstVarRef* const varRefp
|
||||
= new AstVarRef{varp->fileline(), classp, varp, VAccess::WRITE};
|
||||
varRefp->classOrPackagep(classOrPackagep);
|
||||
|
|
@ -989,9 +994,14 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
}
|
||||
AstNodeFTask* initTaskp = m_inlineInitTaskp;
|
||||
if (!initTaskp) {
|
||||
varp->user3(true); // Mark as set up in new()
|
||||
initTaskp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask);
|
||||
UASSERT_OBJ(initTaskp, classp, "No new() in class");
|
||||
varp->user3(true);
|
||||
if (membersel) {
|
||||
initTaskp = VN_AS(m_memberMap.findMember(classp, "randomize"), NodeFTask);
|
||||
UASSERT_OBJ(initTaskp, classp, "No randomize() in class");
|
||||
} else {
|
||||
initTaskp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask);
|
||||
UASSERT_OBJ(initTaskp, classp, "No new() in class");
|
||||
}
|
||||
}
|
||||
initTaskp->addStmtsp(methodp->makeStmt());
|
||||
} else {
|
||||
|
|
@ -2517,6 +2527,32 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
nodep, constrp, constrp->itemsp()->unlinkFrBackWithNext()));
|
||||
}
|
||||
});
|
||||
// For derived classes: clone write_var calls from parent's randomize()
|
||||
if (nodep->extendsp()) {
|
||||
AstClass* parentClassp = nodep->extendsp()->classp();
|
||||
while (parentClassp) {
|
||||
AstFunc* const parentRandomizep
|
||||
= VN_CAST(m_memberMap.findMember(parentClassp, "randomize"), Func);
|
||||
if (parentRandomizep && parentRandomizep->stmtsp()) {
|
||||
// Clone write_var statements from parent (stop at clearConstraints)
|
||||
for (AstNode* stmtp = parentRandomizep->stmtsp(); stmtp;
|
||||
stmtp = stmtp->nextp()) {
|
||||
bool foundClearConstraints = false;
|
||||
stmtp->foreach([&](AstCMethodHard* methodp) {
|
||||
if (methodp->method() == VCMethod::RANDOMIZER_WRITE_VAR) {
|
||||
randomizep->addStmtsp(stmtp->cloneTree(false));
|
||||
} else if (methodp->method()
|
||||
== VCMethod::RANDOMIZER_CLEARCONSTRAINTS) {
|
||||
foundClearConstraints = true;
|
||||
}
|
||||
});
|
||||
if (foundClearConstraints) break;
|
||||
}
|
||||
}
|
||||
parentClassp
|
||||
= parentClassp->extendsp() ? parentClassp->extendsp()->classp() : nullptr;
|
||||
}
|
||||
}
|
||||
randomizep->addStmtsp(implementConstraintsClear(fl, genp));
|
||||
AstTask* setupAllTaskp = getCreateConstraintSetupFunc(nodep);
|
||||
AstTaskRef* const setupTaskRefp = new AstTaskRef{fl, setupAllTaskp, nullptr};
|
||||
|
|
@ -2819,6 +2855,22 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
|
||||
// Add constraints clearing code
|
||||
if (classGenp) {
|
||||
// Clone write_var calls from main randomize() for path-connected variables
|
||||
AstFunc* const mainRandomizep
|
||||
= VN_CAST(m_memberMap.findMember(classp, "randomize"), Func);
|
||||
if (mainRandomizep && mainRandomizep->stmtsp()) {
|
||||
for (AstNode* stmtp = mainRandomizep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
bool foundClearConstraints = false;
|
||||
stmtp->foreach([&](AstCMethodHard* methodp) {
|
||||
if (methodp->method() == VCMethod::RANDOMIZER_WRITE_VAR) {
|
||||
randomizeFuncp->addStmtsp(stmtp->cloneTree(false));
|
||||
} else if (methodp->method() == VCMethod::RANDOMIZER_CLEARCONSTRAINTS) {
|
||||
foundClearConstraints = true;
|
||||
}
|
||||
});
|
||||
if (foundClearConstraints) break;
|
||||
}
|
||||
}
|
||||
randomizeFuncp->addStmtsp(
|
||||
implementConstraintsClear(randomizeFuncp->fileline(), classGenp));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
#!/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('simulator')
|
||||
|
||||
if not test.have_solver:
|
||||
test.skip("No constraint solver installed")
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by PlanV GmbH.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
|
||||
// Test case 1
|
||||
class uvm_reg_field;
|
||||
rand int m_value;
|
||||
endclass
|
||||
|
||||
class reg_class;
|
||||
rand int m_value;
|
||||
rand uvm_reg_field _dummy;
|
||||
constraint _dummy_is_reg {_dummy.m_value == m_value;}
|
||||
function new();
|
||||
_dummy = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class block_class;
|
||||
rand reg_class m_r;
|
||||
function new();
|
||||
m_r = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class tb_test;
|
||||
virtual task run_phase(int phase);
|
||||
block_class regmodel;
|
||||
regmodel = new;
|
||||
// verilator lint_off IGNOREDRETURN
|
||||
void'(regmodel.randomize() with {m_r.m_value == 32'hA5;});
|
||||
// verilator lint_on IGNOREDRETURN
|
||||
if (regmodel.m_r.m_value != 32'hA5) $stop;
|
||||
endtask
|
||||
endclass
|
||||
|
||||
|
||||
// Test case 2
|
||||
class axi_agent_config;
|
||||
rand bit r_ready_delays;
|
||||
constraint r_ready_delays_c {
|
||||
r_ready_delays == 0;
|
||||
}
|
||||
endclass
|
||||
|
||||
class axi_env_config;
|
||||
rand axi_agent_config axim_agt_cfg;
|
||||
function new();
|
||||
axim_agt_cfg = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class axi_base_test;
|
||||
axi_env_config axi_env_cfg;
|
||||
virtual function void build_phase();
|
||||
configure_axi_env();
|
||||
endfunction
|
||||
function void configure_axi_env();
|
||||
axi_env_cfg = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class axi_wrap_test extends axi_base_test;
|
||||
function void configure_axi_env();
|
||||
void'(axi_env_cfg.randomize());
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
|
||||
module t_constraint_global_nested_member;
|
||||
initial begin
|
||||
tb_test tb;
|
||||
axi_wrap_test axi_t;
|
||||
|
||||
tb = new;
|
||||
tb.run_phase(0);
|
||||
|
||||
axi_t = new();
|
||||
axi_t.build_phase();
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue