///////////////////////////////////////////////////////////////////////////////////////////////
// File   : main.c   (for transpose application)
// Date   : february 2014
// author : Alain Greiner
//
// This application makes a transpose for a NN*NN pixels sequence of images.
// The image sequence is read from a file (one byte per pixel).
// The input and output buffers containing the image are distributed in all clusters.
//
// - The image size NN must be a power of 2.
// - The number of clusters containing processors must be a power of 2.
// - The number of processors per cluster (NB_PROCS_MAX) must be a power of 2.
// - The image size NN must be larger or equal to the total number of processor.
//
// For each image the application makes a self test (checksum for each line).
// The actual display on the frame buffer depends on frame buffer availability.
///////////////////////////////////////////////////////////////////////////////////////////////

#include "hard_config.h"
#include "stdio.h"
#include "barrier.h"
#include "malloc.h"

#define NN                  128                 // image size : nlines = npixels = 128
#define NB_IMAGES           2                   // number of images to be handled
#define FILE_PATHNAME       "misc/images.raw"   // file pathname on disk
#define NB_CLUSTERS         (X_SIZE * Y_SIZE)   // number of clusters
#define INSTRUMENTATION_OK  0                   // display statistics on TTY when non zero

///////////////////////////////////////////////////////
// global variables stored in seg_data in cluster(0,0)
///////////////////////////////////////////////////////

// instrumentation counters 
// for each processor (up to 4 processors)
// in each cluster (up to 32 clusters)
unsigned int LOAD_START[NB_CLUSTERS][NB_PROCS_MAX];
unsigned int LOAD_END  [NB_CLUSTERS][NB_PROCS_MAX];
unsigned int TRSP_START[NB_CLUSTERS][NB_PROCS_MAX];
unsigned int TRSP_END  [NB_CLUSTERS][NB_PROCS_MAX];
unsigned int DISP_START[NB_CLUSTERS][NB_PROCS_MAX];
unsigned int DISP_END  [NB_CLUSTERS][NB_PROCS_MAX];

// arrays of pointers on distributed buffers
// one input buffer & one output buffer per cluster
unsigned char*  buf_in [NB_CLUSTERS];
unsigned char*  buf_out[NB_CLUSTERS];

// checksum variables 
unsigned check_line_before[NN];
unsigned check_line_after[NN];

// global synchronisation barrier
giet_barrier_t barrier;

volatile unsigned int init_ok = 1;

