--------------------------------------------------------------------------------------------------------------
--  PC -- (Program Counters and hart interleavers)                                                          --
--  Author(s): Abdallah Cheikh abdallah.cheikh@uniroma1.it (abdallah93.as@gmail.com)                        --
--                                                                                --
--  Date Modified: 17-11-2019                                                                               --
--------------------------------------------------------------------------------------------------------------
--  Program Counter Managing Units -- synchronous process, sinle cycle.                                     --
--  Note: in the present version, gives priority to branching over trapping, except LSU and DSP traps       --
--  i.e. branch instructions are not interruptible. This can be changed but may be unsafe.                  --
--  Implements as many PC units as the  number of harts supported                                           --
--  This entity also implements the hardware context counters that interleve the harts in the core.         --
--------------------------------------------------------------------------------------------------------------

-- ieee packages ------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_misc.all;
use ieee.numeric_std.all;
use std.textio.all;

-- local packages ------------
use work.riscv_klessydra.all;

entity Program_Counter is
  generic (
    THREAD_POOL_SIZE      : integer;
    ACCL_NUM              : natural
  );
  port (
    absolute_jump                     : in  std_logic;
    data_we_o_lat                     : in  std_logic;
    PC_offset                         : in  array_2D(THREAD_POOL_SIZE - 1 downto 0)(31 downto 0);
    taken_branch                      : in  std_logic;
    ie_taken_branch                   : in  std_logic;
    ls_taken_branch                   : in  std_logic;
    dsp_taken_branch                  : in  std_logic_vector(ACCL_NUM - 1 downto 0);
    set_branch_condition              : in  std_logic;
    ie_except_condition               : in  std_logic;
    ls_except_condition               : in  std_logic;
    dsp_except_condition              : in  std_logic_vector(ACCL_NUM - 1 downto 0);
    set_except_condition              : in  std_logic;
    set_mret_condition                : in  std_logic;
    set_wfi_condition                 : in  std_logic;
    harc_ID                           : in  integer range THREAD_POOL_SIZE - 1 downto 0;
    harc_EXEC                         : in  integer range THREAD_POOL_SIZE - 1 downto 0;
    instr_rvalid_IE                   : in  std_logic;
    pc_IE                             : in  std_logic_vector(31 downto 0);
    MSTATUS                           : in  array_2d(THREAD_POOL_SIZE - 1 downto 0)(1 downto 0);
    MIP, MEPC, MCAUSE, MTVEC          : in  array_2D(THREAD_POOL_SIZE - 1 downto 0)(31 downto 0);
    instr_word_IE                     : in  std_logic_vector(31 downto 0);
    reset_state                       : in  std_logic;
    pc_IF                             : out std_logic_vector(31 downto 0);
    harc_IF                           : out integer range THREAD_POOL_SIZE - 1 downto 0;
    served_ie_except_condition        : out std_logic_vector(THREAD_POOL_SIZE - 1 downto 0);
    served_ls_except_condition        : out std_logic_vector(THREAD_POOL_SIZE - 1 downto 0);
    served_dsp_except_condition       : out std_logic_vector(THREAD_POOL_SIZE - 1 downto 0);
    served_except_condition           : out std_logic_vector(THREAD_POOL_SIZE - 1 downto 0);
    served_mret_condition             : out std_logic_vector(THREAD_POOL_SIZE - 1 downto 0);
    served_irq                        : in  std_logic_vector(THREAD_POOL_SIZE - 1 downto 0);
    taken_branch_pending              : out std_logic_vector(THREAD_POOL_SIZE - 1 downto 0);
    taken_branch_pc_lat               : out array_2D(THREAD_POOL_SIZE - 1 downto 0)(31 downto 0);
    incremented_pc                    : out array_2D(THREAD_POOL_SIZE - 1 downto 0)(31 downto 0);

    boot_addr_i                       : in  std_logic_vector(31 downto 0);
    instr_gnt_i                       : in  std_logic
    );
end entity;

architecture PC of Program_counter is

  subtype harc_range is integer range THREAD_POOL_SIZE - 1 downto 0;
  subtype accl_range is integer range ACCL_NUM - 1 downto 0;

  -- pc updater signals
  signal pc_update_enable                  : std_logic_vector(harc_range);
  signal taken_branch_replicated           : std_logic_vector(harc_range);
  signal set_branch_condition_replicated   : std_logic_vector(harc_range);
  signal set_wfi_condition_replicated      : std_logic_vector(harc_range);
  signal ls_except_condition_replicated    : std_logic_vector(harc_range);
  signal ie_except_condition_replicated    : std_logic_vector(harc_range);
  signal dsp_except_condition_replicated   : std_logic_vector(harc_range);
  signal set_except_condition_replicated   : std_logic_vector(harc_range);
  signal set_mret_condition_replicated     : std_logic_vector(harc_range);
  signal relative_to_PC                    : array_2D(harc_range)(31 downto 0);
  signal pc                                : array_2D(harc_range)(31 downto 0);
  signal boot_pc                           : std_logic_vector(31 downto 0);
  signal harc_IF_internal                  : harc_range;
  signal mret_condition_pending_internal   : std_logic_vector(harc_range);
  signal mepc_incremented_pc_internal      : array_2D(harc_range)(31 downto 0);
  signal incremented_pc_internal           : array_2D(harc_range)(31 downto 0);
  signal mepc_interrupt_pc_internal        : array_2D(harc_range)(31 downto 0);
  signal taken_branch_pc_lat_internal      : array_2D(harc_range)(31 downto 0);
  signal taken_branch_pc_pending_internal  : array_2D(harc_range)(31 downto 0);
  signal taken_branch_pending_internal     : std_logic_vector(harc_range);
  signal irq_pending_internal              : std_logic_vector(harc_range);

  ------------------------------------------------------------------------------------------------------------
  -- Subroutine implementing pc updating combinational logic, that is replicated for the threads supported  --
  ------------------------------------------------------------------------------------------------------------
  procedure pc_update(
    signal MTVEC                         : in    std_logic_vector(31 downto 0);
    signal instr_gnt_i, taken_branch     : in    std_logic;
    signal set_wfi_condition             : in    std_logic;
    signal taken_branch_pending          : inout std_logic;
    signal irq_pending                   : in    std_logic;
    signal ie_except_condition           : in    std_logic;
    signal ls_except_condition           : in    std_logic;
    signal dsp_except_condition          : in    std_logic;
    signal set_except_condition          : in    std_logic;
    signal set_mret_condition            : in    std_logic;
    signal pc                            : inout std_logic_vector(31 downto 0);
    signal taken_branch_pc_lat           : in    std_logic_vector(31 downto 0);
    signal taken_branch_pc_pending       : in    std_logic_vector(31 downto 0);
    signal incremented_pc                : in    std_logic_vector(31 downto 0);
    signal boot_pc                       : in    std_logic_vector(31 downto 0);
    signal pc_update_enable              : in    std_logic;
    signal served_ie_except_condition    : out   std_logic;
    signal served_ls_except_condition    : out   std_logic;
    signal served_dsp_except_condition   : out   std_logic;
    signal served_except_condition       : out   std_logic;
    signal served_mret_condition         : out   std_logic) is
  begin
    if pc_update_enable = '1' then

      -- interrupt service launched in the previous instr. cycle
      -- this is done for a second instr. cycle for proper synchronization of flushing
      -- nothing pending
      if not taken_branch = '1' and not taken_branch_pending = '1' then
        pc                      <= incremented_pc;
        served_except_condition <= '0';
        served_ie_except_condition  <= '0';
        served_ls_except_condition  <= '0';
        served_dsp_except_condition <= '0';
        served_mret_condition   <= '0';
      -- taken_branch pending
      elsif taken_branch = '1' then
        pc                      <= taken_branch_pc_lat;
        taken_branch_pending    <= '0';
        served_ie_except_condition  <= '1' when ie_except_condition  = '1' else '0'; -- for CS units;
        served_ls_except_condition  <= '1' when ls_except_condition  = '1' else '0'; -- for CS units;
        served_dsp_except_condition <= '1' when dsp_except_condition = '1' else '0'; -- for CS units;
        served_except_condition     <= '1' when set_except_condition = '1' else '0'; -- for CS units;
        served_mret_condition       <= '1' when set_mret_condition   = '1' else '0'; -- for CS units;
      elsif  taken_branch_pending = '1' then
        pc                      <= taken_branch_pc_pending;
        taken_branch_pending    <= '0';
        served_ie_except_condition  <= '1' when ie_except_condition  = '1' else '0'; -- for CS units;
        served_ls_except_condition  <= '1' when ls_except_condition  = '1' else '0'; -- for CS units;
        served_dsp_except_condition <= '1' when dsp_except_condition = '1' else '0'; -- for CS units;
        served_except_condition     <= '1' when set_except_condition = '1' else '0'; -- for CS units;
        served_mret_condition       <= '1' when set_mret_condition   = '1' else '0'; -- for CS units;
      else
        pc <= boot_pc;                  -- default, should never occur
      end if;
      -- end of pc value update ---
    else                                -- sets registers to record pending requests
      served_except_condition <= '0';
      served_mret_condition   <= '0';
      if taken_branch = '1' then
        taken_branch_pending <= '1';
      end if;
      if set_except_condition = '1' then
        served_except_condition <= '1';
      end if;
      if dsp_except_condition = '1' then
        served_dsp_except_condition <= '1';
      elsif ls_except_condition = '1' then
        served_ls_except_condition <= '1';
      elsif ie_except_condition = '1' then
        served_ie_except_condition <= '1';
      end if;
      if set_mret_condition = '1' then
        served_mret_condition <= '1';
      end if;
    end if;
  end pc_update;
  --------------------------------------------------------------------------------------

