-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 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_alu is
  port ( alu_op_i : in  slv_03_00;
         alu_a_i  : in  slv_31_00;
         alu_b_i  : in  slv_31_00;
         --
         alu_p_o  : out slv_31_00
         );
end entity rv_alu;


architecture rtl of rv_alu is

  signal result_w       : slv_31_00;
  signal add_sub_res_w  : slv_31_00;
  signal alu_b_w        : slv_t(alu_b_i'range);

begin

  --
  -- alu_op_i(3) = '0' => ADD     (signed(rs1) + signed(rs2)
  -- alu_op_i(3) = '1' => SUB     (signed(rs1) - signed(rs2)
  --
  --  ALU_ADD | ALU_SUB  | ALU_LESS_THAN_SIGNED
  --
  alu_b_w       <= slv_t(-signed(alu_b_i)) when (alu_op_i = ALU_LESS_THAN_SIGNED
                                                 or alu_op_i = ALU_SUB) else
                   alu_b_i;
  add_sub_res_w <= adds(alu_a_i, alu_b_w);


  -- - - - - -
  --  ALU
  -- - - - - -
  p_c_alu : process (alu_op_i, alu_a_i, alu_b_i, add_sub_res_w)
    type bs_mtx_t is array (log2(alu_a_i'length) downto 0) of
      slv_t(alu_a_i'range);             -- XLEN = 32 ==> 5 downto 0
    variable bs_mtx_v   : bs_mtx_t;
    variable bs_sign_v  : std_logic;

  begin
    bs_mtx_v   := (others => (others => '0'));
    bs_sign_v  := '0';
    result_w   <= (others => '0');
    --
    case alu_op_i is
      -- - - - - - - - -
      --  shift left << or shift right >> (logic / arith)
      -- - - - - - - - -
      when ALU_SHIFTL | ALU_SHIFTR | ALU_SHIFTR_ARITH =>
        --
        bs_mtx_v(bs_mtx_v'high) := alu_a_i;
        if (alu_op_i = ALU_SHIFTL) then
          bs_mtx_v(bs_mtx_v'high) := rev_bits(alu_a_i);
        end if;
        ---  arithmetic shiftr ?  fill with 1's if MSB set
        if (alu_op_i = ALU_SHIFTR_ARITH) then
          bs_sign_v     := alu_a_i(alu_a_i'left);
        end if;
        --
        for i in bs_mtx_v'high-1 downto 0 loop
          if (alu_b_i(i) = '1') then
            bs_mtx_v(i)(alu_a_i'left downto alu_a_i'length-(2**i)) :=
              (others => bs_sign_v); -- arithmetic / logical
            bs_mtx_v(i)((32-(2**i))-1 downto 0) :=
              bs_mtx_v(i+1)(32-1 downto 2**i);
          else
            bs_mtx_v(i) := bs_mtx_v(i+1);
          end if;
        end loop;
        result_w        <= bs_mtx_v(0);
        if (alu_op_i = ALU_SHIFTL) then
          result_w      <= rev_bits(bs_mtx_v(0));
        end if;
      -- - - - - - - - -
      --  arithmetic
      -- - - - - - - - -
      when ALU_ADD | ALU_SUB =>
        result_w        <= add_sub_res_w;
      -- - - - - - - - -
      --  logical
      -- - - - - - - - -
      when ALU_AND =>
        result_w        <= (alu_a_i and alu_b_i);
      when ALU_OR  =>
        result_w        <= (alu_a_i or  alu_b_i);
      when ALU_XOR =>
        result_w        <= (alu_a_i xor alu_b_i);
      -- - - - - - - - -
      --  comparision
      -- - - - - - - - -
      when ALU_LESS_THAN =>
        if ( lt(alu_a_i, alu_b_i) ) then
          result_w(0)   <=  '1';                            --  0x00000001
        end if;
      when ALU_LESS_THAN_SIGNED =>
        if (alu_a_i(31) /= alu_b_i(31)) then                -- -a - +b
          result_w(0)   <= alu_a_i(31);
        else
          -- result_w)0) <= sub_res_w(31);
          result_w(0)   <= add_sub_res_w(31);
        end if;
      when others  =>
        result_w        <= alu_b_i;                         -- ALU_NONE (LUI)
    end case;
  end process p_c_alu;

  alu_p_o <= result_w;

end rtl;
