A single interface handle connects a testbench to one instance of a design. When a design itself is replicated — multiple identical DUT instances, multiple lanes on a wide bus, multiple channels on a peripheral — the interface connecting to it can be replicated the same way, using an array of interface instances instead of one instance per connection.

6 min read | Intermediate Level

What You'll Learn

  • How to declare and instantiate an array of interface instances
  • How to connect individual array elements to individual design instances
  • How implicit port connections (.*) work with an interface port
  • A parsing gotcha with interfaces that have no port list

Declaring an Interface Array

Below, an interface named myInterface with an empty port list is created and instantiated within the top-level testbench module. It's also fine to omit the parentheses for an empty port list and simply end the statement with a semicolon — but see the note later in this article before doing that.


// interface myInterface;

interface myInterface ();
	reg 		gnt;
	reg 		ack;
	reg [7:0]	irq;

	...
endinterface

module tb;
	// Single interface handle
	myInterface 	if0 ();  

	// An array of interfaces
	myInterface 	wb_if [3:0] ();

	// Rest of the testbench
endmodule

A single interface called if0 can be instantiated, and its signals accessed by referencing that handle — used to drive and sample signals going to the DUT. You can also have an array of interfaces; here, wb_if has 4 instances, declared with the same [3:0] array syntax you'd use for any other array.

Connecting Individual Array Elements to a Design

Each element of an interface array can be connected to its own design instance individually, the same way you'd index into any other array:


module myDesign ( myInterface dut_if,
                  input logic clk);
  
	always @(posedge clk)
		if (dut_if.ack)
			dut_if.gnt <= 1;

endmodule

module tb;
	reg clk;

	// Single interface handle connection
	myInterface  if0;
	myDesign 	 top (if0, clk);

	// Or connect by name
	// myDesign  top (.dut_if(if0), .clk(clk));

	// Multiple design instances connected to the appropriate
	// interface handle
	myDesign 	md0 (wb_if[0], clk);
	myDesign 	md1 (wb_if[1], clk);
	myDesign 	md2 (wb_if[2], clk);
	myDesign 	md3 (wb_if[3], clk);

endmodule

md0 through md3 are four separate instances of the same design, each wired to its own independent interface instance — wb_if[0] through wb_if[3] — so each one has its own ack, gnt, and irq signals rather than sharing a single set.

Quick Check

Q: If md0 and md1 above were both connected to wb_if[0] instead of separate array elements, what would change?

Show Answer

Both design instances would share the exact same ack, gnt, and irq signals — whatever one instance drives, the other would see too. That's rarely what you want when the two instances are meant to behave independently, which is exactly why an array of interfaces exists.

Implicit Port Connections

When an interface is referenced as a port, its variables and nets are assumed to have ref and inout access respectively. If the same identifier is used as both the interface instance name and the port name in the design, implicit port connections can also be used:


module tb;
	reg clk;

	myInterface 	dut_if();

	// Can use implicit port connection when all port signals have same name
	myDesign 		top (.*);

endmodule

An error will occur if an interface without ports is instantiated without parentheses (), even though the interface itself was declared with an empty port list:


// Declare an interface without ports  
interface mif;
  logic m_a;  
endinterface

module tb;
  mif   m_if;      // ERROR !
  mif   m_if();    // Okay  
endmodule  

Output

Output
  mif 	m_if;
      	   |
xmvlog: *E,NOIPRT (testbench.sv,12|10): Unrecognized declaration 'm_if' could be an unsupported keyword, a spelling mistake or missing instance port list '()' [SystemVerilog].  

Where Are Interface Arrays Used?

Interface arrays are the natural fit for anything replicated across a design — connecting to N identical peripheral instances, a multi-lane SerDes or multi-channel DMA engine, or verifying several parallel instances of the same block in a single testbench without hand-writing a separate interface declaration for each one.

Common Beginner Mistakes

Mistake #1: Connecting multiple design instances to the same array element

Every design instance meant to behave independently needs its own interface array element. Why? Two instances sharing one element share every signal on it too, which silently merges behavior that was supposed to stay separate — exactly the scenario in the Quick Check above.

Mistake #2: Omitting () when instantiating a port-less interface

As shown above, mif m_if; fails to compile, while mif m_if(); succeeds. Why? Without the parentheses, the parser can't distinguish the statement from other kinds of declarations — always include () at instantiation, even for an interface with nothing in its own port list, and even for each element of an array.

Mistake #3: Assuming .* connects a mismatched interface port name

.* only auto-connects ports whose names exactly match a visible local signal. Why? If the local interface instance and the module's port aren't named identically, .* silently skips that connection instead of erroring, which can leave a port unconnected without any obvious warning.

Practice Exercise

Challenge: Connect an Array of 8 Design Instances

Using the myInterface and myDesign declarations from earlier, declare an 8-element array of myInterface instances called ch_if, and connect 8 instances of myDesign (ch0 through ch7) to it, one per element.

Hint

Follow the same myInterface name [N:0] (); array declaration pattern shown for wb_if, then index into it the same way md0 through md3 did.

Solution

module tb;
	reg clk;

	// An array of 8 interface instances
	myInterface 	ch_if [7:0] ();

	// 8 design instances, each connected to its own array element
	myDesign 	ch0 (ch_if[0], clk);
	myDesign 	ch1 (ch_if[1], clk);
	myDesign 	ch2 (ch_if[2], clk);
	myDesign 	ch3 (ch_if[3], clk);
	myDesign 	ch4 (ch_if[4], clk);
	myDesign 	ch5 (ch_if[5], clk);
	myDesign 	ch6 (ch_if[6], clk);
	myDesign 	ch7 (ch_if[7], clk);

endmodule

Each myDesign instance gets its own independent set of ack/gnt/irq signals via its own ch_if element — the same one-array-element-per-instance pattern as wb_if earlier, just scaled up to 8 channels.

Summary

In this tutorial, you learned:

  • An interface array is declared with the same array syntax as any other array, and instantiated with () per element
  • Each array element is an independent interface instance with its own signals — connecting more than one design instance to the same element merges their signals together
  • .* auto-connects ports only when the local signal name exactly matches the port name
  • An interface with no port list still needs () at instantiation, or it fails to compile

Continuing the Interfaces & Modports Chapter

See SystemVerilog Interface Intro for how the same interface connects to both a Verilog and a SystemVerilog design, and SystemVerilog Modport for restricting signal directions per module.

Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5