How to Implement a Full Adder in VHDL

In this post, you will learn how to write the VHDL code for a full adder. Many years ago when you need to implement an arithmetic operator such as sum subtraction or multiplication, you had to design it by hand. Modern synthesis tools implement basic of arithmetic using optimized library. This means that you don’t care about the implementation of the adder in the silicon device, i.e. ASIC or FPGA, you can use simply “+” operator to implement your full adder. Moreover, FPGA vendors such as ALTERA or XILINX provide you macro generator for arithmetic operator if you need custom behavior i.e. pipelining to speed up timing performances.

The NUMERIC_STD package

Since 1995, IEEE defines numeric types and arithmetic functions in the standard package ”numeric_std” to outline a standard approach for use arithmetic operator with synthesis tools.

Two numeric types are defined:

  • UNSIGNED: represents UNSIGNED number in vector form
  • SIGNED: represents a SIGNED number in vector form

The base element type is type STD_LOGIC.

The leftmost bit is treated as the most significant bit.

Signed vectors are represented in two’s complement form.

The “numeric_std” package contains overloaded arithmetic operators on the SIGNED and UNSIGNED types. The package also contains useful type conversions functions.

Other packages, before this standardization, were defined by Synopsys:

  • ieee.std_logic_arith
  • ieee.std_logic_unsigned
  • ieee.std_logic_signed

that despite the prefix “ieee” are not standard but Synopsys proprietary.

It is worth of notice that these packages are recognized by the most synthesis tool. In any case, it will be a good VHDL design approach to use standard library “ieee.numeric_std.all” especially is you start new VHDL design.

Full Adder VHDL entity

As you know, when you add two numbers of “N” bit, the results can be “N+1” bit wide. If you handle this increment of dynamics, you are implementing a full adder. If no dynamic increase is handled the adder implementation is simpler than the full adder one but the result can be wrapped around the “N” bit. Generally, if you know that no wrap around will occur, you can use a simple half-adder.

In this post, you can find out more about binary number representation and wrap around concept.

Here below is reported an example of full adder parametric entity. In the VHDL code, the full adder is implemented in line 24 on the registered input. Pay attention that before performing the addition operation you must extend the number of bit of the input operand. This is implemented using the standard “resize” function provided in the “numeric_std” package as in line 31 and 32.

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

entity adder_full_signed_reg is
generic(
  N             : integer := 8);
port (
  i_clk       : in     std_logic;
  i_add1      : in     std_logic_vector(N-1 downto 0);
  i_add2      : in     std_logic_vector(N-1 downto 0);
  o_sum       : out    std_logic_vector(N downto 0));
end adder_full_signed_reg;

architecture rtl of adder_full_signed_reg is

signal r_add1   : signed(N downto 0);
signal r_add2   : signed(N downto 0);
signal w_sum    : signed(N downto 0);

begin

-- combinatorial adder
  w_sum <= r_add1 + r_add2;

r_process : process(i_clk)
begin
  if(rising_edge(i_clk)) then
  
  -- register input and extend sign
    r_add1      <=  resize(signed(i_add1),N+1);
    r_add2      <=  resize(signed(i_add2),N+1);
    
  -- register output
    o_sum       <= std_logic_vector(w_sum);
    
  end if;
end process r_process;

end rtl;

In the example is implemented a signed full adder, if you need an unsigned full adder you need to replace the line containing “signed” data type definition with “unsigned” data type.

 

A simple example is given in the following bunch of VHDL code:

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

entity adder_full_unsigned is
generic(
  N        : integer := 8);
port (
  add1     : in     std_logic_vector(N-1 downto 0);
  add2     : in     std_logic_vector(N-1 downto 0);
  sum      : out    std_logic_vector(N downto 0));
end adder_full_unsigned;

architecture rtl of adder_full_unsigned is

begin

  sum <= std_logic_vector( resize(unsigned(add1),N+1) + resize(unsigned(add2),N+1) );

end rtl;

in this case, the adder is purely combinatorial.


 

Full adder trial layout

In Figure1  is reported a trial layout on ALTERA Quartus II using a Cyclone V FPGA. The signed full adder VHDL code presented above is pure VHDL RTL code so you can use it independently on every kind of FPGA or ASIC.

In Figure1 Quartus II implement sign extension on input operand, then add them and registers the output result as described in the VHDL code.

Figure1 - Full Adder Altera Quartus II RTL viewer
Figure1 – Full Adder Altera Quartus II RTL viewer

You can implement different size of adder just changing the input generic value on “N” that represents the number of bit of the full adder.

Figure2 reports the post-layout result:

Figure - Full Adder post layout report on Cyclone V
Figure2 – Full Adder post layout report on Cyclone V

