LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;




entity UART_VHDL is
   port (
        clock:     in  std_logic;
        reset_N:   in  std_logic;

        address:   in  std_logic;
        writeData: in  std_logic_vector(7 downto 0);
        write:     in  std_logic;
        readData:  out std_logic_vector(7 downto 0);
        read:      in  std_logic;

        serialIn:  in  std_logic;
        serialOut: out std_logic
        );
end UART_VHDL;




ARCHITECTURE DUFF OF UART_VHDL IS

    constant ONCE:  integer := 0;
    constant TWICE: integer := 1;

    type   t_TxState  is (
                         IDLE,
                         SEND
                         );

    type   t_RxState  is (
                         IDLE,
                         START,
                         RECEIVE,
                         STOP,
                         CHECK_STOP_BIT,
                         ON_ERROR,
                         FULL,
                         ERROR
                         );

    constant c_TX:        std_logic := '0';
    constant c_RX:        std_logic := '0';
    constant c_STATUS:    std_logic := '1';

    constant c_RxReady:     integer := 0;
    constant c_RxError:     integer := 1;
    constant c_TxReady:     integer := 2;

    signal   status:      std_logic_vector(2 downto 0);

     signal RxState:          t_RxState;
     signal TxState:          t_TxState;
     signal serialInRetimed:    std_logic_vector(1 downto 0);

     signal Tx,
            Rx:                 std_logic_vector(7 downto 0);
     signal TxShiftReg:         std_logic_vector(9 downto 0);
     signal RxShiftReg:         std_logic_vector(8 downto 0);

     signal RxSampleCount,
            RxBitCount,
            TxSampleCount,
            TxBitCount:         integer range 0 to 8;

     signal TxWritten,
            clearTxWritten,
            loadTxShiftReg,
            shiftTx,
            checkStopBit:   std_logic;
     signal clearRxError,
            RxHasBeenRead,
            copyRxShiftReg,
            shiftRx:        std_logic;

     signal RxError,
            RxErrorEarly:   std_logic;

begin

        --++++++++++++++++++++++++++++
        --                           +
        --   Writeable Registers.    +
        --                           +
        --++++++++++++++++++++++++++++

process (clock, reset_N)
    begin

    if reset_N = '0' then
        TxWritten <= '0';
    else

        if clearTxWritten = '1' then
            TxWritten <= '0';

        elsif write = '1' and  address = c_TX then
            Tx        <= writeData;
            TxWritten <= '1';
        end if;

    end if;

    end process;




    --+++++++++++++++++++++++++++++++
    --                              +
    --   Transmit State Machine.    +
    --                              +
    --+++++++++++++++++++++++++++++++

process (clock, reset_N)
    begin

    if reset_N = '0' then

          TxState         <=  IDLE;
          TxSampleCount   <=  0;
          TxBitCount      <=  0;
          loadTxShiftReg  <= '0';
          shiftTx         <= '0';

    elsif clock'event and clock='1' then

         case TxState is

            when IDLE  =>   shiftTx       <= '0';
                            if( TxWritten = '1' ) then
                                TxSampleCount  <= 0;
                                TxBitCount     <= 0;
                                TxState        <= SEND;
                                loadTxShiftReg <= '1';
                                clearTxWritten <= '1';
                            else
                                loadTxShiftReg <= '0';
                                clearTxWritten <= '0';
                                TxState <= IDLE;
                            end if;

            when SEND  =>   loadTxShiftReg <= '0';
                            if TxSampleCount = 7 then             -- Start Bit, Eight Data Bits and Stop Bit.
                                TxSampleCount <= 0;
                                shiftTx       <= '1';
                                if TxBitCount=9 then
                                    TxState <= IDLE;
                                else
                                    TxBitCount <= TxBitCount+1;
                                    TxState    <= SEND;
                                end if;
                            else
                                shiftTx       <= '0';
                                TxSampleCount <= TxSampleCount+1;
                                TxState <= SEND;
                            end if;

        end case;

    end if;

    end process;




     --++++++++++++++++++++++++++++++++
     --                               +
     --   Transmit Shift Register.    +
     --                               +
     --++++++++++++++++++++++++++++++++

process (clock, reset_N)
    begin

    if reset_N = '0' then

        TxShiftReg <= (others=>'1');

    elsif clock'event and clock='1' then

        if loadTxShiftReg = '1' then
            TxShiftReg <= ('1' & Tx & '0' );    -- Stop bit, eight data bits, start bit.
        elsif shiftTx = '1' then
            TxShiftReg(8 downto 0) <= TxShiftReg(9 downto 1);
        end if;
    end if;

    end process;

serialOut <= TxShiftReg(0);




    --+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    --                                                          +
    --   Re-time any asynchronous signals before use in order   +
    --   to avoid race hazards or problems of metastability     +
    --                                                          +
    --+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

