// 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