VHDL Episode 1 - A simple adder

@ 2011-06-16 by João Paulo Pizani Flor

Previous episodes: * /en/blog/ac/vhdl0

It took me long to start this tutorial, but I had a noble excuse: I was finishing my B.Sc thesis (in Computer Science)… But enough with excuses, let’s start the VHDL coding! In the first episode of our tutorial we deal with a binary adder.

In case you don’t know what a binary adder is, the world’s no. 1 source of information (Wikipedia) is always there to help you: Binary Adder. Our first hardware block described using VHDL is going to be a 1-bit Full Adder:

Our first design
Our first design

And to show that VHDL isn’t such an ugly monster, here’s the complete adder’s code:

library ieee;
use ieee.std_logic_1164.all;

entity fullAdder is
    Port(
        a    : in std_logic;
        b    : in std_logic;
        cin  : in std_logic;
        s    : out std_logic;
        cout : out std_logic);
end fullAdder;

architecture arch of fullAdder is
    signal aXorB : std_logic;
begin
    aXorB <= a xor b;
    s     <= aXorB xor cin;
    cout  <= (a and b) or (cin and aXorB);
end arch;

So let’s first talk a bit about the code just above and then write and run some tests to show that this amazing adder works!

During the following episodes I’m going to show you the VHDL coding style I adopt. The first tidbit of this style is that each hardware block resides in a similarly named text file. Our fullAdder will then reside in fullAdder.vhd.

Each VHDL file has TWO main sections: entity and architecture. The entity section describes the block’s interface to the outside world, the Port(.. construct means exactly what you think it means: it’s a list of the block’s input and output ports. Each port has a name, a direction and a type. By the way, EVERYTHING in VHDL has a type, some people love it, some hate it, nobody remains indifferent :)

The datatype we are using in all of our adder’s ports is std_logic, a logic type which besides ‘0’ and ‘1’ has some other possible values (high impedance, undefined and others I never used). The std_logic type and its std_logic_vector partner are by far the most commonly used VHDL types.

Now a bit about the second section of all VHDL files: the architecture. In this section we define all the building blocks for our circuit, how to interconnect them and which signals to use for these connections. Just before the begin keyword we need to declare everything that’s going to be used in the architecture; in our case we declare only one signal:

signal aXorB : std_logic;

Signals can be thought of as wires, or buses. They also have a name and type, but no direction. After all declarations we start the body of the architecture itself. In our architecture for a full adder we do three parallel assignments. Each of them binds a signal (or port) to an expression. Because the assignment is parallel, the signal (or port) is bound to the expression forever, and if you try to bind a signal more than once the VHDL compiler will complain - it means you are causing a short-circuit :) The parallel assignments in our architecture are just a copy of the logic formulas defining a full adder (according to Wikipedia)

S = A xor B xor Cin
Cout = (A and B) or (Cin and (A xor B))

The coding style that we can infer from this first episode follows:

This is a VHDL coding style which I personally find beautiful and readable. So please don’t start the flame war :)

After the style talk we start the final part of this episode: showing that our design works (testing it) \o/ First of all, let’s run GHDL just to confirm that our code’s syntax is OK:

ghdl -s fullAdder.vhd

Now for the really fun part: we’ll write a testbench for our adder. From now on, for every hardware block that we write, we’ll have a corresponding testbench. A testbench is just a VHDL file providing some inputs to the design under test (DUT) and checking that the outputs are as expected. You can find the testbench for our fullAdder here: http://hpaste.org/47881

The testbench’s code is big for this case, and surely there are ways to make it smaller and prettier. Anyways, I chose to write it this way to show the general guidelines that all next episodes will follow.

The entity section in a testbench is always empty, and in the architecture section there’s a certain pattern:

As in our design (fullAdder) there are only 8 possible input combinations (3 bits), we tested all of them. This is usually not the case, and hardware designers test only the combinations which they consider most “significant” to demonstrate the block’s functionality. Each test case has approximately the same format:

sig_<0> <= value_<0>;
...
sig_<1> <= value_<1>;
wait for x ns;
assert (expected_output = actual_output) report "test_name" severity failure;

We give a value for each of the circuit’s inputs, then wait for some time (so that the outputs are stable) and check whether the actual output equals the one we expected…

Well people, that’s all for today! The first episode of the tutorial was pretty lengthy because a lot of concepts had to be introduced “from scratch”. Here we go with some exercises!

Exercises for today’s episode:

  1. Download the file VHDL1, unzip it, run the simulation and view the generated trace.vcd file in GTKWave. The included Makefile shall help you

  2. Modify some tests, make them fail and then correct them… Hack the code at will :) The same file organization and the same style will be used in all following episodes…