serialInRetimed  <= ( serialInRetimed(ONCE) & serialIn ) when clock'event and clock='1';




    --++++++++++++++++++++++++++++++
    --                             +
    --   Receive State Machine.    +
    --                             +
    --++++++++++++++++++++++++++++++

process (clock, reset_N)
    begin

    if reset_N = '0' then

          RxState         <=  IDLE;
          RxSampleCount   <=  0;
          RxBitCount      <=  0;
          shiftRx         <= '0';
          checkStopBit    <= '0';
          copyRxShiftReg  <= '0';

    elsif clock'event and clock='1' then

    shiftRx <= '0';
    checkStopBit <= '0';
    copyRxShiftReg <= '0';

    case RxState is

        when IDLE    => if serialInRetimed(TWICE) = '0' then       -- Falling Edge of Start Bit.
                            RxSampleCount <= 0;
                            RxBitCount    <= 0;
                            RxState <= RECEIVE;
                        else
                            RxState <= IDLE;
                        end if;

        when START   => if RxSampleCount = 3 then                  -- Centre of Start Bit                        
                            RxState <= RECEIVE;
                        else
                            RxSampleCount <= RxSampleCount+1;
                            RxState <= START;
                        end if;

        when RECEIVE => if RxSampleCount = 7 then                  -- Eight Data Bits.
                            if RxBitCount=7 then
                                RxSampleCount <= 0;
                                RxState <= STOP;
                                shiftRx <= '1';
                            else
                                RxSampleCount <= 0;
                                RxBitCount    <= RxBitCount+1;
                                shiftRx <= '1';
                            end if;
                        else
                            RxSampleCount <= RxSampleCount+1;
                            RxState <= RECEIVE;
                        end if;

        when STOP    => if RxSampleCount = 7 then                   -- Check Stop Bit
                            shiftRx <= '1';
                            RxState <= CHECK_STOP_BIT;
                        else
                            RxSampleCount <= RxSampleCount+1;
                            RxState <= STOP;
                        end if;

        when CHECK_STOP_BIT => checkStopBit <= '1';
                               RxState <= ON_ERROR;

        when ON_ERROR       => if RxErrorEarly = '1' then
                                   copyRxShiftReg <= '1';
                                   RxState <= ERROR;
                               else
                                   copyRxShiftReg <= '1';
                                   RxState <= FULL;
                               end if;

        when FULL    => if RxHasBeenRead = '1' then
                            RxState <= IDLE;
                        end if;

        when ERROR   => if clearRxError = '1' then
                            RxState <= IDLE;
                        end if;

    end case;

    end if;

    end process;




     --+++++++++++++++++++++++++++++++
     --                              +
     --   Receive Shift Register.    +
     --                              +
     --+++++++++++++++++++++++++++++++

process (clock, reset_N)
    begin

    if reset_N = '0' then

        RxShiftReg <= (others=>'1');
        RxError <= '0';

    elsif clock'event and clock='1' then

        if  shiftRx = '1' then
            RxShiftReg(8 downto 0) <= serialInRetimed(TWICE) & RxShiftReg(8 downto 1);
        elsif checkStopBit = '1' then
            if RxShiftReg(8) = '0' then
                RxError <= '1';
            else
                RxError <= '0';
            end if;
        end if;

        if clearRxError = '1' then
            RxError <= '0';
        end if;

        if copyRxShiftReg = '1' then
            Rx <= RxShiftReg(7 downto 0);
        end if;

    end if;

    end process;

RxErrorEarly <= '1' when checkStopBit = '1' and RxShiftReg(8) = '0' else '0';




   --+++++++++++++++++++++++++++
   --                          +
   --   Readable Registers.    +
   --                          +
   --+++++++++++++++++++++++++++

status(c_RxError) <= RxError;
status(c_RxReady) <= '1' when RxState=FULL  else '0';
status(c_TxReady) <= '1' when TxState=IDLE  else '0';

process (clock, reset_N)
    begin

    if reset_N = '0' then

        readData <= (others=>'0');
        clearRxError <= '0';

    elsif clock'event and clock='1' then

        if read = '1' then

            case address is

                when c_RX      =>   readData <= Rx;
                                    clearRxError   <= '0';
                                    RxHasBeenRead  <= '1';

                when c_STATUS  =>   readData <= ( "00000" & status );

                                    if status(c_RxError) = '1' then
                                        clearRxError  <= '1';
                                    else
                                        clearRxError  <= '0';
                                    end if;

                                    RxHasBeenRead  <= '0';

                when others    =>   readData <= (others=>'0');
                                    clearRxError <= '0';
                                    RxHasBeenRead  <= '0';

            end case;

        else
            clearRxError  <= '0';
            RxHasBeenRead <= '0';
        end if;

    end if;

    end process;


end DUFF;