Skip to content

VHDL · Chapter 13.6 · Advanced Data Structures

Protected Types

A protected type is VHDL's closest thing to a class. It bundles private internal state with public methods, procedures and functions, and crucially guarantees mutually-exclusive access when the object is a shared variable touched by several processes. That makes it the safe foundation for testbench infrastructure such as scoreboards, coverage counters, transaction logs, configuration objects, and shared reference models, all of which need many processes to update common state without races. A protected type splits into a declaration, the public method signatures, and a protected body, the private state and method implementations. It replaces the old, unsafe plain shared variable, and because it models behavior rather than hardware it is simulation-only. This lesson covers declaring protected types, their method interface, and where they belong in a verification environment.

Foundation14 min readVHDLProtected TypesTestbenchScoreboardShared VariableSimulation

1. Engineering intuition — safe shared state for a testbench

A testbench is full of state that many processes must update: a scoreboard several monitors write to, a coverage counter incremented from everywhere, a log appended by multiple drivers. If that state is a plain shared variable, concurrent updates race and corrupt it. A protected type fixes this the way object-orientation does: hide the state, expose methods, and let the simulator serialize calls so only one executes at a time. You stop thinking "shared variable" and start thinking "an object with operations" — scoreboard.add(txn), cov.sample(addr), log.write(msg) — each call atomic, the internal state private and consistent. It is encapsulation and safety for the verification environment.

2. Formal explanation — declaration and protected body

protected_type.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- DECLARATION: the public interface (method signatures), like a class header.
type scoreboard_t is protected
  procedure push (expected : in std_logic_vector);     -- public methods
  procedure check (actual   : in std_logic_vector);
  impure function count return natural;
end protected;
 
-- PROTECTED BODY: the PRIVATE state + method implementations.
type scoreboard_t is protected body
  variable q     : queue_t;          -- private internal state (hidden from users)
  variable errs  : natural := 0;
  procedure push (expected : in std_logic_vector) is begin /* enqueue */ end;
  procedure check (actual : in std_logic_vector) is begin
    /* compare against head of q; if mismatch, errs := errs + 1; */
  end;
  impure function count return natural is begin return errs; end;
end protected body;
 
-- Used as a SHARED VARIABLE; calls are mutually exclusive (one at a time).
shared variable sb : scoreboard_t;
--   sb.push(exp);   sb.check(act);   n := sb.count;

A protected type has two parts: the declaration (public method signatures) and the protected body (private variable state plus method bodies). Accessed through a shared variable, its method calls are mutually exclusive, so concurrent processes cannot interleave and corrupt the state. State is reachable only through the methods.

3. Production usage — a coverage counter shared across monitors

shared_coverage.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- One protected coverage object, updated safely from several monitor processes.
shared variable cov : coverage_t;
 
mon_a : process begin
  -- ... observe a transaction ...
  cov.sample(addr_a);            -- atomic update — no race with mon_b
  wait on clk;
end process;
 
mon_b : process begin
  -- ... observe another transaction ...
  cov.sample(addr_b);            -- safely interleaved by the simulator
  wait on clk;
end process;
 
report "coverage = " & integer'image(cov.percent);   -- read via a method

What hardware does this become? None — protected types are simulation-only. They model software-like shared objects (mutual exclusion, private mutable state, dynamic structures) that have no gate equivalent, so they are confined to testbenches and behavioral models. Their value is making the verification environment correct and modular: cov, sb, and log are self-contained objects with safe, atomic operations, shared across all the processes that need them without manual locking or fragile global variables.

4. Structural interpretation — state encapsulated behind methods

multiple processes calling methods of one protected object that encapsulates private statemethod callmethod callencapsulatesmonitor Acalls sb.push / cov.samplemonitor Bcalls sb.check / cov.sampleprotected objectpublic methods (atomic)private statequeue, counters — hidden12
A protected type encapsulates private state behind public methods, like a class, and serializes access when used as a shared variable. Several testbench processes call the same object's methods (push, check, sample); the simulator guarantees the calls are mutually exclusive, so the hidden internal state stays consistent without races. The state is reachable only through the methods, never directly. This is the safe replacement for plain shared variables and is simulation-only. This is an encapsulation structure for verification, captured by a diagram rather than a waveform.

5. Why this is structural, not timing

A protected type is an encapsulation and access-safety construct for simulation — private state plus atomic methods — so the right picture is the object structure above, not a waveform. It has no hardware behavior at all; its "timing" is just the order in which the simulator serializes method calls, an implementation guarantee rather than a circuit waveform. What matters is the structure: state hidden behind methods, shared safely across processes — a design-time property of the verification code, not a signal trace.

6. Debugging example — the racing plain shared variable

Expected: a scoreboard/counter updated correctly from several processes. Observed: lost updates, inconsistent counts, or order-dependent, non-reproducible testbench results. Root cause: the shared state was a plain shared variable of an ordinary type, updated concurrently by multiple processes with no mutual exclusion — so updates interleaved and corrupted each other (and pre-2000 shared variables had no safety at all). Fix: make the shared state a protected type, exposing only atomic methods (push, sample, check); the simulator then serializes access and the state stays consistent. Engineering takeaway: never share mutable testbench state through a plain shared variable — wrap it in a protected type so every update is atomic and the results are reproducible.

protect_shared_state.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- BUG: plain shared variable updated by many processes → races, lost updates.
-- shared variable errors : natural := 0;   errors := errors + 1;  -- not atomic
-- FIX: a protected type; updates go through atomic methods.
shared variable sb : scoreboard_t;   sb.check(act);   -- mutually exclusive

7. Common mistakes & what to watch for

  • Plain shared variables for cross-process state. They race; use a protected type so access is mutually exclusive.
  • Trying to synthesize a protected type. Simulation-only; keep them in testbenches/models, never in RTL.
  • Exposing state instead of methods. Access internal state only through methods; that is what makes updates atomic and the object reusable.
  • Forgetting the body. A protected type needs both the declaration and the protected body; the body holds the real state and implementations.
  • Assuming method calls are parallel. They are serialized (mutually exclusive); design methods to be short and self-contained.

8. Engineering insight & continuity

A protected type is VHDL's class-like construct for verification: private state behind public, atomic methods, shared safely across processes — the correct, race-free way to build scoreboards, coverage, logs, and config objects, and the proper replacement for plain shared variables (and, like them, simulation-only). It brings encapsulation and safety to the testbench. Protected types often manage dynamic internal structures (queues, growing logs), which rest on the last data construct in this module — Access Types and Dynamic Memory — VHDL's pointers and allocation, the subject of the next lesson.