Labs
Teaching Materials
Support Texts
Projects
Quick Resources
Tutorials
Project Examples
Hardware Sources
Software Sources
Research
Self Help
Discussion Forum
Mailing List
Submit Problem
FAQ's




Xilinx University
Program


How-to Easily Design an Adder Using VHDL

Preface
We are going to take a look at designing a simple unsigned adder circuit in VHDL through different coding styles. By doing so, this will get you familiar with designing by different methods and, hopefully, show you to look for the easy solution before attempting to code anything.

The specific circuit we are going to model in this how-to is a 4-bit unsigned adder. In the first pass of the design, we will build a 1-bit full adder (or (3,2) counter) slice that will be used to build the full 4-bit adder. In the second pass of the design, we are going to build the circuit using the IEEE std_logic unsigned package, a much more code efficient and scalable design.

This how-to assumes you have some knowledge of VHDL and understand concepts such as entities, architectures, and signals. If you need further VHDL language references, please check under the tutorials section of Student Resources. Let begin!

1-Bit Full Adder
As you know, a 1-bit full adder is characterized by the following equations:

sum = x xor y xor cin
cout = (x and y) or (x and cin) or (x and cout)

where x, y, and cin are the two inputs and the carry in, respectively while sum and cout are the sum and carry out. A pictorial representation of the component is listed in figure 1.


Figure 1 - 1-Bit Full Adder Component

The VHDL code for the above component (downloadable file add_1_bit.vhd) is as follows:

  library IEEE;
  use IEEE.std_logic_1164.all;

  entity add_1_bit is
    port (
      x: in std_logic;
      y: in std_logic;
      cin: in std_logic;
      sum: out std_logic;
      cout: out std_logic
    );
  end add_1_bit;

  architecture rtl of add_1_bit is
  begin
    sum <= x xor y xor cin;
    cout <= (x and y) or (x and cin) or (y and cin);
  end rtl;

As you can see, we are using the standard IEEE libraries, which include std_logic, in order to create components that can be synthesized easily to hardware. Using signal types such as bit may not synthesize or produce unpredictable results. Always use the std_logic_1164 library!

This is a pretty standard component. In the entity declaration, x, y, and cin are declared as inputs of type std_logic. The outputs are sum and cout of type std_logic. In the architecture section, we define the outputs sum and cout to be concurrent statements based directly off the inputs. Nothing special about this component, I'm sure you have already seen a version of this model.

4-Bit Unsigned Adder using 1-Bit Full Adder Component
Now we are going to make four copies of the above component to make our 4-bit unsigned adder component, thus producing a ripple-carry adder. This is done through instantiating four copies of the above 1-bit adder component in VHDL. Figure 2 illustrates the connections of this component.


Figure 2 - 4-Bit Unsigned Adder

The inputs to the component are the 4-bit unsigned numbers, x and y, and the carry in that includes cin and the internal ripple carries between each 1-bit block. The 4-bit output sum and cout are shown as outputs of the systems. Listed below is the VHDL code for the component (downloadable add_4_bits.vhd).

  library IEEE;
  use IEEE.std_logic_1164.all;

  entity add_4_bits is
    port (
      x: in std_logic_vector(3 downto 0);
      y: in std_logic_vector(3 downto 0);
      cin: in std_logic;
      sum: out std_logic_vector(3 downto 0);
      cout: out std_logic
    );
  end add_4_bits;

  architecture rtl of add_4_bits is
    component add_1_bit
      port (
        x: in std_logic;
        y: in std_logic;
        cin: in std_logic;
        sum: out std_logic;
        cout: out std_logic
      );
    end component;

    signal i_carry: std_logic_vector(2 downto 0);
  begin
    cell_1: add_1_bit
      port map (x(0), y(0), cin, sum(0), i_carry(0));

    cell_2: add_1_bit
      port map (x(1), y(1), i_carry(0), sum(1), i_carry(1));

    cell_3: add_1_bit
      port map (x(2), y(2), i_carry(1), sum(2), i_carry(2));

    cell_4: add_1_bit
      port map (x(3), y(3), i_carry(2), sum(3), cout);
  end rtl;

You can see that we define the component add_1_bit in the architecture declaration section of the code. Please also notice that this declaration follows VHDL-87 format and NOT the '93 version. This is because the synopsis synthesis tool is VHDL-87 compliant and we need to code to those rules in order to generate valid synthesis results. Coding in '93 will not (currently) work.

Four versions of this component are then declared in the body section of the code, with all of the appropriate "glue" between each component. As this point, you must be asking, "isn't there an easy way to do this?". From my perspective, this is alot of code for just a 4-bit unsigned adder; the point to VHDL is to make it easier to perform operations such as the above. Let's take alook at an easier way.

4-Bit Unsigned Adder Using std_logic_unsigned
Using the same entity as above, look at the code below and notice how much and where it has been shortened (downloadable add.vhd).

  library IEEE;
  use IEEE.std_logic_1164.all;
  use IEEE.std_logic_unsigned.all;

  entity add is
    generic (width: integer:=4);
    port (
      x: in std_logic_vector((width-1) downto 0);
      y: in std_logic_vector((width-1) downto 0);
      cin: in std_logic;
      sum: out std_logic_vector((width - 1) downto 0);
      cout: out std_logic
    );
  end add;

  architecture rtl of add is
    signal i_sum: std_logic_vector(width downto 0);
    constant i_cin_ext: std_logic_vector(width downto 1) := (others => '0');
  begin
    i_sum <= ('0' & x) + ('0' & y) + (i_cin_ext & cin);
    sum <= i_sum((width-1) downto 0);
    cout <= i_sum(width);
  end rtl;

The first thing you will notice, at the top of the code, is the reference to the std_logic_unsigned package, used for unsigned std_logic arithmetic operations. This package contains a procedure called "+" which we use as an addition within the body of the architecture. This "+" takes three unsigned std_logic_vectors and adds them, returning a result.

The other should be the use of a generic. A generic lets us produce code that can be changed by the passed parameter. In our case, we let the generic width be the bit width of the two numerands, thus making the code scalable to any width of signals.

For this specific example, we take x and y, concatenate a '0' to the beginning (unsigned logic). For cin, we concatenate a vector one less than the generic constant. These are all added with "+" and assigned to the result i_sum. We then split i_sum into the sum and the carry out. This can be done a number of ways but this is one of them and, as you can see, is much simpler than the previous example. Additionally, with the use the generic width, the component becomes much more reusable, a quality always desired by designers.

I have found that the performance of such code to be better, while easier to code, than other algorithmic techniques. This is probably due to the fact that the synthesis tools will understand what is inferred by the IEEE libraries more than any algorithm you can write. However, be careful of the cin vector. Good synthesis tools will recognize that the cin vector is really only cin padded with zeros, and thus will apply only the cin signal to the input of the cin portion of the fast carry logic for the FPGA. However, others will not; check the output of the synthesis tool to make sure that the fast carry logic was used on the FPGA (assuming such logic exists). Of course performing hand layout methods or using LogicBLOX will produce smaller, faster results. But those methods become technology dependent (i.e. tied to your target device) while this code will not.

fouldsbr@msu.edu

 
 
Designed for 800x600 viewing on Microsoft Internet Explorer 4.0+ or Netscape 4.0 Browsers
Copyright © 2000 Michigan State University