Fix timing control under fork under function (#6407)
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
This commit is contained in:
parent
b45cf5960a
commit
0743d84bcc
|
|
@ -286,6 +286,7 @@ class DynScopeVisitor final : public VNVisitor {
|
||||||
const VNUser2InUse m_inuser2;
|
const VNUser2InUse m_inuser2;
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
|
bool m_inFunc = false; // True if in a function
|
||||||
AstNodeModule* m_modp = nullptr; // Module we are currently under
|
AstNodeModule* m_modp = nullptr; // Module we are currently under
|
||||||
AstNode* m_procp = nullptr; // Function/task/block we are currently under
|
AstNode* m_procp = nullptr; // Function/task/block we are currently under
|
||||||
std::deque<AstNode*> m_frameOrder; // Ordered list of frames (for determinism)
|
std::deque<AstNode*> m_frameOrder; // Ordered list of frames (for determinism)
|
||||||
|
|
@ -367,6 +368,8 @@ class DynScopeVisitor final : public VNVisitor {
|
||||||
void visit(AstNodeFTask* nodep) override {
|
void visit(AstNodeFTask* nodep) override {
|
||||||
VL_RESTORER(m_procp);
|
VL_RESTORER(m_procp);
|
||||||
m_procp = nodep;
|
m_procp = nodep;
|
||||||
|
VL_RESTORER(m_inFunc);
|
||||||
|
m_inFunc = VN_IS(nodep, Func);
|
||||||
if (hasAsyncFork(nodep)) pushDynScopeFrame(m_procp);
|
if (hasAsyncFork(nodep)) pushDynScopeFrame(m_procp);
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
|
@ -433,12 +436,20 @@ class DynScopeVisitor final : public VNVisitor {
|
||||||
if (!isEvent && m_afterTimingControl && nodep->varp()->isWritable()
|
if (!isEvent && m_afterTimingControl && nodep->varp()->isWritable()
|
||||||
&& nodep->access().isWriteOrRW()) {
|
&& nodep->access().isWriteOrRW()) {
|
||||||
// The output variable may not exist after a delay, so we can't just write to it
|
// The output variable may not exist after a delay, so we can't just write to it
|
||||||
nodep->v3warn(
|
if (m_inFunc) {
|
||||||
E_UNSUPPORTED,
|
nodep->v3error(
|
||||||
"Unsupported: Writing to a captured "
|
"Writing to an "
|
||||||
<< (nodep->varp()->isInout() ? "inout" : "output") << " variable in a "
|
<< nodep->varp()->verilogKwd()
|
||||||
<< (VN_IS(nodep->backp(), AssignDly) ? "non-blocking assignment" : "fork")
|
<< " variable of a function after a timing control is not allowed");
|
||||||
<< " after a timing control");
|
} 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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!framep->instance().initialized()) framep->createInstancePrototype();
|
if (!framep->instance().initialized()) framep->createInstancePrototype();
|
||||||
framep->captureVarInsert(nodep->varp());
|
framep->captureVarInsert(nodep->varp());
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,7 @@ class WidthVisitor final : public VNVisitor {
|
||||||
VMemberMap m_memberMap; // Member names cached for fast lookup
|
VMemberMap m_memberMap; // Member names cached for fast lookup
|
||||||
V3TaskConnectState m_taskConnectState; // State to cache V3Task::taskConnects
|
V3TaskConnectState m_taskConnectState; // State to cache V3Task::taskConnects
|
||||||
WidthVP* m_vup = nullptr; // Current node state
|
WidthVP* m_vup = nullptr; // Current node state
|
||||||
|
bool m_underFork = false; // Visiting under a fork
|
||||||
const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations
|
const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations
|
||||||
const AstEnumItem* m_enumItemp = nullptr; // Current enum item
|
const AstEnumItem* m_enumItemp = nullptr; // Current enum item
|
||||||
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||||
|
|
@ -704,7 +705,7 @@ class WidthVisitor final : public VNVisitor {
|
||||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (VN_IS(m_ftaskp, Func)) {
|
if (!m_underFork && VN_IS(m_ftaskp, Func)) {
|
||||||
nodep->v3error("Delays are not legal in functions. Suggest use a task "
|
nodep->v3error("Delays are not legal in functions. Suggest use a task "
|
||||||
"(IEEE 1800-2023 13.4.4)");
|
"(IEEE 1800-2023 13.4.4)");
|
||||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
|
|
@ -727,7 +728,7 @@ class WidthVisitor final : public VNVisitor {
|
||||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
}
|
}
|
||||||
void visit(AstFork* nodep) override {
|
void visit(AstFork* nodep) override {
|
||||||
if (VN_IS(m_ftaskp, Func) && !nodep->joinType().joinNone()) {
|
if (!m_underFork && VN_IS(m_ftaskp, Func) && !nodep->joinType().joinNone()) {
|
||||||
nodep->v3error("Only fork .. join_none is legal in functions. "
|
nodep->v3error("Only fork .. join_none is legal in functions. "
|
||||||
"(IEEE 1800-2023 13.4.4)");
|
"(IEEE 1800-2023 13.4.4)");
|
||||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
|
|
@ -747,6 +748,8 @@ class WidthVisitor final : public VNVisitor {
|
||||||
nodep->replaceWith(newp);
|
nodep->replaceWith(newp);
|
||||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||||
} else if (v3Global.opt.timing().isSetTrue()) {
|
} else if (v3Global.opt.timing().isSetTrue()) {
|
||||||
|
VL_RESTORER(m_underFork);
|
||||||
|
m_underFork = true;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
} else if (v3Global.opt.timing().isSetFalse()) {
|
} else if (v3Global.opt.timing().isSetFalse()) {
|
||||||
nodep->v3warn(E_NOTIMING, "Fork statements require --timing");
|
nodep->v3warn(E_NOTIMING, "Fork statements require --timing");
|
||||||
|
|
@ -1663,7 +1666,7 @@ class WidthVisitor final : public VNVisitor {
|
||||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||||
}
|
}
|
||||||
void visit(AstEventControl* nodep) override {
|
void visit(AstEventControl* nodep) override {
|
||||||
if (VN_IS(m_ftaskp, Func)) {
|
if (!m_underFork && VN_IS(m_ftaskp, Func)) {
|
||||||
nodep->v3error("Event controls are not legal in functions. Suggest use a task "
|
nodep->v3error("Event controls are not legal in functions. Suggest use a task "
|
||||||
"(IEEE 1800-2023 13.4.4)");
|
"(IEEE 1800-2023 13.4.4)");
|
||||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||||
|
|
@ -5466,7 +5469,7 @@ class WidthVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
if (VN_IS(nodep, AssignForce)) checkForceReleaseLhs(nodep, nodep->lhsp());
|
if (VN_IS(nodep, AssignForce)) checkForceReleaseLhs(nodep, nodep->lhsp());
|
||||||
if (AstNode* const controlp = nodep->timingControlp()) {
|
if (AstNode* const controlp = nodep->timingControlp()) {
|
||||||
if (VN_IS(m_ftaskp, Func)) {
|
if (!m_underFork && VN_IS(m_ftaskp, Func)) {
|
||||||
controlp->v3error("Timing controls are not legal in functions. Suggest use a task "
|
controlp->v3error("Timing controls are not legal in functions. Suggest use a task "
|
||||||
"(IEEE 1800-2023 13.4.4)");
|
"(IEEE 1800-2023 13.4.4)");
|
||||||
VL_DO_DANGLING(controlp->unlinkFrBackWithNext()->deleteTree(), controlp);
|
VL_DO_DANGLING(controlp->unlinkFrBackWithNext()->deleteTree(), controlp);
|
||||||
|
|
@ -6631,7 +6634,7 @@ class WidthVisitor final : public VNVisitor {
|
||||||
userIterateChildren(nodep, WidthVP{SELF, PRELIM}.p());
|
userIterateChildren(nodep, WidthVP{SELF, PRELIM}.p());
|
||||||
}
|
}
|
||||||
void visit(AstWait* nodep) override {
|
void visit(AstWait* nodep) override {
|
||||||
if (VN_IS(m_ftaskp, Func)) {
|
if (!m_underFork && VN_IS(m_ftaskp, Func)) {
|
||||||
nodep->v3error("Wait statements are not legal in functions. Suggest use a task "
|
nodep->v3error("Wait statements are not legal in functions. Suggest use a task "
|
||||||
"(IEEE 1800-2023 13.4.4)");
|
"(IEEE 1800-2023 13.4.4)");
|
||||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2025 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('simulator')
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=["--binary -Wno-UNOPTFLAT"])
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2025 by Antmicro.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t(/*AUTOARG*/);
|
||||||
|
|
||||||
|
function int f1;
|
||||||
|
fork begin
|
||||||
|
#1 $stop;
|
||||||
|
end join_none
|
||||||
|
f1 = 0;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function int f2;
|
||||||
|
fork begin
|
||||||
|
int x;
|
||||||
|
x = #5 0; $stop;
|
||||||
|
end join_none
|
||||||
|
f2 = 0;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
event e;
|
||||||
|
function int f3;
|
||||||
|
fork begin
|
||||||
|
int x;
|
||||||
|
@e $stop;
|
||||||
|
x = 0;
|
||||||
|
end join_none
|
||||||
|
f3 = 0;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function int f4;
|
||||||
|
fork begin
|
||||||
|
int x;
|
||||||
|
x = @e 0; $stop;
|
||||||
|
end join_none
|
||||||
|
f4 = 0;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
function int f5;
|
||||||
|
fork begin
|
||||||
|
int x;
|
||||||
|
wait(i == 0) $stop;
|
||||||
|
x = 0;
|
||||||
|
end join_none
|
||||||
|
f5 = 0;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
fork begin
|
||||||
|
i = f1();
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end join_none
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
%Error: t/t_timing_func_fork_bad.v:12:7: Writing to an output variable of a function after a timing control is not allowed
|
||||||
|
: ... note: In instance 't'
|
||||||
|
12 | 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:13:7: Writing to an output variable of a function after a timing control is not allowed
|
||||||
|
: ... note: In instance 't'
|
||||||
|
13 | o1 = 0;
|
||||||
|
| ^~
|
||||||
|
%Error: t/t_timing_func_fork_bad.v:19:7: Writing to an output variable of a function after a timing control is not allowed
|
||||||
|
: ... note: In instance 't'
|
||||||
|
19 | f2 = #5 0; $stop;
|
||||||
|
| ^~
|
||||||
|
%Error: t/t_timing_func_fork_bad.v:20:7: Writing to an inout variable of a function after a timing control is not allowed
|
||||||
|
: ... note: In instance 't'
|
||||||
|
20 | io2 = 0;
|
||||||
|
| ^~~
|
||||||
|
%Error: t/t_timing_func_fork_bad.v:28:7: Writing to an output variable of a function after a timing control is not allowed
|
||||||
|
: ... note: In instance 't'
|
||||||
|
28 | f3 = 0;
|
||||||
|
| ^~
|
||||||
|
%Error: t/t_timing_func_fork_bad.v:29:7: Writing to an output variable of a function after a timing control is not allowed
|
||||||
|
: ... note: In instance 't'
|
||||||
|
29 | o3 = 0;
|
||||||
|
| ^~
|
||||||
|
%Error: t/t_timing_func_fork_bad.v:35:7: Writing to an output variable of a function after a timing control is not allowed
|
||||||
|
: ... note: In instance 't'
|
||||||
|
35 | f4 = @e 0; $stop;
|
||||||
|
| ^~
|
||||||
|
%Error: t/t_timing_func_fork_bad.v:36:7: Writing to an inout variable of a function after a timing control is not allowed
|
||||||
|
: ... note: In instance 't'
|
||||||
|
36 | io4 = 0;
|
||||||
|
| ^~~
|
||||||
|
%Error: t/t_timing_func_fork_bad.v:45:7: Writing to an output variable of a function after a timing control is not allowed
|
||||||
|
: ... note: In instance 't'
|
||||||
|
45 | f5 = 0;
|
||||||
|
| ^~
|
||||||
|
%Error: t/t_timing_func_fork_bad.v:46:7: Writing to an output variable of a function after a timing control is not allowed
|
||||||
|
: ... note: In instance 't'
|
||||||
|
46 | o5 = 0;
|
||||||
|
| ^~
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2025 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('simulator')
|
||||||
|
|
||||||
|
test.compile(fails=True,
|
||||||
|
verilator_flags2=["--binary -Wno-UNOPTFLAT"],
|
||||||
|
expect_filename=test.golden_filename)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2025 by Antmicro.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t(/*AUTOARG*/);
|
||||||
|
|
||||||
|
function int f1(output int o1);
|
||||||
|
fork begin
|
||||||
|
#1 $stop;
|
||||||
|
f1 = 0;
|
||||||
|
o1 = 0;
|
||||||
|
end join_none
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function int f2(inout io2);
|
||||||
|
fork begin
|
||||||
|
f2 = #5 0; $stop;
|
||||||
|
io2 = 0;
|
||||||
|
end join_none
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
event e;
|
||||||
|
function int f3(output int o3);
|
||||||
|
fork begin
|
||||||
|
@e $stop;
|
||||||
|
f3 = 0;
|
||||||
|
o3 = 0;
|
||||||
|
end join_none
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function int f4(inout int io4);
|
||||||
|
fork begin
|
||||||
|
f4 = @e 0; $stop;
|
||||||
|
io4 = 0;
|
||||||
|
end join_none
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
function int f5(output int o5);
|
||||||
|
fork begin
|
||||||
|
wait(i == 0) $stop;
|
||||||
|
f5 = 0;
|
||||||
|
o5 = 0;
|
||||||
|
end join_none
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue