#ifdef SYSTEMC
/*
 * $Id: Operation.cpp 101 2009-01-15 17:19:08Z rosiere $
 *
 * [ Description ]
 * 
 */

#include "Behavioural/Core/Multi_Execute_loop/Execute_loop/Multi_Execute_unit/Execute_unit/Functionnal_unit/Operation/include/Operation.h"
#include "Behavioural/include/Operation.h"

namespace morpheo                    {
namespace behavioural {
namespace core {
namespace multi_execute_loop {
namespace execute_loop {
namespace multi_execute_unit {
namespace execute_unit {
namespace functionnal_unit {

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_unimplemented"
  void operation_unimplemented (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : unimplemented");

    throw ERRORMORPHEO(FUNCTION,"The operation '"+toString(op->_operation)+"' is not implemented in this Functionnal_unit");
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_add"
  void operation_l_add         (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_add");

    Tgeneral_data_t gpr1      = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2      = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);
    
    Tgeneral_data_t gpr3      = param->_mask_data & (gpr1 + gpr2);

    bool            overflow  = ovf  (param->_size_data,gpr1,gpr2,gpr3);
    bool            carry_out = carry(param->_size_data,gpr1,gpr2,gpr3);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
    op->_data_re      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_OV,overflow );
    op->_data_re      = set_flag(op->_data_re,FLAG_CY,carry_out);
    op->_exception    = (overflow==1)?EXCEPTION_ALU_RANGE:EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_addc"
  void operation_l_addc        (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_addc");

    Tgeneral_data_t carry_in   = get_flag(op->_data_rc,FLAG_CY);

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);
    Tgeneral_data_t gpr3       = param->_mask_data & (gpr1 + gpr2 + carry_in);

    bool            overflow   = ovf  (param->_size_data,gpr1,gpr2,gpr3);
    bool            carry_out  = carry(param->_size_data,gpr1,gpr2,gpr3);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
    op->_data_re      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_OV,overflow );
    op->_data_re      = set_flag(op->_data_re,FLAG_CY,carry_out);
    op->_exception    = (overflow==1)?EXCEPTION_ALU_RANGE:EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_sub"
  void operation_l_sub         (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_sub");

    Tgeneral_data_t gpr1      =                       unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2      = neg(param->_size_data,unsigned(param->_size_data,op->_data_rb));
    Tgeneral_data_t gpr3      = param->_mask_data & (gpr1 + gpr2);

    bool            overflow  = ovf  (param->_size_data,gpr1,gpr2,gpr3);
//  bool            carry_out = carry(param->_size_data,gpr1,gpr2,gpr3);
    // In ISA : l.sub don't change flag carry
    bool            carry_out  = get_flag(op->_data_rc,FLAG_CY);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
    op->_data_re      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_OV,overflow );
    op->_data_re      = set_flag(op->_data_re,FLAG_CY,carry_out);
    op->_exception    = (overflow==1)?EXCEPTION_ALU_RANGE:EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_mul"
  void operation_l_mul         (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_mul");

    Tgeneral_data_t gpr1      = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2      = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);
    Tgeneral_data_t gpr3      ;
    bool            overflow  ;
    bool            carry_out ;

