-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 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.
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
--  8250 / 16550 uart
--  V1.1
--
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

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


entity uart16550 is
  port ( clk_i      : in  std_logic;    -- clock input
         xrst_i     : in  std_logic;    -- reset input
         pp_wr_i    : in  std_logic;
         pp_rd_i    : in  std_logic;
         pp_addr_i  : in  slv_02_00;
         pp_data_i  : in  slv_07_00;
         pp_data_o  : out slv_07_00;
         --
         stx_pad_o  : out std_logic;    -- received from transmitter module
         srx_pad_i  : in  std_logic;
         int_o      : out std_logic;    -- from reg
         -- additional signals
         -- modem status bits [3:0],       ! ACTIVE LOW !
         cts_pad_i  : in  std_logic;    -- clear to send,  act low, '0' IF NC!
         dsr_pad_i  : in  std_logic;    -- data set ready        - " " -
         ri_pad_i   : in  std_logic;    -- ring indicator        - " " -
         dcd_pad_i  : in  std_logic;    -- data carrier detect   - " " -
         -- modem control outputs
         rts_pad_o  : out std_logic;    -- request to send
         dtr_pad_o  : out std_logic;    -- data terminal ready
         baud_o     : out std_logic );  -- baud rte (16X real)
end entity uart16550;


