-- © 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;
                    use ieee.numeric_std.all;
LIBRARY ibm;        
                    use ibm.std_ulogic_support.all;
                    use ibm.std_ulogic_function_support.all;
LIBRARY support;    
                    use support.power_logic_pkg.all;
library tri;
use tri.tri_latches_pkg.all;

entity xuq_alu_rol64 is generic(expand_type: integer := 2 );    port (
        word            :in  std_ulogic_vector(0 to 1);  -- PPC word mode rotate <2 copies>
        right           :in  std_ulogic_vector(0 to 2);  -- emulate a shift right with a rotate left <2 copies>
        amt             :in  std_ulogic_vector(0 to 5);  -- shift amout [0:63]
        data_i          :in  std_ulogic_vector(0 to 63); -- data to be shifted
        res_rot         :out std_ulogic_vector(0 to 63)  -- mask shows which rotator bits to keep in the result.
);

-- synopsys translate_off
-- synopsys translate_on

end xuq_alu_rol64;

architecture xuq_alu_rol64 of xuq_alu_rol64 is
    constant tiup                       : std_ulogic := '1';
    constant tidn                       : std_ulogic := '0';

    signal right_b :std_ulogic_vector(0 to 2);
    signal amt_b   :std_ulogic_vector(0 to 5);
    signal word_b :std_ulogic_vector(0 to 1);
    signal word_bus, word_bus_b :std_ulogic_vector(0 to 31 );
    signal data_i0_adj_b :std_ulogic_vector(0 to 31 );
    signal data_i_adj, data_i1_adj_b :std_ulogic_vector(0 to 63);

    signal rolx16_0, rolx16_1, rolx16_2, rolx16_3           :std_ulogic_vector(0 to 63);
    signal rolx04_0, rolx04_1, rolx04_2, rolx04_3           :std_ulogic_vector(0 to 63);
    signal rolx01_0, rolx01_1, rolx01_2, rolx01_3, rolx01_4 :std_ulogic_vector(0 to 63);
    signal shd16, shd16_0_b, shd16_1_b :std_ulogic_vector(0 to 63) ;
    signal shd04, shd04_0_b, shd04_1_b :std_ulogic_vector(0 to 63) ;
    signal        shd01_0_b, shd01_1_b, shd01_2_b :std_ulogic_vector(0 to 63) ;
    signal x16_lft_b, x16_rgt_b, lftx16 :std_ulogic_vector(0 to 3);
    signal x04_lft_b, x04_rgt_b, lftx04 :std_ulogic_vector(0 to 3);
    signal x01_lft_b, x01_rgt_b         :std_ulogic_vector(0 to 3);
    signal                       lftx01 :std_ulogic_vector(0 to 4);


   signal lftx01_inv, lftx01_buf0, lftx01_buf1 :std_ulogic_vector(0 to 4);
   signal lftx04_inv, lftx04_buf0, lftx04_buf1 :std_ulogic_vector(0 to 3);
   signal lftx16_inv, lftx16_buf0, lftx16_buf1 :std_ulogic_vector(0 to 3);
   signal lftx16_0_bus, lftx16_1_bus, lftx16_2_bus, lftx16_3_bus               :std_ulogic_vector(0 to 63);
   signal lftx04_0_bus, lftx04_1_bus, lftx04_2_bus, lftx04_3_bus               :std_ulogic_vector(0 to 63);
   signal lftx01_0_bus, lftx01_1_bus, lftx01_2_bus, lftx01_3_bus, lftx01_4_bus :std_ulogic_vector(0 to 63);



