HOME

    Electronics Directory Articles/ Tutorials eBooks

About Us

FORUM Links Contact Us
   

SystemVerilog Tutorial PART VII: by Abhiram Rao

SystemVerilog Mailboxes and Semaphores

 

    <Previous    Page     Next>

 

Interprocess Synchronization and Communication

High-level and easy-to-use synchronization and communication mechanism are essential to control the kinds of interactions that occur between dynamic processes used to model a complex system or a highly reactive testbench. Verilog provides basic synchronization mechanisms (i.e., -> and @), but they are all limited to static objects and are adequate for synchronization at the hardware level, but fall short of the needs of a highly dynamic, reactive test bench. At the system level, an essential limitation of Verilog is its inability to create dynamic events and communication channels, which match the capability to create dynamic processes. SystemVerilog adds a powerful and easy-to-use set of synchronization and communication mechanisms, all of which can be created and reclaimed dynamically. SystemVerilog adds a semaphore built-in class, which can be used for synchronization and mutual exclusion to shared resources, and a mailbox built-in class that can be used as a communication channel between processes. SystemVerilog also enhances Verilog’s named event data type to satisfy many of the system-level synchronization requirements.

Semaphores and mailboxes are built-in types, nonetheless, they are classes, and can be used as base classes for deriving additional higher level classes. These built-in classes reside in the built-in std package, thus, they can be re-defined by user code in any other scope.

 Semaphores

Conceptually, a semaphore is a bucket. When a semaphore is allocated, a bucket that contains a fixed number of keys is created. Processes using semaphores must first procure a key from the bucket before they can continue to execute. If a specific process requires a key, only a fixed number of occurrences of that process can be in progress simultaneously. All others must wait until a sufficient number of keys is returned to the bucket. Semaphores are typically used for mutual exclusion, access control to shared resources, and for basic synchronization. An example of creating a semaphore is:

                  semaphore sem_score;

Semaphore is a built-in class that provides the following methods:

  •  Create a semaphore with a specified number of keys: new()

  •  Obtain one or more keys from the bucket: get()

  •  Return one or more keys into the bucket: put()

  •  Try to obtain one or more keys without blocking: try_get()

  1. new()

   Semaphores are created with the new() method.

   The prototype for semaphore new() is:

            function new(int keyCount = 0 );

   The KeyCount specifies the number of keys initially allocated to the semaphore bucket.     The number of keys in the bucket can increase beyond KeyCount when more keys are put into the semaphore than are removed. The default value for KeyCount is 0.

The new() function returns the semaphore handle, or null if the semaphore cannot be created.

  2.  put()

       The semaphore put() method is used to return keys to a semaphore.

      The prototype for put() is:

                           task put(int keyCount = 1);

      keyCount specifies the number of keys being returned to the semaphore. The default is 1.

     When the semaphore.put() task is called, the specified number of keys are returned to the semaphore. If a process has been suspended waiting for a key, that process shall execute if enough keys have been returned.

  3.  get()

    The semaphore get() method is used to procure a specified number of keys from a semaphore.

           The prototype for get() is:

                                             task get(int keyCount = 1);

          keyCount specifies the required number of keys to obtain from the semaphore. The default is 1.

          If the specified number of keys are available, the method returns and execution continues. If the specified number of key are not available, the process blocks until the keys become available.

The semaphore waiting queue is First-In First-Out (FIFO). This does not guarantee the order in which processes arrive at the queue, only that their arrival order shall be preserved by the semaphore.

   4.  try_get()

     The semaphore try_get() method is used to procure a specified number of keys from a semaphore, but without blocking.

              The prototype for try_get() is:

                function int try_get(int keyCount = 1);

             keyCount specifies the required number of keys to obtain from the semaphore. The default is 1.If the specified number of keys are available, the method returns 1 and execution continues. If the specified number of key are not available, the method returns 0.

 

 Mailboxes

A mailbox is a communication mechanism that allows messages to be exchanged between processes. Data can be sent to a mailbox by one process and retrieved by another. Conceptually, mailboxes behave like real mailboxes. When a letter is delivered and put into the mailbox, one can retrieve the letter (and any data stored within). However, if the letter has not been delivered when one checks the mailbox, one must choose whether to wait for the letter or retrieve the letter on subsequent trips to the mailbox. Similarly, SystemVerilog’s mailboxes provide processes to transfer and retrieve data in a controlled manner. Mailboxes are created as having either a bounded or unbounded queue size. A bounded mailbox becomes full when it contains the bounded number of messages. A process that attempts to place a message into a full mailbox shall be suspended until enough room becomes available in the mailbox queue. Unbounded mailboxes never suspend a thread in a send operation.

