Skip to content

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

access_types.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- 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 leak

An 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

dynamic_queue.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- 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

access pointer to a linked list of dynamically allocated nodes ending in nullnewnextnexthead : node_ptrpointer variablenode{data, next}node{data, next}nullend of list12
An access type is a pointer to heap-allocated nodes, building dynamic structures of unbounded size. A head pointer refers to the first node; each node holds data and a pointer to the next, forming a linked list (here a queue). new allocates a node and returns a pointer; deallocate frees a consumed node; null terminates the list. Only variables can hold access values, and the whole mechanism is simulation-only because hardware cannot allocate at run time. These dynamic structures usually live inside a protected type. This is a dynamic-memory structure, captured by a diagram rather than a waveform.

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.

free_what_you_allocate.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- 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 memory

7. Common mistakes & what to watch for

  • Forgetting deallocate. Every new needs 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 to null and 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.