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?
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.