VHDL supports clean modular structures enabling to easily reuse your code. In this post I will explain how to create a VHDL library for simulation utilizing GHDL [1] in combination with GTKWave [2] and optionally blend these with the advantages of a git submodule [3].

Both GHDL and GTKWave are free (as in freedom) software bundles licenced under the GPLv2 [4]. git is licences under ... well you certainly already know.

Packages and libraries in VHDL

Writing your first line of VHDL code implies accessing a library (provided, that you write your code in a linear fashion). Every digital designer working with VHDL is familiar with statements like the following.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

ieee is the library shipped with the VHDL compiler and simulator, which is GHDL in our case. std_logic_1164 and numeric_std are so called packages. std_logic_1164 provides us with types like std_logic and corresponding values like '1' or 'U'. Whereas numeric_std deals with numerical computation involving e.g. the type unsigned.

Let's dive into it and take a look at the std_logic_1164 package. Depending on the VHDL standard (VHDL is maintained by the IEEE [5]) a different package is used. One implementation can be found in the GHDL repository [1:1] under libraries/ieee/stdlogic1164.vhdl. In this file we find a lot of interesting things to look at. For instance, we find the declarations for the edge detectors, which are typically employed when sequential logic needs to be created.

-------------------------------------------------------------------
-- edge detection
-------------------------------------------------------------------
function rising_edge  (signal s : STD_ULOGIC) return BOOLEAN;
function falling_edge (signal s : STD_ULOGIC) return BOOLEAN;

Packages in VHDL consist of a declaration and a body. Those two parts can be in one or two seperate files (per package). To give an example, in GHDL for the package std_logic_1164 there is the file stdlogic1164.vhdl

package std_logic_1164 is
-- Declarations
end std_logic_1164;

and the file std_logic_1164-body.vhdl in the very same directory:

package body std_logic_1164 is
-- Implementations
end std_logic_1164;

The edge detectors for instance are implemented like this (in this particular VHDL version).

