View Course Path

VHDL code for a 2-bit multiplier – All modeling styles

In this article, we will be writing the VHDL code for a 2-bit binary multiplier using all the three modeling techniques. We will write the code, testbench and will also create the RTL schematics for the same.

Binary multiplier (2-bit)

A multiplier is a circuit that takes two numbers as input and produces their product as an output. So a binary multiplier takes binary numbers as inputs and produces a result in binary. Before moving forward, lets quickly recap binary multiplication first.

0  x  0  =  0

0  x  1  =  0

1  x  0  =  0

1  x  1  =  1

So there’s always a confusion in students. Especially for students who have studied microprocessors like 8085 in their curriculum. Because in the programming of microprocessors like 8085, we use a technique called “Repetitive addition” for multiplication.

For example, to multiply 5 x 4, you just need to either add ‘4’ five times or add ‘5’ four times. It seems easy at first, but it is a very inefficient technique as it takes a lot of time to execute. Just imagine multiplying numbers of the order of millions or billions. Also, programs that have loops are not easy to implement in hardware.

So we use the “Parallel Binary Multiplier” method for multiplication. Because it is way more efficient. Moreover, it is similar to the method that we use to perform multiplication of decimal numbers. We have covered the 2-bit binary multiplier in detail in our digital electronics course.

In this article, we will focus more on the VHDL code of the circuit. So for reference, we’re using the equations and logic circuit of the 2-bit multiplier, as shown below.

Equations

P(0) <= A(0) AND B(0); 
P(1) <= (A(1) AND B(0)) XOR (A(0) AND B(1)); 
P(2) <= ((A(1) AND B(0)) AND (A(0) AND B(1))) XOR (A(1) AND B(1)); 
P(3) <= ((A(1) AND B(0)) AND (A(0) AND B(1))) AND (A(1) AND B(1));

Circuit diagram

Logic Circuit of Half Adder
The logic circuit of a 2-bit multiplier

Dataflow Modeling

As we know that in the dataflow modeling style, we describe the flow of data through every gate using equations. So let’s start writing a VHDL program using dataflow modeling.

As we have been doing from the start of this VHDL course, in the beginning, we have to include the IEEE library and use its standard logic library.

library ieee;
use ieee.std_logic_1164.all;

After including the library, we need to define an entity in which we define our input and output ports of the circuit.

entity multiply is
      port( A, B : in bit_vector(1 downto 0);
            P: out bit_vector(3 downto 0)
           );
end multiply;

In the above code “multiply” is the name of the entity and in ports, we have created two input ports of 2-bit each using A, B : bit_vector(1 downto 0); this creates two  bit_vector having bits A(0), A(1) and B(0), B(1) and a 4-bit output port using P: out bit_vector(3 downto 0) having bits P(0), P(1), P(2), P(3). Then we end the entity using the end keyword.

Now we move forward to create architecture for the above entity.

architecture dataflow of multiply is
begin

In the above code, architecture is the keyword used to define architecture. “dataflow” is the name of the architecture here, and it can be anything but must be a valid identifier. Then we specify the name of the entity, for which we are writing the architecture, i.e., multiply.

Now that we have completed the entity-architecture pair, we use the begin keyword after which we start writing the code for the architecture, if we have to define any component or signal, we define it before the keyword begin.

P(0) <= A(0) AND B(0); 
P(1) <= (A(1) AND B(0)) XOR (A(0) AND B(1));
P(2) <= ((A(1) AND B(0)) AND (A(0) AND B(1))) XOR (A(1) AND B(1));
P(3) <= ((A(1) AND B(0)) AND (A(0) AND B(1))) AND (A(1) AND B(1));

Now, talking about equations for P(0), it is pretty self-explanatory, just an AND gate.

Let’s get the circuit down here once again.

Logic Circuit of Half Adder
The logic circuit of a 2-bit multiplier

But in P(1), we have to do a sum of two bits coming from two AND gates, as shown in the figure. So we use XOR operation on them because we also know that inside a half adder, the sum is produced by the XOR gate. Let’s get the circuit diagram of a half-adder to simplify the process of understanding the equations for us. Check out the sum output below; it is the EX-OR of the two inputs.

Half Adder

Now look at P(2), it looks confusing at first. But let’s simplify it. We can see in the half adder’s diagram above that the carry output of a half adder is obtained by ANDing the two inputs. So let’s do that first.

We get the output of the first half adder as (A(1) AND B(0)) AND (A(0) AND B(1)). But the job is not done yet. P(2) is actually the output of the SUM component of the second half adder. As we saw earlier, the sum component of the half adder is basically the EXORing of its two inputs.

The first input is (A(1) AND B(0)) AND (A(0) AND B(1))and the second input is (A(1) AND B(1)) . Thus P(1) is equal to (A(1) AND B(0)) AND (A(0) AND B(1)) XOR (A(1) AND B(1)).

If you understood the formation of equation P(2), then P(3) is the same, just instead of XOR we used AND.

end architecture;

We end the architecture using the end keyword.

VHDL program of 2-bit multiplier using dataflow modeling

library ieee;
use ieee.std_logic_1164.all;

entity multiply is 
port (A, B : in bit_vector(1 downto 0);
P : out bit_vector(3 downto 0)
);
end multiply;

architecture dataflow of multiply is
begin
P(0) <= A(0) AND B(0);
P(1) <= (A(1) AND B(0)) XOR (A(0) AND B(1));
P(2) <= ((A(1) AND B(0)) AND (A(0) AND B(1))) XOR (A(1) AND B(1));
P(3) <= ((A(1) AND B(0)) AND (A(0) AND B(1))) AND (A(1) AND B(1));
end architecture;

 

RTL schematic of a 2-bit multiplier using dataflow modeling

RTL schematic of a 2-bit multiplier dataflow modeling.
RTL schematic of a 2-bit multiplier dataflow modeling.

 

Behavioral Modeling

As its name suggests, in this modeling, we define the behavior of the entity using sequential statements.

When we study different modeling styles one thing should be kept in mind that changes only occur in architecture where we specify the circuit. The entity remains the same for all modeling styles.

So we will talk only about the architecture here, the architecture of a 2-bit multiplier in behavioral style modeling is shown below.

We will start writing the architecture using architecture keyword and a label and then bind it to the entity and use begin keyword to write inside the architecture.

architecture behavioral of multiply_behav is 
begin

Then we start a process, it contains a set of instructions that will be executed sequentially, and if the program has multiple processes, then all processes will run concurrently.

process(A,B) is 
begin

Arguments passed to the process are called its sensitivity list. Like in process(A,B) , (A, B) is the sensitivity list, and whenever the value of either A or B changes, the process will be triggered, and all statements inside it will be executed. Here also begin keyword is used to start writing inside the process. process command is used in behavioral modeling.

Now we will use case statements in combination with if/else to construct the logics for a 2-bit binary multiplier.

We will look into one case only, and the rest are similar to write.

case A is
	when "00" =>
		if B="00" then P<="0000";
		elsif B="01" then P<="0000";
		elsif B="10" then P<="0000";
		else P<="0000";
		end if;

In the above code, we select ‘A’ as case, and when A=”00″ is true, we enter in its substatements where we use if-elsif conditional statements to generate output. For, eg. A=”00″ is fixed for this case, and if B=”00″, then the product should also be “0000” so we write that value to the output port. Similarly, we cover all values of B for all cases of A.

VHDL program of 2-bit multiplier using behavioral modeling

library ieee;
use ieee.std_logic_1164.all;

entity multiply_behav is 
  port (A, B : in bit_vector(1 downto 0);
        P : out bit_vector(3 downto 0)
    );
end multiply_behav;

architecture behavioral of multiply_behav is
begin
	process(A,B) is
	begin
		case A is
			when "00" =>
				if B="00" then P<="0000";
				elsif B="01" then P<="0000";
				elsif B="10" then P<="0000";
				else P<="0000";
				end if;
			when "01" =>
				if B="00" then P<="0000";
				elsif B="01" then P<="0001";
				elsif B="10" then P<="0010";
				else P<="0011";
				end if;
			when "10" =>
				if B="00" then P<="0000";
				elsif B="01" then P<="0010";
				elsif B="10" then P<="0100";
				else P<="0110";
				end if;
			when "11" =>
				if B="00" then P<="0000";
				elsif B="01" then P<="0011";
				elsif B="10" then P<="0110";
				else P<="1001";
				end if;
		end case;
end process;
end architecture;

 

RTL schematic of a 2-bit multiplier using behavioral modeling

RTL schematic of a 2-bit multiplier behavioral modeling.
RTL schematic of a 2-bit multiplier behavioral modeling.

Structural Modeling

In structural modeling, we describe the circuit by interconnections of individual components of the circuit.