begin

  harc_IF                  <= harc_IF_internal;
  mepc_incremented_pc      <= mepc_incremented_pc_internal;
  mepc_interrupt_pc        <= mepc_interrupt_pc_internal;
  taken_branch_pc_lat      <= taken_branch_pc_lat_internal;
  incremented_pc           <= incremented_pc_internal;
  taken_branch_pending     <= taken_branch_pending_internal;
  irq_pending              <= irq_pending_internal;

  hardware_context_counter : process(all)
  begin
    if rst_ni = '0' then
      harc_IF_internal <= THREAD_POOL_SIZE -1;
    elsif rising_edge(clk_i) then
      if instr_gnt_i = '1' then
        harc_IF_internal <= harc_IF_internal - 1 when harc_IF_internal > 0 else THREAD_POOL_SIZE -1;
      end if;
    end if;
  end process hardware_context_counter;

  -- this is the multiplexer on the PC_IF
  pc_IF <= pc(harc_IF_internal);

  -- fixed connections, not replicated
  boot_pc                                 <= boot_addr_i(31 downto 8) & std_logic_vector(to_unsigned(128, 8));
  ----------------------------------------------------------------------------------------------
  -- this part of logic and registers is replicated as many times as the supported threads:   --
  pc_update_logic : for h in harc_range generate

    mepc_incremented_pc_internal(h) <= MEPC(h);
    mepc_interrupt_pc_internal(h)   <= MEPC(h) when MCAUSE(h)(30) = '0' else std_logic_vector(unsigned(MEPC(h)) + 4);  -- MCAUSE(30) = '0' indicates that we weren't executing a WFI instruction

    relative_to_PC(h) <= std_logic_vector(to_unsigned(0, 32)) when (absolute_jump = '1')
                         else pc_IE;
    incremented_pc_internal(h) <= std_logic_vector(unsigned(pc(h))+4);
    irq_pending_internal(h)    <= ((MIP(h)(11) or MIP(h)(7) or MIP(h)(3)) and MSTATUS(h)(0));

    set_wfi_condition_replicated(h) <= '1' when set_wfi_condition = '1' and (harc_EXEC = h)
                                  else '0';
    taken_branch_replicated(h) <=      '1' when dsp_taken_branch /= (accl_range => '0') and (harc_EXEC = h)
	                              else '1' when ls_taken_branch  = '1' and (harc_EXEC = h)
	                              else '1' when ie_taken_branch  = '1' and (harc_EXEC = h)
                                  else '0';
    set_branch_condition_replicated(h) <= '1' when set_branch_condition = '1' and (harc_EXEC = h)
                                     else '0';
    dsp_except_condition_replicated(h) <= '1' when dsp_except_condition  /= (accl_range => '0') and (harc_EXEC  = h)
                                     else '0';
    ls_except_condition_replicated(h)  <= '1' when ls_except_condition   = '1' and (harc_EXEC   = h)
                                     else '0';
    ie_except_condition_replicated(h)  <= '1' when ie_except_condition  = '1' and (harc_EXEC = h)
                                     else '0';
    set_except_condition_replicated(h) <= '1' when dsp_except_condition_replicated(h)  = '1' or ls_except_condition_replicated(h) = '1' or ie_except_condition_replicated(h) = '1'
                                     else '0';
    set_mret_condition_replicated(h)   <= '1' when set_mret_condition = '1' and (harc_EXEC = h)
                                     else '0';

    -- latch on the branch address, possibly useless but may be needed in future situations, served_irq has the highest priority, interrupt request are checked before executing any instructions in the IE_Stage

    taken_branch_pc_lat_internal(h) <=
      MTVEC(h)                                                         when dsp_except_condition_replicated(h) = '1'                         else  -- sets MTVEC address for exception trap
      MTVEC(h)                                                         when ls_except_condition_replicated(h)  = '1'                         else  -- sets MTVEC address for exception trap
      std_logic_vector(signed(relative_to_PC(h))+signed(PC_offset(h))) when set_branch_condition_replicated(h) = '1'                         else  -- sets a jump or a branch address
      std_logic_vector(signed(relative_to_PC(h)))                      when set_wfi_condition_replicated(h)    = '1'                         else  -- sets a wfi address (spin lock)
      MTVEC(h)                                                         when ie_except_condition_replicated(h)  = '1'                         else  -- sets MTVEC address for exception trap
      mepc_incremented_pc_internal(h)                                  when set_mret_condition_replicated(h)   = '1' and MCAUSE(h)(31) = '0' else  -- sets return address from exception subroutine
      mepc_interrupt_pc_internal(h)                                    when set_mret_condition_replicated(h)   = '1' and MCAUSE(h)(31) = '1' else  -- sets return address from interrupt subroutine
      MTVEC(h)                                                         when served_irq(h)                                                    else  -- sets MTVEC address for exception trap,
     (others => '0');

    pc_update_enable(h) <= '1' when instr_gnt_i = '1'
                           and (harc_IF_internal = h
                                or taken_branch_replicated(h) = '1'
                                or set_wfi_condition_replicated(h) = '1'
                                or taken_branch_pending_internal(h) = '1')
                           else '0';

    pc_updater : process(clk_i, rst_ni, boot_pc)
    begin
      if rst_ni = '0' then
        pc(h)                               <= (others => '0');  -- better to put 0 to ensure clear synthesis
        taken_branch_pc_pending_internal(h) <= (others => '0');
        taken_branch_pending_internal(h)    <= '0';
        served_ie_except_condition(h)       <= '0';
        served_ls_except_condition(h)       <= '0';
        served_dsp_except_condition(h)      <= '0';
        served_except_condition(h)          <= '0';
        served_mret_condition(h)            <= '0';
      elsif rising_edge(clk_i) then
        -- synch.ly updates pc with new value depending on conditions pending
        -- synch.ly raises "served" signal for the condition that is being served
        -- synch.ly lowers "served" signal for other conditions
        if taken_branch_replicated(h) = '1' then
          taken_branch_pc_pending_internal(h) <= taken_branch_pc_lat_internal(h);
        end if;
        if reset_state = '1' then
          pc(h) <= boot_pc;
        else
          pc_update(MTVEC(h), instr_gnt_i, taken_branch_replicated(h), set_wfi_condition_replicated(h), taken_branch_pending_internal(h),
                    irq_pending_internal(h),ie_except_condition_replicated(h), ls_except_condition_replicated(h), dsp_except_condition_replicated(h),
					set_except_condition_replicated(h), set_mret_condition_replicated(h), pc(h), taken_branch_pc_lat_internal(h), taken_branch_pc_pending_internal(h),
					incremented_pc_internal(h), boot_pc, pc_update_enable(h), served_ie_except_condition(h), served_ls_except_condition(h),
					served_dsp_except_condition(h), served_except_condition(h),
                    served_mret_condition(h));
        end if;
      end if;  --rst , clk
    end process;

  end generate pc_update_logic;
  -- end of replicated logic --

