An interface can be parameterized the same way a module can — letting the same interface definition adapt its signal widths, depths, or other configuration to whatever design it's connected to, instead of writing a separate interface for every bus width you need to support.

6 min read | Intermediate Level

What You'll Learn

  • The basic syntax for declaring a parameter on an interface
  • How to override a parameter's default value at instantiation
  • How to connect a parameterized interface to a design that shares the same parameter
  • What happens when an interface's parameter and a design's parameter don't match

Basic Syntax

A parameter is declared the same way you would for a module — between #( and ), right after the interface name:


interface myBus #(parameter D_WIDTH=31) (input clk);
	logic [D_WIDTH-1:0] data;
	logic               enable;
endinterface

D_WIDTH defaults to 31 here, so data ([D_WIDTH-1:0], or [30:0]) is 31 bits wide unless something overrides it.

Overriding the Default at Instantiation

Just like a module, an interface's parameter is overridden with #(...) at the point of instantiation:


module tb;
	logic clk;

	// Uses the default: D_WIDTH = 31, so data is 31 bits wide
	myBus bus_default (clk);

	// Overrides D_WIDTH to 8, so data is only 8 bits wide
	myBus #(.D_WIDTH(8)) bus_narrow (clk);
endmodule

bus_default keeps the interface's own default of 31, giving it a 31-bit data signal. bus_narrow overrides D_WIDTH to 8, shrinking data down to 8 bits — the same interface definition, sized two different ways in two different instances.

Quick Check

Q: If D_WIDTH is overridden to 15 at instantiation, how many bits wide is data?

Show Answer

15 bits. data is declared [D_WIDTH-1:0], so with D_WIDTH = 15, that's [14:0] — bit 14 down to bit 0 inclusive, which is 15 bits total.

Connecting a Parameterized Interface to a Design

The design that uses the interface typically declares a matching parameter of its own, so both sides agree on the width being used:


module bus_slave #(parameter D_WIDTH = 31) (myBus.slave bus_if);
  logic [D_WIDTH-1:0] captured_data;

  always @ (posedge bus_if.clk)
    if (bus_if.enable)
      captured_data <= bus_if.data;
endmodule

module d_top;
	logic clk;

	// Both the interface and the design agree on an 8-bit width
	myBus      #(.D_WIDTH(8)) bus_if (clk);
	bus_slave  #(.D_WIDTH(8)) slave0 (bus_if);
endmodule

Here, both bus_if and slave0 are instantiated with D_WIDTH(8), so captured_data inside bus_slave is declared the same width as bus_if.data — 8 bits, matching on both sides.

Nothing automatically keeps an interface's parameter and a connected design's parameter in sync — they're two separate parameter overrides that happen to use the same value here because both were written consistently. If one is overridden and the other is forgotten, the two sides can end up with mismatched widths without any compile-time error in many cases, since a narrower signal simply truncates or a wider one zero-extends at the connection.

Where Is Interface Parameterization Used?

A parameterized bus interface is what lets the same VIP support an 8-bit, 16-bit, or 32-bit version of a protocol without maintaining a separate interface file for each width — the design and testbench both instantiate the same interface definition, just with a different parameter override, keeping the signal widths consistent between them.

Common Beginner Mistakes

Mistake #1: Overriding the interface's parameter but not the design's

Since the interface and the connected design each declare their own parameter independently, overriding one without the other leaves them out of sync. Why? This can silently produce a width mismatch at the connection instead of a clear compile-time error, which is much harder to debug than a straightforward mismatch message.

Mistake #2: Assuming the interface's parameter automatically appears on the design side

A design module doesn't inherit a parameter value just because it's connected to a parameterized interface. Why? The design needs its own matching parameter declaration and its own explicit override — the interface and the module are two separate parameterized entities that simply happen to be connected.

Mistake #3: Forgetting the default value entirely

An interface parameter without a default (or with a default that doesn't match common usage) forces every instantiation to explicitly override it, even when most instances would want the same value. Why? Choosing a sensible default, like the most common bus width in a project, keeps most instantiations simple while still allowing the occasional override where it's actually needed.

Practice Exercise

Challenge: Add a Second Parameter

Extend myBus with a second parameter, ADDR_WIDTH, defaulting to 16, and add an addr signal sized [ADDR_WIDTH-1:0]. Then instantiate it with D_WIDTH overridden to 8 and ADDR_WIDTH left at its default.

Hint

Multiple parameters are comma-separated inside the same #(...) list, both when declaring the interface and when overriding values at instantiation — you don't need to override every parameter just because you're overriding one.

Solution

interface myBus #(parameter D_WIDTH=31, parameter ADDR_WIDTH=16) (input clk);
	logic [D_WIDTH-1:0]    data;
	logic [ADDR_WIDTH-1:0] addr;
	logic                  enable;
endinterface

module tb;
	logic clk;

	// D_WIDTH overridden to 8 (8-bit data); ADDR_WIDTH left at its default of 16 (16-bit addr)
	myBus #(.D_WIDTH(8)) bus_if (clk);
endmodule

Only D_WIDTH needed to be named in the override — ADDR_WIDTH simply keeps its declared default of 16 (a 16-bit addr, since [15:0] is 16 bits) since it wasn't mentioned at instantiation.

Summary

In this tutorial, you learned:

  • An interface is parameterized the same way a module is, with #(parameter ...) right after its name
  • A parameter's default is overridden at instantiation with #(.PARAM(value)), the same syntax used for modules
  • A design connected to a parameterized interface needs its own matching parameter declaration and override — nothing links the two automatically
  • Mismatched parameters between an interface and its connected design can silently produce a width mismatch instead of a clear error
  • Multiple parameters are declared and overridden as a comma-separated list; any parameter left out of an override simply keeps its default

Continuing the Interfaces & Modports Chapter

See SystemVerilog Interface for the broader set of interface capabilities, and SystemVerilog Interface Array for connecting multiple instances of the same interface.

Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5