/******************************************************************************/
/*                              SCRAMNet GT                                   */
/******************************************************************************/
/*                                                                            */
/* Copyright (c) 2020 Curtiss-Wright Controls Electronic System, Inc.         */
/*     dtn_support@curtisswright.com 800-252-5601 (U.S. only) 937-252-5601    */
/*                                                                            */
/* This program is free software; you can redistribute it and/or              */
/* modify it under the terms of the GNU General Public License                */
/* as published by the Free Software Foundation; either version 2             */
/* of the License, or (at your option) any later version.                     */
/*                                                                            */
/* See the GNU General Public License for more details.                       */
/*                                                                            */
/******************************************************************************/

#define APP_VER_STRING "GT Random Data Test (gtrand) rev. 2.07 (2011/08/30)"
#define APP_VxW_NAME gtrand

/**************************************************************************/
/****** TAKING CARE OF ALL POSSIBLE OS TYPES - COPY-PASTE *****************/
/**************************************************************************/
/* control by PLATFORM_WIN, PLATFORM_UNIX, PLATFORM_VXWORKS, etc */     /**/
#include <math.h>                                                       /**/
#include <stdio.h>                                                      /**/
#include <stdlib.h>                                                     /**/
#include <time.h>                                                       /**/
#include <string.h>                                                     /**/
#include <ctype.h>                                                      /**/
                                                                        /**/
#define PARSER                                                          /**/
#define MAIN_IN      int main(int argc, char **argv) {                  /**/
                                                                        /**/
#if defined PLATFORM_WIN || defined PLATFORM_RTX                        /**/
    #include <windows.h>                                                /**/
                                                                        /**/
#elif PLATFORM_VXWORKS                                                  /**/
    #undef PARSER                                                       /**/
    #undef MAIN_IN                                                      /**/
    #define VXL     40                                                  /**/
    #define MAIN_IN int APP_VxW_NAME(char *cmdLine) { \
            int argc; char argv0[]="gtrand"; char *argv[VXL]={argv0};   /**/
    #define PARSER     argc = parseCls(cmdLine, argv);                  /**/
                                                                        /**/
static int parseCls(char *cLine, char *argv[VXL])                       /**/
{                                                                       /**/
    char *token, *ptr = NULL;                                           /**/
    int numTokens = 1;                                                  /**/
    char seps[] = " ,\t\n";                                             /**/
    if(cLine == NULL) return numTokens; /* no command line given */     /**/
    printf(" \b"); token=(char*)strtok_r(cLine,seps,&ptr);              /**/
    while(token != NULL)                                                /**/
    { argv[numTokens] = token;                                          /**/
      numTokens++;                                                      /**/
      token = (char*)strtok_r(NULL, seps, &ptr);                        /**/
    }                                                                   /**/
    return numTokens;                                                   /**/
}                                                                       /**/
#endif                                                                  /**/
/************ END OF TAKING CARE OF ALL POSSIBLE OS TYPES *****************/

/**************************************************************************/
/************** APPLICATION-SPECIFIC CODE STARTS HERE *********************/
/**************************************************************************/

#include "scgtapi.h"
#include "usys.h"

typedef struct {

    volatile uint32    inputAvailable;
    volatile uint32    resetFlag;
    volatile uint32    showBadFlag;
    volatile uint32    timeToExit;
    volatile uint32    showOptions;
    volatile uint32    modeChange;
    volatile uint32    autoModeFlag;
    volatile uint32    running;
    usysThreadParams   pt;
} exitThreadParms;

typedef struct
{
    uint32 unit, helpLevel, flags, seconds, mode, bytesPerRW;
    uint32 maxLength, doMaxLen, length, offset, niceMS, numToDump;
    scgtHandle hDriver;
    exitThreadParms exitParms;
    volatile uint32 *boardAddressPIO;
    uint32 popMemSize, mapMemSize;
} iOptionsType;

typedef struct _testParams
{
    uint32 passCount;
    uint32 errs;
    uint32 gtOffset;
    uint32 length;
    uint32 sizeInBytes;
    uint32 mode;
    uint64 value; /* if not random data in buf, then the value to expect */
    void *wbuf;
    void *rbuf;
} testParams;

typedef struct _queue
{
    int dir;
    int index;
} queue;

#define NUM_TESTPARAMS  16      /* size of queue, must be an even number */
#define MAX_DATA_LEN    0x4000  /* 16K per verification buffer */

#define FLAG_SHOW_ERRS      0x01
#define FLAG_HALT           0x02
#define FLAG_ALLOW_PF       0x04
#define FLAG_WRITE_READ     0x08
#define FLAG_RANDOM_ORDER   0x10
#define FLAG_WRITE_REVERSE  0x20
#define FLAG_READ_REVERSE   0x40
#define FLAG_DISP_TESTS     0x80

static void buildOptionsString( char *optionsString, iOptionsType *iO );
static int parseCommand(int argc, char **argv, iOptionsType *iO,
                         char *optionsString);
static void printHelpLevel1(iOptionsType *iO, char *optionsString);
static void printHelpLevel2();
static void printHelpLevel3();
static void *getRuntimeChar(void *voidp);
static int gtrandVerifyOptions(iOptionsType *iO);
static int gtrandBoss(iOptionsType *iO, char *optionsString);

/**************************************************************************/
/************************* G T I N T   C O D E ****************************/
/**************************************************************************/