--------------------------------------------------------------------- end of PC Managing Units ---
--------------------------------------------------------------------------------------------------

end PC;
--------------------------------------------------------------------------------------------------
-- END of Program Counter architecture -----------------------------------------------------------
--------------------------------------------------------------------------------------------------

    mepc_incremented_pc               : out array_2D(THREAD_POOL_SIZE - 1 downto 0)(31 downto 0);
    mepc_interrupt_pc                 : out array_2D(THREAD_POOL_SIZE - 1 downto 0)(31 downto 0);
    irq_pending                       : out std_logic_vector(THREAD_POOL_SIZE - 1 downto 0);
    clk_i                             : in  std_logic;


----------------------------------------------------------------------------------------------------------------
--  Stage ID - (Instruction decode and registerfile read)                                                     --
--  Author(s): Abdallah Cheikh abdallah.cheikh@uniroma1.it (abdallah93.as@gmail.com)                          --
--                                                                                --
--  Date Modified: 07-04-2020                                                                                --
----------------------------------------------------------------------------------------------------------------
--  Registerfiles of the incoming hart are read in this stage in parallel with the decoding                   --
--  Two types of registerfiles can be generaated for XILINX FPGAs LUTAM based or FF based dpeneding on the    --
--  setting of the generic variaabble chosen                                                                  --
--  The scratchpad memory mapper also exists in this stage, which maps the address to the corresponding SPM   --
--  This pipeline stage always takes one cycle latency                                                        --
----------------------------------------------------------------------------------------------------------------

-- ieee packages ------------
library ieee;
use ieee.math_real.all;
use ieee.std_logic_1164.all;
use ieee.std_logic_misc.all;
use ieee.numeric_std.all;
use std.textio.all;

-- local packages ------------
use work.riscv_klessydra.all;
--use work.klessydra_parameters.all;

-- pipeline  pinout --------------------
entity REGISTERFILE is
  generic(
    THREAD_POOL_SIZE           : integer;
    LUTRAM_RF                  : natural;
    accl_en                    : natural;
    SPM_NUM		               : natural;
    Addr_Width                 : natural;
    SPM_STRT_ADDR              : std_logic_vector(31 downto 0);
    RF_SIZE                    : natural;
    RF_CEIL                    : natural;
    SPM_ADDR_WID               : natural
    );
  port (
    -- clock, reset active low
    clk_i                      : in  std_logic;
    rst_ni                     : in  std_logic;
	-- Branch Control Signals
	harc_ID                    : in  integer range THREAD_POOL_SIZE-1 downto 0;
    pc_ID                      : in  std_logic_vector(31 downto 0);  -- pc_ID is PC entering ID stage
	core_busy_IE               : in  std_logic;
	core_busy_LS               : in  std_logic;
    ls_parallel_exec           : in  std_logic;
    dsp_parallel_exec          : in  std_logic;
    dsp_to_jump                : in  std_logic;
    instr_rvalid_ID            : in  std_logic;
	instr_word_ID_lat          : in  std_logic_vector(31 downto 0);
	LS_WB_EN                   : in  std_logic;
	IE_WB_EN                   : in  std_logic;
	MUL_WB_EN                  : in  std_logic;
	IE_WB                      : in  std_logic_vector(31 downto 0);

    rs1_to_sc                  : out std_logic_vector(SPM_ADDR_WID-1 downto 0);
    rs2_to_sc                  : out std_logic_vector(SPM_ADDR_WID-1 downto 0);
    rd_to_sc                   : out std_logic_vector(SPM_ADDR_WID-1 downto 0);
    data_addr_internal_IE      : out std_logic_vector(31 downto 0);
	regfile                    : out array_3d(THREAD_POOL_SIZE-1 downto 0)(RF_SIZE-1 downto 0)(31 downto 0)
    );
end entity;  ------------------------------------------

-- Klessydra T03x (4 stages) pipeline implementation -----------------------
architecture RF of REGISTERFILE is

  subtype harc_range is integer range THREAD_POOL_SIZE - 1 downto 0;

  signal regfile_lutram_rs1  : array_2d((THREAD_POOL_SIZE*RF_SIZE)-1 downto 0)(31 downto 0);
  signal regfile_lutram_rs2  : array_2d((THREAD_POOL_SIZE*RF_SIZE)-1 downto 0)(31 downto 0);
  signal regfile_lutram_rd   : array_2d((THREAD_POOL_SIZE*RF_SIZE)-1 downto 0)(31 downto 0);

  attribute ram_style : string;
  attribute ram_style of RS1_Data_IE        : signal is "reg";
  attribute ram_style of RS2_Data_IE        : signal is "reg";
  attribute ram_style of RD_Data_IE         : signal is "reg";
  attribute ram_style of regfile_lutram_rs1 : signal is "distributed";
  attribute ram_style of regfile_lutram_rs2 : signal is "distributed";
  attribute ram_style of regfile_lutram_rd  : signal is "distributed";

  signal ID_rs1_to_sc           : std_logic_vector(SPM_ADDR_WID-1 downto 0);
  signal ID_rs2_to_sc           : std_logic_vector(SPM_ADDR_WID-1 downto 0);
  signal ID_rd_to_sc            : std_logic_vector(SPM_ADDR_WID-1 downto 0);

  -- instruction operands
  signal RS1_Addr_IE            : std_logic_vector(4 downto 0);   -- debugging signals
  signal RS2_Addr_IE            : std_logic_vector(4 downto 0);   -- debugging signals
  signal RD_Addr_IE             : std_logic_vector(4 downto 0);   -- debugging signals
  signal RD_EN                  : std_logic;
  signal WB_RD                  : std_logic_vector(31 downto 0);
  signal WB_EN                  : std_logic;
  signal harc_WB                : harc_range;
  signal instr_word_WB          : std_logic_vector(31 downto 0);

  function rs1 (signal instr : in std_logic_vector(31 downto 0)) return integer is
  begin
    return to_integer(unsigned(instr(15+(RF_CEIL-1) downto 15)));
  end;

  function rs2 (signal instr : in std_logic_vector(31 downto 0)) return integer is
  begin
    return to_integer(unsigned(instr(20+(RF_CEIL-1) downto 20)));
  end;

  function rd (signal instr : in std_logic_vector(31 downto 0)) return integer is
  begin
    return to_integer(unsigned(instr(7+(RF_CEIL-1) downto 7)));
  end;

