--------------------------------------------------------------------------
-- Project: JIPS, a portable 32-bit RISC CPU written in VHDL
-- This file: Types, constants and functions for JIPS
--
-- Copyright (C) 2008 J. Ahrensfeld
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
--
-- For questions and ideas, please contact the author at jens@jayfield.org
--
--------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;

library work;
use work.mips_util_pkg.all;
use work.mips_types.all;

--------------------------------------------------------------------------
package mips_func_pkg is

	function store_shift(x : word_t; va, shift_off : unsigned(1 downto 0); bypass : std_logic) return word_t;
	function store_be(va : unsigned(1 downto 0); be_src, word2_en, word4_en, align_left, bypass : std_logic) return unsigned;
	function load_shift(x : word_t; va, shift_off : unsigned(1 downto 0); bypass : std_logic) return word_t;
	function load_be(va : unsigned(1 downto 0); align_left, bypass : std_logic) return unsigned;
	function load_sign_ext(x : word_t; bypass, signed, word2_en, word4_en : std_logic) return word_t;
	function events_clr return event_t;
	function event_is_active(e : event_t) return std_logic;
	function "or" (a, b : event_t) return event_t;

end mips_func_pkg;

--------------------------------------------------------------------------
package body mips_func_pkg is

	function store_shift(x : word_t; va, shift_off : unsigned(1 downto 0); bypass : std_logic) return word_t is
	variable stage1 : word_t;
	variable result : word_t;
	variable sa : unsigned(1 downto 0);
	begin
		
		if bypass = '1'	then
			sa := "00";
		else
			sa := va + shift_off;
		end if;
		
		stage1 := x;
		if sa(0) = '1' then
			stage1 := x(23 downto 0) & x(31 downto 24);
		end if;
		result := stage1;
		if sa(1) = '1' then
			result := stage1(15 downto 0) & stage1(31 downto 16);
		end if;
		
		return result;
	end store_shift;
	
--------------------------------------------------------------------------
	function store_be(va : unsigned(1 downto 0); be_src, word2_en, word4_en, align_left, bypass : std_logic) return unsigned is
	variable result : unsigned(3 downto 0);
	begin
		result := (3 downto 0 => be_src);
		if bypass = '0' then
			if word2_en = '1' then
				case va is
					when "00" =>
						result := "00" & be_src & be_src;
					when "10" =>
						result := be_src & be_src & "00";
					when others => null;
				end case;
			elsif word4_en = '1' then
				case va is
					when "00" =>
						result := "000" & be_src;
					when "01" =>
						result := "00" & be_src & '0';
					when "10" =>
						result := '0' & be_src & "00";
					when "11" =>
						result := be_src & "000";
					when others => null;
				end case;
			elsif align_left = '1' then
				case va is
					when "00" =>
						result := be_src & be_src & be_src & be_src;
					when "01" =>
						result := be_src & be_src & be_src & '0';
					when "10" =>
						result := be_src & be_src & "00";
					when "11" =>
						result := be_src & "000";
					when others => null;
				end case;
			else
				case va is
					when "00" =>
						result := "000" & be_src;
					when "01" =>
						result := "00" & be_src & be_src;
					when "10" =>
						result := '0' & be_src & be_src & be_src;
					when "11" =>
						result := be_src & be_src & be_src & be_src;
					when others => null;
				end case;
			end if;
		end if;
		
		return result;
	
	end store_be;
	
--------------------------------------------------------------------------
	function load_shift(x : word_t; va, shift_off : unsigned(1 downto 0); bypass : std_logic) return word_t is
	variable stage1 : word_t;
	variable result : word_t;
	variable sa : unsigned(1 downto 0);
	
	begin
		if bypass = '1' then
			sa 		:= "00";
		else
			sa		:= va + shift_off;
		end if;
		
		stage1 := x;
		if sa(0) = '1' then
			stage1 :=  x(7 downto 0) & x(31 downto 8);
		end if;
		result := stage1;
		if sa(1) = '1' then
			result := stage1(15 downto 0) & stage1(31 downto 16);
		end if;
		
		return result;
	end load_shift;
	
--------------------------------------------------------------------------
	function load_be(va : unsigned(1 downto 0); align_left, bypass : std_logic) return unsigned is
	variable result : unsigned(3 downto 0);
	begin
		result := (3 downto 0 => '1');
		if bypass = '0' then
			if align_left = '1' then
				case va is
					when "00" =>
						result := "1000";
					when "01" =>
						result := "1100";
					when "10" =>
						result := "1110";
					when "11" =>
						result := "1111";
					when others => null;
				end case;
			else
				case va is
					when "00" =>
						result := "1111";
					when "01" =>
						result := "0111";
					when "10" =>
						result := "0011";
					when "11" =>
						result := "0001";
					when others => null;
				end case;
			end if;
		end if;
		
		return result;
	
	end load_be;
	
--------------------------------------------------------------------------
	function load_sign_ext(x : word_t; bypass, signed, word2_en, word4_en : std_logic) return word_t is
	variable result : word_t;
	variable sign : std_logic;
	
	begin
		if bypass = '1' then
			result := x;
		elsif word2_en = '1' then
			sign	:= signed and x(15);
			result := (31 downto 16 => sign) & x(15 downto 0);
		else
			sign	:= signed and x(7);
			result := (31 downto 8 => sign) & x(7 downto 0);
		end if;
		
		return result;
	end load_sign_ext;
	
--------------------------------------------------------------------------
	function events_clr return event_t is
	variable result : event_t;
	begin
		result.NMI				:= '0';
		result.Int				:= '0';
		result.data_load_err	:= '0';
		result.data_store_err	:= '0';
		result.inst_load_err	:= '0';
		result.inst_priv_addr	:= '0';
		result.alu_ovf			:= '0';
		result.alu_uvf			:= '0';
		result.syscall			:= '0';
		result.break			:= '0';
		result.illegal			:= '0';
		
		return result;
		
	end events_clr;

--------------------------------------------------------------------------
	function event_is_active(e : event_t) return std_logic is
	variable result : std_logic;
	begin
		result := '0';
		
		result	:= result or e.NMI;
		result	:= result or e.Int;
		result	:= result or e.data_load_err;
		result	:= result or e.data_store_err;
		result	:= result or e.inst_load_err;
		result	:= result or e.inst_priv_addr;
		result	:= result or e.alu_ovf;
		result	:= result or e.alu_uvf;
		result	:= result or e.syscall;
		result	:= result or e.break;
		result	:= result or e.illegal;

		return result;
		
	end event_is_active;

--------------------------------------------------------------------------
	function "or" (a, b : event_t) return event_t is
	variable result : event_t;
	begin
		result.NMI				:= a.NMI or b.NMI;
		result.Int				:= a.Int or b.Int;
		result.data_load_err	:= a.data_load_err or b.data_load_err;
		result.data_store_err	:= a.data_store_err or b.data_store_err;
		result.inst_load_err	:= a.inst_load_err or b.inst_load_err;
		result.inst_priv_addr	:= a.inst_priv_addr or b.inst_priv_addr;
		result.alu_ovf			:= a.alu_ovf or b.alu_ovf;
		result.alu_uvf			:= a.alu_uvf or b.alu_uvf;
		result.syscall			:= a.syscall or b.syscall;
		result.break			:= a.break or b.break;
		result.illegal			:= a.illegal or b.illegal;
		
		return result;
		
	end "or";

--------------------------------------------------------------------------
end mips_types;

