|
<Previous
TOC
Next >
Process execution threads
SystemVerilog creates a thread of execution for:
Each continuous assignment can also be considered its own thread.
Process
control
SystemVerilog provides constructs that allow one process to terminate or
wait for the completion of other processes. The wait fork construct
waits for the completion of processes. The disable fork construct
stops the execution of processes.
Wait fork
The wait fork statement is used to ensure that all child processes
(processes created by the calling process)
have completed their execution.
The syntax for wait fork is:
wait fork ;
Specifying wait fork causes the calling process to block until all
its sub-processes have completed. Verilog terminates a simulation run when
there is no further activity of any kind. SystemVerilog adds the ability
to automatically terminate the simulation when all its program blocks
finish executing (i.e, they reach the end of their execute block),
regardless of the status of any child processes . The wait fork
statement allows a program block to wait for the completion of all its
concurrent threads before exiting. In the following example, in the task
do_test, the first two processes are spawned and the task blocks until one
of the two processes completes (either exec1, or exec2). Next, two more
processes are spawned in the background.
The wait fork statement shall ensure that the task do_test waits
for all four spawned processes to
complete before returning to its caller.
|
task do_test;
fork
exec1();
exec2();
join_any
fork
exec3();
exec4();
join_none
wait fork; // block until exec1 ... exec4 complete
endtask
|
Disable
fork
The disable fork statement terminates all active descendants
(sub-processes) of the calling process.
The syntax for disable fork is:
disable fork ;
The disable fork statement terminates all descendants of the
calling process, as well as the descendants of
the process’ descendants, that is, if any of the child processes have
descendants of their own, the disable fork statement shall
terminate them as well.
In the example below, the task get_first spawns three versions of a task
that wait for a particular device (1, 7, or 13). The task wait_device
waits for a particular device to become ready and then returns the
device’s address. When the first device becomes available, the get_first
task shall resume execution and proceed to kill the outstanding
wait_device processes.
|
task get_first( output
int adr );
fork
wait_device( 1, adr );
wait_device( 7, adr );
wait_device( 13, adr );
join_any
disable fork;
endtask |
Verilog supports the disable construct, which terminate a process
when applied to the named block being executed by the process. The
disable fork statement differs from disable in that disable
fork considers the dynamic parent-child relationship of the processes,
whereas disable uses the static, syntactical information of the
disabled block. Thus, disable shall end all processes executing a
particular block, whether the processes were forked by the calling thread
or not, while disable fork shall end only those processes that
were spawned by the calling thread.
Fine-grain process control
A process is a built-in class that allows one process to access and
control another process once it has started.
Users can declare variables of type process and safely pass them through
tasks or incorporate them into other
objects. The prototype for the process class is:
|
class process;
enum state { FINISHED, RUNNING, WAITING, SUSPENDED, KILLED };
static function process self();
function state status();
task kill();
task await();
task suspend();
task resume();
endclass |
Objects of type process are created internally when processes are spawned.
Users cannot create objects of type process; attempts to call new
shall not create a new process, and instead result in an error. The
process class cannot be extended. Attempts to extend it shall result in a
compilation error. Objects of type process are unique; they become
available for reuse once the underlying process terminates and all
references to the object are discarded.
The self() function returns a handle to the current process, that
is, a handle to the process making the call. The status() function
returns the process status, as defined by the state enumeration:
-
FINISHED Process terminated normally.
-
RUNNING
Process is currently running (not in a blocking statement).
-
WAITING
Process is waiting in a blocking statement.
-
SUSPENDED
Process is stopped awaiting a resume.
-
KILLED
Process was forcibly killed (via kill or disable).
The kill() task terminates the given process and all its
sub-processes, that is, processes spawned using fork statements by
the process being killed. If the process to be terminated is not blocked
waiting on some other condition, such as an event, wait expression,
or a delay then the process shall be terminated at some unspecified time
in the current time step.
The await() task allows one process to wait for the completion of
another process. It shall be an error to call this task on the current
process, i.e., a process cannot wait for its own completion.
The suspend() task allows a process to suspend either its own
execution or that of another process. If the process to be suspended is
not blocked waiting on some other condition, such as an event, wait
expression, or a delay then the process shall be suspended at some
unspecified time in the current time step. Calling this
method more than once, on the same (suspended) process, has no effect.
The resume() task restarts a previously suspended process. Calling
resume on a process that was suspended while blocked on another condition
shall re-sensitize the process to the event expression, or wait for the
wait condition to become true, or for the delay to expire. If the wait
condition is now true or the original delay has transpired, the process is
scheduled onto the Active or Reactive region, so as to continue its
execution in the current time step. Calling resume on a process that
suspends itself causes the process to continue to execute at the statement
following the call to suspend.
The example below starts an arbitrary number of processes, as specified by
the task argument N. Next, the task waits for the last process to start
executing, and then waits for the first process to terminate. At that
point the parent process forcibly terminates all forked processes that
have not completed yet.
|
task
do_n_way( int N );
process
job[1:N];
for ( int j = 1; j <= N; j++ )
fork
automatic int k = j;
begin
job[j] = process::self(); ... ;
end
join_none
for( int j = 1; j <= N; j++ ) // wait for all processes
to start
wait( job[j] != null );
job[1].await(); // wait for first process to finish
for ( int k = 1; k <= N; k++ )
begin
if ( job[k].status != process::FINISHED )
job[k].kill();
end
endtask |
To know more about threads with examples just
move on to the next page...
<Previous
TOC
Next >
|