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.

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.

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.
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
Often times we find certain pieces of code to be repetitive and called multiple times within the RTL. They mostly do not consume simulation time and might involve complex calculations that need to be done with different data values. In such cases, we can declare a function
and place the repetitive code inside the function and allow it to return the result. This will reduce the amount of lines in the RTL drastically since all you need to do now is to do a function call and pass data on which the computation needs to be performed. In fact, this is very similar to the functions in C.
The purpose of a function is to return a value that is to be used in an expression. A function definition always start with the keyword function
followed by the return type, name and a port list enclosed in parantheses. Verilog knows that a function definition is over when it finds the endfunction
keyword. Note that a function shall have atleast one input declared and the return type will be void
if the function does not return anything.
Syntax
function [automatic] [return_type] name ([port_list]);
[statements]
endfunction
The keyword automatic
will make the function reentrant and items declared within the task are dynamically allocated rather than shared between different invocations of the task. This will be useful for recursive functions and when the same function is executed concurrently by N processes when forked.