mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
Support function non-constant default arguments (#4470).
This commit is contained in:
parent
e6fb7e970d
commit
8bd1c63b32
1
Changes
1
Changes
@ -21,6 +21,7 @@ Verilator 5.015 devel
|
||||
* Support no-parentheses calls to static methods (#4432). [Krzysztof Boroński]
|
||||
* Support block_item_declaration in forks (#4455). [Krzysztof Boroński]
|
||||
* Support assignments of stream expressions on queues to packed values (#4458). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support function non-constant default arguments (#4470).
|
||||
* Support 'let'.
|
||||
* Optimize Verilator executable size by refactoring error reporting routines (#4446). [Anthony Donlon]
|
||||
* Optimize Verilation runtime pointers and graphs (#4396) (#4397) (#4398). [Krzysztof Bieganski, Antmicro Ltd]
|
||||
|
@ -119,6 +119,7 @@ protected:
|
||||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstNodeFTask;
|
||||
virtual AstNodeFTask* cloneType(const string& name) = 0;
|
||||
void dump(std::ostream& str = std::cout) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
bool maybePointedTo() const override { return true; }
|
||||
@ -179,6 +180,15 @@ public:
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
bool isFirstInMyListOfStatements(AstNode* n) const override { return n == stmtsp(); }
|
||||
void propagateAttrFrom(const AstNodeFTask* fromp) {
|
||||
// Creating a wrapper with e.g. cloneType(); preserve some attributes
|
||||
classMethod(fromp->classMethod());
|
||||
isHideLocal(fromp->isHideLocal());
|
||||
isHideProtected(fromp->isHideProtected());
|
||||
isVirtual(fromp->isVirtual());
|
||||
lifetime(fromp->lifetime());
|
||||
underGenerate(fromp->underGenerate());
|
||||
}
|
||||
};
|
||||
class AstNodeFile VL_NOT_FINAL : public AstNode {
|
||||
// Emitted Output file
|
||||
@ -1964,18 +1974,25 @@ public:
|
||||
string verilogKwd() const override;
|
||||
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
void propagateAttrFrom(AstVar* fromp) {
|
||||
void propagateAttrFrom(const AstVar* fromp) {
|
||||
// This is getting connected to fromp; keep attributes
|
||||
// Note the method below too
|
||||
if (fromp->attrFileDescr()) attrFileDescr(true);
|
||||
if (fromp->attrIsolateAssign()) attrIsolateAssign(true);
|
||||
if (fromp->isContinuously()) isContinuously(true);
|
||||
}
|
||||
void propagateWrapAttrFrom(const AstVar* fromp) {
|
||||
// Creating a function wrapper; keep attributes
|
||||
propagateAttrFrom(fromp);
|
||||
direction(fromp->direction());
|
||||
declDirection(fromp->declDirection());
|
||||
lifetime(fromp->lifetime());
|
||||
}
|
||||
bool gateMultiInputOptimizable() const {
|
||||
// Ok to gate optimize; must return false if propagateAttrFrom would do anything
|
||||
return !isUsedClock();
|
||||
}
|
||||
void combineType(AstVar* typevarp) {
|
||||
void combineType(const AstVar* typevarp) {
|
||||
// This is same as typevarp (for combining input & reg decls)
|
||||
// "this" is the input var. typevarp is the reg var.
|
||||
propagateAttrFrom(typevarp);
|
||||
@ -2094,6 +2111,9 @@ public:
|
||||
}
|
||||
ASTGEN_MEMBERS_AstFunc;
|
||||
bool hasDType() const override { return true; }
|
||||
AstNodeFTask* cloneType(const string& name) {
|
||||
return new AstFunc{fileline(), name, nullptr, nullptr};
|
||||
}
|
||||
};
|
||||
class AstLet final : public AstNodeFTask {
|
||||
// Verilog "let" statement
|
||||
@ -2108,6 +2128,7 @@ public:
|
||||
BROKEN_RTN(!VN_IS(stmtsp(), StmtExpr));
|
||||
return nullptr;
|
||||
}
|
||||
AstNodeFTask* cloneType(const string& name) { return new AstLet{fileline(), name}; }
|
||||
};
|
||||
class AstProperty final : public AstNodeFTask {
|
||||
// A property inside a module
|
||||
@ -2116,6 +2137,9 @@ public:
|
||||
: ASTGEN_SUPER_Property(fl, name, stmtp) {}
|
||||
ASTGEN_MEMBERS_AstProperty;
|
||||
bool hasDType() const override { return true; }
|
||||
AstNodeFTask* cloneType(const string& name) {
|
||||
return new AstProperty{fileline(), name, nullptr};
|
||||
}
|
||||
};
|
||||
class AstTask final : public AstNodeFTask {
|
||||
// A task inside a module
|
||||
@ -2123,6 +2147,7 @@ public:
|
||||
AstTask(FileLine* fl, const string& name, AstNode* stmtp)
|
||||
: ASTGEN_SUPER_Task(fl, name, stmtp) {}
|
||||
ASTGEN_MEMBERS_AstTask;
|
||||
AstNodeFTask* cloneType(const string& name) { return new AstTask{fileline(), name, nullptr}; }
|
||||
};
|
||||
|
||||
// === AstNodeFile ===
|
||||
|
166
src/V3Task.cpp
166
src/V3Task.cpp
@ -1379,13 +1379,15 @@ private:
|
||||
nodep->replaceWith(cnewp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
} else if (VN_IS(nodep->backp(), NodeAssign)) {
|
||||
UASSERT_OBJ(nodep->taskp()->isFunction(), nodep, "func reference to non-function");
|
||||
UASSERT_OBJ(nodep->taskp()->isFunction(), nodep,
|
||||
"funcref-like assign to non-function");
|
||||
insertBeforeStmt(nodep, beginp);
|
||||
AstVarRef* const outrefp = new AstVarRef{nodep->fileline(), outvscp, VAccess::READ};
|
||||
nodep->replaceWith(outrefp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
} else if (!VN_IS(nodep->backp(), StmtExpr)) {
|
||||
UASSERT_OBJ(nodep->taskp()->isFunction(), nodep, "func reference to non-function");
|
||||
UASSERT_OBJ(nodep->taskp()->isFunction(), nodep,
|
||||
"funcref-like expression to non-function");
|
||||
AstVarRef* const outrefp = new AstVarRef{nodep->fileline(), outvscp, VAccess::READ};
|
||||
beginp = new AstExprStmt{nodep->fileline(), beginp, outrefp};
|
||||
nodep->replaceWith(beginp);
|
||||
@ -1493,13 +1495,6 @@ private:
|
||||
iterateChildren(nodep);
|
||||
m_insStmtp = nullptr; // Next thing should be new statement
|
||||
}
|
||||
void visit(AstVar* nodep) override {
|
||||
if (nodep->isFuncLocal() && nodep->direction() == VDirection::INPUT && nodep->valuep()) {
|
||||
// It's the default value of optional argument.
|
||||
// Such values are added to function calls on this stage and aren't needed here.
|
||||
pushDeletep(nodep->valuep()->unlinkFrBack());
|
||||
}
|
||||
}
|
||||
void visit(AstStmtExpr* nodep) override {
|
||||
m_insStmtp = nodep;
|
||||
iterateChildren(nodep);
|
||||
@ -1529,13 +1524,15 @@ public:
|
||||
|
||||
const char* const V3Task::s_dpiTemporaryVarSuffix = "__Vcvt";
|
||||
|
||||
V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp) {
|
||||
V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
|
||||
V3TaskConnectState* statep) {
|
||||
// Output list will be in order of the port declaration variables (so
|
||||
// func calls are made right in C)
|
||||
// Missing pin/expr? We return (pinvar, nullptr)
|
||||
// Extra pin/expr? We clean it up
|
||||
|
||||
UINFO(9, "taskConnects " << nodep << endl);
|
||||
std::map<const std::string, int> nameToIndex;
|
||||
std::set<const AstVar*> argWrap; // Which ports are defaulted, forcing arg wrapper creation
|
||||
V3TaskConnects tconnects;
|
||||
UASSERT_OBJ(nodep->taskp(), nodep, "unlinked");
|
||||
|
||||
@ -1624,16 +1621,24 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp)
|
||||
// The default value for this port might be a constant
|
||||
// expression that hasn't been folded yet. Try folding it
|
||||
// now; we don't have much to lose if it fails.
|
||||
newvaluep = V3Const::constifyParamsEdit(VN_AS(portp->valuep(), NodeExpr));
|
||||
newvaluep = V3Const::constifyEdit(VN_AS(portp->valuep(), NodeExpr));
|
||||
if (!VN_IS(newvaluep, Const)) {
|
||||
// Problem otherwise is we might have a varref, task
|
||||
// call, or something else that only makes sense in the
|
||||
// domain of the function, not the callee.
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Non-constant default value in missing argument "
|
||||
<< portp->prettyNameQ() << " in function call to "
|
||||
<< nodep->taskp()->prettyTypeName());
|
||||
newvaluep = new AstConst{nodep->fileline(), AstConst::Unsized32{}, 0};
|
||||
if (statep) {
|
||||
portp->pinNum(i + 1); // Make sure correct, will use to build name
|
||||
UINFO(9, "taskConnects arg wrapper needed " << portp->valuep() << endl);
|
||||
argWrap.emplace(portp);
|
||||
} else { // statep = nullptr, called too late or otherwise to handle args
|
||||
// Problem otherwise is we might have a varref, task
|
||||
// call, or something else that only makes sense in the
|
||||
// domain of the function (or class containing the method),
|
||||
// versus that of the callee.
|
||||
nodep->v3warn(
|
||||
E_UNSUPPORTED,
|
||||
"Unsupported: Non-constant default value in missing argument "
|
||||
<< portp->prettyNameQ() << " in function call to "
|
||||
<< nodep->taskp()->prettyTypeName());
|
||||
newvaluep = new AstConst{nodep->fileline(), AstConst::Unsized32{}, 0};
|
||||
}
|
||||
}
|
||||
}
|
||||
newvaluep = newvaluep->cloneTree(true);
|
||||
@ -1671,12 +1676,131 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp)
|
||||
if (debug() >= 9) { // LCOV_EXCL_START
|
||||
nodep->dumpTree("- ftref-out: ");
|
||||
for (int i = 0; i < tpinnum; ++i) {
|
||||
UINFO(0, " pin " << i << " conn=" << cvtToHex(tconnects[i].second) << endl);
|
||||
UINFO(0, " pin " << i << " pin=" << cvtToHex(tconnects[i].first)
|
||||
<< " conn=" << cvtToHex(tconnects[i].second) << endl);
|
||||
}
|
||||
} // LCOV_EXCL_STOP
|
||||
|
||||
if (!argWrap.empty()) {
|
||||
UINFO(9, "Arg wrapper generation " << nodep << endl);
|
||||
// Create wrapper function with default argument settings.
|
||||
// Needed because the default needs symbol table of the called function.
|
||||
taskConnectWrap(nodep, tconnects, statep, argWrap);
|
||||
// Regenerate all connections, this time connecting to the wrapper
|
||||
return taskConnects(nodep, nodep->taskp()->stmtsp(),
|
||||
// statep null, so can't recurse forever
|
||||
nullptr);
|
||||
}
|
||||
return tconnects;
|
||||
}
|
||||
|
||||
void V3Task::taskConnectWrap(AstNodeFTaskRef* nodep, const V3TaskConnects& tconnects,
|
||||
V3TaskConnectState* statep, const std::set<const AstVar*>& argWrap) {
|
||||
statep->setDidWrap();
|
||||
// Make wrapper name such that is same iff same args are defaulted
|
||||
std::string newname = nodep->name() + "__Vtcwrap";
|
||||
for (const AstVar* varp : argWrap) newname += "_" + cvtToStr(varp->pinNum());
|
||||
const auto namekey = std::make_pair(nodep->taskp(), newname);
|
||||
auto& wrapMapr = statep->wrapMap();
|
||||
const auto it = wrapMapr.find(namekey);
|
||||
AstNodeFTask* newTaskp;
|
||||
if (it != wrapMapr.end()) {
|
||||
newTaskp = it->second;
|
||||
} else {
|
||||
newTaskp = taskConnectWrapNew(nodep->taskp(), newname, tconnects, argWrap);
|
||||
wrapMapr.emplace(namekey, newTaskp);
|
||||
}
|
||||
|
||||
// Remove the defaulted arguments from original outside call
|
||||
for (const auto& tconnect : tconnects) {
|
||||
const AstVar* const portp = tconnect.first;
|
||||
AstArg* const argp = tconnect.second;
|
||||
if (argWrap.find(portp) != argWrap.end()) { // Removed arg
|
||||
statep->pushDeletep(argp->unlinkFrBack());
|
||||
}
|
||||
}
|
||||
// Change outside call to connect to new function
|
||||
nodep->taskp(newTaskp);
|
||||
nodep->name(newTaskp->name());
|
||||
// if (debug() >= 9) nodep->dumpTree("-taskConnectWrap-call ");
|
||||
}
|
||||
|
||||
AstNodeFTask* V3Task::taskConnectWrapNew(AstNodeFTask* taskp, const string& newname,
|
||||
const V3TaskConnects& tconnects,
|
||||
const std::set<const AstVar*>& argWrap) {
|
||||
std::map<const AstVar*, AstVar*> oldNewVars; // Old -> new var mappings
|
||||
|
||||
AstNodeFTask* const newTaskp = taskp->cloneType(newname);
|
||||
newTaskp->propagateAttrFrom(taskp);
|
||||
taskp->addNextHere(newTaskp);
|
||||
|
||||
AstNodeFTaskRef* newCallp = nullptr;
|
||||
AstNode* newCallInsertp = nullptr;
|
||||
if (VN_IS(taskp, Func)) {
|
||||
AstVar* const fvarp = VN_AS(taskp->fvarp(), Var);
|
||||
UASSERT(fvarp, "FuncRef without fvar");
|
||||
AstVar* const newFVarp = fvarp->cloneTree(true);
|
||||
oldNewVars.emplace(fvarp, newFVarp);
|
||||
newFVarp->name(newTaskp->name());
|
||||
newTaskp->fvarp(newFVarp);
|
||||
newTaskp->dtypeFrom(newFVarp);
|
||||
newCallp = new AstFuncRef{taskp->fileline(), taskp->name(), nullptr};
|
||||
newCallp->taskp(taskp);
|
||||
newCallp->dtypeFrom(newFVarp);
|
||||
newCallInsertp
|
||||
= new AstAssign{taskp->fileline(),
|
||||
new AstVarRef{fvarp->fileline(), newFVarp, VAccess::WRITE}, newCallp};
|
||||
newCallInsertp->dtypeFrom(newFVarp);
|
||||
} else if (VN_IS(taskp, Task)) {
|
||||
newCallp = new AstTaskRef{taskp->fileline(), taskp->name(), nullptr};
|
||||
newCallp->taskp(taskp);
|
||||
newCallInsertp = new AstStmtExpr{taskp->fileline(), newCallp};
|
||||
} else {
|
||||
taskp->v3fatalSrc("Unsupported: Non-constant default value in missing argument in a "
|
||||
<< taskp->prettyTypeName());
|
||||
}
|
||||
|
||||
// Create wrapper's ports matching original's
|
||||
for (const auto& tconnect : tconnects) {
|
||||
AstVar* const portp = tconnect.first;
|
||||
AstVar* newPortp;
|
||||
if (argWrap.find(portp) == argWrap.end()) { // Not removed arg
|
||||
newPortp = new AstVar{portp->fileline(), portp->varType(), portp->name(), portp};
|
||||
newPortp->propagateWrapAttrFrom(portp);
|
||||
newPortp->funcLocal(true);
|
||||
if (newPortp->valuep()) newPortp->valuep()->unlinkFrBack()->deleteTree();
|
||||
newTaskp->addStmtsp(newPortp);
|
||||
} else { // Defaulting arg
|
||||
AstNodeExpr* const valuep = VN_AS(portp->valuep(), NodeExpr);
|
||||
// Create local temporary
|
||||
newPortp = new AstVar{portp->fileline(), VVarType::BLOCKTEMP, portp->name(),
|
||||
portp->dtypep()};
|
||||
newPortp->propagateAttrFrom(portp);
|
||||
newPortp->funcLocal(true);
|
||||
newTaskp->addStmtsp(newPortp);
|
||||
// Runtime-assign it to the default
|
||||
AstAssign* const newAssignp = new AstAssign{
|
||||
valuep->fileline(), new AstVarRef{valuep->fileline(), newPortp, VAccess::WRITE},
|
||||
valuep->cloneTree(true)};
|
||||
newTaskp->addStmtsp(newAssignp);
|
||||
}
|
||||
oldNewVars.emplace(portp, newPortp);
|
||||
AstArg* const newArgp
|
||||
= new AstArg{portp->fileline(), portp->name(),
|
||||
new AstVarRef{portp->fileline(), newPortp, VAccess::READ}};
|
||||
newCallp->addPinsp(newArgp);
|
||||
}
|
||||
// Create wrapper call to original, passing arguments, adding setting of return value
|
||||
newTaskp->addStmtsp(newCallInsertp);
|
||||
// Replace any varref's to original to new ports (e.g. in argument equations)
|
||||
newTaskp->foreach([=](AstVarRef* refp) {
|
||||
const auto it = oldNewVars.find(refp->varp());
|
||||
if (it != oldNewVars.end()) refp->varp(it->second);
|
||||
});
|
||||
// if (debug() >= 9) newTaskp->dumpTree("-taskConnectWrap-new ");
|
||||
return newTaskp;
|
||||
}
|
||||
|
||||
string V3Task::assignInternalToDpi(AstVar* portp, bool isPtr, const string& frSuffix,
|
||||
const string& toSuffix, const string& frPrefix) {
|
||||
// Create assignment from internal format into DPI temporary
|
||||
|
23
src/V3Task.h
23
src/V3Task.h
@ -31,6 +31,20 @@
|
||||
using V3TaskConnect = std::pair<AstVar*, AstArg*>; // [port, pin-connects-to]
|
||||
using V3TaskConnects = std::vector<V3TaskConnect>; // [ [port, pin-connects-to] ... ]
|
||||
|
||||
class V3TaskConnectState final {
|
||||
VNDeleter m_deleter; // Allow delayed deletion of nodes
|
||||
bool m_didWrap = false; // Made a wrapper
|
||||
using WrapMap = std::map<std::pair<AstNodeFTask*, std::string>, AstNodeFTask*>;
|
||||
WrapMap m_wrapMap; // Map of {old function, arguments} -> new function
|
||||
public:
|
||||
V3TaskConnectState() {}
|
||||
~V3TaskConnectState() = default;
|
||||
void pushDeletep(AstNode* nodep) { m_deleter.pushDeletep(nodep); }
|
||||
bool didWrap() const { return m_didWrap; }
|
||||
void setDidWrap() { m_didWrap = true; }
|
||||
WrapMap& wrapMap() { return m_wrapMap; }
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Task final {
|
||||
@ -39,7 +53,14 @@ class V3Task final {
|
||||
public:
|
||||
static void taskAll(AstNetlist* nodep);
|
||||
/// Return vector of [port, pin-connects-to] (SLOW)
|
||||
static V3TaskConnects taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp);
|
||||
static V3TaskConnects taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
|
||||
V3TaskConnectState* statep = nullptr);
|
||||
static void taskConnectWrap(AstNodeFTaskRef* nodep, const V3TaskConnects& tconnects,
|
||||
V3TaskConnectState* statep,
|
||||
const std::set<const AstVar*>& argWrap);
|
||||
static AstNodeFTask* taskConnectWrapNew(AstNodeFTask* taskp, const string& newname,
|
||||
const V3TaskConnects& tconnects,
|
||||
const std::set<const AstVar*>& argWrap);
|
||||
static string assignInternalToDpi(AstVar* portp, bool isPtr, const string& frSuffix,
|
||||
const string& toSuffix, const string& frPrefix = "");
|
||||
static string assignDpiToInternal(const string& lhsName, AstVar* rhsp);
|
||||
|
@ -224,6 +224,7 @@ private:
|
||||
|
||||
// STATE
|
||||
VMemberMap m_memberMap; // Member names cached for fast lookup
|
||||
V3TaskConnectState m_taskConnectState; // State to cache V3Task::taskConnects
|
||||
WidthVP* m_vup = nullptr; // Current node state
|
||||
const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations
|
||||
const AstEnumItem* m_enumItemp = nullptr; // Current enum item
|
||||
@ -5483,7 +5484,10 @@ private:
|
||||
// And do the arguments to the task/function too
|
||||
do {
|
||||
reloop:
|
||||
const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
|
||||
// taskConnects may create a new task, and change nodep->taskp()
|
||||
const V3TaskConnects tconnects
|
||||
= V3Task::taskConnects(nodep, nodep->taskp()->stmtsp(), &m_taskConnectState);
|
||||
if (m_taskConnectState.didWrap()) m_memberMap.clear(); // As added a member
|
||||
for (const auto& tconnect : tconnects) {
|
||||
const AstVar* const portp = tconnect.first;
|
||||
AstArg* const argp = tconnect.second;
|
||||
|
@ -237,6 +237,15 @@ private:
|
||||
classEncapCheck(nodep, nodep->varp(), classrefp->classp());
|
||||
} // else might be struct, etc
|
||||
}
|
||||
void visit(AstVar* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
editDType(nodep);
|
||||
if (nodep->isFuncLocal() && nodep->direction() == VDirection::INPUT && nodep->valuep()) {
|
||||
// It's the default value of optional argument.
|
||||
// Such values are added to function calls in V3Width so can be removed now
|
||||
pushDeletep(nodep->valuep()->unlinkFrBack());
|
||||
}
|
||||
}
|
||||
void visit(AstNodePreSel* nodep) override { // LCOV_EXCL_LINE
|
||||
// This check could go anywhere after V3Param
|
||||
nodep->v3fatalSrc("Presels should have been removed before this point");
|
||||
|
23
test_regress/t/t_func_arg_complex.pl
Executable file
23
test_regress/t/t_func_arg_complex.pl
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2023 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
|
||||
|
||||
scenarios(vlt => 1);
|
||||
|
||||
compile(
|
||||
v_flags2 => ["-Wno-PKGNODECL -Wno-UNPACKED -Wno-RANDC -Wno-IMPLICITSTATIC -Wno-CONSTRAINTIGN -Wno-MISINDENT",
|
||||
"--error-limit 200"],
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
106
test_regress/t/t_func_arg_complex.v
Normal file
106
test_regress/t/t_func_arg_complex.v
Normal file
@ -0,0 +1,106 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2023 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`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);
|
||||
|
||||
class Cls;
|
||||
enum { ONEK = 1000, TWOK = 2000 } sev_t;
|
||||
int m_default_data;
|
||||
function int trigger(int data=get_default_data());
|
||||
return data;
|
||||
endfunction
|
||||
|
||||
task triggert(output int o, input int data=get_default_data());
|
||||
o = data;
|
||||
endtask
|
||||
|
||||
virtual function int get_default_data();
|
||||
return m_default_data;
|
||||
endfunction
|
||||
|
||||
function int uvm_report(int severity,
|
||||
int verbosity = (severity == 1) ? ONEK : TWOK);
|
||||
return verbosity;
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
module t(/*AUTOARG*/);
|
||||
|
||||
function int mod_trigger(int data=mod_data());
|
||||
return data;
|
||||
endfunction
|
||||
|
||||
task mod_triggert(output int o, input int data=mod_data());
|
||||
o = data;
|
||||
endtask
|
||||
|
||||
int mod_default_data;
|
||||
function int mod_data();
|
||||
return mod_default_data;
|
||||
endfunction
|
||||
|
||||
int v;
|
||||
|
||||
initial begin
|
||||
begin
|
||||
mod_triggert(v, 1234);
|
||||
`checkd(v, 1234);
|
||||
|
||||
mod_default_data = 42;
|
||||
v = mod_trigger();
|
||||
`checkd(v, 42);
|
||||
v = mod_trigger(11);
|
||||
`checkd(v, 11);
|
||||
mod_default_data = 43;
|
||||
v = mod_trigger();
|
||||
`checkd(v, 43);
|
||||
v = mod_trigger(); // Multiple to test look up of duplicates
|
||||
`checkd(v, 43);
|
||||
|
||||
mod_default_data = 52;
|
||||
mod_triggert(v);
|
||||
`checkd(v, 52);
|
||||
mod_triggert(v); // Multiple to test look up of duplicates
|
||||
`checkd(v, 52);
|
||||
end
|
||||
begin
|
||||
Cls c = new;
|
||||
|
||||
c.m_default_data = 42;
|
||||
v = c.trigger();
|
||||
`checkd(v, 42);
|
||||
v = c.trigger(11);
|
||||
`checkd(v, 11);
|
||||
c.m_default_data = 43;
|
||||
v = c.trigger();
|
||||
`checkd(v, 43);
|
||||
v = c.trigger(); // Multiple to test look up of duplicates
|
||||
`checkd(v, 43);
|
||||
v = c.trigger(); // Multiple to test look up of duplicates
|
||||
`checkd(v, 43);
|
||||
|
||||
c.m_default_data = 52;
|
||||
c.triggert(v);
|
||||
`checkd(v, 52);
|
||||
c.triggert(v); // Multiple to test look up of duplicates
|
||||
`checkd(v, 52);
|
||||
|
||||
v = c.uvm_report(1);
|
||||
`checkd(v, 1000);
|
||||
v = c.uvm_report(2);
|
||||
`checkd(v, 2000);
|
||||
v = c.uvm_report(1, 111);
|
||||
`checkd(v, 111);
|
||||
v = c.uvm_report(1, 222);
|
||||
`checkd(v, 222);
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
@ -687,12 +687,8 @@ endfunction
|
||||
function void uvm_report( uvm_severity severity,
|
||||
string id,
|
||||
string message,
|
||||
//TODO issue #4470 - Fix UVM function non-constant default arguments
|
||||
//TODO %Error: Internal Error: t/t_uvm_pkg_todo.vh:9957:54: ../V3Broken.cpp:262: VarRef missing VarScope pointer
|
||||
//TODO int verbosity = (severity == uvm_severity'(UVM_ERROR)) ? UVM_LOW :
|
||||
//TODO (severity == uvm_severity'(UVM_FATAL)) ? UVM_NONE : UVM_MEDIUM,
|
||||
//TODO remove next line:
|
||||
int verbosity = UVM_LOW,
|
||||
int verbosity = (severity == uvm_severity'(UVM_ERROR)) ? UVM_LOW :
|
||||
(severity == uvm_severity'(UVM_FATAL)) ? UVM_NONE : UVM_MEDIUM,
|
||||
string filename = "",
|
||||
int line = 0,
|
||||
string context_name = "",
|
||||
@ -7554,11 +7550,7 @@ class uvm_event#(type T=uvm_object) extends uvm_event_base;
|
||||
wait_ptrigger();
|
||||
data = get_trigger_data();
|
||||
endtask
|
||||
//TODO issue #4470 - Fix UVM function non-constant default arguments
|
||||
//TODO %Error: t/t_uvm_pkg_todo.vh:7549:47: Expecting expression to be constant, but can't determine constant for FUNCREF 'get_default_data'
|
||||
//TODO virtual function void trigger (T data=get_default_data());
|
||||
//TODO remove next line:
|
||||
virtual function void trigger (T data=null);
|
||||
virtual function void trigger (T data=get_default_data());
|
||||
int skip;
|
||||
cb_type cb_q[$];
|
||||
skip=0;
|
||||
@ -9958,12 +9950,8 @@ class uvm_report_object extends uvm_object;
|
||||
virtual function void uvm_report( uvm_severity severity,
|
||||
string id,
|
||||
string message,
|
||||
//TODO issue #4470 - Fix UVM function non-constant default arguments
|
||||
//TODO %Error: Internal Error: t/t_uvm_pkg_todo.vh:9957:54: ../V3Broken.cpp:262: VarRef missing VarScope pointer
|
||||
//TODO int verbosity = (severity == uvm_severity'(UVM_ERROR)) ? UVM_LOW :
|
||||
//TODO (severity == uvm_severity'(UVM_FATAL)) ? UVM_NONE : UVM_MEDIUM,
|
||||
//TODO remove next line:
|
||||
int verbosity = UVM_ERROR,
|
||||
int verbosity = (severity == uvm_severity'(UVM_ERROR)) ? UVM_LOW :
|
||||
(severity == uvm_severity'(UVM_FATAL)) ? UVM_NONE : UVM_MEDIUM,
|
||||
string filename = "",
|
||||
int line = 0,
|
||||
string context_name = "",
|
||||
@ -18797,13 +18785,8 @@ class uvm_sequence_item extends uvm_transaction;
|
||||
virtual function void uvm_report( uvm_severity severity,
|
||||
string id,
|
||||
string message,
|
||||
|
||||
//TODO issue #4470 - Fix UVM function non-constant default arguments
|
||||
//TODO %Error: Internal Error: t/t_uvm_pkg_todo.vh:18800:54: ../V3Broken.cpp:262: VarRef missing VarScope pointer
|
||||
//TODO int verbosity = (severity == uvm_severity'(UVM_ERROR)) ? UVM_LOW :
|
||||
//TODO (severity == uvm_severity'(UVM_FATAL)) ? UVM_NONE : UVM_MEDIUM,
|
||||
//TODO remove next line:
|
||||
int verbosity = UVM_LOW,
|
||||
int verbosity = (severity == uvm_severity'(UVM_ERROR)) ? UVM_LOW :
|
||||
(severity == uvm_severity'(UVM_FATAL)) ? UVM_NONE : UVM_MEDIUM,
|
||||
string filename = "",
|
||||
int line = 0,
|
||||
string context_name = "",
|
||||
|
Loading…
Reference in New Issue
Block a user