Ripple Carry Adder Module in VHDL and Verilog

A Ripple Carry Adder is made of a number of full-adders cascaded together. It is used to add together two binary numbers using only simple logic gates. The figure below shows 4 full-adders connected together to produce a 4-bit ripple carry adder.

Ripple Carry Adder (4-bit) Block Diagram

As I noted in the Full Adder tutorial, the FPGA designer doesn’t usually need to implement ripple carry adders manually. The FPGA tools are smart enough to know how to add two binary numbers together. The purpose of this exercise is to show how basic circuits can work to perform simple tasks. It is a good example for a beginner.

There are two examples in each VHDL and Verilog shown below. The first contains a simple ripple carry adder made up of just two full adders (it can add together any two-bit inputs). The second example uses a generic (in VHDL) or a parameter (in Verilog) that creates a ripple carry adder that accepts as an input parameter the WIDTH of the inputs. Therefore it is scalable for any input widths.

VHDL Implementation:

Example 1: Two-Bit Ripple Carry Adder in VHDL

Note that the ripple carry adder output (o_result) is one bit larger than both of the two adder inputs. This is because two N bit vectors added together can produce a result that is N+1 in size. For example, b”11″ + b”11″ = b”110″. In decimal, 3 + 3 = 6.

The output o_result is assigned using the ampersand (&) VHDL concatenation operator. As long as inputs to the concatenation operator of the same type they can be concatenated together.

-------------------------------------------------------------------------------
-- File Downloaded from http://www.nandland.com
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;

entity ripple_carry_adder_2_FA is
  port (
    i_add_term1  : in std_logic_vector(1 downto 0);
    i_add_term2  : in std_logic_vector(1 downto 0);
    --
    o_result   : out std_logic_vector(2 downto 0)
    );
end ripple_carry_adder_2_FA;


architecture rtl of ripple_carry_adder_2_FA is

  component full_adder is
    port (
      i_bit1  : in  std_logic;
      i_bit2  : in  std_logic;
      i_carry : in  std_logic;
      o_sum   : out std_logic;
      o_carry : out std_logic);
  end component full_adder;

  signal w_CARRY : std_logic_vector(2 downto 0);
  signal w_SUM   : std_logic_vector(1 downto 0);

  
begin 

  w_CARRY(0) <= '0'; -- no carry input on first full adder FULL_ADDER_1_INST : full_adder port map ( i_bit1 => i_add_term1(0),
      i_bit2  => i_add_term2(0),
      i_carry => w_CARRY(0),
      o_sum   => w_SUM(0),
      o_carry => w_CARRY(1)
      );

  FULL_ADDER_2_INST : full_adder
    port map (
      i_bit1  => i_add_term1(1),
      i_bit2  => i_add_term2(1),
      i_carry => w_CARRY(1),
      o_sum   => w_SUM(1),
      o_carry => w_CARRY(2)
      );
  
  o_result <= w_CARRY(2) & w_SUM;  -- VHDL Concatenation
  
end rtl;



Example 2: Scalable Ripple Carry Adder in VHDL

The second example is more complicated. The ripple carry adder below uses a VHDL generic to allow for different implementations of the same code. This makes the code more versatile and reusable. Using the generic, the code creates a generate statement which instantiates as many full-adders as are specified by the g_WIDTH generic.

This code shows how powerful generics and generate statements can be in creating code that is compact, but very scalable. It can be used for any width of inputs. The digital designer simply needs to set the g_WIDTH appropriately for his or her particular application and the tools will generate the correct amount of logic!

-------------------------------------------------------------------------------
-- File Downloaded from http://www.nandland.com
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;

entity ripple_carry_adder is
  generic (
    g_WIDTH : natural := 2
    );
  port (
    i_add_term1  : in std_logic_vector(g_WIDTH-1 downto 0);
    i_add_term2  : in std_logic_vector(g_WIDTH-1 downto 0);
    --
    o_result   : out std_logic_vector(g_WIDTH downto 0)
    );
end ripple_carry_adder;


architecture rtl of ripple_carry_adder is

  component full_adder is
    port (
      i_bit1  : in  std_logic;
      i_bit2  : in  std_logic;
      i_carry : in  std_logic;
      o_sum   : out std_logic;
      o_carry : out std_logic);
  end component full_adder;

  signal w_CARRY : std_logic_vector(g_WIDTH downto 0);
  signal w_SUM   : std_logic_vector(g_WIDTH-1 downto 0);

  
begin 

  w_CARRY(0) <= '0'; -- no carry input on first full adder SET_WIDTH : for ii in 0 to g_WIDTH-1 generate i_FULL_ADDER_INST : full_adder port map ( i_bit1 => i_add_term1(ii),
        i_bit2  => i_add_term2(ii),
        i_carry => w_CARRY(ii),
        o_sum   => w_SUM(ii),
        o_carry => w_CARRY(ii+1)
        );
  end generate SET_WIDTH;

  o_result <= w_CARRY(g_WIDTH) & w_SUM;  -- VHDL Concatenation
  
end rtl;

VHDL Testbench:

-------------------------------------------------------------------------------
-- File Downloaded from http://www.nandland.com
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;

entity ripple_carry_adder_tb is
end ripple_carry_adder_tb;


architecture rtl of ripple_carry_adder_tb is

  constant c_WIDTH : integer := 2;
  
  signal r_ADD_1  : std_logic_vector(c_WIDTH-1 downto 0) := (others => '0');
  signal r_ADD_2  : std_logic_vector(c_WIDTH-1 downto 0) := (others => '0');
  signal w_RESULT : std_logic_vector(c_WIDTH downto 0);

  component ripple_carry_adder is
    generic (
      g_WIDTH : natural
      );
    port (
      i_add_term1 : in  std_logic_vector(g_WIDTH-1 downto 0);
      i_add_term2 : in  std_logic_vector(g_WIDTH-1 downto 0);
      --
      o_result    : out std_logic_vector(g_WIDTH downto 0)
      );
  end component ripple_carry_adder;