-------------------------------------------------------------------
-- edge detection
-------------------------------------------------------------------
function rising_edge (signal s : STD_ULOGIC) return BOOLEAN is
begin
return (s'event and (To_X01(s) = '1') and
        (To_X01(s'last_value) = '0'));
end rising_edge;

function falling_edge (signal s : STD_ULOGIC) return BOOLEAN is
begin
return (s'event and (To_X01(s) = '0') and
        (To_X01(s'last_value) = '1'));
end falling_edge;

If you want to improve on the structure of your VHDL code your own library (or even libraries) are the best bet you have. The first step is to create a package and define e.g. components inside it. I am not going into the details of VHDL packages too much because others have done that very well before me [6]. Do your research and then you can take the next step.

Adding a library to your project

If this was a cookbook the following section would present a recipe on how to cook the dish I promised in the introduction. First you should have a recent version of GHDL, GTKWave and git installed. As a reference I share my setup with you here.

$ gtkwave --version
GTKWave Analyzer v3.3.98 (w)1999-2019 BSI
...
$ ghdl --version
GHDL 1.0-dev (v0.37.0-819-g9828b513) [Dunoon edition]
...
$ git --version 
git version 2.20.1

If you have not already, then now it is time to fire up your favorite shell.

So, let's assume you want to create some design, that takes my UART implementation as a building block. Assuming, that you use git already to track your design files, we start with creating a submodule.

git submodule add https://git.stiefel.tech/m3x1m0m/max_uart_vhdl.git

Submodules [7] are a great way to improve the live of designer who dislikes reinventing the wheel. In a VHDL file, let's call it top.vhd in this example we want to instatiate a UART transmitter and interface with the rest of our design. In the section above we saw, that we need to tell the compiler about the library and the packages within, which kind of makes the creation of a package necessary in the first place. I made a very simple package uart.vhd. It is part of the submodule, that we just added.

-- uart.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

package uart_pkg is
  component uart_tx is
    port(
          i_clk_baudrate  : in  std_logic;
          i_reset_n       : in  std_logic;
          i_tx_send       : in  std_logic;
          i_tx_data_vec   : in  std_logic_vector(7 downto 0);
          o_tx_pin        : out std_logic;
          o_tx_sent       : out std_logic
        );
  end component;
end package;

Because of the package we now can simply add the UART transmitter to our design adding the following lines of code to the aforementioned top.vhd.

library max_uart;
use max_uart.uart_pkg.all;
-- Alternatively we could write ...
-- use max_uart.uart_pkg.uart_tx;

Now we reached a turning point. The remaining tasks are tool specific and I will show, which commands have to be issued to get it done with GHDL and GTKWave.

In Lattice Diamond adding libraries is also a piece of cake. I might share how I have done it in a later blog post.

Libraries in GHDL

The directory structure looks like this for my specific case.

$ tree .
.
├── LICENSE
├── pixel_line_cnt.gtkw
├── pixel_line_cnt_tb.vhd
├── pixel_line_cnt.vcd
├── pixel_line_cnt.vhd
├── README.md
├── top.gtkw
├── top_tb.vhd
├── top.vcd
├── top.vhd
└── uart_vhdl
    ├── clock_divider_tb.vhd
    ├── clock_divider.vhd
    ├── LICENSE
    ├── README.md
    ├── string_sender.gtkw
    ├── string_sender_tb.vhd
    ├── string_sender.vhd
    ├── top_ecp5_eval.lpf
    ├── top.gtkw
    ├── top_tb.vhd
    ├── top.vhd
    ├── uart_tx.gtkw
    ├── uart_tx.pcf
    ├── uart_tx_tb.vhd
    ├── uart_tx.vhd
    └── uart.vhd

First, we tell GHDL about the necessary design files in the submodule. To not polute the UART directory with output files, it makes sense to create a work directory. The same applies for the main project.

$ mkdir uart_vhdl/work && mkdir work

There are more advanced usage options of GHDL, but for now we analyze individual files, check if we have everything and then execute our simulation.

In my particular case I need the following files to be known be GHDL.

$ ghdl -a --workdir=uart_vhdl/work --work=max_uart uart_vhdl/uart.vhd
$ ghdl -a --workdir=uart_vhdl/work --work=max_uart uart_vhdl/uart_tx.vhd
$ ghdl -a --workdir=uart_vhdl/work --work=max_uart uart_vhdl/clock_divider.vhd

$ ghdl -a --workdir=work --work=work pixel_line_cnt.vhd
$ ghdl -a -Puart_vhdl/work --workdir=work --work=work top.vhd

If the top design where the library is included can be analyzed w/o GHDL puking, you have done everything right.

Basically, two libraries are created: The default work library and our own library, which is called max_uart in that case.

With the --dir option we can check whether it GHDL understood what we want from it.

$ ghdl --dir --workdir=uart_vhdl/work --work=max_uart
# Library max_uart
# Directory: uart_vhdl/work/
package uart_pkg
entity clock_divider
architecture clock_divider_rtl of clock_divider
entity uart_tx
architecture uart_tx_rtl of uart_tx

Nice! The same we can do for our default work library to double-check if GHDL is set up straight. I can now analyze and execute the testbench of my top design, the entity top_tb.

$ ghdl -a -Puart_vhdl/work --workdir=work --work=work top_tb.vhd
$ ghdl -r -Puart_vhdl/work --workdir=work --work=work top_tb --vcd=top.vcd

--vcd creates a file, that we can feed into our wave viewer. I noticed, that GHDL can be quite bitchy with the order of CLI arguments. I guess there is no argparse implementation for Ada- haha. (GHDL is written in Ada.)

For further information on GHDL usage you should take a look at the GHDL manual [8].

As a last step we ask GTKWave to display the freshly generated waveforms.

$ gtkwave top.vcd top.gtkw

Personally, I like to have my sessions settings stored in a .gtk file as you can see above.

I hope this article helped you to get your head around how to construct a clean design structure for your VHDL project, that leverages GHDL, a tremendous tool.


  1. GHDL ↩︎ ↩︎

  2. GTKwave ↩︎

  3. Git ↩︎

  4. GPLv2 ↩︎

  5. IEEE ↩︎

  6. Package File - VHDL Example ↩︎

  7. 7.11 Git Tools - Submodules ↩︎

  8. Chapter 10.6 Library commands, GHDL manual ↩︎