    if (param->_size_data == 32)
      {
        int64_t res = static_cast<int64_t>(gpr1) * static_cast<int64_t>(gpr2);

        log_printf(TRACE,Functionnal_unit,FUNCTION,"  * res   : %llx",res);

        gpr3      = param->_mask_data & static_cast<Tgeneral_data_t>(res);
        carry_out = res != gpr3; 
        overflow  = carry_out;
      }
    else
      {
        throw ERRORMORPHEO(FUNCTION,_("64 bits multiplcation : not yet implemented\n"));
      }

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
    op->_data_re      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_OV,overflow );
    op->_data_re      = set_flag(op->_data_re,FLAG_CY,carry_out);
    op->_exception    = (overflow==1)?EXCEPTION_ALU_RANGE:EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_mulu"
  void operation_l_mulu        (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_mulu");

    Tgeneral_data_t gpr1      = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2      = unsigned(param->_size_data,op->_data_rb);
    Tgeneral_data_t gpr3      ;
    bool            overflow  ;
    bool            carry_out ;

    if (param->_size_data == 32)
      {
        uint64_t res = static_cast<uint64_t>(gpr1) * static_cast<uint64_t>(gpr2);

        log_printf(TRACE,Functionnal_unit,FUNCTION,"  * res   : %llx",res);

        gpr3      = param->_mask_data & static_cast<Tgeneral_data_t>(res);
        carry_out = res != gpr3; 
        overflow  = carry_out;
      }
    else
      {
        throw ERRORMORPHEO(FUNCTION,_("64 bits multiplcation : not yet implemented\n"));
      }

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
    op->_data_re      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_OV,overflow );
    op->_data_re      = set_flag(op->_data_re,FLAG_CY,carry_out);
    op->_exception    = (overflow==1)?EXCEPTION_ALU_RANGE:EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_div"
  void operation_l_div         (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_div");

    Tgeneral_data_t gpr1      = signed(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2      = signed(param->_size_data,op->_data_rb);
    Tgeneral_data_t gpr3      = (gpr2!=0)?(gpr1/gpr2):0;
    bool            overflow  = ovf  (param->_size_data,gpr1,gpr2,gpr3);
    bool            carry_out = (gpr2==0);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
    op->_data_re      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_OV,overflow );
    op->_data_re      = set_flag(op->_data_re,FLAG_CY,carry_out);
    op->_exception    = (overflow==1)?EXCEPTION_ALU_RANGE:EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_divu"
  void operation_l_divu        (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_divu");

    Tgeneral_data_t gpr1      = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2      = unsigned(param->_size_data,op->_data_rb);
    Tgeneral_data_t gpr3      = (gpr2!=0)?(gpr1/gpr2):0;
    bool            overflow  = ovf  (param->_size_data,gpr1,gpr2,gpr3);
    bool            carry_out = (gpr2==0);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
    op->_data_re      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_OV,overflow );
    op->_data_re      = set_flag(op->_data_re,FLAG_CY,carry_out);
    op->_exception    = (overflow==1)?EXCEPTION_ALU_RANGE:EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_and"
  void operation_l_and         (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_and");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);
    Tgeneral_data_t gpr3       = (gpr1 & gpr2);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_or"
  void operation_l_or          (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_or");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);
    Tgeneral_data_t gpr3       = (gpr1 | gpr2);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_xor"
  void operation_l_xor         (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_xor");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);
    Tgeneral_data_t gpr3       = (gpr1 ^ gpr2);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_movhi"
  void operation_l_movhi       (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_movhi");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_immediat);
    Tgeneral_data_t gpr2       = param->_mask_data & (gpr1<<16);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr2;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_cmov"
  void operation_l_cmov        (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_cmov");

    Tgeneral_data_t f_in       = get_flag(op->_data_rc,FLAG_F);

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = unsigned(param->_size_data,op->_data_rb);
    Tgeneral_data_t gpr3       = ((f_in==0)?gpr2:gpr1);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_test_f"
  void operation_l_test_f      (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_test_f");

    Tgeneral_data_t f_in       = get_flag(op->_data_rc,FLAG_F);
    Tgeneral_data_t imm        = unsigned(param->_size_data,op->_immediat);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = f_in != 0;
    op->_address      = imm;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_test_nf"
  void operation_l_test_nf     (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_test_nf");

    Tgeneral_data_t f_in       = get_flag(op->_data_rc,FLAG_F);
    Tgeneral_data_t imm        = unsigned(param->_size_data,op->_immediat);

    log_printf(TRACE,Functionnal_unit,FUNCTION,"  * data_rc     : %d",op->_data_rc);
    log_printf(TRACE,Functionnal_unit,FUNCTION,"  * f_in        : %d",f_in);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = f_in == 0;
    op->_address      = imm;

    log_printf(TRACE,Functionnal_unit,FUNCTION,"  * no_sequence : %d",op->_no_sequence);
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_jalr"
  void operation_l_jalr        (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_jalr");
    // jal  : has_imm = 1, write_rd = 1, read_rb = 0
    // jalr : has_imm = 1, write_rd = 1, read_rb = 1
    // jr   : has_imm = 0, write_rd = 0, read_rb = 1


    Tgeneral_data_t gpr        = unsigned(param->_size_data,op->_data_rb);
    Tgeneral_data_t imm        = unsigned(param->_size_data,op->_immediat);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = imm<<2; // add "00"
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 1;
    op->_address      = gpr>>2; // del "00"
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_extend_s"
  void operation_l_extend_s    (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_extend_s");

    Tgeneral_data_t imm        = unsigned(param->_size_data,op->_immediat);
    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = extend<Tgeneral_data_t>(param->_size_data, gpr1, true , imm);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr2;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_extend_z"
  void operation_l_extend_z    (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_extend_z");

    Tgeneral_data_t imm        = unsigned(param->_size_data,op->_immediat);
    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = extend<Tgeneral_data_t>(param->_size_data, gpr1, false , imm);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr2;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_sll"
  void operation_l_sll         (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_sll");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = param->_mask_shift & unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);
    Tgeneral_data_t gpr3       = shift_logic_left<Tgeneral_data_t>(param->_size_data, gpr1, gpr2);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_srl"
  void operation_l_srl         (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_srl");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = param->_mask_shift & unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);
    Tgeneral_data_t gpr3       = shift_logic_right<Tgeneral_data_t>(param->_size_data, gpr1, gpr2);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_sra"
  void operation_l_sra         (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_sra");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = param->_mask_shift & unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);
    Tgeneral_data_t gpr3       = shift_arithmetic_right<Tgeneral_data_t>(param->_size_data, gpr1, gpr2);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_ror"
  void operation_l_ror         (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_ror");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = param->_mask_shift & unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);
    Tgeneral_data_t gpr3       = rotate_right<Tgeneral_data_t>(param->_size_data, gpr1, gpr2);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr3;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_ff1"
  void operation_l_ff1         (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_ff1");

    Tgeneral_data_t gpr        = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t index;

    for (index=0; (index<param->_size_data) and (((gpr>>index)&1)!= 1); index++);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = (index==param->_size_data)?0:(index+1);
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_fl1"
  void operation_l_fl1         (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_fl1");

    Tgeneral_data_t gpr        = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t index;

    for (index=param->_size_data; (index>0) and (((gpr>>(index-1))&1)!= 1); index--);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = index;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_sfeq"
  void operation_l_sfeq        (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_sfeq");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);

    bool            f_out      = (gpr1 == gpr2);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_F,f_out);
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_sfne"
  void operation_l_sfne        (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_sfne");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);

    bool            f_out      = (gpr1 != gpr2);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_F,f_out);
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_sfgeu"
  void operation_l_sfgeu       (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_sfgeu");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);

    // Tgeneral_data_t is unsigned
    bool            f_out      = (gpr1 >= gpr2);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_F,f_out);
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_sfgtu"
  void operation_l_sfgtu       (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_sfgtu");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);

    // Tgeneral_data_t is unsigned
    bool            f_out      = (gpr1 >  gpr2);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_F,f_out);
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_sfleu"
  void operation_l_sfleu       (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_sfleu");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);

    // Tgeneral_data_t is unsigned
    bool            f_out      = (gpr1 <= gpr2);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_F,f_out);
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_sfltu"
  void operation_l_sfltu       (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_sfltu");

    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);

    // Tgeneral_data_t is unsigned
    bool            f_out      = (gpr1 <  gpr2);

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_F,f_out);
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_sfges"
  void operation_l_sfges       (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_sfges");

    Tgeneral_data_t gpr1       =   op->_data_ra;
    Tgeneral_data_t gpr2       =   (op->_has_immediat==1)?op->_immediat:op->_data_rb;
        
    bool            f_out;

    if (param->_size_data == 32)
      f_out = static_cast<int32_t>(gpr1) >= static_cast<int32_t>(gpr2);
    else
      f_out = static_cast<int64_t>(gpr1) >= static_cast<int64_t>(gpr2);

//     switch (concatenation_bool(sign(param->_size_data,gpr1),sign(param->_size_data,gpr2)))
//       {
//       case 1 /*b01*/ : f_out = 1                               ; break;
//       case 2 /*b10*/ : f_out = 0                               ; break;
//       default        : f_out = signed(param->_size_data,gpr1) >= signed(param->_size_data,gpr2); break;
//       }

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_F,f_out);
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_sfgts"
  void operation_l_sfgts       (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_sfgts");

    Tgeneral_data_t gpr1       = op->_data_ra ;
    Tgeneral_data_t gpr2       = (op->_has_immediat==1)?op->_immediat:op->_data_rb ;

    bool            f_out;

    if (param->_size_data == 32)
      f_out = static_cast<int32_t>(gpr1) >  static_cast<int32_t>(gpr2);
    else
      f_out = static_cast<int64_t>(gpr1) >  static_cast<int64_t>(gpr2);

//     switch (concatenation_bool(sign(param->_size_data,gpr1),sign(param->_size_data,gpr2)))
//       {
//       case 1 /*b01*/ : f_out = 1; break;
//       case 2 /*b10*/ : f_out = 0; break;
//       default        : f_out = signed(param->_size_data,gpr1) >  signed(param->_size_data,gpr2); break;
//       }

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_F,f_out);
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_sfles"
  void operation_l_sfles       (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_sfles");

    Tgeneral_data_t gpr1       = op->_data_ra ;
    Tgeneral_data_t gpr2       = (op->_has_immediat==1)?op->_immediat:op->_data_rb ;

    bool            f_out;

    if (param->_size_data == 32)
      f_out = static_cast<int32_t>(gpr1) <= static_cast<int32_t>(gpr2);
    else
      f_out = static_cast<int64_t>(gpr1) <= static_cast<int64_t>(gpr2);

//     switch (concatenation_bool(sign(param->_size_data,gpr1),sign(param->_size_data,gpr2)))
//       {
//       case 1 /*b01*/ : f_out = 0; break;
//       case 2 /*b10*/ : f_out = 1; break;
//       default        : f_out = signed(param->_size_data,gpr1) <= signed(param->_size_data,gpr2); break;
//       }
    
    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_F,f_out);
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_sflts"
  void operation_l_sflts       (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_sflts");

    Tgeneral_data_t gpr1       = op->_data_ra ;
    Tgeneral_data_t gpr2       = (op->_has_immediat==1)?op->_immediat:op->_data_rb;

    bool            f_out;

    if (param->_size_data == 32)
      f_out = static_cast<int32_t>(gpr1) <  static_cast<int32_t>(gpr2);
    else
      f_out = static_cast<int64_t>(gpr1) <  static_cast<int64_t>(gpr2);

//     switch (concatenation_bool(sign(param->_size_data,gpr1),sign(param->_size_data,gpr2)))
//       {
//       case 1 /*b01*/ : f_out = 0; break;
//       case 2 /*b10*/ : f_out = 1; break;
//       default        : f_out = signed(param->_size_data,gpr1) <  signed(param->_size_data,gpr2); break;
//       }

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
    op->_data_re      = set_flag(op->_data_re,FLAG_F,f_out);
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_mtspr"
  void operation_l_mtspr       (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_mtspr");

    Tgeneral_data_t imm        = unsigned(param->_size_data,op->_immediat);
    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = unsigned(param->_size_data,op->_data_rb);
    Tgeneral_data_t addr       = range<Tgeneral_data_t>(gpr1|imm,16);

    Texception_t    exception;

    // HACK : use the field num_reg_re to pass the SM and SUMRA flag.
    Tcontrol_t      flag_SM    = get_flag(op->_num_reg_re, 0x1);
    Tcontrol_t      flag_SUMRA = get_flag(op->_num_reg_re, 0x2);

    spr_address_t   spr_addr   = reg->_spr_access_mode->translate_address(addr);

    // Test if this group is implemented in this functionnal_unit
    if (reg->_spr_access_mode->valid(spr_addr))
      {
	if (reg->_spr_access_mode->write(spr_addr, flag_SM, flag_SUMRA))
	  {
	    reg->_i_write_spr         = true;
	    reg->_access_num_group    = spr_addr._group;
	    reg->_access_num_register = spr_addr._register;
	    reg->_spr_old             = reg->_spr[spr_addr._group][spr_addr._register];
	    reg->_spr[spr_addr._group][spr_addr._register] = gpr2;

	    exception = EXCEPTION_ALU_NONE;
	  }
	else
	  {
	    exception = EXCEPTION_ALU_SPR_ACCESS_INVALID;
	  }
      }
    else
      {
	exception = EXCEPTION_ALU_SPR_ACCESS_MUST_WRITE;
      }
    
    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr2; // data_rb
  //op->_data_re      = 0;
    op->_exception    = exception;
    op->_no_sequence  = 0;
    op->_address      = addr; // data_ra or imm
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_mfspr"
  void operation_l_mfspr       (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_mfspr");

    Tgeneral_data_t imm        = unsigned(param->_size_data,op->_immediat);
    Tgeneral_data_t gpr1       = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2       = 0;
    Tgeneral_data_t addr       = range<Tgeneral_data_t>(gpr1|imm,16);

    Texception_t    exception;

    // HACK : use the field num_reg_re to pass the SM and SUMRA flag.
    Tcontrol_t      flag_SM    = get_flag(op->_num_reg_re, 0x1);
    Tcontrol_t      flag_SUMRA = get_flag(op->_num_reg_re, 0x2);

    spr_address_t   spr_addr   = reg->_spr_access_mode->translate_address(addr);
    
    // Test if this group is implemented in this functionnal_unit
    if (reg->_spr_access_mode->valid(spr_addr))
      {
	if (reg->_spr_access_mode->read(spr_addr, flag_SM, flag_SUMRA))
	  {
	    reg->_i_read_spr          = true;
	    reg->_access_num_group    = spr_addr._group;
	    reg->_access_num_register = spr_addr._register;
	    
	    gpr2 = reg->_spr[spr_addr._group][spr_addr._register];

	    exception = EXCEPTION_ALU_NONE;
	  }
	else
	  {
	    exception = EXCEPTION_ALU_SPR_ACCESS_INVALID;
	  }
      }
    else
      {
	exception = EXCEPTION_ALU_SPR_ACCESS_MUST_READ;
      }

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_write_rd     = (exception == EXCEPTION_ALU_NONE);
    op->_data_rd      = gpr2; // spr
  //op->_data_re      = 0;
    op->_exception    = exception;
    op->_no_sequence  = 0;
    op->_address      = addr; // data_ra or imm
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_macrc"
  void operation_l_macrc        (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_macrc");

    Tgeneral_data_t gpr = static_cast<Tgeneral_data_t>(range<uint64_t>((static_cast<uint64_t>(reg->_spr[GROUP_MAC][SPR_MACHI]) << 32) | static_cast<uint64_t>(reg->_spr[GROUP_MAC][SPR_MACLO]), param->_size_data));

    reg->_spr[GROUP_MAC][SPR_MACLO] = 0;
    reg->_spr[GROUP_MAC][SPR_MACHI] = 0;

    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
    op->_data_rd      = gpr;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_mac"
  void operation_l_mac          (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_mac");

    Tgeneral_data_t gpr1 = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2 = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);
    uint64_t        temp = range<uint64_t>(gpr1 * gpr2, 32);

    temp = ((static_cast<uint64_t>(reg->_spr[GROUP_MAC][SPR_MACHI]) << 32) | static_cast<uint64_t>(reg->_spr[GROUP_MAC][SPR_MACLO])) + temp;

    reg->_spr[GROUP_MAC][SPR_MACLO] = range<Tgeneral_data_t>(temp    ,32);
    reg->_spr[GROUP_MAC][SPR_MACHI] = range<Tgeneral_data_t>(temp>>32,32);
    
    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

#undef  FUNCTION
#define FUNCTION "Functionnal_unit::operation_l_msb"
  void operation_l_msb          (execute_operation_t * op, execute_register_t * reg, execute_param_t * param)
  {
    log_printf(TRACE,Functionnal_unit,FUNCTION,"Operation : l_msb");

    Tgeneral_data_t gpr1 = unsigned(param->_size_data,op->_data_ra);
    Tgeneral_data_t gpr2 = unsigned(param->_size_data,(op->_has_immediat==1)?op->_immediat:op->_data_rb);
    uint64_t        temp = range<uint64_t>(gpr1 * gpr2, 32);

    temp = ((static_cast<uint64_t>(reg->_spr[GROUP_MAC][SPR_MACHI]) << 32) | static_cast<uint64_t>(reg->_spr[GROUP_MAC][SPR_MACLO])) - temp;

    reg->_spr[GROUP_MAC][SPR_MACLO] = range<Tgeneral_data_t>(temp    ,32);
    reg->_spr[GROUP_MAC][SPR_MACHI] = range<Tgeneral_data_t>(temp>>32,32);
    
    // Result
    op->_timing       = param->_timing[op->_type][op->_operation];
  //op->_data_rd      = 0;
  //op->_data_re      = 0;
    op->_exception    = EXCEPTION_ALU_NONE;
    op->_no_sequence  = 0;
  //op->_address      = 0;
  };

}; // end namespace functionnal_unit
}; // end namespace execute_unit
}; // end namespace multi_execute_unit
}; // end namespace execute_loop
}; // end namespace multi_execute_loop
}; // end namespace core
}; // end namespace behavioural
}; // end namespace morpheo              
#endif
