-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- Copyright 2025 VLSI Solution Oy
-- SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
--
-- Licensed under the Solderpad Hardware License v 2.1 (the "License");
-- you may not use this file except in compliance with the License, or,
-- at your option, the Apache License version 2.0.
-- You may obtain a copy of the License at
--
--    https://solderpad.org/licenses/SHL-2.1/
--
-- Unless required by applicable law or agreed to in writing, any work
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
--  uart_transmitter.vhd
--  V1.1
--
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.uart_pkg.all;


entity uart_transmitter is
  generic ( FIFO_WIDTH   : natural := 8;
            FIFO_DEPTH   : natural := 16;
            FIFO_PNTR_W  : natural := 4 );
  port ( clk_i      : in  std_logic;
         xrst_i     : in  std_logic;
         lcr_i      : in  slv_07_00;
         tf_push_i  : in  std_logic;
         pp_data_i  : in  slv_t(FIFO_WIDTH-1 downto 0);
         enable_i   : in  std_logic;
         tx_reset_i : in  std_logic;
         lsr_mask_i : in  std_logic;       -- reset of fifo
         stx_pad_o  : out std_logic;
         tstate_o   : out slv_02_00;
         tf_count_o : out slv_t(FIFO_PNTR_W+1-1 downto 0) );
end entity uart_transmitter;


architecture rtl of uart_transmitter is

  component uart_tfifo
    -- FIFO parameters
    generic ( FIFO_WIDTH  : natural range 1 to 32 := 8;
              FIFO_DEPTH  : natural range 1 to 32 := 16;
              FIFO_PNTR_W : natural range 1 to 32 := 4 );
    port ( clk_i           : in   std_logic;
           xrst_i          : in   std_logic;
           -- control signals
           push_i          : in   std_logic;     -- push to fifo, active high
           pop_i           : in   std_logic;     -- pop from fifo, active high
           -- status signals
           fifo_reset_i    : in   std_logic; 
           reset_status_i  : in   std_logic; 
           overrun_o       : out  std_logic;
           count_o         : out  slv_t(FIFO_PNTR_W+1-1 downto 0);
           --
           data_i          : in   slv_t(FIFO_WIDTH-1 downto 0);
           data_o          : out  slv_t(FIFO_WIDTH-1 downto 0) );
  end component;


  --  states
  constant s_idle        : slv_02_00 := "000";   -- d0
  constant s_send_start  : slv_02_00 := "001";   -- d1
  constant s_send_byte   : slv_02_00 := "010";   -- d2
  constant s_send_parity : slv_02_00 := "011";   -- d3
  constant s_send_stop   : slv_02_00 := "100";   -- d4
  constant s_pop_byte    : slv_02_00 := "101";   -- d5

  signal counter         : slv_04_00;
  signal bit_counter     : slv_02_00;            -- counts bits to be sent
  signal shift_out       : slv_06_00;            -- output shift register
  signal stx_o_tmp       : std_logic;
  signal parity_xor      : std_logic;            -- parity of the word
  signal tf_pop          : std_logic;
  signal bit_out         : std_logic;

  --  transmitter FIFO signals
  signal tf_data_out     : slv_t(FIFO_WIDTH-1 downto 0);
  signal tf_count_w      : slv_t(FIFO_PNTR_W+1-1 downto 0);

  signal tstate_q        : slv_02_00;