Where you can notice that the number of registers used is 25: 8 for r_add1; 8 for r_add2; 9 for o_sum

the design runs at about 480 MHz.

VHDL code for Half Adder

If you don’t need to increment the dynamic you can use a half adder instead of a full adder.

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

entity adder_half_unsigned_reg is
generic(
  N       : integer := 256);
port (
  i_clk     : in     std_logic;
  i_add1    : in     std_logic_vector(N-1 downto 0);
  i_add2    : in     std_logic_vector(N-1 downto 0);
  o_sum     : out    std_logic_vector(N-1 downto 0));
end adder_half_unsigned_reg;

architecture rtl of adder_half_unsigned_reg is

signal r_add1    : unsigned(N-1 downto 0);
signal r_add2    : unsigned(N-1 downto 0);
signal w_sum     : unsigned(N-1 downto 0);

begin

-- combinatorial adder
  w_sum <= r_add1 + r_add2;

r_process : process(i_clk)
begin
  if(rising_edge(i_clk)) then
  
  -- register input
    r_add1      <=  unsigned(i_add1);
    r_add2      <=  unsigned(i_add2);
    
  -- register output
    o_sum       <= std_logic_vector(w_sum);
    
  end if;
end process r_process;

end rtl;

 

In Figure3 is reported the half-adder implementation where the number of input bit is 256. In this case, it is implemented a 256 bit full adder.

Figure3 - Half-Adder at 256 bit Altera Quartus RTL Viewer
Figure3 – Half-Adder at 256 bit Altera Quartus RTL Viewer

The FPGA is Cyclone V. In this example all the pins are mapped on I/O. it is not a realistic situation. This is only an example to understand how to implement a parametric adder VHDL code on FPGA.

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

entity test_adder_half_unsigned_reg is
generic(
  N          : integer := 256);
port(
  i_clk      : in     std_logic;
  o_sum      : out    std_logic_vector(N-1 downto 0));
end test_adder_half_unsigned_reg;

architecture rtl of test_adder_half_unsigned_reg is

component adder_half_unsigned_reg
generic(
  N        : integer := 256);
port (
  i_clk     : in     std_logic;
  i_add1    : in     std_logic_vector(N-1 downto 0);
  i_add2    : in     std_logic_vector(N-1 downto 0);
  o_sum     : out    std_logic_vector(N-1 downto 0));
end component;

signal r_add1     : std_logic_vector(N-1 downto 0);
signal r_add2     : std_logic_vector(N-1 downto 0);

begin

input_process : process(i_clk)
begin
  if(rising_edge(i_clk)) then
  
  -- register input
    r_add1      <=  std_logic_vector(signed(r_add1) + 1);
    r_add2      <=  std_logic_vector(signed(r_add2) - 1);
        
  end if;
end process input_process;

u_adder_half_unsigned_reg : adder_half_unsigned_reg
generic map(
  N         => N)
port map(
  i_clk       => i_clk   ,
  i_add1      => r_add1  ,
  i_add2      => r_add2  ,
  o_sum       => o_sum   );
  
end rtl;

In order to layout the example, a test_adder_half_unsigned_reg container instantiate the adder_half_unsigned_reg generating two fake input to the component. This, of course, implements additional logic into the FPGA, but it is not important. Our purpose is to verify that the parametric design of half-adder with 256 bit dynamic can fit into the FPGA.  As clear from the post-layout timing report of Figure4, the design can run up to about 110 MHz. Of course, in this case, the number of registers relative to the half adder is 256×3 = 768 over a total of 1277. The other flip-flops are used to implement the test bench that instantiates the half-adder.

Figure4 - 256 bit Half Adder report on Cyclone V
Figure4 – 256 bit Half Adder report on Cyclone V

As you see, the layout tool, Quartus II in this example, is smart enough to implement adder logic even for large input operands.

If the requirements in terms of timing are demanding, a pipeline implementation is required. For example, cascading 4-full-adder of 64 bit with proper carry bit handling.


 

If you appreciated this post, please help us to share it with your friend.

If you need to contact us, please write to: surf.vhdl@gmail.com

We appreciate any of your comment, please post below:

 

3 thoughts on “How to Implement a Full Adder in VHDL

  1. Though the implementation of larger logic diagrams is possible with the above full adder logic a simpler symbol is mostly used to represent the operation. Given below is a simpler schematic representation of a one-bit full adder.

  2. sir i am designing a low pass fir filter but i am stuck with the code. can u please help me out by sending the code and the explanation of the code

    1. you can implement a low pass filter using a FIR filter.
      If you can use matlab just use “FDATOOL”, then you can use this post as example for implementing the VHDL code

Leave a Reply

Your email address will not be published.