How to use shared variables in VHDL

Andrzej Wojciechowski

Since VHDL93, shared variables are available for developers to use in their designs. Since then the language evolved and introduced new options and. Currently it’s not immediately obvious how to correctly use a shared variable. This may lead to warnings during simulation, which can be fixed quite easily.

Use cases

Shared variables can be used used for various purposes. In general, if there is a need to modify a value from multiple places (processes, procedures, etc.), it’s good to consider a shared variable. This powerful feature also makes shared variables potentially dangerous and error-prone.

In the simplest case, we can declare a shared variable just like that:

shared variable name_of_variable : type_of_variable;

It might be good enough for various projects, but in general it may result at least with simulator warnings, and at the worst, with difficult to debug error.

In practice I can divide the uses of shared variables into 2 groups: entity and package usage.

Shared variables in an entity

One potential use case of a shared variable is inside of an entity. I’ve presented a practical example in one of my previous posts about memory models.

In this case the simple shared variable declaration may be sufficient. All signals and variables are generally (or I should rather say: should be) accessible only from within the entity (and maybe a testbench).

However, it can also be beneficial to use shared variable in a protected type (more on that below). As this makes it less error susceptible to errors and prevents warnings from EDA tools.

Shared variables in a package

A different use case is when it’s used inside of a package. In my experience, it’s more common to use a shared variable in a package – primarily for verification purposes.

And this is the exact reason why in my opinion, the shared variables should be used in a package only as a protected type. A verification package can (and often is) used in multiple files, by multiple processes and procedures during testing. It is a significant risk to not protect the shared variable. But how can we make it protected?

In short, this is how to create a protected shared variable:

type name_of_type is protected
   procedure name_of_procedure;
   impure function name_of_function return return_type;
end protected name_of_type;

type name_of_type is protected body
   variable name_of_variable : type_of_variable;

   procedure name_of_procedure is
   begin
      ...
   end of procedure name_of_procedure;

   impure function name_of_function return return_type is
   begin
      ...
   end of impure function name_of_function;
end protected name_of_type;

shared variable name_of_variable : name_of_type;

In essence we’ve encapsulated the variable. Now it can’t be accessed directly. In order to set or get the value (or perform other operations), we need to use a dedicated functions or procedures. And we also need to create those function ad procedures. But all this additional work we invested in the encapsulation, can pay off with less warnings and (what’s more important) less error-prone code.

You may also wonder what it’s the impure function in the code snippet above? Essentially an impure function is simply a function which can use an object (a variable or a signal) that’s not on it’s parameters list, nor it declared inside the function body. Such as a declared variable name_of_variable.