begin

  RF_FF : if LUTRAM_RF = 0 generate

  RF_ACCESS : process(clk_i, rst_ni, instr_word_ID_lat)  -- synch single state process
  begin
    if rst_ni = '0' then
      for h in harc_range loop
        regfile(h)(0) <= (others => '0');
      end loop;
    elsif rising_edge(clk_i) then
      if core_busy_IE = '1' or core_busy_LS = '1' or ls_parallel_exec = '0'  or dsp_parallel_exec = '0' then -- the instruction pipeline is halted
      elsif instr_rvalid_ID = '0' then -- wait for a valid instruction
      else  -- process the incoming instruction

        data_addr_internal_IE <= std_logic_vector(signed(regfile(harc_ID)(rs1(instr_word_ID_lat))) + signed(S_immediate(instr_word_ID_lat)));

        ------------------------------------------------------------
        --  ██████╗ ███████╗ ██████╗ ███████╗██╗██╗     ███████╗  --
        --  ██╔══██╗██╔════╝██╔════╝ ██╔════╝██║██║     ██╔════╝  --
        --  ██████╔╝█████╗  ██║  ███╗█████╗  ██║██║     █████╗    --
        --  ██╔══██╗██╔══╝  ██║   ██║██╔══╝  ██║██║     ██╔══╝    --
        --  ██║  ██║███████╗╚██████╔╝██║     ██║███████╗███████╗  --
        --  ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═╝     ╚═╝╚══════╝╚══════╝  --
        ------------------------------------------------------------

        ----- REGISTERFILE READ IS DONE HERE --------------------------------------------------------------------------------------------------------------
        RS1_Data_IE <= regfile(harc_ID)(rs1(instr_word_ID_lat));
        RS2_Data_IE <= regfile(harc_ID)(rs2(instr_word_ID_lat));
        if accl_en = 1 then
          if dsp_to_jump = '0' then
            RD_Data_IE <= regfile(harc_ID)(rd(instr_word_ID_lat)); -- only the DSP unit reads the accelerator
          else
            RD_Data_IE <= regfile(harc_ID)(0);
          end if;
        end if;
       -- pragma translate_off
        RD_Data_IE  <= regfile(harc_ID)(rd(instr_word_ID_lat)); -- reading the 'rd' data here is only for debugging purposes if the acclerator is disabled
        RS1_Addr_IE <= std_logic_vector(to_unsigned(rs1(instr_word_ID_lat), 5)); -- debugging signals
        RS2_Addr_IE <= std_logic_vector(to_unsigned(rs2(instr_word_ID_lat), 5)); -- debugging signals
        RD_Addr_IE  <= std_logic_vector(to_unsigned(rd(instr_word_ID_lat), 5)); -- debugging signals
       -- pragma translate_on
       ----------------------------------------------------------------------------------------------------------------------------------------------------
      end if;  -- instr. conditions

      if WB_EN = '1' then
        regfile(harc_WB)(rd(instr_word_WB)) <= WB_RD;
      end if;
    end if;  -- clk
  end process;

  end generate;

  RF_LUTRAM : if LUTRAM_RF = 1 generate

  RF_RD_EN : process(all)  -- synch single state process
  begin
    RD_EN <= '0';
    if core_busy_IE = '1' or core_busy_LS = '1' or ls_parallel_exec = '0'  or dsp_parallel_exec = '0' then -- the instruction pipeline is halted
    elsif instr_rvalid_ID = '0' then -- wait for a valid instruction
    else  -- process the incoming instruction
      RD_EN <= '1';
    end if;  -- instr. conditions
  end process;

  RS1_ACCESS : process(clk_i)  -- synch single state process
  begin
    if rising_edge(clk_i) then
      if RD_EN = '1' then
        data_addr_internal_IE <= std_logic_vector(signed(regfile_lutram_rs1(32*harc_ID+rs1(instr_word_ID_lat))) + signed(S_immediate(instr_word_ID_lat)));
        if rs1(instr_word_ID_lat) /= 0 then
          RS1_Data_IE <= regfile_lutram_rs1(32*harc_ID+rs1(instr_word_ID_lat));
        else
          RS1_Data_IE <= (others => '0');
        end if;
      -- pragma translate_off
       RD_Data_IE  <= regfile_lutram_rs1(32*harc_ID+rd(instr_word_ID_lat)); -- reading the 'rd' data here is only for debugging purposes when the acclerator is disabled
       RS1_Addr_IE <= std_logic_vector(to_unsigned(rs1(instr_word_ID_lat), 5)); -- debugging signals
       RS2_Addr_IE <= std_logic_vector(to_unsigned(rs2(instr_word_ID_lat), 5)); -- debugging signals
       RD_Addr_IE  <= std_logic_vector(to_unsigned(rd(instr_word_ID_lat), 5)); -- debugging signals
      -- pragma translate_on
      end if;  -- instr. conditions
      if WB_EN = '1' then
        regfile_lutram_rs1(32*harc_WB+rd(instr_word_WB)) <= WB_RD;
      end if;
    end if;  -- clk
  end process;

  RS2_ACCESS : process(clk_i)  -- synch single state process
  begin
    if rising_edge(clk_i) then
      if RD_EN = '1' then
        if rs2(instr_word_ID_lat) /= 0 then
          RS2_Data_IE <= regfile_lutram_rs2(32*harc_ID+rs2(instr_word_ID_lat));
        else
          RS2_Data_IE <= (others => '0');
        end if;
      end if;  -- instr. conditions
      if WB_EN = '1' then
        regfile_lutram_rs2(32*harc_WB+rd(instr_word_WB)) <= WB_RD;
      end if;
    end if;  -- clk
  end process;

  RD_LUTRAM : if accl_en = 1 generate
  RD_ACCESS : process(clk_i)  -- synch single state process
  begin
    if rising_edge(clk_i) then
      if accl_en = 1 then
        if RD_EN = '1' then
          RD_Data_IE <= regfile_lutram_rd(32*harc_ID+rd(instr_word_ID_lat)); -- only the DSP unit reads the accelerator
        end if;  -- instr. conditions
      end if;
      if WB_EN = '1' then
        regfile_lutram_rd(32*harc_WB+rd(instr_word_WB)) <= WB_RD;
      end if;
    end if;  -- clk
  end process;

  end generate;
  end generate;
-----------------------------------------------------------------------------------------------------
-- Stage WB - (WRITEBACK)
-----------------------------------------------------------------------------------------------------

  harc_WB <= harc_LS_WB when LS_WB_EN = '1' else harc_IE_WB;
  instr_word_WB <= instr_word_LS_WB when LS_WB_EN = '1' else instr_word_IE_WB when (IE_WB_EN = '1' or MUL_WB_EN = '1') else (others => '0');
  WB_EN <= '1' when (LS_WB_EN = '1' or IE_WB_EN = '1' or MUL_WB_EN = '1') else '0';
  WB_RD <= IE_WB when IE_WB_EN = '1' else LS_WB when LS_WB_EN = '1' else MUL_WB when MUL_WB_EN = '1' else (others => '0');

