Fixes #7486.
This commit is contained in:
parent
5d1b4fe8a8
commit
327fc4ffbe
|
|
@ -537,6 +537,12 @@ class AstWith final : public AstNode {
|
|||
// @astgen op2 := valueArgRefp : AstLambdaArgRef
|
||||
// @astgen op3 := exprp : List[AstNode] // Pins, expression and constraints
|
||||
// TODO: Separate expression and constraints
|
||||
private:
|
||||
// 'with (identifier_list) {...}' restricted form (IEEE 1800-2023 18.7).
|
||||
bool m_restricted = false;
|
||||
bool m_validated = false; // identifier_list typo / unused checks already run
|
||||
std::set<std::string> m_restrictedNames;
|
||||
|
||||
public:
|
||||
AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
|
||||
AstNode* exprp)
|
||||
|
|
@ -553,6 +559,18 @@ public:
|
|||
BROKEN_RTN(!valueArgRefp()); // varp needed to know lambda's arg dtype
|
||||
return nullptr;
|
||||
}
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
void restricted(bool flag) { m_restricted = flag; }
|
||||
bool restricted() const { return m_restricted; }
|
||||
bool validated() const { return m_validated; }
|
||||
void validated(bool flag) { m_validated = flag; }
|
||||
void addRestrictedName(const std::string& name) { m_restrictedNames.insert(name); }
|
||||
const std::set<std::string>& restrictedNames() const { return m_restrictedNames; }
|
||||
// True if 'name' binds into the randomize() target class.
|
||||
bool nameResolvesToTarget(const std::string& name) const {
|
||||
return !m_restricted || m_restrictedNames.count(name);
|
||||
}
|
||||
};
|
||||
|
||||
// === AstNodeExpr ===
|
||||
|
|
@ -2821,6 +2839,10 @@ class AstWithParse final : public AstNodeExpr {
|
|||
// @astgen op1 := funcrefp : AstNodeExpr
|
||||
// @astgen op3 := exprsp : List[AstNodeExpr] // With's parenthesis part
|
||||
// @astgen op4 := constraintsp : List[AstNode] // With's braces part
|
||||
private:
|
||||
// True for the 'with (identifier_list) {...}' form, including 'with () {...}'.
|
||||
bool m_restricted = false;
|
||||
|
||||
public:
|
||||
AstWithParse(FileLine* fl, AstNodeExpr* funcrefp, AstNodeExpr* exprsp,
|
||||
AstNode* constraintsp = nullptr)
|
||||
|
|
@ -2831,6 +2853,8 @@ public:
|
|||
}
|
||||
ASTGEN_MEMBERS_AstWithParse;
|
||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||
bool restricted() const { return m_restricted; }
|
||||
void restricted(bool flag) { m_restricted = flag; }
|
||||
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
|
|
|
|||
|
|
@ -3558,3 +3558,30 @@ const char* AstNot::widthMismatch() const VL_MT_STABLE {
|
|||
BROKEN_RTN(lhsp()->widthMin() != widthMin());
|
||||
return nullptr;
|
||||
}
|
||||
void AstWith::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (m_restricted) {
|
||||
str << " [RESTRICTED={";
|
||||
bool first = true;
|
||||
for (const std::string& n : m_restrictedNames) {
|
||||
if (!first) str << ",";
|
||||
str << n;
|
||||
first = false;
|
||||
}
|
||||
str << "}]";
|
||||
}
|
||||
}
|
||||
void AstWith::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolIf(str, "restricted", m_restricted);
|
||||
if (m_restricted) {
|
||||
std::string joined;
|
||||
bool first = true;
|
||||
for (const std::string& n : m_restrictedNames) {
|
||||
if (!first) joined += ",";
|
||||
joined += n;
|
||||
first = false;
|
||||
}
|
||||
dumpJsonStr(str, "restrictedNames", joined);
|
||||
}
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2194,24 +2194,39 @@ class LinkDotFindVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
// Type depends on the method used, let V3Width figure it out later
|
||||
if (nodep->exprsp()
|
||||
|| nodep->constraintsp()) { // Else empty expression and pretend no "with"
|
||||
AstNode* exprOrConstraintsp = nullptr;
|
||||
if (nodep->exprsp() && nodep->constraintsp()) {
|
||||
// When support this probably should change AstWith to separate out
|
||||
// the expr from the constraint equation using separate op2/op3 similar
|
||||
// to AstWithParse
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'randomize with (...) {...}'");
|
||||
} else if (nodep->exprsp())
|
||||
exprOrConstraintsp = nodep->exprsp()->unlinkFrBackWithNext();
|
||||
if (nodep->constraintsp())
|
||||
exprOrConstraintsp = AstNode::addNext(
|
||||
exprOrConstraintsp, nodep->constraintsp()->unlinkFrBackWithNext());
|
||||
// 'with (...) { ... }' constraint_block form is randomize-only
|
||||
// (IEEE 1800-2023 7.12 vs. 18.7); array methods take 'with (expr)'.
|
||||
if (nodep->restricted() && funcrefp->name() != "randomize") {
|
||||
nodep->v3error("'with (...) { ... }' constraint block is only valid for"
|
||||
" randomize() (IEEE 1800-2023 18.7)");
|
||||
funcrefp->addArgsp(argsp);
|
||||
nodep->replaceWith(nodep->funcrefp()->unlinkFrBack());
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
const bool restrictedRandomize = nodep->restricted();
|
||||
if (nodep->exprsp() || nodep->constraintsp()
|
||||
|| restrictedRandomize) { // Else empty expression and pretend no "with"
|
||||
AstLambdaArgRef* const indexArgRefp
|
||||
= new AstLambdaArgRef{argFl, name + "__DOT__index", true};
|
||||
AstLambdaArgRef* const valueArgRefp = new AstLambdaArgRef{argFl, name, false};
|
||||
AstWith* const newp
|
||||
= new AstWith{nodep->fileline(), indexArgRefp, valueArgRefp, exprOrConstraintsp};
|
||||
= new AstWith{nodep->fileline(), indexArgRefp, valueArgRefp, nullptr};
|
||||
// Harvest the identifier_list into AstWith; grammar guarantees AstParseRef.
|
||||
if (restrictedRandomize) {
|
||||
newp->restricted(true);
|
||||
while (AstNode* const itemp = nodep->exprsp()) {
|
||||
newp->addRestrictedName(VN_AS(itemp, ParseRef)->name());
|
||||
itemp->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(itemp), itemp);
|
||||
}
|
||||
}
|
||||
AstNode* exprOrConstraintsp = nullptr;
|
||||
if (nodep->exprsp()) exprOrConstraintsp = nodep->exprsp()->unlinkFrBackWithNext();
|
||||
if (nodep->constraintsp())
|
||||
exprOrConstraintsp = AstNode::addNext(
|
||||
exprOrConstraintsp, nodep->constraintsp()->unlinkFrBackWithNext());
|
||||
newp->addExprp(exprOrConstraintsp); // addExprp() tolerates nullptr
|
||||
funcrefp->withp(newp);
|
||||
}
|
||||
funcrefp->addArgsp(argsp);
|
||||
|
|
@ -3082,7 +3097,9 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
int m_modportNum = 0; // Uniqueify modport numbers
|
||||
int m_indent = 0; // Indentation (tree depth) for debug
|
||||
bool m_inSens = false; // True if in senitem
|
||||
bool m_inWith = false; // True if in with
|
||||
const AstWith* m_currentWithp = nullptr; // Enclosing 'with' (nullptr if none)
|
||||
std::set<std::string>
|
||||
m_restrictedNamesUsed; // Names from current 'with (id_list)' that resolved into target
|
||||
bool m_genericIfaceModule = false; // True if in module containing generic interface
|
||||
std::map<std::string, AstNode*> m_ifClassImpNames; // Names imported from interface class
|
||||
std::set<AstClass*> m_extendsParam; // Classes that have a parameterized super class
|
||||
|
|
@ -3911,7 +3928,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
VSymEnt* classSymp = getThisClassSymp();
|
||||
// In 'randomize() with { this.member }', 'this' refers to randomized
|
||||
// object, not the calling class (IEEE 1800-2023 18.7)
|
||||
if (m_randSymp && m_inWith) classSymp = m_randSymp;
|
||||
if (m_randSymp && m_currentWithp) classSymp = m_randSymp;
|
||||
if (!classSymp) {
|
||||
nodep->v3error("'this' used outside class (IEEE 1800-2023 8.11)");
|
||||
m_ds.m_dotErr = true;
|
||||
|
|
@ -4224,11 +4241,16 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
VSymEnt* foundp;
|
||||
string baddot;
|
||||
VSymEnt* okSymp = nullptr;
|
||||
if (m_randSymp) {
|
||||
// Restricted 'with (id_list)': only id_list names bind into target class.
|
||||
if (m_randSymp
|
||||
&& (!m_currentWithp || m_currentWithp->nameResolvesToTarget(nodep->name()))) {
|
||||
foundp = m_randSymp->findIdFlat(nodep->name());
|
||||
if (foundp) {
|
||||
if (m_currentWithp && m_currentWithp->restricted()) {
|
||||
m_restrictedNamesUsed.insert(nodep->name());
|
||||
}
|
||||
if (!start) m_ds.m_dotPos = DP_MEMBER;
|
||||
if (!m_inWith) {
|
||||
if (!m_currentWithp) {
|
||||
UASSERT_OBJ(m_randMethodCallp, nodep, "Expected to be under randomize()");
|
||||
// This will start failing once complex expressions are allowed on the LHS
|
||||
// of randomize() with args
|
||||
|
|
@ -5174,9 +5196,14 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, nodep->dotted(), baddot,
|
||||
okSymp, true); // Maybe nullptr
|
||||
}
|
||||
if (m_randSymp) {
|
||||
// Restricted 'with (id_list)': only id_list names bind into target class.
|
||||
if (m_randSymp
|
||||
&& (!m_currentWithp || m_currentWithp->nameResolvesToTarget(nodep->name()))) {
|
||||
VSymEnt* const foundp = m_randSymp->findIdFlat(nodep->name());
|
||||
if (foundp && m_inWith) {
|
||||
if (foundp && m_currentWithp) {
|
||||
if (m_currentWithp->restricted()) {
|
||||
m_restrictedNamesUsed.insert(nodep->name());
|
||||
}
|
||||
UINFO(9, indent() << "randomize-with fromSym " << foundp->nodep());
|
||||
AstArg* argsp = nullptr;
|
||||
if (nodep->argsp()) {
|
||||
|
|
@ -5569,11 +5596,45 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
UINFO(5, indent() << "visit " << nodep);
|
||||
checkNoDot(nodep);
|
||||
VL_RESTORER(m_curSymp);
|
||||
VL_RESTORER(m_inWith);
|
||||
VL_RESTORER(m_currentWithp);
|
||||
VL_RESTORER(m_restrictedNamesUsed);
|
||||
{
|
||||
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
|
||||
m_inWith = true;
|
||||
m_currentWithp = nodep;
|
||||
// Validate the identifier_list once per AstWith (m_validated travels
|
||||
// with cloneTree, so cloned witnesses skip the redundant check).
|
||||
const bool firstVisit = !nodep->validated();
|
||||
if (firstVisit) {
|
||||
nodep->validated(true);
|
||||
m_restrictedNamesUsed.clear();
|
||||
// IEEE 1800-2023 18.7: each name in the identifier_list must designate
|
||||
// a member of the randomize() target class.
|
||||
if (nodep->restricted() && m_randSymp) {
|
||||
for (const std::string& n : nodep->restrictedNames()) {
|
||||
if (!m_randSymp->findIdFlat(n)) {
|
||||
nodep->v3error("Identifier '"
|
||||
<< n
|
||||
<< "' in 'with (...)' identifier list is not a"
|
||||
" member of the randomize() target class"
|
||||
" (IEEE 1800-2023 18.7)");
|
||||
m_restrictedNamesUsed.insert(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
// Flag names that the user listed but never referenced in the constraint.
|
||||
if (firstVisit && nodep->restricted()) {
|
||||
for (const std::string& n : nodep->restrictedNames()) {
|
||||
if (!m_restrictedNamesUsed.count(n)) {
|
||||
nodep->v3warn(UNUSED, "Identifier '"
|
||||
<< n
|
||||
<< "' in 'with (...)' identifier list is"
|
||||
" not referenced in the constraint"
|
||||
" block");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4194,7 +4194,11 @@ task_subroutine_callNoMethod<nodeExprp>: // function_subroutine_callNoMethod
|
|||
// // We implement randomize as a normal funcRef, since randomize isn't a keyword
|
||||
// // Note yNULL is already part of expressions, so they come for free
|
||||
| funcRef yWITH__CUR constraint_block { $$ = new AstWithParse{$2, $1, nullptr, $3}; }
|
||||
| funcRef yWITH__PAREN_CUR '(' expr ')' constraint_block { $$ = new AstWithParse{$2, $1, $4, $6}; }
|
||||
| funcRef yWITH__PAREN_CUR '(' inlineConstraintIdListE ')' constraint_block
|
||||
{ AstWithParse* const withParsep
|
||||
= new AstWithParse{$2, $1, $4, $6};
|
||||
withParsep->restricted(true);
|
||||
$$ = withParsep; }
|
||||
;
|
||||
|
||||
function_subroutine_callNoMethod<nodeExprp>: // IEEE: function_subroutine_call (as function)
|
||||
|
|
@ -4211,7 +4215,11 @@ function_subroutine_callNoMethod<nodeExprp>: // IEEE: function_subroutine
|
|||
// // We implement randomize as a normal funcRef, since randomize isn't a keyword
|
||||
// // Note yNULL is already part of expressions, so they come for free
|
||||
| funcRef yWITH__CUR constraint_block { $$ = new AstWithParse{$2, $1, nullptr, $3}; }
|
||||
| funcRef yWITH__PAREN_CUR '(' expr ')' constraint_block { $$ = new AstWithParse{$2, $1, $4, $6}; }
|
||||
| funcRef yWITH__PAREN_CUR '(' inlineConstraintIdListE ')' constraint_block
|
||||
{ AstWithParse* const withParsep
|
||||
= new AstWithParse{$2, $1, $4, $6};
|
||||
withParsep->restricted(true);
|
||||
$$ = withParsep; }
|
||||
;
|
||||
|
||||
system_t_stmt_call<nodeStmtp>: // IEEE: part of system_tf_call (as task returning statement)
|
||||
|
|
@ -5407,6 +5415,21 @@ exprList<nodeExprp>:
|
|||
| exprList ',' expr { $$ = $1->addNext($3); }
|
||||
;
|
||||
|
||||
// identifier_list for 'with' in inline randomize constraints (IEEE 1800-2023 18.7).
|
||||
// Only simple identifiers; non-identifier expressions are parse errors.
|
||||
inlineConstraintIdList<nodeExprp>:
|
||||
id { $$ = new AstParseRef{$<fl>1, *$1, nullptr, nullptr}; }
|
||||
| inlineConstraintIdList ',' id { $$ = $1->addNext(
|
||||
new AstParseRef{$<fl>3, *$3, nullptr, nullptr}); }
|
||||
;
|
||||
|
||||
// Optional identifier_list. Empty 'with () {...}' differs from bare 'with {...}'
|
||||
// via AstWithParse::restricted().
|
||||
inlineConstraintIdListE<nodeExprp>:
|
||||
/* empty */ { $$ = nullptr; }
|
||||
| inlineConstraintIdList { $$ = $1; }
|
||||
;
|
||||
|
||||
exprEListE<nodep>: // expression list with empty commas allowed
|
||||
exprE { $$ = $1; }
|
||||
| exprEListE ',' exprE { $$ = addNextNull($1, $3); }
|
||||
|
|
|
|||
|
|
@ -682,10 +682,224 @@ module Vt_debug_emitv_t;
|
|||
restrict (@(posedge clk) ##1 a[0]
|
||||
);
|
||||
endmodule
|
||||
package Vt_debug_emitv_std;
|
||||
class Vt_debug_emitv_semaphore;
|
||||
int signed m_keyCount;
|
||||
function new;
|
||||
input int signed keyCount;
|
||||
m_keyCount = keyCount;
|
||||
endfunction
|
||||
task put;
|
||||
input int signed keyCount;
|
||||
m_keyCount = (m_keyCount + keyCount);
|
||||
endtask
|
||||
task get;
|
||||
input int signed keyCount;
|
||||
while ((m_keyCount < keyCount)) begin
|
||||
begin
|
||||
|
||||
???? // WAIT
|
||||
(m_keyCount >= keyCount)end
|
||||
end
|
||||
m_keyCount = (m_keyCount - keyCount);
|
||||
endtask
|
||||
function try_get;
|
||||
input int signed keyCount;
|
||||
begin : label3
|
||||
try_get = /*CRESET*/;
|
||||
if ((m_keyCount >= keyCount)) begin
|
||||
begin
|
||||
m_keyCount = (m_keyCount -
|
||||
keyCount);
|
||||
try_get = 'sh1;
|
||||
disable label3;
|
||||
end
|
||||
end
|
||||
try_get = 'sh0;
|
||||
disable label3;
|
||||
end
|
||||
endfunction
|
||||
endclass
|
||||
class Vt_debug_emitv_process;
|
||||
typedef enum int signed{
|
||||
FINISHED = 32'h0,
|
||||
RUNNING = 32'h1,
|
||||
WAITING = 32'h2,
|
||||
SUSPENDED = 32'h3,
|
||||
KILLED = 32'h4
|
||||
} state;
|
||||
VlProcessRef m_process;
|
||||
function self;
|
||||
|
||||
???? // CLASSREFDTYPE 'process'
|
||||
p
|
||||
???? // CLASSREFDTYPE 'process'
|
||||
;
|
||||
begin : label4
|
||||
self = /*CRESET*/;
|
||||
p = new();
|
||||
$c(p.m_process = vlProcess;);
|
||||
self = p;
|
||||
disable label4;
|
||||
end
|
||||
endfunction
|
||||
task set_status;
|
||||
input int signed s;
|
||||
$c(m_process->state(s););
|
||||
endtask
|
||||
function status;
|
||||
begin : label5
|
||||
status = /*CRESET*/;
|
||||
status = ($c(m_process->state()));
|
||||
disable label5;
|
||||
end
|
||||
endfunction
|
||||
task kill;
|
||||
set_status(process::KILLED);
|
||||
endtask
|
||||
task suspend;
|
||||
$error("std::process::suspend() not supported");
|
||||
$stop;
|
||||
endtask
|
||||
task resume;
|
||||
set_status(process::RUNNING);
|
||||
endtask
|
||||
task await;
|
||||
|
||||
???? // WAIT
|
||||
((status() == process::FINISHED) || (status()
|
||||
==
|
||||
process::
|
||||
KILLED))endtask
|
||||
task killQueue;
|
||||
ref
|
||||
???? // CLASSREFDTYPE 'process'
|
||||
processQueue[$]
|
||||
???? // CLASSREFDTYPE 'process'
|
||||
;
|
||||
begin : unnamedblk1_1
|
||||
integer signed __Vrepeat0;
|
||||
__Vrepeat0 = processQueue.size();
|
||||
while ((__Vrepeat0 > 32'h0)) begin
|
||||
begin
|
||||
kill();
|
||||
end
|
||||
__Vrepeat0 = (__Vrepeat0 - 32'h1);
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
|
||||
???? // SYSTEMCSECTION
|
||||
function get_randstate;
|
||||
string s;
|
||||
begin : label6
|
||||
get_randstate = /*CRESET*/;
|
||||
s = string'($c(0));
|
||||
$c(s = m_process->randstate(););
|
||||
get_randstate = s;
|
||||
disable label6;
|
||||
end
|
||||
endfunction
|
||||
task set_randstate;
|
||||
input string s;
|
||||
$c(m_process->randstate(s););
|
||||
endtask
|
||||
function new;
|
||||
endfunction
|
||||
process::FINISHEDprocess::KILLEDprocess::RUNNINGprocess::SUSPENDEDprocess::WAITINGendclass
|
||||
function randomize;
|
||||
randomize = 'sh0;
|
||||
endfunction
|
||||
typedef struct {
|
||||
string name;
|
||||
int signed weight;
|
||||
int signed goal;
|
||||
string comment;
|
||||
int signed at_least;
|
||||
int signed auto_bin_max;
|
||||
int signed cross_num_print_missing;
|
||||
bit cross_retain_auto_bins;
|
||||
bit detect_overlap;
|
||||
bit per_instance;
|
||||
bit get_inst_coverage;
|
||||
} vl_covergroup_options_t;
|
||||
typedef struct {
|
||||
int signed weight;
|
||||
int signed goal;
|
||||
string comment;
|
||||
int signed at_least;
|
||||
int signed auto_bin_max;
|
||||
bit detect_overlap;
|
||||
} vl_coverpoint_options_t;
|
||||
typedef struct {
|
||||
int signed weight;
|
||||
int signed goal;
|
||||
string comment;
|
||||
int signed at_least;
|
||||
int signed cross_num_print_missing;
|
||||
bit cross_retain_auto_bins;
|
||||
} vl_cross_options_t;
|
||||
typedef struct {
|
||||
int signed weight;
|
||||
int signed goal;
|
||||
string comment;
|
||||
bit strobe;
|
||||
bit merge_instances;
|
||||
bit distribute_first;
|
||||
real real_interval;
|
||||
} vl_covergroup_type_options_t;
|
||||
typedef struct {
|
||||
int signed weight;
|
||||
int signed goal;
|
||||
string comment;
|
||||
real real_interval;
|
||||
} vl_coverpoint_type_options_t;
|
||||
typedef struct {
|
||||
int signed weight;
|
||||
int signed goal;
|
||||
string comment;
|
||||
} vl_cross_type_options_t;
|
||||
endpackage
|
||||
package Vt_debug_emitv___024unit;
|
||||
class Vt_debug_emitv_Cls;
|
||||
int signed member;
|
||||
member = 'sh1;
|
||||
int signed rmember1;
|
||||
int signed rmember2;
|
||||
task method;
|
||||
if ((this != this)) begin
|
||||
$stop;
|
||||
|
|
@ -693,7 +907,23 @@ package Vt_debug_emitv___024unit;
|
|||
endtask
|
||||
function new;
|
||||
endfunction
|
||||
function randomize;
|
||||
endfunction
|
||||
endclass
|
||||
function rand_restricted;
|
||||
input
|
||||
???? // CLASSREFDTYPE 'Cls'
|
||||
obj
|
||||
???? // CLASSREFDTYPE 'Cls'
|
||||
;
|
||||
input int signed member;
|
||||
begin : label7
|
||||
rand_restricted = randomize() with (
|
||||
???? // CONSTRAINTEXPR
|
||||
(item.rmember1 < member)) ;
|
||||
disable label7;
|
||||
end
|
||||
endfunction
|
||||
endpackage
|
||||
interface Vt_debug_emitv_Iface;
|
||||
input logic clk;
|
||||
|
|
@ -712,13 +942,13 @@ module Vt_debug_emitv_sub;
|
|||
endtask
|
||||
function f;
|
||||
input int signed v;
|
||||
begin : label3
|
||||
begin : label8
|
||||
if ((v == 'sh0)) begin
|
||||
f = 'sh21;
|
||||
disable label3;
|
||||
disable label8;
|
||||
end
|
||||
f = ({32'h1{{31'h0, v[2]}}} + 32'h1);
|
||||
disable label3;
|
||||
disable label8;
|
||||
end
|
||||
endfunction
|
||||
real r;
|
||||
|
|
|
|||
|
|
@ -21,11 +21,17 @@ endpackage
|
|||
|
||||
class Cls;
|
||||
int member = 1;
|
||||
rand int rmember1;
|
||||
rand int rmember2;
|
||||
function void method;
|
||||
if (this != this) $stop;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
function int rand_restricted(Cls obj, int member);
|
||||
return obj.randomize() with (rmember1, rmember2) { rmember1 < member; rmember2 < member; };
|
||||
endfunction
|
||||
|
||||
interface Iface (
|
||||
input clk
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
%Error-UNSUPPORTED: t/t_randomize_with_constraint.v:13:26: Unsupported: 'randomize with (...) {...}'
|
||||
13 | return obj.randomize() with (
|
||||
| ^~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_randomize_with_constraint.v:21:26: Unsupported: 'randomize with (...) {...}'
|
||||
21 | return obj.randomize() with (
|
||||
| ^~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -9,8 +9,13 @@
|
|||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
if not test.have_solver:
|
||||
test.skip("No constraint solver installed")
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -4,33 +4,105 @@
|
|||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// 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 Cls;
|
||||
rand int m_x;
|
||||
int m_y = -1;
|
||||
rand int m_z;
|
||||
int y = -1; // class member named 'y' intentionally shadows the caller arg
|
||||
int lo = -100; // class member named 'lo' intentionally shadows the caller arg
|
||||
endclass
|
||||
|
||||
function int func1(Cls obj, int y);
|
||||
return obj.randomize() with (
|
||||
m_x) {
|
||||
// 'y' is not in the list -> resolves to the caller arg (10), not class member (-1).
|
||||
function int func_restricted(Cls obj, int y);
|
||||
return obj.randomize() with (m_x) {
|
||||
m_x > 0;
|
||||
m_x < y;
|
||||
};
|
||||
endfunction
|
||||
|
||||
function int func2(Cls obj, int y);
|
||||
return obj.randomize() with (
|
||||
m_x) {
|
||||
m_x > 0;
|
||||
m_x < m_y;
|
||||
// Multi-id list: 'lo' is also a class member but resolves to caller arg.
|
||||
function int func_multi(Cls obj, int lo, int hi);
|
||||
return obj.randomize() with (m_x, m_z) {
|
||||
m_x > lo;
|
||||
m_x < hi;
|
||||
m_z > lo;
|
||||
m_z < hi;
|
||||
m_x < m_z;
|
||||
};
|
||||
endfunction
|
||||
|
||||
// Unrestricted baseline: no id list.
|
||||
function int func_unrestricted(Cls obj);
|
||||
return obj.randomize() with {
|
||||
m_x > 0;
|
||||
m_x < 10;
|
||||
};
|
||||
endfunction
|
||||
|
||||
// Empty id list: no name binds into the class; constraint references obj.* explicitly.
|
||||
function automatic int func_empty(Cls obj, int lo, int hi);
|
||||
// verilog_format: off
|
||||
return obj.randomize() with () {
|
||||
obj.m_x > lo;
|
||||
obj.m_x < hi;
|
||||
};
|
||||
// verilog_format: on
|
||||
endfunction
|
||||
|
||||
// 'local::y' bypasses class scope -> caller arg, not obj.y.
|
||||
function int func_local_qual(Cls obj, int y);
|
||||
obj.y = -42;
|
||||
// verilog_format: off
|
||||
return obj.randomize() with {
|
||||
m_x > 0;
|
||||
m_x < local::y;
|
||||
};
|
||||
// verilog_format: on
|
||||
endfunction
|
||||
|
||||
// Consecutive restricted blocks must not leak each other's id list.
|
||||
function automatic int func_sequential(Cls obj, int hi);
|
||||
int rc = 1;
|
||||
rc &= obj.randomize() with (m_x) { m_x > 0; m_x < hi; };
|
||||
rc &= obj.randomize() with (m_z) { m_z > 0; m_z < hi; };
|
||||
return rc;
|
||||
endfunction
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
Cls c;
|
||||
int i;
|
||||
c = new;
|
||||
i = func1(c, 2);
|
||||
i = func2(c, 2);
|
||||
repeat (20) begin
|
||||
i = func_restricted(c, 10);
|
||||
`checkd(i, 1);
|
||||
`check_range(c.m_x, 1, 9);
|
||||
i = func_multi(c, 0, 50);
|
||||
`checkd(i, 1);
|
||||
`check_range(c.m_x, 1, 49);
|
||||
`check_range(c.m_z, 1, 49);
|
||||
`checkd(c.m_x < c.m_z, 1);
|
||||
i = func_unrestricted(c);
|
||||
`checkd(i, 1);
|
||||
`check_range(c.m_x, 1, 9);
|
||||
i = func_empty(c, 0, 9);
|
||||
`checkd(i, 1);
|
||||
`check_range(c.m_x, 1, 8);
|
||||
i = func_local_qual(c, 8);
|
||||
`checkd(i, 1);
|
||||
`check_range(c.m_x, 1, 7);
|
||||
i = func_sequential(c, 6);
|
||||
`checkd(i, 1);
|
||||
// Statement form: discards return value via void'.
|
||||
void'(c.randomize() with (m_x) { m_x > 0; m_x < 5; });
|
||||
`check_range(c.m_x, 1, 4);
|
||||
end
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
%Error: t/t_randomize_with_idlist_bad.v:14:26: Identifier 'm_z' in 'with (...)' identifier list is not a member of the randomize() target class (IEEE 1800-2023 18.7)
|
||||
14 | return obj.randomize() with (m_x, m_z) { m_x > 0; };
|
||||
| ^~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Warning-UNUSED: t/t_randomize_with_idlist_bad.v:19:26: Identifier 'm_y' in 'with (...)' identifier list is not referenced in the constraint block
|
||||
19 | return obj.randomize() with (m_x, m_y) { m_x > 0; };
|
||||
| ^~~~
|
||||
... For warning description see https://verilator.org/warn/UNUSED?v=latest
|
||||
... Use "/* verilator lint_off UNUSED */" 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=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// 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 m_x;
|
||||
rand int m_y;
|
||||
endclass
|
||||
|
||||
// 'm_z' is not a member of Cls -- typo (rejected as IEEE 1800-2023 18.7 violation).
|
||||
function int F_unknown(Cls obj);
|
||||
return obj.randomize() with (m_x, m_z) { m_x > 0; };
|
||||
endfunction
|
||||
|
||||
// 'm_y' is a class member but never referenced in the constraint -- flagged unused.
|
||||
function int F_unused(Cls obj);
|
||||
return obj.randomize() with (m_x, m_y) { m_x > 0; };
|
||||
endfunction
|
||||
|
||||
module t;
|
||||
Cls c;
|
||||
initial begin
|
||||
c = new;
|
||||
if (F_unknown(c) != 1) $stop;
|
||||
if (F_unused(c) != 1) $stop;
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
%Error: t/t_randomize_with_idlist_method_bad.v:13:21: 'with (...) { ... }' constraint block is only valid for randomize() (IEEE 1800-2023 18.7)
|
||||
13 | total = q.sum() with (item) { item > 0; };
|
||||
| ^~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%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=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// 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
|
||||
|
||||
module t;
|
||||
int q[$];
|
||||
int total;
|
||||
initial begin
|
||||
q.push_back(1);
|
||||
q.push_back(2);
|
||||
total = q.sum() with (item) { item > 0; };
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue