-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- 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_multiplier is
  port ( clk_i          : in  std_logic;
         xrst_i         : in  std_logic;  -- syncr reset
         --  inputs
         mul_op_p1_i    : in  mul_op_t;
         pline_stall_i  : in  std_logic;
         valid_i        : in  std_logic;
         rs1_dat_i      : in  slv_31_00;
         rs2_dat_i      : in  slv_31_00;
         --  outputs
         wb_value_o     : out slv_31_00
         );
end entity rv_multiplier;


architecture rtl of rv_multiplier is

  -- - - - - - - - - - - -
  --  registers / wires
  -- - - - - - - - - - - -

  signal rs1_dat_e1_q   : slv_32_00;  -- NOTE: 33 bits
  signal rs2_dat_e1_q   : slv_32_00;  -- NOTE: 33 bits
  signal mulhi_sel_e1_q : std_logic;

  -- - - - - - - -
  --  multiplier
  -- - - - - - - -
  signal mult_result_w  : slv_65_00; -- NOTE: 66 bits
  signal operand_b_w    : slv_32_00;
  signal operand_a_w    : slv_32_00;
  signal result_w       : slv_31_00;

  signal mult_op_w      : std_logic;

  -- output hold register when 2-stage multiplier (input and output registers)
  signal result_e2_q    : slv_31_00;

begin

  mult_op_w    <= mul_op_p1_i.i_mul    or  mul_op_p1_i.i_mulh   or
                  mul_op_p1_i.i_mulhsu or  mul_op_p1_i.i_mulhu;


  -- sign bit when MULHSU / MULH,  '0' when  MULHU / MUL
  operand_a_w  <= (rs1_dat_i(31) and
                   (mul_op_p1_i.i_mulhsu or mul_op_p1_i.i_mulh))  &  rs1_dat_i;

  -- sign bit when MULH,  '0' when MULHU / MUL / MULHSU
  operand_b_w  <= (rs2_dat_i(31) and mul_op_p1_i.i_mulh)  &  rs2_dat_i;


  --  Pipeline flops for multiplier
  p_operands : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        rs1_dat_e1_q      <= (others => '0');    -- 33b
        rs2_dat_e1_q      <= (others => '0');    -- 33b
        mulhi_sel_e1_q    <= '0';
      else
        if (pline_stall_i = '1') then
          null;
        elsif (valid_i = '1' and mult_op_w = '1') then
          rs1_dat_e1_q    <= operand_a_w;
          rs2_dat_e1_q    <= operand_b_w;
          mulhi_sel_e1_q  <= not mul_op_p1_i.i_mul;
        else
          rs1_dat_e1_q    <= (others => '0');    -- 33b
          rs2_dat_e1_q    <= (others => '0');    -- 33b
          mulhi_sel_e1_q  <= '0';
        end if;
      end if;
    end if;
  end process p_operands;


  -- mult 65:0
  mult_result_w <= slv_t(signed(rs1_dat_e1_q) * signed(rs2_dat_e1_q));


  result_w <= mult_result_w(63 downto 32) when (mulhi_sel_e1_q = '1')
              else mult_result_w(31 downto 0);


  --
  -- 1-stage multiplier (only input registers)
  --
  -- wb_value_o  <= result_w;


  --
  -- 2-stage multiplier (input and output registers)
  --
  p_res_e2 : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        result_e2_q  <= (others => '0');       -- 32b
      elsif (pline_stall_i = '0') then
        result_e2_q  <= result_w;
      end if;
    end if;
  end process p_res_e2;

  wb_value_o  <= result_e2_q;

end rtl;