--------------------------------------------------------------------- end of WB Stage ----------------
------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------
--  ███████╗██████╗ ███╗   ███╗    ███╗   ███╗ █████╗ ██████╗ ██████╗ ███████╗██████╗   --
--  ██╔════╝██╔══██╗████╗ ████║    ████╗ ████║██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗  --
--  ███████╗██████╔╝██╔████╔██║    ██╔████╔██║███████║██████╔╝██████╔╝█████╗  ██████╔╝  --
--  ╚════██║██╔═══╝ ██║╚██╔╝██║    ██║╚██╔╝██║██╔══██║██╔═══╝ ██╔═══╝ ██╔══╝  ██╔══██╗  --
--  ███████║██║     ██║ ╚═╝ ██║    ██║ ╚═╝ ██║██║  ██║██║     ██║     ███████╗██║  ██║  --
--  ╚══════╝╚═╝     ╚═╝     ╚═╝    ╚═╝     ╚═╝╚═╝  ╚═╝╚═╝     ╚═╝     ╚══════╝╚═╝  ╚═╝  --
------------------------------------------------------------------------------------------

  spm_mapper : if accl_en = 1 generate
  RF_FF : if LUTRAM_RF = 0 generate
  Spm_Addr_Mapping : process(all)
  begin
  ID_rs1_to_sc <= std_logic_vector(to_unsigned(SPM_NUM, SPM_ADDR_WID));  -- we assign SPM_NUM to rs1_to_sc as a default case which is out of range (0 to SPM_NUM-1)
  ID_rs2_to_sc <= std_logic_vector(to_unsigned(SPM_NUM, SPM_ADDR_WID));  -- we assign SPM_NUM to rs2_to_sc as a default case which is out of range (0 to SPM_NUM-1)
  ID_rd_to_sc  <= std_logic_vector(to_unsigned(SPM_NUM, SPM_ADDR_WID));  -- we assign SPM_NUM to rd_to_sc  as a default case which is out of range (0 to SPM_NUM-1)
    for i in 0 to SPM_NUM-1 loop -- Decode the address and assign and set the scratchpad number (0 to SPM_NUM-1) to the operand
      if regfile(harc_ID)(rs1(instr_word_ID_lat))(31 downto Addr_Width) >= std_logic_vector(unsigned(SPM_STRT_ADDR(31 downto Addr_Width)) + (i)) and
         regfile(harc_ID)(rs1(instr_word_ID_lat))(31 downto Addr_Width) <  std_logic_vector(unsigned(SPM_STRT_ADDR(31 downto Addr_Width)) + (i+1)) then
        ID_rs1_to_sc <= std_logic_vector(to_unsigned(i, SPM_ADDR_WID));
      end if;
      if regfile(harc_ID)(rs2(instr_word_ID_lat))(31 downto Addr_Width) >= std_logic_vector(unsigned(SPM_STRT_ADDR(31 downto Addr_Width)) + (i)) and
         regfile(harc_ID)(rs2(instr_word_ID_lat))(31 downto Addr_Width) <  std_logic_vector(unsigned(SPM_STRT_ADDR(31 downto Addr_Width)) + (i+1)) then
        ID_rs2_to_sc <= std_logic_vector(to_unsigned(i, SPM_ADDR_WID));
      end if;
      if regfile(harc_ID)(rd(instr_word_ID_lat))(31 downto Addr_Width)  >= std_logic_vector(unsigned(SPM_STRT_ADDR(31 downto Addr_Width)) + (i)) and
         regfile(harc_ID)(rd(instr_word_ID_lat))(31 downto Addr_Width)  <  std_logic_vector(unsigned(SPM_STRT_ADDR(31 downto Addr_Width)) + (i+1)) then
        ID_rd_to_sc <= std_logic_vector(to_unsigned(i, SPM_ADDR_WID));
      end if;
    end loop;
  end process;
  end generate;

  RF_LUTRAM : if LUTRAM_RF = 1 generate
  Spm_Addr_Mapping : process(all)
  begin
  ID_rs1_to_sc <= std_logic_vector(to_unsigned(SPM_NUM, SPM_ADDR_WID));  -- we assign SPM_NUM to rs1_to_sc as a default case which is out of range (0 to SPM_NUM-1)
  ID_rs2_to_sc <= std_logic_vector(to_unsigned(SPM_NUM, SPM_ADDR_WID));  -- we assign SPM_NUM to rs2_to_sc as a default case which is out of range (0 to SPM_NUM-1)
  ID_rd_to_sc  <= std_logic_vector(to_unsigned(SPM_NUM, SPM_ADDR_WID));  -- we assign SPM_NUM to rd_to_sc  as a default case which is out of range (0 to SPM_NUM-1)
    for i in 0 to SPM_NUM-1 loop -- Decode the address and assign and set the scratchpad number (0 to SPM_NUM-1) to the operand
      if regfile_lutram_rs1(32*harc_ID+rs1(instr_word_ID_lat))(31 downto Addr_Width) >= std_logic_vector(unsigned(SPM_STRT_ADDR(31 downto Addr_Width)) + (i)) and
         regfile_lutram_rs1(32*harc_ID+rs1(instr_word_ID_lat))(31 downto Addr_Width) <  std_logic_vector(unsigned(SPM_STRT_ADDR(31 downto Addr_Width)) + (i+1)) then
        ID_rs1_to_sc <= std_logic_vector(to_unsigned(i, SPM_ADDR_WID));
      end if;
      if regfile_lutram_rs2(32*harc_ID+rs2(instr_word_ID_lat))(31 downto Addr_Width) >= std_logic_vector(unsigned(SPM_STRT_ADDR(31 downto Addr_Width)) + (i)) and
         regfile_lutram_rs2(32*harc_ID+rs2(instr_word_ID_lat))(31 downto Addr_Width) <  std_logic_vector(unsigned(SPM_STRT_ADDR(31 downto Addr_Width)) + (i+1)) then
        ID_rs2_to_sc <= std_logic_vector(to_unsigned(i, SPM_ADDR_WID));
      end if;
      if regfile_lutram_rd(32*harc_ID+rd(instr_word_ID_lat))(31 downto Addr_Width)  >= std_logic_vector(unsigned(SPM_STRT_ADDR(31 downto Addr_Width)) + (i)) and
         regfile_lutram_rd(32*harc_ID+rd(instr_word_ID_lat))(31 downto Addr_Width)  <  std_logic_vector(unsigned(SPM_STRT_ADDR(31 downto Addr_Width)) + (i+1)) then
        ID_rd_to_sc <= std_logic_vector(to_unsigned(i, SPM_ADDR_WID));
      end if;
    end loop;
  end process;
  end generate;

  Spm_Addr_Mapping_Synch : process(clk_i, rst_ni)
  begin
    if rst_ni = '0' then
    elsif rising_edge(clk_i) then
      rs1_to_sc <= ID_rs1_to_sc;
      rs2_to_sc <= ID_rs2_to_sc;
      rd_to_sc  <= ID_rd_to_sc;
    end if;
  end process;

  end generate;

---------------------------------------------------------------------- end of ID stage -----------
--------------------------------------------------------------------------------------------------
end RF;
--------------------------------------------------------------------------------------------------
-- END of ID architecture ------------------------------------------------------------------------
-----------

	MUL_WB                     : in  std_logic_vector(31 downto 0);
	LS_WB                      : in  std_logic_vector(31 downto 0);
	instr_word_LS_WB           : in  std_logic_vector(31 downto 0);
	instr_word_IE_WB           : in  std_logic_vector(31 downto 0);
	harc_LS_WB                 : in  integer range THREAD_POOL_SIZE-1 downto 0;


-----------------------------------------------------------------------------------------------------------------
--  SPM -- (Scratchpad Memories)                                                                               --
--  Author(s): Abdallah Cheikh abdallah.cheikh@uniroma1.it (abdallah93.as@gmail.com)                           --
--                                                                                --
--  Date Modified: 17-11-2019                                                                                --
-----------------------------------------------------------------------------------------------------------------
--  The SPMs are implemented as low latency single cycle read memories. The number of memory banks in this     --
--  entity is decided by many factors. The first being the SIMD size whcih decides the number of banks per     --
--  SPM. The second being whether the hardware accelerator is replicated, whuch will replicate the SPM         --
--  for all the harts in the core. And the last is the number of SPMs configured by the parameter "SPM_NUM".   --
--  Each SPM bank has a read and write port which is 32-bits wide, and the mems are implemented in BRAMs       --
-----------------------------------------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use std.textio.all;

-- local packages ------------
use work.riscv_klessydra.all;
--use work.klessydra_parameters.all;

