-----------------------------------------------------------------------
-- UART Rx module, 
--  
-----------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-----------------------------------------------------------------------
ENTITY UART_Basic_Rx IS
    GENERIC( Parity_EN:       BOOLEAN := TRUE;
             Odd_Parity:      BOOLEAN := FALSE);
    PORT( Clk:            IN  STD_LOGIC;                    -- system clock
          clk_EN:         IN  STD_LOGIC;                    -- clock enable (frequency = 16x baud rate)
          Rst:            IN  STD_LOGIC;
          Rx:             IN  STD_LOGIC;                    -- UART serial input
          Data:           OUT STD_LOGIC_VECTOR(7 DOWNTO 0); -- received data
          Data_Err:       OUT STD_LOGIC;                    -- incorrectly received character
          Data_Ready:     OUT STD_LOGIC);                   -- ready to sent next byte
END UART_Basic_Rx;
-----------------------------------------------------------------------
ARCHITECTURE Behavioral OF UART_Basic_Rx IS
  SIGNAL current_state:     STD_LOGIC_VECTOR(3 DOWNTO 0);                       -- control of transmission
  SIGNAL next_state:        STD_LOGIC_VECTOR(3 DOWNTO 0) := "1011";             -- default idle state
  SIGNAL data_int:          STD_LOGIC_VECTOR(7 DOWNTO 0) := (OTHERS => '0');    -- internal data buffer
  SIGNAL sample_cnt:        STD_LOGIC_VECTOR(3 DOWNTO 0);
  SIGNAL prev_Rx:           STD_LOGIC;
  SIGNAL Data_Err_i:        STD_LOGIC;
  SIGNAL busy:              STD_LOGIC := '0';
  SIGNAL par_bit:           STD_LOGIC;
  SIGNAL par_bit_i:         STD_LOGIC;
-----------------------------------------------------------------------
BEGIN

  receive_proc: PROCESS(clk,rst) BEGIN
    IF rst = '1' THEN
      busy <= '0';
      Data_Ready <= '0';
      sample_cnt <= (OTHERS => '0');
      current_state <= "1011";
      Data_Err_i <= '0';
    ELSIF clk'event AND clk = '1' THEN
      IF clk_EN = '1' THEN

        prev_Rx <= Rx;                -- sampling input for edge detection

        -- when idle (not receiving) search for start bit
        IF busy = '0' THEN
          IF Rx = '0' AND prev_Rx = '1' THEN    -- start bit detected
            busy <= '1';
            Data_Ready <= '0';
            sample_cnt <= (OTHERS => '0');
            current_state <= (OTHERS => '0');
            Data_Err_i <= '0';
            Data_Err <= '0';
          END IF;

        -- receiving
        ELSE
          sample_cnt <= sample_cnt + 1;     -- increment counter every second

          IF sample_cnt = "0111" THEN       -- middle of the data bit

            current_state <= next_state;    -- increment state counter (go to next bit)

            CASE (current_state) IS
              WHEN "0000" =>      -- start bit
                data_int(0) <= Rx;  -- not necessary
              WHEN "0001" =>      -- bit 1 (LSB)
                data_int(0) <= Rx;
              WHEN "0010" =>      -- bit 2
                data_int(1) <= Rx;
              WHEN "0011" =>      -- bit 3
                data_int(2) <= Rx;
              WHEN "0100" =>      -- bit 4
                data_int(3) <= Rx;
              WHEN "0101" =>      -- bit 5
                data_int(4) <= Rx;
              WHEN "0110" =>      -- bit 6
                data_int(5) <= Rx;
              WHEN "0111" =>      -- bit 7
                data_int(6) <= Rx;
              WHEN "1000" =>      -- bit 8 (MSB)
                data_int(7) <= Rx;
              WHEN "1001" =>      -- parity bit
                Data_Err_i  <= Rx XOR par_bit;          -- parity check
              WHEN "1010" =>      -- stop bit
                busy <= '0';
                IF Data_Err_i = '0' THEN
                  Data <= data_int;     -- put data on output
                  Data_Err <= NOT Rx;   -- stop bit error indication
                  Data_Ready <= Rx;     -- "Data_Ready" = '1' when correct stop-bit is detected
                ELSE
                  Data_Ready <= '0';
                  Data_Err <= '1';      -- parity error indication
                END IF;
              WHEN "1011" =>      
                busy <= '0';
              WHEN OTHERS =>      -- other states (shold not happen)
                busy <= '0';
                Data_Ready <= '0';
                Data_Err <= '0';
            END CASE;



          END IF;
        END IF;
        
      END IF;
    END IF;
  END PROCESS receive_proc;

-----------------------------------------------------------------------
-- parity generator
  par_bit_i <= Data_int(0) XOR Data_int(1) XOR Data_int(2) XOR Data_int(3)
        XOR Data_int(4) XOR Data_int(5) XOR Data_int(6) XOR Data_int(7);

  par_bit <= NOT par_bit_i WHEN Odd_parity ELSE par_bit_i;
-----------------------------------------------------------------------

  state_mach: PROCESS(current_state) BEGIN
        CASE (current_state) IS
          WHEN "0000" =>      -- start bit
            next_state <= "0001";
          WHEN "0001" =>      -- bit 1
            next_state <= "0010";
          WHEN "0010" =>      -- bit 2
            next_state <= "0011";
          WHEN "0011" =>      -- bit 3
            next_state <= "0100";
          WHEN "0100" =>      -- bit 4
            next_state <= "0101";
          WHEN "0101" =>      -- bit 5
            next_state <= "0110";
          WHEN "0110" =>      -- bit 6
            next_state <= "0111";
          WHEN "0111" =>      -- bit 7
            next_state <= "1000";
          WHEN "1000" =>      -- bit 8
            IF Parity_EN THEN next_state <= "1001";    -- go to parity bit
                         ELSE next_state <= "1010";    -- go to stop bit
             END IF;
          WHEN "1001" =>      -- parity bit
            next_state <= "1010";
          WHEN "1010" =>      -- stop bit
            next_state <= "1011";
          WHEN "1011" =>      -- end of receiving
            next_state <= "1011";
          WHEN OTHERS =>      -- other states (shold not happen)
            next_state <= "1011";
        END CASE;
  END PROCESS state_mach;



END Behavioral;