MAIN_IN

    iOptionsType iO;
    scgtDeviceInfo devInfo;
    char optionsString[120];

    PARSER;        /* prepare argc and agrv if not provided by OS (VxWorks) */

    memset(&iO, 0, sizeof(iOptionsType));
    parseCommand(argc, argv, &iO, optionsString);        /* parsing options */

    if(iO.helpLevel!=0) /*1, 2, 3, ...*/
    {
        printHelpLevel1(&iO, optionsString);
        return 0;
    }

    if (scgtOpen(iO.unit, &iO.hDriver) != SCGT_SUCCESS)
    {
        printf("gtint: could not open unit %i\n", iO.unit);
        return -1;
    }

    if( (iO.boardAddressPIO = scgtMapMem(&iO.hDriver)) == NULL )
    {
        printf("!!!!Failed to map memory for PIO!!!!\n");
    }

    scgtGetDeviceInfo(&iO.hDriver, &devInfo);
    iO.popMemSize = devInfo.popMemSize;
    iO.mapMemSize = devInfo.mappedMemSize;

    if( gtrandVerifyOptions(&iO) )
    {
        return -1;
    }
                                                /* spawn exit thread */
    iO.exitParms.timeToExit = 0;
    iO.exitParms.pt.parameter = &iO.exitParms;
    iO.exitParms.pt.priority = UPRIO_HIGH; /* only use UPRIO_HIGH */
    sprintf(iO.exitParms.pt.threadName, "gtintExit");
    usysCreateThread(&iO.exitParms.pt, getRuntimeChar);
    /* Give thread a moment to start up. Needed on VxWorks, Solaris 8 */
    usysMsTimeDelay(50);

    printf("Running: %s\n", optionsString);
    printf("Hit: 'q' - quit, 'r' - reset, 'h' - help, 'v' - display & 'm' - mode toggles\n");

    gtrandBoss(&iO, optionsString);

    if( iO.exitParms.running )
        usysKillThread(&iO.exitParms.pt);

    scgtClose(&iO.hDriver);
    return 0;
}

/***************************************************************************/
/* Function:    gtrandVerifyOptions                                        */
/* Description: Verifies that options are correct for testing              */
/***************************************************************************/
static int gtrandVerifyOptions(iOptionsType *iO)
{
    if( iO->mapMemSize == 0 )
    {
        printf("No memory mapped for PIO.\n");
        return -1;
    }

    iO->maxLength = iO->mapMemSize;

    if( iO->doMaxLen )
    {
        iO->length = iO->maxLength;
        iO->offset = 0;

        if( iO->mapMemSize < iO->popMemSize )
        {
            printf("WARNING: PIO range limited to mapped region (%u bytes).\n",
                    iO->mapMemSize);
        }
    }

    if( (iO->offset + iO->length ) > iO->maxLength )
        iO->length = iO->maxLength - iO->offset;

    if( iO->offset >= iO->maxLength )
    {
        printf("Invalid offset. Max offset: 0x%x.\n", iO->maxLength);
        return -1;
    }

    if( iO->length < (sizeof(uint64)*NUM_TESTPARAMS) )
    {
        printf("Invalid length (%d). Minimum is %d.\n",
                iO->length, (int)(sizeof(uint64)*NUM_TESTPARAMS));
        return -1;
    }

    return 0;
}

/***************************************************************************/
/* Function:    Sized memory copying functions                             */
/* Description: Copy from source buffer to destination buffer              */
/***************************************************************************/
static void gtCopy8(volatile uint8* src, volatile uint8 *dest, int numBytes, int reverse)
{
    int i, length = numBytes/sizeof(uint8);
    if (reverse)
        for(i=length-1; i>=0; i--) dest[i]=src[i];
    else
        for(i=0; i<length; i++) dest[i]=src[i];
}

static void gtCopy16(volatile uint16* src, volatile uint16 *dest, int numBytes, int reverse)
{
    int i, length = numBytes/sizeof(uint16);
    if (reverse)
        for(i=length-1; i>=0; i--) dest[i]=src[i];
    else
        for(i=0; i<length; i++) dest[i]=src[i];
}

static void gtCopy32(volatile uint32* src, volatile uint32 *dest, int numBytes, int reverse)
{
    int i, length = numBytes/sizeof(uint32);
    if (reverse)
        for(i=length-1; i>=0; i--) dest[i]=src[i];
    else
        for(i=0; i<length; i++) dest[i]=src[i];
}

static void gtCopy64(volatile uint64* src, volatile uint64* dest, int numBytes, int reverse)
{
    int i, length = numBytes/sizeof(uint64);

    if (reverse)
        for(i=length-1; i>=0; i--) dest[i]=src[i];
    else
        for(i=0; i<length; i++) dest[i] = src[i];
}

/****** Random Number Generator - Copyright (c) 1991 Leszek D. Wronski ********/
#define irdn6(a,x) {int _irdP=a[63]; _irdP=(_irdP+1)%63; (x)=(x)-a[_irdP];\
    if((x)<0) (x)=(x)+2147483646; a[_irdP]=(x);a[63]=_irdP;}
#define irdn6_vars(a) long int a[64] = {67,111,112,121,114,105,103,104,116,32,\
  40,99,41,32,49,57,57,49,44,32,76,101,115,122,101,107,32,68,46,32,87,114,111,\
  110,115,107,105,32,32,1775811865,893145566,995405759,2102736524,271520213,\
  456633834,2058258651,1583689592,878442065,1675280054,1627576247,2054802212,\
  60335757, 684462146, 1625503827, 521827728,1266032265, 93253262, 2053047983,\
  685333180, 127474245, 948921754, 1964990155, 1228891816,0}
/*Usage: instantiate irdn6_vars and some long int randvector. To randomize
 starting point you may put some value into randvector . Call irdn6(randvector);
 - new random value will be placed in randvector - use it but do not change it*/
/********************* Random Number Generator - END **************************/

void testStackDump(testParams *testArray, queue* now, queue* old, int queueSize,
                   int qIndex, int numToDump)
{
    /* now and old queues are each of size queueSize */
    /* qIndex is the current index into the now queue */

    int i;
    testParams *tP;
    queue *q = now;

    /* just limit to queueSize, because that many are quaranteed available */
    if( numToDump > queueSize )
        numToDump = queueSize;

    for ( i=0; i<numToDump; i++, qIndex-- )
    {
        if (qIndex < 0)
        {
            qIndex = queueSize - 1;
            q = old;
        }

        tP = &testArray[ q[qIndex].index ];

        if( tP->passCount )
        {
            printf("t-%d b=%d o=%X l=%X ol=%X d=%c i=%d m=%d v=%.0X%.8X e=%d\n",
                i, tP->sizeInBytes, tP->gtOffset, tP->length,
                tP->gtOffset + tP->length, q[qIndex].dir==1 ? 'w' : 'r',
                q[qIndex].index, tP->mode, (uint32)(tP->value >> 32),
                (uint32)(tP->value), tP->errs);
        }
    }
}


