Support function non-constant default arguments (#4470).

This commit is contained in:
Wilson Snyder 2023-09-16 17:37:25 -04:00
parent e6fb7e970d
commit 8bd1c63b32
9 changed files with 345 additions and 49 deletions

View File

@ -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]

View File

@ -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 ===

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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");

View 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;

View 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

View File

@ -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 = "",