Here also entity remains almost the same, but there is a small change

entity multiply_struct is 
port (A, B : in bit_vector(1 downto 0);
P : buffer bit_vector(3 downto 0)
);
end multiply_struct;

For an output port, instead of using out bit we have used buffer, this is because out bit cannot be read by the circuit that precedes it. We will explain it in detail while explaining the architecture. The rest of the entity is the same. Now let’s move to the architecture.

We start writing the architecture for the above entity in the same manner as before.

architecture structural of multiply_struct is

Now, look at the circuit once more.

Logic Circuit of multiplier(structural)
Logic Circuit of multiplier for structural modeling.

We need some AND gates and Half adders to realize the circuit. So now we define components that will be used in the architecture. Then we will declare the architecture of the multiplier and define the components using the component keyword in VHDL.

Entity of a program can be considered as a component in another program.

AND Gate

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity AND2 is
port(
A,B: in BIT;
x : out BIT);
end AND2;

architecture behavioral of AND2 is
begin 
x <= A and B;
end behavioral;

Half Adder

library ieee;
use ieee.std_logic_1164.all;

entity half_adder is 
port (a, b : in BIT;
sum, carry : out BIT
);
end half_adder;

architecture arch of half_adder is
begin
sum <= a xor b;
carry <= a and b;
end arch;

After declaring components’ entity-architecture pairs, we will declare the multiplier’s entity and architecture pair and declare the components.

entity multiply_struct is 
port (A, B : in bit_vector(1 downto 0); 
P : buffer bit_vector(3 downto 0) ); 
end multiply_struct; 

architecture structural of multiply_struct is 

component AND2 
port( A,B: in BIT; 
X : out BIT);
end component; 

component half_adder 
port (A, B : in BIT; 
sum, carry : out BIT); 
end component;

Now, we need to initialize some signals because, as we know that to interconnect components, we have to use signals.

We define four signals of bit type. signal S1,S2,S3,S4:BIT;

Now comes the part of the main architecture.

As usual, we start with begin keyword and instantiate the components using component instantiation statements.

And the components are interconnected through signals.

Let’s focus on one instantiation, and the rest of all are the same.

In the below line of code, A1 is the label of the instantiation and ‘AND2’ is the component that is called here. Then we use another keyword port map, which is used to bind the port/signal to the port of the component’s entity.

A1: AND2 port map(A(0),B(0),P(0));

Here, A(0), B(0), and P(0) are mapped to the Input1, Input2, and Output of the AND gate, respectively.

Let’s look at one more instantiation:

 A2: AND2 port map(A(1),B(0),S1);

Here we mapped A(1), B(0), S1 to the inputs and output of AND gate, but why we have used a signal(S1), can you guess?

This is because we had to connect the output of one component to the input of another component instead of the output port. So to carry it, we need a signal which is used for interconnections of components in structural modeling.

Now, according to our circuit, we can frame the following line of codes.

begin
A1: AND2 port map(A(0),B(0),P(0));
A2: AND2 port map(A(1),B(0),S1);
A3: AND2 port map(A(0),B(1),S2);
A4: AND2 port map(A(1),B(1),S3);
H1: half_adder port map(S1,S2,P(1),S4);
H2: half_adder port map(S4,S3,P(2),P(3));
end architecture;

Then we end the architecture, using end keyword.

VHDL program of 2-bit multiplier using structural modeling

library ieee;
use ieee.std_logic_1164.all;

entity AND2 is
port(
A,B: in BIT;
x : out BIT);
end AND2;

architecture behavioral of AND2 is
begin 
x <= A and B;
end behavioral;

entity half_adder is 
port (a, b : in BIT;
sum, carry : out BIT
);
end half_adder;

architecture arch of half_adder is
begin
sum <= a xor b;
carry <= a and b;
end arch;

entity multiply_struct is 
port (A, B : in bit_vector(1 downto 0);
P : buffer bit_vector(3 downto 0)
);
end multiply_struct;

architecture structural of multiply_struct is
component AND2
port(
A,B: in BIT;
X : out BIT);
end component;

component half_adder
port (A, B : in BIT;
sum, carry : out BIT);
end component;
signal S1,S2,S3,S4:BIT;

begin
A1: AND2 port map(A(0),B(0),P(0));
A2: AND2 port map(A(1),B(0),S1);
A3: AND2 port map(A(0),B(1),S2);
A4: AND2 port map(A(1),B(1),S3);
H1: half_adder port map(S1,S2,P(1),S4);
H2: half_adder port map(S4,S3,P(2),P(3));

