-- 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_rx is
	port(
		clk          : in  std_logic;
		rst          : in  std_logic;
		data         : out std_logic_vector(7 downto 0);
		byte_ready   : out std_logic;
		error        : out std_logic;

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

		rx           : in  std_logic
	);
end entity uart_rx;

architecture RTL of uart_rx is
	constant SYNCH_COUNT : integer := 11; -- Min. 8
	constant BITCOUNT    : integer := 8;

	type state_t is (SYNCH, IDLE, START, RECEIVE, PARITY, STOP);

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

begin
	synchronizer_inst : entity work.synchronizer
		generic map(
			COUNT => 1
		)
		port map(
			clk     => clk,
			rst     => rst,
			dIn(0)  => rx,
			dOut(0) => rx_i
		);

	edgeDetector_inst : entity work.edgeDetector
		port map(clk         => clk,
			     rst         => rst,
			     sig         => rx_i,
			     risingEdge  => open,
			     fallingEdge => open,
			     anyEdge     => rx_edge);

	rxFSM : process(clk, rst, parityOdd, ckDiv) is
	begin
		if rst = '1' then
			state       <= SYNCH;
			bitCounter  <= SYNCH_COUNT;
			clkDivider  <= unsigned(ckDiv);
			error       <= '0';
			parity_calc <= parityOdd;
			data_i      <= x"00";
			data        <= x"00";
			byte_ready  <= '0';
		elsif rising_edge(clk) then
			byte_ready <= '0';
			error      <= '0';
			if (clkDivider = 0) then
				clkDivider <= unsigned(ckDiv);
			else
				clkDivider <= clkDivider - 1;
			end if;
			case state is
				when SYNCH =>           -- Wait for 11 consecutive ones
					if (clkDivider = to_unsigned(0, clkDivider'length)) then
						if rx_i = '1' then
							if bitCounter = 0 then
								state <= IDLE;
							else
								bitCounter <= bitcounter - 1;
							end if;
						else
							bitCounter <= SYNCH_COUNT;
						end if;
					end if;
				when IDLE =>            -- Detect transition for sync
					if rx_i = '0' then
						state       <= START;
						clkDivider  <= unsigned('0' & ckDiv(15 downto 1)); -- cMax_half. After that we are in the middle of the start bit
						parity_calc <= parityOdd;
					end if;
				when START =>
					if (clkDivider = to_unsigned(0, clkDivider'length)) then
						if rx_i = '0' then
							state      <= RECEIVE;
							bitCounter <= 0;
						else
							report "uart_rx: START BIT ERROR" severity warning;
							error <= '1';
							state <= SYNCH;
						end if;
					end if;
				when RECEIVE =>
					if (clkDivider = to_unsigned(0, clkDivider'length)) then
						data_i(bitCounter) <= rx_i;
						if rx_i = '1' then
							parity_calc <= not parity_calc;
						end if;
						if bitCounter = BITCOUNT - 1 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
						if parity_calc = rx_i then
							state <= STOP;
						else
							state <= SYNCH;
							error <= '1';
							report "uart_rx: PARITY ERROR" severity warning;
						end if;
					end if;
				when STOP =>
					if (clkDivider = to_unsigned(0, clkDivider'length)) then
						if (rx_i = '1') then
							bitCounter <= bitCounter + 1;
							if bitCounter = 1 or twoStopBits = '0' then
								state      <= IDLE;
								data       <= data_i;
								byte_ready <= '1';
							end if;
						else
							error <= '1';
							state <= SYNCH;
							report "uart_rx: STOP BIT ERROR" severity warning;
						end if;
					end if;
			end case;
		end if;
	end process rxFSM;

end architecture RTL;
