NVC - VHDL Compiler and Simulator

NVC(1) General Commands Manual NVC(1)

VHDL Compiler and Simulator

nvc -a file ...

nvc -e unit

nvc -r unit

nvc is an implementation of the VHDL language as defined by IEEE standard 1076-1993 and later revisions.

Simulating a design typically involves three steps: analysing one or more source files into the work library; elaborating a top-level design unit; and finally running the elaborated design.

nvc accepts three kinds of options: global options; commands; and options specific to the command. Global options must be placed before the command and specific options must be placed after the command.

file ...
Analyse one or more files into the work library. Reads from standard input if file is ‘-’.
Elaborate a previously analysed top level design unit.
Execute a previously elaborated top level design unit.
Print out a pseudo-VHDL representation of an analysed unit. This is usually only useful for debugging the compiler.
Initialise the working library directory. This is not normally necessary as the library will be automatically created when using other commands such as -a.
Execute scripts to compile common verification frameworks and FPGA vendor libraries.
Print all analysed and elaborated units in the work library.
unit ...
Generate a makefile for already analysed units.
file ...
Check input files for syntax errors only.

Commands can be chained together. For example to analyse a file foo.vhd and then elaborate and run a top-level entity bar:

$ nvc -a foo.vhd -e bar -r

Note that the unit argument for the -r run command is taken from the earlier -e elaborate command.

, --help
Display usage summary.
Set the maximum size in bytes of the simulation heap. This area of memory is used for temporary allocations during process execution and dynamic allocations by the VHDL ‘new’ operator. The size parameter takes an optional k, m, or g suffix to indicate kilobytes, megabytes, and gigabytes respectively. The default size is 16 megabytes.
Do not check the timestamps of source files when the corresponding design unit is loaded from a library.
Add path to the list of directories to search for libraries. See the LIBRARIES section below for details.
Set the maximum amount of memory in bytes used for the internal representations of design units. The default is 16 megabytes but this may be insufficient when elaborating a large design. The size parameter takes an optional k, m, or g suffix to indicate kilobytes, megabytes, and gigabytes respectively. For example -M64m for 64 megabytes.
Specify exactly the location of the logical library name. Libraries mapped in this way will not use the normal search path.
Select the format used for printing error and informational messages. The default full message format is designed for readability whereas the compact messages can be easily parsed by tools.
Select the VHDL standard revision to use. VHDL standard revisions are commonly referred to by the year they were published. For example IEEE 1076-1993 is known as VHDL-93. Specify either the full year such as 1993 or just the last two digits such as 93. The accepted revisions are 1993, 2000, 2002, and 2008. Note there is very limited supported for any features beyond those in VHDL-2002. VHDL-87 is not supported.
Print error messages with the given severity or higher to ‘stderr’ instead of ‘stdout’. The default is to print all messages to ‘stderr’. Valid levels are note, warning, error, and failure.
, --version
Display version and copyright information.
=name, --work=name:path
Use name as the work library. The second variant explicitly specifies the location of the library. See the LIBRARIES section below for details.

Allow compilation of the ‘STANDARD’ package. Not useful in any other circumstances.
Stop after reporting num errors. The default is 20. Zero allows unlimited errors.
Disable certain pedantic LRM conformance checks or rules that were relaxed by later standards. See the RELAXED RULES section below for details.

Enable code coverage reporting (see the CODE COVERAGE section below).
Write generated LLVM IR to the work library directory before and after optimisation.
Print generated intermediate code. This is only useful for debugging the compiler.
Override top-level generic name with value. Integers, enumeration literals, and string literals are supported. For example -gI=5, -gINIT='1', and -gSTR="hello".
Do not save the elaborated design and other generated files to the working library. This is only really useful in combination with the -r option. For example:
$ nvc -e --no-save tb -r
, -01, -02, -O3
Set LLVM optimisation level. Default is -O2.
, --verbose
Prints resource usage information after each elaboration step.

