-- © IBM Corp. 2020
-- Licensed under the Apache License, Version 2.0 (the "License"), as modified by
-- the terms below; you may not use the files in this repository except in
-- compliance with the License as modified.
-- You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
--
-- Modified Terms:
--
--    1) For the purpose of the patent license granted to you in Section 3 of the
--    License, the "Work" hereby includes implementations of the work of authorship
--    in physical form.
--
--    2) Notwithstanding any terms to the contrary in the License, any licenses
--    necessary for implementation of the Work that are available from OpenPOWER
--    via the Power ISA End User License Agreement (EULA) are explicitly excluded
--    hereunder, and may be obtained from OpenPOWER under the terms and conditions
--    of the EULA.  
--
-- Unless required by applicable law or agreed to in writing, the reference design
-- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
-- for the specific language governing permissions and limitations under the License.
-- 
-- Additional rights, including the ability to physically implement a softcore that
-- is compliant with the required sections of the Power ISA Specification, are
-- available at no cost under the terms of the OpenPOWER Power ISA EULA, which can be
-- obtained (along with the Power ISA) here: https://openpowerfoundation.org. 

library ieee;
use ieee.std_logic_1164.all;

package iuq_pkg is
  subtype EFF_IFAR      is std_ulogic_vector( 0 to 61);
  subtype REAL_IFAR     is std_ulogic_vector(22 to 61);
  subtype EFF_DATA_ADD  is std_ulogic_vector( 0 to 61);
  subtype REAL_DATA_ADD is std_ulogic_vector(18 to 61);

  function ib(x : std_ulogic) return boolean;

  -- all functions assume to logic vector strand direction
  function barrel_left(a : std_ulogic_vector; s : std_ulogic_vector) return std_ulogic_vector;
  function barrel_right(a : std_ulogic_vector; s : std_ulogic_vector) return std_ulogic_vector;
  function pri_enc(a : std_ulogic_vector) return std_ulogic_vector;
  function shift_left(a : std_ulogic_vector; s : std_ulogic_vector) return std_ulogic_vector;
  function shift_right(a : std_ulogic_vector; s : std_ulogic_vector) return std_ulogic_vector;
  function mask_left(a : std_ulogic_vector ) return std_ulogic_vector;
  function mask_right(a : std_ulogic_vector ) return std_ulogic_vector;

  function shift_leftx1B(a : std_ulogic_vector; s: std_ulogic_vector) return std_ulogic_vector;
  function shift_rightx1B(a : std_ulogic_vector; s: std_ulogic_vector) return std_ulogic_vector;

  procedure zeros(signal x : out std_ulogic);
  procedure zeros(signal x : out std_ulogic_vector);

  function encode_4to2( a : std_ulogic_vector(0 to 3) ) return std_ulogic_vector;
  function encode_8to3( a : std_ulogic_vector(0 to 7) ) return std_ulogic_vector;
  function encode_16to4(a : std_ulogic_vector(0 to 15)) return std_ulogic_vector;

   -- Instruction type definition
   type PPC_INSTR is record
      vld                 : std_ulogic;
      instr               : std_ulogic_vector(0 to 31);

      ta                  : std_ulogic_vector(0 to 6);
      ta_vld              : std_ulogic;
      ta_typ              : std_ulogic_vector(0 to 1);

      s1                  : std_ulogic_vector(0 to 6);
      s1_vld              : std_ulogic;
      s1_typ              : std_ulogic_vector(0 to 1);

      s2                  : std_ulogic_vector(0 to 6);
      s2_vld              : std_ulogic;
      s2_typ              : std_ulogic_vector(0 to 1);

      s3                  : std_ulogic_vector(0 to 6);
      s3_vld              : std_ulogic;
      s3_typ              : std_ulogic_vector(0 to 1);

      isFxuIssue          : std_ulogic;
      isVsuIssue          : std_ulogic;

      --EX2_exit			  : std_ulogic;
      EX4_exit		  : std_ulogic;
      EX7_exit            : std_ulogic;

      isLWARX   	  : std_ulogic;
      isSTWCX        	  : std_ulogic;
      is_vcrs      	  : std_ulogic;

      pred_update         : std_ulogic;
      pred_taken_cnt      : std_ulogic_vector(0 to 1);

      isINVALID_OP        : std_ulogic;
      isATTN		  : std_ulogic;

      UpdatesLR           : std_ulogic;
      UpdatesCR           : std_ulogic;
      UpdatesCTR          : std_ulogic;
      UsesLR              : std_ulogic;
      UsesCR              : std_ulogic;
      UsesCTR             : std_ulogic;

      is_st               : std_ulogic;
      is_ld               : std_ulogic;
      ibat_err            : std_ulogic;

      tid                 : std_ulogic_vector(0 to 3);

      ifar                : EFF_IFAR;
      bta                 : EFF_IFAR;

   end record;

   function TO_STLV ( x : PPC_INSTR ) return std_ulogic_vector;
   function TO_PPCI ( x : std_ulogic_vector(0 to 97+2*EFF_IFAR'length) ) return PPC_INSTR;


end iuq_pkg;

package body iuq_pkg is

  function ib(x : std_ulogic) return boolean
  is
  begin
    return(x = '1');
  end ib;


   -- These functions all assume TO direction logic
   function barrel_left(a : std_ulogic_vector; s : std_ulogic_vector) return std_ulogic_vector
   is
      variable result : std_ulogic_vector(a'left to a'right);
      variable i : integer := 0;
   begin

      result := a;
      for i in s'left to s'right loop
         if s(i) = '1' then
            exit;
         end if;
         result := result(result'left+1 to result'right)&result(result'left);
      end loop;

      return( result );
   end barrel_left;

   function barrel_right(a : std_ulogic_vector; s : std_ulogic_vector) return std_ulogic_vector
   is
      variable result : std_ulogic_vector(a'left to a'right);
   begin

      result := a;
      for i in a'left to a'right loop
         if s(i) = '1' then
            exit;
         end if;
         result := result(result'right) & result(result'left to result'right-1);
      end loop;

      return( result );
   end barrel_right;


   function pri_enc(a : std_ulogic_vector) return std_ulogic_vector
   is
      variable result : std_ulogic_vector(a'left to a'right);
   begin
      result := (others => '0');

      for i in a'left to a'right loop
         if a(i) = '1' then
            result(i) := '1';
            exit;
         end if;
      end loop;

      return( result );
   end pri_enc;

   function shift_left(a : std_ulogic_vector; s : std_ulogic_vector) return std_ulogic_vector is
      variable result : std_ulogic_vector(a'left to a'right);
   begin
      result := a;
      for i in s'left to s'right loop
         if s(i) = '1' then
            exit;
         end if;
         result := result( result'left+1 to result'right)&'0';
      end loop;
      return(result);
   end shift_left;

   function shift_right(a : std_ulogic_vector; s : std_ulogic_vector) return std_ulogic_vector is
      variable result : std_ulogic_vector(a'left to a'right);
   begin
      result := a;
      for i in s'left to s'right loop
         if s(i) = '1' then
            exit;
         end if;
         result := '0'&result(result'left to result'right-1);
      end loop;
      return(result);
   end shift_right;

   function mask_left(a : std_ulogic_vector ) return std_ulogic_vector is
      variable result : std_ulogic_vector(a'left to a'right);
      variable flag : integer := 0;
   begin
      for i in a'right downto a'left loop
         if ((a(i) = '1') or (flag = 1)) then
            result(i) := '1';
            flag := 1;
         else
            result(i) := a(i);
         end if;
      end loop;
      return( result );
   end mask_left;

   function mask_right(a : std_ulogic_vector ) return std_ulogic_vector is
      variable result : std_ulogic_vector(a'left to a'right);
      variable flag : integer := 0;
   begin
      for i in a'left to a'right loop
         if ((a(i) = '1') or (flag = 1)) then
            result(i) := '1';
            flag := 1;
         else
            result(i) := a(i);
         end if;
      end loop;
      return( result );
   end mask_right;

   function encode_4to2( a : std_ulogic_vector(0 to 3) ) return std_ulogic_vector is
      variable result : std_ulogic_vector(0 to 1);
   begin
      case a is
         when "1000" => result := "00";
         when "0100" => result := "01";
         when "0010" => result := "10";
         when "0001" => result := "11";
         when others => result := "00";
      end case;
      return(result);
   end encode_4to2;

   function encode_8to3( a : std_ulogic_vector(0 to 7) ) return std_ulogic_vector is
      variable result : std_ulogic_vector(0 to 2);
   begin
      case a is
         when "10000000" => result := "000";
         when "01000000" => result := "001";
         when "00100000" => result := "010";
         when "00010000" => result := "011";
         when "00001000" => result := "100";
         when "00000100" => result := "101";
         when "00000010" => result := "110";
         when "00000001" => result := "111";
         when others => result := "000";
      end case;
      return(result);
   end encode_8to3;

   function encode_16to4(a : std_ulogic_vector(0 to 15)) return std_ulogic_vector is
      variable result : std_ulogic_vector(0 to 3);
   begin
      case a is
         when "1000000000000000" => result := "0000";
         when "0100000000000000" => result := "0001";
         when "0010000000000000" => result := "0010";
         when "0001000000000000" => result := "0011";
         when "0000100000000000" => result := "0100";
         when "0000010000000000" => result := "0101";
         when "0000001000000000" => result := "0110";
         when "0000000100000000" => result := "0111";
         when "0000000010000000" => result := "1000";
         when "0000000001000000" => result := "1001";
         when "0000000000100000" => result := "1010";
         when "0000000000010000" => result := "1011";
         when "0000000000001000" => result := "1100";
         when "0000000000000100" => result := "1101";
         when "0000000000000010" => result := "1110";
         when "0000000000000001" => result := "1111";
         when others => result := "0000";
      end case;
      return(result);
   end encode_16to4;

   -- you better make sure that a and result are multiples of 1B with s being a strand representing the number of bytes to shift
   function shift_leftx1B(a : std_ulogic_vector; s: std_ulogic_vector) return std_ulogic_vector is
      variable result : std_ulogic_vector(a'left to a'right);
   begin
      result := a;
      for i in s'left to s'right loop
         if s(i) = '1' then
            exit;
         end if;
         result := result( result'left+8 to result'right)&"00000000";
      end loop;
      return(result);
   end shift_leftx1B;

   function shift_rightx1B(a : std_ulogic_vector; s: std_ulogic_vector) return std_ulogic_vector is
         variable result : std_ulogic_vector(a'left to a'right);
   begin
      result := a;
      for i in s'left to s'right loop
         if s(i) = '1' then
            exit;
         end if;
         result := "00000000"&result(result'left to result'right-8);
      end loop;
      return(result);
   end shift_rightx1B;


  	--------------------------------------------------------------------
  	--- PPC_INSTR manipulation
  	--------------------------------------------------------------------


		-- x bit TID
	  	function TO_STLV ( x : PPC_INSTR ) return std_ulogic_vector
	  	is
	    	variable result : std_ulogic_vector(0 to 97+2*EFF_IFAR'length);
	   	begin
	      	result :=   x.vld             &
	                    x.instr           &
	                    x.ta              &
	                    x.ta_vld          &
	                    x.ta_typ          &
	                    x.s1              &
	                    x.s1_vld          &
	                    x.s1_typ          &
	                    x.s2              &
	                    x.s2_vld          &
	                    x.s2_typ          &
	                    x.s3              &
	                    x.s3_vld          &
	                    x.s3_typ          &
	                    x.isFxuIssue      &
	                    x.isVsuIssue      &
	                    x.EX4_exit        &
	                    x.EX7_exit        &
	                    x.isLWARX         &
	                    x.isSTWCX         &
	                    x.is_vcrs         &
	                    x.pred_update     &
	                    x.pred_taken_cnt  &
	                    x.isINVALID_OP    &
	                    x.isATTN          &
	                    x.UpdatesLR       &
	                    x.UpdatesCR       &
	                    x.UpdatesCTR      &
	                    x.UsesLR          &
	                    x.UsesCR          &
	                    x.UsesCTR         &
	                    x.is_st           &
	                    x.is_ld           &
                            x.ibat_err        &
	                    x.tid             &
	                    x.ifar            &
	                    x.bta
	                    ;

	      	return result;
	   	end TO_STLV;

	   	-- x bit TID
	function TO_PPCI ( x : std_ulogic_vector(0 to 97+2*EFF_IFAR'length) ) return PPC_INSTR is
   		variable result : PPC_INSTR;
	begin

        result.vld                 := x(0);
        result.instr               := x(1 to 32);
        result.ta                  := x(33 to 39);
        result.ta_vld              := x(40);
        result.ta_typ              := x(41 to 42);
        result.s1                  := x(43 to 49);
        result.s1_vld              := x(50);
        result.s1_typ              := x(51 to 52);
        result.s2                  := x(53 to 59);
        result.s2_vld              := x(60);
        result.s2_typ              := x(61 to 62);
        result.s3                  := x(63 to 69);
        result.s3_vld              := x(70);
        result.s3_typ              := x(71 to 72);
        result.isFxuIssue          := x(73);
        result.isVsuIssue          := x(74);
        result.EX4_exit            := x(75);
        result.EX7_exit            := x(76);
        result.isLWARX             := x(77);
	result.isSTWCX        	   := x(78);
	result.is_vcrs      	   := x(79);
        result.pred_update         := x(80);
        result.pred_taken_cnt      := x(81 to 82);
        result.isINVALID_OP        := x(83);
        result.isATTN		   := x(84);
        result.UpdatesLR           := x(85);
        result.UpdatesCR           := x(86);
        result.UpdatesCTR          := x(87);
        result.UsesLR              := x(88);
        result.UsesCR              := x(89);
        result.UsesCTR             := x(90);
        result.is_st               := x(91);
        result.is_ld               := x(92);
        result.ibat_err            := x(93);
        result.tid                 := x(94 to 97);
        result.ifar                := x(98 to 97+EFF_IFAR'length);
        result.bta                 := x(98+EFF_IFAR'length to 97+2*EFF_IFAR'length);

        return result;
	end TO_PPCI;

procedure zeros(signal x : out std_ulogic)
  is
  begin
    x <= '0';
  end zeros;

procedure zeros(signal x : out std_ulogic_vector)
  is
  begin
    for i in x'range loop
      x(i) <= '0';
    end loop;
  end zeros;


end iuq_pkg;
