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

-- Only used for Compile time operation, this will be
-- synthetizable 
use ieee.math_real.all;

library work;
use work.Utils.all;


entity BPadder is 
generic (
	constant D_V: integer := 29;
	constant P_VAL: integer := 11
--	constant P_VAL_EXT: integer   := 32
);
port (
	Clk, Rst_n: in  std_logic;
	
	start_in: in std_logic; -- Start the sequence of bit

	out_bin: out std_logic;
	out_last: out std_logic;
	out_valid: out std_logic;
-- When one the circuit stop the output sequence.
	out_stop: in std_logic; 

    done: out std_logic;
	
-- 	Position memory
	mem_in_en:   out std_logic; -- Enable signal for the memory
	mem_in_adr:  out std_logic_vector(my_log2(D_V)-1 downto 0);
	mem_in_data: in  std_logic_vector(my_log2(P_VAL)-1 downto 0)
);
end BPadder;

architecture Behavioral of BPadder is
-- Constant depending only on the generic passed to the module! If VHDL compiler is trivial
-- this have to be generated at compile time only? 
    constant P_VAL_EXT: integer := next_pow2(2*P_VAL - 3)-1;
	constant LEFT_BITS: integer := P_VAL_EXT mod P_VAL; -- my_mod(P_VAL_EXT, P_VAL);
	
	constant PAD_BITS:       integer := P_VAL_EXT - LEFT_BITS;
	constant FRAME_COUNT:    integer := PAD_BITS / P_VAL;
	constant FRAME_LOG2:     integer := my_log2(FRAME_COUNT);
	constant P_VAL_EXT_LOG2: integer := my_log2(P_VAL_EXT); 
	constant D_V_LOG2: integer       := my_log2(D_V);	
    constant P_VAL_LOG2: integer     := my_log2(P_VAL);	
    
-- Components
    component Counter is
    generic (constant N: integer := 8);
    port 
    ( 
         Rst_n : in std_logic;
         Clk : in std_logic;
         En : in std_logic;
         CntRst: in std_logic; -- When 1 reset the counte to 0 sync reset.
         CntValue : out unsigned(N-1 downto 0);
         TC : out std_logic
     );
     end component;
     
     component Counter2 is
     generic (constant N: integer := 8;
         constant MAX_VAL: integer
     );
     port 
     ( 
          Rst_n : in std_logic;
          Clk : in std_logic;
          En : in std_logic;
          CntRst: in std_logic; -- When 1 reset the counte to 0 sync reset.
          CntValue : out unsigned(N-1 downto 0);
          TC : out std_logic
     );
     end component;
     
-- For debug purposes
    component RegN is
    generic (N: integer := 16);
    port
    (
        CLK:     in     std_logic;
        Rst_n:   in     std_logic;
        En:      in     std_logic;
        D:       in     std_logic_vector(N-1 downto 0);
        Q:       out std_logic_vector(N-1 downto 0)
    );
    end component;

-- Signals
type FSM_State is (ResetState, IdleState, EmitBitState, DoneState);
signal CurrState, NextState: FSM_State;

-- Bit Counter
signal b_cnt_en: std_logic;
signal b_cnt_tc: std_logic;
signal b_cnt_rst: std_logic;
signal b_cnt_val: unsigned(P_VAL_EXT_LOG2-1 downto 0);

-- Frame bit index Counter
signal fb_cnt_en: std_logic;
signal fb_cnt_tc: std_logic;
signal fb_cnt_rst: std_logic;
signal fb_cnt_val: unsigned(P_VAL_LOG2-1 downto 0);

-- Frame Counter
signal f_cnt_en: std_logic;
signal f_cnt_tc: std_logic;
signal f_cnt_rst: std_logic;
signal f_cnt_val: unsigned(FRAME_LOG2-1 downto 0);

-- Mem Adr Counter
signal m_cnt_en: std_logic;
signal m_cnt_tc: std_logic;
signal m_cnt_rst: std_logic;
signal m_cnt_val: unsigned(D_V_LOG2-1 downto 0);



-- DP Signals
signal dp_mem_idx_match: std_logic;
-- Thsi is one when the curr bit the the last bit of a frame
signal dp_b_eof: std_logic;

signal dp_eof_frame_idx: unsigned(P_VAL_EXT_LOG2-1 downto 0);

signal mem_in_data_un: unsigned(P_VAL_LOG2-1 downto 0);

-- FSM
signal fsm_done: std_logic;
signal fsm_out_bin: std_logic;
signal fsm_out_last: std_logic;
signal fsm_out_valid: std_logic;


-- DEBUG Signals
signal dp_mem_idx: unsigned(P_VAL_LOG2-1 downto 0);



begin

StateUpdate: process(Clk, Rst_n)
begin	
	if Rst_n='0' then 
		CurrState <= ResetState;
	elsif rising_edge(Clk) then
		CurrState <= NextState;
	end if;
end process;

FSM_Update: process(CurrState, start_in, out_stop, 
                    b_cnt_tc, dp_b_eof, dp_mem_idx_match, 
                    m_cnt_val)