Include memories and nested arrays in the waveform data. This is disabled by default as it can have significant performance, memory, and disk space overhead.
Terminate the simulation after an assertion failures of severity greater than or equal to level. Valid levels are note, warning, error, and failure. The default is error.
Generate waveform data in format fmt. Currently supported formats are: fst and vcd. The FST format is native to gtkwave(1). FST is preferred over VCD due its smaller size and better performance. VCD is a very widely used format but has limited ability to represent VHDL types and the performance is poor: select this only if you must use the output with a tool that does not support FST. The default format is FST if this option is not provided. Note that GtkWave 3.3.79 or later is required to view the FST output.
Enable or disable warning messages from the standard IEEE packages. The default is warnings enabled.
glob, --exclude=glob
Signals that match glob are included in or excluded from the waveform dump. See section SELECTING SIGNALS for details on how to select particular signals. These options can be given multiple times.
Loads a VHPI plugin from the shared library plugin. See section VHPI for details on the VHPI implementation.
Print various internal statistics about the simulation at the end of the run. This is mostly useful for tuning the runtime itself.
Print a summary of the time taken and memory used at the end of the run.
Stop after N delta cycles. This can be used to detect zero-time loops in your model. The default is 1000 if not specified. Setting this to zero disables the delta cycle limit.
Stop the simulation after the given time has elapsed. Format of T is an integer followed by a time unit in lower case. For example 5ns or 20ms.
Trace simulation events. This is usually only useful for debugging the simulator.
Trace VHPI calls and events. This can be useful for debugging VHPI plugins.
, --wave=file
Write waveform data to file. The file name is optional and if not specified will default to the name of the top-level unit with the appropriate extension for the waveform format. The waveform format can be specified with the --format option. By default all signals in the design will be dumped: see the SELECTING SIGNALS section below for how to control this.

Generate rules that only contain dependencies without actions. These can be useful for inclusion in a hand written makefile.
The generated makefile will work with any POSIX compliant make. Otherwise the output may use extensions specific to GNU make.

Compile libraries into directory dir instead of the default ‘$HOME/.nvc/lib’.

A library is a directory containing analysed design units and other files generated by nvc. The default library is called "work" and is placed in a directory also called work. Note that VHDL also has a concept of the "work library" where the current library can be referred to by the alias work. This confusing behaviour is an unfortunate hangover from the proprietary tools the author used prior to writing nvc.

The name and physical location of the work library is controlled by the --work global option. In the simple case of --work=name the library name is ‘name’ and the physical location is a directory name relative to the current working directory. The physical location can be specified explicitly using --work=name:path where path is the directory name.

The following examples should make this behaviour clear:

$ nvc --work=mylib ...

The work library is named ‘mylib’ and is mapped to a directory with the same name in the current working directory.

$ nvc --work=mylib:somedir ...

The work library is named ‘mylib’ and is mapped to a directory somedir in the current working directory.

$ nvc --work=mylib:/foo/bar ...

The work library is named ‘mylib’ and is mapped to the absolute path /foo/bar.

Concurrent access to a single library by multiple processes is completely safe and protected by a lock in the filesystem using flock(2) that allows multiple concurrent readers but only a single writer.

The --relaxed analysis flag enables “relaxed rules” mode which downgrades the following errors to warnings:
  • Impure function called from pure function.
  • File object declared in pure function.
  • Default expression in object interface declaration is not globally static.
  • Shared variable is not of protected type in VHDL-2000 or later.

Additionally the following languages features from VHDL-2008 and later are enabled in earlier standards:

  • Any visible explicitly declared operator always hides an implicit operator regardless of the region in which it is declared. This is required to analyse code that uses the non-standard Synopsys std_logic_arith package.
  • References to generics and array slices are allowed in locally static expressions using the VHDL-2008 rules.
  • Range bounds with ‘universal_integer’ type are not required to be numeric literals or attributes. This option allows ranges such as ‘-1 to 1’ in VHDL-1993 which otherwise must be written ‘integer'(-1) to 1’.

