Support writing to inout in fork after timing (#7345)

This commit is contained in:
Kamil Danecki 2026-05-08 22:35:34 +02:00 committed by GitHub
parent f85641e40d
commit 5820d2936c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 395 additions and 63 deletions

View File

@ -298,12 +298,39 @@ class BeginVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstVar* nodep) override {
if (nodep->user1SetOnce()) { return; }
// If static variable, move it outside a function.
if (nodep->lifetime().isStatic() && m_ftaskp) {
const std::string newName
= m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name());
if (nodep->isIO()) {
if (nodep->direction().isRef()) {
nodep->v3error(
"It is illegal to use argument passing by reference for subroutines with "
"a lifetime of static (IEEE 1800-2023 13.5.2)");
}
// Create a port that is used for passing value between argument and static
// variable
AstVar* const portp = nodep->cloneTreePure(false);
nodep->replaceWith(portp);
if (nodep->isInput() || nodep->isInout()) {
AstAssign* const initAssignp = new AstAssign{
nodep->fileline(), new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE},
new AstVarRef{portp->fileline(), portp, VAccess::READ}};
portp->addNextHere(initAssignp);
}
if (nodep->isWritable()) {
AstAssign* const endAssignp = new AstAssign{
nodep->fileline(), new AstVarRef{portp->fileline(), portp, VAccess::WRITE},
new AstVarRef{nodep->fileline(), nodep, VAccess::READ}};
m_ftaskp->addStmtsp(endAssignp);
}
} else {
nodep->unlinkFrBack();
}
nodep->name(newName);
nodep->unlinkFrBack();
m_ftaskp->addHereThisAsNext(nodep);
nodep->funcLocal(false);
} else if (m_unnamedScope != "") {

View File

@ -443,20 +443,11 @@ class DynScopeVisitor final : public VNVisitor {
if (!isEvent && m_afterTimingControl && nodep->varp()->isWritable()
&& nodep->access().isWriteOrRW()) {
// The output variable may not exist after a delay, so we can't just write to it
if (m_inFunc) {
nodep->v3error(
"Writing to an "
<< nodep->varp()->verilogKwd()
<< " variable of a function after a timing control is not allowed");
} else {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Writing to a captured "
<< nodep->varp()->verilogKwd()
<< " variable in a "
<< (VN_IS(nodep->backp(), AssignDly)
? "non-blocking assignment"
: "fork")
<< " after a timing control");
}
nodep->v3error(
"Writing to an "
<< nodep->varp()->verilogKwd() << " automatic variable of a "
<< (m_inFunc ? "function" : "task")
<< " after a timing control is not allowed (IEEE 1800-2023 13.2.2)");
}
if (!framep->instance().initialized()) framep->createInstancePrototype();
framep->captureVarInsert(nodep->varp());

View File

@ -70,6 +70,7 @@ class LinkParseVisitor final : public VNVisitor {
int m_randSequenceNum = 0; // RandSequence uniqify number
VLifetime m_lifetime = VLifetime::STATIC_IMPLICIT; // Propagating lifetime
bool m_lifetimeAllowed = false; // True to allow lifetime settings
bool m_hasTimingControl = false; // If current task has timing control
bool m_moduleWithGenericIface = false; // If current module contains generic interface
std::set<AstVar*> m_portDups; // Non-ANSI port datatype duplicating input/output decls
@ -249,6 +250,9 @@ class LinkParseVisitor final : public VNVisitor {
VL_RESTORER(m_lifetime);
VL_RESTORER(m_lifetimeAllowed);
m_lifetimeAllowed = true;
VL_RESTORER(m_hasTimingControl);
m_hasTimingControl
= nodep->exists([](const AstNode* const nodep) { return nodep->isTimingControl(); });
if (!nodep->lifetime().isNone()) {
m_lifetime = nodep->lifetime().makeImplicit();
} else {
@ -383,7 +387,13 @@ class LinkParseVisitor final : public VNVisitor {
}
}
} else if (m_ftaskp) {
if (!nodep->lifetime().isAutomatic()) nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT);
// Variables in static tasks with timing control can be used after the task has ended
// with use of join..fork_none, so they need to be static
if (m_ftaskp->lifetime().isStatic() && m_hasTimingControl) {
nodep->lifetime(VLifetime::STATIC_IMPLICIT);
} else {
nodep->lifetime(VLifetime::AUTOMATIC_IMPLICIT);
}
} else if (nodep->lifetime().isNone()) {
// lifetime shouldn't be unknown, set static if none
nodep->lifetime(VLifetime::STATIC_IMPLICIT);

View File

@ -1,10 +0,0 @@
%Error-UNSUPPORTED: t/t_fork_dynscope_unsup.v:17:7: Unsupported: Writing to a captured inout variable in a fork after a timing control
: ... note: In instance 't'
17 | p = #1 1;
| ^
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_fork_dynscope_unsup.v:22:5: Unsupported: Writing to a captured output variable in a non-blocking assignment after a timing control
: ... note: In instance 't'
22 | q <= #1 1;
| ^
%Error: Exiting due to

View File

@ -0,0 +1,18 @@
#!/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: 2024 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,118 @@
// DESCRIPTION: Verilator: Verilog Test module
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// 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);
// verilog_format: on
module t;
int x1, x2, x3, x4, x5, x6, x7;
initial begin
x1 = -1;
x2 = -1;
x3 = -1;
x4 = -1;
#1;
t1(x1);
t2(x2);
t3(x3);
t4(x4);
`checkd(x1, 0)
`checkd(x2, -1)
`checkd(x3, 1)
`checkd(x4, 2)
#10 t1(x1);
t2(x2);
t3(x3);
t4(x4);
`checkd(x1, 1)
`checkd(x2, -1)
`checkd(x3, 1)
`checkd(x4, 2)
`checkd(x5, 3)
`checkd(x6, 0)
`checkd(x7, 4)
#5;
`checkd(x1, 1)
`checkd(x2, -1)
`checkd(x3, 1)
`checkd(x4, 2)
`checkd(x5, 3)
`checkd(x6, 0)
`checkd(x7, 4)
$write("*-* All Finished *-*\n");
$finish;
end
always #1 t5(x5);
always #1 t6(x6);
always #1 t7(x7);
task t1(output int x);
fork
x = #1 1;
join_none
if ($time < 10) begin
`checkd(x, 0)
end
else begin
`checkd(x, 1)
end
endtask
task t2(inout int x);
fork
x = #1 2;
join_none
`checkd(x, -1)
endtask
task t3(output int x);
if ($time < 10) begin
`checkd(x, 0)
end
else begin
`checkd(x, 1)
end
fork
x = #1 1;
join_none
#2 `checkd(x, 1);
endtask
task t4(inout int x);
if ($time < 10) begin
`checkd(x, -1)
end
else begin
`checkd(x, 2)
end
fork
x = #1 2;
join_none
#2 `checkd(x, 2);
endtask
task t5(output int x);
x <= #1 3;
endtask
task t6(inout int x);
x <= #1 4;
endtask
task static t7(inout int x);
int y = 0;
x <= #1 4;
#2 y = x;
`checkd(x, 4)
endtask
endmodule

View File

@ -0,0 +1,6 @@
%Error: t/t_fork_write_after_timing_bad.v:12:19: It is illegal to use argument passing by reference for subroutines with a lifetime of static (IEEE 1800-2023 13.5.2)
: ... note: In instance 't'
12 | task t1(ref int x);
| ^
... 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
#
# 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: 2024 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.lint(verilator_flags2=["--timing"], fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -1,24 +1,17 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2024 Antmicro
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
module t;
bit p = 0, q = 0;
int x1;
initial begin
t1(p);
t2(q);
t1(x1);
$finish;
end
task t1(inout p);
task t1(ref int x);
fork
p = #1 1;
x = #1 2;
join_none
endtask
task t2(output q);
q <= #1 1;
endtask
endmodule

View File

@ -0,0 +1,18 @@
#!/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('simulator')
test.compile(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,113 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// 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);
// verilog_format: on
module t;
// test static argument
task t1(output int x);
x = #1 x + 1;
endtask
// test concurrent executions of static tasks
int expected;
task t2(inout int x);
#5 `checkd(x, expected);
endtask
task factorial1 (input [31:0] x, output [31:0] out);
if (x >= 2) begin
factorial1 (x - 1, out);
out = out * x;
end
else
out = 1;
endtask
function int factorial2 (input int x);
if (x >= 2)
factorial2 = factorial2(x - 1) * x;
else
factorial2 = 1;
endfunction
int result;
initial begin
// t1
result = 0;
t1(result);
`checkd(result, 1);
t1(result);
`checkd(result, 2);
t1(result);
`checkd(result, 3);
t1(result);
`checkd(result, 4);
factorial1(1, result);
`checkd(result, 1);
factorial1(3, result);
`checkd(result, 6);
factorial1(5, result);
`checkd(result, 120);
`checkd(factorial2(1), 1);
`checkd(factorial2(3), 6);
`checkd(factorial2(5), 120);
// t2
expected = 3;
fork
begin
static int x1 = 1;
t2(x1);
`checkd(x1, 3);
end
join_none
#2
fork
begin
static int x2 = 3;
t2(x2);
`checkd(x2, 3);
end
join_none
#10
expected = 10;
fork
begin
static int x3 = 99;
t2(x3);
`checkd(x3, 10);
end
join_none
#1
fork
begin
static int x4 = 123;
t2(x4);
`checkd(x4, 10);
end
join_none
#1
fork
begin
static int x5 = 10;
t2(x5);
`checkd(x5, 10);
end
join_none
#10
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,6 @@
%Error: t/t_static_task_args_bad.v:12:19: It is illegal to use argument passing by reference for subroutines with a lifetime of static (IEEE 1800-2023 13.5.2)
: ... note: In instance 't'
12 | task t1(ref int x);
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -0,0 +1,15 @@
// DESCRIPTION: Verilator: Verilog Test module
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
module t;
int x1;
initial begin
t1(x1);
$finish;
end
task t1(ref int x);
x = #1 1;
endtask
endmodule

View File

@ -1,42 +1,46 @@
%Error: t/t_timing_func_fork_bad.v:13:9: Writing to an output variable of a function after a timing control is not allowed
%Error: t/t_timing_func_fork_bad.v:13:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2)
: ... note: In instance 't'
13 | f1 = 0;
| ^~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_timing_func_fork_bad.v:14:9: Writing to an output variable of a function after a timing control is not allowed
%Error: t/t_timing_func_fork_bad.v:14:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2)
: ... note: In instance 't'
14 | o1 = 0;
| ^~
%Error: t/t_timing_func_fork_bad.v:22:9: Writing to an output variable of a function after a timing control is not allowed
%Error: t/t_timing_func_fork_bad.v:22:7: Writing to an output automatic variable of a task after a timing control is not allowed (IEEE 1800-2023 13.2.2)
: ... note: In instance 't'
22 | f2 = #5 0;
22 | o1 = 0;
| ^~
%Error: t/t_timing_func_fork_bad.v:29:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2)
: ... note: In instance 't'
29 | f2 = #5 0;
| ^~
%Error: t/t_timing_func_fork_bad.v:24:9: Writing to an inout variable of a function after a timing control is not allowed
%Error: t/t_timing_func_fork_bad.v:31:9: Writing to an inout automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2)
: ... note: In instance 't'
24 | io2 = 0;
31 | io2 = 0;
| ^~~
%Error: t/t_timing_func_fork_bad.v:34:9: Writing to an output variable of a function after a timing control is not allowed
%Error: t/t_timing_func_fork_bad.v:41:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2)
: ... note: In instance 't'
34 | f3 = 0;
41 | f3 = 0;
| ^~
%Error: t/t_timing_func_fork_bad.v:35:9: Writing to an output variable of a function after a timing control is not allowed
%Error: t/t_timing_func_fork_bad.v:42:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2)
: ... note: In instance 't'
35 | o3 = 0;
42 | o3 = 0;
| ^~
%Error: t/t_timing_func_fork_bad.v:43:9: Writing to an output variable of a function after a timing control is not allowed
%Error: t/t_timing_func_fork_bad.v:50:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2)
: ... note: In instance 't'
43 | f4 = @e 0;
50 | f4 = @e 0;
| ^~
%Error: t/t_timing_func_fork_bad.v:45:9: Writing to an inout variable of a function after a timing control is not allowed
%Error: t/t_timing_func_fork_bad.v:52:9: Writing to an inout automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2)
: ... note: In instance 't'
45 | io4 = 0;
52 | io4 = 0;
| ^~~
%Error: t/t_timing_func_fork_bad.v:56:9: Writing to an output variable of a function after a timing control is not allowed
%Error: t/t_timing_func_fork_bad.v:63:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2)
: ... note: In instance 't'
56 | f5 = 0;
63 | f5 = 0;
| ^~
%Error: t/t_timing_func_fork_bad.v:57:9: Writing to an output variable of a function after a timing control is not allowed
%Error: t/t_timing_func_fork_bad.v:64:9: Writing to an output automatic variable of a function after a timing control is not allowed (IEEE 1800-2023 13.2.2)
: ... note: In instance 't'
57 | o5 = 0;
64 | o5 = 0;
| ^~
%Error: Exiting due to

View File

@ -6,7 +6,7 @@
module t;
function int f1(output int o1);
function automatic int f1(output int o1);
fork
begin
#1 $stop;
@ -16,7 +16,14 @@ module t;
join_none
endfunction
function int f2(inout io2);
task automatic t1(output int o1);
fork begin
#1 $stop;
o1 = 0;
end join_none
endtask
function automatic int f2(inout io2);
fork
begin
f2 = #5 0;
@ -27,7 +34,7 @@ module t;
endfunction
event e;
function int f3(output int o3);
function automatic int f3(output int o3);
fork
begin
@e $stop;
@ -37,7 +44,7 @@ module t;
join_none
endfunction
function int f4(inout int io4);
function automatic int f4(inout int io4);
fork
begin
f4 = @e 0;
@ -49,7 +56,7 @@ module t;
int i;
function int f5(output int o5);
function automatic int f5(output int o5);
fork
begin
wait (i == 0) $stop;