-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 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.
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--                               RISCV core
--                                 V1.1
--
--
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

-- - - - - - - -
--  libraries
-- - - - - - - -
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.rv_pkg.all;


entity dbg_bridge_uart is
  generic ( UART_DIVISOR_W  : natural := 9 );
  port ( clk_i       : in  std_logic;
         xrst_i      : in  std_logic;
         --  control
         bit_div_i   : in  slv_t(UART_DIVISOR_W-1 downto 0);
         stop_bits_i : in  std_logic;        -- 0 = 1, 1 = 2
         --  transmit
         wr_i        : in  std_logic;
         data_i      : in  slv_07_00;
         tx_busy_o   : out std_logic;
         --  receive
         rd_i        : in  std_logic;
         data_o      : out slv_07_00;
         rx_ready_o  : out std_logic;
         rx_err_o    : out std_logic;
         --  UART pins
         rxd_i       : in  std_logic;
         txd_o       : out std_logic
         );
end entity dbg_bridge_uart;


architecture rtl of dbg_bridge_uart is

  constant START_BIT : slv_03_00 := "0000";   -- d0
  constant STOP_BIT0 : slv_03_00 := "1001";   -- d9
  constant STOP_BIT1 : slv_03_00 := "1010";   -- d10

  -- - - - - - - - - - - - -
  --  registers / wires
  -- - - - - - - - - - - - -
  --  TX signals
  signal tx_busy_q      : std_logic;
  signal tx_bits_q      : slv_03_00;
  signal tx_count_q     : slv_t(UART_DIVISOR_W-1 downto 0);
  signal tx_shift_reg_q : slv_07_00;
  signal txd_q          : std_logic;

  --  RX signals
  signal rxd_q          : std_logic;
  signal rx_data_q      : slv_07_00;
  signal rx_bits_q      : slv_03_00;
  signal rx_count_q     : slv_t(UART_DIVISOR_W-1 downto 0);
  signal rx_shift_reg_q : slv_07_00;
  signal rx_ready_q     : std_logic;
  signal rx_busy_q      : std_logic;

  signal rx_err_q       : std_logic;

  --  re-sync RXD
  signal rxd_ms_q       : std_logic;

  --  RX clock divider
  signal rx_sample_w    : std_logic;

  --  TX clock divider
  signal tx_sample_w    : std_logic;

  --  UART TX pin
  signal txd_w          : std_logic;

begin

  -- - - - - - - - - - - - -
  --  re-sync RXD
  -- - - - - - - - - - - - -
  p_rx_meta : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        rxd_ms_q <= '1';
        rxd_q    <= '1';
      else
        rxd_ms_q <= rxd_i;
        rxd_q    <= rxd_ms_q;
      end if;
    end if;
  end process p_rx_meta;


  -- - - - - - - - - - - - -
  --  RX clock divider
  -- - - - - - - - - - - - -
  rx_sample_w <= is_zero(rx_count_q);


  p_rx_ckdiv : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        rx_count_q     <= (others => '0');
      else
        --  inactive
        if (rx_busy_q = '0') then
          rx_count_q    <= ('0' & bit_div_i(UART_DIVISOR_W-1 downto 1));
          --  RX bit timer
        elsif (not_zero(rx_count_q)) then
          rx_count_q    <= dec1u(rx_count_q);
          --  active
        elsif (rx_sample_w = '1') then
          --  last bit?
          if ( (rx_bits_q = STOP_BIT0 and stop_bits_i = '0') or
               (rx_bits_q = STOP_BIT1 and stop_bits_i = '1') ) then
            rx_count_q    <= (others => '0');
          else
            rx_count_q    <= bit_div_i;
          end if;
        end if;
      end if;
    end if;
  end process p_rx_ckdiv;

  
  -- - - - - - - - - - - - -
  --  RX shift register
  -- - - - - - - - - - - - -
  p_rx_sft : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        rx_shift_reg_q <= X"00";
        rx_busy_q      <= '0';
      --  RX busy
      else
        if (rx_busy_q = '1' and rx_sample_w = '1') then
          --  last bit?
          if (rx_bits_q = STOP_BIT0 and stop_bits_i = '0') then
            rx_busy_q <= '0';
          elsif (rx_bits_q = STOP_BIT1 and stop_bits_i = '1') then
            rx_busy_q <= '0';
          elsif (rx_bits_q = START_BIT) then
            --  start bit should still be low as sampling mid
            --  way through start bit, so if high, error!
            if (rxd_q = '1') then
              rx_busy_q <= '0';
            end if;
            --  RX shift register
          else 
            rx_shift_reg_q <= (rxd_q & rx_shift_reg_q(7 downto 1));
          end if;
          --  start bit?
        elsif (rx_busy_q = '0' and rxd_q = '0') then
          rx_shift_reg_q <= X"00";
          rx_busy_q      <= '1';
        end if;
      end if;
    end if;
  end process p_rx_sft;



  p_rx_bits : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        rx_bits_q  <= START_BIT;
      else
        if (rx_sample_w = '1' and rx_busy_q = '1') then
          if ((rx_bits_q = STOP_BIT1 and stop_bits_i = '1') or
              (rx_bits_q = STOP_BIT0 and stop_bits_i = '0')) then
            rx_bits_q <= START_BIT;
          else
            rx_bits_q <= inc1u(rx_bits_q);
          end if;
        elsif ( rx_busy_q = '0' and is_zero(bit_div_i) ) then
          rx_bits_q  <= inc1u(START_BIT);
        elsif (rx_busy_q = '0') then
          rx_bits_q  <= START_BIT;

        end if;
      end if;
    end if;
  end process p_rx_bits;


  -- - - - - - - - - - - - -
  --  RX data
  -- - - - - - - - - - - - -
  p_rx_data : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        rx_ready_q      <= '0';
        rx_data_q       <= X"00";
        rx_err_q        <= '0';
      else
        --  if reading data, reset data state
        if (rd_i = '1') then
          rx_ready_q <= '0';
          rx_err_q   <= '0';
        end if;

        if (rx_busy_q = '1' and rx_sample_w = '1') then
          --  stop bit
          if ((rx_bits_q = STOP_BIT1 and stop_bits_i = '1') or
              (rx_bits_q = STOP_BIT0 and stop_bits_i = '0')) then
            --  RXD should be still high
            if (rxd_q = '1') then
              rx_data_q      <= rx_shift_reg_q;
              rx_ready_q     <= '1';
              --  bad stop bit - wait for a full bit period
              --  before allowing start bit detection again
            else
              rx_ready_q     <= '0';
              rx_data_q      <= X"00";
              rx_err_q       <= '1';
            end if;
            --  mid start bit sample - if high then error
          elsif (rx_bits_q = START_BIT and rxd_q = '1') then
            rx_err_q        <= '1';
          end if;
        end if;
      end if;
    end if;
  end process p_rx_data;


  -- - - - - - - - - - - - -
  --  TX clock divider
  -- - - - - - - - - - - - -
  tx_sample_w <= is_zero(tx_count_q);


  p_tx_cnt : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        tx_count_q      <= (others => '0');
      else
        --  idle
        if (tx_busy_q = '0') then
          tx_count_q  <= bit_div_i;
          --  TX bit timer
        elsif (not_zero(tx_count_q)) then
          tx_count_q  <= dec1u(tx_count_q);
        elsif (tx_sample_w = '1') then
          tx_count_q  <= bit_div_i;
        end if;
      end if;
    end if;
  end process p_tx_cnt;


  -- - - - - - - - - - - - -
  --  TX shift register
  -- - - - - - - - - - - - -
  p_tx_sft : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        tx_shift_reg_q <= X"00";
        tx_busy_q      <= '0';
      --  TX busy
      else
        if (tx_busy_q = '1') then
          --  shift tx data
          if (tx_bits_q /= START_BIT and tx_sample_w = '1') then
            tx_shift_reg_q <= ('0' & tx_shift_reg_q(7 downto 1));
          end if;
          --  last bit?
          if (tx_bits_q = STOP_BIT0 and tx_sample_w  = '1' and
              stop_bits_i = '0') then
            tx_busy_q <= '0';
          elsif (tx_bits_q = STOP_BIT1 and tx_sample_w  = '1' and
                 stop_bits_i = '1') then
            tx_busy_q <= '0';
          end if;
          --  buffer data to transmit
        elsif (wr_i = '1') then
          tx_shift_reg_q <= data_i;
          tx_busy_q      <= '1';
        end if;
      end if;
    end if;
  end process p_tx_sft;


  p_tx_bits : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        tx_bits_q  <= X"0";
      else
        if (tx_sample_w = '1' and tx_busy_q = '1') then
          if ((tx_bits_q = STOP_BIT1 and stop_bits_i = '1') or
              (tx_bits_q = STOP_BIT0 and stop_bits_i = '0')) then
            tx_bits_q <= START_BIT;
          else
            tx_bits_q <= inc1u(tx_bits_q);
          end if;
        end if;
      end if;
    end if;
  end process p_tx_bits;


  -- - - - - - - - - - - - -
  --  UART TX pin
  -- - - - - - - - - - - - -
  p_c_tx : process (tx_busy_q, tx_bits_q,  tx_shift_reg_q(0))
  begin
    txd_w <= '1';
    if (tx_busy_q = '1') then
      --  start bit (TXD = L)
      if (tx_bits_q = START_BIT) then
        txd_w <= '0';
        --  stop bits (TXD = H)
      elsif (tx_bits_q = STOP_BIT0 or tx_bits_q = STOP_BIT1) then
        txd_w <= '1';
        --  data bits
      else
        txd_w <= tx_shift_reg_q(0);
      end if;
    end if;
  end process p_c_tx;


  p_tx : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        txd_q <= '1';
      else
        txd_q <= txd_w;
      end if;
    end if;
  end process p_tx;


  -- - - - - - - - - - - - -
  --  drive outputs
  -- - - - - - - - - - - - -
  tx_busy_o  <= tx_busy_q;
  rx_ready_o <= rx_ready_q;
  txd_o      <= txd_q;
  data_o     <= rx_data_q;
  rx_err_o   <= rx_err_q;

end rtl;