architecture rtl of uart16550 is

  subtype vec2 is std_logic_vector(1 downto 0);
  subtype vec_3to1 is std_logic_vector(3 downto 1);
  

  -- UART_IE : interrupt enable register bits
  -- (7) Enable XOFF   Enables the reception of an XOFF character (usually
  --                     ASCII code 19) for flow control.
  -- (6) Enable XON    Enables the reception of an XON character (usually
  --                     ASCII code 17) for flow control.
  -- (5) RTS/CTS       Enables hardware flow control using the RTS
  --                     (Request To Send) and CTS (Clear To Send) signals.
  -- (4) OUT2          Enables the OUT2 interrupt. This interrupt line can
  --                     be used for modem control or other purposes.
  -- 3   OUT1          Enables the OUT1 interrupt. This interrupt line can
  --                     be used for modem control or other purposes.
  -- 2   RDI             Enables the Received Data Interrupt (RDI). This
  --                  interrupt is generated when data is available to be
  --                     read from the receive buffer.
  -- 1   THRE          Enables the Transmitter Holding Register Empty (THRE)
  --                     interrupt. This interrupt is generated when the
  --                     transmit buffer is empty and ready to accept new data.
  -- 0   RLS           Enables the Receiver Line Status (RLS) interrupt. This
  --                     interrupt is generated when there is an error
  --                     condition in the received data.


  -- UART_II: interrupt identification register bits
  -- 7:4  Interrupt ID   Indicates the highest-priority interrupt source.
  --   3  Reserved       Reserved bit (usually set to 0).
  --   2  FIFO enabled   Indicates if the FIFO (First-In-First-Out) mode is
  --                     enabled (1) or not (0).
  -- 1:0  Interrupt      Indicates the presence of an interrupt.
  --   - 00: No interrupt pending
  --   - 01: Interrupt pending from Receiver Line Status (RLS)
  --   - 10: Interrupt pending from Received Data Available (RDA) or
  --         Timeout (if enabled)
  --   - 11: Interrupt pending from Transmitter Holding Register Empty (THRE)


  -- FIFO parameter defines
  constant TX_FIFO_WIDTH    : natural :=  8;
  constant TX_FIFO_DEPTH    : natural := 16;
  constant TX_FIFO_PNTR_W   : natural := log2(TX_FIFO_DEPTH);  -- 4
  -- receiver fifo has width 11 because it has break, parity and framing
  -- error bits
  constant RX_FIFO_WIDTH    : natural := 11;
  constant RX_FIFO_DEPTH    : natural := 16;
  constant RX_FIFO_PNTR_W   : natural := log2(RX_FIFO_DEPTH);  -- 4


  component uart_transmitter
    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 component;


  component uart_receiver
    generic ( FIFO_WIDTH   : natural := 11;   -- 8 + break, parity and framing
              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;
           rxf_pop_i        : in  std_logic;
           srx_pad_i        : in  std_logic;
           enable_i         : in  std_logic;
           rx_reset_i       : in  std_logic;
           lsr_mask_i       : in  std_logic;
           --
           counter_t_o      : out slv_09_00;
           rxf_count_o      : out slv_t(FIFO_PNTR_W+1-1 downto 0);
           rxf_data_out_o   : out slv_t(FIFO_WIDTH-1 downto 0);
           rxf_overrun_o    : out std_logic;
           rxf_error_bit_o  : out std_logic;
           rxstate_o        : out slv_03_00;
           rxf_push_pulse_o : out std_logic );
  end component;

  --  range defs
  constant UART_DL1     : slv_07_00  := X"00";  -- for 'range
  constant UART_DL2     : slv_15_08  := X"00";  -- for 'range

  constant defined_PRESCALER_PRESET_HARD : boolean := false;
  constant PRESCALER_HIGH_PRESET         : slv_07_00 := X"7F";
  constant PRESCALER_LOW_PRESET          : slv_07_00 := X"7F";


  signal rx_meta_q      : slv_01_00;

  signal enable_q       : std_logic;

  signal srx_pad        : std_logic;

  signal ier_q          : slv_03_00;     -- int enable
  signal iir_q          : slv_03_00;     -- int identtification

  --  bits 7 and 6 of fcr. Other bits are ignored
  signal fcr_q          : slv_01_00;
  signal mcr_q          : slv_04_00;
  signal lcr_q          : slv_07_00;
  signal msr_q          : slv_07_00;
  signal dl_q           : slv_15_00;     -- 32-bit divisor latch
  signal scratch_q      : slv_07_00;     -- UART scratch reg
  signal start_dlc_q    : std_logic;     -- activate dlc on writing to UART_DL1
  signal lsr_mask_buf_q : std_logic;     -- delay for lsr_mask condition
  signal msi_reset_q    : std_logic;     -- reset MSR 4 lower bits indicator

  --  16-bit divisor latch counter
  signal dlc_q          : slv_15_00;

  --  trigger level of the receiver FIFO
  signal trigger_level  : slv_03_00;
  signal rx_reset_q     : std_logic;
  signal tx_reset_q     : std_logic;

  signal dlab           : std_logic;     -- divisor latch access bit
  --  loopback bit (MCR bit 4)
  signal loopback                  : std_logic;
  --  effective signals
  signal cts, dsr, ri, dcd         : std_logic;
  --  complement effective signals (considering loopback)
  signal cts_c, dsr_c, ri_c, dcd_c : std_logic;

  -- LSR (line status register)  wires and regs
  --    bit 7  - error in fifo
  --    bit 6  - transmitter empty indicator
  --    bit 5  - transmitter fifo is empty
  --    bit 4  - break indicator
  --    bit 3  - framing error
  --    bit 2  - parity error
  --    bit 1  - receiver overrun
  --    bit 0  - receiver data available
  signal lsr7_0_q                                       : slv_07_00;
  signal lsr0, lsr1, lsr2, lsr3, lsr4, lsr5, lsr6, lsr7 : std_logic;
  signal lsr0r_q, lsr1r_q, lsr2r_q,
    lsr3r_q, lsr4r_q, lsr5r_q, lsr6r_q, lsr7r_q         : std_logic;

  signal lsr0_d_q       : std_logic;    -- delayed
  signal lsr1_d_q       : std_logic;    -- delayed
  signal lsr2_d_q       : std_logic;    -- delayed
  signal lsr3_d_q       : std_logic;    -- delayed
  signal lsr4_d_q       : std_logic;    -- delayed
  signal lsr5_d_q       : std_logic;    -- delayed
  signal lsr6_d_q       : std_logic;    -- delayed
  signal lsr7_d_q       : std_logic;    -- delayed


  signal lsr_mask_w     : std_logic;

  -- interrupt signals
  signal rls_int        : std_logic;  -- receiver line status interrupt
  signal rda_int        : std_logic;  -- receiver data available interrupt
  signal ti_int         : std_logic;  -- timeout indicator interrupt
  signal thre_int       : std_logic;  -- transmitter holding reg empty interrupt
  signal ms_int         : std_logic;  -- modem status interrupt

  -- FIFO signals
  signal tf_push_q      : std_logic;
  signal rxf_pop_q      : std_logic;
  signal rxf_data_out   : std_logic_vector(RX_FIFO_WIDTH-1 downto 0);
   -- an error (parity or framing) is inside the fifo
  signal rxf_error_bit  : std_logic;
  signal rxf_overrun    : std_logic;
  signal rxf_push_pulse : std_logic;
  signal rxf_count_w    : std_logic_vector(RX_FIFO_PNTR_W+1-1 downto 0);
  signal tf_count       : std_logic_vector(TX_FIFO_PNTR_W+1-1 downto 0);
  signal txstate_w      : std_logic_vector(2 downto 0);
  signal counter_t      : std_logic_vector(9 downto 0);

  -- THRE status is delayed one character time when a character is written
  -- to fifo.                        
  signal thre_set_en             : std_logic;
  -- While counter counts, THRE status is blocked (delayed one character cycle)
  signal block_cnt_q             : slv_07_00;
  -- One character length minus stop bit
  signal  block_value            : slv_07_00;

  -- transmitter signals
  signal serial_out              : std_logic;
  signal serial_in               : std_logic;

  signal lsr_mask_condition_w    : std_logic;
  signal iir_read                : std_logic;
  signal msr_read                : std_logic;
  signal fifo_read               : std_logic;
  signal fifo_write              : std_logic;

  signal delayed_modem_signals_q : slv_03_00;

  signal rls_int_d               : std_logic;
  signal thre_int_d              : std_logic;
  signal ms_int_d                : std_logic;
  signal ti_int_d                : std_logic;
  signal rda_int_d               : std_logic;

  -- rise detection signals
  signal rls_int_rise            : std_logic;
  signal thre_int_rise           : std_logic;
  signal ms_int_rise             : std_logic;
  signal ti_int_rise             : std_logic;
  signal rda_int_rise            : std_logic;

  -- interrupt pending flags
  signal rls_int_pnd_q           : std_logic;
  signal rda_int_pnd_q           : std_logic;
  signal thre_int_pnd_q          : std_logic;
  signal ms_int_pnd_q            : std_logic;
  signal ti_int_pnd_q            : std_logic;

  signal pp_data_buf_q           : slv_07_00;