static int gtrandBoss(iOptionsType *iO, char *optionsString)
{
    usysMsTimeType curTime;
    uint32 curSecond = 0, lastSecond = 0;
    volatile uint8* pioB = (uint8*)iO->boardAddressPIO;

    #define SIZES  10
    int sizes[SIZES] = {1,2,4,8,8,4,2,1,1,4}; /* weighted to 8-bit, 32-bit */
    int i, j, itemp, index, qI=0, qIndex=0, mode=iO->mode;
    unsigned int k;
    uint64 sdata = 0, sdata2=0;
    double totalbytes=0;
    uint32 ex[9] = {0}; /* error counters */
    uint32 writes, reads, verrors, words, showErrs=((iO->flags & FLAG_SHOW_ERRS) && 1);
    uint32 segmentLength = iO->length / NUM_TESTPARAMS;
    uint32 minGtOffset, maxGtOffset, maxLength, maxRelativeOffset, sib, length;
    irdn6_vars(gen1);
    long int randv;
    uint32 gtOffset; /* GT physical offset above boardAddressPIO */
    uint8 *readBuf = NULL, *writeBuf = NULL, *wbuf, *rbuf;
    testParams tpQ[NUM_TESTPARAMS * 2] = {{0}}; /* init to all-zeros */

    int stack[NUM_TESTPARAMS] = {0};

    queue orderQueue1[NUM_TESTPARAMS * 2] = {{0}}; /* order in which to run the tests */
    queue orderQueue2[NUM_TESTPARAMS * 2] = {{0}}; /* order in which to run the tests */
    queue *order = orderQueue1, *oldorder = orderQueue2, *tempq;

    int wReverse = iO->flags & FLAG_WRITE_REVERSE;
    int rReverse = iO->flags & FLAG_READ_REVERSE;

    readBuf  = malloc( (MAX_DATA_LEN * NUM_TESTPARAMS * 2) ); // JHM, should align to 64-bit
    writeBuf = malloc( (MAX_DATA_LEN * NUM_TESTPARAMS * 2) ); // JHM, should align to 64-bit

    if ( (readBuf == NULL) || (writeBuf == NULL) )
    {
        printf("Failed to allocate memory\n");

        if ( readBuf != NULL )
            free(readBuf);
        if ( writeBuf != NULL )
            free(writeBuf);
        return -1;
    }

    if ( iO->bytesPerRW )
    {
         /* generate array of sizes from which to choose. Weighted first
            toward 4, then 1, then 8, then 2. */
        uint32 bprw = iO->bytesPerRW;
        i=0;

        while( i < SIZES )
        {
            if ( bprw & 0x4 )         sizes[i]=4;
            else if ( bprw & 0x1 )    sizes[i]=1;
            else if ( bprw & 0x8 )    sizes[i]=8;
            else if ( bprw & 0x2 )    sizes[i]=2;
            else /* restore original value */
            {
                bprw = iO->bytesPerRW;
                continue;
            }

            bprw &= ~sizes[i];
            i++;
        }
    }

    srand(time(NULL));
    srand(rand());
    randv = rand() & 0xeFFFeFFF;

    for( i = 0; i < (NUM_TESTPARAMS*2); i++ )
    {
        tpQ[i].wbuf = &writeBuf[MAX_DATA_LEN*i];
        tpQ[i].rbuf = &readBuf[MAX_DATA_LEN*i];
    }

    for( i = 0; i < NUM_TESTPARAMS; i++ ) { stack[i] = i; }

    usysMsTimeStart(&curTime);

    i = 0; /* the current test */
    memset(ex, 0, 9*sizeof(uint32)); /* error counters */

    while( !iO->exitParms.timeToExit )
    {
        verrors = 0;

        if(iO->exitParms.inputAvailable)               /* check for user input */
        {
            iO->exitParms.inputAvailable = 0;

            if(iO->exitParms.timeToExit == 1)
                break;
            if(iO->exitParms.resetFlag == 1)
            {
                usysMsTimeStop(&curTime);
                curSecond = lastSecond = 0;
                totalbytes = 0;
                usysMsTimeStart(&curTime);
                memset(ex, 0, 9*sizeof(uint32)); /* error counters */
                iO->exitParms.resetFlag = 0;
            }
            if(iO->exitParms.showBadFlag == 1)
            {
                showErrs = (showErrs + 1) % 2;
                iO->exitParms.showBadFlag = 0;
                iO->exitParms.showOptions = 1; /* to display settings next */
            }
            if(iO->exitParms.modeChange == 1)
            {
                mode = (mode + 1) % 6;     /* do not update iO->mode */
                iO->exitParms.modeChange = 0;
                iO->exitParms.showOptions = 1; /* to display settings next */
            }
            if(iO->exitParms.autoModeFlag == 1)
            {
                iO->mode = (iO->mode ^ 0x80000000);
                iO->exitParms.autoModeFlag = 0;
                iO->exitParms.showOptions = 1; /* to display settings next */
            }
            if(iO->exitParms.showOptions == 1)
            {
                char * str = "???";
                iO->exitParms.showOptions = 0;
                printf("\r\nRunning: %s\n", optionsString);

                switch( mode )
                {
                    case 0: str="RANDOM but fixed within a packet"; break;
                    case 1: str="RANDOM every word in every packet";   break;
                    case 2: str="0x55...-0xaa... and swapping";  break;
                    case 3: str="0x55...-0xaa... no swapping";   break;
                    case 4: str="0x00...-0xff... and swapping";  break;
                    case 5: str="0x00...-0xff... no swapping";   break;
                }

                printf("Data mode     ('m' - change) is: m=%d  %s\n",mode,str);
                printf("Auto-vary 'm' ('a' - toggle) is: %s\n",
                       (iO->mode & 0x80000000)? "ON " : "OFF");
                printf("Error display ('v' - toggle) is: %s  \n",
                    showErrs==0 ? "OFF" : "ON" );
                printf("Other options: 'q' - quit, 'r' - reset, 'h' - help\n");
            }
        }

        if ( lastSecond < curSecond)
        {
            printf("\rdT=%d Tx=%d B=%.2e Ex=%d Ex1=%d Ex2=%d Ex4=%d Ex8=%d",
                   curSecond, ex[3], totalbytes, ex[0], ex[1], ex[2], ex[4], ex[8]);
            fflush(stdout);
            lastSecond = curSecond;
            #ifdef PLATFORM_VXWORKS
            /* Give getRuntimeChar thread (and other apps) a chance to run.
               Required when running from VxWorks command shell. */
            usysMsTimeDelay(20);
            #endif
        }

        /* generate the test data (offsets, length, data set, etc) for
           several tests */

        /*
         * qIndex is the base offset in tpQ at which to begin filling in data.
         * Must guarantee that qIndex to qIndex+NUM_TESTPARAMS-1 exist
         * and are available for use.
         */

        qIndex = qI = (qI + NUM_TESTPARAMS)%(NUM_TESTPARAMS*2);

        for(j=1, minGtOffset = iO->offset; j <= NUM_TESTPARAMS; j++, qIndex++)
        {
            if( ((i % 1000) == 0) && ((iO->mode & 0x80000000) != 0) )
            {
                mode = (mode + 1) % 6;     /* do not update iO->mode */
            }

            irdn6(gen1, randv);

            /* select size-in-bytes */
            sib = sizes[randv%SIZES];

            minGtOffset = (minGtOffset + (sib-1)) & ~(sib-1); /* round up to sib */

            maxGtOffset = iO->offset + (segmentLength * j);
            maxGtOffset = (maxGtOffset > iO->mapMemSize) ? iO->mapMemSize : maxGtOffset;
            maxGtOffset &= ~(sib-1); /* round down to sib */

            maxLength = maxGtOffset - minGtOffset;
            maxLength = (maxLength > MAX_DATA_LEN) ? MAX_DATA_LEN : maxLength;
            maxLength &= ~(sib-1); /* round down to sib */

            /* select length */
            length = ((randv % maxLength) & ~(sib-1)) + sib; /* at least 1 word of 'size-in-bytes' */

            irdn6(gen1, randv);

            /* select GT offset */
            /* calculate max relative offset above minGtOffset */
            maxRelativeOffset = (maxGtOffset - minGtOffset) - length;

            if( maxRelativeOffset )
                gtOffset = minGtOffset + ((randv % maxRelativeOffset) & ~(sib-1)); /* the GT offset */
            else
                gtOffset = minGtOffset;

            //printf(" off=%#.4x, len=%#.4x, ol=%#.4x, min=%#.4x, max=%#.4x, len=%#.4x\n", gtOffset, length, gtOffset+length, minGtOffset,maxGtOffset,maxLength);

            minGtOffset = gtOffset + length;

            wbuf = tpQ[qIndex].wbuf;
            words = length/sib;

            if((mode == 2)||(mode == 3)) /* every bit changing DC maintained */
            {
                /* Some gcc compilers want suffix LL on 0x5555555555555555,
                   but MSVC doesn't like it, so hack around it */
                sdata=(((uint64)0x55555555 << 32) | ((uint64)0x55555555));
                /*sdata=0x5555555555555555;*/
                if( ((i % 2) == 0) && (mode == 2) ) sdata=~sdata;
                sdata2=~sdata;
            }
            else if((mode==4)||(mode==5))    /* every bit changing DC biased */
            {
                sdata=0x0;
                if( ((i % 2) == 0) && (mode == 4) ) sdata=~sdata;
                sdata2=~sdata;
            }
            else  /* standard old random mode - the same data within a packet*/
            {
                irdn6(gen1,randv);      /* generate new random variable */
                sdata = (uint64)((uint32)((randv<<1) | (randv&1)));
                sdata = ((~sdata) << 32) | (sdata);
                sdata2=sdata;
            }

            for ( k=0; k<words; k++)     /* Fill packet with random data */
            {
                uint64 val;
     #define SETIT(type, base, offset, val) (((type*)base)[offset]=(type)(val))

                if ( mode == 1 ) /* random data in every word */
                {
                    irdn6(gen1,randv);  /* generate new random variable */
                    val = (uint64)((uint32)((randv<<1) | (randv&1)));
                    val = (~val << 32) | (val);
                }
                else
                {
                    val = ( k % 2 ) ? sdata2 : sdata;
                }

                switch(sib)
                {
                    case 4: SETIT(uint32, wbuf, k, val);  break;
                    case 1: SETIT(uint8,  wbuf, k, val);  break;
                    case 2: SETIT(uint16, wbuf, k, val);  break;
                    case 8: SETIT(uint64, wbuf, k, val);  break;
                }

            }

            tpQ[qIndex].passCount++;
            tpQ[qIndex].gtOffset = gtOffset;
            tpQ[qIndex].length = length;
            tpQ[qIndex].sizeInBytes = sib;
            tpQ[qIndex].mode = mode;

            switch(sib)
            {
                case 4: tpQ[qIndex].value = (uint64)(((uint32*)wbuf)[0]);  break;
                case 1: tpQ[qIndex].value = (uint64)(((uint8*) wbuf)[0]);  break;
                case 2: tpQ[qIndex].value = (uint64)(((uint16*)wbuf)[0]);  break;
                case 8: tpQ[qIndex].value = (uint64)(((uint64*)wbuf)[0]);  break;
            }

            i++;

            #if 0 /* verification code */
            if (tpQ[qIndex].gtOffset & (sib-1))
                printf("error 1\n");
            if (tpQ[qIndex].length & (sib-1))
                printf("error 2\n");
            if (tpQ[qIndex].gtOffset >= iO->mapMemSize)
                printf("error 3\n");
            if( (j > 1) &&
                ( gtOffset < (tpQ[qIndex-1].gtOffset + tpQ[qIndex-1].length) ) )
            {
                printf("error 4\n");
            }
            #endif
        }

        /* generate an ordering for running the tests */

        /* randomize the ordering of the tests */
        for( j = 0; j < NUM_TESTPARAMS; j++ )
        {
            irdn6(gen1, randv);
            index = randv % NUM_TESTPARAMS;
            itemp = stack[index]; stack[index] = stack[j]; stack[j] = itemp;
        }

        for ( j = 0, writes=0, reads=0; j < (NUM_TESTPARAMS*2); j++ )
        {
            int dir=0;

            if( iO->flags & FLAG_RANDOM_ORDER ) /* random order of writes and verifies */
            {
                irdn6(gen1, randv);

                /* must do a write before a read */
                if ( (writes <= reads) ||
                     ((randv & 0x3) /* 75% chance */ && (writes < NUM_TESTPARAMS)) )
                {
                    dir = 1;
                }
            }
            else if ( iO->flags & FLAG_WRITE_READ ) /* write then verify immediately */
            {
                if ( writes <= reads )
                    dir = 1;
            }
            else if ( writes < NUM_TESTPARAMS ) /* no flags, do all writes before verifying */
            {
                dir = 1;
            }

            if ( dir == 1 )
            {
                order[j].dir = 1; /* 1 means write */
                order[j].index = stack[writes++] + qI; /* add base queue index */
            }
            else
            {
                order[j].dir = 0; /* 0 means read */
                order[j].index = stack[reads++] + qI; /* add base queue index */
            }
            //printf("j=%d, dir=%d, qI=%d\n", j, order[j].dir, order[j].index);
        }

        /* execute the tests */
        for ( j = 0; j < (NUM_TESTPARAMS*2); j++ )
        {
            testParams *t = &tpQ[order[j].index];
            wbuf = t->wbuf;
            rbuf = t->rbuf;
            verrors=0;

            if( iO->flags & FLAG_DISP_TESTS )
            {
                printf("b=%d o=%X l=%X ol=%X d=%c m=%d v=%.0X%.8X \n",
                        t->sizeInBytes, t->gtOffset, t->length,
                        t->gtOffset + t->length, order[qIndex].dir==1 ? 'w' : 'r',
                        t->mode, (uint32)(t->value >> 32), (uint32)(t->value));
            }

            if ( order[j].dir == 1 ) /* it's a write test */
            {
                switch(t->sizeInBytes)
                {

                    case 4:
                        gtCopy32(t->wbuf, (void*)(pioB + t->gtOffset), t->length, wReverse);
                        break;
                    case 1:
                        gtCopy8 (t->wbuf, (void*)(pioB + t->gtOffset), t->length, wReverse);
                        break;
                    case 2:
                        gtCopy16(t->wbuf, (void*)(pioB + t->gtOffset), t->length, wReverse);
                        break;
                    case 8:
                        gtCopy64(t->wbuf, (void*)(pioB + t->gtOffset), t->length, wReverse);
                        break;
                }

                ex[3]++;  /* number of iterations, only increment on write */
            }
            else /* it's a read/verify test */
            {
                /* When !(iO->flags & FLAG_ALLOW_PF), read last value of buf
                 * first. This causes FW prefetch disruption b/c next read at
                 * beginning of buf is non-sequential. This will work unless
                 * t->length is the size of only 1 element.
                 */

                if (iO->niceMS)
                    usysMsTimeDelay(iO->niceMS); /* delay between write/read */

                switch(t->sizeInBytes)
                {
                  case 4:
                    if( !(iO->flags & FLAG_ALLOW_PF) )
                    {
//                        ((uint32*)rbuf)[0] = ((volatile uint32*)(pioB + t->gtOffset))[(t->length/4)];
//                        ((uint32*)rbuf)[0] = ((volatile uint32*)(pioB + t->gtOffset))[((t->length-1)/4)];
                        ((uint32*)rbuf)[0] = ((volatile uint32*)pioB)[((iO->maxLength-1)/4)];
                    }

                    gtCopy32((void*)(pioB + t->gtOffset), t->rbuf, t->length, rReverse);

                    for( k=0; k < (t->length/4); k++)
                    {
                        if( ((uint32*)rbuf)[k] != ((uint32*)wbuf)[k] )
                        {
                            verrors++;
                            if(showErrs)
                              printf("\nERR b=4 l=%X o=%X ro=%X w=%.2X r=%.2X v=%.2X e=%d",
                                t->length, (uint32)(t->gtOffset + (k*4)), k*4,
                                ((uint32*)wbuf)[k],
                                ((uint32*)rbuf)[k],
                                ((uint32*)wbuf)[k] ^ ((uint32*)rbuf)[k],
                                verrors);
                        }
                    }

                    break;

                  case 1:
                    if( !(iO->flags & FLAG_ALLOW_PF) )
                    {
//                        ((uint8*)rbuf)[0] = ((uint8*)(pioB + t->gtOffset))[(t->length)];
//                        ((uint8*)rbuf)[0] = ((volatile uint8*)(pioB + t->gtOffset))[(t->length-1)];
                        ((uint8*)rbuf)[0] = ((volatile uint8*)pioB)[(iO->maxLength-1)];
                    }

                    gtCopy8 ((void*)(pioB + t->gtOffset), t->rbuf, t->length, rReverse);

                    for( k=0; k < t->length; k++)
                    {
                        if( ((uint8*)rbuf)[k] != ((uint8*)wbuf)[k] )
                        {
                            verrors++;
                            if(showErrs)
                              printf("\nERR b=1 l=%X o=%X ro=%X w=%.2X r=%.2X v=%.2X e=%d",
                                t->length, (uint32)(t->gtOffset + k), k,
                                ((uint8*)wbuf)[k],
                                ((uint8*)rbuf)[k],
                                ((uint8*)wbuf)[k] ^ ((uint8*)rbuf)[k],
                                verrors);
                        }
                    }

                    break;

                  case 2:
                    if( !(iO->flags & FLAG_ALLOW_PF) )
                    {
//                        ((uint16*)rbuf)[0] = ((uint16*)(pioB + t->gtOffset))[(t->length/2)];
//                        ((uint16*)rbuf)[0] = ((volatile uint16*)(pioB + t->gtOffset))[((t->length-1)/2)];
                        ((uint16*)rbuf)[0] = ((volatile uint16*)pioB)[((iO->maxLength-1)/2)];
                    }

                    gtCopy16((void*)(pioB + t->gtOffset), t->rbuf, t->length, rReverse);

                    for( k=0; k < (t->length/2); k++)
                    {
                        if( ((uint16*)rbuf)[k] != ((uint16*)wbuf)[k] )
                        {
                            verrors++;
                            if(showErrs)
                              printf("\nERR b=2 l=%X o=%X ro=%X w=%.2X r=%.2X v=%.2X e=%d",
                                t->length, (uint32)(t->gtOffset + (k*2)), k*2,
                                ((uint16*)wbuf)[k],
                                ((uint16*)rbuf)[k],
                                ((uint16*)wbuf)[k] ^ ((uint16*)rbuf)[k],
                                verrors);
                        }
                    }
                    break;

                  case 8:
                    if( !(iO->flags & FLAG_ALLOW_PF) )
                    {
//                        ((uint64*)rbuf)[0] = ((uint64*)(pioB + t->gtOffset))[(t->length/8)];
//                        ((uint64*)rbuf)[0] = ((volatile uint64*)(pioB + t->gtOffset))[((t->length-1)/8)];
                        ((uint64*)rbuf)[0] = ((volatile uint64*)pioB)[((iO->maxLength-1)/8)];
                    }

                    gtCopy64((void*)(pioB + t->gtOffset), t->rbuf, t->length, rReverse);

                    for( k=0; k < (t->length/8); k++)
                    {
                        if( ((uint64*)rbuf)[k] != ((uint64*)wbuf)[k] )
                        {
                            verrors++;
                            if(showErrs)
                              printf("\nERR b=8 l=%X o=%X ro=%X w=%.8X%.8X r=%.8X%.8X v=%.8X%.8X e=%d",
                                t->length, (uint32)(t->gtOffset + (k*8)), k*8,
                                (uint32)(((uint64*)wbuf)[k] >> 32),
                                (uint32)(((uint64*)wbuf)[k]),
                                (uint32)(((uint64*)rbuf)[k] >> 32),
                                (uint32)(((uint64*)rbuf)[k]),
                                ((uint32)(((uint64*)wbuf)[k] >> 32)) ^
                                    ((uint32)(((uint64*)rbuf)[k] >> 32)),
                                ((uint32)(((uint64*)wbuf)[k])) ^
                                    ((uint32)(((uint64*)rbuf)[k])),
                                verrors);
                        }
                    }

                    break;
                }

                totalbytes += (double)t->length; /* only increment on read */

            }

            t->errs = verrors;
            ex[t->sizeInBytes] += verrors;
            ex[0] += verrors;

            if( verrors )
            {
                if(showErrs)
                {
                    printf("\n");
                    testStackDump(tpQ, order, oldorder, (NUM_TESTPARAMS * 2), j, iO->numToDump);
                }

                if( iO->flags & FLAG_HALT )
                    goto halt;
            }
        }

        /* swap ordering queue to maintain history of tests */
        tempq = order;     order = oldorder;      oldorder = tempq;

        curSecond = (usysMsTimeGetElapsed(&curTime) / 1000);

        /* if not "run forever" and time met or exceeded */
        if( (iO->seconds != -1) && (curSecond >= iO->seconds) )
        {
            iO->exitParms.timeToExit = 1;
        }

        fflush(stdout);
    }

halt:
    usysMsTimeStop(&curTime);

    if ( readBuf != NULL )
        free(readBuf);
    if ( writeBuf != NULL )
        free(writeBuf);

    return 0;
}

