Search code examples
vhdlmultiplication

VHDL NxM -bit parallel block multiplier


I am trying to create schematics for a NxM -bit parallel block multiplier using generic parameters to define the size of the multiplier, and generate statements to describe the internal structure. I am having some trouble with my sums and carries and I am not able to implement what I want to do in VHDL code.

Here is what I got so far:

1-bit multiplier:

library IEEE;  
use IEEE.std_logic_1164.all;  
use IEEE.std_logic_arith.all;  
use ieee.numeric_std.all;  
use ieee.std_logic_unsigned.all;

entity mult is  
 port (  
  a     :  in std_logic;  
  b     :  in std_logic;  
  Sin   :  in std_logic;        --sum-in  
  Cin   :  in std_logic;        --carry-in  
  Sout  :  out std_logic;       --sum-out  
  Cout  :  out std_logic        --carry-out  
);  
end mult;  

architecture mult of mult is  
  begin  

    Sout <= '1' when a = '0' and b = '0' and Sin = '0' and Cin = '1' else
            '1' when a = '0' and b = '0' and Sin = '1' and Cin = '0' else
            '1' when a = '0' and b = '1' and Sin = '1' and Cin = '0' else
            '1' when a = '0' and b = '1' and Sin = '0' and Cin = '1' else
            '1' when a = '1' and b = '0' and Sin = '0' and Cin = '1' else
            '1' when a = '1' and b = '0' and Sin = '1' and Cin = '0' else
            '1' when a = '1' and b = '1' and Sin = '0' and Cin = '0' else
            '1' when a = '1' and b = '1' and Sin = '1' and Cin = '1' else
            '0';
    Cout <= '1' when a = '0' and b = '0' and Sin = '1' and Cin = '1' else
            '1' when a = '0' and b = '1' and Sin = '1' and Cin = '1' else
            '1' when a = '1' and b = '0' and Sin = '1' and Cin = '1' else
            '1' when a = '1' and b = '1' and Sin = '0' and Cin = '1' else
            '1' when a = '1' and b = '1' and Sin = '1' and Cin = '0' else
            '1' when a = '1' and b = '1' and Sin = '1' and Cin = '1' else
            '0';

end mult;

Used it as a component in an NxM multiplier:

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use ieee.numeric_std.all;
use ieee.std_logic_unsigned.all;

entity generic_mult is
        generic (bits: integer);
 port (
  a     :  in std_logic_vector(bits-1 downto 0);
  b     :  in std_logic_vector(bits-1 downto 0);
  answer:  out std_logic_vector(bits*2-1 downto 0)      --sum-out
  );
end entity generic_mult;

