If you are writing universal, portable VHDL code, be it a design that targets multiple chip families, or a library that should work with multiple simulators, you may have noticed that it can be quite challenging to make things work everywhere. Different vendors provide their own macros and IPs, simulators provide their own utility libraries, tools support different VHDL attributes. Your code may be treated differently or even fail to compile if it uses language features that are not supported by one of the tools.
In all these cases, to get similar behavior it’s possible to find a proper approach for each tool, but it requires using different vendors or tool-specific VHDL code. So portability is basically a question of how to organize these specific code pieces.
Prior to VHDL 2019, you could use VHDL language features such as generics, generate statements, or configurations. But besides the fact that these options work only in limited contexts, using them also requires all code to be compilable with all target tools.
In order to solve compilation problems, one may try to split their design in such a way as to have parts that differ in separate design units and files, and maintain compilation lists for each configuration. This is certainly an option, but a pretty tedious one.
If it’s only about having simulation specific code,
pragma translate_off/on comments may do the trick, however, they are of no help in other cases.
Nevertheless, such comments may be considered a primitive version of full-fledged preprocessors that are available in some other languages. Going further in this direction, you may find some external preprocessor implementations for VHDL that bring greater levels of customizability but also create a hassle when trying to integrate them into your workflow.
Now, VHDL 2019 has added a preprocessor (Conditional Analysis) to the language standard. Unfortunately, the EDA market is not as fast as one would like when it comes to adopting the latest standards. However, there is good news: some simulators and synthesis tools already support Conditional Analysis. And now there is an IDE that does it too.
Let’s look at what Conditional Analysis is and how it can help solve the aforementioned problems.
VHDL Conditional Analysis represents a simplified subset of preprocessing directives found in the SystemVerilog or C languages. Conditional directives in VHDL do not allow complex preprocessor magic, so metaprogramming is not an option. But, by keeping it simple, the code remains readable and maintainable.
What you can do is enable or disable analysis of portions of the design code based on the current tool, your custom configuration, or even on the VHDL standard used to parse the file. For example, the following snippets are equivalent:
-- pragma translate_off signal valid : std_ulogic := '0'; -- pragma translate_on
`if TOOL_TYPE /= "SYNTHESIS" then signal valid : std_ulogic := '0'; `end if
The second code snippet is an example of a conditional
`if directive: here we check the tool type, and if it’s not a synthesis tool we declare a
Code in disabled
`if directive branches is completely omitted during the compilation process, so it does not have to contain compilable code:
`if DUMMY = "" xor DUMMY = "" then You can use conditional directives for multiline comments, however, that would definitely be an abuse `end
The syntax for the conditional directives is similar to VHDL’s
if statement syntax. However, being tool directives by nature, they can be used anywhere throughout design code, e.g:
`if LIB = "some" then library some; use some.all; `elsif lib = "other" then library other; use other.all; `else library general; use general.all; `end if
Conditions that can be used in conditional directives are limited to comparing conditional identifiers with constant, literal values. You may combine such comparisons in complex expressions:
`if (BOARD = "MAIN" and REV >= "1.2") or DEBUG /= "" then ... `end
It is also possible to write nested conditional directives. If the upper-level directive condition is not satisfied, nested directives will be disabled similar to other VHDL code:
`if TOOL_TYPE = "SIMULATION" then -- Using a simulator `if VHDL_VERSION >= "2008" then -- Newer VHDL version simulation code `else -- Older VHDL version simulation code `end if `else -- Not a simulator `end if
All conditional identifiers have string values. Comparison follows general VHDL rules. Conditional identifiers are not case sensitive while their values are.
And if an identifier is not defined, its value is considered to be an empty string
"" (e.g. in the previous example we are checking whether the
DEBUG identifier is defined).
But let’s move on and discuss conditional identifiers.
There’s a bunch of identifiers that are provided by a tool:
Identifiers starting with
TOOL_ can be used to determine what tool is used to analyze VHDL code. In addition,
VHDL_VERSION stores the VHDL version that was used to analyze the file (possible values are
"2019"). It may look strange that
VHDL_VERSION can have values of standards prior to VHDL 2019, but tools that support VHDL 2019 may actually make Conditional Analysis available when compiling files using previous standards, too (consult your tool documentation to confirm this).
TOOL_TYPE can be either
"FORMAL". As for
TOOL_VERSION, it’s safe to use ordering operators (
>=, …) to check the version, as newer versions should always be greater than previous ones. Other predefined identifier values are effectively at the discretion of the tool vendor. Here are some examples of what these values can be:
TOOL_TYPE = "SIMULATION" TOOL_VENDOR = "Aldec" TOOL_NAME = "Riviera-PRO" TOOL_EDITION = "2020.04 Linux64" TOOL_VERSION = "2020.04.130.7729"
TOOL_TYPE = "SYNTHESIS" TOOL_VENDOR = "INTEL CORPORATION" TOOL_NAME = "QUARTUS" TOOL_EDITION = "PRIME PRO" TOOL_VERSION = "21.3.0"
Apart from using predefined identifiers, you are free to define your own conditional identifiers and specify their values. However, the VHDL standard does not provide the possibility to do this from within VHDL code itself, allowing instead for each tool to implement its own way of providing custom conditional identifiers. You’ll have to check your tool documentation, but it’s basically either adding them as arguments to the compiler invocation, setting them in tool preferences, or supplying a file in a tool-specific format with custom definitions.
Note that because of these differences, it may be harder to organize different sets of conditional identifiers and values for different parts of your design if you are targeting multiple tools.
`if conditional directives there are also
`error directives that can produce compilation warnings or errors.
They can be used to indicate what conditional branch is active or stop compilation if a specific combination of conditional values is not supported:
`if RST_TYPE = "async" then `warning "Reset type is set to async. Beware of asynchronous reset crossings" `elsif RST_TYPE /= "sync" then `error "Unsupported reset type. Please set RST_TYPE to either `sync` or `async`" `end if
The parameter in these directives is a message string literal. You can not concatenate any conditional identifier values.
Predefined conditional identifier values are also available as regular VHDL constants in the
std.env package, making them usable in regular VHDL expressions.
For example, you can check if your simulator supports Conditional Analysis and what values predefined conditional identifiers have, by running the following code:
use std.env.all; entity check is end entity; architecture behavioral of check is `if VHDL_VERSION = "" then constant TOOL_TYPE : string := "n/a"; constant TOOL_VENDOR : string := "n/a"; constant TOOL_NAME : string := "n/a"; constant TOOL_EDITION : string := "n/a"; constant TOOL_VERSION : string := "n/a"; `end if begin status : process begin report "Tool has " & `if VHDL_VERSION = "" then "NO " & `end if "support for VHDL 2019 Conditional Analysis:" & LF & "VHDL VERSION = " & VHDL_VERSION & LF & "TOOL TYPE = " & TOOL_TYPE & LF & "TOOL VENDOR = " & TOOL_VENDOR & LF & "TOOL NAME = " & TOOL_NAME & LF & "TOOL EDITION = " & TOOL_EDITION & LF & "TOOL VERSION = " & TOOL_VERSION; wait; end process; end architecture;
NOTE: In this example, if your simulator supports Conditional Analysis, it will use variables from
std.env package, otherwise it should ignore unsupported directives and use dummy constants. The proper message is constructed in a similar manner.
Both Sigasi Studio and Sigasi’s extension for VS Code support VHDL Conditional Analysis: directives highlighting and formatting, hovers with conditional identifier values, type-time syntax error reporting, conditional warnings, and errors, definitions for your own conditional identifiers, as well as the configuration of default identifiers to mimic your target simulator or synthesis tool:
Check out this video to see Conditional Analysis in action.
- VHDL 2019: Usability and APIs (blog post)
- VHDL 2019: Enhanced generic types (blog post)
- VHDL 2019: Interfaces (blog post)
- What's new in VHDL 2019? (blog post)
- VHDL 2019 Conditional Analysis (screencast)