0

I made a simple multiplication unit (16-bit operands, 32-bit result) based on the "shift-left, add" principle, and it does not work properly in the adder part when the overflow bit needs to be considered.

I have an idea what should be done (make a 17-bit sum vector and add an overflow bit to the product), but I don't know how to implement it. Below you can find the waveform file where I am showing the error location and the MPU.vhd file. The screenshot does not show the final result, but it is obvious that it will be wrong. How can I get it to work with large 16-bit numbers?

Place where the overflow bit is skipped

MPU.vhd

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

entity MPU_OP is
    port (C: in std_logic;    -- Clock
    RST : in std_logic;       -- Reset
    LAB : in std_logic;       -- Load A and B, P <= 0
    SHIFT : in std_logic;     -- Shift B and P
    OUTHL : in std_logic;     -- Output most(0) or least(1) significant word P
    DA : in std_logic_vector(15 downto 0);   -- Data A
    DB : in std_logic_vector(15 downto 0);   -- Data B
    DP : out std_logic_vector(15 downto 0)); -- Word result P
end entity;

architecture BEH of MPU_OP is
signal A, B : std_logic_vector(15 downto 0); -- A, B
signal Pi, Ai, Si : integer;
signal S : signed(15 downto 0); -- Sum 
signal S2 : signed(16 downto 0);
signal P : signed(31 downto 0); -- Product

begin 
    RG_A : process(C, RST) -- Register for A
    begin
        if (RST = '1') then
            A <= X"0000";
        elsif rising_edge(C) then                                        `-- Reg_A`
            if LAB = '1' then
                A <= DA;
            end if;
        end if;
    end process;
    
    RG_B : process(C, RST) -- Register for B
    begin
        if (RST = '1') then
            B <= X"0000";
        elsif rising_edge(C) then
            if LAB = '1' then
                B <= DB;
            elsif (SHIFT = '1') then
                B <= std_logic_vector(shift_left(unsigned(B), 1));       `-- Reg_B`
            end if;
        end if;
    end process;    
    
    Adder : process (C)
    begin
        Pi <= to_integer(unsigned(std_logic_vector(P(15 downto 0))));
        Ai <= to_integer(unsigned(A));

        if B(15) = '1' then
            S <= P(15 downto 0) + signed(A);                             -- Adder
            Si <= Pi + Ai;
        else
            S <= P(15 downto 0);    
            Si <= Pi;
        S2 <= to_signed(Si, S2'length); 
        end if;
    end process;

    
    RG_P : process (C, RST, P) -- Register for P   
    begin
        if RST = '1' then 
            P <= X"00000000";
        elsif rising_edge(C) then
            if LAB = '1' then
                P <= X"00000000";
            elsif SHIFT = '1' then
                if B /= "1000000000000000" then
                    P <= P(30 downto 16) & S & '0'; -- Shift LEFT        `-- Reg_Product`
                else
                    P <= P(31 downto 16) & S;
                end if;
            end if;
        end if;
    end process;
    
    MUX_P : DP <= std_logic_vector(P(15 downto 0)) when OUTHL = '1' else
        std_logic_vector(P(31 downto 16));
    
end architecture;
        

Below is the minimal code to reproduce the above problem and the testbench for it.

MPU.vhd

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

entity MPU_TEST is
    port (C: in std_logic;
    LAB : in std_logic;
    SHIFT : in std_logic;
    DA : in std_logic_vector(15 downto 0);
    DB : in std_logic_vector(15 downto 0);
    DP : out std_logic_vector(31 downto 0));
end entity;

architecture BEH of MPU_TEST is
signal A, B : std_logic_vector(15 downto 0);
signal S : signed(15 downto 0);
signal P : signed(31 downto 0); 

begin 
    RG_A : process(C)
    begin
        if rising_edge(C) then
            if LAB = '1' then
                A <= DA;
            end if;
        end if;
    end process;
    
    RG_B : process(C) 
    begin 
        if rising_edge(C) then
            if LAB = '1' then
                B <= DB;
            elsif (SHIFT = '1') then
                B <= std_logic_vector(shift_left(unsigned(B), 1));
            end if;
        end if;
    end process;    
    
    Adder : process (C)
    begin
        if B(15) = '1' then
            S <= P(15 downto 0) + signed(A);
        else
            S <= P(15 downto 0);    
        end if;
    end process;

    
    RG_P : process (C, P) 
    begin 
        if rising_edge(C) then
            if LAB = '1' then
                P <= X"00000000";
            elsif SHIFT = '1' then
                if B /= "1000000000000000" then
                    P <= P(30 downto 16) & S & '0';
                else
                    P <= P(31 downto 16) & S;
                end if;
            end if;
        end if;
    end process;
    
    DP <= std_logic_vector(P);
end architecture;
            

MPU_TB.vhd

library ieee;
use ieee.NUMERIC_STD.all;
use ieee.std_logic_1164.all;

entity mpu_test_tb is
end mpu_test_tb;

architecture TB_ARCHITECTURE of mpu_test_tb is
    component mpu_test
    port(
        C : in STD_LOGIC;
        LAB : in STD_LOGIC;
        SHIFT : in STD_LOGIC;
        DA : in STD_LOGIC_VECTOR(15 downto 0);
        DB : in STD_LOGIC_VECTOR(15 downto 0);
        DP : out STD_LOGIC_VECTOR(31 downto 0) );
    end component;


    signal C : STD_LOGIC;
    signal LAB : STD_LOGIC;
    signal SHIFT : STD_LOGIC;
    signal DA : STD_LOGIC_VECTOR(15 downto 0);
    signal DB : STD_LOGIC_VECTOR(15 downto 0);

    signal DP : STD_LOGIC_VECTOR(31 downto 0);


begin


    UUT : mpu_test
        port map (
            C => C,
            LAB => LAB,
            SHIFT => SHIFT,
            DA => DA,
            DB => DB,
            DP => DP
        );

    process
    begin 
        C <= '0';
        wait for 5 ns;
        C <= '1';
        wait for 5 ns;
    end process;
    
    LAB <= '1', '0' after 10 ns;
    SHIFT <= '0', '1' after 10 ns, '0' after 170 ns;
    DA <= X"1234";
    DB <= X"F234";
    
end TB_ARCHITECTURE;

And a new signal with an incorrect multiplication result. The error occurs in adder.

enter image description here

mkrieger1
  • 19,194
  • 5
  • 54
  • 65

1 Answers1

0

I managed to get it to work. I explicitly added "0" when shifting the product and added a 17th bit to S to keep the overflow bit. Also, the signal ranges have been changed, which should be stored in the product and sum. Ended up with the code below.

MPU.vhd

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

entity MPU_TEST is
    port (C: in std_logic;
    LAB : in std_logic;
    SHIFT : in std_logic;
    DA : in std_logic_vector(15 downto 0);
    DB : in std_logic_vector(15 downto 0);
    DP : out std_logic_vector(31 downto 0));
end entity;

architecture BEH of MPU_TEST is
signal A, B : std_logic_vector(15 downto 0);
signal S : signed(16 downto 0);
signal P : signed(31 downto 0); 

begin 
    RG_A : process(C)
    begin
        if rising_edge(C) then
            if LAB = '1' then
                A <= DA;
            end if;
        end if;
    end process;
    
    RG_B : process(C) 
    begin 
        if rising_edge(C) then
            if LAB = '1' then
                B <= DB;
            elsif (SHIFT = '1') then
                B <= std_logic_vector(shift_left(unsigned(B), 1));
            end if;
        end if;
    end process;    
    
    Adder : process (C)
    begin
        if B(15) = '1' then
            S <= P(16 downto 0) + signed(A);
        else
            S <= P(16 downto 0);    
        end if;
    end process;

    
    RG_P : process (C, P) 
    begin 
        if rising_edge(C) then
            if LAB = '1' then
                P <= X"00000000";
            elsif SHIFT = '1' then
                if B /= "1000000000000000" then
                    P <= P(30 downto 17) & S & '0';
                else
                    P <= P(31 downto 17) & S;
                end if;
            end if;
        end if;
    end process;
    
    DP <= std_logic_vector(P);
end architecture;
    

Simulation screenshots of the correct waveforms First example Second example