begin

  tf_count_o <= tf_count_w;
  tstate_o   <= tstate_q;

  fifo_tx : uart_tfifo     -- error bit signal is not used in transmitter FIFO
    generic map ( FIFO_WIDTH   => FIFO_WIDTH,
                  FIFO_DEPTH   => FIFO_DEPTH,
                  FIFO_PNTR_W  => FIFO_PNTR_W )
    port map (  clk_i           => clk_i, 
                xrst_i          => xrst_i,
                -- control signals
                push_i          => tf_push_i,
                pop_i           => tf_pop,
                -- status signals
                fifo_reset_i    => tx_reset_i,
                reset_status_i  => lsr_mask_i,
                overrun_o       => OPEN,
                count_o         => tf_count_w,
                --
                data_i          => pp_data_i,
                data_o          => tf_data_out );


  -- transmitter state machine
  p_stm : process (clk_i)
    variable uart_lc_ep_sp_v : std_logic_vector(1 downto 0);
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        tstate_q        <= s_idle;
        stx_o_tmp       <= '1';
        counter         <= (others => '0');       -- 4:0
        shift_out       <= (others => '0');       -- 6:0
        bit_out         <= '0';
        parity_xor      <= '0';
        tf_pop          <= '0';
        bit_counter     <= (others => '0');       -- 2:0
        uart_lc_ep_sp_v := "00";
      elsif (enable_i = '1') then
        uart_lc_ep_sp_v := "00";
        case tstate_q is
          when s_idle =>
            if ( unsigned(tf_count_w) = 0) then   -- if tf_count_w = 0
              tstate_q     <= s_idle;
              stx_o_tmp  <= '1';
            else
              tf_pop     <= '0';
              stx_o_tmp  <= '1';
              tstate_q     <= s_pop_byte;
            end if;
          when s_pop_byte =>
            tf_pop <= '1';
            case lcr_i(1 downto 0) is  -- UART_LC_BITS number of bits in a word
              when "00" =>
                bit_counter <= "100";
                parity_xor  <= tf_data_out(4) xor tf_data_out(3) xor
                               tf_data_out(2) xor tf_data_out(1) xor
                               tf_data_out(0);
              when "01" =>
                bit_counter <= "101";
                parity_xor  <= tf_data_out(5) xor tf_data_out(4) xor
                               tf_data_out(3) xor tf_data_out(2) xor
                               tf_data_out(1) xor tf_data_out(0);
              when "10" =>
                bit_counter <= "110";
                parity_xor  <= tf_data_out(6) xor tf_data_out(5) xor
                               tf_data_out(4) xor tf_data_out(3) xor
                               tf_data_out(2) xor tf_data_out(1) xor
                               tf_data_out(0);
              when "11" =>
                bit_counter <= "111";
                parity_xor  <= tf_data_out(7) xor tf_data_out(6) xor
                               tf_data_out(5) xor tf_data_out(4) xor
                               tf_data_out(3) xor tf_data_out(2) xor
                               tf_data_out(1) xor tf_data_out(0);
              when others =>
                null;
            end case;
            shift_out(6 downto 0) <= tf_data_out(7 downto 1);
            bit_out               <= tf_data_out(0);
            tstate_q              <= s_send_start;
          when s_send_start =>
            tf_pop <= '0';
            if (unsigned(counter) = 0) then
              counter <= "01111";
            elsif (unsigned(counter) = 1) then     -- "00001"
              counter  <= (others => '0');
              tstate_q   <= s_send_byte;
            else
              counter  <= dec1u(counter);          -- - 1
            end if;
            stx_o_tmp    <= '0';
          when s_send_byte =>
            if (unsigned(counter) = 0) then
              counter <= "01111";
            elsif (unsigned(counter) = 1) then     -- "00001"
              if (unsigned(bit_counter) > 0) then
                bit_counter <= dec1u(bit_counter); -- - 1
                shift_out(5 downto 0) <= shift_out(6 downto 1);
                bit_out               <= shift_out(0);
                tstate_q <= s_send_byte;
              elsif (lcr_i(UART_LC_PE) = '0') then -- end of byte -parity enable
                tstate_q <= s_send_stop;
              else
                uart_lc_ep_sp_v  := lcr_i(UART_LC_EP) & lcr_i(UART_LC_SP);
                case uart_lc_ep_sp_v is
                  when "00"   =>    bit_out  <= not parity_xor;
                  when "01"   =>    bit_out  <= '1';
                  when "10"   =>    bit_out  <= parity_xor;
                  when "11"   =>    bit_out  <= '0';
                  when others =>    null;
                end case;
                tstate_q <= s_send_parity;
              end if;
              counter <= (others => '0');
            else
              counter <= dec1u(counter);           -- - 1
            end if;
            stx_o_tmp <= bit_out;                  -- set output pin
          when s_send_parity =>
            if (unsigned(counter) = 0) then
              counter <= "01111";
            elsif (unsigned(counter) = 1) then     -- "00001"
              counter <= (others => '0');
              tstate_q <= s_send_stop;
            else
              counter <= dec1u(counter);           -- - 1
            end if;
            stx_o_tmp <= bit_out;
          when s_send_stop =>
            if (unsigned(counter) = 0) then
              --
              if (lcr_i(UART_LC_SB) = '0') then    -- lcr_i(UART_LC_BITS) = "--"
                counter <= "01101";                -- 1 stop bit ok igor
              elsif (lcr_i(UART_LC_SB) = '1' and
                     lcr_i(UART_LC_BITS'range) = "00") then
                counter <= "10101";                -- 1.5 stop bit
              else
                counter <= "11101";                -- 2 stop bits
              end if;
            elsif (unsigned(counter) = 1) then     -- "00001"
              counter  <= (others => '0');
              tstate_q <= s_idle;
            else
              counter <= dec1u(counter);           -- - 1
            end if;
            stx_o_tmp <= '1';
          when others =>                           -- should never get here
            tstate_q  <= s_idle;
        end case;
      else
        tf_pop          <= '0';                    -- tf_pop must be 1 clk width
        uart_lc_ep_sp_v := "00";
      end if;
    end if;
  end process p_stm;


  --  break condition
  stx_pad_o <= '0' when (lcr_i(UART_LC_BC) = '1') else stx_o_tmp;

end rtl;
