SystemVerilog functions have the same characteristics as the ones in Verilog.

Functions

The primary purpose of a function is to return a value that can be used in an expression and cannot consume simulation time.

  • A function cannot have time controlled statements like @, #, fork join, or wait
  • A function cannot start a task since tasks are allowed to consume simulation time

Click here to refresh functions in Verilog !

ANSI-C style declaration

  
  
module tb;
	
  	// There are two ways to call the function:
  	initial begin
      // 1. Call function and assign value to a variable, and then use variable
      int s = sum(3, 4);
      $display ("sum(3,4) = %0d", s);
      
      // 2. Call function and directly use value returned
      $display ("sum(5,9) = %0d", sum(5,9));
      
      $display ("mul(3,1) = %0d", mul(3,1));
    end
  
  	// This function returns value of type "byte", and accepts two 
  	// arguments "x" and "y". A return variable of the same name as
  	// function is implicitly declared and hence "sum" can be directly
  	// assigned without having to declare a separate return variable
	function byte sum (int x, int y);
		sum = x + y;
	endfunction
  
  	// Instead of assigning to "mul", the computed value can be returned
  	// using "return" keyword
  	function byte mul (int x, y);
      	return x * y;
  	endfunction
endmodule

  

As we saw in a previous article, bigger and complex designs are built by integrating multiple modules in a hierarchical manner. Modules can be instantiated within other modules and ports of these instances can be connected with other signals inside the parent module.

These port connections can be done via an ordered list or by name.

Port Connection by ordered list

One method of making the connection between the port expressions listed in a module instantiation with the signals inside the parent module is by the ordered list.

mydesign is a module instantiated with the name d0 in another module called tb_top. Ports are connected in a certain order which is determined by the position of that port in the port list of the module declaration. For example, b in the testbench is connected to y of the design simply because both are at the second position in the list of ports.

  
  
	module mydesign ( input  x, y, z,     // x is at position 1, y at 2, x at 3 and
	                  output o);          // o is at position 4
	                  
	endmodule

	module tb_top;
		wire [1:0]  a;
		wire        b, c;
		
		mydesign d0  (a[0], b, a[1], c);  // a[0] is at position 1 so it is automatically connected to x
		                                  // b is at position 2 so it is automatically connected to y
		                                  // a[1] is at position 3 so it is connected to z
		                                  // c is at position 4, and hence connection is with o
	endmodule

  

Order of ports in the design module should be known for a correct connection.

This is very inconvenient because the order might change if a new port is added to the list or when the number of ports in the design is very large.

Ports are a set of signals that act as inputs and outputs to a particular module and are the primary way of communicating with it. Think of a module as a fabricated chip placed on a PCB and it becomes quite obvious that the only way to communicate with the chip is through its pins. Ports are like pins and are used by the design to send and receive signals from the outside world.

verilog-port

Types of Ports

Port Description
Input The design module can only receive values from outside using its input ports
Output The design module can only send values to the outside using its output ports
Inout The design module can either send or receive values using its inout ports

Ports are by default considered as nets of type wire.

Syntax

Ports declared as inout can act as both input and output.

  
  
	input  [net_type] [range] list_of_names; 	// Input port
	inout  [net_type] [range] list_of_names; 	// Input & Output port
	output [net_type] [range] list_of_names; 	// Output port driven by a wire
	output [var_type] [range] list_of_names; 	// Output port driven by a variable

  

Example

In the code shown below, there are three input ports, one output port and one inout port.

  
  
module  my_design ( input wire			clk,
                    input 					en,
                    input 					rw,
                    inout [15:0]	  data,
                    output 					int );
                    
	// Design behavior as Verilog code
	
endmodule

  

It is illegal to use the same name for multiple ports.

  
  
	input  aport;         // First declaration - valid
	input  aport;         // Error - already declared
	output aport;         // Error - already declared

  

There are ways to group a set of statements together that are syntactically equivalent to a single statement and are known as block statements. There are two kinds of block statements: sequential and parallel.

Sequential

Statements are wrapped using begin and end keywords and will be executed sequentially in the given order, one after the other. Delay values are treated relative to the time of execution of the previous statement. After all the statements within the block are executed control may be passed elsewhere.

initial-begin-end
  
  
module design0;
	bit [31:0] data;
  
	// "initial" block starts at time 0
	initial begin
      
		// After 10 time units, data becomes 0xfe 
		#10   data = 8'hfe;
		$display ("[Time=%0t] data=0x%0h", $time, data);
      
		// After 20 time units, data becomes 0x11
		#20   data = 8'h11;
		$display ("[Time=%0t] data=0x%0h", $time, data);
	end
endmodule

  

In the example above, first statement in the begin-end block will be executed at 10 time units, and the second statement at 30 time units because of the relative nature. It is 20 time units after execution of the previous statement.

Simulation Log

ncsim> run
[Time=10] data=0xfe
[Time=30] data=0x11
ncsim: *W,RNQUIE: Simulation is complete.

A function is meant to do some processing on the input and return a single value, whereas a task is more general and can calculate multiple result values and return them using output and inout type arguments. Tasks can contain simulation time consuming elements such as @, posedge and others.

Syntax

A task need not have a set of arguments in the port list, in which case it can be kept empty.

  
  
	// Style 1
	task [name];
		input  [port_list];
		inout  [port_list];
		output [port_list];
		begin
			[statements]
		end
	endtask

	// Style 2
	task [name] (input [port_list], inout [port_list], output [port_list]);
		begin
			[statements]
		end
	endtask
	
	// Empty port list
	task [name] ();
		begin
			[statements]
		end
	endtask