/***************************************************************************/
/* Function:    getRuntimeChar                                             */
/* Description: Thread that reads keyboard                                 */
/***************************************************************************/
static void *getRuntimeChar(void *voidp)
{
    exitThreadParms *eP = (exitThreadParms*) voidp;
    char ch;
    int ich;
    eP->running = 1;
    while (!(eP->timeToExit))
    {
        /* RTX has no stdio, so break on EOF so we won't spin */
        if ((ich = getchar()) == EOF)
            break;

        ch = toupper(ich); /* toupper may be a macro */
        eP->inputAvailable = 1;
        if (ch == 'Q')
        {
            eP->running = 0;
            eP->timeToExit = 1;
        }
        else if (ch == 'R')
            eP->resetFlag = 1;
        else if (ch == 'V')
            eP->showBadFlag = 1;
        else if (ch == 'H')
            eP->showOptions = 1;
        else if (ch == 'M')
            eP->modeChange = 1;
        else if (ch == 'A')
            eP->autoModeFlag = 1;
    }
    return voidp;
}

/**************************************************************************/
/*  function:     buildOptionsString                                      */
/*  description:  Updates the options string                              */
/**************************************************************************/
static void buildOptionsString( char *optionsString, iOptionsType *iO )
{
    char *str = optionsString;
    int off = 0;
    off += sprintf(&str[off],
              "gtrand %s-u %d -o 0x%x -l %d -b %d -f %#x -s %d -n %d -m %d -x %d",
              iO->doMaxLen  ? "-A ":"", iO->unit, iO->offset, iO->length,
              iO->bytesPerRW, iO->flags, iO->seconds, iO->niceMS, iO->mode,
              iO->numToDump);
}

/**************************************************************************/
/*  function:     parseCommand                                            */
/*  description:  Getting argv details into internal usable form          */
/**************************************************************************/
static int parseCommand(int argc,char **argv,iOptionsType *iO,char *optionsString)
{
    char *endptr, nullchar = 0;
    int  i, j, len, tookParam=0;                         /* setting defaults */
    iO->helpLevel = 1;        iO->length = 0x20000;    iO->seconds = -1;
    iO->niceMS = 0;           iO->numToDump = 5;       iO->mode = -1;
    iO->bytesPerRW = 0xf;

    /* Initialize optionsString with default options */
    buildOptionsString(optionsString, iO);

    if(argc == 1)                                      /* start processing */
        return 0;

    for(i = 1; i < argc; i++, tookParam=0)
    {
        len = strlen(argv[i]);
        if((argv[i][0] != '-') || (len < 2))
        {
            printf("\nERROR: Unexpected option: \"%s\"\n\n", argv[i]);
            return -1;
        }

        if ( (argv[i][1] == '-') )
        {
            if( !strcmp( &argv[i][2], "version" ) )
            {
                printf("%s\n", APP_VER_STRING);
                printf(" - built with API revision %s\n", scgtGetApiRevStr());
                exit (0);
            }
            else if( !strcmp( &argv[i][2], "help" ) )
            {
                iO->helpLevel=2;
                return 0;
            }
        }
                                         /* do options with arguments first*/
        for(j=1; j<len; j++)
        {
            if( (j == (len-1)) && ((i+1) < argc))
            {
                tookParam = 1;  endptr = &nullchar;
                /* test for options which take parameters */
                /* these options only valid as last char of -string */
                if(     argv[i][j]=='u')
                    iO->unit=strtoul(argv[i+1],&endptr,0);
                else if(argv[i][j]=='o')
                    iO->offset=strtoul(argv[i+1],&endptr,0);
                else if(argv[i][j]=='l')
                    iO->length=strtoul(argv[i+1],&endptr,0);
                else if(argv[i][j]=='b')
                    iO->bytesPerRW=strtoul(argv[i+1],&endptr,0);
                else if(argv[i][j]=='f')
                    iO->flags = strtoul(argv[i+1],&endptr,0);
                else if(argv[i][j]=='n')
                    iO->niceMS = strtoul(argv[i+1],&endptr,0);
                else if(argv[i][j]=='m')
                    iO->mode = strtoul(argv[i+1],&endptr,0);
                else if(argv[i][j]=='s')
                    iO->seconds = strtoul(argv[i+1],&endptr,0);
                else if(argv[i][j]=='x')
                    iO->numToDump = strtoul(argv[i+1],&endptr,0);
                else tookParam = 0;

                if( tookParam && (*endptr) )
                {
                   printf("\nInvalid parameter \"%s\" for option \"%c\"\n\n",
                           argv[i+1], argv[i][j]);
                   return -1;
                }
            }

            /* options without arguments now */
            if(toupper(argv[i][j])=='A') iO->doMaxLen = 1;
            else if(tolower(argv[i][j])=='h')
            {
                iO->helpLevel = (argc > 2) ? 3 : 2;
                return 0;
            }
            else if(!tookParam)
            {
                printf("\nERROR: Unexpected option: \"%c\"\n\n",argv[i][j]);
                return -1;
            }
        }
        if (tookParam) i++;
    }

    if ( (iO->flags & (FLAG_WRITE_READ|FLAG_RANDOM_ORDER)) && (iO->niceMS == 0) )
    {
        printf("WARNING: Consider using option -n # with -f 0x08 or -f 0x10 to avoid false\n"
               "         data errors from network latency on short length buffers.\n");
    }

    if( !iO->bytesPerRW )
        iO->bytesPerRW = 0xf;

    /* enforce 64-bit alignment regardless of size selected */
    iO->offset &= ~7;
    iO->length &= ~7;
    iO->helpLevel=0;

    buildOptionsString(optionsString, iO);
    return 0;
}

