[ Content | Sidebar ]

Archives for vhdl

New NVC version 1.7

August 7th, 2022

I’ve just released another version of my VHDL simulator. The highlight of this release is greatly improved VHDL-2008 support. It’s now sufficiently complete to run OSVVM, UVVM, and NEORV32.

A special thank you to Brian Padalino and T. Meissner for sponsoring me!

Download: nvc-1.7.0.tar.gz

This release is signed with my PGP key ID 74319F1A: nvc-1.7.0.tar.gz.sig

  • Breaking change: In-tree builds are no longer supported: use a separate build directory instead.
  • Breaking change: The --force-init command is deprecated and has no effect.
  • Added support for VHDL-2008 type generics on packages and entities.
  • Diagnostic messages have been enhanced with more contextual information.
  • Added support for record element constraints and record fields with unconstrained array types.
  • Alias of multidimensional array allowed in VHDL-2008 mode.
  • Implemented VHDL-2008 rules for aggregates with slices.
  • VHPI is now always enabled at build time and the --enable-vhpi configure option has no effect.
  • Arithmetic operations that overflow the underlying machine type now produce an error (#101).
  • Added support for VHDL-2008 force/release assignments.
  • Basic support for external names in VHDL-2008.
  • Matching case case? statements are supported in VHDL-2008 mode.
  • Fixed several bugs in the implementation of guarded signals.
  • Implemented VHDL-2008 rules for generic visibility.
  • Shared variable declaration permitted in entity declaration.
  • Case expression no longer requires a locally static subtype in VHDL-2008 mode (#460).
  • The VHDL heap is now garbage collected as required by VHDL-2019 and the deallocate operator has no effect other than setting the access to null.
  • A new global option -H specifies the size of the simulation heap and defaults to 16 megabytes.
  • Concurrent procedure call allowed in entity statement part.
  • Added support for 'SUBTYPE and 'ELEMENT attributes in VHDL-2008.
  • The new top-level --init command creates a new empty library directory.
  • The -a analysis command now reads from the standard input if the file name is -.
  • Added support for array element constraints in VHDL-2008.
  • The --prefer-explicit analysis option which was deprecated before the 1.0 release has been removed.
  • A new --relaxed analysis option enables “relaxed rules” mode. This has the same effect as enabling all the existing --relax= options. However some constructs will still produce warnings.
  • The --relax= analysis option is deprecated and is now equivalent to passing --relaxed. The individual options are ignored.
  • Added support for generic subprograms in VHDL-2008.
  • New command --install allows easy installation of common third-party packages such as OSVVM and UVVM.
  • Identifiers in waveform dumps are now in lower case instead of upper case.
  • The function CURRENT_DELTA_CYCLE in NVC.SIM_PKG can be used to query the current delta cycle number.

VHDL generic subprograms

June 25th, 2022

I recently added support for VHDL-2008 generic subprograms to NVC. As far as I know it’s the first open source VHDL simulator to support them and allows you to write type-generic functions and procedures like this:

function fact generic (type t;
                       function "*"(l, r : t) return t is <>;
                       function "-"(l, r : t) return t is <>;
                       function "<"(l, r : t) return boolean is <>;
                       one : t)
        (n : t) return t is
begin
    if n < one then
        return one;
    else
        return n * fact(n - one);
    end if;
end function;

And then make concrete instances of the generic function:

function fact_int is new fact
    generic map (t => integer, one => 1);
function fact_real is new fact
    generic map (t => real, one => 1.0);

The is <> syntax in the declaration above picks up the default *, -, and < operators for that type from the context so there’s no need to specify them in the generic map.

assert fact_int(5) = 120;
assert fact_real(4.0) = 24.0;

Filed in vhdl - Comments closed

Major new NVC version 1.6.0

January 25th, 2022

Recently I’ve been doing a lot of work on my VHDL simulator and I’ve just released the first new major version in over a year and a half.

Download: nvc-1.6.0.tar.gz

This release is signed with my PGP key ID 74319F1A: nvc-1.6.0.tar.gz.sig

This release contains many improvements and bug fixes detailed below, but the highlights are much improved language support, especially for configurations and some VHDL-2008 features, as well as better debugging support.

  • Name resolution and overload resolution has been completely rewritten which should fix a number of long-standing issues.
  • The elaboration phase was largely written which fixes a number of long-standing issues and significantly improves elaboration speed.
  • VHDL-2008 IEEE standard libraries are now built and installed in addition to the VHDL-1993 libraries.
  • The VHDL-1993 standard libraries are now derived from the Apache 2.0 licensed sources from VHDL-2019.
  • There is now a project website at https://www.nickg.me.uk/nvc/. Please link to this in preference to the GitHub project page.
  • Compiled VHDL code now includes DWARF debug information which is used for runtime stack trace if libdw or libdwarf is installed.
  • Added support for VHDL-2008 reduction operators, match operators, and condition conversion.
  • Added support for VHDL-2008 element resolution.
  • Variable assignment now supports aggregate targets.
  • The --relax=impure option allows pure functions to call impure functions.
  • Added support for VHDL-2008 “all” sensitised processes.
  • Added support for ports and generics in block statements.
  • Added support for the ’BASE attribute.
  • Type name now allowed in element association choice (#407).
  • Implement textio READ procedure for REAL.
  • LLVM 6.0 or later is now required to build.
  • Added support for MINIMUM, MAXIMUM, and TO_STRING predefined operators in VHDL-2008.
  • VCD files are now generated from FST data in a similar manner to fst2vcd(1). This should improve compatibility with other tools.
  • Added support for ’LAST_ACTIVE attribute (#423).
  • Added support for ’DRIVING and ’DRIVING_VALUE attributes.
  • Added a new option --ieee-warnings=off to disable warning messages from the standard IEEE packages.
  • Support for configurations has been significantly improved (#372).
  • Added support for VHDL-2008 delimited comments.
  • Added support for guard expressions on blocks.
  • Added support for guarded signals.
  • Added support for HREAD, HWRITE, and other TEXTIO additions in VHDL-2008.
  • Code generation now happens in parallel when LLVM is built with multi-threading enabled.
  • Link time optimisation (LTO) is now enabled for release builds where supported.
  • The default assertion failure message for certain simple scalar comparisons now shows the values of the left and right hand sides.
  • Added support for VHDL-2008 conditional variable assignment statements.
  • Added support for VHDL-2008 extended bit string literals.
  • Non-globally-static actuals allowed in port maps in VHDL-2008 mode.
  • Added support for VHDL-2008 sequential conditional signal assignment statements.
  • Added basic support for package generics and package instantiation.
  • Nested arrays can now be included in the waveform dump but only if the --dump-arrays option is passed. This is disabled my default due the significant performance and memory overhead.
  • Added support for record types in waveform dump (#216).
  • Added support for foreign subprograms using the VHPIDIRECT protocol.
  • Library build is now reproducible when running make -j.
  • Fix a constant folding crash with nested records.
  • Fixed a crash when a record aggregate contains an “others” association and the fields have array types with different lengths.
  • Fixed a stack overflow when a subprogram with unconstrained array arguments is called repeatedly in a loop (#414).
  • Fixed intermittent crash when evaluating nested constant records (#425).
  • Fixed missing import libraries on Windows (#424).
  • Standard libraries are now installed under $prefix/lib/nvc/ instead of $prefix/share/nvc.
  • New configure option --disable-vital disables building the VITAL packages whose license status is unclear.
  • Support for the LXT wave output format, which was deprecated in version 1.5, has been removed. Use the default FST format instead.
  • The fetch-ieee.sh script which did nothing since the last release has been removed.
  • The --codegen command, which has been deprecated since 1.3, was removed.
  • The --profile option now prints internal simulation statistics instead of the top processes by CPU time.

Filed in vhdl - Comments closed

New NVC version 1.5.3

November 13th, 2021

I’ve released a new version 1.5.3 of NVC, the VHDL compiler I’m working on. This is hopefully the final bug fix release on the 1.5 branch containing the following changes back-ported from the development branch:

  • Handle access(2) returning EPERM in macOS sandbox.
  • Fix race when multiple processes concurrently update a library.
  • Fix --syntax command when file contains multiple design units.
  • Allow constant folding of nand/nor/xor/xnor.
  • Fix potential out of memory condition when evaluating complex assert expressions.
  • Fix incorrect result of mod operator with negative operands.
  • Fixed intermittent crash when evaluating nested constant records.
  • Buffer too small for printing TIME’HIGH.

nvc-1.5.3.tar.gz
nvc-1.5.3.tar.gz.sig

This release is signed with GPG key fingerprint 0784 505A DB5D 7D86 D2BD E6DA BCDB 295F 7431 9F1A. Download both the .sig and .tar.gz files and verify with:

gpg --keyserver pgp.mit.edu --recv-keys BCDB295F74319F1A
gpg --verify nvc-1.5.3.tar.gz

Filed in vhdl - Comments closed

New NVC version 1.5.2

July 28th, 2021

I’ve released a new version 1.5.2 of the VHDL compiler I’m working on. This is the second bug fix release on the 1.5 branch containing the following changes back-ported from the development branch:

  • Link libexecinfo on FreeBSD.
  • Implement textio READ procedure for BIT and TIME.
  • Fixed a crash when a long running procedure suspends in a loop.
  • Fix static linking with LLVM 12.0.
  • Fix crash when assigning to a signal declared in a package.
  • Fix incorrect recording of dependencies which caused a failure to load generated DLLs on Windows.
  • Fix file locking error when a library is located on NFS.
  • Optimise loading large library index from disk.
  • Fix a crash when using ‘VALUE with enumeration subtypes.
  • Fix a crash when a signal with more than 256 elements is declared in a package.

nvc-1.5.2.tar.gz
nvc-1.5.2.tar.gz.sig

This release is signed with GPG key fingerprint 0784 505A DB5D 7D86 D2BD E6DA BCDB 295F 7431 9F1A. Download both the .sig and .tar.gz files and verify with:

gpg --keyserver pgp.mit.edu --recv-keys BCDB295F74319F1A
gpg --verify nvc-1.5.2.tar.gz

There is now a public git mirror at https://git.nickg.me.uk/nvc.git if you prefer not to use GitHub.

Filed in vhdl - Comments closed

NVC Version 1.5

July 19th, 2020

I’m pleased to announce a new version of nvc, the VHDL simulator I’ve been working on for the best part of a decade now. I haven’t added any major new features recently but I’ve fixed several bugs listed below, and also updated the code generation to be compatible with LLVM 7 and later. Due to a licensing change the IEEE standard library sources can now be redistributed, but note that distributing modifications is not permitted so these are truly free software.

  • IEEE library sources are now distributed
  • Updated FST library to match GtkWave 3.3.79
  • The LXT wave output format is deprecated, use FST instead
  • Fix incorrect file name in assertion message
  • Fix crash while recovering from parse error
  • Add --dump-json command to print AST as JSON (from Sebastien Van Cauwenberghe)
  • Fix crash when using LLVM 7 and later
  • Fix spurious assertion failure in std.textio.readline
  • Reals are now rounded to the nearest integer as specified by the LRM
  • Fix crash when constant folding uses too much memory
  • Improved memory management in evaluator (thanks to Frank Mori Hess)
  • Various other minor fixes and improvements

Download the source package here: nvc-1.5.tar.gz.

Filed in vhdl - Comments closed

VHDL Compiler Updates

May 5th, 2017

It’s been a long time since I wrote anything about the VHDL compiler and simulator I’m working on, nvc, but I am still developing it, albeit at a slightly slower pace than before.

Since the last update I’ve started making stable releases, and yesterday I released version 1.2.1. Actually I think most people are better off using the master branch: one thing I’m really proud of is the coverage of the regression tests, and they run on every commit using Travis so it should always be stable enough for everyday usage. The “stable” versioning is really to satisfy the requirements of packagers. For instance it is now part of Homebrew for OS X.

The big change in the last year has been the removal of the old hacky constant folding pass and replacing it with one based on the “vcode” intermediate code layer that I wrote about before. The old version used to walk over the AST nodes and attempt to collapse them into a simpler form by evaluating expressions and even evaluating function calls. Unfortunately this was often buggy and sometimes didn’t match the run-time result the code generator would have produced. Now constant folding is done using the same AST-to-vcode lowering pass the code generator uses, to generate vcode “thunks” for compile-time constant expression. These are then evaluated using a new vcode interpreter. This now means almost all side-effect free functions with constant arguments will be folded at compile time. Even those with complex looping, recursion, memory allocation, etc.

Unfortunately this rewrite took way longer than expected, partly due to not having much time to work on it, and partly because all the dependencies into the rest of the code got very complex.

Apart from that I’ve fixed a lot of bugs. And this means you can simulate some quite complex designs in it, for instance the J-Core open source SuperH CPU clone.

Filed in vhdl - Comments closed

New Code Generator

April 6th, 2015

I haven’t written much about my VHDL compiler recently and there has been little public GitHub activity. This is because I’ve spent the last six months completely rewriting the way the back-end code generation works and I finally merged the result back into the master branch last week. This rather lengthy post attempts to explain what I’ve done.

There were numerous problems with the old code generator. Before the rewrite cgen.c was a 7000 line behemoth that took VHDL syntax trees at one end and produced a complete LLVM module at the other. It dealt with everything from the minutiae of memory layout and branch prediction hints, to how to implement displays for nested sub-programs, to high level VHDL rules for things like bounds checking.

It was also bordering unmaintainable as it had started life as a small toy project to learn LLVM and slowly accreted features and optimisations as I implemented more and more of VHDL. There were several bugs in it that were just too painful to fix.

It was also very difficult to test: most of the rest of the modules are tested using the check C unit test framework but I couldn’t find any way to do this with the LLVM output. Instead the code generator is tested indirectly by the regression tests that run through the full compiler and simulator: this makes it hard to test for small specific features, like optimisations.

I also had several other plans for VHDL-specific optimisations and unifying the elaboration-time evaluator with the code generator that just weren’t tractable with the current system. For example the old code generator threw away most VHDL type information early on and reduced variables and signals to LLVM’s 8, 16, 32, or 64-bit integers which misses many opportunities to eliminate bounds checks.

So I finally decided to rewrite it using a scheme I’d been toying with for a while. The back end is now based around a new intermediate form called “V-code” after the P-code of Pascal. A new “lowering” phase in lower.c translates VHDL syntax trees into V-code and the code generator in cgen.c translates V-code into LLVM.

The intention of the lowering phase is to translate VHDL into something executable without getting bogged down with implementation concerns. The code generation phase focusses on generating efficient LLVM bitcode without worrying about high-level features.

I wanted V-code to be higher-level than LLVM – so that I could express VHDL features like signals, processes suspending, bounds checking etc. – but lower level than VHDL so I could hide complexities such as array directions, aggregates, non-zero-based indices, and so on.

I think the V-code language I ended up with fits this pretty well. The model is infinite register single static assignment just like LLVM but simplified in many ways – for example there are no phi nodes. V-code is strongly typed having both “normal” types such as integers, pointers, records, and arrays and VHDL-specific types such as signals, accesses, and files.

Unlike many similar intermediate languages where integer types are a certain bit width, in V-code integer registers have two ranges associated with them: a static “type” range and a dynamic “bound” range. For example, consider the following snippet:

subtype small_int is integer range 1 to 10;
 
function get_bit(x : bit_vector(1 to 20);
                 n : small_int) return bit is
    variable i : integer := n;
begin
    return x(i + 1);
end function;

If we compile this with the --dump-vcode argument the compiler will output the V-code:

Name       WORK.PACK.GET_BIT$JQuWORK.PACK.SMALL_INT;
Kind       function
Context    WORK.PACK-body
Blocks     1
Registers  12
Types      9
Variables  1
  I                                     // -2^31..2^31-1 
Result     0..1
Parameters 2
  r0    X                               // @<0..1> => 0..1 
  r1    N                               // -2^31..2^31-1 => 1..10 
Begin
   0: // Elided bounds check for r1 
      I := store r1
      r3 := const 1                     // -2^31..2^31-1 => 1 
      r4 := add r1 + r3                 // -2^31..2^31-1 => 2..11 
      // Elided bounds check for r4 
      r8 := sub r4 - r3                 // -2^31..2^31-1 => 1..10 
      r9 := cast r8                     // # => 1..10 
      r10 := add r0 + r9                // @<0..1> => 0..1 
      r11 := load indirect r10          // 0..1 
      // Elided bounds check for r11 
      return r11

The comments after each register definition give the type range and the bound range after a “=>” if different. The parameter N represented by register r1 has a type range of -2^31..2^31-1 as it’s a subtype of INTEGER but a bound range of 1..10 which means the compiler can assume its value is always within this range.

Here the old code generator would have inserted three bounds checks: one on the array access and two trivial subtype checks on I‘s initial value and the returned value. The new code generator can eliminate all three as indicated by the “Elided bounds check for …” comments.

The interesting case is the array access: the expression I + 1 ends up in register r4. This has type range -2^31..2^31-1 again so it’ll be represented by a 32-bit integer but the bound range is 2..11 which is calculated from the range of the LHS (1..10) and the RHS (1..1). When we compare this against the array range 1..20 we know this check can never fail and so skip it.

If we change the return statement to this:

return x(i + 11);

Then r4 has range 11..20 and it’s possible for this array access to be out-of-bounds so the lower phase inserts a runtime check:

   0: // Elided bounds check for r1 
      I := store r1
      r3 := const 11                    // -2^31..2^31-1 => 11 
      r4 := add r1 + r3                 // -2^31..2^31-1 => 12..21 
      r5 := const 1                     // -2^31..2^31-1 => 1 
      bounds r4 in 1 .. 20
      r9 := sub r4 - r5                 // -2^31..2^31-1 => 11..20 
      r10 := cast r9                    // # => 11..20 
      r11 := add r0 + r10               // @<0..1> => 0..1 
      r12 := load indirect r11          // 0..1 
      // Elided bounds check for r12 
      return r12

As an aside, I spent a lot of time making the debug output from V-code human readable. The output is colour-coded and all register definitions are annotated with their type and bounds. I’ve wasted too much time trying to parse the LLVM bitcode textual format!

At the moment bounds propagation only occurs for certain arithmetic operations and only across a single basic block. However this seems to be effective for a large number of practical examples. Another neat side-effect of this scheme is that V-code has constant folding almost for free: a “constant” is any register with a bound range containing a single value. The emit_* functions which generate op-codes typically short-circuit and return a constant register when all their inputs are constants.

The following example shows how V-code represents VHDL-specific features like signals and process control flow.

architecture rtl of example is
    signal x : bit;
begin
    process is
    begin
        x <= '1';
        wait for 1 ps;
        x <= '0';
        wait for 2 ps;
    end process;
end architecture;

The wait-for statement here causes the process to suspend for the given amount of simulation time and then resume at the following statement. In NVC this is implemented as a jump table at the start of a function generated for a process. A wait statement stores the index of the next block in the process’s global state object and returns from the function. This works well but trying to debug the control flow of the generated LLVM can be very difficult. Instead V-code has “waiting” as a first-class control flow op-code: it looks just like a normal jump and the implementation details are hidden in the code generator.

Name       :example:line_7
Kind       process
Context    WORK.EXAMPLE.elab
Blocks     4
Registers  14
Types      8
Variables  0
Begin
   0: r0 := nets :example:x             // $<0..1> => 0..1 
      r1 := const 1                     // # => 1 
      alloc driver nets r0+r1 driven r0+r1
      return 
   1: r2 := const 0                     // -2^63..2^63-1 => 0 
      r3 := nets :example:x             // $<0..1> => 0..1 
      r4 := const 1                     // 0..1 => 1 
      // Elided bounds check for r4 
      r5 := const 0                     // -2^63..2^63-1 => 0 
      r6 := const 1                     // # => 1 
      sched waveform r3 count r6 values r4 reject r2 after r5
      r7 := const 1000                  // -2^63..2^63-1 => 1000 
      wait 2 for r7
   2: r8 := const 0                     // -2^63..2^63-1 => 0 
      r9 := nets :example:x             // $<0..1> => 0..1 
      r10 := const 0                    // 0..1 => 0 
      // Elided bounds check for r10 
      r11 := const 0                    // -2^63..2^63-1 => 0 
      r12 := const 1                    // # => 1 
      sched waveform r9 count r12 values r10 reject r8 after r11
      r13 := const 2000                 // -2^63..2^63-1 => 2000 
      wait 3 for r13
   3: jump 1

Operations on signals are also first-class: the type annotation $<0..1> means a signal of values in the range 0..1 (i.e. the bit type). The op-codes “alloc driver” and “sched waveform” handle common operations on signals without confusing the logic with implementation details (both of these are implemented as function calls into the runtime kernel).

The new structure allows me to write much more targeted unit tests. For example the new test_lower.c test suite checks the generated op-codes for a large number of small examples. This means regression tests can be written for micro-optimisations and similar tweaks that were impossible to test before.

Overall the performance is slightly better than with the previous code generator: the bigram.vhd micro-benchmark runs about 10% faster now. This test originally spent most of its time in the runtime kernel but after I optimised that it is now dominated by the to_unsigned library function so there’s room for code generation improvements there. However, I want to spend the next month or so fixing the large number of GitHub issues that have accumulated.

Filed in vhdl - Comments closed

Writes Your Makefiles For You

December 31st, 2013

I’ve been dogfooding my VHDL compiler for a project at work and now that it’s gotten to the point where it can simulate non-trivial designs, compile times are becoming significant. Especially when files use Xilinx primitives as the vcomponents library takes a while to load.

At the moment I’m re-analysing every source file for each change I make, so obviously an improvement would be to write a makefile and only re-analyse the files that have changed and those that depend on them (e.g. an architecture must be re-analysed if the corresponding entity changed). But figuring out and maintaining the dependencies by hand is tedious and error prone so I’ve written a makefile generator that recursively finds the dependencies for previously analysed or elaborated units in a library. Invoke it like this:

nvc --make my_top_level >Makefile

Then running make will do the minimum amount of work to rebuild all the out of date files. If the argument to --make is an elaborated design then two convenience targets run and wave will be added to run the simulation and run with waveform output respectively.

The code is fairly compact: only 400 or so lines of C.

This also turned out to be handy for solving a long-standing problem of not being able to bootstrap the standard and IEEE libraries with parallel make (make -j). Previously the dependencies in the automake input file were incomplete, but now these are generated automatically by a gen-deps target. The output (e.g. here) is then mangled with sed and committed into the git repository (this solves a chicken-and-egg problem where the gen-deps target can only be run in an already built tree).

VHDL Code Coverage

November 24th, 2013

I recently added a code coverage option to the VHDL compiler, nvc, I’m working on. I tend to find code coverage a really useful tool when I’m writing RTL, especially the sort of control-dominated designs I do in my day job. I find Modelsim’s HTML coverage reports a bit frustrating so I’m trying to do something more user-friendly in my simulator.

If you elaborate your design with the --cover option the generated code will be annotated to gather the following kinds of coverage:

  • Statement – A counter is added for each executable statement in the design. A statement must be executed at least once to be “covered”.
  • Branch – A branch is covered if it is both taken and not-taken at least once during execution
  • Condition – A condition here is a Boolean sub-expression of branch test and it is covered by evaluating to both TRUE and FALSE at least once. For example if A and B then contains one branch but two sub-conditions.

After a run with coverage enabled the statistics are automatically reported:

** Note: coverage report generated in /tmp/work/WORK.TEST.cover/
            282/289 statements covered
            71/94 branches covered
            85/108 conditions covered

A HTML report is then generated which contains a top-level summary and a detailed report for each source file:

coverage

You can mouse over a non-covered branch or condition to get a hint as to why it was not covered.

The implementation is currently a work-in-progress but functions well enough for light usage. The biggest limitation at the moment is that the report only contains aggregated statistics per-file rather than per-instance statistics.

Filed in vhdl - Comments closed