Carry Lookahead Adder in VHDL and Verilog
A Carry Lookahead (Look Ahead) 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 carry lookahead adder. Carry lookahead adders are similar to Ripple Carry Adders. The difference is that carry lookahead adders are able to calculate the Carry bit before the Full Adder is done with its operation. This gives it an advantage over the Ripple Carry Adder because it is able to add two numbers together faster. The drawback is that it takes more logic. You will find there is often a balance between speed of execution and resources used when designing FPGAs and ASICs.
There are two examples for each VHDL and Verilog shown below. The first contains a simple carry lookahead adder made up of four full adders (it can add together any four-bit inputs). The second example uses a generic that creates a carry look ahead adder that accepts as an input parameter the WIDTH of the inputs. Therefore it is scalable for any input widths.
VHDL Implementation:
Example 1: Four-Bit Carry Lookahead Adder in VHDL
Note that the carry lookahead 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 carry_lookahead_adder_4_bit is port ( i_add1 : in std_logic_vector(3 downto 0); i_add2 : in std_logic_vector(3 downto 0); -- o_result : out std_logic_vector(4 downto 0) ); end carry_lookahead_adder_4_bit; architecture rtl of carry_lookahead_adder_4_bit 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_G : std_logic_vector(3 downto 0); -- Generate signal w_P : std_logic_vector(3 downto 0); -- Propagate signal w_C : std_logic_vector(4 downto 0); -- Carry signal w_SUM : std_logic_vector(3 downto 0); begin FULL_ADDER_BIT_0 : full_adder port map ( i_bit1 => i_add1(0), i_bit2 => i_add2(0), i_carry => w_C(0), o_sum => w_SUM(0), o_carry => open ); FULL_ADDER_BIT_1 : full_adder port map ( i_bit1 => i_add1(1), i_bit2 => i_add2(1), i_carry => w_C(1), o_sum => w_SUM(1), o_carry => open ); FULL_ADDER_BIT_2 : full_adder port map ( i_bit1 => i_add1(2), i_bit2 => i_add2(2), i_carry => w_C(2), o_sum => w_SUM(2), o_carry => open ); FULL_ADDER_BIT_3 : full_adder port map ( i_bit1 => i_add1(3), i_bit2 => i_add2(3), i_carry => w_C(3), o_sum => w_SUM(3), o_carry => open ); -- Create the Generate (G) Terms: Gi=Ai*Bi w_G(0) <= i_add1(0) and i_add2(0); w_G(1) <= i_add1(1) and i_add2(1); w_G(2) <= i_add1(2) and i_add2(2); w_G(3) <= i_add1(3) and i_add2(3); -- Create the Propagate Terms: Pi=Ai+Bi w_P(0) <= i_add1(0) or i_add2(0); w_P(1) <= i_add1(1) or i_add2(1); w_P(2) <= i_add1(2) or i_add2(2); w_P(3) <= i_add1(3) or i_add2(3); -- Create the Carry Terms: w_C(0) <= '0'; -- no carry input w_C(1) <= w_G(0) or (w_P(0) and w_C(0)); w_C(2) <= w_G(1) or (w_P(1) and w_C(1)); w_C(3) <= w_G(2) or (w_P(2) and w_C(2)); w_C(4) <= w_G(3) or (w_P(3) and w_C(3)); -- Final Answer o_result <= w_C(4) & w_SUM; -- VHDL Concatenation end rtl;
Example 2: Scalable Carry Lookahead Adder in VHDL
The second example is more complicated. The above carry lookahead adder 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 carry_lookahead_adder is generic ( g_WIDTH : natural ); port ( i_add1 : in std_logic_vector(g_WIDTH-1 downto 0); i_add2 : in std_logic_vector(g_WIDTH-1 downto 0); -- o_result : out std_logic_vector(g_WIDTH downto 0) ); end carry_lookahead_adder; architecture rtl of carry_lookahead_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_G : std_logic_vector(g_WIDTH-1 downto 0); -- Generate signal w_P : std_logic_vector(g_WIDTH-1 downto 0); -- Propagate signal w_C : std_logic_vector(g_WIDTH downto 0); -- Carry signal w_SUM : std_logic_vector(g_WIDTH-1 downto 0); begin -- Create the Full Adders GEN_FULL_ADDERS : for ii in 0 to g_WIDTH-1 generate FULL_ADDER_INST : full_adder port map ( i_bit1 => i_add1(ii), i_bit2 => i_add2(ii), i_carry => w_C(ii), o_sum => w_SUM(ii), o_carry => open ); end generate GEN_FULL_ADDERS; -- Create the Generate (G) Terms: Gi=Ai*Bi -- Create the Propagate Terms: Pi=Ai+Bi -- Create the Carry Terms: GEN_CLA : for jj in 0 to g_WIDTH-1 generate w_G(jj) <= i_add1(jj) and i_add2(jj); w_P(jj) <= i_add1(jj) or i_add2(jj); w_C(jj+1) <= w_G(jj) or (w_P(jj) and w_C(jj)); end generate GEN_CLA; w_C(0) <= '0'; -- no carry input o_result <= w_C(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 carry_lookahead_adder_tb is end carry_lookahead_adder_tb; architecture behave of carry_lookahead_adder_tb is constant c_WIDTH : integer := 3; 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 carry_lookahead_adder is generic ( g_WIDTH : natural ); port ( i_add1 : in std_logic_vector(g_WIDTH-1 downto 0); i_add2 : in std_logic_vector(g_WIDTH-1 downto 0); -- o_result : out std_logic_vector(g_WIDTH downto 0) ); end component carry_lookahead_adder; begin -- Instantiate the Unit Under Test (UUT) UUT : carry_lookahead_adder generic map ( g_WIDTH => c_WIDTH ) port map ( i_add1 => r_ADD_1, i_add2 => r_ADD_2, o_result => w_RESULT ); -- Test bench is non-synthesizable process is begin r_ADD_1 <= "000"; r_ADD_2 <= "001"; wait for 10 ns; r_ADD_1 <= "100"; r_ADD_2 <= "010"; wait for 10 ns; r_ADD_1 <= "010"; r_ADD_2 <= "110"; wait for 10 ns; r_ADD_1 <= "111"; r_ADD_2 <= "111"; wait for 10 ns; end process; end behave;
Verilog Implementation:
Example 3: 4-Bit Carry Lookahead Adder in Verilog
Note that the carry lookahead 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 carry_lookahead_adder_4_bit ( input [3:0] i_add1, input [3:0] i_add2, output [4:0] o_result ); wire [4:0] w_C; wire [3:0] w_G, w_P, w_SUM; full_adder full_adder_bit_0 ( .i_bit1(i_add1[0]), .i_bit2(i_add2[0]), .i_carry(w_C[0]), .o_sum(w_SUM[0]), .o_carry() ); full_adder full_adder_bit_1 ( .i_bit1(i_add1[1]), .i_bit2(i_add2[1]), .i_carry(w_C[1]), .o_sum(w_SUM[1]), .o_carry() ); full_adder full_adder_bit_2 ( .i_bit1(i_add1[2]), .i_bit2(i_add2[2]), .i_carry(w_C[2]), .o_sum(w_SUM[2]), .o_carry() ); full_adder full_adder_bit_3 ( .i_bit1(i_add1[3]), .i_bit2(i_add2[3]), .i_carry(w_C[3]), .o_sum(w_SUM[3]), .o_carry() ); // Create the Generate (G) Terms: Gi=Ai*Bi assign w_G[0] = i_add1[0] & i_add2[0]; assign w_G[1] = i_add1[1] & i_add2[1]; assign w_G[2] = i_add1[2] & i_add2[2]; assign w_G[3] = i_add1[3] & i_add2[3]; // Create the Propagate Terms: Pi=Ai+Bi assign w_P[0] = i_add1[0] | i_add2[0]; assign w_P[1] = i_add1[1] | i_add2[1]; assign w_P[2] = i_add1[2] | i_add2[2]; assign w_P[3] = i_add1[3] | i_add2[3]; // Create the Carry Terms: assign w_C[0] = 1'b0; // no carry input assign w_C[1] = w_G[0] | (w_P[0] & w_C[0]); assign w_C[2] = w_G[1] | (w_P[1] & w_C[1]); assign w_C[3] = w_G[2] | (w_P[2] & w_C[2]); assign w_C[4] = w_G[3] | (w_P[3] & w_C[3]); assign o_result = {w_C[4], w_SUM}; // Verilog Concatenation endmodule // carry_lookahead_adder_4_bit
Example 4: Parameterizable Carry Lookahead Adder in Verilog
The above carry lookahead 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 carry_lookahead_adder #(parameter WIDTH) ( input [WIDTH-1:0] i_add1, input [WIDTH-1:0] i_add2, output [WIDTH:0] o_result ); wire [WIDTH:0] w_C; wire [WIDTH-1:0] w_G, w_P, w_SUM; // Create the Full Adders genvar ii; generate for (ii=0; ii<WIDTH; ii=ii+1) begin full_adder full_adder_inst ( .i_bit1(i_add1[ii]), .i_bit2(i_add2[ii]), .i_carry(w_C[ii]), .o_sum(w_SUM[ii]), .o_carry() ); end endgenerate // Create the Generate (G) Terms: Gi=Ai*Bi // Create the Propagate Terms: Pi=Ai+Bi // Create the Carry Terms: genvar jj; generate for (jj=0; jj<WIDTH; jj=jj+1) begin assign w_G[jj] = i_add1[jj] & i_add2[jj]; assign w_P[jj] = i_add1[jj] | i_add2[jj]; assign w_C[jj+1] = w_G[jj] | (w_P[jj] & w_C[jj]); end endgenerate assign w_C[0] = 1'b0; // no carry input on first adder assign o_result = {w_C[WIDTH], w_SUM}; // Verilog Concatenation endmodule // carry_lookahead_adder
Testbench:
/////////////////////////////////////////////////////////////////////////////// // File Downloaded from http://www.nandland.com /////////////////////////////////////////////////////////////////////////////// `include "carry_lookahead_adder.v" module carry_lookahead_adder_tb (); parameter WIDTH = 3; reg [WIDTH-1:0] r_ADD_1 = 0; reg [WIDTH-1:0] r_ADD_2 = 0; wire [WIDTH:0] w_RESULT; carry_lookahead_adder #(.WIDTH(WIDTH)) carry_lookahead_inst ( .i_add1(r_ADD_1), .i_add2(r_ADD_2), .o_result(w_RESULT) ); initial begin #10; r_ADD_1 = 3'b000; r_ADD_2 = 3'b001; #10; r_ADD_1 = 3'b010; r_ADD_2 = 3'b010; #10; r_ADD_1 = 3'b101; r_ADD_2 = 3'b110; #10; r_ADD_1 = 3'b111; r_ADD_2 = 3'b111; #10; end endmodule // carry_lookahead_adder_tb
Leave A Comment