architecture behavioral of generic_mult is

 component mult is
  port (
    a    :  in std_logic;
    b    :  in std_logic;
    Sin  :  in std_logic;       --sum-in
    Cin  :  in std_logic;       --carry-in
    Sout :  out std_logic;      --sum-out
    Cout :  out std_logic       --carry-out
  );
 end component;

        type mem_word  is array (0 to bits) of std_logic_vector(bits downto 0);

        signal  carry_internal : mem_word;
        signal  sum_internal : mem_word;

  begin
    this_is_label: for N in 1 to bits generate --Im sorry, my labels are horrible :(
        this_is_label3: for M in 0 to bits-1 generate

            this_is_label2: mult
          port map (
            a => a(N-1),
            b => b(M),
            Cin => carry_internal(M)(N),
            Cout => carry_internal(M+1)(N),
            Sin => sum_internal(M)(N),
            Sout => sum_internal(M+1)(N-1)
        );
    end generate;
end generate;
    labellll: for N in 0 to bits-1 generate
        sum_internal(N+1)(N) <= carry_internal(N)(N);
    carry_internal(0) <= (others => '0');
    sum_internal(0)   <= (others => '0');

    answer(bits*2-1) <= carry_internal(bits)(bits);
    answer(bits downto 0) <= sum_internal(bits);
    end generate;
end behavioral;

And a testbench for it:

library IEEE;
use IEEE.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_unsigned.all;

entity NM_mult_tb is
end NM_mult_tb;

architecture behavioral of NM_mult_tb is
    component generic_mult

        generic (bits: integer := 4);
 port (
  a     :  in std_logic_vector(bits-1 downto 0);
  b     :  in std_logic_vector(bits-1 downto 0);
  answer:  out std_logic_vector(bits*2-1 downto 0)      --sum-out
  );
    end component;
   --declaring inputs and initializing them
        signal a  :  std_logic_vector(3 downto 0) :="0101";
        signal b  :  std_logic_vector(3 downto 0) :="1010";
        signal Sin:  std_logic_vector(3 downto 0) :="0000";
        signal Cin:  std_logic := '0';
   --declaring outputs and initializing them
        signal answer :  std_logic_vector(7 downto 0);  --sum-out
        signal correct:  std_logic;                     --carry-out

BEGIN
    -- Instantiating the Design Under Test (DUT)
   dut: generic_mult
        GENERIC MAP (4)
        PORT MAP (
          a => a,
          b => b,
         answer => answer
        );

   -- Stimulus process
    correct <= '1' when to_integer(unsigned(a))*to_integer(unsigned(b)) = 
to_integer(unsigned(answer)) else '0';
  stim_proc: process
   begin
        wait for 1 ns;
        a <= a + 1;
        if a = "0" then b <= b + 1; end if;
  end process;

END;

When I simulate it I see that there is something wrong with carry-ins and sum-ins and outs and my answer has undefined bits in it:

https://i.sstatic.net/y4ED9.png

If you have read my post this far, thank you very much for your attention. If someone could find the time to help me out with my problem I would be very grateful!

Sincerely,
A confused VHDL beginner


Solution

  • Libraries

    So, for doing arithmetic you only need one arithmetic library: numeric_std. Don't use std_logic_arith because it is not standardized, so behavior can differ between implementations.

    Then std_logic_unsigned could be risky. Jim Lewis explains here that its could be a good idea to use it, but I think that you should not. Better to use integer, unsigned and signed data types for all comparisons.


    Entity mult

    What you are implementing is lookup-tables. Combinatorial logic statements. This is not arithmetic, so no arithmetic libraries are needed.

    However, you could simplify the code. Let's take this description ar reference.

    The 1x1 multiplier can be defined using logic operators (based on a full adder described here):

    architecture rtl of mult is
        signal FA_a, FA_b : std_logic;
    begin
        FA_a <= Sin;
        FA_b <= a AND b;
        Sout <= FA_a XOR FA_b XOR Cin;
        Cout <= (FA_a AND FA_b) OR (Cin AND (FA_a XOR FA_b));
    end architecture;
    

    Or just skip this all and use the addition operator, which does the logic for you.

    architecture rtl of mult is
        use ieee.numeric_std.all;
        signal FA_a, FA_b, FA_Cin : unsigned(1 downto 0) := (others => '0');
        signal FA_out : unsigned(1 downto 0);
    begin
        FA_a(0) <= Sin;
        FA_b(0) <= a AND b;
        FA_Cin(0) <= Cin;
        FA_out <= FA_a + FA_b + FA_Cin;
        Sout <= FA_out(0);
        Cout <= FA_out(1);
    end architecture;
    

    Entity generic_mult

    First off, your multiplier is MxM, not MxN. Next you complain about your labels in the comments... would take just as much effort to change them ;).

    Regarding your implementation, you are assigning signals multiple times in the labellll generate block. It should be:

    labellll: for N in 0 to bits-1 generate
        sum_internal(N+1)(N) <= carry_internal(N)(N);
    end generate;
    
    carry_internal(0) <= (others => '0');
    sum_internal(0)   <= (others => '0');
    
    answer(bits*2-1) <= carry_internal(bits)(bits);
    answer(bits downto 0) <= sum_internal(bits);
    

    but this is not the biggest problem, as it can be resolved. The problem you do have, has to do with multiple drivers. In this_is_label2 you have the line:

    Sout => sum_internal(M+1)(N-1)
    

    And in labellll you have the line:

    sum_internal(N+1)(N) <= carry_internal(N)(N);
    

    Two statements, which are both assigning a value to sum_internal. If both 1 and 0 are assigned, this will resolve to X.

    But this is not necesary, because it is the result of an error in your design. Please see my link again to see how you should implement the multiplier. I'm not going to do it for you (it's not that difficult and probably your homework. You don't learn anything if other people do your homework for you. ;) )


    Testbench

    You're combining unsigned arithmetic (to_integer(unsigned(a))*to_integer(unsigned(b))) with std_logic_vector arithmetic (a <= a + 1;). Don't do that. Preferably only use unsigned.


    using the multiplication operator

    As stated by pev.hall, you could just simplify everything and use the multiplication operator from the numeric_std package.

    entity generic_mult is
        generic (
            M: positive;
            N: positive
            );
        port (
            a : in std_logic_vector(M-1 downto 0);
            b : in std_logic_vector(N-1 downto 0);
            answer : out std_logic_vector(M+N-1 downto 0)
        );
    end entity;
    
    architecture rtl of generic_mult is
        use ieee.numeric_std.all;
    begin
        answer <= std_logic_vector(unsigned(a)*unsigned(b));
    end architecture;