begin

   -- -------------------------------------------------------------
   -- how the ppc emulates a rot32 using rot64 hardware.
   -- this makes the wrapping corect for the low order 32 bits.
   -- upper 32 result bits a garbage
   ----------------------------------------------------------------

   word_b(0 to 1) <= not word(0 to 1) ;

   word_bus_b( 0 to 15) <= (0  to 15 => word_b(0) );
   word_bus_b(16 to 31) <= (16 to 31 => word_b(1) );
   word_bus  ( 0 to 15) <= (0  to 15 => word  (0) );
   word_bus  (16 to 31) <= (16 to 31 => word  (1) );


   u_dhi0adj: data_i0_adj_b(0 to 31) <= not( data_i( 0 to 31) and  word_bus_b(0 to 31) );
   u_dhi1adj: data_i1_adj_b(0 to 31) <= not( data_i(32 to 63) and  word_bus  (0 to 31) );
   u_dhiadj:  data_i_adj   (0 to 31) <= not( data_i0_adj_b(0 to 31) and data_i1_adj_b(0 to 31) );

   u_dlo0adj: data_i1_adj_b(32 to 63) <= not( data_i(32 to 63)  );
   u_dloadj:  data_i_adj   (32 to 63) <= not( data_i1_adj_b(32 to 63) );

   -----------------------------------------------------------------
   -- decoder without the adder
   -----------------------------------------------------------------
   --rotate right by [n] == rotate_left by width -[n] == !n + 1

   right_b(0 to 2) <= not right(0 to 2) ;
   u_amt_b:  amt_b(0 to 5)   <= not amt(0 to 5) ;

   u_x16lft_0: x16_lft_b(0) <= not( right_b(0) and amt_b(0) and amt_b(1) );
   u_x16lft_1: x16_lft_b(1) <= not( right_b(0) and amt_b(0) and amt  (1) );
   u_x16lft_2: x16_lft_b(2) <= not( right_b(0) and amt  (0) and amt_b(1) );
   u_x16lft_3: x16_lft_b(3) <= not( right_b(0) and amt  (0) and amt  (1) );

   u_x16rgt_0: x16_rgt_b(0) <= not( right  (0) and amt_b(0) and amt_b(1) );
   u_x16rgt_1: x16_rgt_b(1) <= not( right  (0) and amt_b(0) and amt  (1) );
   u_x16rgt_2: x16_rgt_b(2) <= not( right  (0) and amt  (0) and amt_b(1) );
   u_x16rgt_3: x16_rgt_b(3) <= not( right  (0) and amt  (0) and amt  (1) );

   u_lftx16_0: lftx16(0) <=  not( x16_lft_b(0) and x16_rgt_b(3) ) ;
   u_lftx16_1: lftx16(1) <=  not( x16_lft_b(1) and x16_rgt_b(2) ) ;
   u_lftx16_2: lftx16(2) <=  not( x16_lft_b(2) and x16_rgt_b(1) ) ;
   u_lftx16_3: lftx16(3) <=  not( x16_lft_b(3) and x16_rgt_b(0) ) ;



   u_x04lft_0: x04_lft_b(0) <= not( right_b(1) and amt_b(2) and amt_b(3) );
   u_x04lft_1: x04_lft_b(1) <= not( right_b(1) and amt_b(2) and amt  (3) );
   u_x04lft_2: x04_lft_b(2) <= not( right_b(1) and amt  (2) and amt_b(3) );
   u_x04lft_3: x04_lft_b(3) <= not( right_b(1) and amt  (2) and amt  (3) );

   u_x04rgt_0: x04_rgt_b(0) <= not( right  (1) and amt_b(2) and amt_b(3) );
   u_x04rgt_1: x04_rgt_b(1) <= not( right  (1) and amt_b(2) and amt  (3) );
   u_x04rgt_2: x04_rgt_b(2) <= not( right  (1) and amt  (2) and amt_b(3) );
   u_x04rgt_3: x04_rgt_b(3) <= not( right  (1) and amt  (2) and amt  (3) );

   u_lftx04_0: lftx04(0) <=  not( x04_lft_b(0) and x04_rgt_b(3) ) ;
   u_lftx04_1: lftx04(1) <=  not( x04_lft_b(1) and x04_rgt_b(2) ) ;
   u_lftx04_2: lftx04(2) <=  not( x04_lft_b(2) and x04_rgt_b(1) ) ;
   u_lftx04_3: lftx04(3) <=  not( x04_lft_b(3) and x04_rgt_b(0) ) ;



   u_x01lft_0: x01_lft_b(0) <= not( right_b(2) and amt_b(4) and amt_b(5) );
   u_x01lft_1: x01_lft_b(1) <= not( right_b(2) and amt_b(4) and amt  (5) );
   u_x01lft_2: x01_lft_b(2) <= not( right_b(2) and amt  (4) and amt_b(5) );
   u_x01lft_3: x01_lft_b(3) <= not( right_b(2) and amt  (4) and amt  (5) );

   u_x01rgt_0: x01_rgt_b(0) <= not( right  (2) and amt_b(4) and amt_b(5) );
   u_x01rgt_1: x01_rgt_b(1) <= not( right  (2) and amt_b(4) and amt  (5) );
   u_x01rgt_2: x01_rgt_b(2) <= not( right  (2) and amt  (4) and amt_b(5) );
   u_x01rgt_3: x01_rgt_b(3) <= not( right  (2) and amt  (4) and amt  (5) );

   u_lftx01_0: lftx01(0) <=  not( x01_lft_b(0)                  ) ; -- the shift is like the +1
   u_lftx01_1: lftx01(1) <=  not( x01_lft_b(1) and x01_rgt_b(3) ) ;
   u_lftx01_2: lftx01(2) <=  not( x01_lft_b(2) and x01_rgt_b(2) ) ;
   u_lftx01_3: lftx01(3) <=  not( x01_lft_b(3) and x01_rgt_b(1) ) ;
   u_lftx01_4: lftx01(4) <=  not(                  x01_rgt_b(0) ) ;

   u_lftx16_inv:   lftx16_inv (0 to 3) <= not( lftx16    (0 to 3) );
   u_lftx16_buf0:  lftx16_buf0(0 to 3) <= not( lftx16_inv(0 to 3) );
   u_lftx16_buf1:  lftx16_buf1(0 to 3) <= not( lftx16_inv(0 to 3) );

   u_lftx04_inv:   lftx04_inv (0 to 3) <= not( lftx04    (0 to 3) );
   u_lftx04_buf0:  lftx04_buf0(0 to 3) <= not( lftx04_inv(0 to 3) );
   u_lftx04_buf1:  lftx04_buf1(0 to 3) <= not( lftx04_inv(0 to 3) );

   u_lftx01_inv:   lftx01_inv (0 to 4) <= not( lftx01    (0 to 4) );
   u_lftx01_buf0:  lftx01_buf0(0 to 4) <= not( lftx01_inv(0 to 4) );
   u_lftx01_buf1:  lftx01_buf1(0 to 4) <= not( lftx01_inv(0 to 4) );


   lftx16_0_bus( 0 to 31) <= ( 0 to 31 => lftx16_buf0(0) );
   lftx16_0_bus(32 to 63) <= (32 to 63 => lftx16_buf1(0) );
   lftx16_1_bus( 0 to 31) <= ( 0 to 31 => lftx16_buf0(1) );
   lftx16_1_bus(32 to 63) <= (32 to 63 => lftx16_buf1(1) );
   lftx16_2_bus( 0 to 31) <= ( 0 to 31 => lftx16_buf0(2) );
   lftx16_2_bus(32 to 63) <= (32 to 63 => lftx16_buf1(2) );
   lftx16_3_bus( 0 to 31) <= ( 0 to 31 => lftx16_buf0(3) );
   lftx16_3_bus(32 to 63) <= (32 to 63 => lftx16_buf1(3) );

   lftx04_0_bus( 0 to 31) <= ( 0 to 31 => lftx04_buf0(0) );
   lftx04_0_bus(32 to 63) <= (32 to 63 => lftx04_buf1(0) );
   lftx04_1_bus( 0 to 31) <= ( 0 to 31 => lftx04_buf0(1) );
   lftx04_1_bus(32 to 63) <= (32 to 63 => lftx04_buf1(1) );
   lftx04_2_bus( 0 to 31) <= ( 0 to 31 => lftx04_buf0(2) );
   lftx04_2_bus(32 to 63) <= (32 to 63 => lftx04_buf1(2) );
   lftx04_3_bus( 0 to 31) <= ( 0 to 31 => lftx04_buf0(3) );
   lftx04_3_bus(32 to 63) <= (32 to 63 => lftx04_buf1(3) );

   lftx01_0_bus( 0 to 31) <= ( 0 to 31 => lftx01_buf0(0) );
   lftx01_0_bus(32 to 63) <= (32 to 63 => lftx01_buf1(0) );
   lftx01_1_bus( 0 to 31) <= ( 0 to 31 => lftx01_buf0(1) );
   lftx01_1_bus(32 to 63) <= (32 to 63 => lftx01_buf1(1) );
   lftx01_2_bus( 0 to 31) <= ( 0 to 31 => lftx01_buf0(2) );
   lftx01_2_bus(32 to 63) <= (32 to 63 => lftx01_buf1(2) );
   lftx01_3_bus( 0 to 31) <= ( 0 to 31 => lftx01_buf0(3) );
   lftx01_3_bus(32 to 63) <= (32 to 63 => lftx01_buf1(3) );
   lftx01_4_bus( 0 to 31) <= ( 0 to 31 => lftx01_buf0(4) );
   lftx01_4_bus(32 to 63) <= (32 to 63 => lftx01_buf1(4) );



   -----------------------------------------------------------------
   -- the shifter
   -----------------------------------------------------------------


   rolx16_0(0 to 63) <= data_i_adj( 0 to 63)   ;
   rolx16_1(0 to 63) <= data_i_adj(16 to 63) & data_i_adj(0 to 15) ;
   rolx16_2(0 to 63) <= data_i_adj(32 to 63) & data_i_adj(0 to 31) ;
   rolx16_3(0 to 63) <= data_i_adj(48 to 63) & data_i_adj(0 to 47) ;


   u_shd16_0: shd16_0_b(0 to 63) <= not( ( lftx16_0_bus(0 to 63) and rolx16_0(0 to 63) )  or 
                                         ( lftx16_1_bus(0 to 63) and rolx16_1(0 to 63) )  ); 
   u_shd16_1: shd16_1_b(0 to 63) <= not( ( lftx16_2_bus(0 to 63) and rolx16_2(0 to 63) )  or 
                                         ( lftx16_3_bus(0 to 63) and rolx16_3(0 to 63) )  ); 
   u_shd16:   shd16    (0 to 63) <= not( shd16_0_b(0 to 63) and shd16_1_b(0 to 63) );


   rolx04_0(0 to 63) <= shd16( 0 to 63);
   rolx04_1(0 to 63) <= shd16( 4 to 63) & shd16( 0 to  3);
   rolx04_2(0 to 63) <= shd16( 8 to 63) & shd16( 0 to  7);
   rolx04_3(0 to 63) <= shd16(12 to 63) & shd16( 0 to 11);

   u_shd04_0: shd04_0_b(0 to 63) <= not( ( lftx04_0_bus(0 to 63) and rolx04_0(0 to 63) )  or 
                                         ( lftx04_1_bus(0 to 63) and rolx04_1(0 to 63) )  ); 
   u_shd04_1: shd04_1_b(0 to 63) <= not( ( lftx04_2_bus(0 to 63) and rolx04_2(0 to 63) )  or 
                                         ( lftx04_3_bus(0 to 63) and rolx04_3(0 to 63) )  ); 
   u_shd04:   shd04    (0 to 63) <= not( shd04_0_b(0 to 63) and shd04_1_b(0 to 63) );

   rolx01_0(0 to 63) <= shd04(0 to 63);
   rolx01_1(0 to 63) <= shd04(1 to 63) & shd04( 0 );
   rolx01_2(0 to 63) <= shd04(2 to 63) & shd04( 0 to 1);
   rolx01_3(0 to 63) <= shd04(3 to 63) & shd04( 0 to 2);
   rolx01_4(0 to 63) <= shd04(4 to 63) & shd04( 0 to 3);
    


   u_shd01_0: shd01_0_b(0 to 63) <= not( ( lftx01_0_bus(0 to 63) and rolx01_0(0 to 63) )  or 
                                         ( lftx01_1_bus(0 to 63) and rolx01_1(0 to 63) )  ); 
   u_shd01_1: shd01_1_b(0 to 63) <= not( ( lftx01_2_bus(0 to 63) and rolx01_2(0 to 63) )  or 
                                         ( lftx01_3_bus(0 to 63) and rolx01_3(0 to 63) )  ); 
   u_shd01_2: shd01_2_b(0 to 63) <= not(   lftx01_4_bus(0 to 63) and rolx01_4(0 to 63)    ); 
   u_shd01:   res_rot  (0 to 63) <= not( shd01_0_b(0 to 63) and shd01_1_b(0 to 63) and shd01_2_b(0 to 63) );

end architecture xuq_alu_rol64;
