Emit nested constraint setup tasks in depth-first order (deepest first) so outer-scope soft constraints get higher priority per IEEE 18.5.13.
This commit is contained in:
parent
15c60c83aa
commit
3ba9077726
|
|
@ -3959,18 +3959,14 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
AstNodeExpr* beginValp = nullptr;
|
||||
AstVar* genp = getRandomGenerator(nodep);
|
||||
if (genp) {
|
||||
// Phase 1: Process all constraints (create tasks, run ConstraintExprVisitor)
|
||||
// Setup task refs are NOT added to setupAllTaskp here -- done in phase 2
|
||||
nodep->foreachMember([&](AstClass* const classp, AstConstraint* const constrp) {
|
||||
AstTask* taskp = VN_AS(constrp->user2p(), Task);
|
||||
if (!taskp) {
|
||||
taskp = newSetupConstraintTask(classp, constrp->name());
|
||||
constrp->user2p(taskp);
|
||||
}
|
||||
AstTaskRef* const setupTaskRefp = new AstTaskRef{constrp->fileline(), taskp};
|
||||
setupTaskRefp->classOrPackagep(classp);
|
||||
|
||||
AstTask* const setupAllTaskp = getCreateConstraintSetupFunc(nodep);
|
||||
|
||||
setupAllTaskp->addStmtsp(setupTaskRefp->makeStmt());
|
||||
|
||||
if (AstTask* const resizeTaskp = VN_CAST(constrp->user3p(), Task)) {
|
||||
AstTask* const resizeAllTaskp = getCreateAggrResizeTask(nodep);
|
||||
|
|
@ -3989,6 +3985,38 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
nodep, constrp, constrp->itemsp()->unlinkFrBackWithNext()));
|
||||
}
|
||||
});
|
||||
|
||||
// Phase 2: Add setup task refs in depth order (deepest first = lowest priority)
|
||||
// Ensures outer-scope soft constraints override inner-scope (IEEE 18.5.13)
|
||||
{
|
||||
// Count nesting depth by GLOBAL_CONSTRAINT_SEPARATOR occurrences
|
||||
const auto constraintDepth = [](const AstConstraint* constrp) {
|
||||
int depth = 0;
|
||||
size_t pos = 0;
|
||||
while ((pos = constrp->name().find(GLOBAL_CONSTRAINT_SEPARATOR, pos))
|
||||
!= std::string::npos) {
|
||||
++depth;
|
||||
pos += std::strlen(GLOBAL_CONSTRAINT_SEPARATOR);
|
||||
}
|
||||
return depth;
|
||||
};
|
||||
int maxDepth = 0;
|
||||
nodep->foreachMember([&](AstClass* const, AstConstraint* const constrp) {
|
||||
maxDepth = std::max(maxDepth, constraintDepth(constrp));
|
||||
});
|
||||
AstTask* const setupAllTaskp = getCreateConstraintSetupFunc(nodep);
|
||||
for (int d = maxDepth; d >= 0; --d) {
|
||||
nodep->foreachMember(
|
||||
[&](AstClass* const classp, AstConstraint* const constrp) {
|
||||
if (constraintDepth(constrp) != d) return;
|
||||
AstTask* const taskp = VN_AS(constrp->user2p(), Task);
|
||||
AstTaskRef* const setupTaskRefp
|
||||
= new AstTaskRef{constrp->fileline(), taskp};
|
||||
setupTaskRefp->classOrPackagep(classp);
|
||||
setupAllTaskp->addStmtsp(setupTaskRefp->makeStmt());
|
||||
});
|
||||
}
|
||||
}
|
||||
// For derived classes: clone write_var calls from parent's randomize()
|
||||
if (nodep->extendsp()) {
|
||||
AstClass* parentClassp = nodep->extendsp()->classp();
|
||||
|
|
|
|||
|
|
@ -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,68 @@
|
|||
// 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: Soft cross-object constraints should respect IEEE 1800-2023 18.5.13
|
||||
// outer-scope soft has higher priority than inner-scope soft.
|
||||
|
||||
class sub_cfg_c;
|
||||
rand int unsigned timeout;
|
||||
rand bit enabled;
|
||||
constraint soft_defaults {
|
||||
soft timeout == 1000;
|
||||
soft enabled == 0;
|
||||
}
|
||||
endclass
|
||||
|
||||
class parent_cfg_c;
|
||||
rand bit enabled;
|
||||
rand sub_cfg_c sub_a;
|
||||
constraint soft_defaults { soft enabled == 0; }
|
||||
constraint propagate_cons {
|
||||
if (enabled) { sub_a.enabled == 1; }
|
||||
}
|
||||
function new();
|
||||
sub_a = new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class top_test_c;
|
||||
rand parent_cfg_c cfg;
|
||||
rand sub_cfg_c extra_cfg;
|
||||
constraint cfg_hard_cons { cfg.enabled == 1; }
|
||||
constraint cfg_soft_cons {
|
||||
soft cfg.sub_a.timeout == 5000;
|
||||
soft extra_cfg.timeout == 9999;
|
||||
soft extra_cfg.enabled == 1;
|
||||
}
|
||||
function new();
|
||||
cfg = new();
|
||||
extra_cfg = new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
static top_test_c obj = new();
|
||||
repeat (10) begin
|
||||
`checkd(obj.randomize(), 1)
|
||||
// Two-level cross-object soft: parent's soft overrides child's
|
||||
`checkd(obj.cfg.sub_a.timeout, 32'd5000)
|
||||
// One-level cross-object soft: parent's soft overrides child's
|
||||
`checkd(obj.extra_cfg.timeout, 32'd9999)
|
||||
`checkd(obj.extra_cfg.enabled, 1'b1)
|
||||
// Hard constraint propagation still works
|
||||
`checkd(obj.cfg.enabled, 1'b1)
|
||||
`checkd(obj.cfg.sub_a.enabled, 1'b1)
|
||||
end
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue