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

entity MatrixCore is
  generic (
    MATRIX_SIZE : integer := 5
  );
  port (
    clk : in std_logic;
    rst : in std_logic;
    input_matrix : in std_logic_vector((MATRIX_SIZE * MATRIX_SIZE) - 1 downto 0);
    output_matrix : out std_logic_vector((MATRIX_SIZE * MATRIX_SIZE) - 1 downto 0)
  );
end entity MatrixCore;

architecture Behavioral of MatrixCore is
  type matrix_array is array(0 to MATRIX_SIZE-1, 0 to MATRIX_SIZE-1) of real;
  signal random_matrix : matrix_array;
  signal processing_done : std_logic := '0';
  signal result_matrix : matrix_array := (others => '0');
begin
  process(clk, rst)
  begin
    if rst = '1' then
      processing_done <= '0';
    elsif rising_edge(clk) then
      if processing_done = '0' then
        -- Generate random matrix (replace with your actual matrix loading logic)
        for i in 0 to MATRIX_SIZE-1 loop
          for j in 0 to MATRIX_SIZE-1 loop
            random_matrix(i, j) <= real(to_integer(unsigned(input_matrix(i*MATRIX_SIZE + j*32 + 31 downto i*MATRIX_SIZE + j*32))));
          end loop;
        end loop;

        -- Matrix multiplication
        for i in 0 to MATRIX_SIZE-1 loop
          for j in 0 to MATRIX_SIZE-1 loop
            -- Initialize the result to 0 for each element
            result_matrix(i, j) <= 0.0;
            
            -- Loop through each element in the second matrix (k)
            for k in 0 to MATRIX_SIZE-1 loop
              -- Perform element-wise multiplication and accumulate
              result_matrix(i, j) <= result_matrix(i, j) + (random_matrix(i, k) * random_matrix(k, j));
            end loop;
          end loop;
        end loop;

        processing_done <= '1';
      end if;
    end if;
  end process;
  
  output_matrix <= std_logic_vector(to_unsigned(result_matrix, (MATRIX_SIZE * MATRIX_SIZE) - 1 downto 0));
end architecture Behavioral;

entity TransformerEngine is
  generic (
    NUM_CORES : integer := 4
  );
  port (
    clk : in std_logic;
    rst : in std_logic;
    input_matrix : in std_logic_vector((MATRIX_SIZE * MATRIX_SIZE) - 1 downto 0);
    output_matrices : out std_logic_vector((NUM_CORES * MATRIX_SIZE * MATRIX_SIZE) - 1 downto 0)
  );
end entity TransformerEngine;

architecture Behavioral of TransformerEngine is
  signal cores_output : std_logic_vector((NUM_CORES * MATRIX_SIZE * MATRIX_SIZE) - 1 downto 0);
  signal core_outputs : std_logic_vector((MATRIX_SIZE * MATRIX_SIZE) - 1 downto 0);
  signal core_index : integer range 0 to NUM_CORES-1 := 0;
begin
  process(clk, rst)
  begin
    if rst = '1' then
      core_index <= 0;
    elsif rising_edge(clk) then
      if core_index < NUM_CORES then
        core_outputs <= (others => '0');
        for i in 0 to MATRIX_SIZE-1 loop
          for j in 0 to MATRIX_SIZE-1 loop
            core_outputs(i*MATRIX_SIZE + j*32 + 31 downto i*MATRIX_SIZE + j*32) <= input_matrix(i*MATRIX_SIZE + j*32 + 31 downto i*MATRIX_SIZE + j*32);
          end loop;
        end loop;

        cores_output(core_index*MATRIX_SIZE*MATRIX_SIZE + (MATRIX_SIZE*MATRIX_SIZE-1) downto core_index*MATRIX_SIZE*MATRIX_SIZE) <= core_outputs;
        core_index <= core_index + 1;
      end if;
    end if;
  end process;

  MatrixCore_insts: for i in 0 to NUM_CORES-1 generate
    MatrixCore_inst : MatrixCore
      generic map (
        MATRIX_SIZE => MATRIX_SIZE
      )
      port map (
        clk => clk,
        rst => rst,
        input_matrix => cores_output(i*MATRIX_SIZE*MATRIX_SIZE + (MATRIX_SIZE*MATRIX_SIZE-1) downto i*MATRIX_SIZE*MATRIX_SIZE),
        output_matrix => output_matrices(i*MATRIX_SIZE*MATRIX_SIZE + (MATRIX_SIZE*MATRIX_SIZE-1) downto i*MATRIX_SIZE*MATRIX_SIZE)
      );
  end generate;

end architecture Behavioral;