4 Bit Binary Multiplier in VHDL

It's been quite some time since I last posted. Amidst this crazy pandemic, I found some time to blog. 
This post is about simple VHDL code to model a 4-Bit Binary multiplier.
There are two ways to write a code for this-

  1. Structural modeling (using Dataflow modeling)
  2. Behavioral modeling
The first approach requires you to understand the logic diagram of a 4-bit binary multiplier and requires gate-to-gate mapping which is a tedious task. The latter is a rather smart way to code wherein you let the software choose the right gates and components, while you write a general code.

Solved: For a binary multiplier that multiplies two unsigned fo ...

This is the diagram I'm talking about. Needless to say, it makes sense to write a code that doesn't use this diagram i.e behavioral modeling.

To start with here's a basic code that does exactly what we want. 

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity multiplier_4bit is
   port
   (
      num1, num2: in std_logic_vector(3 downto 0);
      ans: out std_logic_vector(7 downto 0)
   );
end entity multiplier_4bit;

architecture Behavioral of multiplier_4bit is
begin

   ans <= std_logic_vector(unsigned(num1) * unsigned(num2));

end architecture Behavioral;

It doesn't get easier than this. The code is pretty compact and simple, all thanks to one library we've imported- IEEE.NUMERIC_STD
This library provides one powerful datatype called 'unsigned', which is similar to std_logic_vector and contains several overloaded definitions for addition, subtraction, multiplication etc between two unsigned numbers.

ans <= std_logic_vector(unsigned(num1) * unsigned(num2));
This is the line where we unleash(!) the power of the unsigned datatype by typecasting the two std_logic_vector inputs to unsigned. We multiply the two unsigned numbers and then again convert the result to std_logic_vector datatype.
 

But for such a task importing an entire library may be an overkill, so can we get rid of 'unsigned' and the library itself and write our own functions instead?
Yep, we can, and it's easier done than said. Here's the code to achieve the same functionality without using a library- 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity multiplier_4bit is
   port
   (
      num1, num2: in std_logic_vector(3 downto 0);
      ans: out std_logic_vector(7 downto 0)
   );
end entity multiplier_4bit;

architecture Behavioral of multiplier_4bit is

----------------------------------------------------------------------------------
-----------------------------------Functions------------------------------------------------
-- The function that converts vector to int
function vector_to_int (A : std_logic_vector) return integer is variable answer : integer;
  begin
    answer := 0;
    
    for I in 0 to A'length-1 loop
    if (A(I) = '1') then 
        answer:= answer + (2 ** I);
    else
    next;
      
    end if;  
    end loop;

    return answer;
end vector_to_int;
 
  
  --function to convert to int to vector
  function int_to_vector (A : integer) return std_logic_vector is variable answer : std_logic_vector(7 downto 0);
  variable dividend : integer :=A;
  variable remainder : integer :=0;
  begin
  answer:= "00000000";

  for index in 0 to 7 loop
  remainder := dividend mod 2;
  
  if (remainder = 1) then
  answer(index) := '1';
  else
  answer(index) := '0';
  end if;
  
  dividend:= dividend - remainder;
  dividend := dividend/2;
  end loop;
  return answer;
  end int_to_vector;
---------------------------------------------------------------------------------
----------------------------end of function declarations-------------------------
begin
   
   ans <= std_logic_vector(int_to_vector(vector_to_int(num1) * vector_to_int(num2)));

end architecture Behavioral;


Notice that, we're no longer using the NUMERIC_STD library and if you remove the code between the dashes(------), it's almost the same as the previous code, just that we've created two new functions-

  1. vector_to_int (std_logic_vector) --> Converts a std_logic_vector to integer.
  2. int_to_vector (integer) --> Converts an integer to an 8-bit std_logic_vector.

 In VHDL functions are written in the declarative part of the architecture (which is the part of the code enclosed between lots of dashes ---------).
Lets quickly look at how the functions work.

The first one that converts a std_logic_vector to an integer, follows the basic method in which you convert a binary number to decimal.
JavaScript Math: Convert a binary number to a decimal number ...


The function to do this is pretty straightforward and follows the explanation above. Convince yourself that the function below is doing exactly what the image above explains.

function vector_to_int (A : std_logic_vector) return integer is variable answer : integer;
  begin
    answer := 0;
    
    for I in 0 to A'length-1 loop
    if (A(I) = '1') then 
        answer:= answer + (2 ** I);
    else
    next;
      
    end if;  
    end loop;

    return answer;
end vector_to_int;
 

Now, the second function that converts an integer to std_logic_vector follows the repeated- remainder method to convert decimal to binary.

Decimal to Binary Conversion Methods - Examples & Explanation ...
This is the exact concept that is implemented into the code-

  function int_to_vector (A : integer) return std_logic_vector is variable answer : std_logic_vector(7 downto 0);
  variable dividend : integer :=A;
  variable remainder : integer :=0;
  begin
  answer:= "00000000";

  for index in 0 to 7 loop
  remainder := dividend mod 2;
  
  if (remainder = 1) then
  answer(index) := '1';
  else
  answer(index) := '0';
  end if;
  
  dividend:= dividend - remainder;
  dividend := dividend/2;
  end loop;
  return answer;
  end int_to_vector;

That's all for the functions!


ans <= std_logic_vector(int_to_vector(vector_to_int(num1) * vector_to_int(num2)));
Finally, it boils down to calling the two functions correctly to multiply two numbers.

By replacing the '*' operator to '+', '-' or a series of operations in the line above, we can easily modify the code to perform addition, subtraction, etc.


The code worked perfectly when tested using a testbench simulation.
Here's the code for a simple testbench that prints the multiplication table of 5.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;

entity multiplier_4bit_tb is
--  Port ( );
end multiplier_4bit_tb;

architecture Behavioral of multiplier_4bit_tb is
component multiplier_4bit
port( 
num1, num2: in std_logic_vector(3 downto 0);
      ans: out std_logic_vector(7 downto 0)
);
end component;

signal num1 : std_logic_vector(3 downto 0);
signal num2 : std_logic_vector(3 downto 0);
signal ans : std_logic_vector(7 downto 0);
begin
utt: multiplier_4bit port map(
num1=>num1,
num2=>num2,
ans=>ans
);
stimulus: process
begin
 
 for v in 1 to 10 loop
 num1<= std_logic_vector(to_unsigned(5,4));
 num2<= std_logic_vector(to_unsigned(v,4));
 wait for 10 ns;
 end loop;
 
 wait;
end process;

end Behavioral;


I hope I've done justice in explaining my approach to code a 4-bit multiplier!

Cheers,
Siddharth

Comments