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

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

entity fetch is
	port (
		clk        : in  std_logic;
		res_n      : in  std_logic;
		stall      : in  std_logic;
		flush      : in  std_logic;

		-- to control
		mem_busy   : out std_logic;

		pcsrc      : in  std_logic;
		pc_in      : in  pc_type;
		pc_out     : out pc_type := (others => '0');
		instr      : out instr_type;

		-- memory controller interface
		mem_out   : out mem_out_type;
		mem_in    : in  mem_in_type
	);
end entity;

architecture rtl of fetch is
	signal pc_out_next : pc_type := (others => '0');
begin
	-- concurrent
	mem_busy <= mem_in.busy;
	mem_out.address(ADDR_WIDTH - 1 downto 0) <= pc_out_next(PC_WIDTH - 1 downto PC_WIDTH - ADDR_WIDTH);
	mem_out.rd <= '1';
	mem_out.wr <= '0';
	mem_out.byteena <= "1111";
	mem_out.wrdata <= (others => '0');
	instr(31 downto 24) <= NOP_INST(31 downto 24) when flush = '1' else mem_in.rddata(7  downto 0);
	instr(23 downto 16) <= NOP_INST(23 downto 16) when flush = '1' else mem_in.rddata(15 downto 8);
	instr(15 downto 8)  <= NOP_INST(15 downto 8)  when flush = '1' else mem_in.rddata(23 downto 16);
	instr(7 downto 0)   <= NOP_INST(7 downto 0)   when flush = '1' else mem_in.rddata(31 downto 24);

	-- sequential
	sync : process(clk, res_n)
	begin
		if res_n = '0' then
			pc_out <= (others => '0'); --initialize to -4
		elsif rising_edge(clk) then
			pc_out <= pc_out_next;
		end if;
	end process;

	async : process(all)
	begin
		pc_out_next <= pc_out;

		-- assumes instruction has the same length as mem data
		-- only change pc
		if  stall = '0' and pcsrc = '1' then
			-- use pc_in
			pc_out_next <= pc_in;
		elsif stall = '0' and pcsrc = '0' then
			-- use pc_out + 4 (bytes)
			pc_out_next <= std_logic_vector(unsigned(pc_out) + x"4");
		end if;
	end process;
	
end architecture;
