verilator/test_regress/t/t_disable_task_by_name.v

437 lines
7.8 KiB
Systemverilog

// 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 z = 0;
int always_value = 0;
int self_entry = 0;
int self_after_disable = 0;
int par_started = 0;
int par_finished = 0;
int par_parent_continued = 0;
int named_block_fork_fail = 0;
int task_named_block_fork_fail = 0;
event named_block_fork_ev;
event task_named_block_fork_ev;
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 finish_z;
z++;
endtask
task always_foo;
always_value = #2 1;
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
task task_named_block_fork_disable;
begin : t_named_block
fork
begin
#5;
task_named_block_fork_fail = 1;
end
begin
@task_named_block_fork_ev;
disable t_named_block;
task_named_block_fork_fail = 2;
end
join
task_named_block_fork_fail = 3;
end
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();
always #6 disable always_foo;
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;
// Disable task by name from an external always block while task executes
always_value = 0;
#5;
always_foo;
#4;
if (always_value != 0) $stop;
// Module task disabled by sibling process in a fork
fork
increment_x();
#1 disable increment_x;
join_none
#10;
if (x != 1) $stop;
// Re-disabling after prior disable (no active invocations) is a no-op
disable increment_x;
#1;
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;
// Disable named block containing fork from inside a fork branch
fork
begin : named_block_under_test
fork
begin
#5;
named_block_fork_fail = 1;
end
begin
@named_block_fork_ev;
disable named_block_under_test;
named_block_fork_fail = 2;
end
join
named_block_fork_fail = 3;
end
join_none
#2;
->named_block_fork_ev;
#10;
if (named_block_fork_fail != 0) $stop;
// Same case as above, but with the named block inside a task
fork
task_named_block_fork_disable();
join_none
#2;
->task_named_block_fork_ev;
#10;
if (task_named_block_fork_fail != 0) $stop;
// Disabling a task after it already finished is a no-op
finish_z();
if (z != 1) $stop;
disable finish_z;
#1;
if (z != 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