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, orinout. - A type:
wireorreg(orlogicin 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
regoutput (data_out) driven inside analwaysblock. - A
wireoutput (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.