-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 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 spi_lite_fifo is
  generic ( WIDTH   : natural := 8;
            DEPTH   : natural := 4;
            ADDR_W  : natural := 2 );
  port ( clk_i       : in  std_logic;
         xrst_i      : in  std_logic;
         --
         data_in_i   : in  slv_t(WIDTH-1 downto 0);
         push_i      : in  std_logic;
         pop_i       : in  std_logic;
         flush_i     : in  std_logic;
         --
         data_out_o  : out slv_t(WIDTH-1 downto 0);
         accept_o    : out std_logic;
         valid_o     : out std_logic
         );
end entity spi_lite_fifo;


architecture rtl of spi_lite_fifo is

  constant COUNT_W   : natural := ADDR_W + 1;

  type rfile_t is array (DEPTH-1 downto 0) of slv_t(WIDTH-1 downto 0);
  signal rfile_q     : rfile_t;
  signal rd_ptr_q    : slv_t(ADDR_W-1 downto 0);
  signal wr_ptr_q    : slv_t(ADDR_W-1 downto 0);
  signal count_q     : slv_t(COUNT_W-1 downto 0);

  signal accept_o_w  : std_logic;
  signal valid_o_w   : std_logic;

begin

  p_fifo : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        count_q   <= (COUNT_W-1 downto 0 => '0');
        rd_ptr_q  <= (ADDR_W-1 downto 0 => '0');
        wr_ptr_q  <= (ADDR_W-1 downto 0 => '0');
      else
        if (flush_i = '1') then
          count_q   <= (COUNT_W-1 downto 0 => '0');
          rd_ptr_q  <= (ADDR_W-1 downto 0 => '0');
          wr_ptr_q  <= (ADDR_W-1 downto 0 => '0');
        else
          --  push
          if (push_i = '1' and accept_o_w = '1') then
            rfile_q(to_uint(wr_ptr_q)) <= data_in_i;
            wr_ptr_q                   <= inc1u(wr_ptr_q);
          end if;

          --  pop
          if (pop_i = '1' and valid_o_w = '1') then
            rd_ptr_q      <= inc1u(rd_ptr_q);
          end if;

          --  count up
          if ( (push_i = '1' and accept_o_w = '1') and
               (pop_i = '0' or valid_o_w = '0') ) then
            count_q        <= inc1u(count_q);
            --  count down
          elsif ((push_i = '0' or accept_o_w = '0') and
                 (pop_i = '1' and valid_o_w = '1') ) then
              count_q     <= dec1u(count_q);
          end if;
        end if;
      end if;
    end if;
  end process p_fifo;


  valid_o_w    <= not_zero(count_q);
  valid_o      <= valid_o_w;
  accept_o_w   <= ne(count_q, DEPTH);
  accept_o     <= accept_o_w;

  data_out_o   <= rfile_q(to_uint(rd_ptr_q));

end rtl;



-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--                               RISCV core
--                                 V1.1
--
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

-- - - - - - - -
--  libraries
-- - - - - - - -


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


-- - - - - - - - - - - - -
--  module:  SPI-Lite peripheral (Xilinx IP emulation)
-- - - - - - - - - - - - -
entity vsspi_lite is
  port ( clk_i       : in  std_logic;
         xrst_i      : in  std_logic;
         --  vsbus
         pp_addr_i   : in  slv_07_00;
         pp_data_i   : in  slv_31_00;      -- ! NC [30:10]
         pp_rd_i     : in  std_logic;
         pp_wr_i     : in  slv_03_00;      -- ! NC [ 3:1]
         pp_data_o   : out slv_31_00;
         --
         spi_miso_i  : in  std_logic;
         spi_clk_o   : out std_logic;
         spi_cs_o    : out std_logic;
         spi_mosi_o  : out std_logic;
         intr_o      : out std_logic
        );
end entity vsspi_lite;


architecture rtl of vsspi_lite is

  --  control register
  constant SPI_CR                        : slv_07_00 := X"60";
  constant SPI_CR_LOOP_DEFAULT           : std_logic := '0';
  constant SPI_CR_LOOP_B                 : natural   := 0;

  --  enable
  constant SPI_CR_SPE_DEFAULT            : std_logic := '0';
  constant SPI_CR_SPE_B                  : natural   := 1;

  constant SPI_CR_MASTER_DEFAULT         : std_logic := '0';
  constant SPI_CR_MASTER_B               : natural   := 2;

  constant SPI_CR_CPOL_DEFAULT           : std_logic := '0';
  constant SPI_CR_CPOL_B                 : natural   := 3;

  constant SPI_CR_CPHA_DEFAULT           : std_logic := '0';
  constant SPI_CR_CPHA_B                 : natural   := 4;

  constant SPI_CR_TXFIFO_RST_DEFAULT     : std_logic := '0';
  constant SPI_CR_TXFIFO_RST_B           : natural   := 5;

  constant SPI_CR_RXFIFO_RST_DEFAULT     : std_logic := '0';
  constant SPI_CR_RXFIFO_RST_B           : natural   := 6;

  constant SPI_CR_MANUAL_SS_DEFAULT      : std_logic := '0';
  constant SPI_CR_MANUAL_SS_B            : natural   := 7;

  constant SPI_CR_TRANS_INHIBIT_DEFAULT  : std_logic := '0';
  constant SPI_CR_TRANS_INHIBIT_B        : natural   := 8;

  constant SPI_CR_LSB_FIRST_DEFAULT      : std_logic := '0';
  constant SPI_CR_LSB_FIRST_B            : natural   := 9;

  --  status register
  constant SPI_SR                        : slv_07_00 := X"64";
  constant SPI_SR_RX_EMPTY_B             : natural   := 0;
  -- receive FIFO is full
  constant SPI_SR_RX_FULL_B              : natural   := 1;
  -- transmit FIFO is empty
  constant SPI_SR_TX_EMPTY_B             : natural   := 2;
  -- transmit FIFO is full
  constant SPI_SR_TX_FULL_B              : natural   := 3;

  -- mode fault error
  -- constant SPI_SR_MODE_FAULT          : natural   := 4;   -- NOT IMPLEMENTED

  -- data transmit register
  constant SPI_DTR                       : slv_07_00 := X"68";
  constant SPI_DTR_DATA_R                : slv_07_00 := X"00";

  -- data receive register
  constant SPI_DRR                       : slv_07_00 := X"6C";
  constant SPI_DRR_DATA_R                : slv_07_00 := X"00";

  -- 32-bit slave select register
  constant SPI_SSR                       : slv_07_00 := X"70";
  constant SPI_SSR_VALUE_DEFAULT         : std_logic := '1';
  constant SPI_SSR_VALUE_B               : natural   := 0;


  -- IPIF (SPI) global int enable reg
  constant SPI_DGIER                     : slv_07_00 := X"1C";
  --
  constant SPI_DGIER_GIE_DEFAULT         : std_logic := '0';
  constant SPI_DGIER_GIE_B               : natural   := 31;

  -- IPIF (SPI) interrupt status reg
  constant SPI_IPISR                     : slv_07_00 := X"20";
  constant SPI_IPISR_TX_EMPTY_B          : natural   := 2;

  -- IPIF (SPI) interrupt enable reg
  constant SPI_IPIER                     : slv_07_00 := X"28";
  constant SPI_IPIER_TX_EMPTY_DEFAULT    : std_logic := '0';
  constant SPI_IPIER_TX_EMPTY_B          : natural   := 2;

  -- IPIF (SPI) reset register
  constant SPI_SRR                       : slv_07_00 := X"40";
  constant SPI_SRR_RESET_DEFAULT         : std_logic := '0';
  constant SPI_SRR_RESET_R               : slv_31_00 := X"00000000";


  constant C_SCK_RATIO                   : slv_31_00 := X"00000002";


  component spi_lite_fifo
    generic ( WIDTH   : natural := 8;
              DEPTH   : natural := 4;
              ADDR_W  : natural := 2 );
    port ( clk_i       : in  std_logic;
           xrst_i      : in  std_logic;
           --  inputs
           data_in_i   : in  slv_t(WIDTH-1 downto 0);
           push_i      : in  std_logic;
           pop_i       : in  std_logic;
           flush_i     : in  std_logic;
           --  outputs
           data_out_o  : out slv_t(WIDTH-1 downto 0);
           accept_o    : out std_logic;
           valid_o     : out std_logic );
  end component;


  --  retime write data in higher level

  --  spi_dgier_gie [internal]
  signal spi_dgier_gie_q             : std_logic;

  --  spi_ipier_tx_empty [internal]
  signal spi_ipier_tx_empty_q        : std_logic;

  --  spi_srr_reset [auto_clr]
  signal spi_srr_reset_q             : slv_31_00;

  --  spi_cr_loop [internal]
  signal spi_cr_loop_q               : std_logic;
  --  spi_cr_spe [internal]
  signal spi_cr_spe_q                : std_logic;
  --  spi_cr_master [internal]
  signal spi_cr_master_q             : std_logic;
  --  spi_cr_cpol [internal]
  signal spi_cr_cpol_q               : std_logic;

  --  spi_cr_cpha [internal]
  signal spi_cr_cpha_q               : std_logic;

  --  spi_cr_txfifo_rst [auto_clr]
  signal spi_cr_txfifo_rst_q         : std_logic;

  --  spi_cr_rxfifo_rst [auto_clr]
  signal spi_cr_rxfifo_rst_q         : std_logic;
  --  spi_cr_manual_ss [internal]
  signal spi_cr_manual_ss_q          : std_logic;
  --  spi_cr_trans_inhibit [internal]
  signal spi_cr_trans_inhibit_q      : std_logic;
  --  spi_cr_lsb_first [internal]
  signal spi_cr_lsb_first_q          : std_logic;

  --  spi_ssr_value [internal]
  signal spi_ssr_value_q             : std_logic;

  signal spi_sr_rx_empty_in_w        : std_logic;
  signal spi_sr_rx_full_in_w         : std_logic;
  signal spi_sr_tx_empty_in_w        : std_logic;
  signal spi_sr_tx_full_in_w         : std_logic;
  signal spi_drr_data_in_w           : slv_07_00;

  --  read mux
  signal data_w                      : slv_31_00;

  signal spi_drr_rd_req_w            : std_logic;
  signal spi_ipisr_wr_req_w          : std_logic;
  signal spi_cr_wr_req_w             : std_logic;
  signal spi_dtr_wr_req_w            : std_logic;

  --  TX FIFO
  signal sw_reset_w                  : std_logic;
  signal tx_fifo_flush_w             : std_logic;
  signal rx_fifo_flush_w             : std_logic;
  signal tx_accept_w                 : std_logic;
  signal tx_ready_w                  : std_logic;
  signal tx_data_raw_w               : slv_07_00;
  signal tx_pop_w                    : std_logic;

  --  reverse order if LSB first
  signal tx_data_w                   : slv_07_00;
  --  RX FIFO
  signal rx_accept_w                 : std_logic;
  signal rx_ready_w                  : std_logic;
  signal rx_data_w                   : slv_07_00;
  signal rx_push_w                   : std_logic;

  --  registers
  signal active_q                    : std_logic;
  signal bit_count_q                 : slv_05_00;
  signal shift_reg_q                 : slv_07_00;
  signal clk_div_q                   : slv_31_00;
  signal done_q                      : std_logic;
  signal spi_clk_q                   : std_logic;
  signal spi_mosi_q                  : std_logic;
  --  implementation
  signal enable_w                    : std_logic;
  --  something to do, SPI enabled...
  signal start_w                     : std_logic;
  --  loopback more or normal
  signal miso_w                      : std_logic;

  signal clk_en_w                    : std_logic;
  --  sample, drive pulse generation
  signal sample_w                    : std_logic;
  signal drive_w                     : std_logic;

  signal spi_mosi_o_w                : std_logic;

  --  delayed done_q for FIFO level check
  signal check_tx_level_q            : std_logic;
  --  interrupt
  signal intr_q                      : std_logic;