begin
    fsm_out_bin <= '0';
    fsm_out_last <= '0';
    fsm_out_valid <= '0';
    
    m_cnt_en <= '0';    
    f_cnt_en <= '0';    
	b_cnt_en <= '0';
	fb_cnt_en <= '0';
	
	mem_in_en <= '0';
	
	b_cnt_rst <= '0';    
    m_cnt_rst <= '0';    
    f_cnt_rst <= '0';    
    fb_cnt_rst <= '0';
	
	case CurrState is
		when ResetState => 
			b_cnt_en <= '0';
			
			b_cnt_rst <= '1';    
			m_cnt_rst <= '1';	
			f_cnt_rst <= '1';	
			fb_cnt_rst <= '1';
			
			
			fsm_out_bin <= '0';
			fsm_out_last <= '0';
			fsm_out_valid <= '0';
			
			fsm_done <= '0';
			
			NextState <= IdleState;
		when IdleState =>
			NextState <= IdleState;
			if start_in='1' then 
			    fsm_done <= '0';
				-- mem_in_en <= '1';
				NextState <= EmitBitState;
			end if;
	    when EmitBitState =>
	        fsm_out_valid <= '1';
	        b_cnt_en  <= '1';
	        fb_cnt_en <= '1';
	        
	        mem_in_en <= '1';
	        
	        if (out_stop='1') then
	           -- Stop the circuit at the current state
	           b_cnt_en <= '0';
	           f_cnt_en <= '0';
	           m_cnt_en <= '0';
	           fb_cnt_en <= '0';
	        end if;
	        
	        -- Handle the check to see if the current b_cnt_val == the index
	        -- read from memory, if so out_bin is one and we increment the m_idx 
	        -- counter;
	        m_cnt_en <= '0';
	        if (dp_mem_idx_match='1') then
	           fsm_out_bin <= '1';
	           m_cnt_en <= '1';
	        end if;
	        
	        f_cnt_en <= '0';
	        if (dp_b_eof='1') then
	           m_cnt_rst <= '1';
	           f_cnt_en <= '1';
	        end if;
	    
			NextState <= EmitBitState;
			if (b_cnt_tc='1') then
			   fsm_out_last <= '1';
			   NextState <= DoneState;
		  end if;
		  		
		when DoneState =>   
		    fsm_done <= '1';
		    NextState <= IdleState;
		when others =>
		  NextState <= IdleState;	
	end case;
end process;


-- Datapath
-- Simulation shows that in the range 8:32678 the max number of f_dx is always <= 3
-- se we care only on those cases.
dp_eof_frame_idx <=   to_unsigned(P_VAL-1, P_VAL_EXT_LOG2) when f_cnt_val   = "00" else
                      to_unsigned(2*P_VAL-1, P_VAL_EXT_LOG2) when f_cnt_val = "01" else
                      to_unsigned(3*P_VAL-1, P_VAL_EXT_LOG2) when f_cnt_val = "10" else 
                      (others => '0'); 

 dp_b_eof <= '1' when  b_cnt_val = dp_eof_frame_idx else
             '0';

-- Calculate the current frame bit index without having and additional
-- counter.
--dp_mem_idx_tmp <= to_unsigned(0, P_VAL_EXT_LOG2)         when f_cnt_val = "00" else
--                  to_unsigned(P_VAL-1, P_VAL_EXT_LOG2)     when f_cnt_val = "01" else
--                  to_unsigned(2*P_VAL-1, P_VAL_EXT_LOG2)   when f_cnt_val = "10" else
--                  to_unsigned(3*P_VAL-1, P_VAL_EXT_LOG2)   when f_cnt_val = "11";

--dp_mem_idx <= b_cnt_val - dp_mem_idx_tmp when f_cnt_val="00" else
--              b_cnt_val - dp_mem_idx_tmp - 1;
       
             
--dp_mem_idx_match <= '1' when mem_in_data_un = dp_mem_idx else -- fb_cnt_val else
--                    '0';

dp_mem_idx <= fb_cnt_val;
dp_mem_idx_match <= '1' when mem_in_data_un = dp_mem_idx else -- fb_cnt_val else
                    '0';

-- Memory read ADDR
mem_in_adr <= std_logic_vector(m_cnt_val);

-- See if the curr bit have to be set to one
mem_in_data_un <= unsigned(mem_in_data);




-- out_bin <= dp_mem_idx_match when fsm_out_valid='1' else '0';
out_valid <= fsm_out_valid;

F_IDX_CNT: Counter generic map(N => FRAME_LOG2) 
port map (
	Clk => Clk, Rst_n => Rst_n, 
    En => f_cnt_en, TC => f_cnt_tc,
    CntValue => f_cnt_val,
    CntRst   => f_cnt_rst
);

B_IDX_CNT: Counter generic map(N => P_VAL_EXT_LOG2) 
port map (
	Clk => Clk, Rst_n => Rst_n, 
    En => b_cnt_en, TC => b_cnt_tc,
    CntValue => b_cnt_val,
    CntRst   => b_cnt_rst
);

M_IDX_CNT: Counter2 generic map(N => D_V_LOG2, MAX_VAL => D_V) 
port map (
	Clk => Clk, Rst_n => Rst_n, 
    En  => m_cnt_en, TC => m_cnt_tc,
    CntValue => m_cnt_val,
    CntRst   => m_cnt_rst
);

FB_IDX_CNT: Counter2 generic map(N => P_VAL_LOG2, MAX_VAL => P_VAL) 
port map (
	Clk => Clk, Rst_n => Rst_n, 
    En  => fb_cnt_en, TC => fb_cnt_tc,
    CntValue => fb_cnt_val,
    CntRst   => fb_cnt_rst
);

-- Assign the entity output signals
done <= fsm_done;
out_bin <= fsm_out_bin;
out_last  <= fsm_out_last;
out_valid  <= fsm_out_valid;


end Behavioral;



