-- ================================================================================ --
-- NEORV32 CPU - Arithmetic/Logic Unit                                              --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32              --
-- Copyright (c) NEORV32 contributors.                                              --
-- Copyright (c) 2020 - 2026 Stephan Nolting. All rights reserved.                  --
-- Licensed under the BSD-3-Clause license, see LICENSE for details.                --
-- SPDX-License-Identifier: BSD-3-Clause                                            --
-- ================================================================================ --

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

library neorv32;
use neorv32.neorv32_package.all;

entity neorv32_cpu_alu is
  generic (
    -- RISC-V ISA Extensions --
    RISCV_ISA_M      : boolean; -- mul/div extension
    RISCV_ISA_Zba    : boolean; -- address-generation instruction
    RISCV_ISA_Zbb    : boolean; -- basic bit-manipulation instruction
    RISCV_ISA_Zbc    : boolean; -- carry-less multiplication instructions
    RISCV_ISA_Zbkb   : boolean; -- bit-manipulation instructions for cryptography
    RISCV_ISA_Zbkc   : boolean; -- carry-less multiplication instructions
    RISCV_ISA_Zbkx   : boolean; -- cryptography crossbar permutation extension
    RISCV_ISA_Zbs    : boolean; -- single-bit instructions
    RISCV_ISA_Zfinx  : boolean; -- 32-bit floating-point extension
    RISCV_ISA_Zibi   : boolean; -- branch with immediate
    RISCV_ISA_Zicond : boolean; -- integer conditional operations
    RISCV_ISA_Zknd   : boolean; -- cryptography NIST AES decryption extension
    RISCV_ISA_Zkne   : boolean; -- cryptography NIST AES encryption extension
    RISCV_ISA_Zknh   : boolean; -- cryptography NIST hash extension
    RISCV_ISA_Zksed  : boolean; -- ShangMi block cipher extension
    RISCV_ISA_Zksh   : boolean; -- ShangMi hash extension
    RISCV_ISA_Zmmul  : boolean; -- multiply-only M sub-extension
    RISCV_ISA_Xcfu   : boolean; -- custom (instr.) functions unit
    -- Tuning Options --
    FAST_MUL_EN      : boolean; -- use DSPs for M extension's multiplier
    FAST_SHIFT_EN    : boolean  -- use barrel shifter for shift operations
  );
  port (
    -- global control --
    clk_i  : in  std_ulogic; -- global clock, rising edge
    rstn_i : in  std_ulogic; -- global reset, low-active, async
    ctrl_i : in  ctrl_bus_t; -- main control bus
    -- data input --
    rs1_i  : in  std_ulogic_vector(31 downto 0); -- register source 1
    rs2_i  : in  std_ulogic_vector(31 downto 0); -- register source 2
    -- data output --
    cmp_o  : out std_ulogic_vector(1 downto 0);  -- comparator status
    res_o  : out std_ulogic_vector(31 downto 0); -- ALU result
    add_o  : out std_ulogic_vector(31 downto 0); -- address computation result
    csr_o  : out std_ulogic_vector(31 downto 0); -- CSR read data
    -- status --
    done_o : out std_ulogic -- co-processor operation done?
  );
end neorv32_cpu_alu;

architecture neorv32_cpu_alu_rtl of neorv32_cpu_alu is

  -- Zibi ISA extension: compare with immediate --
  function zibi_cmp_f(sel : std_ulogic_vector(4 downto 0); cmp : std_ulogic_vector) return std_ulogic is
    variable imm_v : std_ulogic_vector(31 downto 0);
  begin
    if (sel = "00000") then
      imm_v := (others => '1');
    else
      imm_v := replicate_f('0', 27) & sel;
    end if;
    return bool_to_ulogic_f(imm_v = cmp);
  end function zibi_cmp_f;

  -- wiring --
  signal opa, opb, cp_res : std_ulogic_vector(31 downto 0);
  signal cmp_rs1, cmp_rs2, opa_x, opb_x, addsub : std_ulogic_vector(32 downto 0);
  signal cmp : std_ulogic_vector(1 downto 0);

  -- co-processor interface --
  type cp_data_t  is array (0 to 6) of std_ulogic_vector(31 downto 0);
  signal cp_result : cp_data_t;
  signal cp_valid : std_ulogic_vector(6 downto 0);
  signal cfu_inst : std_ulogic_vector(31 downto 0);
  signal fpu_csr_en, fpu_csr_we, cfu_done, cfu_busy : std_ulogic;
  signal fpu_csr_rd, cfu_res : std_ulogic_vector(31 downto 0);

begin

  -- Comparator Unit ------------------------------------------------------------------------
  -- -------------------------------------------------------------------------------------------
  cmp_rs1 <= (rs1_i(rs1_i'left) and (not ctrl_i.alu_unsigned)) & rs1_i; -- sign-extend
  cmp_rs2 <= (rs2_i(rs2_i'left) and (not ctrl_i.alu_unsigned)) & rs2_i; -- sign-extend
  cmp(0) <= '1' when (rs1_i = rs2_i) else '0';
  cmp(1) <= '1' when (signed(cmp_rs1) < signed(cmp_rs2)) else '0'; -- signed or unsigned comparison

  zibi_enabled:
  if RISCV_ISA_Zibi generate
    cmp_o(0) <= zibi_cmp_f(ctrl_i.rf_rs2, rs1_i) when (ctrl_i.ir_funct3(2 downto 1) = "01") else cmp(0);
    cmp_o(1) <= cmp(1);
  end generate;

  zibi_disabled:
  if not RISCV_ISA_Zibi generate
    cmp_o <= cmp;
  end generate;

  -- ALU Core -------------------------------------------------------------------------------
  -- -------------------------------------------------------------------------------------------
  alu_core: process(ctrl_i, addsub, cp_res, opb, rs1_i)
  begin
    res_o <= (others => '0');
    case ctrl_i.alu_op is
      when alu_op_zero_c => res_o <= (others => '0');
      when alu_op_add_c  => res_o <= addsub(31 downto 0);
      when alu_op_cp_c   => res_o <= cp_res;
      when alu_op_slt_c  => res_o(0) <= addsub(addsub'left); -- carry/borrow
      when alu_op_movb_c => res_o <= opb;
      when alu_op_xor_c  => res_o <= opb xor rs1_i;
      when alu_op_or_c   => res_o <= opb or  rs1_i;
      when alu_op_and_c  => res_o <= opb and rs1_i;
      when others        => res_o <= (others => 'X'); -- undefined
    end case;
  end process alu_core;

  -- operands --
  opa   <= ctrl_i.pc_cur  when (ctrl_i.alu_opa_mux = '1') else rs1_i;
  opb   <= ctrl_i.alu_imm when (ctrl_i.alu_opb_mux = '1') else rs2_i;
  opa_x <= (opa(opa'left) and (not ctrl_i.alu_unsigned)) & opa; -- sign-extend
  opb_x <= (opb(opb'left) and (not ctrl_i.alu_unsigned)) & opb; -- sign-extend

  -- adder/subtractor
  add_o  <= addsub(31 downto 0); -- direct output
  addsub <= std_ulogic_vector(unsigned(opa_x) - unsigned(opb_x)) when (ctrl_i.alu_sub = '1') else
            std_ulogic_vector(unsigned(opa_x) + unsigned(opb_x));

  -- **************************************************************************************************************************
  -- Co-Processors
  -- **************************************************************************************************************************

  -- result and status --
  done_o <= cp_valid(0) or cp_valid(1) or cp_valid(2) or cp_valid(3) or cp_valid(4) or cp_valid(5) or cp_valid(6);
  cp_res <= cp_result(0) or cp_result(1) or cp_result(2) or cp_result(3) or cp_result(4) or cp_result(5) or cp_result(6);

  -- ALU[I]-Opcode Co-Processor: Shifter Unit (Base ISA) ------------------------------------
  -- -------------------------------------------------------------------------------------------
  neorv32_cpu_alu_shifter_inst: entity neorv32.neorv32_cpu_alu_shifter
  generic map (
    FAST_SHIFT_EN => FAST_SHIFT_EN -- use barrel shifter for shift operations
  )
  port map (
    -- global control --
    clk_i   => clk_i,           -- global clock, rising edge
    rstn_i  => rstn_i,          -- global reset, low-active, async
    ctrl_i  => ctrl_i,          -- main control bus
    -- data input --
    rs1_i   => rs1_i,           -- register source 1
    shamt_i => opb(4 downto 0), -- shift amount
    -- result and status --
    res_o   => cp_result(0),    -- operation result
    valid_o => cp_valid(0)      -- data output valid
  );

  -- ALU-Opcode Co-Processor: Integer Multiplication/Division Unit (M ISA Extension) --------
  -- -------------------------------------------------------------------------------------------
  neorv32_cpu_alu_muldiv_enabled:
  if RISCV_ISA_M or RISCV_ISA_Zmmul generate
    neorv32_cpu_alu_muldiv_inst: entity neorv32.neorv32_cpu_alu_muldiv
    generic map (
      FAST_MUL_EN => FAST_MUL_EN, -- use DSPs for faster multiplication
      DIVISION_EN => RISCV_ISA_M  -- implement divider hardware
    )
    port map (
      -- global control --
      clk_i   => clk_i,        -- global clock, rising edge
      rstn_i  => rstn_i,       -- global reset, low-active, async
      ctrl_i  => ctrl_i,       -- main control bus
      -- data input --
      rs1_i   => rs1_i,        -- register source 1
      rs2_i   => rs2_i,        -- register source 2
      -- result and status --
      res_o   => cp_result(1), -- operation result
      valid_o => cp_valid(1)   -- data output valid
    );
  end generate;

  neorv32_cpu_alu_muldiv_disabled:
  if (not RISCV_ISA_M) and (not RISCV_ISA_Zmmul) generate
    cp_result(1) <= (others => '0');
    cp_valid(1)  <= '0';
  end generate;

  -- ALU[I]-Opcode Co-Processor: Bit-Manipulation Unit (B ISA Extension) --------------------
  -- -------------------------------------------------------------------------------------------
  neorv32_cpu_alu_bitmanip_enabled:
  if RISCV_ISA_Zba or RISCV_ISA_Zbb or RISCV_ISA_Zbc or RISCV_ISA_Zbkb or RISCV_ISA_Zbkc or RISCV_ISA_Zbkx or RISCV_ISA_Zbs generate
    neorv32_cpu_alu_bitmanip_inst: entity neorv32.neorv32_cpu_alu_bitmanip
    generic map (
      FAST_SHIFT => FAST_SHIFT_EN,  -- use barrel shifter for shift operations
      ZBA        => RISCV_ISA_Zba,  -- address-generation instruction
      ZBB        => RISCV_ISA_Zbb,  -- basic bit-manipulation instruction
      ZBC        => RISCV_ISA_Zbc,  -- carry-less multiplication instructions
      ZBKB       => RISCV_ISA_Zbkb, -- bit-manipulation instructions for cryptography
      ZBKC       => RISCV_ISA_Zbkc, -- carry-less multiplication instructions
      ZBKX       => RISCV_ISA_Zbkx, -- crossbar permutation instructions for cryptography
      ZBS        => RISCV_ISA_Zbs   -- single-bit instructions
    )
    port map (
      -- global control --
      clk_i   => clk_i,           -- global clock, rising edge
      rstn_i  => rstn_i,          -- global reset, low-active, async
      ctrl_i  => ctrl_i,          -- main control bus
      -- data input --
      less_i  => cmp(1),          -- compare less
      rs1_i   => rs1_i,           -- register source 1
      rs2_i   => rs2_i,           -- register source 2
      shamt_i => opb(4 downto 0), -- shift amount
      -- result and status --
      res_o   => cp_result(2),    -- operation result
      valid_o => cp_valid(2)      -- data output valid
    );
  end generate;

  neorv32_cpu_alu_bitmanip_disabled:
  if not (RISCV_ISA_Zba or RISCV_ISA_Zbb or RISCV_ISA_Zbc or RISCV_ISA_Zbkb or RISCV_ISA_Zbkc or RISCV_ISA_Zbkx or RISCV_ISA_Zbs) generate
    cp_result(2) <= (others => '0');
    cp_valid(2)  <= '0';
  end generate;

  -- FLOAT-Opcode Co-Processor: Single-Precision FPUUnit (Zfinx ISA Extension) --------------
  -- -------------------------------------------------------------------------------------------
  neorv32_cpu_alu_fpu_enabled:
  if RISCV_ISA_Zfinx generate
    neorv32_cpu_alu_fpu_inst: entity neorv32.neorv32_cpu_alu_fpu
    port map (
      -- global control --
      clk_i       => clk_i,                       -- global clock, rising edge
      rstn_i      => rstn_i,                      -- global reset, low-active, async
      ctrl_i      => ctrl_i,                      -- main control bus
      -- CSR interface --
      csr_we_i    => fpu_csr_we,                  -- write enable
      csr_addr_i  => ctrl_i.csr_addr(1 downto 0), -- address
      csr_wdata_i => ctrl_i.csr_wdata,            -- write data
      csr_rdata_o => fpu_csr_rd,                  -- read data
      -- data input --
      equal_i     => cmp(0),                      -- compare equal
      less_i      => cmp(1),                      -- compare less
      rs1_i       => rs1_i,                       -- register source 1
      rs2_i       => rs2_i,                       -- register source 2
      -- result and status --
      res_o       => cp_result(3),                -- operation result
      valid_o     => cp_valid(3)                  -- data output valid
    );

    -- CSR proxy --
    fpu_csr_en <= '1' when (ctrl_i.csr_addr(11 downto 2) = csr_fflags_c(11 downto 2)) else '0';
    fpu_csr_we <= fpu_csr_en and ctrl_i.csr_we;
    csr_o      <= fpu_csr_rd when (fpu_csr_en = '1') else (others => '0');
  end generate;

  neorv32_cpu_alu_fpu_disabled:
  if not RISCV_ISA_Zfinx generate
    fpu_csr_en   <= '0';
    fpu_csr_we   <= '0';
    fpu_csr_rd   <= (others => '0');
    csr_o        <= (others => '0');
    cp_result(3) <= (others => '0');
    cp_valid(3)  <= '0';
  end generate;

  -- CUSTOM/OP32-Opcode Co-Processor: Custom Functions Unit (Xcfu ISA Extension) ------------
  -- -------------------------------------------------------------------------------------------
  neorv32_cpu_alu_cfu_enabled:
  if RISCV_ISA_Xcfu generate
    neorv32_cpu_alu_cfu_inst: entity neorv32.neorv32_cpu_alu_cfu
    port map (
      -- global control --
      clk_i    => clk_i,             -- global clock, rising edge
      rstn_i   => rstn_i,            -- global reset, low-active, async
      -- request
      start_i  => ctrl_i.alu_cp_cfu, -- start trigger, single-shot
      inst_i   => cfu_inst,          -- instruction word
      rs1_i    => rs1_i,             -- register source 1
      rs2_i    => rs2_i,             -- register source 2
      -- response --
      result_o => cfu_res,           -- operation result
      valid_o  => cfu_done           -- result valid, operation done
    );

    -- CFU instruction word --
    cfu_inst <= ctrl_i.ir_funct12 & ctrl_i.rf_rs1 & ctrl_i.ir_funct3 & ctrl_i.rf_rd &
                opcode_cust0_c(6) & ctrl_i.ir_opcode(5 downto 4) & opcode_cust0_c(3 downto 0); -- constrained OPCODE

    -- response proxy --
    cfu_proxy: process(rstn_i, clk_i)
    begin
      if (rstn_i = '0') then
        cfu_busy     <= '0';
        cp_result(4) <= (others => '0');
      elsif rising_edge(clk_i) then
        if (cfu_done = '1') or (ctrl_i.cpu_trap = '1') then -- terminate also on trap
          cfu_busy <= '0';
        elsif (ctrl_i.alu_cp_cfu = '1') then
          cfu_busy <= '1';
        end if;
        if (cp_valid(4) = '1') then
          cp_result(4) <= cfu_res;
        else -- output zero if there is no CFU operation
          cp_result(4) <= (others => '0');
        end if;
      end if;
    end process cfu_proxy;
    cp_valid(4) <= cfu_done and (ctrl_i.alu_cp_cfu or cfu_busy);
  end generate;

  neorv32_cpu_alu_cfu_disabled:
  if not RISCV_ISA_Xcfu generate
    cfu_inst     <= (others => '0');
    cfu_res      <= (others => '0');
    cfu_done     <= '0';
    cfu_busy     <= '0';
    cp_result(4) <= (others => '0');
    cp_valid(4)  <= '0';
  end generate;

  -- ALU-Opcode Co-Processor: Conditional Operations Unit (Zicond ISA Extension) ------------
  -- -------------------------------------------------------------------------------------------
  neorv32_cpu_alu_cond_enabled:
  if RISCV_ISA_Zicond generate
    neorv32_cpu_alu_cond_inst: entity neorv32.neorv32_cpu_alu_cond
    port map (
      -- global control --
      clk_i   => clk_i,        -- global clock, rising edge
      rstn_i  => rstn_i,       -- global reset, low-active, async
      ctrl_i  => ctrl_i,       -- main control bus
      -- data input --
      rs1_i   => rs1_i,        -- register source 1
      rs2_i   => rs2_i,        -- register source 2
      -- result and status --
      res_o   => cp_result(5), -- operation result
      valid_o => cp_valid(5)   -- data output valid
    );
  end generate;

  neorv32_cpu_alu_cond_disabled:
  if not RISCV_ISA_Zicond generate
    cp_result(5) <= (others => '0');
    cp_valid(5)  <= '0';
  end generate;

  -- ALU[I]-Opcode Co-Processor: Scalar Cryptography Unit (Zk* ISA Extensions) --------------
  -- -------------------------------------------------------------------------------------------
  neorv32_cpu_alu_crypto_enabled:
  if RISCV_ISA_Zknd or RISCV_ISA_Zkne or RISCV_ISA_Zknh or RISCV_ISA_Zksed or RISCV_ISA_Zksh generate
    neorv32_cpu_alu_crypto_inst: entity neorv32.neorv32_cpu_alu_crypto
    generic map (
      EN_ZKND  => RISCV_ISA_Zknd,  -- NIST AES decryption extension
      EN_ZKNE  => RISCV_ISA_Zkne,  -- NIST AES encryption extension
      EN_ZKNH  => RISCV_ISA_Zknh,  -- NIST hash extension
      EN_ZKSED => RISCV_ISA_Zksed, -- ShangMi block cipher extension
      EN_ZKSH  => RISCV_ISA_Zksh   -- ShangMi hash extension
    )
    port map (
      -- global control --
      clk_i   => clk_i,        -- global clock, rising edge
      rstn_i  => rstn_i,       -- global reset, low-active, async
      ctrl_i  => ctrl_i,       -- main control bus
      -- data input --
      rs1_i   => rs1_i,        -- register source 1
      rs2_i   => rs2_i,        -- register source 2
      -- result and status --
      res_o   => cp_result(6), -- operation result
      valid_o => cp_valid(6)   -- data output valid
    );
  end generate;

  neorv32_cpu_alu_crypto_disabled:
  if not (RISCV_ISA_Zknd or RISCV_ISA_Zkne or RISCV_ISA_Zknh or RISCV_ISA_Zksed or RISCV_ISA_Zksh) generate
    cp_result(6) <= (others => '0');
    cp_valid(6)  <= '0';
  end generate;

end neorv32_cpu_alu_rtl;
