Fixes #7376.
This commit is contained in:
parent
ab0264deca
commit
38b7734530
|
|
@ -66,6 +66,19 @@ enum ClassRandom : uint8_t {
|
||||||
static constexpr const char* GLOBAL_CONSTRAINT_SEPARATOR = "__DT__";
|
static constexpr const char* GLOBAL_CONSTRAINT_SEPARATOR = "__DT__";
|
||||||
static constexpr const char* BASIC_RANDOMIZE_FUNC_NAME = "__VBasicRand";
|
static constexpr const char* BASIC_RANDOMIZE_FUNC_NAME = "__VBasicRand";
|
||||||
|
|
||||||
|
// Walk extends chain to find __Vrandmode variable (stored in AstClass::user2p).
|
||||||
|
// user2p is only set on the root class where __Vrandmode was created, so
|
||||||
|
// derived classes need this chain walk. Accepts AstNodeModule* for flexibility.
|
||||||
|
static AstVar* getRandModeVarFromClass(AstNodeModule* classp) {
|
||||||
|
while (classp) {
|
||||||
|
if (classp->user2p()) return VN_AS(classp->user2p(), Var);
|
||||||
|
AstClass* const cp = VN_CAST(classp, Class);
|
||||||
|
if (!cp || !cp->extendsp()) return nullptr;
|
||||||
|
classp = cp->extendsp()->classp();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// ######################################################################
|
// ######################################################################
|
||||||
// Establishes the target of a rand_mode() call
|
// Establishes the target of a rand_mode() call
|
||||||
|
|
||||||
|
|
@ -1085,7 +1098,7 @@ class ConstraintExprVisitor final : public VNVisitor {
|
||||||
AstNodeExpr* randModeAccess;
|
AstNodeExpr* randModeAccess;
|
||||||
if (membersel) {
|
if (membersel) {
|
||||||
AstNodeModule* const varClassp = VN_AS(varp->user2p(), NodeModule);
|
AstNodeModule* const varClassp = VN_AS(varp->user2p(), NodeModule);
|
||||||
AstVar* const effectiveRandModeVarp = VN_AS(varClassp->user2p(), Var);
|
AstVar* const effectiveRandModeVarp = getRandModeVarFromClass(varClassp);
|
||||||
if (effectiveRandModeVarp) {
|
if (effectiveRandModeVarp) {
|
||||||
// Member's class has randmode, use it
|
// Member's class has randmode, use it
|
||||||
AstNodeExpr* parentAccess = membersel->fromp()->cloneTree(false);
|
AstNodeExpr* parentAccess = membersel->fromp()->cloneTree(false);
|
||||||
|
|
@ -1346,7 +1359,7 @@ class ConstraintExprVisitor final : public VNVisitor {
|
||||||
initTaskp->addStmtsp(methodp->makeStmt());
|
initTaskp->addStmtsp(methodp->makeStmt());
|
||||||
if (isGlobalConstrained && membersel && randMode.usesMode) {
|
if (isGlobalConstrained && membersel && randMode.usesMode) {
|
||||||
AstNodeModule* const varClassp = VN_AS(varp->user2p(), NodeModule);
|
AstNodeModule* const varClassp = VN_AS(varp->user2p(), NodeModule);
|
||||||
AstVar* const subRandModeVarp = VN_AS(varClassp->user2p(), Var);
|
AstVar* const subRandModeVarp = getRandModeVarFromClass(varClassp);
|
||||||
if (subRandModeVarp) {
|
if (subRandModeVarp) {
|
||||||
AstNodeExpr* const parentAccess = membersel->fromp()->cloneTree(false);
|
AstNodeExpr* const parentAccess = membersel->fromp()->cloneTree(false);
|
||||||
AstMemberSel* const randModeSel
|
AstMemberSel* const randModeSel
|
||||||
|
|
@ -2574,6 +2587,8 @@ class ConstraintExprVisitor final : public VNVisitor {
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
iterate(inlinedp);
|
iterate(inlinedp);
|
||||||
}
|
}
|
||||||
|
// Skip non-constraint stmts appended to the iterating list during inline randomize-with
|
||||||
|
void visit(AstNodeStmt* nodep) override {}
|
||||||
void visit(AstNodeExpr* nodep) override {
|
void visit(AstNodeExpr* nodep) override {
|
||||||
if (editFormat(nodep)) return;
|
if (editFormat(nodep)) return;
|
||||||
nodep->v3fatalSrc(
|
nodep->v3fatalSrc(
|
||||||
|
|
@ -3039,13 +3054,6 @@ class RandomizeVisitor final : public VNVisitor {
|
||||||
classp->user2p(randModeVarp);
|
classp->user2p(randModeVarp);
|
||||||
return randModeVarp;
|
return randModeVarp;
|
||||||
}
|
}
|
||||||
static AstVar* getRandModeVar(AstClass* const classp) {
|
|
||||||
if (classp->user2p()) return VN_AS(classp->user2p(), Var);
|
|
||||||
if (AstClassExtends* const extendsp = classp->extendsp()) {
|
|
||||||
return getRandModeVar(extendsp->classp());
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
AstVar* getCreateConstraintModeVar(AstClass* const classp) {
|
AstVar* getCreateConstraintModeVar(AstClass* const classp) {
|
||||||
if (classp->user4p()) return VN_AS(classp->user4p(), Var);
|
if (classp->user4p()) return VN_AS(classp->user4p(), Var);
|
||||||
if (AstClassExtends* const extendsp = classp->extendsp()) {
|
if (AstClassExtends* const extendsp = classp->extendsp()) {
|
||||||
|
|
@ -3287,7 +3295,7 @@ class RandomizeVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
static AstNodeStmt* wrapIfRandMode(AstClass* classp, AstVar* const varp, AstNodeStmt* stmtp) {
|
static AstNodeStmt* wrapIfRandMode(AstClass* classp, AstVar* const varp, AstNodeStmt* stmtp) {
|
||||||
const RandomizeMode rmode = {.asInt = varp->user1()};
|
const RandomizeMode rmode = {.asInt = varp->user1()};
|
||||||
return VN_AS(wrapIfMode(rmode, getRandModeVar(classp), stmtp), NodeStmt);
|
return VN_AS(wrapIfMode(rmode, getRandModeVarFromClass(classp), stmtp), NodeStmt);
|
||||||
}
|
}
|
||||||
AstNode* wrapIfConstraintMode(AstClass* classp, AstConstraint* const constrp, AstNode* stmtp) {
|
AstNode* wrapIfConstraintMode(AstClass* classp, AstConstraint* const constrp, AstNode* stmtp) {
|
||||||
const RandomizeMode rmode = {.asInt = constrp->user1()};
|
const RandomizeMode rmode = {.asInt = constrp->user1()};
|
||||||
|
|
@ -3955,7 +3963,7 @@ class RandomizeVisitor final : public VNVisitor {
|
||||||
if (commonPrefixp == exprp) break;
|
if (commonPrefixp == exprp) break;
|
||||||
AstVar* const randVarp = getVarFromRef(exprp);
|
AstVar* const randVarp = getVarFromRef(exprp);
|
||||||
AstClass* const classp = VN_AS(randVarp->user2p(), Class);
|
AstClass* const classp = VN_AS(randVarp->user2p(), Class);
|
||||||
AstVar* const randModeVarp = getRandModeVar(classp);
|
AstVar* const randModeVarp = getRandModeVarFromClass(classp);
|
||||||
if (savedRandModeVarps.find(randModeVarp) == savedRandModeVarps.end()) {
|
if (savedRandModeVarps.find(randModeVarp) == savedRandModeVarps.end()) {
|
||||||
AstVar* const randModeTmpVarp
|
AstVar* const randModeTmpVarp
|
||||||
= makeTmpRandModeVar(exprp, randModeVarp, storeStmtsp, restoreStmtsp);
|
= makeTmpRandModeVar(exprp, randModeVarp, storeStmtsp, restoreStmtsp);
|
||||||
|
|
@ -4202,7 +4210,7 @@ class RandomizeVisitor final : public VNVisitor {
|
||||||
FileLine* fl = nodep->fileline();
|
FileLine* fl = nodep->fileline();
|
||||||
AstFunc* const randomizep = V3Randomize::newRandomizeFunc(m_memberMap, nodep);
|
AstFunc* const randomizep = V3Randomize::newRandomizeFunc(m_memberMap, nodep);
|
||||||
AstVar* const fvarp = VN_AS(randomizep->fvarp(), Var);
|
AstVar* const fvarp = VN_AS(randomizep->fvarp(), Var);
|
||||||
AstVar* const randModeVarp = getRandModeVar(nodep);
|
AstVar* const randModeVarp = getRandModeVarFromClass(nodep);
|
||||||
addPrePostCall(nodep, randomizep, "pre_randomize");
|
addPrePostCall(nodep, randomizep, "pre_randomize");
|
||||||
|
|
||||||
// Call nested pre_randomize on rand class-type members (IEEE 18.4.1)
|
// Call nested pre_randomize on rand class-type members (IEEE 18.4.1)
|
||||||
|
|
@ -4585,7 +4593,7 @@ class RandomizeVisitor final : public VNVisitor {
|
||||||
UASSERT_OBJ(randModeTarget.classp, nodep,
|
UASSERT_OBJ(randModeTarget.classp, nodep,
|
||||||
"Should have checked in RandomizeMarkVisitor");
|
"Should have checked in RandomizeMarkVisitor");
|
||||||
AstVar* const receiverp = randModeTarget.receiverp;
|
AstVar* const receiverp = randModeTarget.receiverp;
|
||||||
AstVar* const randModeVarp = getRandModeVar(randModeTarget.classp);
|
AstVar* const randModeVarp = getRandModeVarFromClass(randModeTarget.classp);
|
||||||
AstNodeExpr* const lhsp = makeModeAssignLhs(nodep->fileline(), randModeTarget.classp,
|
AstNodeExpr* const lhsp = makeModeAssignLhs(nodep->fileline(), randModeTarget.classp,
|
||||||
randModeTarget.fromp, randModeVarp);
|
randModeTarget.fromp, randModeVarp);
|
||||||
replaceWithModeAssign(nodep,
|
replaceWithModeAssign(nodep,
|
||||||
|
|
@ -4833,7 +4841,7 @@ class RandomizeVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set rand mode if present (not needed if classGenp exists and was copied)
|
// Set rand mode if present (not needed if classGenp exists and was copied)
|
||||||
AstVar* const randModeVarp = getRandModeVar(classp);
|
AstVar* const randModeVarp = getRandModeVarFromClass(classp);
|
||||||
if (!classGenp && randModeVarp) addSetRandMode(randomizeFuncp, localGenp, randModeVarp);
|
if (!classGenp && randModeVarp) addSetRandMode(randomizeFuncp, localGenp, randModeVarp);
|
||||||
|
|
||||||
// Generate constraint setup code and a hardcoded call to the solver
|
// Generate constraint setup code and a hardcoded call to the solver
|
||||||
|
|
|
||||||
|
|
@ -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,116 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
typedef logic [31:0] uvm_reg_data_t;
|
||||||
|
|
||||||
|
class uvm_object;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class uvm_reg_field extends uvm_object;
|
||||||
|
rand uvm_reg_data_t value;
|
||||||
|
int unsigned m_size;
|
||||||
|
|
||||||
|
function uvm_reg_data_t get;
|
||||||
|
return value;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function void set_rand_mode(bit rand_mode);
|
||||||
|
value.rand_mode(rand_mode);
|
||||||
|
uvm_reg_field_valid.constraint_mode(rand_mode);
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function bit get_rand_mode();
|
||||||
|
return bit'(value.rand_mode());
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
constraint uvm_reg_field_valid {
|
||||||
|
if (64'd64 > {32'd0, m_size}) {
|
||||||
|
{32'd0, value} < (64'd1 << m_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void configure(int unsigned size);
|
||||||
|
m_size = size;
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class uvm_reg extends uvm_object;
|
||||||
|
virtual function void build();
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class regA extends uvm_reg;
|
||||||
|
rand uvm_reg_field fA1;
|
||||||
|
rand uvm_reg_field fA2;
|
||||||
|
|
||||||
|
virtual function void build();
|
||||||
|
this.fA1 = new;
|
||||||
|
this.fA2 = new;
|
||||||
|
this.fA1.configure(16);
|
||||||
|
this.fA2.configure(16);
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class test extends uvm_object;
|
||||||
|
regA rg;
|
||||||
|
|
||||||
|
function new;
|
||||||
|
rg = new;
|
||||||
|
rg.build();
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
task run_test;
|
||||||
|
uvm_reg_data_t pre_fA1;
|
||||||
|
uvm_reg_data_t pre_fA2;
|
||||||
|
int rand_ok;
|
||||||
|
|
||||||
|
// Disable fA1, enable fA2
|
||||||
|
rg.fA1.set_rand_mode(0);
|
||||||
|
rg.fA2.set_rand_mode(1);
|
||||||
|
|
||||||
|
if (rg.fA1.get_rand_mode() != 0) $stop;
|
||||||
|
if (rg.fA2.get_rand_mode() != 1) $stop;
|
||||||
|
|
||||||
|
pre_fA1 = rg.fA1.value;
|
||||||
|
|
||||||
|
rand_ok = rg.randomize();
|
||||||
|
if (rand_ok != 0) begin
|
||||||
|
// fA1 should be unchanged
|
||||||
|
if (rg.fA1.get() !== pre_fA1) begin
|
||||||
|
$display("%%Error: fA1 changed: got=%0h exp=%0h", rg.fA1.get(), pre_fA1);
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Re-enable fA1, disable fA2
|
||||||
|
rg.fA1.set_rand_mode(1);
|
||||||
|
rg.fA2.set_rand_mode(0);
|
||||||
|
|
||||||
|
pre_fA2 = rg.fA2.value;
|
||||||
|
|
||||||
|
rand_ok = rg.randomize();
|
||||||
|
if (rand_ok != 0) begin
|
||||||
|
// fA2 should be unchanged
|
||||||
|
if (rg.fA2.get() !== pre_fA2) begin
|
||||||
|
$display("%%Error: fA2 changed: got=%0h exp=%0h", rg.fA2.get(), pre_fA2);
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
endclass
|
||||||
|
|
||||||
|
module top;
|
||||||
|
initial begin
|
||||||
|
test t;
|
||||||
|
t = new;
|
||||||
|
t.run_test();
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue