-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 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_fetch is
  port ( clk_i               : in  std_logic;
         xrst_i              : in  std_logic;
         --
         imem_rdata_i        : in  slv_31_00;
         imem_accept_i       : in  std_logic;
         imem_valid_i        : in  std_logic;
         imem_error_i        : in  std_logic;
         imem_page_fault_i   : in  std_logic;
         imem_rd_o           : out std_logic;
         imem_flush_o        : out std_logic;
         imem_invalidate_o   : out std_logic;
         imem_pc_o           : out slv_31_00;
         imem_priv_o         : out slv_01_00;
         --
         fetch_accept_i      : in  std_logic;
         branch_request_i    : in  std_logic;
         branch_pc_i         : in  slv_31_00;
         branch_priv_i       : in  slv_01_00;
         fetch_invalidate_i  : in  std_logic;
         --
         fetch_valid_o       : out std_logic;
         fetch_instr_o       : out slv_31_00;
         fetch_pc_o          : out slv_31_00;
         fetch_fault_fetch_o : out std_logic;
         fetch_fault_page_o  : out std_logic
         );
end entity rv_fetch;


architecture rtl of rv_fetch is

  -- - - - - - - - - - - - -
  --  registers / wires
  -- - - - - - - - - - - - -
  signal active_q              : std_logic;

  signal imem_busy_w           : std_logic;

  --  stall flag
  signal stall_w               : std_logic;
  --  buffered branch
  signal branch_q              : std_logic;
  signal branch_pc_q           : slv_31_00;
  signal branch_priv_q         : slv_01_00;

  --  request tracking
  signal imem_fetch_q          : std_logic;
  signal imem_invalidate_q     : std_logic;

  --  PC
  signal pc_f_q                : slv_31_00;
  signal pc_d_q                : slv_31_00;

  signal fetch_resp_drop_w     : std_logic;

  signal priv_f_q              : slv_01_00;
  signal branch_d_q            : std_logic;

  --  response buffer
  signal skid_buffer_q         : slv_65_00;
  signal skid_valid_q          : std_logic;

  --  cannot read output
  signal imem_rd_o_w           : std_logic;
  signal imem_invalidate_o_w   : std_logic;
  signal fetch_valid_o_w       : std_logic;
  signal fetch_fault_page_o_w  : std_logic;
  signal fetch_fault_fetch_o_w : std_logic;
  signal fetch_pc_o_w          : slv_31_00;
  signal fetch_instr_o_w       : slv_31_00;

begin

  -- stall flag
  stall_w  <= (not fetch_accept_i) or imem_busy_w or (not imem_accept_i);


  -- - - - - - - - - - - - -
  --  ICACHE fetch tracking
  -- - - - - - - - - - - - -
  p_ifetch : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        imem_fetch_q  <= '0';
      elsif (imem_rd_o_w = '1' and imem_accept_i = '1') then
        imem_fetch_q  <= '1';
      elsif (imem_valid_i = '1') then
        imem_fetch_q  <= '0';
      end if;
    end if;
  end process p_ifetch;


  -- - - - - - - - - - - - -
  --  buffered branch
  -- - - - - - - - - - - - -
  p_branch : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        branch_q         <= '0';
        branch_pc_q      <= X"00000000";
        branch_priv_q    <= PRIV_MACHINE;
      else
        if (branch_request_i = '1') then
          branch_q       <= '1';
          branch_pc_q    <= branch_pc_i;
          branch_priv_q  <= branch_priv_i;
        elsif (imem_rd_o_w = '1' and imem_accept_i = '1') then
          branch_q       <= '0';
          branch_pc_q    <= X"00000000";
        end if;
      end if;
    end if;
  end process p_branch;


  p_iinv : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        imem_invalidate_q <= '0';
      elsif (imem_invalidate_o_w = '1' and  imem_accept_i = '0') then
        imem_invalidate_q <= '1';
      else
        imem_invalidate_q <= '0';
      end if;
    end if;
  end process p_iinv;


  -- - - - - - - - - - - - -
  --  PC
  -- - - - - - - - - - - - -
  p_pc_f : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        active_q      <= '0';                                 --  active flag
        pc_f_q        <= X"00000000";
        branch_d_q    <= '0';
        priv_f_q      <= PRIV_MACHINE;
      else
        if (branch_q = '1' and  stall_w = '0') then           -- branch request
          active_q    <= '1';
          pc_f_q      <= branch_pc_q;
          priv_f_q    <= branch_priv_q;                       -- branch buf
          branch_d_q  <= '1';                                 -- branch buf
        elsif (stall_w = '0') then                            -- NPC
          pc_f_q      <= inc1u(pc_f_q(31 downto 2)) & "00";   -- +4
          branch_d_q  <= '0';
        end if;
      end if;
    end if;
  end process p_pc_f;


  --  last fetch address
  p_pc_d1 : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        pc_d_q <= X"00000000";
      elsif (imem_rd_o_w = '1' and imem_accept_i = '1') then
        pc_d_q <= pc_f_q;
      end if;
    end if;
  end process p_pc_d1;


  -- - - - - - - - - - - - -
  --  response buffer
  -- - - - - - - - - - - - -
  p_skid_buf : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        skid_buffer_q  <= (65 downto 0 => '0');
        skid_valid_q   <= '0';
      else        -- instruction output back-pressured - hold in skid buffer
        skid_valid_q    <= '0';
        skid_buffer_q   <= (65 downto 0 => '0');
        if (fetch_valid_o_w = '1' and  fetch_accept_i = '0') then
          skid_valid_q  <= '1';
          skid_buffer_q <= (fetch_fault_page_o_w  &  fetch_fault_fetch_o_w &
                            fetch_pc_o_w          &  fetch_instr_o_w);
        end if;
      end if;
    end if;
  end process p_skid_buf;


  -- - - - - - - - - - - - -
  --  outputs
  -- - - - - - - - - - - - -
  imem_rd_o_w           <= active_q and fetch_accept_i and (not imem_busy_w);
  imem_rd_o             <= imem_rd_o_w;
  imem_pc_o             <= (pc_f_q(31 downto 2) & "00");
  imem_priv_o           <= priv_f_q;
  imem_flush_o          <= fetch_invalidate_i or imem_invalidate_q;
  imem_invalidate_o_w   <= '0';
  imem_invalidate_o     <= imem_invalidate_o_w;

  imem_busy_w           <= imem_fetch_q and (not imem_valid_i);


  fetch_resp_drop_w     <= branch_q or branch_d_q;

  fetch_valid_o_w       <= (imem_valid_i or skid_valid_q) and
                           (not fetch_resp_drop_w);
  fetch_valid_o         <= fetch_valid_o_w;

  fetch_pc_o_w          <= skid_buffer_q(63 downto 32) when (skid_valid_q = '1')
                           else (pc_d_q(31 downto 2) & "00");
  fetch_pc_o            <= fetch_pc_o_w;

  fetch_instr_o_w       <= skid_buffer_q(31 downto 0)  when (skid_valid_q = '1')
                           else imem_rdata_i;


  fetch_instr_o         <= fetch_instr_o_w;

  --  faults
  fetch_fault_fetch_o_w <= skid_buffer_q(64) when (skid_valid_q = '1')
                           else imem_error_i;
  fetch_fault_fetch_o   <= fetch_fault_fetch_o_w;
  fetch_fault_page_o_w  <= skid_buffer_q(65) when (skid_valid_q = '1')
                           else imem_page_fault_i;
  fetch_fault_page_o    <= fetch_fault_page_o_w;


end rtl;