Every signal object in an elaborated design has a unique hierarchical path name. In VHDL this can be accessed using the ‘PATH_NAME’ attribute.

A signal can be referred to using its full path name, for example ‘:top:sub:x’, and ‘:top:other:x’ are two different signals named ‘x’ in the design. The character ‘:’ is a hierarchy separator. The special character ‘*’ is a wildcard that matches zero or more characters and may be used refer to a group of signals. For example ‘:top:*:x’, ‘*:x’, and ‘:top:sub:*’, all select both of the previous signals.

Path names and globs can be used to exclude or explicitly include signals in a waveform dump. For simple cases this can be done using the --include and --exclude arguments. For example --exclude=:top:sub:*” will exclude all matching signals from the waveform dump. Multiple inclusion and exclusion patterns can be provided.

Specifying large numbers of patterns on the command line quickly becomes cumbersome. Instead inclusion and exclusion patterns can be read from a text file. If the top-level unit name is ‘top’ then inclusion patterns should be placed in a file called top.include and exclusion patterns in a file called top.exclude. These files should be in the working directory where the ‘nvc -r’ command is executed. The format is one glob per line, with comments preceded by a ‘#’ character.

When both inclusion and exclusion patterns are present, exclusions have precedence over inclusions. If no inclusion patterns are present then all signals are implicitly included.

nvc supports a subset of VHPI allowing access to signal values and events at runtime. The standard VHPI header file <vhpi_user.h> will be placed in the system include directory as part of the installation process. VHPI plugins should be compiled as shared libraries; for example:
$ cc -shared -fPIC my_plugin.c -o my_plugin.so
$ nvc -r --load my_plugin.so my_tb

The plugin should define a global vhpi_startup_routines which is a NULL-terminated list of functions to call when the plugin is loaded:

void (*vhpi_startup_routines[])() = {

Functions defined in VHPI plugin libraries may be called from VHDL using the VHPIDIRECT protocol. The VHDL function should be declared with the ‘FOREIGN’ attribute giving the name of the function symbol exported from the plugin. For example:

function my_func (x : integer; y : bit_vector; z : std_logic) return integer;
attribute foreign of my_func : function is "VHPIDIRECT my_func";

Where ‘my_func’ is a global function defined in the plugin library as follows.

int32_t my_func(int32_t x, const uint8_t *y, uint8_t z) { ... }

Foreign procedures may be defined similarly:

function my_proc (x : out integer; y : out bit_vector; z : std_logic);
attribute foreign of my_proc : function is "VHPIDIRECT my_proc";

void my_proc(int32_t *x, uint8_t *y, uint8_t z) { ... }

Note that scalar ‘out’ parameters are passed by pointer.

There is a simple mapping between VHDL and C types.

The smallest C integer type that holds the full range of the VHDL type.
C double regardless of the range of the VHDL type.
Enumerated types
The smallest unsigned integer type that holds the full range of the VHDL type.
Constrained arrays
Pointer to the element type.
Unconstrained arrays
Pointer to the element type. Note that the length and bounds are not available and must be passed explicitly as separate arguments.
Not yet supported.

Here are several examples for common types:

VHDL type C type
uint8_t *
uint8_t *

Foreign functions should not modify arrays passed as ‘in’ arguments, although this is not enforced. Additionally foreign subprograms should not retain any pointers passed as arguments after the subprogram returns.

Controls whether nvc uses ANSI colour escape sequences to print diagnostic messages. The possible values are never, always, and auto which enables colour if stdout is connected to a terminal. The default is auto.

gtkwave(1), ghdl(1)

Written by Nick Gasson ⟨nick@nickg.me.uk⟩

Report bugs to nick@nickg.me.uk or using the GitHub issue tracker at https://github.com/nickg/nvc/issues. Please include enough information to reproduce the problem, ideally with a small VHDL test case.
August 11, 2022 Debian