---------------------------------------------------------------------------------------------------
entity Scratchpad_memory is
  generic(
    SPM_NUM               : natural;

  port(
    clk_i          : in  std_logic;
    sc_we          : in  array_2d(ACCL_NUM - 1 downto 0)(SIMD*SPM_NUM-1 downto 0);
    sc_addr_wr     : in  array_3d(ACCL_NUM - 1 downto 0)(SIMD*SPM_NUM-1 downto 0)(Addr_Width-(SIMD_BITS+3) downto 0);
    sc_addr_rd     : in  array_3d(ACCL_NUM - 1 downto 0)(SIMD*SPM_NUM-1 downto 0)(Addr_Width-(SIMD_BITS+3) downto 0);
    sc_data_wr     : in  array_3d(ACCL_NUM - 1 downto 0)(SIMD*SPM_NUM-1 downto 0)(Data_Width-1 downto 0);
    sc_data_rd     : out array_3d(ACCL_NUM - 1 downto 0)(SIMD*SPM_NUM-1 downto 0)(Data_Width-1 downto 0)
    );
end Scratchpad_memory;

---------------------------------------------------------------------------------------------------
architecture SC of Scratchpad_memory is

subtype accl_range is integer range ACCL_NUM - 1 downto 0;

signal mem : array_3d(ACCL_NUM*SIMD*SPM_NUM-1 downto 0)(2**(Addr_Width-(SIMD_BITS+2))-1 downto 0)(Data_Width-1 downto 0);
signal h   : std_logic_vector(ACCL_NUM*SIMD*SPM_NUM downto 0);
attribute ram_style : string;
attribute ram_style of mem : signal is "block";

begin

 --------- replicate logic three times --------------------------------
  spm_replicas : for g in accl_range generate
  spm_banks    : for h in 0 to SIMD*SPM_NUM -1 generate

    write_logic: process(clk_i) --
    begin
      if (clk_i'event and clk_i='1') then
         sc_data_rd(g)(h) <= mem(g*SIMD*SPM_NUM + h)(to_integer(unsigned(sc_addr_rd(g)(h))));
        if sc_we(g)(h) = '1' then         --write mode
          mem(g*SIMD*SPM_NUM + h)(to_integer(unsigned(sc_addr_wr(g)(h)))) <= sc_data_wr(g)(h);
        end if; -- we
      end if; -- clk
    end process;

  end generate spm_banks;
  end generate spm_replicas;
  -- end of replicated logic --------------------------------------------

--------------------------------------------------------------------- end of SPM Logic -----------
--------------------------------------------------------------------------------------------------

end SC;
--------------------------------------------------------------------------------------------------
-- END of Scratchpad Memory architecture ---------------------------------------------------------
--------------------------------------------------------------------------------------------------

    Addr_Width            : natural;
    SIMD                  : natural;
    --------------------------------
    ACCL_NUM              : natural;


-----------------------------------------------------------------------------------------------------------------
--  SPI -- (Scratchpad Memory Interface)                                                                       --
--  Author(s): Abdallah Cheikh abdallah.cheikh@uniroma1.it (abdallah93.as@gmail.com)                           --
--                                                                                --
--  Date Modified: 02-04-2019                                                                                --
-----------------------------------------------------------------------------------------------------------------
--  The SPI connects the SPM to the LSU and DSP units. The LSU reads and wirtes one word (32-bits) at a time   --
--  and a bank interleaver switches between the internal banks of the SPM. The DSP reads and writes one SPM    --
--  line which is "32*SIMD" bits wide. Hence there is no need for the bank interleaver. However, the DSP       --
--  can perform misaligned reads and writes. So it needs read and write data rotators to allign the source     --
--  operands to the appropriate functional units, and memory banks. The SPI provides 2 rd and 1 wr port to     --
--  the DSP with a two cycle read latency (one for reading the banks, the other is for rotating)               --
-----------------------------------------------------------------------------------------------------------------

-- ieee packages ------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_misc.all;
use ieee.numeric_std.all;
use std.textio.all;

-- local packages ------------
use work.riscv_klessydra.all;
--use work.klessydra_parameters.all;

-- SCI pinout --------------------
entity Scratchpad_memory_interface is
  generic(
    accl_en               : natural;
    SPM_NUM		            : natural;
    Addr_Width            : natural;
    SIMD                  : natural;
    --------------------------------
    ACCL_NUM              : natural;
    SIMD_BITS             : natural;
    Data_Width            : natural;
    SIMD_Width            : natural
  );
  port (
    clk_i, rst_ni              : in  std_logic;
    data_rvalid_i              : in  std_logic;
    state_LS                   : in  fsm_LS_states;
    sc_word_count_wire         : in  integer;
    spm_bcast                  : in  std_logic;
    harc_LS_wire               : in  integer range ACCL_NUM-1 downto 0;
    dsp_we_word                : in  array_2d(ACCL_NUM-1 downto 0)(SIMD-1 downto 0);
    ls_sc_data_write_wire      : in  std_logic_vector(Data_Width-1 downto 0);
    dsp_sc_data_write_wire     : in  array_2d(ACCL_NUM-1 downto 0)(SIMD_Width-1 downto 0);
    ls_sc_read_addr            : in  std_logic_vector(Addr_Width-(SIMD_BITS+3) downto 0);
    ls_sc_write_addr           : in  std_logic_vector(Addr_Width-(SIMD_BITS+3) downto 0);
    dsp_sc_write_addr          : in  array_2d(ACCL_NUM-1 downto 0)(Addr_Width-1 downto 0);
    ls_sci_req                 : in  std_logic_vector(SPM_NUM-1 downto 0);
    ls_sci_we                  : in  std_logic_vector(SPM_NUM-1 downto 0);
    dsp_sci_req                : in  array_2d(ACCL_NUM-1 downto 0)(SPM_NUM-1 downto 0);
    dsp_sci_we                 : in  array_2d(ACCL_NUM-1 downto 0)(SPM_NUM-1 downto 0);
    kmemld_inflight            : in  std_logic_vector(SPM_NUM-1 downto 0);
    kmemstr_inflight           : in  std_logic_vector(SPM_NUM-1 downto 0);
    dsp_to_sc                  : in  array_3d(ACCL_NUM-1 downto 0)(SPM_NUM-1 downto 0)(1 downto 0);
    dsp_sc_read_addr           : in  array_3d(ACCL_NUM-1 downto 0)(1 downto 0)(Addr_Width-1 downto 0);
    dsp_sc_data_read           : out array_3d(ACCL_NUM-1 downto 0)(1 downto 0)(SIMD_Width-1 downto 0);
    ls_sc_data_read_wire       : out std_logic_vector(Data_Width-1 downto 0);
    ls_sci_wr_gnt              : out std_logic;
    dsp_sci_wr_gnt             : out std_logic_vector(ACCL_NUM-1 downto 0);
    ls_data_gnt_i              : out std_logic_vector(SPM_NUM-1 downto 0);
    dsp_data_gnt_i             : out std_logic_vector(ACCL_NUM-1 downto 0)
	);
end entity;  ------------------------------------------

architecture SCI of Scratchpad_memory_interface is

subtype accl_range is integer range ACCL_NUM - 1 downto 0;

signal dsp_sc_data_write_int_wire      : array_2d(accl_range)(SIMD_Width-1 downto 0);
signal ls_sc_data_read_int_wire        : array_2d(accl_range)(Data_Width-1 downto 0);
signal ls_data_gnt_internal            : array_2d(accl_range)(SPM_NUM-1 downto 0);
signal rd_offset                       : array_3d(accl_range)(1 downto 0)(SIMD-1 downto 0);
signal wr_offset                       : array_2d(accl_range)(SIMD-1 downto 0);
signal dsp_sc_data_read_int_wire       : array_3d(accl_range)(1 downto 0)(SIMD_Width-1 downto 0);
signal dsp_sc_read_addr_lat            : array_3d(accl_range)(1 downto 0)(SIMD_BITS+1 downto 0);  --  Only need the lower part to check for the word access
signal dsp_sci_req_lat                 : array_2d(accl_range)(SPM_NUM-1 downto 0);
signal dsp_to_sc_lat                   : array_3d(accl_range)(SPM_NUM-1 downto 0)(1 downto 0);
signal dsp_sc_data_read_wire           : array_3d(accl_range)(1 downto 0)(SIMD_Width-1 downto 0);
signal ls_sc_data_read_replicated      : array_2d(accl_range)(Data_Width-1 downto 0);
signal ls_sc_data_read_wire_replicated : array_2d(accl_range)(Data_Width-1 downto 0);
signal dsp_sci_wr_gnt_lat              : std_logic_vector(accl_range);
signal ls_sci_wr_gnt_replicated        : std_logic_vector(accl_range);
signal ls_sci_wr_gnt_lat_replicated    : std_logic_vector(accl_range);
signal halt_dsp                        : std_logic_vector(accl_range);
signal block_dsp_rd                    : std_logic_vector(accl_range);
signal sc_word_count                   : array_2d_int(accl_range);
signal sc_we                           : array_2d(accl_range)(SIMD*SPM_NUM-1  downto 0);
signal sc_addr_wr                      : array_3d(accl_range)(SIMD*SPM_NUM-1 downto 0)(Addr_Width-(SIMD_BITS+3) downto 0);
signal sc_addr_rd                      : array_3d(accl_range)(SIMD*SPM_NUM-1 downto 0)(Addr_Width-(SIMD_BITS+3) downto 0);
signal sc_data_wr                      : array_3d(accl_range)(SIMD*SPM_NUM-1 downto 0)(Data_Width-1 downto 0);
signal sc_data_rd                      : array_3d(accl_range)(SIMD*SPM_NUM-1 downto 0)(Data_Width-1 downto 0);

component Scratchpad_memory
  generic(
    SPM_NUM               : natural;
    Addr_Width            : natural;
    SIMD                  : natural;
    --------------------------------
    ACCL_NUM              : natural;
    SIMD_BITS             : natural;
    Data_Width            : natural
  );
  port(
    clk_i                 : in  std_logic;
    sc_we                 : in  array_2d(accl_range)(SIMD*SPM_NUM-1 downto 0);
    sc_addr_wr            : in  array_3d(accl_range)(SIMD*SPM_NUM-1 downto 0)(Addr_Width-(SIMD_BITS+3) downto 0);
    sc_addr_rd            : in  array_3d(accl_range)(SIMD*SPM_NUM-1 downto 0)(Addr_Width-(SIMD_BITS+3) downto 0);
    sc_data_wr            : in  array_3d(accl_range)(SIMD*SPM_NUM-1 downto 0)(Data_Width-1 downto 0);
    sc_data_rd            : out array_3d(accl_range)(SIMD*SPM_NUM-1 downto 0)(Data_Width-1 downto 0)
    );
end component;
--------------------------------------------------------------------------------------------------
-------------------------------- SCI BEGIN -------------------------------------------------------
begin

  SC : Scratchpad_memory
    generic map(
      SPM_NUM		    => SPM_NUM,
      Addr_Width        => Addr_Width,
      SIMD              => SIMD,
      --------------------------------
      ACCL_NUM          => ACCL_NUM,
      SIMD_BITS         => SIMD_BITS,
      Data_Width        => Data_Width
    )
    port map(
       sc_we            => sc_we,
       clk_i            => clk_i,
       sc_addr_rd       => sc_addr_rd,
       sc_addr_wr       => sc_addr_wr,
       sc_data_wr       => sc_data_wr,
       sc_data_rd       => sc_data_rd
      );

  process(all)
  begin
    for h in accl_range loop
      for i in 0 to SPM_NUM-1 loop
        ls_data_gnt_i(i) <=  ls_data_gnt_internal(h)(0) or ls_data_gnt_internal(h)(i);
      end loop;
    end loop;
  end process;

  SPM_replicated : for h in accl_range generate

  SCI_Exec_Unit : process(clk_i, rst_ni)  -- single cycle unit, fully synchronous
  begin
    if rst_ni = '0' then
      dsp_sc_read_addr_lat(h) <= (others => (others => '0'));
      dsp_to_sc_lat(h)        <= (others => (others => '0'));
      dsp_sci_req_lat(h)      <= (others => '0');
      ls_data_gnt_internal(h) <= (others => '0');
      halt_dsp(h)             <= '0';
      sc_word_count(h)        <= 0;
    elsif rising_edge(clk_i) then
      halt_dsp(h)                     <= '0';
      dsp_sci_wr_gnt_lat(h)           <= dsp_sci_wr_gnt(h);
      ls_sci_wr_gnt_lat_replicated(h) <= ls_sci_wr_gnt_replicated(h);
      dsp_sci_req_lat(h)              <= dsp_sci_req(h);
      dsp_to_sc_lat(h)                <= dsp_to_sc(h);
      if harc_LS_wire = h or spm_bcast = '1' then
        sc_word_count(h)            <= sc_word_count_wire;
      end if;
      if unsigned(ls_data_gnt_internal(h)) /= 0 then
        ls_sc_data_read_replicated(h)  <= ls_sc_data_read_wire_replicated(h);
      end if;
      if (dsp_sci_wr_gnt(h) = '0' and dsp_sci_we(h) /= (0 to SPM_NUM-1 => '0')) then
        halt_dsp(h) <= '1';
      end if;
      if halt_dsp(h) = '0' then
        dsp_sc_data_read(h) <= dsp_sc_data_read_wire(h);
      end if;

      for i in 0 to SPM_NUM-1 loop
        if ls_sci_req(i) = '1' then  -- AAA most probably useless
          ls_data_gnt_internal(h)(i) <= '1';
        elsif ls_sci_req(i) = '0' then
          ls_data_gnt_internal(h)(i) <= '0';
        end if;
        if dsp_sci_req(h)(i) = '1' then
          for k in 0 to 1 loop
            dsp_sc_read_addr_lat(h)(k) <= dsp_sc_read_addr(h)(k)(SIMD_BITS+1 downto 0);
          end loop;
        end if;
      end loop;
    end if;
  end process;

  ls_sc_data_read_wire <= ls_sc_data_read_wire_replicated(harc_LS_wire);
  ls_sci_wr_gnt        <= ls_sci_wr_gnt_replicated(harc_LS_wire);

  SCI_Exec_Unit_comb : process(all)
  begin
    dsp_data_gnt_i(h)  <= '0';
    block_dsp_rd(h)    <= '0';
    for l in 0 to (SIMD*SPM_NUM)-1 loop
      sc_we(h)(l)      <= '0';
      sc_addr_rd(h)(l) <= (others => '0');

    dsp_sci_wr_gnt(h)                  <= dsp_sci_wr_gnt_lat(h);
    ls_sc_data_read_wire_replicated(h) <= ls_sc_data_read_replicated(h);
    dsp_sc_data_write_int_wire(h)      <= (others => '0');
    dsp_sc_data_read_wire(h)           <= dsp_sc_data_read(h);
    for i in 0 to SPM_NUM-1 loop	-- Loop through scratchpads A,B,C,D

      if data_rvalid_i = '1' then        -- LS write port
        if ls_sci_req(i) = '1' and ls_sci_we(i) = '1' and ls_sci_wr_gnt = '1' then
          if harc_LS_wire = h or spm_bcast = '1' then
            sc_we(h)((SIMD)*i + sc_word_count(h)) <= '1';
            sc_data_wr(h)(sc_word_count(h) + (SIMD)*i) <= ls_sc_data_write_wire(31 downto 0);
            sc_addr_wr(h)(sc_word_count(h) + (SIMD)*i) <= ls_sc_write_addr;
          end if;
        end if;
      end if;

      if ls_data_gnt_internal(h)(i) = '1' then
        if harc_LS_wire = h then
          ls_sc_data_read_wire_replicated(h) <= sc_data_rd(h)((SIMD)*i + sc_word_count(h));  -- sc_word_count because data being read is delayed one cycle after the request
        end if;
      end if;

      if ls_sci_req(i) = '1' then         -- LS read port
        if harc_LS_wire = h then
          sc_addr_rd(h)(sc_word_count_wire + (SIMD)*i) <= ls_sc_read_addr;
        end if;
      end if;

      if dsp_sci_we(h)(i) = '1' and dsp_sci_wr_gnt(h) = '1' then         -- DSP write port;
        for j in 0 to SIMD-1 loop        -- Loop through the sub-scratchpad banks
          sc_we(h)((SIMD)*i+j)    <= dsp_we_word(h)(j);
          sc_addr_wr(h)((SIMD)*i+j) <= std_logic_vector(unsigned(dsp_sc_write_addr(h)(Addr_Width-1 downto SIMD_BITS+2)) + wr_offset(h)(j));
          sc_data_wr(h)((SIMD)*i+j) <= dsp_sc_data_write_int_wire(h)(31+32*j downto 32*j);
        end loop;
      end if;

      if dsp_sci_req(h)(i) = '1' and dsp_to_sc(h)(i)(0) = '1' and dsp_data_gnt_i(h) = '1' then         -- DSP read port 1
        for j in 0 to SIMD-1 loop      -- Loop through the sub-scratchpad banks
          sc_addr_rd(h)((SIMD)*i+j) <= std_logic_vector(unsigned(dsp_sc_read_addr(h)(0)(Addr_Width-1 downto SIMD_BITS+2)) + rd_offset(h)(0)(j));
        end loop;
      end if;
      for j in 0 to SIMD-1 loop        -- Loop through the sub-scratchpad banks
        if dsp_sci_req_lat(h)(i) = '1' and dsp_to_sc_lat(h)(i)(0) = '1' then         -- DSP read port 1
          dsp_sc_data_read_int_wire(h)(0)(31+32*j downto 32*j) <= sc_data_rd(h)((SIMD)*i+j);
        end if;
      end loop;

      if dsp_sci_req(h)(i) = '1' and dsp_to_sc(h)(i)(1) = '1' and dsp_data_gnt_i(h) = '1' then       -- DSP read port 2
        for j in 0 to SIMD-1 loop        -- Loop through the sub-scratchpads
          sc_addr_rd(h)((SIMD)*i+j) <= std_logic_vector(unsigned(dsp_sc_read_addr(h)(1)(Addr_Width-1 downto SIMD_BITS+2)) + rd_offset(h)(1)(j));
        end loop;
      end if;
      for j in 0 to SIMD-1 loop        -- Loop through the sub-scratchpads
        if dsp_sci_req_lat(h)(i) = '1' and dsp_to_sc_lat(h)(i)(1) = '1' then         -- DSP read port 2
          dsp_sc_data_read_int_wire(h)(1)(31+32*j downto 32*j) <= sc_data_rd(h)((SIMD)*i+j);
        end if;
      end loop;

      if (kmemld_inflight(i) = '1' or kmemstr_inflight(i) = '1') and dsp_sci_req(h)(i) = '1' then
        block_dsp_rd(h) <= '1';
      end if;

      -- Allow a DSP read only if the SPM(i) being loaded belongs to another thread and the instruction is not a broadcast load (data hazard)
      if kmemld_inflight(i) = '1' and dsp_sci_req(h)(i) = '1' and h /= harc_LS_wire and spm_bcast = '0' then
        dsp_data_gnt_i(h) <= '1';
      -- Allow a dsp read only when it is not currently being read by a kmemstr becuase we only have one read port (structural hazard)
      elsif kmemstr_inflight(i) = '1' and dsp_sci_req(h)(i) = '1' and h /= harc_LS_wire then
        dsp_data_gnt_i(h) <= '1';
      -- Allow a DSP read if there are no current LSU accesses to SPM(i)
      elsif kmemld_inflight(i) = '0' and kmemstr_inflight(i) = '0' and dsp_sci_req(h)(i) = '1' and block_dsp_rd(h) = '0' then
        dsp_data_gnt_i(h) <= '1';
      end if;

      if dsp_sci_we(h) = (0 to SPM_NUM-1 => '0') then
        dsp_sci_wr_gnt(h) <= '0';
      -- Allow the DSP to write only if the kmemld is filling the SPM(i) of another thread
      elsif kmemld_inflight(i) = '1' and dsp_sci_we(h)(i) = '1' and h /= harc_LS_wire and spm_bcast = '0' then
        dsp_sci_wr_gnt(h) <= '1';
      -- Allow the DSP to write only when the kmemstr is reading SPM(i) of another thread
      elsif kmemstr_inflight(i) = '1' and dsp_sci_we(h)(i) = '1' and  h /= harc_LS_wire then
        dsp_sci_wr_gnt(h) <= '1';
      -- Allow the DSP to write if there are no current LSU accesses to SPM(i)
      elsif kmemld_inflight(i) = '0' and kmemstr_inflight(i) = '0' and dsp_sci_we(h)(i) = '1' then
        dsp_sci_wr_gnt(h) <= '1';
      end if;

      if kmemld_inflight(i) = '1' and dsp_sci_we(h)(i) = '0' then -- One LSU write enable request will put the ls_sci_wr_gnt to '1' if there are no ongoing DSP writes to the same scratchpad
        ls_sci_wr_gnt_replicated(h) <= '1';
      elsif kmemld_inflight(i) = '1' and dsp_sci_we(h)(i) = '1' and (h /= harc_LS_wire) and spm_bcast = '0' then
        ls_sci_wr_gnt_replicated(h) <= '1';
      --elsif unsigned(kmemld_inflight) = 0 then   -- All the ls_sci_we must be zero in-order to switch the ls_sci_wr_gnt back to '0'
      elsif kmemld_inflight = (SPM_NUM-1 downto 0 => '0') then
        ls_sci_wr_gnt_replicated(h) <= '0';
      end if;
    end loop;

 -----------------------------------------------------------------------------------------------
--  ██████╗  █████╗ ████████╗ █████╗     ██████╗  ██████╗ ████████╗ █████╗ ████████╗███████╗  --
--  ██╔══██╗██╔══██╗╚══██╔══╝██╔══██╗    ██╔══██╗██╔═══██╗╚══██╔══╝██╔══██╗╚══██╔══╝██╔════╝  --
--  ██║  ██║███████║   ██║   ███████║    ██████╔╝██║   ██║   ██║   ███████║   ██║   █████╗    --
--  ██║  ██║██╔══██║   ██║   ██╔══██║    ██╔══██╗██║   ██║   ██║   ██╔══██║   ██║   ██╔══╝    --
--  ██████╔╝██║  ██║   ██║   ██║  ██║    ██║  ██║╚██████╔╝   ██║   ██║  ██║   ██║   ███████╗  --
--  ╚═════╝ ╚═╝  ╚═╝   ╚═╝   ╚═╝  ╚═╝    ╚═╝  ╚═╝ ╚═════╝    ╚═╝   ╚═╝  ╚═╝   ╚═╝   ╚══════╝  --
------------------------------------------------------------------------------------------------

    for i in 0 to SIMD-1 loop
      --if (to_integer(unsigned(dsp_sc_write_addr(h)(SIMD_BITS+1 downto 0))) = 4*i) and (i /= 0) then
      if (dsp_sc_write_addr(h)(SIMD_BITS+1 downto 0) = std_logic_vector(to_unsigned(4*i, SIMD_BITS+2))) and (i /= 0) then
        wr_offset(h)(i-1 downto 0) <= (others => '1');
      end if;
    end loop;
    for i in 0 to SIMD-1 loop
      --if (to_integer(unsigned(dsp_sc_write_addr(h)(SIMD_BITS+1 downto 0))) = 4*i) then
      if dsp_sc_write_addr(h)(SIMD_BITS+1 downto 0) = std_logic_vector(to_unsigned(4*i, SIMD_BITS+2)) then
        for j in 0 to SIMD-1 loop
          if j <= (SIMD-1)-i then
            dsp_sc_data_write_int_wire(h)(31+32*(j+i) downto 32*(j+i)) <= dsp_sc_data_write_wire(h)(31+32*j downto 32*j);
          elsif j > (SIMD-1)-i then
            dsp_sc_data_write_int_wire(h)(31+32*(j-(SIMD-1)+(i-1)) downto 32*(j-(SIMD-1)+(i-1))) <= dsp_sc_data_write_wire(h)(31+32*j downto 32*j);
          end if;
        end loop;
      end if;
    end loop;
    --for i in 0 to 4*SIMD-1 loop
    --  for j in 0 to 4*SIMD-1 loop
    --    if j <= (4*SIMD-1)-i then
    --      dsp_sc_data_write_int_wire(h)(7+8*(j+i) downto 8*(j+i)) <= dsp_sc_data_write_wire(h)(7+8*j downto 8*j);
    --    elsif j > (4*SIMD-1)-i then
    --      dsp_sc_data_write_int_wire(h)(7+8*(j-(SIMD-1)+(i-1)) downto 8*(j-(SIMD-1)+(i-1))) <= dsp_sc_data_write_wire(h)(7+8*j downto 8*j);
    --    end if;
    --  end loop;
    --end loop;

    for k in 0 to 1 loop  -- index for the rs1 and rs2 read addresses
      for i in 0 to SIMD-1 loop -- index points to the
        --if (to_integer(unsigned(dsp_sc_read_addr(h)(k)(SIMD_BITS+1 downto 0))) = 4*i) and (i /= 0) then -- 4*i instead of i is because the address is word aligned and not byte aligned
        if (dsp_sc_read_addr(h)(k)(SIMD_BITS+1 downto 0) = std_logic_vector(to_unsigned(4*i, SIMD_BITS+2))) and (i /= 0) then
          rd_offset(h)(k)(i-1 downto 0) <= (others => '1'); -- sets the bank offset withing the scratchpad that will be used for the correct bank access
        end if;
      end loop;
      for i in 0 to SIMD-1 loop
        --if (to_integer(unsigned(dsp_sc_read_addr_lat(h)(k))) = 4*i) then
        if dsp_sc_read_addr_lat(h)(k) = std_logic_vector(to_unsigned(4*i, SIMD_BITS+2)) then
          for j in 0 to SIMD-1 loop
            if j >= i then
              dsp_sc_data_read_wire(h)(k)(31+32*(j-i) downto 32*(j-i)) <= dsp_sc_data_read_int_wire(h)(k)(31+32*j downto 32*j);
            elsif j < i then
              dsp_sc_data_read_wire(h)(k)(31+32*((SIMD-1)-i+(j+1)) downto 32*((SIMD-1)-i+(j+1))) <= dsp_sc_data_read_int_wire(h)(k)(31+32*j downto 32*j);
            end if;
          end loop;
        end if;
      end loop;
      --for i in 0 to 4*SIMD-1 loop
      --  for j in 0 to 4*SIMD-1 loop
      --    if j >= i then
      --      dsp_sc_data_read_wire(h)(k)(7+8*(j-i) downto 8*(j-i)) <= dsp_sc_data_read_int_wire(h)(k)(7+8*j downto 8*j);
      --    elsif j < i then
      --      dsp_sc_data_read_wire(h)(k)(7+8*((SIMD-1)-i+(j+1)) downto 8*((SIMD-1)-i+(j+1))) <= dsp_sc_data_read_int_wire(h)(k)(7+8*j downto 8*j);
      --    end if;
      --  end loop;
      --end loop;
    end loop;
  end process;

  end generate SPM_replicated;

--------------------------------------------------------------------- end of SCI Logic -----------
--------------------------------------------------------------------------------------------------

end SCI;
--------------------------------------------------------------------------------------------------
-- END of SCI architecture -----------------------------------------------------------------------
--------------------------------------------------------------------------------------------------

      sc_addr_wr(h)(l) <= (others => '0');
      sc_data_wr(h)(l) <= (others => '0');
    end loop;
    rd_offset(h)                       <= (others => (others => '0'));