begin


  p_regs : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        spi_cr_loop_q           <= SPI_CR_LOOP_DEFAULT;
        spi_cr_spe_q            <= SPI_CR_SPE_DEFAULT;
        spi_cr_master_q         <= SPI_CR_MASTER_DEFAULT;
        spi_cr_cpol_q           <= SPI_CR_CPOL_DEFAULT;
        spi_cr_cpha_q           <= SPI_CR_CPHA_DEFAULT;
        spi_cr_txfifo_rst_q     <= SPI_CR_TXFIFO_RST_DEFAULT;
        spi_cr_rxfifo_rst_q     <= SPI_CR_RXFIFO_RST_DEFAULT;
        spi_cr_manual_ss_q      <= SPI_CR_MANUAL_SS_DEFAULT;
        spi_cr_trans_inhibit_q  <= SPI_CR_TRANS_INHIBIT_DEFAULT;
        spi_cr_lsb_first_q      <= SPI_CR_LSB_FIRST_DEFAULT;
        spi_ssr_value_q         <= SPI_SSR_VALUE_DEFAULT;
        spi_srr_reset_q         <= (31 downto 0 => SPI_SRR_RESET_DEFAULT);
        spi_ipier_tx_empty_q    <= SPI_IPIER_TX_EMPTY_DEFAULT;
        spi_dgier_gie_q         <= SPI_DGIER_GIE_DEFAULT;
      else
        --
        spi_cr_txfifo_rst_q <= SPI_CR_TXFIFO_RST_DEFAULT;          -- auto reset
        spi_cr_rxfifo_rst_q <= SPI_CR_RXFIFO_RST_DEFAULT;          -- auto reset
        spi_srr_reset_q <= (31 downto 0 => SPI_SRR_RESET_DEFAULT); -- auto reset
        --
        if (pp_wr_i(0) = '1') then
          case pp_addr_i is
            when SPI_CR =>                     -- 0x60      -- control register
              --  spi_cr_loop [internal]
              spi_cr_loop_q           <= pp_data_i(SPI_CR_LOOP_B);   -- 0
              --  spi_cr_spe [internal]
              spi_cr_spe_q            <= pp_data_i(SPI_CR_SPE_B);    -- 1 enable
              --  spi_cr_master [internal]
              spi_cr_master_q         <= pp_data_i(SPI_CR_MASTER_B); -- 2
              --  spi_cr_cpol [internal]
              spi_cr_cpol_q           <= pp_data_i(SPI_CR_CPOL_B);   -- 3
              --  spi_cr_cpha [internal]
              spi_cr_cpha_q           <= pp_data_i(SPI_CR_CPHA_B);   -- 4
              --  spi_cr_txfifo_rst [auto_clr]
              spi_cr_txfifo_rst_q     <= pp_data_i(SPI_CR_TXFIFO_RST_B);    -- 5
              --  spi_cr_rxfifo_rst [auto_clr]
              spi_cr_rxfifo_rst_q     <= pp_data_i(SPI_CR_RXFIFO_RST_B);    -- 6
              --  spi_cr_manual_ss [internal]
              spi_cr_manual_ss_q      <= pp_data_i(SPI_CR_MANUAL_SS_B);     -- 7
              --  spi_cr_trans_inhibit [internal]
              spi_cr_trans_inhibit_q  <= pp_data_i(SPI_CR_TRANS_INHIBIT_B); -- 8
              --  spi_cr_lsb_first [internal]
              spi_cr_lsb_first_q      <= pp_data_i(SPI_CR_LSB_FIRST_B);     -- 9
            when SPI_SSR =>                               -- slave select reg
              --  spi_ssr_value [internal]
              spi_ssr_value_q         <= pp_data_i(SPI_SSR_VALUE_B);
            when SPI_SRR =>                               -- reset register
              --  spi_srr_reset [auto_clr]
              spi_srr_reset_q         <= pp_data_i(SPI_SRR_RESET_R'range);
            when SPI_IPIER =>                            -- 0x28 int enable reg
              --  spi_ipier_tx_empty [internal]
              spi_ipier_tx_empty_q    <= pp_data_i(SPI_IPIER_TX_EMPTY_B);
            when  SPI_DGIER =>                     -- 0x1C global int enable reg
              --  spi_dgier_gie [internal]  (global int enable reg)
              spi_dgier_gie_q <= pp_data_i(SPI_DGIER_GIE_B);

            when SPI_DRR =>                            -- 0x6C data receive reg
              null;                                    --   read only
            when SPI_SR =>                             -- 0x64 status register
              null;                                    --   read only
           when others =>
              null;
          end case;
        end if;
      end if;
    end if;
  end process p_regs;



  -- - - - - - - - - - - - -
  --  read mux
  -- - - - - - - - - - - - -
  p_c_omux : process (pp_addr_i, spi_dgier_gie_q, spi_sr_tx_empty_in_w,
                      spi_ipier_tx_empty_q, spi_cr_loop_q, spi_cr_spe_q,
                      spi_cr_master_q, spi_cr_cpol_q, spi_cr_cpha_q,
                      spi_cr_manual_ss_q, spi_cr_trans_inhibit_q,
                      spi_cr_lsb_first_q, spi_sr_rx_empty_in_w,
                      spi_sr_rx_full_in_w, spi_sr_tx_full_in_w,
                      spi_drr_data_in_w, spi_ssr_value_q)
  begin
    --
    data_w <= X"00000000";
    --
    case pp_addr_i is
      when SPI_DGIER =>                    -- global int enable reg
        data_w(SPI_DGIER_GIE_B)         <= spi_dgier_gie_q;         -- 31
      when SPI_IPISR =>                    -- irq status
        data_w(SPI_IPISR_TX_EMPTY_B)    <= spi_sr_tx_empty_in_w;    -- 2
      when SPI_IPIER =>
        data_w(SPI_IPIER_TX_EMPTY_B)    <= spi_ipier_tx_empty_q;    -- 2
      when SPI_SRR =>
        null;
      when SPI_CR =>                                                -- 0x60
        data_w(SPI_CR_LOOP_B)           <= spi_cr_loop_q;           -- 0
        data_w(SPI_CR_SPE_B)            <= spi_cr_spe_q;            -- 1
        data_w(SPI_CR_MASTER_B)         <= spi_cr_master_q;         -- 2
        data_w(SPI_CR_CPOL_B)           <= spi_cr_cpol_q;           -- 3
        data_w(SPI_CR_CPHA_B)           <= spi_cr_cpha_q;           -- 4
        -- write only
--      data_w(SPI_CR_TXFIFO_RST_B)     <= spi_cr_txfifo_rst_q;     -- 5
        -- write only
--      data_w(SPI_CR_RXFIFO_RST_B)     <= spi_cr_rxfifo_rst_q;     -- 6
        data_w(SPI_CR_MANUAL_SS_B)      <= spi_cr_manual_ss_q;      -- 7
        data_w(SPI_CR_TRANS_INHIBIT_B)  <= spi_cr_trans_inhibit_q;  -- 8
        data_w(SPI_CR_LSB_FIRST_B)      <= spi_cr_lsb_first_q;      -- 9
      when SPI_SR =>                -- status register, read only
        data_w(SPI_SR_RX_EMPTY_B)       <= spi_sr_rx_empty_in_w;    -- 0
        data_w(SPI_SR_RX_FULL_B)        <= spi_sr_rx_full_in_w;     -- 1
        data_w(SPI_SR_TX_EMPTY_B)       <= spi_sr_tx_empty_in_w;    -- 2
        data_w(SPI_SR_TX_FULL_B)        <= spi_sr_tx_full_in_w;     -- 3
      when SPI_DRR =>               -- data receive register
        data_w(SPI_DRR_DATA_R'range)    <= spi_drr_data_in_w;       -- 7:0
      when SPI_SSR =>
        data_w(SPI_SSR_VALUE_B)         <= spi_ssr_value_q;         -- 0
      when others =>
        data_w                          <= X"00000000";
    end case;
  end process p_c_omux;


  pp_data_o          <= data_w when (pp_rd_i = '1') else X"00000000";


  spi_drr_rd_req_w   <= '1' when ( pp_rd_i = '1' and pp_addr_i = SPI_DRR )
                        else '0';

  spi_ipisr_wr_req_w <= '1' when (pp_wr_i(0) = '1' and
                                  (pp_addr_i = SPI_IPISR)) else '0';

  spi_cr_wr_req_w    <= '1' when (pp_wr_i(0) = '1' and
                                  (pp_addr_i = SPI_CR)) else '0';
  -- data transmit register write
  spi_dtr_wr_req_w   <= '1' when (pp_wr_i(0) = '1' and
                                  (pp_addr_i = SPI_DTR)) else '0';


  -- - - - - - - - - - - - -
  --  TX FIFO
  -- - - - - - - - - - - - -
  sw_reset_w      <= eq(spi_srr_reset_q, X"0000000A");
  tx_fifo_flush_w <= sw_reset_w or spi_cr_txfifo_rst_q;
  rx_fifo_flush_w <= sw_reset_w or spi_cr_rxfifo_rst_q;


  u_tx_fifo : spi_lite_fifo
    generic map ( WIDTH  => 8,
                  DEPTH  => 8,  -- 4,
                  ADDR_W => 3 ) -- 2 )
    port map ( clk_i      => clk_i,
               xrst_i     => xrst_i,
               --
               flush_i    => tx_fifo_flush_w,
               --
               data_in_i  => pp_data_i(SPI_DTR_DATA_R'range),
               push_i     => spi_dtr_wr_req_w,
               accept_o   => tx_accept_w,
               --
               pop_i      => tx_pop_w,
               data_out_o => tx_data_raw_w,
               valid_o    => tx_ready_w );

  spi_sr_tx_empty_in_w <= (not tx_ready_w);
  spi_sr_tx_full_in_w  <= (not tx_accept_w);


  --  reverse order if LSB first
  tx_data_w <= ( tx_data_raw_w(0) & tx_data_raw_w(1) & tx_data_raw_w(2) &
                 tx_data_raw_w(3) & tx_data_raw_w(4) & tx_data_raw_w(5) &
                 tx_data_raw_w(6) & tx_data_raw_w(7) )
               when (spi_cr_lsb_first_q = '1')
               else tx_data_raw_w;


  -- - - - - - - - - - - - -
  --  RX FIFO
  -- - - - - - - - - - - - -
  u_rx_fifo : spi_lite_fifo
    generic map ( WIDTH  => 8,
                  DEPTH  => 8,  -- 4,
                  ADDR_W => 3 ) -- 2 )
    port map ( clk_i      => clk_i,
               xrst_i     => xrst_i,
               --
               flush_i    => rx_fifo_flush_w,
               --
               data_in_i  => rx_data_w,
               push_i     => rx_push_w,
               accept_o   => rx_accept_w,
               --
               pop_i      => spi_drr_rd_req_w,
               data_out_o => spi_drr_data_in_w,
               valid_o    => rx_ready_w );

  spi_sr_rx_empty_in_w <= (not rx_ready_w);
  spi_sr_rx_full_in_w  <= (not rx_accept_w);



  enable_w <= spi_cr_spe_q and spi_cr_master_q and (not spi_cr_trans_inhibit_q);

  --  something to do, SPI enabled...
  start_w <= enable_w and (not active_q) and ( not done_q) and tx_ready_w;

  --  loopback more or normal
  miso_w  <= spi_mosi_o_w when (spi_cr_loop_q = '1') else spi_miso_i;


  --  SPI clock generator
  p_ckdiv : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        clk_div_q <= X"00000000";
      else
        if (start_w = '1' or sw_reset_w = '1' or clk_div_q = X"00000000") then
          clk_div_q <= C_SCK_RATIO;
        else
          clk_div_q <= dec1u(clk_div_q);
        end if;
      end if;
    end if;
  end process p_ckdiv;


    clk_en_w <= '1' when (clk_div_q = X"00000000") else '0';


  -- - - - - - - - - - - - - - - - - -
  --  sample, drive pulse generation
  -- - - - - - - - - - - - - - - - - -
  p_c_smpl : process (start_w, spi_cr_cpha_q, active_q, clk_en_w,
                      bit_count_q)
  begin
    --
    sample_w <= '0';
    drive_w  <= '0';
    --
    --  SPI = IDLE
    if (start_w = '1') then
      drive_w  <= (not spi_cr_cpha_q);    -- drive initial data (CPHA=0)
      --  SPI = ACTIVE
    elsif (active_q  = '1' and clk_en_w = '1') then
      -- sample
      --  CPHA=0, sample on the first edge
      --  CPHA=1, sample on the second edge
      if (bit_count_q(0) = spi_cr_cpha_q) then
        sample_w <= '1';
        --  drive (CPHA = 1)
      elsif (spi_cr_cpha_q = '1') then
        drive_w <= '1';
        --  drive (CPHA = 0)
      else
        drive_w <= '0';
        if ( (bit_count_q /= "000000") and  (bit_count_q /= "001111") ) then
          drive_w <= '1';
        end if;
      end if;
    end if;
  end process p_c_smpl;


  -- - - - - - - - - - - - -
  --  shift register
  -- - - - - - - - - - - - -
  p_sft : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        shift_reg_q    <= X"00";
        spi_clk_q      <= '0';
        spi_mosi_q     <= '0';
      else
        --  SPI = RESET (or potentially update CPOL)
        if ( sw_reset_w  = '1' or
             (spi_cr_wr_req_w  = '1' and (start_w = '0')) ) then
          shift_reg_q    <= X"00";
          spi_clk_q      <= spi_cr_cpol_q;
          --  SPI = IDLE
        elsif (start_w = '1') then
          spi_clk_q      <= spi_cr_cpol_q;

          --  CPHA = 0
          if (drive_w = '1') then
            spi_mosi_q    <= tx_data_w(7);
            shift_reg_q   <= (tx_data_w(6 downto 0) & '0');
            --  CPHA = 1
          else
            shift_reg_q   <= tx_data_w;
          end if;
          --  SPI = ACTIVE
        elsif (active_q = '1' and clk_en_w = '1') then
          --  toggle SPI clock output
          if (spi_cr_loop_q = '0') then
            spi_clk_q <= (not spi_clk_q);
          end if;
          --  drive MOSI
          if (drive_w = '1') then
            spi_mosi_q  <= shift_reg_q(7);
            shift_reg_q <= (shift_reg_q(6 downto 0) & '0');
            --  sample MISO
          elsif (sample_w = '1') then
            shift_reg_q(0) <= miso_w;
          end if;
        end if;
      end if;
    end if;
  end process p_sft;


  -- - - - - - - - - - - - -
  --  bit counter
  -- - - - - - - - - - - - -
  p_bcnt : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        bit_count_q     <= "000000";
        active_q        <= '0';
        done_q          <= '0';
      else
        if (sw_reset_w = '1') then
          bit_count_q   <= "000000";
          active_q      <= '0';
          done_q        <= '0';
        elsif (start_w = '1') then
          bit_count_q   <= "000000";
          active_q      <= '1';
          done_q        <= '0';
        elsif (active_q = '1' and clk_en_w = '1') then
          --  end of SPI transfer reached
          if (bit_count_q = "001111") then  -- 15
            -- go back to IDLE active_q
            active_q    <= '0';

            -- set transfer complete flags
            done_q      <= '1';
            -- increment cycle counter
          else
            bit_count_q <= inc1u(bit_count_q);
          end if;
        else
          done_q        <= '0';
        end if;
      end if;
    end if;
  end process p_bcnt;


  --  delayed done_q for FIFO level check
  p_dffr(clk_i, xrst_i, done_q, check_tx_level_q);


  --  interrupt
  p_irq : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        intr_q <= '0';
      else
        if (check_tx_level_q = '1' and spi_ipier_tx_empty_q = '1' and
            spi_sr_tx_empty_in_w = '1') then
          intr_q <= '1';
        elsif (spi_ipisr_wr_req_w = '1' and
               pp_data_i(SPI_IPISR_TX_EMPTY_B) = '1') then
          intr_q <= '0';
        end if;
      end if;
    end if;
  end process p_irq;



  spi_clk_o    <= spi_clk_q;
  spi_mosi_o_w <= spi_mosi_q;
  spi_mosi_o   <= spi_mosi_o_w;

  --  reverse order if LSB first
  rx_data_w    <= (shift_reg_q(0) & shift_reg_q(1) & shift_reg_q(2) &
                   shift_reg_q(3) & shift_reg_q(4) & shift_reg_q(5) &
                   shift_reg_q(6) & shift_reg_q(7) )
                  when (spi_cr_lsb_first_q = '1')
                  else shift_reg_q;

  rx_push_w  <= done_q;
  tx_pop_w   <= done_q;

  spi_cs_o   <= spi_ssr_value_q;
  intr_o     <= spi_dgier_gie_q  and intr_q;   -- global int must be enabled

end rtl;
