<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:
-
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.
|