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.
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
Leave A Comment