/**************************************************************************/
/*  function:     printHelpLevel1                                         */
/*  description:  Print hints and call more help if needed                */
/**************************************************************************/
static void printHelpLevel1( iOptionsType *iO, char *optionsString)
{
    printf( "%s\n\n", APP_VER_STRING );
    printf("Usage: gtrand [-Ah] [-u unit] [-o offset] [-l length] [-m mode]\n"
           "              [-b bytes] [-s seconds] [-f flags] [-n ms] [-x historySize]\n");
    printf("Defaults: %s\n", optionsString);
#ifdef PLATFORM_VXWORKS
    printf("VxWorks users: Enclose options list in a set of quotes \" \".\n");
#endif
    printf("\n");
    if(iO->helpLevel>1)
        printHelpLevel2();
    if(iO->helpLevel>2)
        printHelpLevel3();
}

/**************************************************************************/
/*  function:     printHelpLevel2                                         */
/*  description:  Print more hints                                        */
/**************************************************************************/
static void printHelpLevel2()
{
    printf(
"Options:\n"
"  -u # - unit/board number.\n"
"  -o # - shared GT memory offset (rounded down to 64bit alignment)\n"
"  -l # - length in bytes on which to operate (rounded down to 64bit alignment)\n"
"  -A   - all, operate on full memory/register range (overrides -o,-l)\n"
"  -b # - read/write operation size flags:  0x1 - bytes, 0x2 - 16bit words,\n"
"         0x4 - 32bit words, 0x8 - 64bit words, 0xF - mixed(default),\n"
"  -f # - operation modifier flags (positionally coded), default is 0:\n"
"         0x01 - enable display of errors (toggle at runtime with 'v').\n"
"         0x02 - halt on errors\n"
"         0x04 - allow prefetch errors\n"
"         0x08 - verify written buffers immediately following write\n"
"         0x10 - psuedo-random write/verify ordering\n"
"         0x20 - write buffers to GT memory with decreasing offset (slow!).\n"
"         0x40 - read buffers from GT memory with decreasing offset (slow!).\n"
"         0x80 - show test parameters before running\n"
"  -m # - data generation mode (default is -1 for 'auto-vary', see -h 1)\n"
"  -n # - nice-ness value (ms delay between write and verify)\n"
"  -s # - seconds to execute the test (default = forever)\n"
"  -x # - show test history for # previous test on error (requires -f 0x01).\n"
    );
    printf(
"Runtime options:\n"
"   q - quit,          v - toggle error display,        r - reset,\n"
"   h - runtime help,  m - toggle data pattern mode     a - toggle auto-mode\n"
    );
    printf("Note:  run `gtrand -h 1' for more information.\n");
}

