Support complex expressions as std::randomize arguments (#6860)
This commit is contained in:
parent
7023f38d12
commit
72a6da5ac8
|
|
@ -170,13 +170,23 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
||||||
if (VN_IS(pinp, With)) continue;
|
if (VN_IS(pinp, With)) continue;
|
||||||
const AstArg* const argp = VN_CAST(pinp, Arg);
|
const AstArg* const argp = VN_CAST(pinp, Arg);
|
||||||
if (!argp) continue;
|
if (!argp) continue;
|
||||||
const AstNodeExpr* const exprp = argp->exprp();
|
const AstNodeExpr* exprp = argp->exprp();
|
||||||
|
// Traverse through expression to find the base variable
|
||||||
|
while (exprp) {
|
||||||
if (const AstNodeVarRef* const varrefp = VN_CAST(exprp, NodeVarRef)) {
|
if (const AstNodeVarRef* const varrefp = VN_CAST(exprp, NodeVarRef)) {
|
||||||
if (varrefp->varp() == varp) return true;
|
if (varrefp->varp() == varp) return true;
|
||||||
} else if (const AstMemberSel* const memberselp = VN_CAST(exprp, MemberSel)) {
|
break;
|
||||||
|
}
|
||||||
|
if (const AstMemberSel* const memberselp = VN_CAST(exprp, MemberSel)) {
|
||||||
if (memberselp->varp() == varp) return true;
|
if (memberselp->varp() == varp) return true;
|
||||||
|
exprp = memberselp->fromp();
|
||||||
} else if (const AstArraySel* const arrselp = VN_CAST(exprp, ArraySel)) {
|
} else if (const AstArraySel* const arrselp = VN_CAST(exprp, ArraySel)) {
|
||||||
if (VN_AS(arrselp->fromp(), VarRef)->varp() == varp) return true;
|
exprp = arrselp->fromp();
|
||||||
|
} else if (const AstStructSel* const strselp = VN_CAST(exprp, StructSel)) {
|
||||||
|
exprp = strselp->fromp();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -331,6 +341,52 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get randomized variables from (std::)randomize() arguments
|
||||||
|
// and mark AstNodeModule nodes in which they are defined
|
||||||
|
void handleRandomizeArgument(AstNodeExpr* exprp, AstVar* const varp, const bool stdRandomize) {
|
||||||
|
// IEEE 1800-2023 18.11: "Arguments are limited to the names of properties
|
||||||
|
// of the calling object; expressions are not allowed."
|
||||||
|
// However, for compatibility with other simulators, we support complex
|
||||||
|
// expressions like obj.member[idx].field in inline randomize().
|
||||||
|
while (exprp) {
|
||||||
|
AstVar* randVarp = nullptr;
|
||||||
|
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
|
||||||
|
randVarp = memberSelp->varp();
|
||||||
|
exprp = memberSelp->fromp();
|
||||||
|
} else if (AstArraySel* const arraySelp = VN_CAST(exprp, ArraySel)) {
|
||||||
|
exprp = arraySelp->fromp();
|
||||||
|
continue; // Skip ArraySel, continue traversing
|
||||||
|
} else if (AstStructSel* const structSelp = VN_CAST(exprp, StructSel)) {
|
||||||
|
exprp = structSelp->fromp();
|
||||||
|
continue; // Skip StructSel, continue traversing
|
||||||
|
} else if (AstVarRef* const varrefp = VN_CAST(exprp, VarRef)) {
|
||||||
|
randVarp = varrefp->varp();
|
||||||
|
varrefp->user1(true);
|
||||||
|
varrefp->access(VAccess::READWRITE);
|
||||||
|
exprp = nullptr;
|
||||||
|
} else {
|
||||||
|
// All invalid and unsupported expressions should be caught in V3Width
|
||||||
|
exprp->v3fatalSrc("Unexpected expression type in randomize() argument");
|
||||||
|
}
|
||||||
|
UASSERT_OBJ(randVarp, exprp, "No rand variable found");
|
||||||
|
if (randVarp == varp) return;
|
||||||
|
AstNode* backp = randVarp;
|
||||||
|
while (backp && !VN_IS(backp, NodeModule)) backp = backp->backp();
|
||||||
|
if (stdRandomize) {
|
||||||
|
UASSERT_OBJ(backp, randVarp, "No class or module found for rand variable");
|
||||||
|
backp->user1(IS_STD_RANDOMIZED);
|
||||||
|
} else {
|
||||||
|
// Inline randomized then
|
||||||
|
UASSERT_OBJ(VN_IS(backp, Class), randVarp,
|
||||||
|
"No class found for inline randomized variable");
|
||||||
|
RandomizeMode randMode = {};
|
||||||
|
randMode.usesMode = true;
|
||||||
|
randVarp->user1(randMode.asInt);
|
||||||
|
backp->user1(IS_RANDOMIZED_INLINE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
void visit(AstClass* nodep) override {
|
void visit(AstClass* nodep) override {
|
||||||
VL_RESTORER(m_classp);
|
VL_RESTORER(m_classp);
|
||||||
|
|
@ -534,33 +590,7 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
||||||
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
|
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
|
||||||
AstArg* const argp = VN_CAST(pinp, Arg);
|
AstArg* const argp = VN_CAST(pinp, Arg);
|
||||||
if (!argp) continue;
|
if (!argp) continue;
|
||||||
AstNodeExpr* exprp = argp->exprp();
|
handleRandomizeArgument(argp->exprp(), nullptr, true);
|
||||||
while (exprp) {
|
|
||||||
AstVar* randVarp = nullptr;
|
|
||||||
AstVarRef* varrefp = nullptr;
|
|
||||||
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
|
|
||||||
randVarp = memberSelp->varp();
|
|
||||||
exprp = memberSelp->fromp();
|
|
||||||
} else if ((varrefp = VN_CAST(exprp, VarRef))) {
|
|
||||||
randVarp = varrefp->varp();
|
|
||||||
varrefp->user1(true);
|
|
||||||
exprp = nullptr;
|
|
||||||
} else {
|
|
||||||
varrefp = VN_AS(VN_CAST(exprp, ArraySel)->fromp(), VarRef);
|
|
||||||
randVarp = varrefp->varp();
|
|
||||||
varrefp->user1(true);
|
|
||||||
varrefp->access(VAccess::READWRITE);
|
|
||||||
exprp = nullptr;
|
|
||||||
}
|
|
||||||
UASSERT_OBJ(randVarp, nodep, "No rand variable found");
|
|
||||||
AstNode* backp = randVarp;
|
|
||||||
while (backp && (!VN_IS(backp, Class) && !VN_IS(backp, NodeModule))) {
|
|
||||||
backp = backp->backp();
|
|
||||||
}
|
|
||||||
UASSERT_OBJ(VN_IS(backp, NodeModule), randVarp,
|
|
||||||
"No class or module found for rand variable");
|
|
||||||
backp->user1(IS_STD_RANDOMIZED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -568,7 +598,6 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
||||||
AstArg* const argp = VN_CAST(pinp, Arg);
|
AstArg* const argp = VN_CAST(pinp, Arg);
|
||||||
if (!argp) continue;
|
if (!argp) continue;
|
||||||
classp->user1(IS_RANDOMIZED_INLINE);
|
classp->user1(IS_RANDOMIZED_INLINE);
|
||||||
AstNodeExpr* exprp = argp->exprp();
|
|
||||||
AstVar* fromVarp = nullptr; // If nodep is a method call, this is its receiver
|
AstVar* fromVarp = nullptr; // If nodep is a method call, this is its receiver
|
||||||
if (AstMethodCall* methodCallp = VN_CAST(nodep, MethodCall)) {
|
if (AstMethodCall* methodCallp = VN_CAST(nodep, MethodCall)) {
|
||||||
if (AstMemberSel* const memberSelp = VN_CAST(methodCallp->fromp(), MemberSel)) {
|
if (AstMemberSel* const memberSelp = VN_CAST(methodCallp->fromp(), MemberSel)) {
|
||||||
|
|
@ -578,25 +607,7 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
||||||
fromVarp = varrefp->varp();
|
fromVarp = varrefp->varp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (exprp) {
|
handleRandomizeArgument(argp->exprp(), fromVarp, false);
|
||||||
AstVar* randVarp = nullptr;
|
|
||||||
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
|
|
||||||
randVarp = memberSelp->varp();
|
|
||||||
exprp = memberSelp->fromp();
|
|
||||||
} else {
|
|
||||||
AstVarRef* const varrefp = VN_AS(exprp, VarRef);
|
|
||||||
randVarp = varrefp->varp();
|
|
||||||
exprp = nullptr;
|
|
||||||
}
|
|
||||||
if (randVarp == fromVarp) break;
|
|
||||||
UASSERT_OBJ(randVarp, nodep, "No rand variable found");
|
|
||||||
AstNode* backp = randVarp;
|
|
||||||
while (backp && !VN_IS(backp, Class)) backp = backp->backp();
|
|
||||||
RandomizeMode randMode = {};
|
|
||||||
randMode.usesMode = true;
|
|
||||||
randVarp->user1(randMode.asInt);
|
|
||||||
VN_AS(backp, Class)->user1(IS_RANDOMIZED_INLINE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void visit(AstConstraintExpr* nodep) override {
|
void visit(AstConstraintExpr* nodep) override {
|
||||||
|
|
|
||||||
|
|
@ -6781,7 +6781,7 @@ class WidthVisitor final : public VNVisitor {
|
||||||
// IEEE 1800-2023 (18.12) limits args to current scope variables.
|
// IEEE 1800-2023 (18.12) limits args to current scope variables.
|
||||||
// Verilator accepts this for compatibility with other simulators.
|
// Verilator accepts this for compatibility with other simulators.
|
||||||
continue;
|
continue;
|
||||||
} else if (VN_IS(exprp, VarRef) || VN_IS(exprp, ArraySel)) {
|
} else if (VN_IS(exprp, VarRef) || VN_IS(exprp, ArraySel) || VN_IS(exprp, StructSel)) {
|
||||||
// Valid usage
|
// Valid usage
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -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,101 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2025 by Antmicro.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
typedef struct {int x;} struct_t;
|
||||||
|
|
||||||
|
class inner_class;
|
||||||
|
logic [3:0] c;
|
||||||
|
logic [3:0] d[2];
|
||||||
|
struct_t s;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class test_class;
|
||||||
|
logic [3:0] member[5];
|
||||||
|
logic [3:0] member_2d[3][4];
|
||||||
|
inner_class b;
|
||||||
|
endclass
|
||||||
|
|
||||||
|
module t;
|
||||||
|
initial begin
|
||||||
|
test_class example;
|
||||||
|
example = new;
|
||||||
|
example.b = new;
|
||||||
|
|
||||||
|
// Simple array element access
|
||||||
|
repeat (5) begin
|
||||||
|
if (std::randomize(example.member[1]) with {example.member[1] inside {[0 : 3]};} == 0) $stop;
|
||||||
|
if (example.member[1] > 3) $stop;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Different array indices
|
||||||
|
repeat (5) begin
|
||||||
|
if (std::randomize(example.member[0]) with {example.member[0] inside {[5 : 7]};} == 0) $stop;
|
||||||
|
if (example.member[0] < 5 || example.member[0] > 7) $stop;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Last element
|
||||||
|
repeat (5) begin
|
||||||
|
if (std::randomize(example.member[4]) with {example.member[4] inside {[10 : 15]};} == 0)
|
||||||
|
$stop;
|
||||||
|
if (example.member[4] < 10 || example.member[4] > 15) $stop;
|
||||||
|
end
|
||||||
|
|
||||||
|
// 2D array access
|
||||||
|
repeat (5) begin
|
||||||
|
if (std::randomize(
|
||||||
|
example.member_2d[1][2]
|
||||||
|
) with {
|
||||||
|
example.member_2d[1][2] inside {[8 : 12]};
|
||||||
|
} == 0)
|
||||||
|
$stop;
|
||||||
|
if (example.member_2d[1][2] < 8 || example.member_2d[1][2] > 12) $stop;
|
||||||
|
end
|
||||||
|
|
||||||
|
// 2D array different indices
|
||||||
|
repeat (5) begin
|
||||||
|
if (std::randomize(
|
||||||
|
example.member_2d[0][0]
|
||||||
|
) with {
|
||||||
|
example.member_2d[0][0] inside {[1 : 4]};
|
||||||
|
} == 0)
|
||||||
|
$stop;
|
||||||
|
if (example.member_2d[0][0] < 1 || example.member_2d[0][0] > 4) $stop;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Nested object: obj.b.c
|
||||||
|
repeat (5) begin
|
||||||
|
if (std::randomize(example.b.c) with {example.b.c inside {[5 : 9]};} == 0) $stop;
|
||||||
|
if (example.b.c < 5 || example.b.c > 9) $stop;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Nested object with array: obj.b.d[0]
|
||||||
|
repeat (5) begin
|
||||||
|
if (std::randomize(example.b.d[0]) with {example.b.d[0] inside {[11 : 14]};} == 0) $stop;
|
||||||
|
if (example.b.d[0] < 11 || example.b.d[0] > 14) $stop;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Nested object with array: obj.b.d[1] (different index)
|
||||||
|
repeat (5) begin
|
||||||
|
if (std::randomize(example.b.d[1]) with {example.b.d[1] inside {[2 : 6]};} == 0) $stop;
|
||||||
|
if (example.b.d[1] < 2 || example.b.d[1] > 6) $stop;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Nested object with struct: obj.b.s.x
|
||||||
|
repeat (5) begin
|
||||||
|
if (std::randomize(example.b.s.x) with {example.b.s.x inside {[7 : 9]};} == 0) $stop;
|
||||||
|
if (example.b.s.x < 7 || example.b.s.x > 9) $stop;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Inline randomization of object with array
|
||||||
|
repeat (5) begin
|
||||||
|
if (example.randomize(b) with {b.d[1] inside {[2 : 6]};} == 0) $stop;
|
||||||
|
if (example.b.d[1] < 2 || example.b.d[1] > 6) $stop;
|
||||||
|
end
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue