VHDL · Chapter 1.6 · Foundation
Ports and Port Modes
A port is a wire on the boundary of an entity, the pins of the block and therefore its external contract with the rest of the hardware. Every port carries a mode that fixes the direction data may flow and whether the block may read it, drive it, or both. This lesson covers the four modes, in, out, inout, and buffer, and the read-versus-drive rule each one enforces. You will see which modes you actually use in real RTL and which are legacy, and the single most common beginner trap of reaching for buffer when an ordinary internal signal is the right answer instead.
Foundation13 min readVHDLPortsPort ModesinoutbufferRTL Design
1. Intuition — ports are the pins of the block
In the last lesson the entity was a black box and its ports were the pins on the boundary. A port is exactly that: a named wire that crosses the edge of the block, the only way signals get in or out. Everything a block promises to the outside world is expressed through its ports — they are its contract.
A contract needs a direction. A pin labelled "clock input" must be driven from outside and read inside; a pin labelled "result output" is the opposite. VHDL makes that direction explicit with a port's mode, and then enforces it — try to drive an input from inside the block and the compiler stops you. That enforcement is a feature: it catches a whole class of wiring mistakes before simulation.
2. The four modes
| Mode | Data flow | Inside the block you may… | Real-world use |
|---|---|---|---|
in | outside → block | read it (never drive it) | clocks, resets, data inputs — everywhere |
out | block → outside | drive it (treat as write-only) | results, status, data outputs — everywhere |
inout | both directions | read and drive (resolved type) | tri-state pads, shared buses — occasional |
buffer | block → outside, readable back | drive it and read it back | discouraged — legacy |
Two of these — in and out — are the entire vocabulary of most RTL. inout shows
up at true bidirectional boundaries (a chip's external data bus, an I²C/SDA line).
buffer is a historical wart you should recognise but rarely write.
3. A multi-mode entity
Here is a small block that uses several modes at once — note the comment on each port declaring who drives it:
library ieee;
use ieee.std_logic_1164.all;
entity port_demo is
port (
clk : in std_logic; -- driven outside, read inside
rst_n : in std_logic; -- driven outside, read inside
data_in : in std_logic_vector(7 downto 0); -- driven outside
data_out : out std_logic_vector(7 downto 0); -- driven inside
sda : inout std_logic -- bidirectional (tri-state)
);
end entity port_demo;The entity says nothing about behaviour — only the shape and direction of each
pin. An in port is a value the block is handed; an out port is a value the block
must produce; an inout port is a wire the block sometimes drives and sometimes
releases (to high-impedance 'Z') so something else can drive it.
4. Directionality as hardware
It helps to see the contract. Inputs arrive on one side, outputs leave on the other, and a bidirectional port is the one wire that does both:
5. The read-vs-drive rule, in a waveform
Direction is not bookkeeping — it is what makes a port an input or an output in
hardware. A clean way to feel it: the in ports are stimulus (they come from
outside) and the out ports are response (the block produces them). For a tiny
half-adder — sum <= a xor b; carry <= a and b; — the inputs are driven externally
and both outputs are produced inside and flow out:
half-adder — in ports a, b are stimulus; out ports sum, carry are response
6 cycles6. Why beginners misuse buffer
Here is the trap, almost word for word how it happens. You write a counter whose count is an output, and inside you try to increment it:
port ( q : out std_logic_vector(3 downto 0); ... );
...
q <= q + 1; -- ✗ you are READING q, but q is mode `out`Reading an out port like this is classically illegal (VHDL-2008 relaxed it, but
relying on that hurts portability). The compiler complains, and the beginner "fixes"
it by changing the mode to buffer — which does let you read the output back. It
compiles, so it looks right. It is not: buffer ports carry connection restrictions
that bite the moment you instantiate the block, and most teams ban them in code
review.
The correct fix is an internal signal: drive it inside, then assign the port from
it. The signal is freely readable; the port stays a clean out.
architecture rtl of counter is
signal q_i : unsigned(3 downto 0) := (others => '0'); -- internal, readable
begin
process (clk)
begin
if rising_edge(clk) then
q_i <= q_i + 1; -- read + drive the internal signal freely
end if;
end process;
q <= std_logic_vector(q_i); -- drive the `out` port from it
end architecture rtl;Rule of thumb: if you need to read a value internally, it is an internal signal — an
out port is for driving outward only. That single habit makes buffer unnecessary.
7. Common mistakes & what to watch for
- Driving an
inport from inside. Inputs are read-only inside the block; assign one and the compiler rejects it (correctly). - Reading an
outport. Treatoutas write-only; use an internal signal if you need the value back, rather than switching tobuffer. - Using
bufferto dodge a read error. It compiles but creates connection restrictions and review friction — reach for an internal signal instead. - Reaching for
inoutwhen you do not have a true bidirectional wire.inoutimplies tri-state drive ('Z') and a resolved type; do not use it just to move data both ways at different times — that is two ports.
8. Summary & next step
A port is a wire on the entity boundary and the block's external contract; its
mode fixes direction and the read/drive rule. In practice you live in in and
out; inout is for genuine bidirectional wires; buffer is legacy and almost
always replaced by an internal signal. Getting modes right means the compiler catches
your wiring mistakes for free.
Ports carry typed values — std_logic, std_logic_vector, and the rest — and those
types come from libraries. The next lesson covers libraries and the use clause:
where std_logic actually comes from, what library and use do, and how ieee and
work give your design its types, arithmetic, and shared definitions.