-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 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_issue is
  generic ( RISCV_M      : std_logic := '1';  -- RV mul / div extension
            RISCV_E      : std_logic := '0';  -- RV rf embedded extension
            BYPASS_LOAD  : std_logic := '1';
            BYPASS_MULT  : std_logic := '1' );
  port ( clk_i                      : in  std_logic;
         xrst_i                     : in  std_logic;   -- syncr reset
         --  inputs
         fetch_valid_i              : in  std_logic;
         fetch_instr_i              : in  slv_31_00;
         fetch_pc_i                 : in  slv_31_00;
         fetch_fault_fetch_i        : in  std_logic;
         fetch_fault_page_i         : in  std_logic;
         -- instruction group (csr, mul, div, ...)
         idec_group_i               : in  i_group_t;
         iss_stall_o                : out std_logic;   -- pline stall
         iss_step_o                 : out std_logic;
         --
         branch_exe_req_i           : in  std_logic;
         branch_exe_pc_i            : in  slv_31_00;
         --
         mem_ld_fault_i             : in  std_logic;
         mem_st_fault_i             : in  std_logic;
         mem_ack_i                  : in  std_logic;
         mem_error_i                : in  std_logic;
         dmem_rdata_i               : in  slv_31_00;
         dmem_ldtype_i              : in  dmem_ldtyp_t;
         --
         rs1_dat_o                  : out slv_31_00;
         rs2_dat_o                  : out slv_31_00;
         rs1_idx_o                  : out slv_04_00;
         --
         exec_opc_vld_o             : out std_logic;
          -- multiplier / divider / ALU
         muldiv_opc_vld_o           : out std_logic;
         div_wb_vld_i               : in  std_logic;
         mul_wb_i                   : in  slv_31_00;
         div_wb_i                   : in  slv_31_00;
         alu_wb_i                   : in  slv_31_00;
           --
         amo_op_i                   : in  amo_op_t;    -- lr / sc / amofunc
         amofunc_p2_i               : in  slv_03_00;
         amo_pending_i              : in  std_logic;   -- from bus logic
         --
         lsu_stall_i                : in  std_logic;
         --  outputs
         fetch_accept_o             : out std_logic;
         branch_request_o           : out std_logic;
         branch_pc_o                : out slv_31_00;
         branch_priv_o              : out slv_01_00;
         lsu_opcode_valid_o         : out std_logic;
         --
         iword_p2_o                 : out slv_31_00;
         pc_e1_o                    : out slv_31_00;
         --
         csr_opcode_valid_o         : out std_logic;
         csr_opcode_invalid_o       : out std_logic;
         --
         branch_csr_req_i           : in  std_logic;
         branch_csr_pc_i            : in  slv_31_00;
         branch_csr_priv_i          : in  slv_01_00;
         csr_result_value_e1_i      : in  slv_31_00;
         csr_wr_e1_i                : in  std_logic;
         csr_wdata_e1_i             : in  slv_31_00;
         csr_result_exception_e1_i  : in  slv_05_00;
         --
         take_irq_i                 : in  std_logic;
         irq_block_o                : out std_logic;
         --
         csr_wb_write_o             : out std_logic;
         csr_wb_waddr_o             : out slv_11_00;
         csr_wb_wdata_o             : out slv_31_00;
         --
         csr_wb_exception_o         : out slv_05_00;
         csr_wb_exception_pc_o      : out slv_31_00;
         csr_wb_exception_addr_o    : out slv_31_00
         );
end entity rv_issue;


architecture rtl of rv_issue is

  component rv_pipe_ctrl
    generic ( BYPASS_LOAD  : std_logic := '1';
              BYPASS_MULT  : std_logic := '1' );
    port ( clk_i                      : in  std_logic;
           xrst_i                     : in  std_logic;   -- syncr reset
           -- issue
           issue_accept_i             : in  std_logic;
           pline_stall_i              : in  std_logic;
           issue_lsu_i                : in  std_logic;
           issue_csr_i                : in  std_logic;
           issue_csr_reg_i            : in  std_logic;   -- reg rd/wr
           issue_div_i                : in  std_logic;
           issue_mul_i                : in  std_logic;
           issue_branch_i             : in  std_logic;
           issue_rd_valid_i           : in  std_logic;
           issue_rd_i                 : in  slv_04_00;
           issue_exception_i          : in  slv_05_00;
           take_irq_i                 : in  std_logic;
           issue_branch_taken_i       : in  std_logic;
           issue_branch_target_i      : in  slv_31_00;
           issue_pc_i                 : in  slv_31_00;
           iword_p1_i                 : in  slv_31_00;
           -- execution stage 1: ALU result
           alu_wb_i                   : in  slv_31_00;
           -- execution stage 1: CSR read result / early exceptions
           csr_result_value_e1_i      : in  slv_31_00;
           csr_wr_e1_i                : in  std_logic;
           csr_wdata_e1_i             : in  slv_31_00;
           csr_result_exception_e1_i  : in  slv_05_00;
           -- execution stage 1
           load_e1_o                  : out std_logic;
           store_e1_o                 : out std_logic;
           mul_e1_o                   : out std_logic;
           rd_e1_o                    : out slv_04_00;
           pc_e1_o                    : out slv_31_00;
           -- execution stage 2 : Other results
           mem_ld_fault_i             : in  std_logic;
           mem_st_fault_i             : in  std_logic;
           mem_ack_i                  : in  std_logic;
           mem_err_i                  : in  std_logic;
           dmem_rdata_i               : in  slv_31_00;
           dmem_ldtype_i              : in dmem_ldtyp_t;
           mul_wb_i                   : in  slv_31_00;
           -- execution stage 2
           load_e2_o                  : out std_logic;
           mul_e2_o                   : out std_logic;
           rd_e2_o                    : out slv_04_00;
           result_e2_o                : out slv_31_00;
           --
           iword_p2_o                 : out slv_31_00;
           -- out of pipe : divide result
           div_wb_vld_i               : in  std_logic;
           div_wb_i                   : in  slv_31_00;
           -- commit
           csr_wb_o                   : out std_logic;
           rd_wb_o                    : out slv_04_00;
           result_wb_o                : out slv_31_00;
           pc_wb_o                    : out slv_31_00;
           exception_wb_o             : out slv_05_00;
           csr_write_wb_o             : out std_logic;
           csr_waddr_wb_o             : out slv_11_00;
           csr_wdata_wb_o             : out slv_31_00;
           --
           stall_o                    : out std_logic;
           squash_e1_e2_o             : out std_logic );
  end component;


  component rv_regfile
    generic ( RISCV_E  : std_logic := '0' );      -- RV rf embedded extension
    port ( clk_i      : in  std_logic;
           xrst_i     : in  std_logic;
           --  write port
           rd_addr_i  : in  slv_04_00;
           rd_data_i  : in  slv_31_00;
           -- read port a
           ra_addr_i  : in  slv_04_00;
           ra_data_o  : out slv_31_00;
           --  read port b
           rb_addr_i  : in  slv_04_00;
           rb_data_o  : out slv_31_00 );
  end component;

  --  issue select
  signal opcode_valid_w        : std_logic;

  signal rs1_idx_w             : slv_04_00;
  signal rs2_idx_w             : slv_04_00;
  signal rd_idx_w              : slv_04_00;

  --  pipeline status tracking
  signal pipe_squash_e1_e2_w   : std_logic;  -- squash when excwption

  signal pline_accept_w        : std_logic;
  signal pipe_stall_w          : std_logic;

  signal pipe_load_e1_w        : std_logic;
  signal pipe_store_e1_w       : std_logic;
  signal pipe_mul_e1_w         : std_logic;
  signal pipe_load_e2_w        : std_logic;
  signal pipe_mul_e2_w         : std_logic;
  signal pipe_csr_wb_w         : std_logic;

  signal pipe_rd_e1_w          : slv_04_00;

  signal pipe_rd_e2_w          : slv_04_00;
  signal pipe_result_e2_w      : slv_31_00;

  signal pipe_rd_wb_w          : slv_04_00;
  signal pipe_result_wb_w      : slv_31_00;

  signal issue_fault_w         : slv_t(EXCEPTION_W-1 downto 0);

  -- blocking events (division, CSR unit access)
  signal div_pending_q         : std_logic;
  signal csr_pending_q         : std_logic;

  -- issue / scheduling logic
  -- pragma translate_off
  signal scoreboard_w          : slv_31_00;  -- NC
  -- pragma translate_on

  -- register file
  signal rf_rs1_dat_w          : slv_31_00;
  signal rf_rs2_dat_w          : slv_31_00;

  signal issue_a_dat_w         : slv_31_00;
  signal issue_b_dat_w         : slv_31_00;

  signal ra_e2_sel_w           : std_logic;
  signal rb_e2_sel_w           : std_logic;

  signal rs1_sel_w             : slv_01_00;
  signal rs2_sel_w             : slv_01_00;
  signal rs1_dat_w             : slv_31_00;
  signal rs2_dat_w             : slv_31_00;

  -- cannot read output
  signal muldiv_opc_vld_o_w    : std_logic;
  signal csr_opcode_valid_o_w  : std_logic;

