Implement disable task by name, add regression testing.
This commit is contained in:
parent
7c923bb330
commit
4756d21cc0
|
|
@ -37,6 +37,7 @@
|
|||
#include "V3Error.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
|
@ -65,6 +66,7 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
std::vector<AstNodeBlock*> m_blockStack; // All begin blocks above current node
|
||||
V3UniqueNames m_queueNames{
|
||||
"__VprocessQueue"}; // Names for queues needed for 'disable' handling
|
||||
std::unordered_map<const AstTask*, AstVar*> m_taskDisableQueues; // Per-task process queues
|
||||
|
||||
// METHODS
|
||||
// Get (and create if necessary) the JumpBlock for this statement
|
||||
|
|
@ -177,6 +179,42 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
return new AstStmtExpr{
|
||||
fl, new AstMethodCall{fl, queueRefp, "push_back", new AstArg{fl, "", processSelfp}}};
|
||||
}
|
||||
static AstStmtExpr* getQueueKillStmtp(FileLine* const fl, AstVar* const processQueuep) {
|
||||
AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp();
|
||||
AstClass* const processClassp
|
||||
= VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class);
|
||||
AstVarRef* const queueRefp = new AstVarRef{fl, topPkgp, processQueuep, VAccess::READWRITE};
|
||||
AstTaskRef* const killQueueCall
|
||||
= new AstTaskRef{fl, VN_AS(getMemberp(processClassp, "killQueue"), Task),
|
||||
new AstArg{fl, "", queueRefp}};
|
||||
killQueueCall->classOrPackagep(processClassp);
|
||||
return new AstStmtExpr{fl, killQueueCall};
|
||||
}
|
||||
AstVar* getOrCreateTaskDisableQueuep(AstTask* const taskp, FileLine* const fl) {
|
||||
const auto it = m_taskDisableQueues.find(taskp);
|
||||
if (it != m_taskDisableQueues.end()) return it->second;
|
||||
|
||||
AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp();
|
||||
AstClass* const processClassp
|
||||
= VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class);
|
||||
AstVar* const processQueuep = new AstVar{
|
||||
fl, VVarType::VAR, m_queueNames.get(taskp->name()), VFlagChildDType{},
|
||||
new AstQueueDType{fl, VFlagChildDType{},
|
||||
new AstClassRefDType{fl, processClassp, nullptr}, nullptr}};
|
||||
processQueuep->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
topPkgp->addStmtsp(processQueuep);
|
||||
|
||||
AstVarRef* const queueWriteRefp
|
||||
= new AstVarRef{fl, topPkgp, processQueuep, VAccess::WRITE};
|
||||
AstStmtExpr* const pushCurrentProcessp = getQueuePushProcessSelfp(queueWriteRefp);
|
||||
if (taskp->stmtsp()) {
|
||||
taskp->stmtsp()->addHereThisAsNext(pushCurrentProcessp);
|
||||
} else {
|
||||
taskp->addStmtsp(pushCurrentProcessp);
|
||||
}
|
||||
m_taskDisableQueues.emplace(taskp, processQueuep);
|
||||
return processQueuep;
|
||||
}
|
||||
void handleDisableOnFork(AstDisable* const nodep, const std::vector<AstBegin*>& forks) {
|
||||
// The support utilizes the process::kill()` method. For each `disable` a queue of
|
||||
// processes is declared. At the beginning of each fork that can be disabled, its process
|
||||
|
|
@ -215,12 +253,7 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
beginp->stmtsp()->addHereThisAsNext(pushCurrentProcessp);
|
||||
}
|
||||
}
|
||||
AstVarRef* const queueRefp = new AstVarRef{fl, topPkgp, processQueuep, VAccess::READWRITE};
|
||||
AstTaskRef* const killQueueCall
|
||||
= new AstTaskRef{fl, VN_AS(getMemberp(processClassp, "killQueue"), Task),
|
||||
new AstArg{fl, "", queueRefp}};
|
||||
killQueueCall->classOrPackagep(processClassp);
|
||||
AstStmtExpr* const killStmtp = new AstStmtExpr{fl, killQueueCall};
|
||||
AstStmtExpr* const killStmtp = getQueueKillStmtp(fl, processQueuep);
|
||||
nodep->addNextHere(killStmtp);
|
||||
|
||||
// 'process::kill' does not immediately kill the current process
|
||||
|
|
@ -417,9 +450,23 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
void visit(AstDisable* nodep) override {
|
||||
UINFO(8, " DISABLE " << nodep);
|
||||
AstNode* const targetp = nodep->targetp();
|
||||
UASSERT_OBJ(targetp, nodep, "Unlinked disable statement");
|
||||
if (VN_IS(targetp, Task)) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling task by name");
|
||||
if (!targetp) {
|
||||
// Linking errors on the disable target are already reported upstream.
|
||||
// Drop this node to avoid cascading into an internal assertion.
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
if (AstTask* const taskp = VN_CAST(targetp, Task)) {
|
||||
AstVar* const processQueuep = getOrCreateTaskDisableQueuep(taskp, nodep->fileline());
|
||||
AstStmtExpr* const killStmtp = getQueueKillStmtp(nodep->fileline(), processQueuep);
|
||||
nodep->addNextHere(killStmtp);
|
||||
|
||||
// process::kill does not terminate the currently running process immediately.
|
||||
// If we disable the current task by name from inside itself, jump to its end.
|
||||
if (m_ftaskp == taskp) {
|
||||
AstJumpBlock* const blockp = getJumpBlock(taskp, false);
|
||||
killStmtp->addNextHere(new AstJumpGo{nodep->fileline(), blockp});
|
||||
}
|
||||
} else if (AstFork* const forkp = VN_CAST(targetp, Fork)) {
|
||||
std::vector<AstBegin*> forks;
|
||||
for (AstBegin* itemp = forkp->forksp(); itemp; itemp = VN_AS(itemp->nextp(), Begin)) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,4 @@
|
|||
9 | disable abcd;
|
||||
| ^~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Internal Error: t/t_disable_bad.v:9:5: ../V3LinkJump.cpp:#: Unlinked disable statement
|
||||
9 | disable abcd;
|
||||
| ^~~~~~~
|
||||
... This fatal error may be caused by the earlier error(s); resolve those first.
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -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(timing_loop=True, verilator_flags2=["--timing"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,354 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// 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
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
int self_entry = 0;
|
||||
int self_after_disable = 0;
|
||||
|
||||
int par_started = 0;
|
||||
int par_finished = 0;
|
||||
int par_parent_continued = 0;
|
||||
|
||||
class StaticCls;
|
||||
static int ticks = 0;
|
||||
|
||||
static task run();
|
||||
forever begin
|
||||
#10;
|
||||
ticks++;
|
||||
end
|
||||
endtask
|
||||
|
||||
static task stop();
|
||||
disable run;
|
||||
endtask
|
||||
endclass
|
||||
|
||||
task increment_x;
|
||||
x++;
|
||||
#2;
|
||||
x++;
|
||||
endtask
|
||||
|
||||
task increment_y;
|
||||
y++;
|
||||
#5;
|
||||
y++;
|
||||
endtask
|
||||
|
||||
task self_stop;
|
||||
self_entry = 1;
|
||||
disable self_stop;
|
||||
self_after_disable = 1;
|
||||
endtask
|
||||
|
||||
task worker;
|
||||
par_started++;
|
||||
#20;
|
||||
par_finished++;
|
||||
endtask
|
||||
|
||||
task parent_disables_worker;
|
||||
fork
|
||||
worker();
|
||||
worker();
|
||||
join_none
|
||||
#5;
|
||||
disable worker;
|
||||
par_parent_continued = 1;
|
||||
endtask
|
||||
|
||||
class Cls;
|
||||
int m_time = 0;
|
||||
|
||||
task get_and_send();
|
||||
forever begin
|
||||
#10;
|
||||
m_time += 10;
|
||||
end
|
||||
endtask
|
||||
|
||||
task post_shutdown_phase();
|
||||
disable get_and_send;
|
||||
endtask
|
||||
endclass
|
||||
|
||||
class NamedA;
|
||||
int v = 0;
|
||||
|
||||
task run();
|
||||
forever begin
|
||||
#10;
|
||||
v++;
|
||||
end
|
||||
endtask
|
||||
|
||||
task stop();
|
||||
disable run;
|
||||
endtask
|
||||
endclass
|
||||
|
||||
class NamedB;
|
||||
int v = 0;
|
||||
|
||||
task run();
|
||||
forever begin
|
||||
#10;
|
||||
v++;
|
||||
end
|
||||
endtask
|
||||
endclass
|
||||
|
||||
class BaseNamed;
|
||||
int v = 0;
|
||||
|
||||
task run();
|
||||
forever begin
|
||||
#10;
|
||||
v++;
|
||||
end
|
||||
endtask
|
||||
endclass
|
||||
|
||||
class ChildNamed extends BaseNamed;
|
||||
int child_v = 0;
|
||||
|
||||
task run();
|
||||
forever begin
|
||||
#10;
|
||||
child_v++;
|
||||
end
|
||||
endtask
|
||||
|
||||
task stop();
|
||||
disable run;
|
||||
endtask
|
||||
endclass
|
||||
|
||||
package PkgRun;
|
||||
int v = 0;
|
||||
|
||||
task run();
|
||||
forever begin
|
||||
#10;
|
||||
v++;
|
||||
end
|
||||
endtask
|
||||
|
||||
task stop();
|
||||
disable run;
|
||||
endtask
|
||||
endpackage
|
||||
|
||||
interface Ifc;
|
||||
int v = 0;
|
||||
|
||||
task run();
|
||||
forever begin
|
||||
#10;
|
||||
v++;
|
||||
end
|
||||
endtask
|
||||
|
||||
task stop();
|
||||
disable run;
|
||||
endtask
|
||||
endinterface
|
||||
|
||||
program Prog;
|
||||
int v = 0;
|
||||
|
||||
task run();
|
||||
forever begin
|
||||
#10;
|
||||
v++;
|
||||
end
|
||||
endtask
|
||||
|
||||
task stop();
|
||||
disable run;
|
||||
endtask
|
||||
endprogram
|
||||
|
||||
module WorkerMod;
|
||||
int m = 0;
|
||||
|
||||
task run();
|
||||
forever begin
|
||||
#10;
|
||||
m++;
|
||||
end
|
||||
endtask
|
||||
endmodule
|
||||
|
||||
module t;
|
||||
Ifc ifc1();
|
||||
Prog prog1();
|
||||
WorkerMod mod1();
|
||||
|
||||
initial begin
|
||||
automatic Cls c = new;
|
||||
automatic NamedA a = new;
|
||||
automatic NamedB b = new;
|
||||
automatic BaseNamed base_obj = new;
|
||||
automatic ChildNamed child_obj = new;
|
||||
int m_time_before_shutdown;
|
||||
int mod1_before;
|
||||
int a_before;
|
||||
int b_before;
|
||||
int static_before;
|
||||
int base_before;
|
||||
int child_before;
|
||||
int ifc_before;
|
||||
int prog_before;
|
||||
int pkg_before;
|
||||
|
||||
// Module task disabled by sibling process in a fork
|
||||
fork
|
||||
increment_x();
|
||||
#1 disable increment_x;
|
||||
join_none
|
||||
#10;
|
||||
if (x != 1) $stop;
|
||||
|
||||
// Another basic module-task disable-by-name case
|
||||
fork
|
||||
increment_y();
|
||||
#3 disable increment_y;
|
||||
join_none
|
||||
#10;
|
||||
if (y != 1) $stop;
|
||||
|
||||
// Self-disable in task by name
|
||||
self_stop();
|
||||
if (self_entry != 1) $stop;
|
||||
if (self_after_disable != 0) $stop;
|
||||
|
||||
// Same task launched in parallel, disabled from parent task context
|
||||
parent_disables_worker();
|
||||
#30;
|
||||
if (par_started != 2) $stop;
|
||||
if (par_finished != 0) $stop;
|
||||
if (par_parent_continued != 1) $stop;
|
||||
|
||||
// Same task launched in parallel, disabled from sibling process context
|
||||
par_started = 0;
|
||||
par_finished = 0;
|
||||
fork
|
||||
worker();
|
||||
worker();
|
||||
begin
|
||||
#5;
|
||||
disable worker;
|
||||
end
|
||||
join_none
|
||||
#30;
|
||||
if (par_started != 2) $stop;
|
||||
if (par_finished != 0) $stop;
|
||||
|
||||
// Static class task disabled by name from another static task
|
||||
fork
|
||||
StaticCls::run();
|
||||
StaticCls::run();
|
||||
join_none
|
||||
#25;
|
||||
if (StaticCls::ticks == 0) $stop;
|
||||
static_before = StaticCls::ticks;
|
||||
StaticCls::stop();
|
||||
#30;
|
||||
if (StaticCls::ticks != static_before) $stop;
|
||||
|
||||
// Same task name in different class scopes: disable only one scope
|
||||
fork
|
||||
a.run();
|
||||
b.run();
|
||||
join_none
|
||||
#25;
|
||||
if (a.v == 0 || b.v == 0) $stop;
|
||||
a_before = a.v;
|
||||
b_before = b.v;
|
||||
a.stop();
|
||||
#30;
|
||||
if (a.v != a_before) $stop;
|
||||
if (b.v <= b_before) $stop;
|
||||
|
||||
// Same task name across inheritance scopes: disable only derived task
|
||||
fork
|
||||
base_obj.run();
|
||||
child_obj.run();
|
||||
join_none
|
||||
#25;
|
||||
if (base_obj.v == 0 || child_obj.child_v == 0) $stop;
|
||||
base_before = base_obj.v;
|
||||
child_before = child_obj.child_v;
|
||||
child_obj.stop();
|
||||
#30;
|
||||
if (child_obj.child_v != child_before) $stop;
|
||||
if (base_obj.v <= base_before) $stop;
|
||||
|
||||
// Interface task disabled by name through interface scope
|
||||
fork
|
||||
ifc1.run();
|
||||
join_none
|
||||
#25;
|
||||
if (ifc1.v == 0) $stop;
|
||||
ifc_before = ifc1.v;
|
||||
ifc1.stop();
|
||||
#30;
|
||||
if (ifc1.v != ifc_before) $stop;
|
||||
|
||||
// Program task disabled by name through program scope
|
||||
fork
|
||||
prog1.run();
|
||||
join_none
|
||||
#25;
|
||||
if (prog1.v == 0) $stop;
|
||||
prog_before = prog1.v;
|
||||
prog1.stop();
|
||||
#30;
|
||||
if (prog1.v != prog_before) $stop;
|
||||
|
||||
// Package task disabled by name through package scope
|
||||
fork
|
||||
PkgRun::run();
|
||||
join_none
|
||||
#25;
|
||||
if (PkgRun::v == 0) $stop;
|
||||
pkg_before = PkgRun::v;
|
||||
PkgRun::stop();
|
||||
#30;
|
||||
if (PkgRun::v != pkg_before) $stop;
|
||||
|
||||
// Dotted hierarchical task disable of module task by instance path
|
||||
fork
|
||||
mod1.run();
|
||||
join_none
|
||||
#25;
|
||||
if (mod1.m == 0) $stop;
|
||||
mod1_before = mod1.m;
|
||||
disable mod1.run;
|
||||
#30;
|
||||
if (mod1.m != mod1_before) $stop;
|
||||
|
||||
// Class task disabled by name from outside that task
|
||||
fork
|
||||
c.get_and_send();
|
||||
join_none
|
||||
#35;
|
||||
if (c.m_time == 0) $fatal;
|
||||
m_time_before_shutdown = c.m_time;
|
||||
c.post_shutdown_phase();
|
||||
#30;
|
||||
if (c.m_time != m_time_before_shutdown) $fatal;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
%Error: t/t_disable_task_scope_bad.v:34:18: Can't find definition of 'missing_task' in dotted block/task: 'ifc1.missing_task'
|
||||
34 | disable ifc1.missing_task;
|
||||
| ^~~~~~~~~~~~
|
||||
... Known scopes under 'ifc1': <no instances found>
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_disable_task_scope_bad.v:35:19: Can't find definition of 'missing_task' in dotted block/task: 'prog1.missing_task'
|
||||
35 | disable prog1.missing_task;
|
||||
| ^~~~~~~~~~~~
|
||||
... Known scopes under 'prog1': <no instances found>
|
||||
%Error: t/t_disable_task_scope_bad.v:36:26: Can't find definition of 'missing_task' in dotted block/task: 'outer1.inner.missing_task'
|
||||
36 | disable outer1.inner.missing_task;
|
||||
| ^~~~~~~~~~~~
|
||||
... Known scopes under 'outer1.inner': <no instances found>
|
||||
%Error: t/t_disable_task_scope_bad.v:37:18: Found definition of 'ifc1.data' as a VAR but expected a block/task
|
||||
37 | disable ifc1.data;
|
||||
| ^~~~
|
||||
%Error: t/t_disable_task_scope_bad.v:38:19: Found definition of 'prog1.data' as a VAR but expected a block/task
|
||||
38 | disable prog1.data;
|
||||
| ^~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#!/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-2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('linter')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// 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
|
||||
|
||||
interface Ifc;
|
||||
task run;
|
||||
endtask
|
||||
int data = 0;
|
||||
endinterface
|
||||
|
||||
program Prog;
|
||||
task run;
|
||||
endtask
|
||||
int data = 0;
|
||||
endprogram
|
||||
|
||||
module Inner;
|
||||
task run;
|
||||
endtask
|
||||
endmodule
|
||||
|
||||
module Outer;
|
||||
Inner inner();
|
||||
endmodule
|
||||
|
||||
module t;
|
||||
Ifc ifc1();
|
||||
Prog prog1();
|
||||
Outer outer1();
|
||||
|
||||
initial begin
|
||||
disable ifc1.missing_task;
|
||||
disable prog1.missing_task;
|
||||
disable outer1.inner.missing_task;
|
||||
disable ifc1.data;
|
||||
disable prog1.data;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
%Error: t/t_disable_task_target_bad.v:21:13: Found definition of 'foo' as a VAR but expected a block/task
|
||||
21 | disable foo;
|
||||
| ^~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_disable_task_target_bad.v:22:13: Found definition of 'c' as a VAR but expected a block/task
|
||||
22 | disable c.run;
|
||||
| ^
|
||||
%Error: t/t_disable_task_target_bad.v:22:15: Can't find definition of block/task: 'run'
|
||||
22 | disable c.run;
|
||||
| ^~~
|
||||
%Error: Exiting due to
|
||||
4
test_regress/t/t_disable_task_unsup.py → test_regress/t/t_disable_task_target_bad.py
Executable file → Normal file
4
test_regress/t/t_disable_task_unsup.py → test_regress/t/t_disable_task_target_bad.py
Executable file → Normal file
|
|
@ -4,12 +4,12 @@
|
|||
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
test.scenarios('linter')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// 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
|
||||
|
||||
class Cls;
|
||||
task run;
|
||||
endtask
|
||||
endclass
|
||||
|
||||
module t;
|
||||
task foo;
|
||||
endtask
|
||||
|
||||
Cls c;
|
||||
|
||||
initial begin
|
||||
int foo;
|
||||
c = new;
|
||||
disable foo;
|
||||
disable c.run;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
%Error-UNSUPPORTED: t/t_disable_task_unsup.v:37:10: Unsupported: disabling task by name
|
||||
37 | #1 disable increment_x;
|
||||
| ^~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_disable_task_unsup.v:26:5: Unsupported: disabling task by name
|
||||
26 | disable get_and_send;
|
||||
| ^~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2025 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
int x = 0;
|
||||
|
||||
task increment_x;
|
||||
x++;
|
||||
#2;
|
||||
x++;
|
||||
endtask
|
||||
|
||||
class driver;
|
||||
int m_time = 0;
|
||||
|
||||
task get_and_send();
|
||||
forever begin
|
||||
#10;
|
||||
m_time += 10;
|
||||
end
|
||||
endtask
|
||||
|
||||
task post_shutdown_phase();
|
||||
disable get_and_send;
|
||||
endtask
|
||||
endclass
|
||||
|
||||
module t;
|
||||
|
||||
driver c;
|
||||
|
||||
initial begin
|
||||
fork
|
||||
increment_x();
|
||||
#1 disable increment_x;
|
||||
join
|
||||
|
||||
if (x != 1) $stop;
|
||||
|
||||
c = new;
|
||||
fork
|
||||
c.get_and_send;
|
||||
join_none
|
||||
if (c.m_time != 0) $stop;
|
||||
|
||||
#11;
|
||||
if ($time != 12) $stop;
|
||||
if (c.m_time != 10) $stop;
|
||||
|
||||
#20;
|
||||
if ($time != 32) $stop;
|
||||
if (c.m_time != 30) $stop;
|
||||
c.post_shutdown_phase;
|
||||
|
||||
#20;
|
||||
if ($time != 52) $stop;
|
||||
if (c.m_time != 30) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue