-- A simple UART (TX)
-- Author   : Kurisu <kurisu@shimatta.de>
-- Modifed  : Markus <markus-oliver.koch@thalesgroup.com>

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

entity uart_tx is
	port(
		clk          : in  std_logic;
		rst          : in  std_logic;
		data         : in  std_logic_vector(7 downto 0);
		byte_ready   : in  std_logic;
		busy         : out std_logic;

		ckDiv        : in  std_logic_vector(15 downto 0);
		parityEnable : in  std_logic;
		parityOdd    : in  std_logic;
		twoStopBits  : in  std_logic;

		tx           : out std_logic
	);
end entity uart_tx;

architecture RTL of uart_tx is
	type state_t is (IDLE, START, TRANSMIT, PARITY, STOP);

	signal state       : state_t;
	signal clkDivider  : unsigned(15 downto 0);
	signal data_i      : std_logic_vector(7 downto 0);
	signal bitCounter  : integer range 0 to 7;
	signal parity_calc : std_logic;

begin
	txFSM : process(clk, rst, ckDiv, parityOdd) is
	begin
		if rst = '1' then
			data_i      <= x"00";
			state       <= IDLE;
			tx          <= '1';
			bitCounter  <= 0;
			busy        <= '0';
			clkDivider  <= unsigned(ckDiv);
			parity_calc <= parityOdd;
		elsif rising_edge(clk) then
			busy <= '1';

			if (clkDivider = 0) then
				clkDivider <= unsigned(ckDiv);
			else
				clkDivider <= clkDivider - 1;
			end if;

			case state is
				when IDLE =>
					busy <= '0';
					tx   <= '1';
					if byte_ready = '1' then
						data_i      <= data;
						state       <= START;
						clkDivider  <= unsigned(ckDiv);
						bitCounter  <= 0;
						parity_calc <= '0';
						busy        <= '1';
						tx          <= '1';
					end if;
				when START =>
					tx    <= '0';
					state <= TRANSMIT;
				when TRANSMIT =>
					if (clkDivider = to_unsigned(0, clkDivider'length)) then
						tx <= data_i(bitCounter);
						if data_i(bitCounter) = '1' then
							parity_calc <= not parity_calc;
						end if;
						if bitCounter = 7 then
							bitCounter <= 0;
							if parityEnable = '1' then
								state <= PARITY;
							else
								state <= STOP;
							end if;
						else
							bitCounter <= bitCounter + 1;
						end if;
					end if;
				when PARITY =>
					if (clkDivider = to_unsigned(0, clkDivider'length)) then
						tx    <= parity_calc;
						state <= STOP;
					end if;
				when STOP =>
					if (clkDivider = to_unsigned(0, clkDivider'length)) then
						tx         <= '1';
						bitCounter <= bitCounter + 1;
						if (bitCounter = 1 and twoStopBits = '0') or (bitCounter = 2 and twoStopBits = '1') then
							state <= IDLE;
						end if;
					end if;
			end case;
		end if;
	end process txFSM;
end architecture RTL;
