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 Log

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

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