//////////////////////////////////////////
__attribute__ ((constructor)) void main()
//////////////////////////////////////////
{

    int          file = 0;                                         // file descriptor
    unsigned int l;                                                // line index for loops
    unsigned int p;                                                // pixel index for loops
    unsigned int c;                                                // cluster index for loops

    // get processor identifiers
    unsigned int x;                                                // x cluster coordinate
    unsigned int y;                                                // y cluster coordinate
    unsigned int lpid;                                             // local processor index
    giet_proc_xyp( &x, &y, &lpid);             

    unsigned int npixels    = NN * NN;                             // pixels per image
    unsigned int nblocks    = npixels / 512;                       // blocks per image
    unsigned int image      = 0;                                   // image counter

    unsigned int cluster_id = (x * Y_SIZE) + y;                    // "continuous" index   
    unsigned int ntasks     = NB_CLUSTERS * NB_PROCS_MAX;          // number of tasks
    unsigned int task_id    = (cluster_id * NB_PROCS_MAX) + lpid;  // "continuous" task index

    // Processor [0,0,0] makes initialisation
    // It includes parameters checking, barriers initialization,
    // distributed buffers allocation, and file open
    if ( (x==0) && (y==0) && (lpid==0) )
    {
        // Parameters checking
        if ((NB_PROCS_MAX != 1) && (NB_PROCS_MAX != 2) && (NB_PROCS_MAX != 4))
        { 
            giet_exit("[TRANSPOSE ERROR] NB_PROCS_MAX must be 1, 2 or 4");
        }
        if ((NB_CLUSTERS != 1) && (NB_CLUSTERS != 2) && (NB_CLUSTERS != 4) && 
            (NB_CLUSTERS != 8) && (NB_CLUSTERS != 16) && (NB_CLUSTERS != 32) )
        {
            giet_exit("[TRANSPOSE ERROR] number of clusters must be 1,2,4,8,16,32");
        }
        if ( ntasks > NN )
        {
            giet_exit("[TRANSPOSE ERROR] number of tasks larger than number of lines");
        }

        giet_shr_printf("\n[TRANSPOSE] Processor[0,0,0] starts at cycle %d\n"
                        " - x_size    = %d\n"
                        " - y_size    = %d\n"
                        " - nprocs    = %d\n"
                        " - nclusters = %d\n"
                        " - ntasks    = %d\n",
                        giet_proctime(), X_SIZE, Y_SIZE, NB_PROCS_MAX, NB_CLUSTERS, ntasks );

        // Barrier initialisation
        barrier_init( &barrier, ntasks );

        giet_shr_printf("\n[TRANSPOSE] Proc [0,0,0] completes barrier init at cycle %d\n",
                        giet_proctime() );

        // Distributed buffers allocation
        // The buffers containing one image are distributed in clusters
        // (one buf_in and one buf_out per cluster).
        // Each buffer contains (NN*NN / NB_CLUSTERS) bytes.
        for ( c = 0 ; c < NB_CLUSTERS ; c++ )
        {
            unsigned int rx = c / Y_SIZE;
            unsigned int ry = c % Y_SIZE;

            buf_in[c]  = remote_malloc( npixels/NB_CLUSTERS, rx, ry );
            buf_out[c] = remote_malloc( npixels/NB_CLUSTERS, rx, ry );

            giet_shr_printf("\n[TRANSPOSE] Proc [0,0,0] completes buffer allocation"
                            " for cluster[%d,%d] at cycle %d\n"
                            " - buf_in  = %x\n"
                            " - buf_out = %x\n",
                            rx, ry, giet_proctime(), 
                            (unsigned int)buf_in[c], 
                            (unsigned int)buf_out[c] );
        }

        // open file containing images
        file = giet_fat_open( "misc/images.raw", 0);

        if (file < 0)
        { 
            giet_shr_printf("\n[TRANSPOSE ERROR] Proc [%d,%d,%d]"
                            " cannot open file misc/images.raw",
                            x, y, lpid );
            giet_exit(" open() failure");
        }
        else
        {
            giet_shr_printf("\n[TRANSPOSE] Proc [0,0,0] open file misc/images.raw\n");
        }
        init_ok = 0;
    }
    else   // others processors wait initialisation completion
    {
        while ( init_ok == 1 );
        giet_shr_printf("\n[TRANSPOSE] Processor[%d,%d,%d] starts at cycle %d\n", x, y, lpid);
    }
    
    /////////////////////////
    // Main loop (on images)
    while (image < NB_IMAGES)
    {
        // pseudo parallel load from disk to buf_in buffer : nblocks/NB_CLUSTERS blocks
        // only task running on processor with (lpid == 0) does it

        LOAD_START[cluster_id][lpid] = giet_proctime();

        if (lpid == 0)
        {
            giet_fat_read( file,
                           buf_in[cluster_id],
                           (nblocks / NB_CLUSTERS),
                           ((image*nblocks) + ((nblocks*cluster_id)/NB_CLUSTERS)) );

            giet_shr_printf("\n[TRANSPOSE] Proc [%d,%d,%d] completes load"
                            "  for image %d at cycle %d\n",
                            x, y, lpid, image, giet_proctime() );
        }

        LOAD_END[cluster_id][lpid] = giet_proctime();

        /////////////////////////
        barrier_wait( &barrier );

        // parallel transpose from buf_in to buf_out
        // each task makes the transposition for nlt lines (nlt = NN/ntasks)
        // from line [task_id*nlt] to line [(task_id + 1)*nlt - 1]
        // (p,l) are the absolute pixel coordinates in the source image


        TRSP_START[cluster_id][lpid] = giet_proctime();

        unsigned int nlt   = NN / ntasks;      // number of lines per task
        unsigned int nlc   = NN / NB_CLUSTERS;   // number of lines per cluster

        unsigned int src_cluster;
        unsigned int src_index;
        unsigned int dst_cluster;
        unsigned int dst_index;

        unsigned char byte;

        unsigned int first = task_id * nlt;    // first line index for a given task
        unsigned int last  = first + nlt;      // last line index for a given task

        for ( l = first ; l < last ; l++ )
        {
            check_line_before[l] = 0;
         
            // in each iteration we transfer one byte
            for ( p = 0 ; p < NN ; p++ )
            {
                // read one byte from local buf_in
                src_cluster = l / nlc;
                src_index   = (l % nlc)*NN + p;
                byte        = buf_in[src_cluster][src_index];

                // compute checksum
                check_line_before[l] = check_line_before[l] + byte;

                // write one byte to remote buf_out
                dst_cluster = p / nlc; 
                dst_index   = (p % nlc)*NN + l;
                buf_out[dst_cluster][dst_index] = byte;
            }
        }

        if ( lpid == 0 )
        {
            giet_shr_printf("\n[TRANSPOSE] proc [%d,%d,0] completes transpose"
                            " for image %d at cycle %d\n", 
                            x, y, image, giet_proctime() );

        }
        TRSP_END[cluster_id][lpid] = giet_proctime();

        /////////////////////////
        barrier_wait( &barrier );

        // optional parallel display from local buf_out to frame buffer
        // all processors contribute to display using memcpy...

        if ( USE_FBF )  // external frame buffer available
        {
            DISP_START[cluster_id][lpid] = giet_proctime();

            unsigned int  npt   = npixels / ntasks;   // number of pixels per task

            giet_fb_sync_write( npt * task_id, 
                                &buf_out[cluster_id][lpid*npt], 
                                npt );

            if ( lpid == 0 )
            {
                giet_shr_printf("\n[TRANSPOSE] Proc [%d,%d,0] completes display"
                                " for image %d at cycle %d\n",
                                x, y, image, giet_proctime() );
            }

            DISP_END[cluster_id][lpid] = giet_proctime();

            /////////////////////////
            barrier_wait( &barrier );
        }

        // checksum done by processor (lpid == 0) in each cluster

        if ( lpid == 0 )
        {
            unsigned int success = 1;
            unsigned int start   = cluster_id * nlc;
            unsigned int stop    = start + nlc;

            for ( l = start ; l < stop ; l++ )
            {
                check_line_after[l] = 0;

                for ( p = 0 ; p < NN ; p++ )
                {
                    // read one byte in remote buffer
                    src_cluster = p / nlc;
                    src_index   = (p % nlc)*NN + l;

                    unsigned char byte = buf_out[src_cluster][src_index];

                    check_line_after[l] = check_line_after[l] + byte;
                }

                if ( check_line_before[l] != check_line_after[l] ) success = 0;
            }

            if ( success ) 
            {
                giet_shr_printf("\n[TRANSPOSE] proc [%d,%d,0] checksum OK"
                                " for image %d at cycle %d\n",
                                x, y, image, giet_proctime() );
            }
            else
            {
                giet_shr_printf("\n[TRANSPOSE] proc [%d,%d,0] checksum KO"
                                " for image %d at cycle %d\n",
                                x, y, image, giet_proctime() );
            }
        }

        /////////////////////////
        barrier_wait( &barrier );

        // instrumentation done by processor [0,0,0] 
        if ( (x==0) && (y==0) && (lpid==0) && INSTRUMENTATION_OK )
        {
            int cc, pp;
            unsigned int min_load_start = 0xFFFFFFFF;
            unsigned int max_load_start = 0;
            unsigned int min_load_ended = 0xFFFFFFFF;
            unsigned int max_load_ended = 0;
            unsigned int min_trsp_start = 0xFFFFFFFF;
            unsigned int max_trsp_start = 0;
            unsigned int min_trsp_ended = 0xFFFFFFFF;
            unsigned int max_trsp_ended = 0;
            unsigned int min_disp_start = 0xFFFFFFFF;
            unsigned int max_disp_start = 0;
            unsigned int min_disp_ended = 0xFFFFFFFF;
            unsigned int max_disp_ended = 0;

            for (cc = 0; cc < NB_CLUSTERS; cc++)
            {
                for (pp = 0; pp < NB_PROCS_MAX; pp++)
                {
                    if (LOAD_START[cc][pp] < min_load_start)  min_load_start = LOAD_START[cc][pp];
                    if (LOAD_START[cc][pp] > max_load_start)  max_load_start = LOAD_START[cc][pp];
                    if (LOAD_END[cc][pp]   < min_load_ended)  min_load_ended = LOAD_END[cc][pp]; 
                    if (LOAD_END[cc][pp]   > max_load_ended)  max_load_ended = LOAD_END[cc][pp];
                    if (TRSP_START[cc][pp] < min_trsp_start)  min_trsp_start = TRSP_START[cc][pp];
                    if (TRSP_START[cc][pp] > max_trsp_start)  max_trsp_start = TRSP_START[cc][pp];
                    if (TRSP_END[cc][pp]   < min_trsp_ended)  min_trsp_ended = TRSP_END[cc][pp];
                    if (TRSP_END[cc][pp]   > max_trsp_ended)  max_trsp_ended = TRSP_END[cc][pp];
                    if (DISP_START[cc][pp] < min_disp_start)  min_disp_start = DISP_START[cc][pp];
                    if (DISP_START[cc][pp] > max_disp_start)  max_disp_start = DISP_START[cc][pp];
                    if (DISP_END[cc][pp]   < min_disp_ended)  min_disp_ended = DISP_END[cc][pp];
                    if (DISP_END[cc][pp]   > max_disp_ended)  max_disp_ended = DISP_END[cc][pp];
                }
            }

            giet_shr_printf(" - LOAD_START : min = %d / max = %d / med = %d / delta = %d\n",
                            min_load_start, max_load_start, (min_load_start+max_load_start)/2, 
                            max_load_start-min_load_start); 

            giet_shr_printf(" - LOAD_END   : min = %d / max = %d / med = %d / delta = %d\n",
                            min_load_ended, max_load_ended, (min_load_ended+max_load_ended)/2, 
                            max_load_ended-min_load_ended); 

            giet_shr_printf(" - TRSP_START : min = %d / max = %d / med = %d / delta = %d\n",
                            min_trsp_start, max_trsp_start, (min_trsp_start+max_trsp_start)/2, 
                            max_trsp_start-min_trsp_start); 

            giet_shr_printf(" - TRSP_END   : min = %d / max = %d / med = %d / delta = %d\n",
                            min_trsp_ended, max_trsp_ended, (min_trsp_ended+max_trsp_ended)/2, 
                            max_trsp_ended-min_trsp_ended); 

            giet_shr_printf(" - DISP_START : min = %d / max = %d / med = %d / delta = %d\n",
                            min_disp_start, max_disp_start, (min_disp_start+max_disp_start)/2, 
                            max_disp_start-min_disp_start); 

            giet_shr_printf(" - DISP_END   : min = %d / max = %d / med = %d / delta = %d\n",
                            min_disp_ended, max_disp_ended, (min_disp_ended+max_disp_ended)/2, 
                            max_disp_ended-min_disp_ended); 
        }

        image++;

        /////////////////////////
        barrier_wait( &barrier );

    } // end while image      

    // Processor[0,0,0] releases the Distributed buffers
    if ( (x==0) && (y==0) && (lpid==0) )
    {
        for ( c = 0 ; c < NB_CLUSTERS ; c++ )
        {
            free( buf_in[c] );
            free( buf_in[c] );
        }
    }

    giet_exit("Completed");

} // end main()

// Local Variables:
// tab-width: 3
// c-basic-offset: 
// c-file-offsets:((innamespace . 0)(inline-open . 0))
// indent-tabs-mode: nil
// End:

// vim: filetype=cpp:expandtab:shiftwidth=3:tabstop=3:softtabstop=3