begin

  -- synchronizing and sampling serial RX input
  p_meta : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        rx_meta_q <= (others => '1');
      else
        rx_meta_q <=  rx_meta_q(0) & srx_pad_i;
      end if;
    end if;
  end process p_meta;

  srx_pad <= rx_meta_q(1);



  baud_o     <= enable_q; -- baud_o is actually the enable signal


  cts        <= not cts_pad_i;
  dsr        <= not dsr_pad_i;
  ri         <= not ri_pad_i;
  dcd        <= not dcd_pad_i;

  cts_c      <= mcr_q(UART_MC_RTS)  when (loopback = '1') else cts_pad_i;
  dsr_c      <= mcr_q(UART_MC_DTR)  when (loopback = '1') else dsr_pad_i;
  ri_c       <= mcr_q(UART_MC_OUT1) when (loopback = '1') else ri_pad_i;
  dcd_c      <= mcr_q(UART_MC_OUT2) when (loopback = '1') else dcd_pad_i;

  dlab       <= lcr_q(UART_LC_DL);   -- 7: divisor latch access bit
  loopback   <= mcr_q(4);

  -- assign modem outputs
  rts_pad_o <= mcr_q(UART_MC_RTS);
  dtr_pad_o <= mcr_q(UART_MC_DTR);

  -- handle loopback
  serial_in <= serial_out when (loopback = '1') else srx_pad;
  stx_pad_o <= '1' when (loopback = '1')        else serial_out;



  transmitter : uart_transmitter
    generic map ( FIFO_WIDTH   => TX_FIFO_WIDTH,
                  FIFO_DEPTH   => TX_FIFO_DEPTH,
                  FIFO_PNTR_W  => TX_FIFO_PNTR_W )
    port map (clk_i       => clk_i,
              xrst_i      => xrst_i,
              lcr_i       => lcr_q,
              tf_push_i   => tf_push_q,
              pp_data_i   => pp_data_buf_q,
              enable_i    => enable_q,
              tx_reset_i  => tx_reset_q,
              lsr_mask_i  => lsr_mask_w,
              stx_pad_o   => serial_out,
              tstate_o    => txstate_w,
              tf_count_o  => tf_count );


  -- Receiver Instance
  receiver : uart_receiver
    generic map ( FIFO_WIDTH   => RX_FIFO_WIDTH,
                  FIFO_DEPTH   => RX_FIFO_DEPTH,
                  FIFO_PNTR_W  => RX_FIFO_PNTR_W )
    port map ( clk_i            => clk_i,
               xrst_i           => xrst_i,
               lcr_i            => lcr_q,
               rxf_pop_i        => rxf_pop_q,
               srx_pad_i        => serial_in,
               enable_i         => enable_q, 
               rx_reset_i       => rx_reset_q,
               lsr_mask_i       => lsr_mask_w,
               --
               counter_t_o      => counter_t,
               rxf_count_o      => rxf_count_w,
               rxf_data_out_o   => rxf_data_out,
               rxf_error_bit_o  => rxf_error_bit,
               rxf_overrun_o    => rxf_overrun,
               rxstate_o        => OPEN,                      -- NC
               rxf_push_pulse_o => rxf_push_pulse );


  --  asynchronous reading here because the pp_* inputs are retimed in
  --  higher level
  p_c_omux : process (dl_q, dlab, ier_q, iir_q, scratch_q, lcr_q, lsr7_0_q,
                      msr_q, rxf_data_out, pp_addr_i, pp_rd_i)
    -- asynchrounous reading
  begin
    pp_data_o <= X"00";
    if (pp_rd_i = '1') then
      case pp_addr_i is
        when UART_REG_RB  =>                                  -- "000"
          if (dlab = '1') then
            pp_data_o <=  dl_q(UART_DL1'range);
          else
            pp_data_o <=  rxf_data_out(10 downto 3);
          end if;
        when UART_REG_IE  =>                                  -- "001"
          if (dlab = '1') then
            pp_data_o <=  dl_q(UART_DL2'range);
          else
            pp_data_o <=  ("0000" & ier_q);
          end if;
        when UART_REG_II  => pp_data_o <= ("1100" & iir_q);   -- "010"
        when UART_REG_LC  => pp_data_o <= lcr_q;
        when UART_REG_LS  => pp_data_o <= lsr7_0_q;           -- "101"
        when UART_REG_MS  => pp_data_o <= msr_q;
        when UART_REG_SR  => pp_data_o <= scratch_q;
        when others       => pp_data_o <= X"00";
      end case;
    end if;
  end process p_c_omux;


  p_pop : process (pp_rd_i, pp_addr_i,  dlab)
  begin
    rxf_pop_q   <= '0';
    if (pp_rd_i = '1' and pp_addr_i = UART_REG_RB and dlab = '0') then
      rxf_pop_q <= '1';            -- advance read pointer
    end if;
  end process p_pop;


  lsr_mask_condition_w <= (pp_rd_i and eq(pp_addr_i, UART_REG_LS) and
                           (not dlab));
  iir_read   <= (pp_rd_i and eq(pp_addr_i, UART_REG_II) and (not dlab));
  msr_read   <= (pp_rd_i and eq(pp_addr_i, UART_REG_MS) and (not dlab));
  fifo_read  <= (pp_rd_i and eq(pp_addr_i, UART_REG_RB) and (not dlab));
  fifo_write <= (pp_wr_i and eq(pp_addr_i, UART_REG_TR) and (not dlab));


  --  lsr_mask_buf_q delayed signal handling
  p_lsr : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        lsr_mask_buf_q <= '0';
      else --  reset bits in the Line Status Register
        lsr_mask_buf_q <= lsr_mask_condition_w;
      end if;
    end if;
  end process p_lsr;


  --  lsr_mask is rise detected
  lsr_mask_w <= lsr_mask_condition_w and (not lsr_mask_buf_q);


  --  msi_reset_q signal handling
  p_msi : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        msi_reset_q <= '1';
      elsif (msi_reset_q = '1') then
        msi_reset_q <= '0';
      elsif (msr_read = '1') then
        msi_reset_q <= '1';        -- reset bits in Modem Status Register
      end if;
    end if;
  end process p_msi;


  --
  --   WRITES AND RESETS
  --
  --  line control register
  p_lcr : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        lcr_q <= "00000011";     -- 8n1 setting
      elsif (pp_wr_i = '1' and pp_addr_i = UART_REG_LC) then
        lcr_q <= pp_data_i;
      end if;
    end if;
  end process p_lcr;


   
  --  FIFO control register and rx_reset_q, tx_reset_q signals
  p_rst : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        fcr_q      <= "11";
        rx_reset_q <= '0';
        tx_reset_q <= '0';
      elsif (pp_wr_i = '1' and pp_addr_i = UART_REG_FC) then
        fcr_q      <= pp_data_i(7 downto 6);
        rx_reset_q <= pp_data_i(1);
        tx_reset_q <= pp_data_i(2);
      else
        rx_reset_q <= '0';
        tx_reset_q <= '0';
      end if;
    end if;
  end process p_rst;


  --  modem control register
  p_mcr : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        mcr_q <= "00000";
      elsif (pp_wr_i = '1' and pp_addr_i = UART_REG_MC) then
        mcr_q <= pp_data_i(4 downto 0);
      end if;
    end if;
  end process p_mcr;


  --  scratch register
  --  line control register
  p_scratch : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        scratch_q <= X"00";    -- 8n1 setting
      elsif (pp_wr_i = '1' and pp_addr_i = UART_REG_SR) then
        scratch_q <= pp_data_i;
      end if;
    end if;
  end process p_scratch;


  --  interrupt enable register or UART_DL2
  p_ier : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        ier_q <= "0000";                      -- no interrupts after reset
        if (defined_PRESCALER_PRESET_HARD) then
          dl_q(UART_DL2'range) <= PRESCALER_HIGH_PRESET;
        else
          dl_q(UART_DL2'range) <= X"00";
        end if;
      else
        if (pp_wr_i = '1' and pp_addr_i = UART_REG_IE) then
          if (dlab = '1') then
            if (defined_PRESCALER_PRESET_HARD) then
              dl_q(UART_DL2'range) <= dl_q(UART_DL2'range);
            else
              dl_q(UART_DL2'range) <= pp_data_i;
            end if;
          else
            ier_q <= pp_data_i(3 downto 0); -- ier uses only 4 lsb
          end if;
        end if;
      end if;
    end if;
  end process p_ier;


  --  TX_FIFO or UART_DL1
  p_dl1 : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        if (defined_PRESCALER_PRESET_HARD) then
          dl_q(UART_DL1'range)  <= PRESCALER_LOW_PRESET;
        else
          dl_q(UART_DL1'range)  <= X"00";
        end if;
        start_dlc_q   <= '0';
        tf_push_q     <= '0';
        pp_data_buf_q <= (others => '0');
      elsif (pp_wr_i = '1' and pp_addr_i = UART_REG_TR) then
        if (dlab = '1') then
          if (defined_PRESCALER_PRESET_HARD) then
            dl_q(UART_DL1'range) <= dl_q(UART_DL1'range);
          else
            dl_q(UART_DL1'range) <= pp_data_i;
          end if;
          start_dlc_q <= '1'; -- enable DL counter
          tf_push_q   <= '0';
        else
          start_dlc_q <= '0';
          tf_push_q   <= '1';
          pp_data_buf_q <= pp_data_i;
        end if; 
      else
        start_dlc_q <= '0';
        tf_push_q   <= '0';
      end if;
    end if;
  end process p_dl1;



  --  receiver FIFO trigger level selection logic (asynchronous mux)
  p_c_trglev : process (fcr_q)
  begin
    case fcr_q(UART_FC_TL'range) is
      when "00"   => trigger_level <= "0001";   -- 1;
      when "01"   => trigger_level <= "0100";   -- 4;
      when "10"   => trigger_level <= "1000";   -- 8;
      when "11"   => trigger_level <= "1110";   -- 14;
      when others =>
        null;
    end case;
  end process p_c_trglev;
  

  --
  --  STATUS REGISTERS
  --

  --  modem status register
  p_msr : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        msr_q                             <= X"00";
        delayed_modem_signals_q(3 downto 0) <= "0000";
      else
        if (msi_reset_q = '1') then
          msr_q(UART_MS_DDCD downto UART_MS_DCTS) <= X"0";
        else
           msr_q(UART_MS_DDCD downto UART_MS_DCTS) <=
             msr_q(UART_MS_DDCD downto UART_MS_DCTS) or
             ((dcd & ri & dsr & cts) xor delayed_modem_signals_q(3 downto 0));
        end if;
        msr_q(UART_MS_CDCD downto UART_MS_CCTS) <= dcd_c & ri_c & dsr_c & cts_c;
        delayed_modem_signals_q(3 downto 0)     <= dcd & ri & dsr & cts;
      end if;
    end if;
  end process p_msr;


  --  line status register

  --  activation conditions
  --  data in receiver fifo available set condition
  lsr0 <= '1' when (unsigned(rxf_count_w) = 0 and rxf_push_pulse = '1')
          else '0';
  lsr1 <= rxf_overrun;     -- receiver overrun error
  lsr2 <= rxf_data_out(1); -- parity error bit
  lsr3 <= rxf_data_out(0); -- framing error bit
  lsr4 <= rxf_data_out(2); -- break error in the character
  lsr5 <= eq(tf_count, "00000") and thre_set_en; -- transmitter fifo is empty
  lsr6 <= eq(tf_count, "00000") and thre_set_en and -- transmitter empty
          eq(txstate_w, "000");  -- /*`S_IDLE */ = 0  
  lsr7 <= rxf_error_bit or rxf_overrun;

  lsr7_0_q <=  ( lsr7r_q & lsr6r_q & lsr5r_q & lsr4r_q &
                 lsr3r_q & lsr2r_q & lsr1r_q & lsr0r_q );
  


  --  lsr bit0 (receiver data available)
  p_lsr0 : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        lsr0_d_q <= '0';           -- delayed
        lsr0r_q  <= '0';
      else
        lsr0_d_q <= lsr0;          -- delayed
        --
        if ( ( unsigned(rxf_count_w) = 1 and rxf_pop_q = '1' and
               rxf_push_pulse = '0' ) or
             rx_reset_q = '1' ) then
          lsr0r_q <= '0'; -- deassert condition
        else
          -- set on rise of lsr0 and keep asserted until deasserted
          lsr0r_q <= lsr0r_q or (lsr0 and (not lsr0_d_q));
        end if;
      end if;
    end if;
  end process p_lsr0;


  --  lsr bit 1 (receiver overrun)
  p_lsr1 : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        lsr1_d_q <= '0';           -- delayed
        lsr1r_q  <= '0';
      else
        lsr1_d_q <= lsr1;          -- delayed
        --
        if (lsr_mask_w = '1') then
          lsr1r_q <= '0';
        else
          lsr1r_q  <= lsr1r_q or (lsr1 and (not lsr1_d_q)); -- set on rise
      end if;
      end if;
    end if;
  end process p_lsr1;


  --  lsr bit 2 (parity error)
  p_lsr2 : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        lsr2_d_q <= '0';           -- delayed
        lsr2r_q  <= '0';
      else
        lsr2_d_q <= lsr2;          -- delayed
        --
        if (lsr_mask_w = '1') then
          lsr2r_q <= '0';
        else
          lsr2r_q <= lsr2r_q or (lsr2 and (not lsr2_d_q)); -- set on rise
        end if;
      end if;
    end if;
  end process p_lsr2;


  --  lsr bit 3 (framing error)
  p_lsr3 : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        lsr3_d_q <= '0';           -- delayed
        lsr3r_q  <= '0';
      else
        lsr3_d_q <= lsr3;          -- delayed
        --
        if (lsr_mask_w = '1') then
          lsr3r_q <= '0';
        else
          lsr3r_q <= lsr3r_q or (lsr3 and (not lsr3_d_q)); -- set on rise
        end if;
      end if;
    end if;
  end process p_lsr3;


  --  lsr bit 4 (break indicator)
  p_lsr4 : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        lsr4_d_q <= '0';           -- delayed
        lsr4r_q  <= '0';
    else
      lsr4_d_q <= lsr4;            -- delayed
      if (lsr_mask_w = '1') then
        lsr4r_q <= '0';
      else
        lsr4r_q <= lsr4r_q or (lsr4 and (not lsr4_d_q));
        end if;
      end if;
    end if;
  end process p_lsr4;


  --  lsr bit 5 (transmitter fifo is empty)
  p_lsr5 : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        lsr5_d_q <= '1';           -- delayed
        lsr5r_q  <= '1';
      else
        lsr5_d_q <= lsr5;          -- delayed
        if (fifo_write = '1') then
          lsr5r_q <= '0';
        else
          lsr5r_q <= lsr5r_q or (lsr5 and (not lsr5_d_q));
        end if;
      end if;
    end if;
  end process p_lsr5;


  --  lsr bit 6 (transmitter empty indicator)
  p_lsr6 : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        lsr6_d_q <= '1';           -- delayed
        lsr6r_q  <= '1';
      else
        lsr6_d_q <= lsr6;          -- delayed
        if (fifo_write= '1') then
          lsr6r_q <= '0';
        else
          lsr6r_q <=  lsr6r_q or (lsr6 and (not lsr6_d_q));
        end if;
      end if;
    end if;
  end process p_lsr6;


  --  lsr bit 7 (error in fifo)
  p_lsr7 : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        lsr7_d_q <= '0';           -- delayed
        lsr7r_q  <= '0';
      else
        lsr7_d_q <= lsr7;          -- delayed
        if (lsr_mask_w = '1') then
          lsr7r_q <= '0';
        else
          lsr7r_q <= lsr7r_q or (lsr7 and (not lsr7_d_q));
        end if;
      end if;
    end if;
  end process p_lsr7;



  --  frequency divider
  p_dlc : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        dlc_q <= X"0000";
      elsif ( start_dlc_q = '1' or unsigned(dlc_q) = 0 ) then
        dlc_q <= dec1u(dl_q);     --  - 1   - 0x0001 -preset counter
      else
        dlc_q <= dec1u(dlc_q);    -- - 1    - 0x0001 -decrement cntr
      end if;
    end if;
  end process p_dlc;


  --  enable signal generation logic
  p_ena : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        enable_q <= '0';
      elsif ((unsigned(dl_q) /= 0) and unsigned(dlc_q) = 0) then
        -- dl_q > 0  and dlc_q=0
        enable_q <= '1';
      else
        enable_q <= '0';
      end if;
    end if;
  end process p_ena;



  --  delaying THRE status for one character cycle after
  --  a character is written to an empty fifo.
  p_c_lcr : process (lcr_q)
  begin
    case lcr_q(3 downto 0) is
      when "0000"                           =>
        block_value <=  X"5F";                        --   95 - 6 bits
      when "0100"                           =>
        block_value <=  X"67";                        --  103 - 6.5 bits
      when "0001" | "1000"                  =>
        block_value <=  X"6F";                        --  111 - 7 bits
      when "1100"                           =>
        block_value <=  X"77";                        --  119 - 7.5 bits
      when "0010" | "0101" | "1001"         =>
        block_value <=  X"7F";                        --  127 - 8 bits
      when "0011" | "0110" | "1010"| "1101" =>
        block_value <=  X"8F";                        --  143 - 9 bits
      when "0111" | "1011" | "1110"         =>
        block_value <=  X"9F";                        --  159 - 10 bits
      when "1111"                           =>
        block_value <=   X"AF";                       --  175 - 11 bits
      when others                           =>
        null;
    end case;
  end process p_c_lcr;



  --  counting time of one character minus stop bit
  p_bcnt : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        block_cnt_q <= X"00";
      elsif(lsr5r_q = '1' and fifo_write = '1') then
        -- THRE bit set & write to fifo occured
        block_cnt_q <= block_value;
      elsif ( enable_q = '1' and block_cnt_q /= X"00" ) then
        -- only work on enable times
        -- decrement break counter
        block_cnt_q <= dec1u(block_cnt_q);            -- - 1    - X"01"
      end if;  -- always of break condition detection
    end if;
  end process p_bcnt;


  -- Generating THRE status enable signal
  thre_set_en <= '1' when (unsigned(block_cnt_q) = 0) else '0';


  --
  --    INTERRUPT LOGIC
  --
  rls_int  <= ier_q(UART_IE_RLS) and
              ( lsr7_0_q(UART_LS_OE) or lsr7_0_q(UART_LS_PE) or
                lsr7_0_q(UART_LS_FE) or lsr7_0_q(UART_LS_BI) );


  rda_int  <= ier_q(UART_IE_RDA) when
              ( unsigned(rxf_count_w) >= unsigned(trigger_level) )
              else '0';
  
  thre_int <= ier_q(UART_IE_THRE) and lsr7_0_q(UART_LS_TFE);
  ms_int   <= ier_q(UART_IE_MS) and (not_zero(msr_q(3 downto 0)));
  ti_int   <= ier_q(UART_IE_RDA) and (eq(counter_t, "0000000000")) and
              (not_zero(rxf_count_w));



  --  delay lines
  p_dlines : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        rls_int_d  <= '0';
        rda_int_d  <= '0';
        thre_int_d <= '0';
        ms_int_d   <= '0';
        ti_int_d   <= '0';
      else
        rls_int_d  <= rls_int;
        rda_int_d  <= rda_int;
        thre_int_d <= thre_int;
        ms_int_d   <= ms_int;
        ti_int_d   <= ti_int;
      end if;
    end if;
  end process p_dlines;


  --  rise detection signals
  rda_int_rise    <= rda_int  and (not rda_int_d);
  rls_int_rise    <= rls_int  and (not rls_int_d);
  thre_int_rise   <= thre_int and (not thre_int_d);
  ms_int_rise     <= ms_int   and (not ms_int_d);
  ti_int_rise     <= ti_int   and (not ti_int_d);


  --  interrupt pending flags assignments
  p_rlsintpflag : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        rls_int_pnd_q <= '0';
      else
        if (lsr_mask_w = '1') then
          rls_int_pnd_q <= '0';             -- reset condition
        else
          if (rls_int_rise = '1') then
            rls_int_pnd_q <= '1';          -- latch condition
          else
            --  default operation: remove if masked
            rls_int_pnd_q <= rls_int_pnd_q and ier_q(UART_IE_RLS);
          end if;
        end if;
      end if;
    end if;
  end process p_rlsintpflag;


  p_rdsintpflag : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        rda_int_pnd_q <= '0';
      else 
        if ( (unsigned(rxf_count_w) = unsigned('0' & trigger_level)) and
             fifo_read = '1') then
          rda_int_pnd_q <= '0';                    -- reset condition
        else
          if (rda_int_rise = '1') then
            rda_int_pnd_q <= '1';                  -- latch condition
          else
            --  default operation: remove if masked
            rda_int_pnd_q <= rda_int_pnd_q and ier_q(UART_IE_RDA);
          end if;
        end if;
      end if;
    end if;
  end process p_rdsintpflag;



  p_threintpflag : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        thre_int_pnd_q <= '0';
      else 
        if ( fifo_write = '1' or
             (iir_read = '1' and iir_q(UART_II_IP) = '0' and
              iir_q(UART_II_II'range) = UART_II_THRE) ) then
          thre_int_pnd_q <= '0';
        else
          if (thre_int_rise = '1') then
            thre_int_pnd_q <= '1';
          else
            thre_int_pnd_q <= thre_int_pnd_q and ier_q(UART_IE_THRE);
          end if;
        end if;
      end if;
    end if;
  end process p_threintpflag;


  p_msintpflag : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        ms_int_pnd_q <= '0';
      elsif (msr_read = '1') then
        ms_int_pnd_q <= '0';
      elsif (ms_int_rise = '1') then
        ms_int_pnd_q <= '1';
      else
        ms_int_pnd_q <= ms_int_pnd_q and ier_q(UART_IE_MS);
      end if;
    end if;
  end process p_msintpflag;


  p_tiintpflag : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        ti_int_pnd_q <= '0';
      elsif (fifo_read = '1') then
        ti_int_pnd_q <=  '0';
      elsif (ti_int_rise = '1') then
        ti_int_pnd_q <= '1';
      else
        ti_int_pnd_q <= ti_int_pnd_q and ier_q(UART_IE_RDA);
      end if;
    end if;
  end process p_tiintpflag;
  --  end of pending flags


  --  INT_O logic
  p_int : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        int_o <= '0';
      else
        if (rls_int_pnd_q = '1') then
          int_o <= not lsr_mask_w;
        elsif (rda_int_pnd_q = '1') then
          int_o <= '1';
        elsif (ti_int_pnd_q = '1') then
          int_o <= not fifo_read;
        elsif (thre_int_pnd_q = '1') then
          int_o <= not (fifo_write and iir_read);
        elsif (ms_int_pnd_q = '1') then
          int_o <= not msr_read;
        else
          int_o <= '0';    -- if no interrupt are pending
        end if;
      end if;
    end if;
  end process p_int;



  --  interrupt identification register
  --   3:1 : int identification
  --     0 : int pending (when '0')
  p_iir : process (clk_i)
  begin
    if (rising_edge(clk_i)) then
      if (xrst_i = '0') then
        iir_q <= "0001";
      else
        if (rls_int_pnd_q = '1') then    -- interrupt is pending
          --  set identification register to correct value
          iir_q(UART_II_II'range) <= UART_II_RLS;   -- 011, receiver Line Status
          --  and clear the IIR bit 0 (interrupt pending)
          iir_q(UART_II_IP) <= '0';
        else
          --  the sequence of conditions determines priority of
          --  interrupt identification
          if (rda_int = '1') then                    -- 010,
            iir_q(UART_II_II'range) <= UART_II_RDA;  -- receiver data available
            iir_q(UART_II_IP) <= '0';
          elsif (ti_int_pnd_q = '1') then
            iir_q(UART_II_II'range) <= UART_II_TI;   -- 110, timeout indication
            iir_q(UART_II_IP) <= '0';
          elsif (thre_int_pnd_q = '1') then
            iir_q(UART_II_II'range) <= UART_II_THRE; -- 001, transmitter holding
            iir_q(UART_II_IP) <= '0';
          elsif (ms_int_pnd_q = '1') then
            iir_q(UART_II_II'range) <= UART_II_MS;   -- 000, modem status
            iir_q(UART_II_IP) <= '0';
          else    -- no interrupt is pending
            iir_q(UART_II_II'range) <= (others => '0');  -- 3:1 => "000"
            iir_q(UART_II_IP) <= '1';                    --   0 => '1'
          end if;
        end if;
      end if;
    end if;
  end process p_iir;
 
end rtl;
