----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date: 04/13/2021 05:50:38 PM
-- Design Name: 
-- Module Name: OutConverter - Behavioral
-- Project Name: 
-- Target Devices: 
-- Tool Versions: 
-- Description: 
--  This file implement the output logic to convert the FP IFFT output back into a binary sequence.
--  It perform a multiplication and a a modulo operation. 
-- Dependencies: 
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
-- 
----------------------------------------------------------------------------------

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

entity OutConverter is
generic (SAMPLES_LOG2: integer);
port
(
    Clk: in std_logic; Rst_n: in std_logic;
    in_data: in std_logic_vector(32 downto 0);
    out_data_bin: out std_logic;
	out_data_int: out std_logic_vector((3+SAMPLES_LOG2)-1 downto 0) 
	       := (others => '0')
);
end OutConverter;

architecture Behavioral of OutConverter is
   
--
-- NOTE: This is how the modulw work, we receive the inputs from a unscaled IFTT so that 
-- we need, to obtain the integer and frac part first obtain the unormalized FP 
-- number. To do so we have to multiply the in_data by NUM_SAPLES that is equivalent
-- to shift left by SAMPLES_LOG2 position the in_data. We first extend the input data 
-- by SAMPLES_LOG2 bits to avoid losing integer part information. We than shift and divide 
-- the result value into it's integer and fration part. Adter the shifting the number have 
-- the decimal point still in the same position so it is in the format Q(33+SAMPLES_LOG2).30.
-- We than need to round the integer part using some rounding methos. We use round to the even
-- so we look at the first bit of the frac part and is it is 1 we have a number Z= 0.5 so that 
-- we add one to the integer part. Finally we need to obtain the binary of this number and to do
-- so we need to compute out_rd % 2 that is equivalent to look at the last bit of the result vector.
--  
    constant IN_WIDTH: integer := 33;
	constant EXT_IN_WIDTH: integer := IN_WIDTH + SAMPLES_LOG2;
	
    constant OUT_INT_WIDTH: integer := SAMPLES_LOG2+3;
	constant OUT_FRAC_WIDTH: integer := IN_WIDTH - 3;
	
	
	signal out_rd: std_logic_vector(OUT_INT_WIDTH-1 downto 0);
    signal out_int_part: std_logic_vector(OUT_INT_WIDTH-1 downto 0);
    signal out_frac_part: std_logic_vector(OUT_FRAC_WIDTH-1 downto 0);
    
	constant NULL_BITS: std_logic_vector(SAMPLES_LOG2-1 downto 0) := (others => '0');
		
	constant SLACK_BITS: integer := 2;	
		
	signal in_data_exp: std_logic_vector(EXT_IN_WIDTH-1 downto 0);	
	signal in_data_shift: std_logic_vector((IN_WIDTH+SAMPLES_LOG2)-1 downto 0);

    signal debug_value_rd: std_logic_vector(OUT_INT_WIDTH-1 downto 0);
    signal debug_data_out_int:  std_logic_vector(OUT_INT_WIDTH-1 downto 0);
    signal debug_data_out_frac: std_logic_vector(OUT_FRAC_WIDTH-1 downto 0);
	signal debug_data_in_shift: std_logic_vector(EXT_IN_WIDTH-1 downto 0);	
begin

-- Shift by SAMPLES_LOG2 to remove the 1/N of the IFFT
	in_data_exp <= NULL_BITS & in_data;
	in_data_shift <= std_logic_vector(shift_left(signed(in_data_exp), (SAMPLES_LOG2)));

-- Extract the the fractional and the integer part of the fixed point number. 
   out_int_part <= in_data_shift(EXT_IN_WIDTH-1 downto OUT_FRAC_WIDTH); -- 30
   out_frac_part <= in_data_shift(OUT_FRAC_WIDTH-1 downto 0);

-- Round the result, using floor method, to take in consideration the fractional part of it.
-- We just look at the first bit after the decimal. We have split the FP number into
-- two numbers one integer and frac part. In the form X.Y.
   out_rd <= std_logic_vector(unsigned(out_int_part) + 1) when out_frac_part(OUT_FRAC_WIDTH-2)='1' else
             out_int_part;

-- Obtain the bit result by take the modulo two of the result that is equivalent to look at the 
-- first bit of the integer part. 
   out_data_bin <= out_rd(0); 
   out_data_int <= out_rd;
   	
-- DEBUG Signals out
   debug_value_rd      <= out_rd;
   debug_data_out_int  <= out_int_part;
   debug_data_out_frac <= out_frac_part; 
   debug_data_in_shift  <= in_data_shift; 

end Behavioral;
