--------------------------------------------------------------------------
-- 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;

--------------------------------------------------------------------------
package mips_types is

	-- Revision of the CPU
	constant REVISION : integer := 13;
	constant WORD_WIDTH : integer := 32;
	
	--Types
	subtype instr_name_t is string(1 to 12);
	type instr_name_array_t is array (0 to 63) of instr_name_t;
	subtype opcode_t is unsigned (5 downto 0);
	subtype word_t is unsigned (WORD_WIDTH-1 downto 0);
	subtype reg_ptr_t is unsigned (4 downto 0);
	subtype shamt_t is unsigned(4 downto 0);
	subtype sa_t is natural range 0 to 63;
	subtype func_t is unsigned(5 downto 0);

	type chip_debug_t is record	
		imem_err			: std_logic;
		dmem_err			: std_logic;
	end record;

	type op_t is
	(
		op_illegal,
		op_nop,
		op_sll,
		op_srl,
		op_sra,
		op_sllv,
		op_srlv,
		op_srav,
		op_jr,
		op_jalr,
		op_syscall,
		op_break,
		op_mfhi,
		op_mthi,
		op_mflo,
		op_mtlo,
		op_mult,
		op_multu,
		op_div,
		op_divu,
		op_add,
		op_addu,
		op_sub,
		op_subu,
		op_and,
		op_or,
		op_xor,
		op_nor,
		op_slt,
		op_sltu,
		op_bltz,
		op_bgez,
		op_bltzal,
		op_bgezal,
		op_j,
		op_jal,
		op_beq,
		op_bne,
		op_blez,
		op_bgtz,
		op_addi,
		op_addiu,
		op_slti,
		op_sltiu,
		op_andi,
		op_ori,
		op_xori,
		op_lui,
		op_lb,
		op_lh,
		op_lwl,
		op_lw,
		op_lbu,
		op_lhu,
		op_lwr,
		op_sb,
		op_sh,
		op_swl,
		op_sw,
		op_swr,
		op_cop0,
		op_cop1,
		op_cop2,
		op_cop3,
		op_mfc0,
		op_cfc0,
		op_mtc0,
		op_ctc0,
		op_bcf0,
		op_bct0,
		op_mfc1,
		op_cfc1,
		op_mtc1,
		op_ctc1,
		op_bcf1,
		op_bct1,
		op_mfc2,
		op_cfc2,
		op_mtc2,
		op_ctc2,
		op_bcf2,
		op_bct2,
		op_mfc3,
		op_cfc3,
		op_mtc3,
		op_ctc3,
		op_bcf3,
		op_bct3,
		op_lwc0,
		op_lwc1,
		op_lwc2,
		op_lwc3,
		op_swc0,
		op_swc1,
		op_swc2,
		op_swc3
	);
	
	attribute ENUM_ENCODING : string;
	
	type imm_src_t is (src_imm32, src_imm16, src_imm16_high);
	type sa_src_t is (sa_src_reg, sa_src_imm);
	type alu_src1_t is (alu_src_reg);
	type alu_src2_t is (alu_src_reg, alu_src_imm);
	type bc_src_t is (bc_eq_ne, bc_lez_gtz, bc_ltz_gez);
	type shift_opsel_t is (shift_sll, shift_srl, shift_sra);
	type wptr_src_t is (wptr_src_imm, wptr_src_const);
	
	type pc_t is record	
		curr			: word_t;
		last			: word_t;
		nxt				: word_t;
		pc_branch		: word_t;
		branch_take		: std_logic;
	end record;
	
	type alu_outsel_t is
	(
		alu_adder,
		alu_shift2,
		alu_and,
		alu_or,
		alu_xor,
		alu_nor,
		alu_ltu,
		alu_lts
	);

	type sdu_t is record
		imem_dep		: STD_LOGIC;
		dmem_dep		: STD_LOGIC;
		mul_dep			: STD_LOGIC;
		ID_nop			: STD_LOGIC;
		EX_nop			: STD_LOGIC;
		MEM_nop			: STD_LOGIC;
		WB_nop			: STD_LOGIC;
		ID_stall		: STD_LOGIC;
		EX_stall		: STD_LOGIC;
		MEM_stall		: STD_LOGIC;
		WB_stall		: STD_LOGIC;
		stall_all		: STD_LOGIC;
	end record;

	type hdu_t is record
		alu_fwd_a_ex	: boolean;
		alu_fwd_a_mem	: boolean;
		alu_fwd_a_wb	: boolean;
		alu_fwd_b_ex	: boolean;
		alu_fwd_b_mem	: boolean;
		alu_fwd_b_wb	: boolean;
	end record;

	type alu_flags_t is record
		c				: STD_LOGIC;
		uvf				: STD_LOGIC;
		ovf				: STD_LOGIC;
		ltu				: STD_LOGIC;
		lts				: STD_LOGIC;
	end record;
		
	type bcu_flags_t is record
		eq				: STD_LOGIC;
		ltz				: STD_LOGIC;
	end record;

	type event_t is record
		NMI				: STD_LOGIC;
		Int				: STD_LOGIC;
		data_load_err	: STD_LOGIC;
		data_store_err	: STD_LOGIC;
		inst_load_err	: STD_LOGIC;
		inst_priv_addr	: STD_LOGIC;
		alu_ovf			: STD_LOGIC;
		alu_uvf			: STD_LOGIC;
		syscall			: STD_LOGIC;
		break			: STD_LOGIC;
		illegal			: STD_LOGIC;
	end record;
	
	type exc_flags_t is record
		Int				: STD_LOGIC;
		DAdEL			: STD_LOGIC;
		DAdES			: STD_LOGIC;
		IAdEL			: STD_LOGIC;
		IAdEK			: STD_LOGIC;
		Ov				: STD_LOGIC;
		Sys				: STD_LOGIC;
		Bp				: STD_LOGIC;
		RI				: STD_LOGIC;
		IBE				: STD_LOGIC;
		DBE				: STD_LOGIC;
	end record;

	type except_t is record
		act				: STD_LOGIC;
		epc				: word_t;
		cause			: word_t;
	end record;
	
	type cache_ctrl_t is record
		inv_addr		: word_t;
		inv_at			: STD_LOGIC;
		inv_all			: STD_LOGIC;
	end record;
	
	type cop0_ctrl_in_t is record
		sdu				: sdu_t;
		events			: event_t;
		bd_wb			: STD_LOGIC;
		epc_mem			: word_t;
		epc_wb			: word_t;
		imem_addr		: word_t;
		dmem_addr		: word_t;
		exc_req			: STD_LOGIC;
		exc_ack			: STD_LOGIC;
	end record;

	type cop0_ctrl_out_t is record
		EB				: STD_LOGIC;
		exc_inject		: STD_LOGIC;
		exc_commit		: STD_LOGIC;
		exc_pending		: STD_LOGIC;
		exc_exit		: STD_LOGIC;
		user_mode		: STD_LOGIC;
		NMI				: STD_LOGIC;
		int				: STD_LOGIC;
		exc_vec			: word_t;
		icache			: cache_ctrl_t;
		dcache			: cache_ctrl_t;
	end record;
	
	type alu_ctrl_t is record
		outsel			: alu_outsel_t;
		add				: STD_LOGIC;
		shift_right		: STD_LOGIC;
		shift_arith		: STD_LOGIC;
		op1_src			: alu_src1_t;
		op2_src			: alu_src2_t;
	end record;
	
	type shift_ctrl_t is record
		shamt_nrm		: shamt_t;
		shamt_rnd		: shamt_t;
		shift_right		: STD_LOGIC;
		shift_arith		: STD_LOGIC;
	end record;

	type ctrl_lines_t is record
		branch			: STD_LOGIC;
		jump_long		: STD_LOGIC;
		jump			: STD_LOGIC;
		bc_not			: STD_LOGIC;
		bc_src			: bc_src_t;
		imm_src			: imm_src_t;
		alu				: alu_ctrl_t;
		reg_write		: STD_LOGIC;
		reg_link		: STD_LOGIC;
		dmem_we			: STD_LOGIC;
		dmem_en			: STD_LOGIC;
		shamt2_srcsel	: sa_src_t;
		wptr_srcsel		: wptr_src_t;
		cop_instr_en	: STD_LOGIC;
		cop_read		: STD_LOGIC;
		shift_offset	: unsigned(1 downto 0);
		shift_byp		: STD_LOGIC;
		byte_en_byp		: STD_LOGIC;
		sign_ext_byp	: STD_LOGIC;
		word4_en		: STD_LOGIC;
		word2_en		: STD_LOGIC;
		align_left		: STD_LOGIC;
		load_signed		: STD_LOGIC;
		mul_access		: STD_LOGIC;
		mul_hilo_we		: STD_LOGIC;
		mul_mul_divn	: STD_LOGIC;
		mul_start		: STD_LOGIC;
		mul_s_un		: STD_LOGIC;
		mul_hilo_sel	: STD_LOGIC;
		exc_break		: STD_LOGIC;
		exc_syscall		: STD_LOGIC;
		exc_illegal		: STD_LOGIC;
		except_en		: STD_LOGIC;
		alu_exc_en		: STD_LOGIC;
	end record;

	type ID_t is record
		nop			: STD_LOGIC;
		exc			: std_logic;
		IR			: word_t;
		op			: op_t;
		pcn			: word_t;
		epc			: word_t;
		jimm32		: word_t;
		bimm18		: word_t;
		imm			: word_t;
		shamt		: shamt_t;
		ctrl		: ctrl_lines_t;
		reg_write	: STD_LOGIC;
		reg_a_rptr	: reg_ptr_t;
		reg_b_rptr	: reg_ptr_t;
		reg_a		: word_t;
		reg_b		: word_t;
		events		: event_t;
	end record;
	
	type EX_t is record
		nop			: STD_LOGIC;
		exc			: std_logic;
		IR			: word_t;
		op			: op_t;
		pcn			: word_t;
		epc			: word_t;
		reg_a		: word_t;
		reg_b		: word_t;
		ctrl		: ctrl_lines_t;
		shift_ctrl	: shift_ctrl_t;
		alu_op1		: word_t;
		alu_op2		: word_t;
		alu_op2_s	: word_t;
		alu_flags	: alu_flags_t;
		result		: word_t;
		reg_write	: STD_LOGIC;
		wreg_we		: STD_LOGIC;
		reg_wptr	: reg_ptr_t;
		reg_a_rptr	: reg_ptr_t;
		reg_b_rptr	: reg_ptr_t;
		va			: word_t;
		pa_off		: unsigned(1 downto 0);
		events_in	: event_t;
		events		: event_t;
	end record;
	
	type MEM_t is record
		nop			: STD_LOGIC;
		exc			: std_logic;
		op			: op_t;
		pcn			: word_t;
		epc			: word_t;
		ctrl		: ctrl_lines_t;
		ex_result	: word_t;
		reg_wptr	: reg_ptr_t;
		wreg_we		: STD_LOGIC;
		data		: word_t;
		va			: word_t;
		pa_off		: unsigned(1 downto 0);
		events_in	: event_t;
		events		: event_t;
	end record;
	
	type WB_t is record
		nop			: STD_LOGIC;
		exc			: std_logic;
		events		: event_t;
		op			: op_t;
		reg_wptr	: reg_ptr_t;
		wreg_we		: STD_LOGIC;
		data		: word_t;
		bd			: STD_LOGIC;
	end record;

	
	type rom_special_t is array (0 to 2**func_t'length-1) of ctrl_lines_t;
	type rom_regimm_t is array (0 to 2**reg_ptr_t'length-1) of ctrl_lines_t;
	type rom_opcode_t is array (0 to 2**opcode_t'length-1) of ctrl_lines_t;

	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;

	function lg2(x : natural) return natural;
	function po2(x : natural) return natural;

end mips_types;

--------------------------------------------------------------------------
package body mips_types 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";

--------------------------------------------------------------------------
	function lg2(x : natural) return natural is
	begin
		return natural(ceil(log2(real(x))));
	end lg2;	
	
--------------------------------------------------------------------------
	function po2(x : natural) return natural is
	begin

		return 2**lg2(x);
	end po2;	

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

