Add error on function invoking task.

This commit is contained in:
Wilson Snyder 2025-09-23 19:51:34 -04:00
parent 90bc1daa9d
commit 734e7a9526
12 changed files with 153 additions and 56 deletions

View File

@ -21,6 +21,7 @@ Verilator 5.041 devel
* Add $(LDFLAGS) and $(LIBS) to when building shared libraries (#6425) (#6426). [Ahmed El-Mahmoudy]
* Add ASSIGNEQEXPR when use `=` inside expressions (#5567). [Ethan Sifferman]
* Add IMPLICITSTATIC also on procedure variables.
* Add error on function invoking task.
* Deprecate sensitivity list on public_flat_rw attributes (#6443). [Geza Lore]
* Deprecate clocker attribute and --clk option (#6463). [Geza Lore]
* Support modports referencing clocking blocks (#4555) (#6436). [Ryszard Rozak, Antmicro Ltd.]

View File

@ -132,6 +132,8 @@ class AstNodeFTask VL_NOT_FINAL : public AstNode {
bool m_recursive : 1; // Recursive or part of recursion
bool m_static : 1; // Static method in class
bool m_underGenerate : 1; // Under generate (for warning)
bool m_verilogFunction : 1; // Declared by user as function (versus internal-made)
bool m_verilogTask : 1; // Declared by user as task (versus internal-made)
bool m_virtual : 1; // Virtual method in class
bool m_needProcess : 1; // Needs access to VlProcess of the caller
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
@ -162,6 +164,8 @@ protected:
, m_recursive{false}
, m_static{false}
, m_underGenerate{false}
, m_verilogFunction{false}
, m_verilogTask{false}
, m_virtual{false}
, m_needProcess{false} {
addStmtsp(stmtsp);
@ -227,6 +231,10 @@ public:
void isStatic(bool flag) { m_static = flag; }
bool underGenerate() const { return m_underGenerate; }
void underGenerate(bool flag) { m_underGenerate = flag; }
bool verilogFunction() const { return m_verilogFunction; }
void verilogFunction(bool flag) { m_verilogFunction = flag; }
bool verilogTask() const { return m_verilogTask; }
void verilogTask(bool flag) { m_verilogTask = flag; }
bool isVirtual() const { return m_virtual; }
void isVirtual(bool flag) { m_virtual = flag; }
bool needProcess() const { return m_needProcess; }

View File

@ -2901,6 +2901,8 @@ void AstNodeFTask::dump(std::ostream& str) const {
if (recursive()) str << " [RECURSIVE]";
if (taskPublic()) str << " [PUBLIC]";
if (isStatic()) str << " [STATIC]";
if (verilogTask()) str << " [VTASK]";
if (verilogFunction()) str << " [VFUNC]";
if ((dpiImport() || dpiExport()) && cname() != name()) str << " [c=" << cname() << "]";
}
bool AstNodeFTask::isPure() {

View File

@ -45,11 +45,13 @@ class WidthCommitVisitor final : public VNVisitor {
const VNUser2InUse m_inuser2;
// STATE
AstNodeModule* m_modp = nullptr;
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
AstNodeModule* m_modp = nullptr; // Current module
std::string m_contNba; // In continuous- or non-blocking assignment
bool m_dynsizedelem
= false; // Writing a dynamically-sized array element, not the array itself
VMemberMap m_memberMap; // Member names cached for fast lookup
bool m_taskRefWarn = true; // Allow task reference warnings
bool m_underSel = false; // Under AstMemberSel or AstSel
bool m_underAlwaysClocked = false; // Under always with sequential SenTree
std::vector<AstNew*> m_virtualNewsp; // Instantiations of virtual classes
@ -216,6 +218,13 @@ private:
iterateChildren(nodep);
editDType(nodep);
}
void visit(AstFork* nodep) override {
VL_RESTORER(m_taskRefWarn);
// fork..join_any is allowed to call tasks, and UVM does this
if (nodep->joinType().joinNone()) m_taskRefWarn = false;
iterateChildren(nodep);
editDType(nodep);
}
void visit(AstSenTree* nodep) override {
if (nodep->sensesp() && nodep->sensesp()->isComboStar()) {
UASSERT_OBJ(!nodep->sensesp()->nextp(), nodep, "Shouldn't be senitems after .*");
@ -317,6 +326,8 @@ private:
void visit(AstNodeFTask* nodep) override {
if (!nodep->taskPublic() && !nodep->dpiExport() && !nodep->dpiImport())
m_tasksp.push_back(nodep);
VL_RESTORER(m_ftaskp);
m_ftaskp = nodep;
iterateChildren(nodep);
editDType(nodep);
{
@ -419,6 +430,17 @@ private:
iterateChildren(nodep);
editDType(nodep);
classEncapCheck(nodep, nodep->taskp(), VN_CAST(nodep->classOrPackagep(), Class));
if (nodep->taskp() && nodep->taskp()->verilogTask() && m_ftaskp
&& m_ftaskp->verilogFunction() && m_taskRefWarn) {
nodep->v3error("Functions cannot invoke tasks (IEEE 1800-2023 13.4)\n"
<< nodep->warnContextPrimary() << "\n"
<< nodep->warnMore() << "... Suggest make caller 'function "
<< m_ftaskp->prettyName() << "' a task\n"
<< m_ftaskp->warnContextSecondary() << "\n"
<< nodep->warnMore() << "... Or, suggest make called 'task "
<< nodep->taskp()->prettyName() << "' a function void\n"
<< nodep->taskp()->warnContextSecondary());
}
if (nodep->taskp()) nodep->taskp()->user2(1);
if (AstNew* const newp = VN_CAST(nodep, New)) {
if (!VN_IS(newp->backp(), Assign)) return;

View File

@ -4546,14 +4546,17 @@ lifetime<lifetime>: // ==IEEE: lifetime
taskId<nodeFTaskp>:
id
{ $$ = new AstTask{$<fl>$, *$1, nullptr}; }
{ $$ = new AstTask{$<fl>$, *$1, nullptr};
$$->verilogTask(true); }
//
| id/*interface_identifier*/ '.' idAny
{ $$ = new AstTask{$<fl>$, *$3, nullptr};
$$->verilogTask(true);
BBUNSUP($2, "Unsupported: Out of block function declaration"); }
//
| packageClassScope id
{ $$ = new AstTask{$<fl>$, *$2, nullptr};
$$->verilogTask(true);
$$->classOrPackagep($1);
$$->classMethod(true); }
;
@ -4584,18 +4587,23 @@ funcId<nodeFTaskp>: // IEEE: function_data_type_or_implicit + part o
$$->fvarp(GRAMMARP->createArray(refp, $4, true)); }
// // To verilator tasks are the same as void functions (we separately detect time passing)
| yVOID taskId
{ $$ = $2; }
{ $$ = $2; // Internals represent it as a task, not a function (TODO cleanup)
$$->verilogTask(false);
$$->verilogFunction(true); }
;
funcIdNew<nodeFTaskp>: // IEEE: from class_constructor_declaration
yNEW__ETC
{ $$ = new AstFunc{$<fl>1, "new", nullptr, nullptr};
$$->verilogFunction(true);
$$->isConstructor(true); }
| yNEW__PAREN
{ $$ = new AstFunc{$<fl>1, "new", nullptr, nullptr};
$$->verilogFunction(true);
$$->isConstructor(true); }
| packageClassScopeNoId yNEW__PAREN
{ $$ = new AstFunc{$<fl>2, "new", nullptr, nullptr};
$$->verilogFunction(true);
$$->classOrPackagep($1);
$$->isConstructor(true);
$$->classMethod(true); }
@ -4605,16 +4613,19 @@ fIdScoped<funcp>: // IEEE: part of function_body_declaration/task_
// // IEEE: [ interface_identifier '.' | class_scope ] function_identifier
id
{ $<fl>$ = $<fl>1;
$$ = new AstFunc{$<fl>$, *$1, nullptr, nullptr}; }
$$ = new AstFunc{$<fl>$, *$1, nullptr, nullptr};
$$->verilogFunction(true); }
//
| id/*interface_identifier*/ '.' idAny
{ $<fl>$ = $<fl>1;
$$ = new AstFunc{$<fl>$, *$1, nullptr, nullptr};
$$->verilogFunction(true);
BBUNSUP($2, "Unsupported: Out of block function declaration"); }
//
| packageClassScope id
{ $<fl>$ = $<fl>1;
$$ = new AstFunc{$<fl>$, *$2, nullptr, nullptr};
$$->verilogFunction(true);
$$->classMethod(true);
$$->classOrPackagep($1); }
;

View File

@ -1,10 +1,10 @@
%Error: t/t_func_task_bad.v:10:11: Cannot call a task/void-function as a function: 'task_as_func'
: ... note: In instance 't'
10 | if (task_as_func(1'b0)) $stop;
| ^~~~~~~~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_func_task_bad.v:10:7: Logical operator IF expects a non-complex data type on the If.
%Error: t/t_func_task_bad.v:10:9: Cannot call a task/void-function as a function: 'a_task'
: ... note: In instance 't'
10 | if (task_as_func(1'b0)) $stop;
| ^~
10 | if (a_task(1'b0)) $stop;
| ^~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_func_task_bad.v:10:5: Logical operator IF expects a non-complex data type on the If.
: ... note: In instance 't'
10 | if (a_task(1'b0)) $stop;
| ^~
%Error: Exiting due to

View File

@ -6,12 +6,12 @@
module t;
initial begin
if (task_as_func(1'b0)) $stop;
end
initial begin
if (a_task(1'b0)) $stop; // <--- Bad: Calling task _as_ function
end
task task_as_func;
input ign;
endtask
task a_task;
input ign;
endtask
endmodule

View File

@ -0,0 +1,12 @@
%Error: t/t_func_task_bad2.v:14:5: Functions cannot invoke tasks (IEEE 1800-2023 13.4)
: ... note: In instance 't'
14 | a_task(1'b0);
| ^~~~~~
: ... Suggest make caller 'function func_calls_task' a task
13 | function void func_calls_task;
| ^~~~~~~~~~~~~~~
: ... Or, suggest make called 'task a_task' a function void
9 | task a_task;
| ^~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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('linter')
test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,23 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2011 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t;
task a_task;
input ign;
endtask
function void func_calls_task;
a_task(1'b0); // <--- Bad: Calling task _from_ function
endfunction
function void func_ok;
fork
a_task(1'b0); // ok, and done in UVM
join_none
endfunction
endmodule

View File

@ -4,39 +4,41 @@
// any use, without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
module t (input clk);
// AstLet and AstProperty are also NodeFTasks, but lets are substituted earlier and properties should be "used" by their asserts so also not deadified
let nclk = ~clk;
assert property(@(posedge clk)1);
module t (
input clk
);
// AstLet and AstProperty are also NodeFTasks, but lets are substituted earlier and properties should be "used" by their asserts so also not deadified
let nclk = ~clk;
assert property (@(posedge clk) 1);
function void livefunc();
endfunction
task livetask;
endtask
// Tasks/functions that are called somewhere will not be deadified
initial begin
livefunc();
livetask();
$finish;
end
function void livefunc();
endfunction
task livetask;
endtask
// Tasks/functions that are called somewhere will not be deadified
initial begin
livefunc();
livetask();
$finish;
end
// These should be deadified
function deadfunc();
deeptask2();
endfunction
task deadtask;
deeptask1();
endtask
// A chain of dead tasks calling each other to ensure V3Dead can remove chained dead tasks
task deeptask1;
deeptask2();
endtask
task deeptask2;
deeptask3();
endtask
task deeptask3;
deeptask4();
endtask
task deeptask4;
endtask
// These should be deadified
task deadfunc();
deeptask2();
endtask
task deadtask;
deeptask1();
endtask
// A chain of dead tasks calling each other to ensure V3Dead can remove chained dead tasks
task deeptask1;
deeptask2();
endtask
task deeptask2;
deeptask3();
endtask
task deeptask3;
deeptask4();
endtask
task deeptask4;
endtask
endmodule

View File

@ -14,7 +14,7 @@ module t();
class Foo;
function void do_something(int captured_var);
task do_something(int captured_var);
fork
begin
int my_var;
@ -23,7 +23,7 @@ module t();
my_other_var = captured_var; /* Capture the same value "twice" */
my_var++;
static_var++; /* Write to a value with static lifetime (valid) */
$display("Vars in forked process: %d %d", my_var, my_other_var);
$display("Vars in forked process: %0d %0d", my_var, my_other_var);
if (my_var != 2)
$stop;
if (my_other_var != 1)
@ -32,7 +32,7 @@ module t();
end
join_none
$display("Leaving fork's parent");
endfunction
endtask
endclass
initial begin
@ -44,14 +44,14 @@ module t();
end
always @(evt) begin
$display("Static variable: %d", static_var);
$display("Static variable: %0d", static_var);
if (static_var != 1)
$stop;
fork
begin
automatic int my_auto_var = 0;
my_auto_var++;
$display("Automatic variable in fork: %d", my_auto_var);
$display("Automatic variable in fork: %0d", my_auto_var);
if (my_auto_var != 1) $stop;
end
join_none