This commit is contained in:
parent
e0f1f316aa
commit
7cd49a8028
|
|
@ -722,8 +722,8 @@ void VlRandomizer::clearAll() {
|
|||
|
||||
void VlRandomizer::markRandc(const char* name) { m_randcVarNames.insert(name); }
|
||||
|
||||
void VlRandomizer::solveBefore(const char* beforeName, const char* afterName) {
|
||||
m_solveBefore.emplace_back(std::string(beforeName), std::string(afterName));
|
||||
void VlRandomizer::solveBefore(const std::string& beforeName, const std::string& afterName) {
|
||||
m_solveBefore.emplace_back(beforeName, afterName);
|
||||
}
|
||||
|
||||
bool VlRandomizer::nextPhased(VlRNG& rngr) {
|
||||
|
|
|
|||
|
|
@ -631,8 +631,8 @@ public:
|
|||
void clearConstraints();
|
||||
void clearAll(); // Clear both constraints and variables
|
||||
void markRandc(const char* name); // Mark variable as randc for cyclic tracking
|
||||
void solveBefore(const char* beforeName,
|
||||
const char* afterName); // Register solve-before ordering
|
||||
void solveBefore(const std::string& beforeName,
|
||||
const std::string& afterName); // Register solve-before ordering
|
||||
void set_randmode(const VlQueue<CData>& randmode) { m_randmodep = &randmode; }
|
||||
#ifdef VL_DEBUG
|
||||
void dump() const;
|
||||
|
|
|
|||
|
|
@ -767,6 +767,88 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
return "";
|
||||
}
|
||||
|
||||
// Build a C++ expression (as AstNodeExpr) that evaluates to a const char*
|
||||
// containing the SMT variable name for a solve-before variable reference.
|
||||
// Handles simple vars, member selects, and array element selects (for foreach).
|
||||
// Returns nullptr for unsupported expression types.
|
||||
// Helper: build a dynamic AstCExpr for "baseName[idx]" pattern
|
||||
AstCExpr* buildArraySelNameExpr(FileLine* fl, const std::string& baseName,
|
||||
const AstArraySel* selp) {
|
||||
AstCExpr* const p = new AstCExpr{fl, ""};
|
||||
p->add("(\""s + baseName + "[\" + std::to_string(");
|
||||
p->add(selp->bitp()->cloneTreePure(false));
|
||||
p->add(") + \"]\")");
|
||||
p->dtypeSetString();
|
||||
return p;
|
||||
}
|
||||
|
||||
// Helper: get fromp from MemberSel or StructSel
|
||||
static AstNodeExpr* getSelFromp(AstNodeExpr* exprp) {
|
||||
if (AstMemberSel* const mp = VN_CAST(exprp, MemberSel)) return mp->fromp();
|
||||
if (AstStructSel* const sp = VN_CAST(exprp, StructSel)) return sp->fromp();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstNodeExpr* buildSolveBeforeNameExpr(FileLine* fl, AstNodeExpr* exprp) {
|
||||
if (const AstVarRef* const varrefp = VN_CAST(exprp, VarRef)) {
|
||||
AstCExpr* const p = new AstCExpr{fl, AstCExpr::Pure{}, "\"" + varrefp->name() + "\"s"};
|
||||
p->dtypeSetString();
|
||||
return p;
|
||||
}
|
||||
// Handle MemberSel or StructSel (V3Width converts MemberSel -> StructSel for structs)
|
||||
if (AstNodeExpr* const selFromp = getSelFromp(exprp)) {
|
||||
const std::string selName = exprp->name();
|
||||
// Check if fromp chain contains ArraySel (e.g., cfg[i].w)
|
||||
if (const AstArraySel* const arrSelp = VN_CAST(selFromp, ArraySel)) {
|
||||
std::string baseName;
|
||||
if (const AstVarRef* const vp = VN_CAST(arrSelp->fromp(), VarRef)) {
|
||||
baseName = vp->name();
|
||||
} else if (const AstMemberSel* const mp = VN_CAST(arrSelp->fromp(), MemberSel)) {
|
||||
baseName = buildMemberPath(mp);
|
||||
}
|
||||
if (baseName.empty()) return nullptr;
|
||||
AstCExpr* const p = new AstCExpr{fl, ""};
|
||||
p->add("(\""s + baseName + "[\" + std::to_string(");
|
||||
p->add(arrSelp->bitp()->cloneTreePure(false));
|
||||
p->add(") + \"]." + selName + "\")");
|
||||
p->dtypeSetString();
|
||||
return p;
|
||||
}
|
||||
// Static member path (obj.field)
|
||||
if (const AstVarRef* const vp = VN_CAST(selFromp, VarRef)) {
|
||||
const std::string path = vp->name() + "." + selName;
|
||||
AstCExpr* const p = new AstCExpr{fl, AstCExpr::Pure{}, "\"" + path + "\"s"};
|
||||
p->dtypeSetString();
|
||||
return p;
|
||||
}
|
||||
if (VN_IS(selFromp, MemberSel)) {
|
||||
const std::string path
|
||||
= buildMemberPath(VN_AS(selFromp, MemberSel)) + "." + selName;
|
||||
AstCExpr* const p = new AstCExpr{fl, AstCExpr::Pure{}, "\"" + path + "\"s"};
|
||||
p->dtypeSetString();
|
||||
return p;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
if (const AstArraySel* const selp = VN_CAST(exprp, ArraySel)) {
|
||||
// arr[i] -> dynamic name
|
||||
std::string baseName;
|
||||
if (const AstVarRef* const vp = VN_CAST(selp->fromp(), VarRef)) {
|
||||
baseName = vp->name();
|
||||
} else if (const AstMemberSel* const mp = VN_CAST(selp->fromp(), MemberSel)) {
|
||||
baseName = buildMemberPath(mp);
|
||||
}
|
||||
if (baseName.empty()) return nullptr;
|
||||
return buildArraySelNameExpr(fl, baseName, selp);
|
||||
}
|
||||
// Packed struct member: SEL(ARRAYSEL(...)) -- bit select on array element.
|
||||
// Solver registers the whole element, so promote to array element level.
|
||||
if (const AstSel* const bitSelp = VN_CAST(exprp, Sel)) {
|
||||
return buildSolveBeforeNameExpr(fl, bitSelp->fromp());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstSFormatF* getConstFormat(AstNodeExpr* nodep) {
|
||||
return new AstSFormatF{nodep->fileline(), (nodep->width() & 3) ? "#b%b" : "#x%x", false,
|
||||
nodep};
|
||||
|
|
@ -1876,32 +1958,25 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
AstNodeModule* const genModp = VN_AS(m_genp->user2p(), NodeModule);
|
||||
|
||||
for (AstNodeExpr* lhsp = nodep->lhssp(); lhsp; lhsp = VN_CAST(lhsp->nextp(), NodeExpr)) {
|
||||
const std::string lhsName = extractSolveBeforeVarName(lhsp);
|
||||
if (lhsName.empty()) {
|
||||
lhsp->v3warn(CONSTRAINTIGN,
|
||||
"Unsupported: non-variable expression in solve...before");
|
||||
AstNodeExpr* const lhsTestp = buildSolveBeforeNameExpr(fl, lhsp);
|
||||
if (!lhsTestp) {
|
||||
lhsp->v3fatalSrc("Unexpected expression type in solve...before lhs");
|
||||
continue;
|
||||
}
|
||||
VL_DO_DANGLING(lhsTestp->deleteTree(), lhsTestp);
|
||||
for (AstNodeExpr* rhsp = nodep->rhssp(); rhsp;
|
||||
rhsp = VN_CAST(rhsp->nextp(), NodeExpr)) {
|
||||
const std::string rhsName = extractSolveBeforeVarName(rhsp);
|
||||
if (rhsName.empty()) {
|
||||
rhsp->v3warn(CONSTRAINTIGN,
|
||||
"Unsupported: non-variable expression in solve...before");
|
||||
AstNodeExpr* const rhsNamep = buildSolveBeforeNameExpr(fl, rhsp);
|
||||
if (!rhsNamep) {
|
||||
rhsp->v3fatalSrc("Unexpected expression type in solve...before rhs");
|
||||
continue;
|
||||
}
|
||||
AstCMethodHard* const callp = new AstCMethodHard{
|
||||
fl, new AstVarRef{fl, genModp, m_genp, VAccess::READWRITE},
|
||||
VCMethod::RANDOMIZER_SOLVE_BEFORE};
|
||||
callp->dtypeSetVoid();
|
||||
AstNodeExpr* const beforeNamep
|
||||
= new AstCExpr{fl, AstCExpr::Pure{}, "\"" + lhsName + "\""};
|
||||
beforeNamep->dtypeSetUInt32();
|
||||
AstNodeExpr* const afterNamep
|
||||
= new AstCExpr{fl, AstCExpr::Pure{}, "\"" + rhsName + "\""};
|
||||
afterNamep->dtypeSetUInt32();
|
||||
callp->addPinsp(beforeNamep);
|
||||
callp->addPinsp(afterNamep);
|
||||
callp->addPinsp(buildSolveBeforeNameExpr(fl, lhsp));
|
||||
callp->addPinsp(rhsNamep);
|
||||
nodep->addHereThisAsNext(callp->makeStmt());
|
||||
}
|
||||
}
|
||||
|
|
@ -3784,6 +3859,12 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
continue;
|
||||
}
|
||||
|
||||
// Recursively handle ConstraintForeach nodes (dist can be inside foreach)
|
||||
if (AstConstraintForeach* const cfep = VN_CAST(itemp, ConstraintForeach)) {
|
||||
if (cfep->bodyp()) lowerDistConstraints(taskp, cfep->bodyp());
|
||||
continue;
|
||||
}
|
||||
|
||||
AstConstraintExpr* const constrExprp = VN_CAST(itemp, ConstraintExpr);
|
||||
if (!constrExprp) continue;
|
||||
AstDist* const distp = VN_CAST(constrExprp->exprp(), Dist);
|
||||
|
|
|
|||
|
|
@ -7843,12 +7843,10 @@ constraint_primary<nodeExprp>: // ==IEEE: constraint_primary
|
|||
constraint_expressionList<nodep>: // ==IEEE: { constraint_expression }
|
||||
constraint_expression { $$ = $1; }
|
||||
| ySOLVE solve_before_list yBEFORE solve_before_list ';'
|
||||
{ ($<fl>1)->v3warn(CONSTRAINTIGN, "Ignoring unsupported: solve-before only supported as top-level constraint statement");
|
||||
$$ = nullptr; DEL($2, $4); }
|
||||
{ $$ = new AstConstraintBefore{$1, $2, $4}; }
|
||||
| constraint_expressionList constraint_expression { $$ = addNextNull($1, $2); }
|
||||
| constraint_expressionList ySOLVE solve_before_list yBEFORE solve_before_list ';'
|
||||
{ ($<fl>2)->v3warn(CONSTRAINTIGN, "Ignoring unsupported: solve-before only supported as top-level constraint statement");
|
||||
$$ = $1; DEL($3, $5); }
|
||||
{ $$ = addNextNull($1, new AstConstraintBefore{$<fl>2, $3, $5}); }
|
||||
;
|
||||
|
||||
constraint_expression<nodep>: // ==IEEE: constraint_expression
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
%Warning-CONSTRAINTIGN: t/t_constraint_solve_before_expr_unsup.v:12:27: Unsupported: non-variable expression in solve...before
|
||||
: ... note: In instance 't'
|
||||
12 | constraint c { solve arr[0] before 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
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// 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 int x;
|
||||
rand int y;
|
||||
rand int arr[4];
|
||||
|
||||
constraint c { solve arr[0] before y; } // BAD: non-variable expression
|
||||
endclass
|
||||
|
||||
module t;
|
||||
// verilator lint_off IMPLICITSTATIC
|
||||
initial begin
|
||||
Cls c = new;
|
||||
void'(c.randomize());
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
%Warning-CONSTRAINTIGN: t/t_constraint_solve_before_unsup.v:20:7: Ignoring unsupported: solve-before only supported as top-level constraint statement
|
||||
20 | solve x before data[i];
|
||||
| ^~~~~
|
||||
... 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.
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_solve_before_unsup.v:29:7: Ignoring unsupported: solve-before only supported as top-level constraint statement
|
||||
29 | solve x before cfg[i].w, cfg[i].r;
|
||||
| ^~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_solve_before_unsup.v:30:7: Ignoring unsupported: solve-before only supported as top-level constraint statement
|
||||
30 | solve cfg[i].l before cfg[i].x;
|
||||
| ^~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
typedef struct {
|
||||
rand bit l;
|
||||
rand bit x;
|
||||
rand bit w;
|
||||
rand bit r;
|
||||
} reg_t;
|
||||
|
||||
class Packet;
|
||||
rand bit [7:0] data[5];
|
||||
rand bit x;
|
||||
|
||||
constraint c_data {
|
||||
foreach (data[i]) {
|
||||
solve x before data[i];
|
||||
data[i] inside {8'h10, 8'h20, 8'h30, 8'h40, 8'h50};
|
||||
}
|
||||
}
|
||||
|
||||
rand reg_t cfg[];
|
||||
|
||||
constraint solves_only_c {
|
||||
foreach (cfg[i]) {
|
||||
solve x before cfg[i].w, cfg[i].r;
|
||||
solve cfg[i].l before cfg[i].x;
|
||||
}
|
||||
}
|
||||
endclass
|
||||
|
||||
module t;
|
||||
Packet p;
|
||||
|
||||
initial begin
|
||||
p = new;
|
||||
void'(p.randomize());
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -9,8 +9,13 @@
|
|||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('linter')
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)
|
||||
if not test.have_solver:
|
||||
test.skip("No constraint solver installed")
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// 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
|
||||
|
||||
module t;
|
||||
class item;
|
||||
rand int unsigned arr[4];
|
||||
rand int unsigned wgt_zero;
|
||||
rand int unsigned wgt_nonzero;
|
||||
|
||||
constraint wgt_c {
|
||||
wgt_zero inside {[1:10]};
|
||||
wgt_nonzero inside {[1:10]};
|
||||
}
|
||||
|
||||
constraint dist_foreach_c {
|
||||
foreach (arr[i]) {
|
||||
arr[i] dist {0 :/ wgt_zero, [1:15] :/ wgt_nonzero};
|
||||
}
|
||||
}
|
||||
endclass
|
||||
|
||||
initial begin
|
||||
static item it = new;
|
||||
repeat (20) begin
|
||||
`checkd(it.randomize(), 1);
|
||||
foreach (it.arr[i]) begin
|
||||
`checkd(it.arr[i] <= 32'd15, 1);
|
||||
end
|
||||
end
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -4,13 +4,18 @@
|
|||
# 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: 2024 Wilson Snyder
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)
|
||||
if not test.have_solver:
|
||||
test.skip("No constraint solver installed")
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
// 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 checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
typedef struct {
|
||||
rand bit l;
|
||||
rand bit x;
|
||||
rand bit w;
|
||||
rand bit r;
|
||||
} reg_t;
|
||||
|
||||
typedef struct packed {
|
||||
bit l;
|
||||
bit x;
|
||||
bit w;
|
||||
bit r;
|
||||
} preg_t;
|
||||
|
||||
module t;
|
||||
class item;
|
||||
rand bit [3:0] mode;
|
||||
rand bit [7:0] data[4];
|
||||
rand int x;
|
||||
rand int y;
|
||||
rand int arr[4];
|
||||
|
||||
constraint mode_c {
|
||||
mode inside {[0:3]};
|
||||
}
|
||||
|
||||
constraint data_c {
|
||||
foreach (data[i]) {
|
||||
solve mode before data[i];
|
||||
if (mode == 0)
|
||||
data[i] == 8'h00;
|
||||
else
|
||||
data[i] inside {[8'd1:8'd255]};
|
||||
}
|
||||
}
|
||||
|
||||
// Static array index in solve...before (non-foreach)
|
||||
constraint arr_c {
|
||||
solve arr[0] before y;
|
||||
}
|
||||
endclass
|
||||
|
||||
class Packet;
|
||||
rand bit [7:0] pdata[5];
|
||||
rand bit px;
|
||||
rand reg_t cfg[3];
|
||||
rand preg_t pcfg[3];
|
||||
|
||||
constraint c_pdata {
|
||||
foreach (pdata[i]) {
|
||||
solve px before pdata[i];
|
||||
pdata[i] inside {8'h10, 8'h20, 8'h30, 8'h40, 8'h50};
|
||||
}
|
||||
}
|
||||
|
||||
constraint c_cfg {
|
||||
foreach (cfg[i]) {
|
||||
solve px before cfg[i].w, cfg[i].r;
|
||||
solve cfg[i].l before cfg[i].x;
|
||||
}
|
||||
}
|
||||
|
||||
constraint c_pcfg {
|
||||
foreach (pcfg[i]) {
|
||||
solve px before pcfg[i].w, pcfg[i].r;
|
||||
solve pcfg[i].l before pcfg[i].x;
|
||||
}
|
||||
}
|
||||
endclass
|
||||
|
||||
initial begin
|
||||
static item it = new;
|
||||
static Packet pkt = new;
|
||||
|
||||
// Test 1: solve...before with conditional constraints
|
||||
repeat (20) begin
|
||||
`checkd(it.randomize(), 1);
|
||||
if (it.mode == 0) begin
|
||||
foreach (it.data[i]) begin
|
||||
`checkh(it.data[i], 8'h00);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Test 2: solve...before with unpacked/packed struct array members
|
||||
repeat (20) begin
|
||||
`checkd(pkt.randomize(), 1);
|
||||
foreach (pkt.pdata[i]) begin
|
||||
`checkd(pkt.pdata[i] inside {8'h10, 8'h20, 8'h30, 8'h40, 8'h50}, 1);
|
||||
end
|
||||
end
|
||||
|
||||
// Test 3: solve...before with static array index (non-foreach)
|
||||
repeat (20) begin
|
||||
`checkd(it.randomize(), 1);
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue