-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 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 rv_csr_rf (CSR: Control and Status Registers)
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
entity rv_csr_rf is
  generic ( RISCV_S_U        : std_logic := '1';
            SUPPORT_MTIMECMP : std_logic := '1');
  port ( clk_i             : in  std_logic;
         xrst_i            : in  std_logic;  -- syncr reset
         ext_irq_i         : in  std_logic;
         timer_irq_i       : in  std_logic;
         --
         cpu_id_i          : in  slv_31_00;
         misa_i            : in  slv_31_00;
         --
         exception_i       : in  slv_05_00;
         exception_pc_i    : in  slv_31_00;
         exception_addr_i  : in  slv_31_00;
         --  CSR read port
         csr_ren_i         : in  std_logic;
         csr_raddr_i       : in  slv_11_00;
         csr_rdata_o       : out slv_31_00;
         --  CSR write port
         csr_waddr_i       : in  slv_11_00;
         csr_wdata_i       : in  slv_31_00;
         --
         csr_branch_o      : out std_logic;
         csr_target_o      : out slv_31_00;
         --  CSR registers
         priv_o            : out slv_01_00;
         status_o          : out slv_31_00;
         satp_o            : out slv_31_00;
         --  masked interrupt output
         interrupt_o       : out slv_31_00
         );
end entity rv_csr_rf;


architecture rtl of rv_csr_rf is

  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- MSTATUS REGISTER (csr_sr_q)
  -- Bits     Field Name   Description
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- 0        UIE          User Interrupt Enable
  -- 1        SIE          Supervisor Interrupt Enable
  -- 2        Reserved
  -- 3        MIE          Machine Interrupt Enable
  -- 4        UPIE         User Previous Interrupt Enable
  -- 5        SPIE         Supervisor Previous Interrupt Enable
  -- 6        Reserved
  -- 7        MPIE         Machine Previous Interrupt Enabler
  -- 8        SPP          Supervisor Previous Privilege
  -- [10:9]   Reserved
  -- [12:11]  MPP          Machine Previous Privilege
  -- [14:13]  FS           Floating Point State
  -- [16:15]  XS           User Mode Extension State
  -- 17       MPRIV        Modify Privilege (access memory as MPP)
  -- 18       SUM          Permit Supervisor User Memory Access
  -- 19       MXR          Make Executable Readable
  -- 20       TVM          Trap Virtual memory
  -- 21       TW           Timeout Wait (traps S-Mode wfi)
  -- 22       TSR          Trap SRET
  -- [23:30]  Reserved
  -- [31]     SD           State Dirty (FS and XS summary bit)
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  --
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- [63:0]   mtime        Machine Time Register
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  --
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- [63:0]   mtimecmp     Machine Time Compare Register
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  --
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- satp - Supervisor Address Translation and Protection Register
  -- Bits     Field Name   Description
  -- 31       MODE         MODE=1 uses Sv32 Address Translation
  -- [30:22]  ASID         Address Space Identifier
  -- [21:0]   PPN          Physical Page Number of the root page table
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  -- - - - - - - - - - -
  --  registers / wires
  -- - - - - - - - - - -
  --  CSR - machine
  signal csr_mepc_q      : slv_31_00;
  signal csr_mcause_q    : slv_31_00;
  signal csr_sr_q        : slv_31_00;
  signal csr_mtvec_q     : slv_31_00;
  signal csr_mip_q       : slv_31_00;
  signal csr_mie_q       : slv_31_00;
  signal csr_mpriv_q     : slv_01_00;
  signal csr_mcycle_q    : slv_31_00;
  signal csr_mcycle_h_q  : slv_31_00; -- mtime[63:32]
  signal csr_mscratch_q  : slv_31_00; -- mtime[31:0]
  signal csr_mtval_q     : slv_31_00;
  signal csr_mtimecmp_q  : slv_31_00;
  signal csr_mtime_ie_q  : std_logic;         
  signal csr_medeleg_q   : slv_31_00;
  signal csr_mideleg_q   : slv_31_00;

  --  CSR - supervisor
  --    sepc    : Supervisor Exception Program Counter
  --    stvec   : Supervisor Trap Vector Base Address Register
  --    scause  : Supervisor Cause Register
  --    stval   : Supervisor Trap Value
  --    satp    : Supervisor Address Translation and Protection
  --    sscratch: Supervisor Scratch Register
  signal csr_sepc_q      : slv_31_00;
  signal csr_stvec_q     : slv_31_00;
  signal csr_scause_q    : slv_31_00;
  signal csr_stval_q     : slv_31_00;
  signal csr_satp_q      : slv_31_00;
  signal csr_sscratch_q  : slv_31_00;

  -- - - - - - - - - - -
  --  masked interrupts
  -- - - - - - - - - - -
  signal irq_pending_w   : slv_31_00;
  signal irq_masked_w    : slv_31_00;
  signal irq_priv_w      : slv_01_00;

  -- valid only in SUPER mode
  signal m_interrupts_w  : slv_31_00;
  signal s_enabled_w     : std_logic;
  signal s_interrupts_w  : slv_31_00;
  signal m_enabled_w     : std_logic;

  signal irq_priv_q      : slv_01_00;

  signal csr_mip_upd_q   : std_logic;

  signal buffer_mip_w    : std_logic;

  signal rdata_w         : slv_31_00;

  --  CSR - machine
  signal csr_mepc_w      : slv_31_00;
  signal csr_mcause_w    : slv_31_00;
  signal csr_mtval_w     : slv_31_00;
  signal csr_sr_w        : slv_31_00;
  signal csr_mtvec_w     : slv_31_00;
  signal csr_mip_w       : slv_31_00;
  signal csr_mie_w       : slv_31_00;
  signal csr_mpriv_w     : slv_01_00;
  signal csr_mcycle_w    : slv_31_00;
  signal csr_mscratch_w  : slv_31_00;
  signal csr_mtimecmp_w  : slv_31_00;
  signal csr_mtime_ie_w  : std_logic;
  signal csr_medeleg_w   : slv_31_00;
  signal csr_mideleg_w   : slv_31_00;

  signal csr_mip_next_q  : slv_31_00;
  signal csr_mip_next_w  : slv_31_00;

  --  CSR - supervisor
  signal csr_sepc_w      : slv_31_00;
  signal csr_stvec_w     : slv_31_00;
  signal csr_scause_w    : slv_31_00;
  signal csr_stval_w     : slv_31_00;
  signal csr_satp_w      : slv_31_00;
  signal csr_sscratch_w  : slv_31_00;

  signal is_exception_w  : std_logic;
  signal exception_s_w   : std_logic;

  signal interrupt_w     : slv_31_00;

  -- - - - - - - -
  --  CSR branch
  -- - - - - - - -
  signal branch_w        : std_logic;
  signal branch_target_w : slv_31_00;

begin

  g_super : if ( RISCV_S_U = '1' ) generate
    irq_pending_w  <= (csr_mip_q and csr_mie_q);
    irq_masked_w   <=  m_interrupts_w when ( not_zero(m_interrupts_w) )
                       else s_interrupts_w;

    irq_priv_w     <= PRIV_MACHINE when ( not_zero(m_interrupts_w) )
                      else PRIV_SUPER;
    --
    m_enabled_w    <= lt(csr_mpriv_q, PRIV_MACHINE) or
                      ( eq(csr_mpriv_q, PRIV_MACHINE) and
                        csr_sr_q(SR_MIE_R) );

    s_enabled_w    <= lt(csr_mpriv_q, PRIV_SUPER) or
                      ( eq(csr_mpriv_q, PRIV_SUPER) and
                        csr_sr_q(SR_SIE_R) );
    
    m_interrupts_w <= (irq_pending_w and  (not csr_mideleg_q)) when
                      (m_enabled_w = '1')
                      else (others => '0');     --  32b
    
    s_interrupts_w <=  (irq_pending_w and csr_mideleg_q) when
                       (s_enabled_w = '1')
                       else (others => '0');    --  32b

    -- - - - - - - - -
    --  CSR read port
    -- - - - - - - - -
    p_c_omux : process (csr_raddr_i, csr_mscratch_q, csr_mepc_q, csr_mtvec_q,
                        csr_mcause_q, csr_mtval_q, csr_sr_q, csr_mip_q,
                        csr_mie_q, csr_mcycle_q, csr_mcycle_h_q, cpu_id_i,
                        misa_i, csr_medeleg_q, csr_mideleg_q, csr_mtimecmp_q,
                        csr_sepc_q, csr_stvec_q, csr_scause_q, csr_stval_q,
                        csr_satp_q, csr_sscratch_q)
    begin
      rdata_w <= (others => '0');               -- 32b

      case csr_raddr_i is                       -- 11:0
        --  CSR - machine
        when CSR_MSCRATCH =>
          rdata_w <= csr_mscratch_q    and CSR_MSCRATCH_MASK;
        when CSR_MEPC =>
          rdata_w <= csr_mepc_q        and CSR_MEPC_MASK;
        when CSR_MTVEC =>
          rdata_w <= csr_mtvec_q       and CSR_MTVEC_MASK;
        when CSR_MCAUSE =>
          rdata_w <= csr_mcause_q      and CSR_MCAUSE_MASK;
        when CSR_MTVAL =>
          rdata_w <= csr_mtval_q       and CSR_MTVAL_MASK;
        when CSR_MSTATUS =>
          rdata_w <= csr_sr_q          and CSR_MSTATUS_MASK;
        when CSR_MIP =>
          rdata_w <= csr_mip_q         and CSR_MIP_MASK;
        when CSR_MIE =>
          rdata_w <= csr_mie_q         and CSR_MIE_MASK;
        when CSR_MCYCLE | CSR_MTIME =>
          rdata_w <= csr_mcycle_q;
        when CSR_MTIMEH =>
          rdata_w <= csr_mcycle_h_q;
        when CSR_MHARTID =>
          rdata_w <= cpu_id_i;
        when CSR_MARCHID =>
          rdata_w   <= VRV_ARCHID;
        when CSR_MIMPID =>
          rdata_w    <= HW_VERSION;             -- hardware implementationv id
        when CSR_MISA =>
          rdata_w    <= misa_i;
        when CSR_MEDELEG =>
          rdata_w    <= (csr_medeleg_q and CSR_MEDELEG_MASK);
        when CSR_MIDELEG =>
          rdata_w    <= (csr_mideleg_q and CSR_MIDELEG_MASK);
        when CSR_MTIMECMP =>
          if (SUPPORT_MTIMECMP = '1') then      -- non-std behaviour
            rdata_w  <= csr_mtimecmp_q;
          end if;
        --
        -- regs in SUPERVISOR mode only
        --
        when CSR_SSTATUS =>
          rdata_w <= (csr_sr_q       and  CSR_SSTATUS_MASK);
        when CSR_SIP =>
          rdata_w <= (csr_mip_q      and  CSR_SIP_MASK);
        when CSR_SIE =>
          rdata_w <= (csr_mie_q      and CSR_SIE_MASK);
        when CSR_SEPC =>
          rdata_w <= (csr_sepc_q     and CSR_SEPC_MASK);
        when CSR_STVEC =>
          rdata_w <= (csr_stvec_q    and CSR_STVEC_MASK);
        when CSR_SCAUSE =>
          rdata_w <= (csr_scause_q   and CSR_SCAUSE_MASK);
        when CSR_STVAL =>
          rdata_w <= (csr_stval_q    and CSR_STVAL_MASK);
        when CSR_SATP =>
          rdata_w <= (csr_satp_q     and CSR_SATP_MASK);
        when CSR_SSCRATCH =>
          rdata_w <= (csr_sscratch_q and CSR_SSCRATCH_MASK);
        when others =>                          -- default:  rdata_w = 0  (32b)
          null;
      end case;
    end process p_c_omux;
  end generate g_super;



  g_machine : if ( RISCV_S_U /= '1' ) generate
    irq_pending_w   <= (csr_mip_q and csr_mie_q);
    irq_masked_w    <= irq_pending_w when (csr_sr_q(SR_MIE_R) = '1')
                       else (others => '0');  -- 32b
    irq_priv_w      <= PRIV_MACHINE;
    m_enabled_w     <= '0';                   -- NC
    s_enabled_w     <= '0';                   -- NC
    m_interrupts_w  <=  (others => '0');      -- NC
    s_interrupts_w  <=  (others => '0');      -- NC

    -- - - - - - - - -
    --  CSR read port
    -- - - - - - - - -
    p_c_omux : process (csr_raddr_i, csr_mscratch_q, csr_mepc_q, csr_mtvec_q,
                        csr_mcause_q, csr_mtval_q, csr_sr_q, csr_mip_q,
                        csr_mie_q, csr_mcycle_q, csr_mcycle_h_q, cpu_id_i,
                        misa_i, csr_mtimecmp_q)
    begin
      rdata_w <= (others => '0');               -- 32b
      case csr_raddr_i is                       -- 11:0
        --  CSR - machine
        when CSR_MSCRATCH =>
          rdata_w <= csr_mscratch_q and CSR_MSCRATCH_MASK;
        when CSR_MEPC =>
          rdata_w <= csr_mepc_q     and CSR_MEPC_MASK;
        when CSR_MTVEC =>
          rdata_w <= csr_mtvec_q    and CSR_MTVEC_MASK;
        when CSR_MCAUSE =>
          rdata_w <= csr_mcause_q   and CSR_MCAUSE_MASK;
        when CSR_MTVAL =>
          rdata_w <= csr_mtval_q    and CSR_MTVAL_MASK;
        when CSR_MSTATUS =>
          rdata_w <= csr_sr_q       and CSR_MSTATUS_MASK;
        when CSR_MIP =>
          rdata_w <= csr_mip_q      and CSR_MIP_MASK;
        when CSR_MIE =>
          rdata_w <= csr_mie_q      and CSR_MIE_MASK;
        when CSR_MCYCLE | CSR_MTIME =>
          rdata_w <= csr_mcycle_q;
        when CSR_MTIMEH =>
          rdata_w <= csr_mcycle_h_q;
        when CSR_MARCHID =>
          rdata_w   <= VRV_ARCHID;
        when CSR_MIMPID =>
          rdata_w   <= HW_VERSION;              -- hardware implementationv id
        when CSR_MHARTID =>
          rdata_w <= cpu_id_i;
        when CSR_MISA =>
          rdata_w <= misa_i;
        when CSR_MTIMECMP =>
          if (SUPPORT_MTIMECMP = '1') then      -- non-std behaviour
            rdata_w <=  csr_mtimecmp_q;
          end if;
        when others =>                          -- default:  rdata_w = 32b
          null;
      end case;
    end process p_c_omux;
  end generate g_machine;


  p_irq_priv : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        irq_priv_q    <= PRIV_MACHINE;
      else
        if ( not_zero(irq_masked_w) ) then
          irq_priv_q  <= irq_priv_w;
        end if;
      end if;
    end if;
  end process p_irq_priv;


  interrupt_w  <= irq_masked_w;
  interrupt_o  <= interrupt_w;


  p_mip : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        csr_mip_upd_q    <= '0';
      else
        if ((csr_ren_i = '1' and csr_raddr_i = CSR_MIP) or
            (csr_ren_i = '1' and csr_raddr_i = CSR_SIP)) then
          csr_mip_upd_q  <= '1';
        elsif (csr_waddr_i = CSR_MIP or csr_waddr_i = CSR_SIP or
               not_zero(exception_i) ) then
          csr_mip_upd_q  <= '0';
        end if;
      end if;
    end if;
  end process p_mip;

  buffer_mip_w  <= (csr_ren_i  and eq(csr_raddr_i, CSR_MIP)) or
                   (csr_ren_i  and eq(csr_raddr_i, CSR_SIP)) or
                   csr_mip_upd_q;

  csr_rdata_o   <= rdata_w;
  priv_o        <= csr_mpriv_q;
  status_o      <= csr_sr_q;
  satp_o        <= csr_satp_q;


  -- - - - - - - - - - - - - -
  --  CSR register next state
  -- - - - - - - - - - - - - -
  is_exception_w <= '1' when (
    (exception_i and EXCEPTION_TYPE_MASK) = EXCEPTION_EXCEPTION)
                    else '0';

  exception_s_w  <= RISCV_S_U  and
                    is_exception_w and
                    le(csr_mpriv_q, PRIV_SUPER) and
                    csr_medeleg_q(
                      to_uint(exception_i(EXCEPTION_SUBTYPE_R'range)));

  p_c_csr : process (csr_mip_next_q, csr_mepc_q, csr_sr_q, csr_mcause_q,
                     csr_mtval_q, csr_mtvec_q, csr_mip_q, csr_mie_q,
                     csr_mpriv_q, csr_mscratch_q,csr_mcycle_q,csr_mtimecmp_q,
                     csr_mtime_ie_q, csr_medeleg_q, csr_mideleg_q, csr_sepc_q,
                     csr_stvec_q, csr_scause_q, csr_stval_q, csr_satp_q,
                     csr_sscratch_q, exception_i, irq_priv_q,
                     is_exception_w, exception_s_w,
                     exception_pc_i, interrupt_w, exception_addr_i,
                     csr_waddr_i, csr_wdata_i, ext_irq_i, timer_irq_i)
    variable csr_mip_next_w_v : slv_t(csr_mip_next_w'range);
    variable csr_mip_w_v      : slv_t(csr_mip_w'range);
    variable csr_mie_w_v      : slv_t(csr_mie_w'range);
    variable csr_sr_w_v       : slv_t(csr_sr_w'range);
  begin
    --  CSR - machine
    csr_mip_next_w_v := csr_mip_next_q;
    csr_mip_w_v      := csr_mip_q;
    csr_mepc_w       <= csr_mepc_q;
    csr_sr_w         <= csr_sr_q;
    csr_sr_w_v       := csr_sr_q;
    csr_mcause_w     <= csr_mcause_q;
    csr_mtval_w      <= csr_mtval_q;
    csr_mtvec_w      <= csr_mtvec_q;
    csr_mie_w        <= csr_mie_q;
    csr_mie_w_v      := csr_mie_q;
    csr_mpriv_w      <= csr_mpriv_q;
    csr_mscratch_w   <= csr_mscratch_q;
    csr_mcycle_w     <= inc1u(csr_mcycle_q);  -- + 1
    csr_mtimecmp_w   <= csr_mtimecmp_q;
    csr_mtime_ie_w   <= csr_mtime_ie_q;
    csr_medeleg_w    <= csr_medeleg_q;
    csr_mideleg_w    <= csr_mideleg_q;
    --  CSR - super
    csr_sepc_w       <= csr_sepc_q;
    csr_stvec_w      <= csr_stvec_q;
    csr_scause_w     <= csr_scause_q;
    csr_stval_w      <= csr_stval_q;
    csr_satp_w       <= csr_satp_q;
    csr_sscratch_w   <= csr_sscratch_q;
    --  interrupts
    if ((exception_i and EXCEPTION_TYPE_MASK) = EXCEPTION_INTERRUPT) then
      --  machine mode interrupts
      if (irq_priv_q = PRIV_MACHINE) then
        --  save interrupt / supervisor state
        csr_sr_w_v(SR_MPIE_R)      := csr_sr_w_v(SR_MIE_R);
        csr_sr_w_v(SR_MPP_R'range) := csr_mpriv_q;

        --  disable interrupts and enter supervisor mode
        csr_sr_w_v(SR_MIE_R)  := '0';

        --  raise priviledge to machine level
        csr_mpriv_w           <= PRIV_MACHINE;

        --  record interrupt source PC
        csr_mepc_w            <= exception_pc_i;
        csr_mtval_w           <= (others => '0');  -- 32b

        --  priority encoded interrupt cause
        if (interrupt_w(IRQ_M_SOFT) = '1') then
          csr_mcause_w  <= MCAUSE_INTERRUPT or IRQ_M_SOFT_B;
        elsif (interrupt_w(IRQ_M_TIMER) = '1') then
          csr_mcause_w  <= MCAUSE_INTERRUPT or IRQ_M_TIMER_B;
        elsif (interrupt_w(IRQ_M_EXT) = '1') then
          csr_mcause_w  <= MCAUSE_INTERRUPT or IRQ_M_EXT_B;
        end if;
        --  supervisor mode interrupts
      else
        --  save interrupt / supervisor state
        csr_sr_w_v(SR_SPIE_R) := csr_sr_w_v(SR_SIE_R);
        if (csr_mpriv_q = PRIV_SUPER) then
          csr_sr_w_v(SR_SPP_R)  := '1';
        else
          csr_sr_w_v(SR_SPP_R)  := '0';
        end if;
        --  disable interrupts and enter supervisor mode
        csr_sr_w_v(SR_SIE_R)  := '0';
        --  raise priviledge to machine level
        csr_mpriv_w  <= PRIV_SUPER;
        --  record fault source PC
        csr_sepc_w   <= exception_pc_i;
        csr_stval_w  <= (others => '0');   -- 32b
        --  priority encoded interrupt cause
        if (interrupt_w(IRQ_S_SOFT) = '1') then
          csr_scause_w <= MCAUSE_INTERRUPT or IRQ_S_SOFT_B;
        elsif (interrupt_w(IRQ_S_TIMER) = '1') then
          csr_scause_w <= MCAUSE_INTERRUPT or IRQ_S_TIMER_B;         -- +5
        elsif (interrupt_w(IRQ_S_EXT) = '1') then
          csr_scause_w <= MCAUSE_INTERRUPT or IRQ_S_EXT_B;           -- +9
        end if;
      end if;
    elsif ( ge(exception_i, EXCEPTION_ERET_U) and       -- >= 30  ERET U/S/H/M
            le(exception_i, EXCEPTION_ERET_M) ) then    -- <= 33
      --  MRET (return from machine)
      if (exception_i(1 downto 0) = PRIV_MACHINE) then
        --  set privilege level to previous MPP
        csr_mpriv_w          <= csr_sr_w_v(SR_MPP_R'range);
        --  interrupt enable pop
        csr_sr_w_v(SR_MIE_R)  := csr_sr_w_v(SR_MPIE_R);
        csr_sr_w_v(SR_MPIE_R) := '1';
        csr_sr_w_v(SR_MPP_R'range) := SR_MPP_U;
        --  SRET (return from supervisor)
      else
        -- per riscv-privileged sret shall return illegal instruction
        -- if TSR=1 in mstatus
        --  set privilege level to previous privilege level
        if (csr_sr_w_v(SR_SPP_R) = '1') then
          csr_mpriv_w <=  PRIV_SUPER;
        else
          csr_mpriv_w <=  PRIV_USER;
        end if;
        --  interrupt enable pop
        csr_sr_w_v(SR_SIE_R)  := csr_sr_w_v(SR_SPIE_R);
        csr_sr_w_v(SR_SPIE_R) := '1';
        --  set next SPP to user mode
        csr_sr_w_v(SR_SPP_R) := '0';
      end if;
      --  exception - handled in super mode
    elsif (is_exception_w = '1' and exception_s_w = '1') then
      --  save interrupt / supervisor state
      csr_sr_w_v(SR_SPIE_R) := csr_sr_w_v(SR_SIE_R);
      if (csr_mpriv_q = PRIV_SUPER) then
        csr_sr_w_v(SR_SPP_R)  := '1';
      else
        csr_sr_w_v(SR_SPP_R)  := '0';
      end if;
      --  disable interrupts and enter supervisor mode
      csr_sr_w_v(SR_SIE_R)    := '0';
      --  raise priviledge to machine level
      csr_mpriv_w             <= PRIV_SUPER;
      --  record fault source PC
      csr_sepc_w              <= exception_pc_i;
      --  bad address / PC
      case exception_i is
        when EXCEPTION_MISALIGNED_FETCH | EXCEPTION_FAULT_FETCH |
          EXCEPTION_PAGE_FAULT_INST =>
          csr_stval_w <= exception_pc_i;
        when EXCEPTION_ILLEGAL_INSTRUCTION | EXCEPTION_MISALIGNED_LOAD |
          EXCEPTION_FAULT_LOAD | EXCEPTION_MISALIGNED_STORE |
          EXCEPTION_FAULT_STORE | EXCEPTION_PAGE_FAULT_LOAD |
          EXCEPTION_PAGE_FAULT_STORE =>
          csr_stval_w <= exception_addr_i; -- suoervisor trap value register
        when others =>
          csr_stval_w <= (others => '0');  -- 32b
      end case;
      --  fault cause
      csr_scause_w <= (31 downto 4 => '0') & exception_i(3 downto 0);
      --  exception - handled in machine mode
    elsif (is_exception_w = '1') then
      --  save interrupt / supervisor state
      csr_sr_w_v(SR_MPIE_R)      := csr_sr_w_v(SR_MIE_R);
      csr_sr_w_v(SR_MPP_R'range) := csr_mpriv_q;
      --  disable interrupts and enter supervisor mode
      csr_sr_w_v(SR_MIE_R)  := '0';
      --  raise priviledge to machine level
      csr_mpriv_w  <= PRIV_MACHINE;
      --  record fault source PC
      csr_mepc_w   <= exception_pc_i;
      --  bad address / PC
      case exception_i is                     -- machine trap value
        when EXCEPTION_MISALIGNED_FETCH    |
             EXCEPTION_FAULT_FETCH         |
             EXCEPTION_PAGE_FAULT_INST     => csr_mtval_w <= exception_pc_i;
        when EXCEPTION_ILLEGAL_INSTRUCTION |
             EXCEPTION_MISALIGNED_LOAD     |
             EXCEPTION_FAULT_LOAD          |
             EXCEPTION_MISALIGNED_STORE    |
             EXCEPTION_FAULT_STORE         |
             EXCEPTION_PAGE_FAULT_LOAD     |
             EXCEPTION_PAGE_FAULT_STORE    => csr_mtval_w <= exception_addr_i;
        when others                        => csr_mtval_w <= X"00000000";
      end case;
      --  fault cause
      csr_mcause_w <= (31 downto 4 => '0') &  exception_i(3 downto 0);
    else
      case csr_waddr_i is
        --  CSR - machine
        when CSR_MSCRATCH =>
          csr_mscratch_w  <= csr_wdata_i and CSR_MSCRATCH_MASK;
        when CSR_MEPC =>
          csr_mepc_w      <= csr_wdata_i and CSR_MEPC_MASK;
        when CSR_MTVEC =>
          csr_mtvec_w     <= csr_wdata_i and CSR_MTVEC_MASK;
        when CSR_MCAUSE =>
          csr_mcause_w    <= csr_wdata_i and CSR_MCAUSE_MASK;
        when CSR_MTVAL =>
          csr_mtval_w     <= csr_wdata_i and CSR_MTVAL_MASK;
        when CSR_MSTATUS =>
          csr_sr_w_v      := csr_wdata_i and CSR_MSTATUS_MASK;
        when CSR_MIP =>
          csr_mip_w_v     := csr_wdata_i and CSR_MIP_MASK;
        when CSR_MIE =>
          csr_mie_w_v     := csr_wdata_i and CSR_MIE_MASK;
        when CSR_MEDELEG =>
          csr_medeleg_w   <= csr_wdata_i and CSR_MEDELEG_MASK;
        when CSR_MIDELEG =>
          csr_mideleg_w   <= csr_wdata_i and CSR_MIDELEG_MASK;
          --  non-std behaviour
        when CSR_MTIMECMP =>
          csr_mtimecmp_w  <= csr_wdata_i and CSR_MTIMECMP_MASK;
          csr_mtime_ie_w  <= '1';
          --  CSR - super
        when CSR_SEPC =>
          csr_sepc_w      <= csr_wdata_i and CSR_SEPC_MASK;
        when CSR_STVEC =>
          csr_stvec_w     <= csr_wdata_i and CSR_STVEC_MASK;
        when CSR_SCAUSE =>
          csr_scause_w    <= csr_wdata_i and CSR_SCAUSE_MASK;
        when CSR_STVAL =>
          csr_stval_w     <= csr_wdata_i and CSR_STVAL_MASK;
        when CSR_SATP =>
          csr_satp_w      <= csr_wdata_i and CSR_SATP_MASK;
        when CSR_SSCRATCH =>
          csr_sscratch_w  <= csr_wdata_i and CSR_SSCRATCH_MASK;
        when CSR_SSTATUS =>
          csr_sr_w_v      := (csr_sr_w_v and (not CSR_SSTATUS_MASK)) or
                             (csr_wdata_i and CSR_SSTATUS_MASK);
        -- per privileged specs SEIP, STIP bits in sip is read-only and
        -- is cleared by platform specific (controller/timer)
        -- SSIP maybe writeable
        when CSR_SIP =>
          csr_mip_w_v     := (csr_mip_w_v and (not CSR_SIP_MASK)) or
                             (csr_wdata_i and CSR_SIP_MASK);
        when CSR_SIE =>
          csr_mie_w_v     := (csr_mie_w_v and (not CSR_SIE_MASK)) or
                             (csr_wdata_i and CSR_SIE_MASK);
        when others =>
          null;
      end case;
    end if;
    --
    --  external interrupts
    --  NOTE: If the machine level interrupts are delegated to supervisor,
    --        route the interrupts there instead..
    -- SEIP bit requires an register to store value set/cleared by CSRs
    --  which gets ORed upon read via CSR with hardware status bit (maybe
    --  just with MEIP bit) specification does not say anything if this
    --  should depend on mideleg register
    if (ext_irq_i = '1'  and  csr_mideleg_q(SR_IP_MEIP_R) = '1') then     -- 11
      csr_mip_next_w_v(SR_IP_SEIP_R) := '1';                              --  9
    end if;
    if (ext_irq_i  = '1' and csr_mideleg_q(SR_IP_MEIP_R) = '0') then      -- 11
      csr_mip_next_w_v(SR_IP_MEIP_R) := '1';                              -- 11
    end if;
    if (timer_irq_i  = '1' and  csr_mideleg_q(SR_IP_MTIP_R) = '1') then   --  7
      csr_mip_next_w_v(SR_IP_STIP_R) := '1';                              --  5
    end if;
    if (timer_irq_i  = '1' and csr_mideleg_q(SR_IP_MTIP_R) = '0') then    --  7
      csr_mip_next_w_v(SR_IP_MTIP_R) := '1';                              --  7
    end if;
    -- make flags go off as input level signals go off
    -- per riscv specification ints are level
    if (ext_irq_i = '0'  and  csr_mideleg_q(SR_IP_MEIP_R) = '1') then     -- 11
      csr_mip_next_w_v(SR_IP_SEIP_R) := '0';                              --  9
    end if;
    if (ext_irq_i  = '0' and csr_mideleg_q(SR_IP_MEIP_R) = '0') then      -- 11
      csr_mip_next_w_v(SR_IP_MEIP_R) := '0';                              -- 11
    end if;
    --  optional: internal timer compare interrupt
    if (SUPPORT_MTIMECMP = '1' and csr_mcycle_q = csr_mtimecmp_q) then
      if (csr_mideleg_q(SR_IP_MTIP_R) = '1') then                         --  7
        csr_mip_next_w_v(SR_IP_STIP_R) := csr_mtime_ie_q;                 --  5
      else
        csr_mip_next_w_v(SR_IP_MTIP_R) := csr_mtime_ie_q;                 --  7
      end if;
      csr_mtime_ie_w  <= '0';
    end if;
    --
    csr_mip_next_w <= csr_mip_next_w_v;
    -- mask current mip_w_v so csr_mip_next_w_v can decide if signal is still
    -- asserted and flag shall be kept or not
    csr_mip_w      <= (csr_mip_w_v and X"FFFFF0FF") or csr_mip_next_w_v;
    csr_mie_w      <= csr_mie_w_v;
    csr_sr_w       <= csr_sr_w_v;
    --
  end process p_c_csr;


  -- - - - - - - -
  --  sequential
  -- - - - - - - -
  p_csr : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        --  CSR - Machine
        csr_mepc_q         <= X"00000000";            -- 32b
        csr_sr_q           <= X"00000000";            -- 32b
        csr_mcause_q       <= X"00000000";            -- 32b
        csr_mtval_q        <= X"00000000";            -- 32b
        csr_mtvec_q        <= X"00000000";            -- 32b
        csr_mip_q          <= X"00000000";            -- 32b
        csr_mie_q          <= X"00000000";            -- 32b
        csr_mpriv_q        <= PRIV_MACHINE;   
        csr_mcycle_q       <= X"00000000";            -- 32b
        csr_mcycle_h_q     <= X"00000000";            -- 32b
        csr_mscratch_q     <= X"00000000";            -- 32b
        csr_mtimecmp_q     <= X"00000000";            -- 32b
        csr_mtime_ie_q     <= '0';                    --  1b
        csr_medeleg_q      <= X"00000000";            -- 32b
        csr_mideleg_q      <= X"00000000";            -- 32b
        --  CSR - super 
        csr_sepc_q         <= X"00000000";            -- 32b
        csr_stvec_q        <= X"00000000";            -- 32b
        csr_scause_q       <= X"00000000";            -- 32b
        csr_stval_q        <= X"00000000";            -- 32b
        csr_satp_q         <= X"00000000";            -- 32b
        csr_sscratch_q     <= X"00000000";            -- 32b
        csr_mip_next_q     <= X"00000000";            -- 32b
      else
        --  CSR - machine
        csr_mepc_q         <= csr_mepc_w;
        csr_sr_q           <= csr_sr_w;
        csr_mcause_q       <= csr_mcause_w;
        csr_mtval_q        <= csr_mtval_w;
        csr_mtvec_q        <= csr_mtvec_w;
        csr_mip_q          <= csr_mip_w;
        csr_mie_q          <= csr_mie_w;
        csr_mcycle_q       <= csr_mcycle_w;
        csr_mscratch_q     <= csr_mscratch_w;
        if (SUPPORT_MTIMECMP = '1') then
          csr_mtimecmp_q   <= csr_mtimecmp_w;
          csr_mtime_ie_q   <= csr_mtime_ie_w;
        else
          csr_mtimecmp_q   <= X"00000000";            -- 32b
          csr_mtime_ie_q   <= '0';                    --  1b
        end if;
        --  CSR - super
        if (RISCV_S_U = '1') then
          csr_mpriv_q      <= csr_mpriv_w;
          csr_medeleg_q    <= (csr_medeleg_w  and CSR_MEDELEG_MASK);
          csr_mideleg_q    <= (csr_mideleg_w  and CSR_MIDELEG_MASK);
          csr_sepc_q       <= (csr_sepc_w     and CSR_SEPC_MASK);
          csr_stvec_q      <= (csr_stvec_w    and CSR_STVEC_MASK);
          csr_scause_q     <= (csr_scause_w   and CSR_SCAUSE_MASK);
          csr_stval_q      <= (csr_stval_w    and CSR_STVAL_MASK);
          csr_satp_q       <= (csr_satp_w     and CSR_SATP_MASK);
        else
          csr_mpriv_q      <= PRIV_MACHINE;
          csr_medeleg_q    <= X"00000000";            --  32b
          csr_mideleg_q    <= X"00000000";            --  32b
          csr_sepc_q       <= X"00000000";            --  32b
          csr_stvec_q      <= X"00000000";            --  32b
          csr_scause_q     <= X"00000000";            --  32b
          csr_stval_q      <= X"00000000";            --  32b
          csr_satp_q       <= X"00000000";            --  32b
        end if;

        if (RISCV_S_U = '1') then
          csr_sscratch_q   <= (csr_sscratch_w and CSR_SSCRATCH_MASK);
        else
          csr_sscratch_q   <= X"00000000";            --  32b
        end if;

        if (buffer_mip_w = '1') then
          csr_mip_next_q   <=  csr_mip_next_w;
        else
          csr_mip_next_q   <=  X"00000000";           --  32b
        end if;
        --  increment upper cycle counter on lower 32-bit overflow
        if ( csr_mcycle_q = X"FFFFFFFF" ) then
          csr_mcycle_h_q   <= inc1u(csr_mcycle_h_q);  --  + 1
        end if;
      end if;
    end if;
  end process p_csr;


  -- - - - - - - -
  --  CSR branch
  -- - - - - - - -
  p_c_branch : process (irq_priv_q, csr_mtvec_q, csr_stvec_q, exception_i,
                        csr_mepc_q, csr_sepc_q, is_exception_w, exception_s_w,
                        exception_pc_i)
  begin
    branch_w        <= '0';
    branch_target_w <= X"00000000";                   -- 32b
    --  interrupts
    if (exception_i = EXCEPTION_INTERRUPT) then
      branch_w        <= '1';
      if (irq_priv_q = PRIV_MACHINE) then
        branch_target_w <= csr_mtvec_q;
      else
        branch_target_w <= csr_stvec_q;
      end if;
      --  exception return
    elsif ( ge(exception_i, EXCEPTION_ERET_U) and     -- 30
            le(exception_i, EXCEPTION_ERET_M) ) then  -- 33
      --  MRET (return from machine)
      if (exception_i(1 downto 0) = PRIV_MACHINE) then
        branch_w        <= '1';
        branch_target_w <= csr_mepc_q;
        --  SRET (return from supervisor)
      else
        branch_w        <= '1';
        branch_target_w <= csr_sepc_q;
      end if;
      --  exception - handled in super mode
    elsif (is_exception_w = '1' and exception_s_w = '1') then
      branch_w        <= '1';
      branch_target_w <= csr_stvec_q;
      --  exception - handled in machine mode
    elsif (is_exception_w = '1') then
      branch_w        <= '1';
      branch_target_w <= csr_mtvec_q;
      --  fence / SATP register writes cause pipeline flushes
    elsif (exception_i = EXCEPTION_FENCE) then
      branch_w        <= '1';
      branch_target_w <= addu(exception_pc_i, 4);     -- 32b
    end if;
  end process p_c_branch;

  csr_branch_o <= branch_w;
  csr_target_o <= branch_target_w;

end rtl;
