System Verilog allows us to read and write into files in the disk.
How to open and close a file ?
A file can be opened for either read or write using the $fopen()
system task. This task will return a 32-bit integer handle called a file descriptor. This handle should be used to read and write into that file until it is closed. The file descriptor can be closed with the $fclose()
system task. No further reads or writes to the file descriptor is allowed once it is closed.
Example
In the code shown below, we will declare a int
variable called fd to hold the file descriptor. fd is initially zero, and gets a valid value from $fopen()
and can be checked to see if the file opened successfully. The file is finally closed when $fclose()
is executed.
module tb;
initial begin
// 1. Declare an integer variable to hold the file descriptor
int fd;
// 2. Open a file called "note.txt" in the current folder with a "read" permission
// If the file does not exist, then fd will be zero
fd = $fopen ("./note.txt", "r");
if (fd) $display("File was opened successfully : %0d", fd);
else $display("File was NOT opened successfully : %0d", fd);
// 2. Open a file called "note.txt" in the current folder with a "write" permission
// "fd" now points to the same file, but in write mode
fd = $fopen ("./note.txt", "w");
if (fd) $display("File was opened successfully : %0d", fd);
else $display("File was NOT opened successfully : %0d", fd);
// 3. Close the file descriptor
$fclose(fd);
end
endmodule
It is important to close all open files before end of simulation to completely write contents into the file
Simulation Logncsim> run Open failed on file "./note.txt". No such file or directory File was NOT opened successfully : 0 File was opened successfully : -2147483645 ncsim: *W,RNQUIE: Simulation is complete.
Every now and then you come across the need to avoid testbench recompilation, and instead be able to accept values from the command line just like any scripting language like bash or perl would do. In SystemVerilog, this information is provided to the simulation as an optional argument always starting with the +
character. These arguments passed in from the command line are accessible in SV code via the following system functions called as plusargs.
Syntax
$test$plusargs (user_string)
$value$plusargs (user_string, variable)
$test$plusargs
The function $test$plusargs
is typically used when a value for the argument is not required. It searches the list of plusargs for a user specified string. A variable can also be used to specify the string, and any null character will be ignored. If the prefix of one of the supplied plusargs matches all characters in the provided string, the function will return a non-zero integer, and otherwise zero.
A SystemVerilog mailbox
is a way to allow different processes to exchange data between each other. It is similar to a real postbox where letters can be put into the box and a person can retrieve those letters later on.
SystemVerilog mailboxes are created as having either a bounded or unbounded queue size. A bounded mailbox
can only store a limited amount of data, and if a process attempts to store more messages into a full mailbox, it will be suspended until there's enough room in the mailbox. However, an unbounded mailbox
has unlimited size.
There are two types:
- Generic Mailbox that can accept items of any data type
- Parameterized Mailbox that can accept items of only a specific data type
SystemVerilog Mailbox vs Queue
Although a SystemVerilog mailbox essentially behaves like a queue, it is quite different from the queue
data type. A simple queue can only push and pop items from either the front or the back. However, a mailbox is a built-in class that uses semaphores to have atomic control the push and pop from the queue. Moreover, you cannot access a given index within the mailbox queue, but only retrieve items in FIFO order.
Where is a mailbox used ?
A SystemVerilog mailbox
is typically used when there are multiple threads running in parallel and want to share data for which a certain level of determinism is required.
Generic Mailbox Example
Two processes are concurrently active in the example shown below, where one initial
block puts data into the mailbox and another initial
block gets data from the mailbox.
module tb;
// Create a new mailbox that can hold utmost 2 items
mailbox mbx = new(2);
// Block1: This block keeps putting items into the mailbox
// The rate of items being put into the mailbox is 1 every ns
initial begin
for (int i=0; i < 5; i++) begin
#1 mbx.put (i);
$display ("[%0t] Thread0: Put item #%0d, size=%0d", $time, i, mbx.num());
end
end
// Block2: This block keeps getting items from the mailbox
// The rate of items received from the mailbox is 2 every ns
initial begin
forever begin
int idx;
#2 mbx.get (idx);
$display ("[%0t] Thread1: Got item #%0d, size=%0d", $time, idx, mbx.num());
end
end
endmodule
Semaphore is just like a bucket with a fixed number of keys. Processes that use a semaphore must first get a key from the bucket before they can continue to execute. Other proceses must wait until keys are available in the bucket for them to use. In a sense, they are best used for mutual exclusion, access control to shared resources and basic synchronization.
Syntax
semaphore [identifier_name];
Note that semaphore
is a built-in class and hence it should be used just like any other class object. It has a few methods with which we can allocate the number of keys for that semaphore
object, get and put keys into the bucket.
Methods
Sometimes we come across scenarios where we want the solver to randomly pick one out of the many statements. The keyword randcase
introduces a case
statement that randomly selects one of its branches. The case item expressions are positive integer values that represent the weights associated with each item. Probability of selecting an item is derived by the division of that item's weight divided by the sum of all weights.
Syntax
randcase
item : statement;
...
endcase
Example
The sum of all weights is 9, and hence the probability of taking the first branch is 1/9 or 11.11%, the probability of taking the second branch is 5/9 or 55.56% and the probability of taking the last branch is 3/9 or 33.33%.