HOME

    Electronics Directory Articles/ Tutorials eBooks

About Us

FORUM Links Contact Us
   

SystemVerilog Tutorial PART 15: by Abhiram Rao

Threads in SystemVerilog

 

<Previous     TOC      Next >


Process execution threads

 

SystemVerilog creates a thread of execution for:

  •  Each initial block

  •  Each always block

  •  Each parallel statement in a fork...join (or join_any or join_none) statement group

  •  Each dynamic process

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 >

 

 

Home   |    About Us   |   Articles/ Tutorials   |   Downloads   |   Feedback   |   Links   |   eBooks   |   Privacy Policy
Copyright © 2005-2007 electroSofts.com.
webmaster@electroSofts.com