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

use work.mem_pkg.all;
use work.core_pkg.all;
use work.op_pkg.all;

entity memu is
	port (
		-- to mem
		op   : in  memu_op_type;
		A    : in  data_type;
		W    : in  data_type;
		R    : out data_type := (others => '0');

		B    : out std_logic := '0';
		XL   : out std_logic := '0';
		XS   : out std_logic := '0';

		-- to memory controller
		D    : in  mem_in_type;
		M    : out mem_out_type := MEM_OUT_NOP
	);
end entity;

architecture rtl of memu is
	type word_arr is array(0 to 3) of std_logic_vector(7 downto 0);
	 
	-- write pipeline bytes
	alias W_0 : std_logic_vector(7 downto 0) is W(7 downto 0);
	alias W_1 : std_logic_vector(7 downto 0) is W(15 downto 8);
	alias W_2 : std_logic_vector(7 downto 0) is W(23 downto 16);
	alias W_3 : std_logic_vector(7 downto 0) is W(31 downto 24);
	signal W_arr : word_arr;
	
	
	-- read pipeline bytes
	alias R_0 : std_logic_vector(7 downto 0) is R(7 downto 0);
	alias R_1 : std_logic_vector(7 downto 0) is R(15 downto 8);
	alias R_2 : std_logic_vector(7 downto 0) is R(23 downto 16);
	alias R_3 : std_logic_vector(7 downto 0) is R(31 downto 24);
	signal R_arr : word_arr;
	
	-- write mem bytes
	alias MW_0 : std_logic_vector(7 downto 0) is M.wrdata(7 downto 0);
	alias MW_1 : std_logic_vector(7 downto 0) is M.wrdata(15 downto 8);
	alias MW_2 : std_logic_vector(7 downto 0) is M.wrdata(23 downto 16);
	alias MW_3 : std_logic_vector(7 downto 0) is M.wrdata(31 downto 24);
	signal MW_arr : word_arr;
	
	-- read mem bytes
	alias DR_0 : std_logic_vector(7 downto 0) is D.rddata(7 downto 0);
	alias DR_1 : std_logic_vector(7 downto 0) is D.rddata(15 downto 8);
	alias DR_2 : std_logic_vector(7 downto 0) is D.rddata(23 downto 16);
	alias DR_3 : std_logic_vector(7 downto 0) is D.rddata(31 downto 24);
	signal DR_arr : word_arr;
	
	-- adress alias
	alias A_byte : std_logic_vector(1 downto 0) is A(1 downto 0);
	-- signals
	-- signal exep_l, exep_s : std_logic;
begin
	
	-- concurrent
	XL <= '1' when (op.memread = '1' and (
		((op.memtype = MEM_H or op.memtype = MEM_HU) and A_byte(0) = '1') or -- cant access uneven half word
		((op.memtype = MEM_W and A_byte /= "00") -- cant access word if not full 
		)))
		else '0';
	XS <= '1' when (op.memwrite = '1' and (
		((op.memtype = MEM_H or op.memtype = MEM_HU) and A_byte(0) = '1') or -- cant write uneven half word
		((op.memtype = MEM_W and A_byte /= "00") -- cant write word if not full 
		)))
		else '0';
	
	M.address <= A(15 downto 2);
	
	W_arr <= (W_0, W_1, W_2, W_3);
	R_0 <= R_arr(0);
	R_1 <= R_arr(1);
	R_2 <= R_arr(2);
	R_3 <= R_arr(3);
	MW_0 <= MW_arr(0);
	MW_1 <= MW_arr(1);
	MW_2 <= MW_arr(2);
	MW_3 <= MW_arr(3);
	DR_arr <= (DR_0, DR_1, DR_2, DR_3);
	
	M.address <= A(15 downto 2);
	
	-- memop
	M.rd <= op.memread when XL = '0' else '0';
	M.wr <= op.memwrite when XS = '0' else '0';
	B <= (op.memread and not XL) or D.busy;
	
	-- synchronous
	translation : process(all) -- very confuse but looks cool right :3
	begin
		case op.memtype is
			when MEM_B =>
				R_arr <= (0 => DR_arr(to_integer(unsigned(not A_byte))), others => (others => DR_arr(to_integer(unsigned(not A_byte)))(7)));
				MW_arr <= (others => (others => '-'));
				MW_arr(to_integer(unsigned(not A_byte))) <= W_arr(0);
				M.byteena <= (others => '0');
				M.byteena(to_integer(unsigned(not A_byte))) <= '1';
				
			when MEM_BU =>
				R_arr <= (0 => DR_arr(to_integer(unsigned(not A_byte))), others => (others => '0'));
				MW_arr <= (others => (others => '-'));
				MW_arr(to_integer(unsigned(not A_byte))) <= W_arr(0);
				M.byteena <= (others => '0');
				M.byteena(to_integer(unsigned(not A_byte))) <= '1';
				
			when MEM_H => 
				if A_byte(1) = '0' then
					R_arr <= (0 => DR_arr(3), 1 => DR_arr(2), others => (others => DR_arr(2)(7)));
					MW_arr <= (3 => W_arr(0), 2 => W_arr(1), others => (others => '-'));
					M.byteena <= "1100";
				else
					R_arr <= (0 => DR_arr(1), 1 => DR_arr(0), others => (others => DR_arr(0)(7)));
					MW_arr <= (1 => W_arr(0), 0 => W_arr(1), others => (others => '-'));
					M.byteena <= "0011";
				end if;
			
			when MEM_HU =>
				if A_byte(1) = '0' then
					R_arr <= (0 => DR_arr(3), 1 => DR_arr(2), others => (others => '0'));
					MW_arr <= (3 => W_arr(0), 2 => W_arr(1), others => (others => '-'));
					M.byteena <= "1100";
				else
					R_arr <= (0 => DR_arr(1), 1 => DR_arr(0), others => (others => '0'));
					MW_arr <= (1 => W_arr(0), 0 => W_arr(1), others => (others => '-'));
					M.byteena <= "0011";
				end if;
			
			when MEM_W =>
				R_arr <= (0 => DR_arr(3), 1 => DR_arr(2), 2 => DR_arr(1), 3 => DR_arr(0));
				MW_arr <= (0 => W_arr(3), 1 => W_arr(2), 2 => W_arr(1), 3 => W_arr(0));
				M.byteena <= "1111";
		end case;
		
	end process;
	
end architecture;
