Connecting module ports by explicitly writing out every .port_name(signal_name) pair gets repetitive fast, especially once a design has dozens of ports that already share names with the signals feeding them. SystemVerilog adds two shorthands, .name and .*, that let a module instantiation connect ports automatically whenever the port name and the local signal name match.

7 min read | Intermediate Level

What You'll Learn

  • How the .name shorthand connects a single port to an identically named signal
  • How .* connects every remaining port at once, and when to mix it with explicit connections
  • The one rule that trips up most people: .name and .* behave differently when no matching signal exists
  • Where these shortcuts matter most in design versus verification code

Why Port Connections Get Repetitive

A typical module instantiation lists every port explicitly:


module alu (
  output [7:0] alu_out,
  output       zero,
  input  [7:0] ain,
  input  [7:0] bin,
  input  [2:0] opcode
);
  // ...
endmodule

module top;
  wire [7:0] alu_out, ain, bin;
  wire       zero;
  wire [2:0] opcode;

  alu alu_inst (
    .alu_out (alu_out),
    .zero    (zero),
    .ain     (ain),
    .bin     (bin),
    .opcode  (opcode)
  );
endmodule

Every connection here repeats the same identifier twice: once as the port name, once as the local signal name. When a designer deliberately names local signals after the ports they connect to - a very common RTL convention - that repetition is pure boilerplate. SystemVerilog lets you drop it.

The .name Shorthand

.port_name on its own is shorthand for .port_name(port_name). It connects the port to a signal of the exact same name declared in the instantiating scope:


alu alu_inst (
  .alu_out,   // same as .alu_out(alu_out)
  .zero,      // same as .zero(zero)
  .ain,       // same as .ain(ain)
  .bin,       // same as .bin(bin)
  .opcode     // same as .opcode(opcode)
);

Two conditions have to hold for .name to work: a signal with that exact name has to exist in the instantiating module, and its data type has to be equivalent to the port's. If either condition fails, elaboration reports an error - .name never silently falls back to leaving the port unconnected, even if the port has a default value.

To genuinely leave a port unconnected while still documenting it, use empty parentheses instead: .zero(). That is different from .zero, and the distinction matters - see the mistakes section below.

The .* Wildcard

.* extends the same idea to every port in one go. It connects every port that is not explicitly listed elsewhere in the same instantiation, as long as a same-named, same-type signal exists locally:


alu alu_inst (.*);   // connects alu_out, zero, ain, bin and opcode automatically

.* can appear at most once per instantiation, but it can be mixed with explicit connections to call out exceptions - either a port that needs a different signal, or one you want to leave deliberately unconnected:


alu alu_inst (
  .zero  (),          // leave this one unconnected
  .ain   (ain_reg),   // connect to a differently named signal
  .*                  // everything else (alu_out, bin, opcode) connects by name
);

.name vs. .*: The Default-Value Difference

Both shortcuts connect ports by matching names, but they disagree on what happens when a port has a default value and no matching local signal exists. This is the single most useful distinction to remember:

.name.*
Matching signal foundConnects itConnects it
No matching signal, port has a default valueError - the default value is ignoredDefault value is used
No matching signal, no default valueErrorPort is left unconnected
Appears more than once per instantiationAllowed (one per port)Illegal

The reasoning is intent. Writing .rst_n by name is a deliberate statement that you want to connect that exact port, so a missing signal is treated as a mistake worth flagging. .* is a broad, catch-all shortcut, so a port it cannot match quietly falls back to whatever the module author intended by giving it a default in the first place.

Quick Check

Q: A module has input logic rst_n = 1'b1. The instantiating scope has no signal called rst_n. What happens with .rst_n versus with .*?

Show Answer

.rst_n is an elaboration error, because an implicit named connection never uses the default value. .* instead uses the default value 1'b1, since the port is left for the wildcard to resolve and no matching signal was found.

Using These Shortcuts in Design and Verification

In RTL design, .* is most valuable at the top level of a block where a wrapper module's ports are already named to match the sub-block underneath it one-for-one - common when a module is a thin reset, clock-gating, or pin-muxing wrapper around a larger core. Explicit connections then call out only the handful of signals that genuinely differ.

In verification, .* is even more common. A testbench module typically declares its signals with exactly the DUT's port names on purpose, so the entire DUT instantiation collapses to dut (.*);. Any signal the testbench deliberately drives through a different net - for example, a force/release wire used only for fault injection - is still called out explicitly alongside the wildcard.

Common Beginner Mistakes

Mistake #1: Confusing .port with .port()

.rst_n (no parentheses) asks for an implicit connection to a signal called rst_n. .rst_n() (empty parentheses) explicitly leaves the port unconnected. Typing the parentheses by habit, out of muscle memory from explicit connections, silently changes the meaning from "connect it" to "don't connect it."

Mistake #2: Relying on .* when names are similar but not identical

Wrong: wire [7:0] aluOut; with a port named alu_out, expecting .* to connect them.

Right: match the exact spelling and capitalization, or connect that one port explicitly.

SystemVerilog is case-sensitive and does not infer connections from similar names - .* only matches an exact, identical identifier. A near-miss name is simply left unconnected or defaulted, with no warning that points directly at the typo.

Mistake #3: Expecting .* to adapt port widths

An explicit named connection can slice a wider bus down to size, such as .dataout(dataout[7:0]). .name and .* cannot - they require the local signal's type to already be equivalent to the port's. A width mismatch simply fails to connect implicitly; it has to be connected explicitly instead.

Practice Exercise

Challenge: Spot the Unconnected Port

Given a module fifo with ports clk, rst_n, wr_en, full, and an instantiating scope that declares local signals clk, rst_n, wr_en but not full, what does fifo fifo_inst (.*); do with the full port, if full is an output with no default value?

Hint

Outputs don't take default values the way inputs do - think about what "no matching signal, no default value" means for .* from the comparison table above.

Solution

full is left unconnected, exactly as if you had written .full() explicitly. No elaboration error occurs, because .* never errors on an unmatched port - it either uses a default value or leaves the port unconnected. The design still elaborates, but any logic that was supposed to read full from this instance now reads a floating signal, which is worth a lint check.

Summary

In this tutorial, you learned:

  • .name is shorthand for .name(name) - it requires an exact, equivalent-type signal match or it is an error
  • .* connects every remaining port by the same name-matching rule, but falls back to a default value or leaves the port unconnected instead of erroring
  • .* can appear only once per instantiation, and can be mixed with explicit connections to call out exceptions
  • Neither shortcut can adapt bus widths or dissimilar names - that still requires an explicit named connection
  • .* is especially common in verification, where testbench signals are deliberately named to match the DUT
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5