/****************************************************************************
**  SCALASCA    http://www.scalasca.org/                                   **
*****************************************************************************
**  Copyright (c) 1998-2012                                                **
**  Forschungszentrum Juelich GmbH, Juelich Supercomputing Centre          **
**                                                                         **
**  Copyright (c) 2003-2008                                                **
**  University of Tennessee, Innovative Computing Laboratory               **
**                                                                         **
**  See the file COPYRIGHT in the package base directory for details       **
****************************************************************************/

#include <stdio.h>

#include "elg_pform.h"
#include "elg_defs.h"
#include "elg_topol.h"
#include "epk_conf.h"
#include <firmware/include/personality.h>
#include <spi/include/kernel/process.h>
#include <spi/include/kernel/location.h>
#ifdef __GNUC__
#include <hwi/include/bqc/A2_inlines.h>   // for GetTimebase()
#endif
#include <hwi/include/common/uci.h>

#define BGQ_GROUP_ON_NODEBOARD

static double elg_clockspeed=1.0e-6/DEFAULT_FREQ_MHZ;

static int torus_size[6];
static int torus_coord[6];
static int torus_joined[6];

static Personality_t mybgq;

/* platform specific initialization */
void elg_pform_init() {
  uint64_t netflags;

  Kernel_GetPersonality(&mybgq, sizeof(Personality_t));
  elg_clockspeed = 1.0e-6/(double)(mybgq.Kernel_Config.FreqMHz);

  torus_size[0] = mybgq.Network_Config.Anodes;
  torus_size[1] = mybgq.Network_Config.Bnodes;
  torus_size[2] = mybgq.Network_Config.Cnodes;
  torus_size[3] = mybgq.Network_Config.Dnodes;
  torus_size[4] = mybgq.Network_Config.Enodes;
  torus_size[5] = 64;

  torus_coord[0] = mybgq.Network_Config.Acoord;
  torus_coord[1] = mybgq.Network_Config.Bcoord;
  torus_coord[2] = mybgq.Network_Config.Ccoord;
  torus_coord[3] = mybgq.Network_Config.Dcoord;
  torus_coord[4] = mybgq.Network_Config.Ecoord;
  torus_coord[5] = Kernel_ProcessorID();

  netflags = mybgq.Network_Config.NetFlags;

  torus_joined[0] = netflags & ND_ENABLE_TORUS_DIM_A;
  torus_joined[1] = netflags & ND_ENABLE_TORUS_DIM_B;
  torus_joined[2] = netflags & ND_ENABLE_TORUS_DIM_C;
  torus_joined[3] = netflags & ND_ENABLE_TORUS_DIM_D;
  torus_joined[4] = netflags & ND_ENABLE_TORUS_DIM_E;
  torus_joined[5] = 0;
}

void elg_pform_mpi_init() {}

void elg_pform_mpi_finalize() {} 

/* directory of global file system  */
const char* elg_pform_gdir() {
  return epk_get(ELG_PFORM_GDIR);
}

/* directory of local file system  */
const char* elg_pform_ldir() {
  return epk_get(ELG_PFORM_LDIR);
}

/* is a global clock provided ? */ 
int elg_pform_is_gclock() {
  return 1;
}

/* local or global wall-clock time in seconds */
double elg_pform_wtime() {
#ifdef __IBMC__
  return ( __mftb() * elg_clockspeed );
#elif defined __GNUC__
  return ( GetTimeBase() * elg_clockspeed );
#else
#error "Platform BGQ: cannot determine timebase"
#endif
}

/* platform name */
const char*  elg_pform_name() {
  return epk_get(EPK_MACHINE_NAME);
}

/* number of nodes */
int elg_pform_num_nodes() {
  return ( torus_size[0] * torus_size[1] * torus_size[2] *
           torus_size[3] * torus_size[4]);
}

/* unique numeric SMP-node identifier */
long elg_pform_node_id() {
  BG_UniversalComponentIdentifier uci = mybgq.Kernel_Config.UCI;
  /* use upper part of UCI (26bit; upto ComputeCard; ignore lower 38bit)
   * but only use the 20 bits (FFFFF) that describe row,col,mp,nb,cc */
  return ((uci>>38)&0xFFFFF);
}

/* unique string SMP-node identifier */
const char* elg_pform_node_name() {
  static char buf[48];
  BG_UniversalComponentIdentifier uci = mybgq.Kernel_Config.UCI;
  unsigned int row, col, mp, nb, cc;
  bg_decodeComputeCardOnNodeBoardUCI(uci, &row, &col, &mp, &nb, &cc);
  sprintf(buf, "R%x%x-M%d-N%02x-J%02x <%d,%d,%d,%d,%d>", row, col, mp, nb, cc,
          torus_coord[0], torus_coord[1], torus_coord[2],
          torus_coord[3], torus_coord[4]);
  return buf;
}

/* number of CPUs */
int elg_pform_num_cpus() {
  return 64;
}

/* hardware topology */
ELG_HW_TOPOL* elg_pform_hw_topol() {
  static ELG_HW_TOPOL myt;
  /* Axis Mapping:  E,F -> x 
   *                A,B -> y
   *                C,D -> z */
  static char axismap[] = "EFABCD";   /* F == #smt cores == 64 */

  #define mapIdx(i) axismap[i]-'A'

  myt.xcoord = torus_coord[mapIdx(0)] * torus_size[mapIdx(1)]
             + torus_coord[mapIdx(1)];
  myt.ycoord = torus_coord[mapIdx(2)] * torus_size[mapIdx(3)]
             + torus_coord[mapIdx(3)];
  myt.zcoord = torus_coord[mapIdx(4)] * torus_size[mapIdx(5)]
             + torus_coord[mapIdx(5)];
  myt.xsize  = torus_size[mapIdx(0)] * torus_size[mapIdx(1)];
  myt.ysize  = torus_size[mapIdx(2)] * torus_size[mapIdx(3)];
  myt.zsize  = torus_size[mapIdx(4)] * torus_size[mapIdx(5)];
  myt.xperiod = torus_joined[mapIdx(0)] && torus_joined[mapIdx(1)];
  myt.yperiod = torus_joined[mapIdx(2)] && torus_joined[mapIdx(3)];
  myt.zperiod = torus_joined[mapIdx(4)] && torus_joined[mapIdx(5)];

  return &myt;
}

int elg_pform_hw_core_id(int tid) {
  return Kernel_ProcessorID();
}

int elg_pform_map_thread_on_x(int x, int tid) {
  return torus_coord[4] * 64 + Kernel_ProcessorID();
}
