This commit is contained in:
parent
f843780aad
commit
0e26b049ea
|
|
@ -40,6 +40,7 @@
|
|||
#include "V3FileLine.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3MemberMap.h"
|
||||
#include "V3Task.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <queue>
|
||||
|
|
@ -547,7 +548,16 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
|||
return;
|
||||
}
|
||||
|
||||
if (nodep->name() != "randomize") return;
|
||||
if (nodep->name() != "randomize") {
|
||||
// Propagate user1 from children (same pattern as visit(AstNodeExpr*))
|
||||
if (m_constraintExprGenp || m_inStdWith) {
|
||||
nodep->user1((nodep->op1p() && nodep->op1p()->user1())
|
||||
|| (nodep->op2p() && nodep->op2p()->user1())
|
||||
|| (nodep->op3p() && nodep->op3p()->user1())
|
||||
|| (nodep->op4p() && nodep->op4p()->user1()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
AstClass* classp = m_classp;
|
||||
if (const AstMethodCall* const methodCallp = VN_CAST(nodep, MethodCall)) {
|
||||
if (const AstClassRefDType* const classRefp
|
||||
|
|
@ -862,6 +872,55 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
return selp;
|
||||
}
|
||||
|
||||
// Extract return expression from a simple function body, or nullptr if too complex.
|
||||
// Uses foreach to walk all assigns regardless of body organization (JumpBlock nesting etc.).
|
||||
// Takes the last assignment to the return variable -- the first is the initializer.
|
||||
AstNodeExpr* extractReturnExpr(AstFunc* funcp) {
|
||||
AstVar* const retVarp = VN_CAST(funcp->fvarp(), Var);
|
||||
if (!retVarp) return nullptr;
|
||||
AstNodeExpr* retExprp = nullptr;
|
||||
int assignCount = 0;
|
||||
funcp->foreach([&](AstAssign* assignp) {
|
||||
AstVarRef* const lhsp = VN_CAST(assignp->lhsp(), VarRef);
|
||||
if (!lhsp || lhsp->varp() != retVarp) return;
|
||||
retExprp = assignp->rhsp();
|
||||
++assignCount;
|
||||
});
|
||||
// Simple function: initializer + return (2) or just return (1)
|
||||
if (assignCount > 2 || !retExprp) return nullptr;
|
||||
return retExprp;
|
||||
}
|
||||
|
||||
// Bottom-up user1 propagation on inlined expression tree
|
||||
void propagateUser1InlineRecurse(AstNodeExpr* nodep) {
|
||||
if (VN_IS(nodep, NodeVarRef)) {
|
||||
nodep->user1(VN_AS(nodep, NodeVarRef)->varp()->rand().isRandomizable());
|
||||
return;
|
||||
}
|
||||
if (VN_IS(nodep, Const)) {
|
||||
nodep->user1(false);
|
||||
return;
|
||||
}
|
||||
bool anyChild = false;
|
||||
if (AstNodeExpr* const cp = VN_CAST(nodep->op1p(), NodeExpr)) {
|
||||
propagateUser1InlineRecurse(cp);
|
||||
anyChild |= cp->user1();
|
||||
}
|
||||
if (AstNodeExpr* const cp = VN_CAST(nodep->op2p(), NodeExpr)) {
|
||||
propagateUser1InlineRecurse(cp);
|
||||
anyChild |= cp->user1();
|
||||
}
|
||||
if (AstNodeExpr* const cp = VN_CAST(nodep->op3p(), NodeExpr)) {
|
||||
propagateUser1InlineRecurse(cp);
|
||||
anyChild |= cp->user1();
|
||||
}
|
||||
if (AstNodeExpr* const cp = VN_CAST(nodep->op4p(), NodeExpr)) {
|
||||
propagateUser1InlineRecurse(cp);
|
||||
anyChild |= cp->user1();
|
||||
}
|
||||
nodep->user1(anyChild);
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
AstVar* varp = nodep->varp();
|
||||
|
|
@ -1892,6 +1951,50 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
if (editFormat(nodep)) return;
|
||||
nodep->v3fatalSrc("Method not handled in constraints? " << nodep);
|
||||
}
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
if (editFormat(nodep)) return;
|
||||
|
||||
AstFunc* const funcp = VN_CAST(nodep->taskp(), Func);
|
||||
if (!funcp) {
|
||||
// Tasks have no return value and can't appear in expressions.
|
||||
// Parser rejects this, so reaching here indicates a compiler bug.
|
||||
nodep->v3fatalSrc("Unexpected task call in constraint expression");
|
||||
return;
|
||||
}
|
||||
|
||||
AstNodeExpr* const retExprp = extractReturnExpr(funcp);
|
||||
if (!retExprp) {
|
||||
nodep->v3warn(CONSTRAINTIGN,
|
||||
"Unsupported: complex function in constraint, treating as state");
|
||||
nodep->user1(false);
|
||||
if (editFormat(nodep)) return;
|
||||
return;
|
||||
}
|
||||
|
||||
// Map formal parameters to actual arguments using V3Task infrastructure
|
||||
const V3TaskConnects tconnects
|
||||
= V3Task::taskConnects(nodep, funcp->stmtsp(), nullptr, false);
|
||||
|
||||
// Clone return expression, substitute params with args
|
||||
AstNodeExpr* const inlinedp = retExprp->cloneTreePure(false);
|
||||
for (const auto& tconnect : tconnects) {
|
||||
const AstVar* const portp = tconnect.first;
|
||||
AstArg* const argp = tconnect.second;
|
||||
if (!argp || !argp->exprp()) continue;
|
||||
inlinedp->foreach([&](AstVarRef* refp) {
|
||||
if (refp->varp() == portp) {
|
||||
refp->replaceWith(argp->exprp()->cloneTreePure(false));
|
||||
VL_DO_DANGLING(refp->deleteTree(), refp);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
inlinedp->dtypep(nodep->dtypep());
|
||||
propagateUser1InlineRecurse(inlinedp);
|
||||
nodep->replaceWith(inlinedp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
iterate(inlinedp);
|
||||
}
|
||||
void visit(AstNodeExpr* nodep) override {
|
||||
if (editFormat(nodep)) return;
|
||||
nodep->v3fatalSrc(
|
||||
|
|
|
|||
|
|
@ -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,58 @@
|
|||
// 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);
|
||||
`define check_range(gotv,minv,maxv) do if ((gotv) < (minv) || (gotv) > (maxv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d-%0d\n", `__FILE__,`__LINE__, (gotv), (minv), (maxv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
class FuncConstraintTest;
|
||||
rand bit [7:0] value;
|
||||
rand bit [7:0] mask;
|
||||
|
||||
function bit [7:0] get_min_value(bit [7:0] m);
|
||||
return m & 8'hF0;
|
||||
endfunction
|
||||
|
||||
function bit [7:0] get_max_value(bit [7:0] m);
|
||||
return m | 8'h0F;
|
||||
endfunction
|
||||
|
||||
constraint func_con {
|
||||
mask inside {[8'h10:8'hF0]};
|
||||
value >= get_min_value(mask);
|
||||
value <= get_max_value(mask);
|
||||
}
|
||||
|
||||
function new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
FuncConstraintTest fct;
|
||||
int rand_ok;
|
||||
bit [7:0] min_val;
|
||||
bit [7:0] max_val;
|
||||
|
||||
initial begin
|
||||
fct = new();
|
||||
|
||||
repeat (10) begin
|
||||
rand_ok = fct.randomize();
|
||||
`checkd(rand_ok, 1)
|
||||
|
||||
`check_range(fct.mask, 8'h10, 8'hF0)
|
||||
|
||||
min_val = fct.get_min_value(fct.mask);
|
||||
max_val = fct.get_max_value(fct.mask);
|
||||
`check_range(fct.value, min_val, max_val)
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
%Warning-CONSTRAINTIGN: t/t_constraint_func_call_unsup.v:18:23: Unsupported: complex function in constraint, treating as state
|
||||
18 | constraint c { x <= complex_func(y); }
|
||||
| ^~~~~~~~~~~~
|
||||
... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest
|
||||
... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message.
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#!/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('vlt')
|
||||
|
||||
test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// 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
|
||||
|
||||
class Cls;
|
||||
rand bit [7:0] x;
|
||||
rand bit [7:0] y;
|
||||
|
||||
function bit [7:0] complex_func(bit [7:0] m);
|
||||
if (m > 128)
|
||||
return m;
|
||||
else
|
||||
return m + 1;
|
||||
endfunction
|
||||
|
||||
constraint c { x <= complex_func(y); }
|
||||
endclass
|
||||
|
||||
module t;
|
||||
Cls obj;
|
||||
|
||||
initial begin
|
||||
obj = new;
|
||||
void'(obj.randomize());
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue