-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 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 vsirq_ctrl is
  generic ( NR_IRQS : natural range 1 to 32 := 5 );
  port ( clk_i        : in  std_logic;
         xrst_i       : in  std_logic;
         --  vsbus
         pp_addr_i    : in  slv_31_00;
         pp_data_i    : in  slv_31_00;
         pp_rd_i      : in  std_logic;
         pp_wr_i      : in  slv_03_00;   -- ! 3:1 NC
         pp_data_o    : out slv_31_00;
         --
         interrupt_i  : in  slv_t(NR_IRQS-1 downto 0);
         intr_o       : out std_logic
         );
end entity vsirq_ctrl;


architecture rtl of vsirq_ctrl is

  subtype irq_slv_t     is slv_t(NR_IRQS-1 downto 0);

  --  interrupt status register
  constant IRQ_ISR              : slv_07_00 := X"00";
  --  interrupt pending register
  constant IRQ_IPR              : slv_07_00 := X"04";
  --  interrupt enable register
  constant IRQ_IER              : slv_07_00 := X"08";
  --  interrupt acknowledge register
  constant IRQ_IAR              : slv_07_00 := X"0C";
  --  set interrupt enable bits
  constant IRQ_SIE              : slv_07_00 := X"10";
  --  clear interrupt enable bits
  constant IRQ_CIE              : slv_07_00 := X"14";
  --  interrupt vector register
  constant IRQ_IVR              : slv_07_00 := X"18";
  --  master enable register
  constant IRQ_MER              : slv_07_00 := X"1C";
  constant IRQ_MER_ME_DEFAULT   : std_logic := '0';
  constant IRQ_MER_ME_B         : natural := 0;
  --  hardware interrupt enable (HIE)
  constant IRQ_MER_HIE_B        : natural := 1;
  constant IRQ_MER_HIE_DEFAULT  : std_logic := '0';


  --  irq_mer_me (internal)
  signal irq_mer_me_q           : std_logic;  -- master enable register
  signal irq_mer_hie_q          : std_logic;  -- hardware interrupt enable reg

  signal irq_ipr_pending_in_w   : irq_slv_t;

  --  read mux
  signal data_w                 : slv_31_00;

  --  IRQ enable
  signal irq_enable_q           : irq_slv_t;

  --  IRQ pending
  signal irq_pending_q          : irq_slv_t;
  --  IRQ vector
  signal ivr_vector_w           : slv_31_00;
  --  IRQ output
  signal intr_q                 : std_logic;

  signal wr_en_w                : std_logic;

begin

  wr_en_w <= not_zero(pp_wr_i);


  -- - - - - - - - - - - - -
  --  read mux
  -- - - - - - - - - - - - -
  p_c_omux : process (pp_addr_i, irq_pending_q, irq_ipr_pending_in_w,
                      irq_enable_q, ivr_vector_w, irq_mer_hie_q,
                      irq_mer_me_q)
  begin
    --
    data_w <= X"00000000";
    --
    case pp_addr_i(7 downto 0) is
      when IRQ_ISR =>
        data_w(NR_IRQS-1 downto 0)  <= irq_pending_q;
      when IRQ_IPR =>
        data_w(NR_IRQS-1 downto 0)  <= irq_ipr_pending_in_w;
      when IRQ_IER =>
        data_w(NR_IRQS-1 downto 0)  <= irq_enable_q;
      when IRQ_IVR =>
        data_w                      <= ivr_vector_w;          -- 31:0
      when IRQ_MER =>
        data_w(1)                   <= irq_mer_hie_q;         -- 1
        data_w(0)                   <= irq_mer_me_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";


  -- - - - - - - - - - - - -
  --  IRQ enable
  --  IRQ pending
  --  IRQ master enable register
  -- - - - - - - - - - - - -
  p_iena : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        irq_enable_q  <= (others => '0');
        irq_pending_q <= (others => '0');
        irq_mer_hie_q <= IRQ_MER_HIE_DEFAULT;
        irq_mer_me_q  <= IRQ_MER_ME_DEFAULT;
      else
        --
        irq_pending_q <= irq_pending_q or interrupt_i;
        --
        if ( wr_en_w = '1' ) then
          case pp_addr_i(7 downto 0) is
            when IRQ_IER =>             -- 0x08
              -- WRITE INT ENA
              irq_enable_q <= pp_data_i(NR_IRQS-1 downto 0);
            when IRQ_SIE =>             -- 0x10
              -- SET INT ENA
              irq_enable_q <= irq_enable_q or pp_data_i(NR_IRQS-1 downto 0);
            when IRQ_CIE =>             -- 0x14
              -- CLR INT ENA
              irq_enable_q <= irq_enable_q and
                              (not pp_data_i(NR_IRQS-1 downto 0));
            when IRQ_MER =>             -- 0x1C
              irq_mer_hie_q <= pp_data_i(IRQ_MER_HIE_B);
              irq_mer_me_q  <= pp_data_i(IRQ_MER_ME_B);
            when IRQ_IAR =>             -- 0x0C
              irq_pending_q <= interrupt_i or
                               (irq_pending_q and
                                (not pp_data_i(NR_IRQS-1 downto 0)));
            when IRQ_ISR =>             -- 0x00
              if (irq_mer_hie_q = '0') then
                irq_pending_q <= pp_data_i(NR_IRQS-1 downto 0);
              end if;
            when others =>
              null;
          end case;
        end if;
      end if;
    end if;
  end process p_iena;


  irq_ipr_pending_in_w <= irq_pending_q and irq_enable_q;


  -- - - - - - - - - - - - -
  --  IRQ vector
  -- - - - - - - - - - - - -
  p_c_ivec : process (irq_ipr_pending_in_w)
  begin
    --
    ivr_vector_w <= X"FFFFFFFF";
    --
    for ii in 0 to NR_IRQS-1 loop
      if (irq_ipr_pending_in_w(ii) = '1') then
        ivr_vector_w <= X"000000"  & "000" & slv_t(to_unsigned(ii, 5)); -- 0-31
        exit;
      end if;
    end loop;
  end process p_c_ivec;



  -- - - - - - - - - - - - -
  --  IRQ output
  -- - - - - - - - - - - - -
  p_irq : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') Then
      if (xrst_i = '0') then
        intr_q <= '0';
      else
        intr_q <= '0';
        if ( irq_mer_me_q = '1' and not_zero(irq_ipr_pending_in_w) ) then
          intr_q <= '1';
        end if;
      end if;
    end if;
  end process p_irq;


  intr_o <= intr_q;

end rtl;