end architecture;

 

RTL schematic of a 2-bit multiplier using structural modeling

RTL schematic of a 2-bit multiplier structural modeling.
RTL schematic of a 2-bit multiplier structural modeling.

Testbench

A testbench is a special VHDL program written to test the working of another VHDL program. It basically injects the provided values into its input ports and reads its output ports and shows as waveforms.

It has a similar structure as of a VHDL program but has a blank entity and uses an entity a component which is the entity of program under test.

Now, let’s write a testbench for our 2-bit multiplier. One thing you should understand and remember that testbench for all modeling styles is the same.

We start the testbench by including the necessary library, which is the same as the program under test.

library ieee;
use ieee.std_logic_1164.all;

 

Then we create a blank entity as testbench does not define actual hardware.

entity multiply_behav_tb is
end multiply_behav_tb;

 

Now we write the architecture of the testbench and before begin we declare the component and initialize signals.

architecture tb of multiply_behav_tb is

component multiply_behav is
         port (A, B : in bit_vector(1 downto 0);
         P : out bit_vector(3 downto 0)
);
end component;

signal A, B : bit_vector(1 downto 0);
signal P : bit_vector(3 downto 0);
begin

 

Then we map the ports of the testbench to the ports of the entity under test so that it can inject and read values from them.

UUT : multiply_behav port map (
A => A,
B => B,
P => P);

 

Then we start a process,  and give it a label(‘Force’ in this case) and we define a constant time period to use later for delays, and begin the process.

Force:process
constant period: time := 20 ns;
begin

 

Now we can finally inject values to inputs. Generally, we try to give all possible input combinations, here we do the same. And after every input, we provide a delay.

A <= "00";
B <= "00";
wait for period;

A <= "00";
B <= "01";
wait for period;

A <= "00";
B <= "10";
wait for period;

A <= "00";
B <= "11";
wait for period;

A <= "01";
B <= "00";
wait for period;

A <= "01";
B <= "01";
wait for period;

A <= "01";
B <= "10";
wait for period;

A <= "01";
B <= "11";
wait for period;

A <= "10";
B <= "00";
wait for period;

A <= "10";
B <= "01";
wait for period;

A <= "10";
B <= "10";
wait for period;

A <= "10";
B <= "11";
wait for period;

A <= "11";
B <= "00";
wait for period;

A <= "11";
B <= "01";
wait for period;

A <= "11";
B <= "10";
wait for period;

A <= "11";
B <= "11";
wait for period;

 

Then we use a wait statement to terminate the process and end process to kill it, and one more end to finish the architecture.

wait;
end process;
end tb;

 

Full testbench code for the 2-bit multiplier

library ieee;
use ieee.std_logic_1164.all;

entity multiply_behav_tb is
end multiply_behav_tb;

architecture tb of multiply_behav_tb is

component multiply_behav is
port (A, B : in bit_vector(1 downto 0);
P : out bit_vector(3 downto 0)
);
end component;
signal A, B : bit_vector(1 downto 0);
signal P : bit_vector(3 downto 0);
begin
UUT : multiply_behav port map (
A => A,
B => B,
P => P);


Force:process
constant period: time := 20 ns;
begin
A <= "00";
B <= "00";
wait for period;

A <= "00";
B <= "01";
wait for period;

A <= "00";
B <= "10";
wait for period;

A <= "00";
B <= "11";
wait for period;

A <= "01";
B <= "00";
wait for period;

A <= "01";
B <= "01";
wait for period;

A <= "01";
B <= "10";
wait for period;

A <= "01";
B <= "11";
wait for period;

A <= "10";
B <= "00";
wait for period;

A <= "10";
B <= "01";
wait for period;

A <= "10";
B <= "10";
wait for period;

A <= "10";
B <= "11";
wait for period;

A <= "11";
B <= "00";
wait for period;

A <= "11";
B <= "01";
wait for period;

A <= "11";
B <= "10";
wait for period;

A <= "11";
B <= "11";
wait for period;
wait;
end process;
end tb;

 

Simulation Result (waveform)

Waveform of 2-bit multiplier.
Waveform result of the 2-bit multiplier.

As always, if you have any queries, we would love to address them. Just drop in a comment in the comments section below.

Leave a Reply

Your email address will not be published. Required fields are marked *