begin 

  -- Instantiate the Unit Under Test (UUT)
  UUT : ripple_carry_adder
    generic map (
      g_WIDTH     => c_WIDTH
      )
    port map (
      i_add_term1 => r_ADD_1,
      i_add_term2 => r_ADD_2,
      o_result    => w_RESULT
      );
  
  -- Test bench is non-synthesizable
  process is
  begin
    r_ADD_1 <= "00";
    r_ADD_2 <= "01";
    wait for 10 ns;
    r_ADD_1 <= "10";
    r_ADD_2 <= "01";
    wait for 10 ns;
    r_ADD_1 <= "01";
    r_ADD_2 <= "11";
    wait for 10 ns;
    r_ADD_1 <= "11";
    r_ADD_2 <= "11";
    wait for 10 ns;
  end process;
  
end rtl;

Verilog Implementation:

Example 3: Two-Bit Ripple Carry Adder in Verilog

Note that the ripple carry adder output (o_result) is one bit larger than both of the two adder inputs. This is because two N bit vectors added together can produce a result that is N+1 in size. For example, b”11″ + b”11″ = b”110″. In decimal, 3 + 3 = 6.

The output o_result is assigned using the brackets {, } Verilog concatenation operator.

///////////////////////////////////////////////////////////////////////////////
// File Downloaded from http://www.nandland.com
///////////////////////////////////////////////////////////////////////////////

`include "full_adder.v"

module ripple_carry_adder_2_FA 
  (
   input [1:0]  i_add_term1,
   input [1:0]  i_add_term2,
   output [2:0] o_result
   );
    
  wire [2:0]    w_CARRY;
  wire [1:0]    w_SUM;
  
  // No carry input on first full adder  
  assign w_CARRY[0] = 1'b0;
  
  full_adder full_adder_1
    ( 
      .i_bit1(i_add_term1[0]),
      .i_bit2(i_add_term2[0]),
      .i_carry(w_CARRY[0]),
      .o_sum(w_SUM[0]),
      .o_carry(w_CARRY[1])
      );

  full_adder full_adder_2
    ( 
      .i_bit1(i_add_term1[1]),
      .i_bit2(i_add_term2[1]),
      .i_carry(w_CARRY[1]),
      .o_sum(w_SUM[1]),
      .o_carry(w_CARRY[2])
      );
  
  assign o_result = {w_CARRY[2], w_SUM};   // Verilog Concatenation

endmodule // ripple_carry_adder_2_FA

Example 4: Parameterizable Ripple Carry Adder in Verilog

Again, this version of the code is more complicated. The above ripple carry adder uses a Verilog parameter to allow for different implementations of the same code. This makes the code more versatile and reusable. Using the parameter, the code creates a generate statement which instantiates as many full-adders as are specified by the WIDTH parameter.

This code shows how powerful parameters and generate statements can be in creating code that is compact, but very malleable. It can be used for any width of inputs. The digital designer simply needs to set the WIDTH appropriately for his or her particular application and the tools will generate the correct amount of logic!

///////////////////////////////////////////////////////////////////////////////
// File Downloaded from http://www.nandland.com
///////////////////////////////////////////////////////////////////////////////

`include "full_adder.v"

module ripple_carry_adder 
  #(parameter WIDTH)
  (
   input [WIDTH-1:0] i_add_term1,
   input [WIDTH-1:0] i_add_term2,
   output [WIDTH:0]  o_result
   );
    
  wire [WIDTH:0]     w_CARRY;
  wire [WIDTH-1:0]   w_SUM;
  
  // No carry input on first full adder  
  assign w_CARRY[0] = 1'b0;        
  
  genvar             ii;
  generate 
    for (ii=0; ii<WIDTH; ii=ii+1) 
      begin
        full_adder full_adder_inst
            ( 
              .i_bit1(i_add_term1[ii]),
              .i_bit2(i_add_term2[ii]),
              .i_carry(w_CARRY[ii]),
              .o_sum(w_SUM[ii]),
              .o_carry(w_CARRY[ii+1])
              );
      end
  endgenerate
  
  assign o_result = {w_CARRY[WIDTH], w_SUM};   // Verilog Concatenation

endmodule // ripple_carry_adder

Verilog Testbench:

///////////////////////////////////////////////////////////////////////////////
// File Downloaded from http://www.nandland.com
///////////////////////////////////////////////////////////////////////////////

`include "ripple_carry_adder.v"

module ripple_carry_adder_tb ();

  parameter WIDTH = 2;

  reg [WIDTH-1:0] r_ADD_1 = 0;
  reg [WIDTH-1:0] r_ADD_2 = 0;
  wire [WIDTH:0]  w_RESULT;
  
  ripple_carry_adder #(.WIDTH(WIDTH)) ripple_carry_inst
    (
     .i_add_term1(r_ADD_1),
     .i_add_term2(r_ADD_2),
     .o_result(w_RESULT)
     );

  initial
    begin
      #10;
      r_ADD_1 = 2'b00;
      r_ADD_2 = 2'b01;
      #10;
      r_ADD_1 = 2'b10;
      r_ADD_2 = 2'b01;
      #10;
      r_ADD_1 = 2'b01;
      r_ADD_2 = 2'b11;
      #10;
      r_ADD_1 = 2'b11;
      r_ADD_2 = 2'b11;
      #10;
    end

endmodule // ripple_carry_adder_tb

Learn Verilog

Modules

Learn VHDL