library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

---------------------------------------------------------------------------------------------------------
--                              INPUT / OUTPUT
---------------------------------------------------------------------------------------------------------
entity Receiver is
generic(
    samplesPerHalf  :     integer := 1
);
Port (
    clk         :IN     STD_LOGIC;
    clkLiFi     :IN     STD_LOGIC;
    RX          :IN     STD_LOGIC;
    
    ResetTmr    :OUT    STD_LOGIC := '1';
    DataReady   :OUT    STD_LOGIC := '1';
    Data        :OUT    STD_LOGIC_VECTOR(31 downto 0) := (others => '0')
);
end Receiver;

architecture Behavioral of Receiver is
---------------------------------------------------------------------------------------------------------
--                              State Machine
---------------------------------------------------------------------------------------------------------
-- Fast clock states
TYPE State_type IS (Idle, checkStartSymbol, ReceiveData);
SIGNAL State : State_Type := Idle;

-- Slower clock (LiFi clock) states
TYPE manchester_state_type IS (firstHalf, middleEdge ,secondHalf, preparingNextRun);
signal manchesterState: manchester_state_type := firstHalf;

---------------------------------------------------------------------------------------------------------
--                              Signals 
---------------------------------------------------------------------------------------------------------
signal ReceivedBit      : STD_LOGIC := '0';
signal BitReady         : STD_LOGIC := '0';
signal ResetInternal    : STD_LOGIC := '0';

---------------------------------------------------------------------------------------------------------
--                              Processes
---------------------------------------------------------------------------------------------------------
begin
-- This clock is much faster then the other clock
stateMachine : process(clk)
-- Counter that counts the bit position
variable counter            : integer := 0;

-- originaly, the BitReady signal was used as a flag, but this is a violation of multidriven nets
variable waitingForNewBit   : STD_LOGIC := '1';

constant StartData        : STD_LOGIC_VECTOR(7 downto 0) := "11110000";
begin
    if(rising_edge(clk)) then    
        case State is
            -- When idle (waiting for new data to arrive)
            when idle =>
                -- Set the data ready pulse low again
                DataReady <= '0';
            
                -- Stop the clock divider until there is a rising edge on the RX line
                ResetTmr <= '1';
                
                -- If the line goes high
                if(RX = '1') then
                    -- receive 8 bits
                    counter := 8;
                    ResetTmr <= '0';
                    ResetInternal <= '0';
                    
                    -- Go to the next state
                    State <= checkStartSymbol;
                end if;
            
            -- When checking for the start symbol sequence (1111 0000)
            when checkStartSymbol =>
                -- Start the clock divider
                ResetTmr <= '0';
                
                -- if bitReady is false (other process is receiving a new bit)
                if (bitReady = '0') then
                    waitingForNewBit := '1';
                    
                -- If the line decoder has a new bit ready (and this process is waiting for it)
                elsif (waitingForNewBit = '1') then
                    -- Set the internal flag
                    waitingForNewBit := '0';
                    
                    -- decrease the counter (remaining bits/ bit position)
                    counter := counter - 1;
                    
                    -- If the start sequence is not correct
                    if(StartData(counter) /= ReceivedBit) then
                        
                        -- Reset the other SM
                        ResetInternal <= '1';
                        
                        -- Go back to start
                        State <= idle;
                    -- else if all bits are received
                    elsif (counter = 0) then
                        -- Receive 32 bits
                        counter := 32;
                        
                        -- go to the receive data state
                        State <= ReceiveData;
                    end if;
                end if;
        
            -- When receiving data
            when ReceiveData =>
                -- if bitReady is false (other process is receiving a new bit)
                if (bitReady = '0') then
                    waitingForNewBit := '1';
                    
                -- If the line decoder has a new bit ready (and this process is waiting for it)
                elsif (waitingForNewBit = '1') then
                    -- Set the internal flag
                    waitingForNewBit := '0';
                
                    -- decrease the counter (remaining bits/ bit position)
                    counter := counter - 1;
            
                    -- Set the next bit
                    Data(counter) <= ReceivedBit;
                    
                    -- If all bits are received
                    if(counter = 0) then
                        -- Indicate the data is valid again
                        DataReady <= '1';
                        
                        -- Force the second SM to return to its start position
                        ResetInternal <= '1';
                        
                        -- Go to the idle state
                        State <= idle;
                    end if;
            end if;
        end case;
    end if;
end process;

lineDecoder : process(clkLiFi, ResetInternal)

variable zeroCounter : integer := 0;
variable oneCounter : integer := 0;
variable sampleCounter : integer := 0;

begin
    -- If the process was triggered by the internal reset
    if(ResetInternal = '1') then
        -- Set the bitReady flag to false again
        BitReady <= '0';
    
        -- Reset the counters
        zeroCounter := 0;
        oneCounter := 0;
        
        -- Reset the state machine
        manchesterState <= firstHalf;
    
    elsif(rising_edge(clkLiFi)) then    
        case manchesterState is
            -- when in the first half of the manchester code...
            when firstHalf =>
                -- increase the counter
                sampleCounter := sampleCounter +1;
                
                -- If receiving a zero...
                if(RX = '0') then
                    -- increase the amount of times the line was equal to the zero manchester symbol
                    zeroCounter := zeroCounter +1;
                else
                    -- else increase the amount times the line was equal to the one manchester symbol
                    oneCounter := oneCounter +1;
                end if;
                
                -- If all samples were taken
                if (sampleCounter = samplesPerHalf) then
                    -- reset the counter
                    sampleCounter := 0;
                    
                    -- go to the next state
                    manchesterState <= middleEdge;
                end if;
            when middleEdge =>
                -- Go to the next state, don't count the chaning edge
                manchesterState <= secondHalf;
            when secondHalf =>
                -- Do the same as in the first half, only now use the invert
                if(RX = '1') then
                    -- increase the amount of times the line was equal to the zero manchester symbol
                    zeroCounter := zeroCounter +1;
                else
                    -- else increase the amount times the line was equal to the one manchester symbol
                    oneCounter := oneCounter +1;
                end if;
                
                -- increase the counter
                sampleCounter := sampleCounter +1;
                
                -- If all samples were taken
                if (sampleCounter = samplesPerHalf) then
                    -- Set the next received bit based on the number of ones and zero's that were counted
                    if(zeroCounter >= oneCounter) then
                        ReceivedBit <= '0';
                    else
                        ReceivedBit <= '1';
                    end if;
                    
                    -- Indicate a new bit is ready
                    BitReady <= '1';
                    
                    -- reset the counter
                    sampleCounter := 0;
                    
                    -- go to the next state
                    manchesterState <= preparingNextRun;
                end if;
                
            -- the line is changing state, this is the ideal moment to reset the counters
            when preparingNextRun =>
                -- Set the bitReady flag to false again
                BitReady <= '0';
            
                -- Reset the counters
                zeroCounter := 0;
                oneCounter := 0;
                -- Go to the next state
                manchesterState <= firstHalf;
            end case;
        end if;
end process;
end Behavioral;