/**************************************************************************/
/*  function:     printHelpLevel3                                         */
/*  description:  Print really detailed hints                             */
/**************************************************************************/
static void printHelpLevel3()
{
    printf(
"\n"
"   Gtrand performs PIO data transfers and verifies the data. Gtrand allows\n"
"the user to specify (-b) combinations of 8-bit, 16-bit, 32-bit, and 64-bit\n"
"word-sizes for use in data transfer. By default, the word-size is varied\n"
"among all sizes.\n"
"\n"
"   Gtrand operates on the range of GT memory specified by the -o and -l\n"
"options. Several (%d) non-overlapping subranges of this memory are randomly\n"
"selected for verification. By default, all the buffers are written before\n"
"any are verified. They are then verified in the order written. (See -f for\n"
"for variations to the default behavior.) This process repeats infinitely\n"
"or until the desired number of seconds (-s) have elapsed. The maximum length\n"
"of a subrange is %d, and the minumum is the word-size.\n"
"\n", NUM_TESTPARAMS, MAX_DATA_LEN);  
    printf(
"   The data pattern generation mode used by gtrand can be selected with the\n"
"-m option. The operating mode can be selected at startup, or changed with\n"
"runtime options 'm' and 'a'. The string '...' below means 'repeated for all\n"
"bytes in the word'. The modes are:"
"  0 - RANDOM but fixed within a packet,\n"
"  1 - RANDOM every word in every packet,\n"
"  2 - 0x55...-0xaa... and swapping,\n"
"  3 - 0x55...-0xaa... no swapping,\n"
"  4 - 0x00...-0xff... and swapping,\n"
"  5 - 0x00...-0xff... no swapping,\n"
" -1 - auto-vary, cycles through other modes.\n"
"\n");
    printf(
"   The behavior of gtrand can be modified with bitwise OR'ed -f flags.\n"
"The flags are:\n"
"  0x01 - enables the display of error information, and the (-x) history stack,\n"
"  0x02 - causes the test to exit after the first error is detected,\n"
"  0x04 - allows FW prefetch errors to occur (see note below),\n"
"  0x08 - causes verification of a write buffer immediately after writing,\n"
"  0x10 - causes verification to be randomly interspersed between writes,\n"
"  0x20 - writes buffers to GT memory with decreasing offset (this is slower),\n"
"  0x40 - reads buffers from GT memory with decreasing offset (this is slower),\n"
"  0x80 - causes the printout of test parameter before the test is performed.\n"
"\n");
    printf(
"Note: Prefetch errors can occur if two sequential GT memory buffers are\n"
"verified consecutively. This occurs when the first buffer (b1) is written\n"
"then read, which places several words of GT memory in the PIO prefetch cache,\n"
"and then the second buffer (b2) is written then read. When the read of b2\n"
"begins, the first one or more words are pulled from the prefetch cache, which\n"
"contains stale values. By default gtrand avoids such errors by reading the\n"
"last word of a buffer (to disrupt the prefetch cache) before retrieving the\n"
"rest of the buffer (in first to last ordering). However, this scheme can\n"
"still be foiled if the buffer is one word long and 64-bit in size on a 32-bit\n"
"CPU. Some compilers read the upper 32-bits before the lower 32-bits of the\n"
"64-bit word, causing the subsequent 'real' read of the upper 32-bit word\n"
"of data to still be pulled from the prefetch cache.\n"
"\n");
    printf(
"   The 'niceness' option -n allows for insertion of a # milli-second delay\n"
"before verification of each buffer. On most systems, the smallest realizable\n"
"delay is ~20 ms. The default value is 0 (no delay), which is fine with the\n"
"default behavior of writing all buffers before verifying. With flags -f 0x08\n"
"and 0x10, one should consider inserting this delay to avoid false data errors\n"
"on short length buffers, which can result from network latency or write\n"
"latency being larger than the application write-to-read turn-around latency.\n"
"\n");
    printf(
"GTRAND DISPLAY\n"
"\n"
"Error printouts use the following character indicators:\n"
"b    - word size in bytes for the test\n"
"l    - length of the test buffer\n"
"o    - GT memory offset having the error\n"
"ro   - relative offset from start of buffer\n"
"w    - the value written\n"
"r    - the value read\n"
"v    - XOR of w and r values\n"
"e    - errors detected so far in this buffer\n"
"\n");
    printf(
"The test stack of previous tests uses the following character indicators.\n"
"Only those that vary from error printouts are listed:\n"
"t-#  - indicates these are test params from the test # tests prior.\n"
"o    - base GT memory offset of the buffer\n"
"ol   - o + l, the next offset past the end of the buffer\n"
"d    - the 'direction' of the test, r for read/verify and w for write.\n"
"m    - the -m data mode\n"
"v    - the first data value in the write buffer (useful in some -m modes)\n"
"e    - total errors detected in the test buffer.\n"
"\n");
    printf(
"The default display uses the following character indicators:\n"
"dt   - The elapsed test time in seconds\n"
"Tx   - The number of tests performed\n"
"B    - Total bytes verified (including those in error)\n"
"Ex   - Total errors detected (sum of Ex# below).\n"
"Ex1  - Total errors detected in 8-bit  (1-byte) tests.\n"
"Ex2  - Total errors detected in 16-bit (2-byte) tests.\n"
"Ex4  - Total errors detected in 32-bit (4-byte) tests.\n"
"Ex8  - Total errors detected in 64-bit (8-byte) tests.\n"
"\n");
}
