Menu

Verilog Module Ports: Input, Output, and Inout Explained

How to declare module ports - input, output, and inout - the ANSI-style port list, and when an output should be wire vs reg.

This page includes runnable editors - edit, run, and see output instantly.

A Module's Interface

A module's port list is its interface - everything the outside world can see and touch. Inside the module, the body computes outputs from inputs. The ports are the wires that cross the boundary.

The modern ANSI-style declaration puts direction, type, and width inline:

module my_module(
    input  wire        clk,
    input  wire        reset,
    input  wire [7:0]  data_in,
    output reg  [7:0]  data_out,
    output wire        valid
);
    // body
endmodule

That's the entire shape. Each port has:

  • A direction: input, output, or inout.
  • A type: wire or reg (or logic in SystemVerilog).
  • A width: a range like [7:0], or single-bit if omitted.
  • A name: the identifier you'll use inside the body.

The Three Directions

input - Driven From Outside

Inputs are always wire. The driver is outside the module - either a higher-level module's signal or the testbench's reg. Inside this module, you can read inputs in expressions but never assign to them:

module reader(input wire [7:0] data);
    initial $display("data = %h", data);
endmodule

Writing data = ... inside the module would be an error.

output - Driven From Inside

Outputs are driven by something inside this module. They can be either wire (driven by assign or a sub-module output) or reg (driven from always/initial).

module driver(
    input  wire       a,
    input  wire       b,
    input  wire       clk,
    output wire       y,    // wire - driven by assign
    output reg        q     // reg  - driven by always
);
    assign y = a & b;       // OK because y is wire

    always @(posedge clk)
        q <= a;             // OK because q is reg
endmodule

Trying to assign to y inside the always block, or to drive q with an assign, would be a compile error. The keyword choice has to match the driver.

inout - Bidirectional

inout ports are wires that can be driven by either the module or whatever's connected outside, alternately. They show up at chip boundaries - I²C SDA lines, bidirectional GPIO pins, shared data buses. Inside the module, you control direction with a tri-state pattern:

module bidir_pin(
    input  wire data_out,
    input  wire output_enable,
    output wire data_in,
    inout  wire pin
);
    assign pin     = output_enable ? data_out : 1'bz;
    assign data_in = pin;
endmodule

pin is the shared wire. When output_enable is high, the module drives pin to data_out. When low, it releases the pin to high-impedance (z), letting an external driver use it. data_in always observes whatever's currently on the pin.

inout is rare in pure-internal logic. If you're building a memory controller, a CPU core, an image-processing pipeline, you might never need it. It's a feature for the I/O ring at chip boundaries.

Single-Bit vs Multi-Bit Ports

The width range is optional - omit it for a single-bit port:

input wire valid,         // 1 bit
input wire [7:0] data,    // 8 bits
input wire [31:0] addr,   // 32 bits

You can use parameters for widths, which is how parameterized modules work:

module bus #(
    parameter WIDTH = 32
)(
    input  wire [WIDTH-1:0] in,
    output wire [WIDTH-1:0] out
);
    assign out = in;
endmodule

ANSI vs Verilog-1995 Styles

You'll occasionally see older code that splits the port list and the declarations:

// Old Verilog-1995 style - DON'T do this in new code
module foo(clk, data_in, data_out);
    input clk;
    input [7:0] data_in;
    output reg [7:0] data_out;
    // ...
endmodule

The port list contains only names; each port is then declared separately inside the body. It's verbose, prone to "named in port list but never declared" errors, and twice as much typing. The ANSI-2001 style (the one shown throughout these docs) replaces both:

module foo(
    input  wire       clk,
    input  wire [7:0] data_in,
    output reg  [7:0] data_out
);
    // ...
endmodule

Use the ANSI style. Tools have supported it since 2001.

A Complete Example

That single module has:

  • A parameter (WIDTH).
  • Inputs for clock, reset, load enable, data, and shift enable.
  • A reg output (data_out) driven inside an always block.
  • A wire output (msb_out) driven by a continuous assignment.
  • A complete ANSI-style declaration that lists each port's direction, type, and width inline.

That's how almost every synthesizable module you'll write is shaped.

What Comes Next

The next doc - Module Instantiation - shows how to take this module and use it inside a larger design. The shifter you just wrote isn't useful on its own; it earns its keep when something instantiates it and wires it into a system.

Frequently Asked Questions

What are the three port directions in Verilog?

input, output, and inout. input is driven from outside the module. output is driven from inside the module. inout is bidirectional - useful for tri-state buses where the module both drives and reads the same pin. The vast majority of internal ports are input or output; inout only appears at chip pins.

What is the difference between output wire and output reg?

output wire means the output is driven by a continuous assignment or by a sub-module output - everything outside an always block. output reg means it's driven from a procedural block (initial or always). The keyword controls whether you can target the signal from inside an always, not whether the synthesized hardware contains a flip-flop.

What is the ANSI style for Verilog ports?

ANSI style declares each port's direction and type inline in the port list: module foo(input wire [7:0] data, output reg [7:0] result);. The older Verilog-1995 style only listed names in the port list and re-declared them inside the module. Always use the ANSI style in new code - it's shorter, less error-prone, and standard everywhere modern.

Can you have multiple inputs and outputs in a Verilog module?

Yes - a module can have as many ports as you need, in any combination of directions. Separate them with commas in the port list. The order in the port list determines positional connection order at instantiation, but you almost always use named connections (.port(signal)) to avoid relying on order.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED