----------------------------------------------------------------------------
-- ECE 501
-- Homework Assignment 4
-- smult_1.VHD
-- smult_1, Booth's Algorithm
-- serial multiplier (signed)
-- Jordan Christman
----------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;

entity smult_1 is
generic (
	input_size		: integer := 8);
port (
	product			: out signed(2 * input_size - 1 downto 0);
	data_ready		: out std_logic;
	input_1			: in signed(input_size - 1 downto 0);
	input_2			: in signed(input_size - 1 downto 0);
	start			: in std_logic;
	reset			: in std_logic;
	clk				: in std_logic);
end smult_1;

----------------------------------------------------------------------------
--
-- 								BEHAVIOR
--
----------------------------------------------------------------------------
architecture behavior of smult_1 is
-- State Machine states
type state_type is(init, load_state, right_shift, done);
signal state, nxt_state 	: state_type;

-- Control signals
signal shift		: std_logic;
signal add_A		: std_logic;
signal add_S		: std_logic;
signal load			: std_logic;

-- Data Signals
constant maxcount	: integer := input_size - 1;
signal A_reg 		: signed((2*input_size) downto 0) := (others => '0');
signal S_reg 		: signed((2*input_size) downto 0) := (others => '0');
signal P_reg 		: signed((2*input_size) downto 0) := (others => '0');
signal sum_S		: signed((2*input_size) downto 0) := (others => '0');
signal sum_A		: signed((2*input_size) downto 0) := (others => '0');
signal count 		: integer range 0 to maxcount + 1 := 0;

signal start_count_lead		: std_logic := '0';
signal start_count_follow	: std_logic := '0';
signal start_count			: std_logic := '0';

begin
-----------------------------------------
--			STATE MACHINE
--     		(Two Process)
--
-- This state machine is used to determine
-- what state smult_1 is in based on then
-- count value and the LSB's of the P 
-- register
-----------------------------------------
state_proc: process(clk)
begin
	if rising_edge(clk) then
		if(reset = '0') then
			state <= init;
		else
			state <= nxt_state;
		end if;
	end if;
end process state_proc;

state_machine: process(state, start, start_count, count, P_reg(1 downto 0))
begin
-- Initialize nxt_state and control signals
nxt_state <= state;
shift <= '0';
add_A <= '0';
add_S <= '0';
load <= '0';
data_ready <= '0';

case state is
	-- Initialization State
	when init =>
		if(start_count = '1') then
			nxt_state <= load_state;
		else
			nxt_state <= init;
		end if;
		
	-- Loading State	
	when load_state =>
		load <= '1';
		nxt_state <= right_shift;
	
	-- Right Shift state (Multiplication is occurring)
	when right_shift =>
		shift <= '1';
		if(count /= maxcount) then
			nxt_state <= right_shift;
		else
			nxt_state <= done;
		end if;
		
		-- Read 2 LSB's of P_reg
		if(P_reg(1 downto 0) = "01") then
			add_A <= '1';
		elsif(P_reg(1 downto 0) = "10") then
			add_S <= '1';
		end if;

	-- Multiplication is complete (ready to receive new inputs)
	when done =>
		data_ready <= '1';
		if(start = '0') then
			nxt_state <= init;
		else
			nxt_state <= done;
		end if;
	
	-- All other states
	when others =>
		nxt_state <= init;
		
	end case;
end process state_machine;

-----------------------------------------
-- 			EDGE DETECTION
--
-- This is used to detect a rising edge of
-- a signal
-----------------------------------------
start_count <= start_count_lead and (not start_count_follow);

start_count_proc: process(clk)
begin
	if(rising_edge(clk)) then
		if(reset = '0') then
			start_count_lead <= '0';
			start_count_follow <= '0';
		else
			start_count_lead <= start;
			start_count_follow <= start_count_lead;
		end if;
	end if;
end process start_count_proc;

-----------------------------------------
--			 COUNT PROCESS
--
-- This process is a counter that keeps
-- track of the number of cycles iterated
-- in the state machine 
-----------------------------------------
count_proc: process(clk)
begin
	if(rising_edge(clk)) then
		if((start_count = '1') or (reset = '0')) then
			count <= 0;
		elsif(state = right_shift) then
			count <= count + 1;
		end if;
	end if;
end process count_proc;

-----------------------------------------
--		MULTIPLIER PROCESS
--
-- This process is used to apply the 
-- actual multiplication via shifts
-- and additions
-----------------------------------------
-- Determine the Sum of S_reg and A_reg
sum_S <= P_reg + S_reg;
sum_A <= P_reg + A_reg;

mult_proc: process(clk)
begin
	if(rising_edge(clk)) then
		if(reset = '0') then
			P_reg <= (others => '0');
			A_reg <= (others => '0');
			S_reg <= (others => '0');
			
		elsif(load = '1') then
			-- A_reg
			A_reg(2*input_size downto input_size + 1) <= input_1;
			A_reg(input_size downto 0) <= (others => '0');
			
			-- S_reg
			S_reg(2*input_size downto input_size + 1) <= (not input_1) + 1;
			S_reg(input_size downto 0) <= (others => '0');
			
			-- P_reg
			P_reg(2*input_size downto input_size + 1) <= (others => '0');
			P_reg(input_size downto 1) <= input_2;
			P_reg(0) <= '0';
			
		elsif(add_A = '1') then
			P_reg <= sum_A(2*input_size) & sum_A(2*input_size downto 1);
			
		elsif(add_S = '1') then
			P_reg <= sum_S(2*input_size) & sum_S(2*input_size downto 1);
		
		elsif(shift = '1') then
			P_reg <= P_reg(2*input_size) & P_reg(2*input_size downto 1);
		
		end if;
	end if;
end process mult_proc;

-- Defining the output
product <= P_reg(2*input_size downto 1);
end behavior;