-----------------------------------------------------------------------
-- UART Tx module, paralel data are captured at first rising
--  edge of "Clk" when "Start" is asserter ("Ready" must be set before
--  the start).
-- Module doesn't supports parity and only single stop bit is send.
-----------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-----------------------------------------------------------------------
ENTITY UART_Basic_Tx IS
    GENERIC( period:   INTEGER := 5208);
    PORT( clk:            IN  STD_LOGIC;                    -- System clock
          Data:           IN  STD_LOGIC_VECTOR(7 DOWNTO 0); -- data to be sent
          Start:          IN  STD_LOGIC;                    -- start of transmision
          Tx:             OUT STD_LOGIC;                    -- UART output
          Ready:          OUT STD_LOGIC := '1');            -- ready to sent next byte
END UART_Basic_Tx;
-----------------------------------------------------------------------
ARCHITECTURE Behavioral OF UART_Basic_Tx IS

  TYPE Tx_state IS (idle, startb, bit0, bit1, bit2, bit3, bit4, bit5, bit6, bit7, stopb);

  SIGNAL current_state:     Tx_state := idle;               -- control of transmission
  SIGNAL next_state:        Tx_state := idle;               -- default idle state
  SIGNAL data_int:          STD_LOGIC_VECTOR(7 DOWNTO 0) := (OTHERS => '0'); -- internal data buffer
  SIGNAL busy:              STD_LOGIC := '0';
  SIGNAL start_i:           STD_LOGIC := '0';

  SIGNAL clk_EN:            STD_LOGIC := '0';               -- clock enable (frequency = baud rate)
  SIGNAL clk_gen:           STD_LOGIC_VECTOR(20 DOWNTO 1) := (OTHERS => '0');
  SIGNAL per_bin:           STD_LOGIC_VECTOR(21 DOWNTO 1) := (OTHERS => '0');   -- one more bit for sign
-----------------------------------------------------------------------
BEGIN
-----------------------------------------------------------------------
-- generate clock enable signal with frequency eaquel to baud rate
  per_bin <= CONV_STD_LOGIC_VECTOR(period, 21);
  ---------------------------------------------------------------------
  clk_gen_proc: PROCESS(clk) BEGIN
    IF clk'EVENT AND clk = '1' THEN
      IF clk_gen = per_bin(20 DOWNTO 1) THEN
        clk_gen(20 DOWNTO 2) <= (OTHERS => '0');
        clk_gen(1) <= '1';
        clk_EN <='1';
      ELSE
        clk_EN <='0';
        clk_gen <= clk_gen + 1;
      END IF;
    END IF;
  END PROCESS clk_gen_proc;
-----------------------------------------------------------------------
  Tx_proc: PROCESS(clk) BEGIN
    IF clk'EVENT AND clk = '1' THEN

      IF Start = '1' AND busy = '0' THEN
        Start_i <= '1';     -- start pulse latch
        busy <= '1';        -- disable acces to UART ("Ready" is set to '0')
        data_int <= Data;   -- copy data to internal buffer
      END IF;

      IF clk_EN = '1' THEN
        IF Start_i = '1' THEN    -- start of transmission
          Start_i <= '0';
          Tx <= '0';                            -- Start bit
          current_state <= startb;
        ELSIF busy = '1' THEN

          current_state <= next_state;
          -- UART state machine
          CASE current_state IS
            WHEN startb => Tx <= data_int(0); data_int <= '0' & data_int(7 DOWNTO 1); busy <= '1';  -- LSB
            WHEN bit0   => Tx <= data_int(0); data_int <= '0' & data_int(7 DOWNTO 1); busy <= '1';
            WHEN bit1   => Tx <= data_int(0); data_int <= '0' & data_int(7 DOWNTO 1); busy <= '1';
            WHEN bit2   => Tx <= data_int(0); data_int <= '0' & data_int(7 DOWNTO 1); busy <= '1';
            WHEN bit3   => Tx <= data_int(0); data_int <= '0' & data_int(7 DOWNTO 1); busy <= '1';
            WHEN bit4   => Tx <= data_int(0); data_int <= '0' & data_int(7 DOWNTO 1); busy <= '1';
            WHEN bit5   => Tx <= data_int(0); data_int <= '0' & data_int(7 DOWNTO 1); busy <= '1';
            WHEN bit6   => Tx <= data_int(0); data_int <= '0' & data_int(7 DOWNTO 1); busy <= '1';  -- MSB
            WHEN bit7   => Tx <= '1'; busy <= '1';  -- stop bit
            WHEN stopb  => Tx <= '1'; busy <= '1';  -- UART idle state; after stop bit the transceiver is ready
            WHEN OTHERS => Tx <= '1'; busy <= '0';
          END CASE;

        ELSE
          Tx <= '1';                                -- idle state on the line
        END IF;

      END IF;
    END IF;
  END PROCESS Tx_proc;

  Ready <= NOT Busy;

-----------------------------------------------------------------------

  state_machine: PROCESS (current_state) BEGIN
    CASE current_state IS
      WHEN startb => next_state <= bit0;
      WHEN bit0   => next_state <= bit1;
      WHEN bit1   => next_state <= bit2;
      WHEN bit2   => next_state <= bit3;
      WHEN bit3   => next_state <= bit4;
      WHEN bit4   => next_state <= bit5;
      WHEN bit5   => next_state <= bit6;
      WHEN bit6   => next_state <= bit7;
      WHEN bit7   => next_state <= stopb;
      WHEN stopb  => next_state <= idle;
      WHEN OTHERS => next_state <= idle;
    END CASE;
  END PROCESS state_machine;

-----------------------------------------------------------------------

END Behavioral;