An example of creating a mailbox is:

                   mailbox mbox_pointer;

Mailbox is a built-in class that provides the following methods:

  •  Create a mailbox: new()

  •  Place a message in a mailbox: put()

  •  Try to place a message in a mailbox without blocking: try_put()

  •  Retrieve a message from a mailbox: get() or peek()

  •  Try to retrieve a message from a mailbox without blocking: try_get() or try_peek()

  •  Retrieve the number of messages in the mailbox: num()

  1. new()   

                Mailboxes are created with the new() method.

      The prototype for mailbox new() is:

                        function new(int bound = 0);

     The new() function returns the mailbox handle, or null if the mailboxes cannot be created. If the bound argument is zero then the  mailbox is unbounded (the default) and a put() operation shall never block. If bound is non-zero, it represents the size of the mailbox queue.

The bound must be positive. Negative bounds are illegal and can result in indeterminate behavior, but implementations can issue a warning.

  2. num()   

      The number of messages in a mailbox can be obtained via the num() method.

      The prototype for num() is:

                   function int num();

      The num() method returns the number of messages currently in the mailbox.The returned value should be used with care, since it is valid only until the next get() or put() is executed on the mailbox. These mailbox operations can be from different processes than the one executing the num() method. Therefore, the validity of the returned value shall depend on the time that the other methods start and finish.

  3. put()

      The put() method places a message in a mailbox.

      The prototype for put() is:

                  task put( singular message);

      The message is any singular expression, including object handles.

      The put() method stores a message in the mailbox in strict FIFO order. If the mailbox was created with a bounded queue the process shall be suspended until there is enough room in the queue.

  4. try_put()

      The try_put() method attempts to place a message in a mailbox.

      The prototype for try_put() is:

              function int try_put( singular message);

      The message is any singular expression, including object handles.

    The try_put() method stores a message in the mailbox in strict FIFO order. This method is meaningful only for bounded mailboxes. If the mailbox is not full then the specified message is placed in the mailbox and the function returns 1. If the mailbox is full, the method returns 0.

  5. get()

      The get() method retrieves a message from a mailbox.

      The prototype for get() is:

             task get( ref singular message );

    The message can be any singular expression, and it must be a valid left-hand side expression. The get() method retrieves one message from the mailbox, that is, removes one message from the mailbox queue. If the mailbox is empty then the current process blocks until a message is placed in the mailbox. If there is a type mismatch between the message variable and the message in the mailbox, a runtime error is generated.Non-parameterized mailboxes are type-less, that is, a single mailbox can send and receive different types of data. Thus, in addition to the data being sent (i.e., the message queue), a mailbox implementation must maintain the message data type placed by put(). This is required in order to enable the runtime type checking.

The mailbox waiting queue is FIFO. This does not guarantee the order in which processes arrive at the queue,only that their arrival order shall be preserved by the mailbox.

  6. try_get()

    The try_get() method attempts to retrieves a message from a mailbox without blocking.

    The prototype for try_get() is:

             function int try_get( ref singular message );

    The message can be any singular expression, and it must be a valid left-hand side expression.

The try_get() method tries to retrieve one message from the mailbox. If the mailbox is empty, then the method returns 0. If there is a type mismatch between the message variable and the message in the mailbox, the method returns –1. If a message is available and the message type matches the type of the message variable, the message is retrieved and the method returns 1.

  7. peek()

     The peek() method copies a message from a mailbox without removing the message from the queue.

     The prototype for peek() is:

              task peek( ref singular message );

The message can be any singular expression, and it must be a valid left-hand side expression.

The peek() method copies one message from the mailbox without removing the message from the mailbox queue. If the mailbox is empty then the current process blocks until a message is placed in the mailbox. If there is a type mismatch between the message variable and the message in the mailbox, a runtime error is generated.

Note that calling peek() can cause one message to unblock more than one process. As long as a message remains in the mailbox queue, any process blocked in either a peek() or get() operation shall become unblocked.

  8. try_peek()

      The try_peek() method attempts to copy a message from a mailbox without blocking.

      The prototype for try_peek() is:

             function int try_peek( ref singular message );

The message can be any singular expression, and it must be a valid left-hand side expression.

The try_peek() method tries to copy one message from the mailbox without removing the message from the mailbox queue. If the mailbox is empty, then the method returns 0. If there is a type mismatch between the message variable and the message in the mailbox, the method returns –1. If a message is available and the message type matches, the type of the message variable, the message is copied and the method returns 1.

 <Previous    Page       Next>

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