VHDL · Chapter 13.7 · Advanced Data Structures
Access Types and Dynamic Memory
Access types are VHDL's version of pointers, the most software-like construct in the language. A pointer type refers to an object on the heap; new allocates an object and returns a pointer to it, deallocate frees that object, and null marks an empty pointer. With these pieces you build genuinely dynamic structures whose size is not fixed in advance, such as linked lists, unbounded queues, growing scoreboards, and dynamic transaction objects. Two firm rules follow. Only variables, never signals, can hold access values, and because real hardware is finite and static, access types are strictly simulation-only. They are a verification tool, often hidden inside a protected type as the queue behind a scoreboard. This lesson shows how to allocate and free objects, build a dynamic structure, and avoid memory leaks.
Foundation14 min readVHDLAccess TypesPointersDynamic MemoryTestbenchSimulation
1. Engineering intuition — pointers for unbounded testbench data
Hardware is fixed: a register file has N entries, a FIFO has a set depth — every storage element exists before the
circuit runs. But a testbench often needs unbounded, dynamic data: a queue of expected transactions whose
length you do not know, a log that grows, a list you splice. That needs allocation at run time — exactly what an
access type (a pointer) provides. new creates an object on demand, a pointer refers to it, deallocate
reclaims it. This is ordinary software data-structure thinking, and it is fine in simulation. It has no place in
RTL precisely because you cannot allocate a gate at run time — which is why access types are a verification tool,
not a hardware one.
2. Formal explanation — new, deallocate, null
-- An ACCESS type is a pointer to a heap-allocated object.
type node_t; -- incomplete type (for self-reference)
type node_ptr is access node_t; -- the pointer type
type node_t is record
data : std_logic_vector(31 downto 0);
next_node : node_ptr; -- points to the next node → linked list
end record;
-- Only VARIABLES can hold access values (never signals).
variable head : node_ptr := null; -- 'null' = empty pointer
-- new ALLOCATES; deallocate FREES.
procedure push (variable lst : inout node_ptr; d : std_logic_vector) is
variable n : node_ptr;
begin
n := new node_t; -- allocate a node on the heap
n.data := d; n.next_node := lst; lst := n; -- link it in
end;
-- ... later: deallocate(n); -- free it to avoid a leakAn access type (access T) is a pointer; new allocates a T and returns a pointer, deallocate
frees it, and null is the empty value. Only variables may be of an access type. Self-referential records
(via an incomplete type) build linked lists, queues, and trees of dynamic size.
3. Production usage — a dynamic queue behind a scoreboard
-- Access types usually live INSIDE a protected type (13.6): the queue behind a scoreboard.
type scoreboard_t is protected body
variable head, tail : node_ptr := null; -- dynamic, unbounded queue of expected items
procedure push (expected : in std_logic_vector) is
variable n : node_ptr := new node_t; -- allocate per transaction
begin
n.data := expected; n.next_node := null;
if tail = null then head := n; else tail.next_node := n; end if;
tail := n;
end;
procedure check (actual : in std_logic_vector) is
variable old : node_ptr := head;
begin
-- compare actual to head.data ... then unlink and FREE the consumed node:
head := head.next_node; deallocate(old); -- reclaim memory → no leak
end;
end protected body;What hardware does this become? None — access types are simulation-only. There is no synthesizable
meaning to "allocate a node at run time," so they live entirely in testbenches and behavioral models. Their job is
to give verification code the dynamic, unbounded structures it needs — here a queue that grows and shrinks with
the stream of transactions, wrapped safely inside a protected scoreboard. Each new is matched by a deallocate
when the node is consumed, so the queue does not leak over a long simulation.
4. Structural interpretation — pointers to allocated nodes
5. Why this is structural, not timing
Access types are a dynamic-memory construct — pointers, allocation, linked structures — that exists only in simulation, so the structure diagram above is the right picture, not a waveform. They have no hardware behavior and thus no RTL timing; what matters is the shape of the data structure (a growing queue, a list) and the allocate/free discipline that keeps it correct. This is purely a verification-code property — how dynamic data is organized and managed — not a signal trace, which is why it closes the data-structures module rather than the hardware ones.
6. Debugging example — the leak (and the access signal)
Expected: a long simulation runs with stable memory. Observed: the simulator's memory grows without bound
over time (eventually slowing or crashing), or a compile error that a signal cannot be an access type. Root
cause: allocated nodes were never deallocated when consumed, so the heap grew forever (a memory leak); or
the design tried to make a signal an access type, which is illegal — only variables can hold pointers. Fix:
match every new with a deallocate when a node is removed (free consumed queue entries), and hold access values
only in variables (typically inside a protected type). Engineering takeaway: access types need disciplined
allocate/free to avoid leaks over long sims, and they live only in variables — a signal can never be an access
type.
-- BUG: consume the head but never free it → leak grows every transaction.
-- head := head.next_node; -- old node orphaned, not reclaimed
-- FIX: deallocate the consumed node.
old := head; head := head.next_node; deallocate(old); -- reclaim memory7. Common mistakes & what to watch for
- Forgetting
deallocate. Everynewneeds a matching free when the object is done, or the sim leaks memory over time. - Access types on signals. Illegal — only variables can hold pointers; keep them in variables (often inside a protected type).
- Trying to synthesize them. Simulation-only; dynamic allocation has no hardware meaning. Use fixed arrays for RTL storage.
- Dangling pointers. Do not use a pointer after
deallocate; set it tonulland check before dereferencing. - Reinventing standard containers. Prefer protected-type wrappers / library queues over hand-rolled pointer code where available (cleaner and safer).
8. Engineering insight & continuity
Access types are VHDL's pointers — new/deallocate/null over heap-allocated, self-referential nodes — for the
dynamic, unbounded structures testbenches need (queues, lists, scoreboards), held only in variables and
strictly simulation-only because hardware is finite and static. Paired with protected types, they build the safe,
growing data structures of a real verification environment. This completes Module 13: Advanced Data Structures —
from arrays of records and memories through protected and access types, you can now model both hardware storage and
testbench data. That naturally leads into the next module, Testbench Development (Module 14), where these
constructs come together to build self-checking, data-driven verification environments.