begin

  -- - - - - - - - - - - - -
  --  instruction decoder
  -- - - - - - - - - - - - -
  rs1_idx_w        <= fetch_instr_i(RS1_R'range);   -- p1  19:15 rf src 1
  rs2_idx_w        <= fetch_instr_i(RS2_R'range);   -- p1  24:20 rf src 2
  rd_idx_w         <= fetch_instr_i(RD_R'range);    -- p1  11:7  rf destination


  -- - - - - - - - - - - - -
  --  issue select
  -- - - - - - - - - - - - -
  opcode_valid_w   <= fetch_valid_i and (not pipe_squash_e1_e2_w) and
                      (not branch_csr_req_i);

  -- branch request (CSR branch - ecall, xret, or branch instruction)
  branch_request_o <= branch_csr_req_i or branch_exe_req_i;
  branch_pc_o      <= branch_csr_pc_i when (branch_csr_req_i = '1')
                      else branch_exe_pc_i;
  branch_priv_o    <= branch_csr_priv_i;

  issue_fault_w <= EXCEPTION_FAULT_FETCH when (fetch_fault_fetch_i = '1') else
                   EXCEPTION_PAGE_FAULT_INST when (fetch_fault_page_i = '1')
                   else (EXCEPTION_W-1 downto 0 => '0');

  iss_stall_o      <= pipe_stall_w;
  iss_step_o       <= pline_accept_w and (not pipe_squash_e1_e2_w);


  u_pipe_ctrl : rv_pipe_ctrl
    generic map ( BYPASS_LOAD  => BYPASS_LOAD,
                  BYPASS_MULT  => BYPASS_MULT )
    port map ( clk_i                     => clk_i,
               xrst_i                    => xrst_i,
               --
               -- issue
               issue_accept_i            => pline_accept_w,
               pline_stall_i             => pipe_stall_w,
               --
               issue_rd_valid_i          => idec_group_i.rd_valid, -- sb alloc
               issue_lsu_i               => idec_group_i.lsu,
               issue_csr_i               => idec_group_i.csr,
               issue_csr_reg_i           => idec_group_i.csr_reg,  -- reg rd/wr
               issue_div_i               => idec_group_i.div,
               issue_mul_i               => idec_group_i.mul,
               --
               issue_branch_i            => idec_group_i.branch,
               issue_rd_i                => rd_idx_w,
               issue_exception_i         => issue_fault_w,
               issue_pc_i                => fetch_pc_i,
               iword_p1_i                => fetch_instr_i,
               issue_branch_taken_i      => branch_exe_req_i,      -- exec
               issue_branch_target_i     => branch_exe_pc_i,       -- exec
               take_irq_i                => take_irq_i,
               -- execution stage 1: ALU result
               alu_wb_i                  => alu_wb_i,              -- e1 result
               csr_result_value_e1_i     => csr_result_value_e1_i,
               csr_wr_e1_i               => csr_wr_e1_i,
               csr_wdata_e1_i            => csr_wdata_e1_i,
               csr_result_exception_e1_i => csr_result_exception_e1_i,
               -- execution stage 1
               iword_p2_o                => iword_p2_o,
               pc_e1_o                   => pc_e1_o,
               --
               load_e1_o                 => pipe_load_e1_w,
               store_e1_o                => pipe_store_e1_w,
               mul_e1_o                  => pipe_mul_e1_w,
               rd_e1_o                   => pipe_rd_e1_w,
               -- execution stage 2: other results
               mem_ld_fault_i            => mem_ld_fault_i,
               mem_st_fault_i            => mem_st_fault_i,
               mem_ack_i                 => mem_ack_i,
               mem_err_i                 => mem_error_i,
               dmem_rdata_i              => dmem_rdata_i,
               dmem_ldtype_i             => dmem_ldtype_i,
               -- execution stage 2
               load_e2_o                 => pipe_load_e2_w,
               mul_e2_o                  => pipe_mul_e2_w,
               stall_o                   => pipe_stall_w,
               squash_e1_e2_o            => pipe_squash_e1_e2_w,
               --
               mul_wb_i                  => mul_wb_i,
               -- out of pipe: divide result
               div_wb_vld_i              => div_wb_vld_i,
               div_wb_i                  => div_wb_i,
               -- commit
               csr_wb_o                  => pipe_csr_wb_w,
               --
               rd_e2_o                   => pipe_rd_e2_w,
               result_e2_o               => pipe_result_e2_w, -- BB mem/mul/e2
               --
               rd_wb_o                   => pipe_rd_wb_w,  -- rfile waddr (rd)
               result_wb_o               => pipe_result_wb_w, -- e2 result
               --
               pc_wb_o                   => csr_wb_exception_pc_o,
               exception_wb_o            => csr_wb_exception_o,
               --
               csr_write_wb_o            => csr_wb_write_o,
               csr_waddr_wb_o            => csr_wb_waddr_o,
               csr_wdata_wb_o            => csr_wb_wdata_o );


  -- - - - - - - - - - - - -
  --  pipe1 - status tracking
  -- - - - - - - - - - - - -
  csr_wb_exception_addr_o  <= pipe_result_wb_w;


  -- - - - - - - - - - - - - - - - - - - - - - -
  --  blocking events (division, CSR unit access)
  -- - - - - - - - - - - - - - - - - - - - - - -
  --  division operations take 32+/16+ cycles and stall
  --  the pipeline (complete out-of-pipe) until completed
  p_div_pend : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        div_pending_q  <= '0';
      elsif (pipe_squash_e1_e2_w = '1') then     -- squash when excp
        div_pending_q  <= '0';
      elsif (muldiv_opc_vld_o_w = '1' and idec_group_i.div = '1') then
        div_pending_q  <= '1';
      elsif (div_wb_vld_i = '1') then
        div_pending_q  <= '0';
      end if;
    end if;
  end process p_div_pend;


  -- CSR operations are infrequent - avoid any complications of pipelining them.
  -- These only take a 2-3 cycles anyway and may result in a pipe flush
  -- (e.g. ecall, ebreak..).
  p_csr_pend : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        csr_pending_q    <= '0';
      else
        if (pipe_squash_e1_e2_w = '1') then     -- squash when excp
          csr_pending_q  <= '0';
        elsif (csr_opcode_valid_o_w = '1' and idec_group_i.csr = '1') then
          csr_pending_q  <= '1';
        elsif (pipe_csr_wb_w = '1') then
          csr_pending_q  <= '0';
        end if;
      end if;
    end if;
  end process p_csr_pend;


  -- - - - - - - - - - - - -
  --  issue / scheduling logic
  -- - - - - - - - - - - - -
  p_c_scoreb : process (pipe_load_e2_w, pipe_rd_e2_w, pipe_mul_e2_w,
                        pipe_load_e1_w, pipe_mul_e1_w, pipe_rd_e1_w,
                        pipe_store_e1_w,
                        lsu_stall_i, pipe_stall_w,
                        div_pending_q, csr_pending_q, opcode_valid_w,
                        rs1_idx_w, rs2_idx_w, rd_idx_w,
                        idec_group_i )
    variable scoreb_v : slv_31_00;
  begin
    scoreb_v        := X"00000000";
    pline_accept_w  <= '0';
    --  execution units with >= 2 cycle latency
    scoreb_v(to_uint(pipe_rd_e2_w)) := (pipe_load_e2_w and (not BYPASS_LOAD))
                                      or (pipe_mul_e2_w and (not BYPASS_MULT));
    --  execution units with >= 1 cycle latency (loads / multiply)
    scoreb_v(to_uint(pipe_rd_e1_w)) := pipe_load_e1_w or pipe_mul_e1_w;
    --  do not start multiply, division or CSR operation in the cycle
    --  after a load (leaving only ALU operations and branches)
    if ((pipe_load_e1_w = '1'   or pipe_store_e1_w = '1')  and
        (idec_group_i.mul = '1' or idec_group_i.div  = '1' or
         idec_group_i.csr = '1')) then
    scoreb_v := X"FFFFFFFF";
    end if;
    --  stall - no issues...
    if (lsu_stall_i = '1' or pipe_stall_w = '1' or div_pending_q = '1' or
        csr_pending_q = '1') then
      null;
    --  primary slot (lsu, branch, alu, mul, div, csr)
    elsif (opcode_valid_w  = '1' and
           (scoreb_v(to_uint(rs1_idx_w)) = '0' and
            scoreb_v(to_uint(rs2_idx_w)) = '0' and
            ( scoreb_v(to_uint(rd_idx_w)) = '0' ) )) then
      pline_accept_w <= '1';
    end if;
    --
    -- pragma translate_off
    scoreboard_w <= scoreb_v;           -- NC
    -- pragma translate_on
    --
  end process p_c_scoreb;


  lsu_opcode_valid_o   <= pline_accept_w and (not take_irq_i);
  exec_opc_vld_o       <= pline_accept_w;

  muldiv_opc_vld_o_w   <= pline_accept_w  and RISCV_M;
  muldiv_opc_vld_o     <= muldiv_opc_vld_o_w;

  irq_block_o          <= ( csr_pending_q or idec_group_i.csr ) or
                          ( amo_pending_i or not_zero(amofunc_p2_i) );

  fetch_accept_o       <= (pline_accept_w and (not take_irq_i))
                          when (opcode_valid_w = '1')
                          else '1';


  -- - - - - - - - - - - - -
  --  register file
  -- - - - - - - - - - - - -
  -- register file: 1W2R
  u_regfile : rv_regfile
    generic map ( RISCV_E => RISCV_E )          -- RV rf embedded extension
    port map (clk_i       => clk_i,
              xrst_i      => xrst_i,
              --  write port
              rd_addr_i   => pipe_rd_wb_w,
              rd_data_i   => pipe_result_wb_w,
              --  read port a
              ra_addr_i   => rs1_idx_w,
              ra_data_o   => rf_rs1_dat_w,      -- zero when (rs1_idx_w = 0)
              --  read port b
              rb_addr_i   => rs2_idx_w,
              rb_data_o   => rf_rs2_dat_w );    -- zero when (rs2_idx_w = 0)


  -- Bypass - WB   pipe_result_wb_w
  -- Bypass - E2   pipe_result_e2_w    dmem
  -- Bypass - E1   alu_wb_i

  p_c_issue : process (rf_rs1_dat_w, rf_rs2_dat_w, pipe_rd_wb_w,
                       rs1_idx_w, pipe_result_wb_w, rs2_idx_w,
                       pipe_rd_e1_w, alu_wb_i)
    variable rs1_id_v : slv_01_00;
    variable rs2_id_v : slv_01_00;
  begin
    rs1_id_v(1) := (eq(rs1_idx_w, pipe_rd_wb_w) or eq(rs1_idx_w, pipe_rd_e1_w))
                   and not_zero(rs1_idx_w);
    rs1_id_v(0) := eq(rs1_idx_w, pipe_rd_e1_w);
    case rs1_id_v is
      when "11"    => issue_a_dat_w <= alu_wb_i;
      when "10"    => issue_a_dat_w <= pipe_result_wb_w;
      when others  => issue_a_dat_w <= rf_rs1_dat_w;
    end case;
    --
    rs2_id_v(1) := (eq(rs2_idx_w, pipe_rd_wb_w) or eq(rs2_idx_w, pipe_rd_e1_w))
                   and not_zero(rs2_idx_w);
    rs2_id_v(0) := eq(rs2_idx_w, pipe_rd_e1_w);
    case rs2_id_v is
      when "11"    => issue_b_dat_w <= alu_wb_i;
      when "10"    => issue_b_dat_w <= pipe_result_wb_w;
      when others  => issue_b_dat_w <= rf_rs2_dat_w;
    end case;
  end process p_c_issue;

  ra_e2_sel_w <= eq(rs1_idx_w, pipe_rd_e2_w) and
                 ne(rs1_idx_w, pipe_rd_e1_w) and
                 not_zero(rs1_idx_w);

  rb_e2_sel_w <= eq(rs2_idx_w, pipe_rd_e2_w) and
                 ne(rs2_idx_w, pipe_rd_e1_w) and
                 not_zero(rs2_idx_w);

  -- - - - - - - - - - - - -
  --  issue slot 0
  -- - - - - - - - - - - - -
  -- dmem / mul / pipe.reg : alu / csr
  rs1_dat_o            <= pipe_result_e2_w when (ra_e2_sel_w = '1')
                          else issue_a_dat_w;
  rs2_dat_o            <= pipe_result_e2_w when (rb_e2_sel_w = '1')
                          else issue_b_dat_w;

  rs1_idx_o            <= rs1_idx_w;   -- reg addr rs1


  csr_opcode_valid_o_w  <= pline_accept_w and (not take_irq_i);
  csr_opcode_valid_o    <= csr_opcode_valid_o_w;
  csr_opcode_invalid_o  <= pline_accept_w and idec_group_i.invalid;

end rtl;
