/*
 *
 * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ 
 * 
 * 
 *  Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions 
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the   
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
*/

/*  
 *  This is a simple example which brings up HyperLink.  It can work
 *  between two devices or in loopback on one device.
 *
 *  It is suggested to use the platform GEL file, since as evmc6678l.gel,
 *  that comes with CCS 5.x or newer.  This sets up the system PLL and
 *  DDR.  This example configures the PLL associated with hyperlink.
 *
 *  Once HyperLink is configured, data is exchanged.  The buffer is 
 *  arranged such that the logic is agnostic to whether there are two
 *  devices connected, or it is a single device in loopback.
 *
 *  There are two types of data exchanges.  The first is a token exchange,
 *  where a token (sequence number) is passed symmetrically between the
 *  two device.  This exchange uses the dataBuffer.cpuTokens and 
 *  dataBuffer.dmaTokens.  This is a synchronous transfer, because
 *  each side waits until it sees the appropriate response from the
 *  other side before proceeding.
 *
 *  Since the transaction is symmetric it also works with loopback.  
 *  The token is actually passed each direction twice, such that each device
 *  issues remote reads and remote writes.
 *
 *  The second type of data exchange is asynchronous block transfer.  
 *  This transfer can be run on either or both sides independantly.  
 *  A block of data is remotely written then read back via
 *  dataBuffer.cpuBlock and dataBuffer.dmaBlock.
 *
 *  Only the cpu transfers are currently implemented (cpuTokens and
 *  dmaTokens)
 *  
 *  The defines below are configured for the C6678 and C6670 EVM
 *  coupled with a SAS cable.
 *
 *  For other configurations, change the definitions starting 
 *  at "Beginning of user configuration" as required.
 *
 */

#include <string.h>
/* Use regular "printf" when not using BIOS */
//#include <xdc/runtime/System.h>
#define System_printf printf
#include <stdio.h>
#include <ti/drv/hyplnk/hyplnk.h>
#include <ti/csl/csl_bootcfgAux.h>
#include <ti/csl/csl_cacheAux.h>
#include <ti/csl/cslr_device.h>
#include <ti/csl/csl_pscAux.h>
#include <c6x.h>

#include "hyplnkLLDIFace.h"
#include "hyplnkLLDCfg.h"
#include "hyplnkIsr.h"
#include "hyplnkPlatCfg.h"

/*=============================================================================
 *=============================================================================
 * Beginning of user configuration
 *=============================================================================
 ============================================================================*/

/*****************************************************************************
 * There are configuration parameters such as link speed and loopback
 * in ti/drv/hyplnk/example/common/hyplnkLLDCfg.h
 ****************************************************************************/

/*****************************************************************************
 * Select one or more test cases
 *
 * The synchronized token exchange requires the same SW on each endpoint
 *
 * The asynchronous block transfers do not require any SW running on the other 
 * endpoint, however both sides can optionally run the test at the same time.
 *****************************************************************************/

/* Synchronized token passing between two endpoints using CPU IO */
#define hyplnk_EXAMPLE_TEST_CPU_TOKEN_EXCHANGE

/* Synchronized token passing between two endpoints using EDMA IO */
//#define hyplnk_EXAMPLE_TEST_DMA_TOKEN_EXCHANGE

/* Asynchronous block transfers using CPU (memset/memcmp) */
#define hyplnk_EXAMPLE_TEST_CPU_BLOCK_XFER

/* Asynchronous block transfers using EDMA */
//#define hyplnk_EXAMPLE_TEST_DMA_BLOCK_XFER
//

/*****************************************************************************
 * Cache line size (128 works on any location) 
 *****************************************************************************/
#define hyplnk_EXAMPLE_LINE_SIZE      128

/*****************************************************************************
 * Size of block transfer
 *****************************************************************************/
#define hyplnk_EXAMPLE_BLOCK_BUF_SIZE 4096

/*=============================================================================
 *=============================================================================
 * End of user configuration
 *=============================================================================
 ============================================================================*/

/* Adjust timings based on loopback */
#ifdef hyplnk_EXAMPLE_LOOPBACK
  #define hyplnk_EXAMPLE_FIRST_TOKEN_WAIT_LIMIT   hyplnk_EXAMPLE_uS_TO_CYCLES(   100)
  #define hyplnk_EXAMPLE_BULK_TOKEN_WAIT_LIMIT    hyplnk_EXAMPLE_uS_TO_CYCLES(   100)
#else
  #define hyplnk_EXAMPLE_FIRST_TOKEN_WAIT_LIMIT   hyplnk_EXAMPLE_uS_TO_CYCLES(30000000)
  #ifdef hyplnk_EXAMPLE_TEST_CPU_BLOCK_XFER
    #define hyplnk_EXAMPLE_BULK_TOKEN_WAIT_LIMIT  hyplnk_EXAMPLE_uS_TO_CYCLES(    1000)
  #else
    #define hyplnk_EXAMPLE_BULK_TOKEN_WAIT_LIMIT  hyplnk_EXAMPLE_uS_TO_CYCLES(      10)
  #endif
#endif

/* Number of times to pass tokens between remote and local */
#ifdef hyplnk_EXAMPLE_TEST_CPU_BLOCK_XFER
  #define hyplnk_EXAMPLE_NTOKENS    0x10000 /* token pass round trip cycles */
#else
  #define hyplnk_EXAMPLE_NTOKENS    0x1000000 /* token pass round trip cycles */
#endif
/* Number of iterations (* NTOKENS); 0 is forever */
#define hyplnk_EXAMPLE_NITERS     0       

/* Convert speed to string for printing */
#if defined(hyplnk_EXAMPLE_SERRATE_01p250)
  #define hyplnk_EXAMPLE_LINK_SPEED "1.25G"
#elif defined(hyplnk_EXAMPLE_SERRATE_03p125)
  #define hyplnk_EXAMPLE_LINK_SPEED "3.125"
#elif defined(hyplnk_EXAMPLE_SERRATE_06p250)
  #define hyplnk_EXAMPLE_LINK_SPEED "6.25"
#elif defined(hyplnk_EXAMPLE_SERRATE_07p500)
  #define hyplnk_EXAMPLE_LINK_SPEED "7.5"
#elif defined(hyplnk_EXAMPLE_SERRATE_10p000)
  #define hyplnk_EXAMPLE_LINK_SPEED "10.0"
#elif defined(hyplnk_EXAMPLE_SERRATE_12p500)
  #define hyplnk_EXAMPLE_LINK_SPEED "12.5"
#else
  #define hyplnk_EXAMPLE_LINK_SPEED "Unknown - add #define"
#endif

#ifdef hyplnk_EXAMPLE_ALLOW_4_LANES
  #define hyplnk_EXAMPLE_MAX_LANES 4
#else
  #define hyplnk_EXAMPLE_MAX_LANES 1
#endif

typedef struct {
  uint32_t value;
  char     padding[hyplnk_EXAMPLE_LINE_SIZE - 4];
} hyplnkExampleOneToken_t;

/* bidirectional token buffers */
typedef struct {
  hyplnkExampleOneToken_t srcLoc2Rem;
  hyplnkExampleOneToken_t srcRem2Loc;
  hyplnkExampleOneToken_t dstLoc2Rem;
  hyplnkExampleOneToken_t dstRem2Loc;
} hyplnkExampleTokenBuffer_t;

/* Memory block transfer buffer */
typedef struct {
  uint32_t buffer[hyplnk_EXAMPLE_BLOCK_BUF_SIZE];
} hyplnkExampleBlockBuffer_t;

/* Combined shared data structure containing tokens and blocks */
typedef struct {
#ifdef hyplnk_EXAMPLE_TEST_CPU_TOKEN_EXCHANGE
  hyplnkExampleTokenBuffer_t cpuTokens;
#endif
#ifdef hyplnk_EXAMPLE_TEST_DMA_TOKEN_EXCHANGE
  hyplnkExampleTokenBuffer_t dmaTokens;
#endif
#ifdef hyplnk_EXAMPLE_TEST_CPU_BLOCK_XFER
  hyplnkExampleBlockBuffer_t cpuBlock;
#endif
#ifdef hyplnk_EXAMPLE_TEST_DMA_BLOCK_XFER
  hyplnkExampleBlockBuffer_t dmaBlock;
#endif
  /* If user didn't define any tests, this blank struct will generate error */
} hyplnkExampleDataBuffer_t;

#ifdef hyplnk_EXAMPLE_TEST_CPU_TOKEN_EXCHANGE
/*****************************************************************************
 * Write val into ptr, writeback cache, and wait for result to land.
 * This function is agnostic of physical location of ptr (L2, MSMC, DDR, etc)
 ****************************************************************************/
void hyplnkExampleSet (volatile uint32_t *ptr, uint32_t val)
{
  *ptr = val;
  CACHE_wbL1d ((void *)ptr, sizeof(*ptr), CACHE_FENCE_WAIT);
  CACHE_wbL2  ((void *)ptr, sizeof(*ptr), CACHE_FENCE_WAIT);
}

/*****************************************************************************
 * Invalidate cache, then read ptr
 * This function is agnostic of physical location of ptr (L2, MSMC, DDR, etc)
 ****************************************************************************/
uint32_t hyplnkExampleGet (volatile uint32_t *ptr)
{
  CACHE_invL1d ((void *)ptr, sizeof(*ptr), CACHE_WAIT);
  CACHE_invL2  ((void *)ptr, sizeof(*ptr), CACHE_WAIT);

  return *ptr;
}

/*****************************************************************************
 * Pass one "token" by writing to the "write" address then attempting
 * to read the same value back via the "read" address.  
 *
 * The CPU generates the transactions
 *
 * 0: pass
 * 1: fail
 ****************************************************************************/
int hyplnkExampleCPUTokenExchange (hyplnkExampleTokenBuffer_t *local, 
                                   hyplnkExampleTokenBuffer_t *remote, 
                                   uint32_t token,
                                   uint64_t timeLimit)
{
  uint64_t startTime;
  int done = 0;
  uint32_t val1, val2;
  int other_side_copied = 0, i_copied = 0; 

  /* Write my srcLoc2Rem, and the other side's srcRem2Loc */
  hyplnkExampleSet(&local->srcLoc2Rem.value, token);
  hyplnkExampleSet(&remote->srcRem2Loc.value, token);

  /* Wait for the remote side to copy:
   * local->dstLoc2Rem.value = local->srcLoc2Rem.value 
   * and
   * remote->dstRem2Loc.value = remote->srcRem2Loc.value
   */
  startTime = hyplnkExampleReadTime();
  while (((hyplnkExampleReadTime() - startTime) < timeLimit) && (!done)) {
    /* see if the other side did his copy */
    if (! other_side_copied) {
      val1 = hyplnkExampleGet(&local->dstLoc2Rem.value);
      val2 = hyplnkExampleGet(&remote->dstRem2Loc.value);
      other_side_copied = ((val1 == token) && (val2 == token));
    }
    /* Do my copy */
    if (! i_copied) {
      val1 = hyplnkExampleGet(&remote->srcLoc2Rem.value);
      val2 = hyplnkExampleGet(&local->srcRem2Loc.value);
      i_copied = ((val1 == token) && (val2 == token));
      if (i_copied) {
        hyplnkExampleSet(&remote->dstLoc2Rem.value, val1);
        hyplnkExampleSet(&local->dstRem2Loc.value, val2);
      }
    }
    done = i_copied && other_side_copied;
  }
  if (! done) {
    System_printf("fail %d %d\n", other_side_copied, i_copied);
  }
  return ! done;
}
#endif /* hyplnk_EXAMPLE_TEST_CPU_TOKEN_EXCHANGE */

#ifdef hyplnk_EXAMPLE_TEST_DMA_TOKEN_EXCHANGE
/*****************************************************************************
 * Pass one "token" by writing to the "write" address then attempting
 * to read the same value back via the "read" address.  
 *
 * The EDMA generates the transactions
 *
 * 0: pass
 * 1: fail
 ****************************************************************************/
int hyplnkExampleDMATokenExchange (hyplnkExampleTokenBuffer_t *local, 
                                   hyplnkExampleTokenBuffer_t *remote, 
                                   uint32_t token,
                                   uint64_t timeLimit)
{
  return 1; /* ERROR: Not implemented */
}
#endif /* hyplnk_EXAMPLE_TEST_DMA_TOKEN_EXCHANGE */

#ifdef hyplnk_EXAMPLE_TEST_CPU_BLOCK_XFER
/*****************************************************************************
 * Write a block of data to remote side then verify it
 *
 * The CPU generates the transactions via memcpy and memset
 *
 * 0: pass
 * 1: fail
 ****************************************************************************/
int hyplnkExampleCPUBlockXfer (hyplnkExampleBlockBuffer_t *remote, 
                               uint32_t value,
                               uint64_t timeLimit)
{
  int i;
  uint32_t *remoteBuf;
  int fail = 0;

  hyplnkExampleCheckOneStat (hyplnk_LOCATION_LOCAL, "before block IO", 1);

  /* Write the data to the remote buffer */
  for (i = 0, remoteBuf = remote->buffer; 
       i < hyplnk_EXAMPLE_BLOCK_BUF_SIZE; 
       i++) {
    *remoteBuf++ = value;
  }

  CACHE_wbInvL1d ((void *)remote, sizeof(*remote), CACHE_WAIT);
  CACHE_wbInvL2  ((void *)remote, sizeof(*remote), CACHE_WAIT);
  _mfence();

  /* Verify the data in the remote buffer */
  for (i = 0, remoteBuf = remote->buffer; 
       i < hyplnk_EXAMPLE_BLOCK_BUF_SIZE; 
       i++) {
    if (*remoteBuf++ != value) fail++;
  }

  return fail;
}
#endif /* hyplnk_EXAMPLE_TEST_CPU_BLOCK_XFER */

#ifdef hyplnk_EXAMPLE_TEST_DMA_BLOCK_XFER
/*****************************************************************************
 * Write a block of data to remote side then verify it
 *
 * The EDMA generates the transactions
 *
 * 0: pass
 * 1: fail
 ****************************************************************************/
int hyplnkExampleDMABlockXfer (hyplnkExampleBlockBuffer_t *remote, 
                               uint32_t value,
                               uint64_t timeLimit)
{
  return 1; /* ERROR: Not implemented */
}
#endif /* hyplnk_EXAMPLE_TEST_DMA_BLOCK_XFER */

/*****************************************************************************
 * Perform an IO cycle (token pass or read/write)
 *
 * 0: pass
 * 1: fail
 ****************************************************************************/
int hyplnkExampleIOCycle (hyplnkExampleDataBuffer_t *local, 
                          hyplnkExampleDataBuffer_t *remote, 
                          uint32_t value,
                          uint64_t timeLimit)
{
  int oneRetVal;
  int retval = 0;
#ifdef hyplnk_EXAMPLE_TEST_CPU_TOKEN_EXCHANGE
  oneRetVal = hyplnkExampleCPUTokenExchange (
                &local->cpuTokens, &remote->cpuTokens, value, timeLimit);
  if (oneRetVal) {
    System_printf ("hyplnkExampleCPUTokenExchange failed: %d\n", oneRetVal);
    retval = 1;
  }
#endif
#ifdef hyplnk_EXAMPLE_TEST_DMA_TOKEN_EXCHANGE
  oneRetVal = hyplnkExampleDMATokenExchange (
                &local->dmaTokens, &remote->dmaTokens, value, timeLimit);
  if (oneRetVal) {
    System_printf ("hyplnkExampleDMATokenExchange failed: %d\n", oneRetVal);
    retval = 1;
  }
#endif
#ifdef hyplnk_EXAMPLE_TEST_CPU_BLOCK_XFER
  oneRetVal = hyplnkExampleCPUBlockXfer (&remote->cpuBlock, value, timeLimit);
  if (oneRetVal) {
    System_printf ("hyplnkExampleCPUBlockXfer failed: %d\n", oneRetVal);
    retval = 1;
  }
#endif
#ifdef hyplnk_EXAMPLE_TEST_DMA_BLOCK_XFER
  oneRetVal = hyplnkExampleDMABlockXfer (&remote->dmaBlock, value, timeLimit);
  if (oneRetVal) {
    System_printf ("hyplnkExampleDMABlockXfer failed: %d\n", oneRetVal);
    retval = 1;
  }
#endif

  return retval;
}



/* This is the actual data buffer that will be accessed.  
 *
 * When accessing this location directly, the program is playing
 * the role of the remote device (remember we are in loopback).
 *
 * If this is placed in an L2 RAM there are no other dependancies.
 *
 * If this is placed in MSMC or DDR RAM, then the SMS
 * or SES MPAX must be configured to allow the access.
 */
#pragma DATA_SECTION (dataBuffer, ".bss:remoteable");
#pragma DATA_ALIGN (dataBuffer, hyplnk_EXAMPLE_LINE_SIZE);
hyplnkExampleDataBuffer_t dataBuffer;

/* 
 * This pointer is the local address within the Hyperlink's address
 * space which will access dataBuffer via HyperLink.
 *
 */
hyplnkExampleDataBuffer_t *bufferThroughHypLnk;

void main (void)
{
  hyplnkRet_e retVal;
  uint32_t token;
  int i;
  uint64_t start_time, total_time;
  TSCL = 1;
  int iteration = 0;
  uint8_t mar;

  System_printf ("Version #: 0x%08x; string %s\n", Hyplnk_getVersion(), Hyplnk_getVersionStr());

  /* Hyperlink is not up yet, so we don't have to worry about concurrence */
  memset (&dataBuffer, 0, sizeof(dataBuffer));

  /* Set up the system PLL, PSC, and DDR as required for this HW */
  System_printf ("About to do system setup (PLL, PSC, and DDR)\n");
  if ((retVal = hyplnkExampleSysSetup()) != hyplnk_RET_OK) {
    System_printf ("system setup failed (%d)\n", (int)retVal);
    exit(1);
  }
  System_printf ("system setup worked\n");

  /* Install the ISR */
#ifdef hyplnk_EXAMPLE_ERROR_INTERRUPT
  hyplnkExampleInstallIsr();
#endif

  /* Enable the peripheral */
  System_printf ("About to set up HyperLink Peripheral\n");
  if ((retVal = hyplnkExamplePeriphSetup()) != hyplnk_RET_OK) {
    System_printf ("system setup failed (%d)\n", (int)retVal);
    exit(1);
  }
  System_printf ("Peripheral setup worked\n");

  /* Set up address mapsrc */
  if ((retVal = hyplnkExampleAddrMap (&dataBuffer, (void **)&bufferThroughHypLnk)) != hyplnk_RET_OK) {
    System_printf ("Address map setup failed (%d)\n", (int)retVal);
    exit(1);
  }

  /* Enable caching of hlink area */
  mar = ((uint32_t)bufferThroughHypLnk >> 24);
  CACHE_setL2Size (CACHE_32KCACHE);
  CACHE_enableCaching (mar);

  /* Perform one read/write cycle */
  System_printf ("About to read/write once\n");
  if (hyplnkExampleIOCycle (&dataBuffer, bufferThroughHypLnk, 0xfee1dead,
                            hyplnk_EXAMPLE_FIRST_TOKEN_WAIT_LIMIT)) {
    System_printf ("Single write failed\n");
    exit(1);
  }
  System_printf ("Single write test passed\n");

  /* Bulk token pass test */
  for (iteration = 0; 
       (hyplnk_EXAMPLE_NITERS == 0) || (iteration < hyplnk_EXAMPLE_NITERS); 
       iteration++) {
    System_printf ("About to pass %d tokens; iteration = %d\n", 
                   hyplnk_EXAMPLE_NTOKENS, iteration);

    /* Run one more read/write as a barrier (to compensate for above printf) */
    /* Do not printf unless test fails (else printf will CAUSE fail!) */
    if (hyplnkExampleIOCycle (&dataBuffer, bufferThroughHypLnk, 0xbabeface,
                              hyplnk_EXAMPLE_FIRST_TOKEN_WAIT_LIMIT)) {
      System_printf ("Single write failed (when synchronizing bulk test)\n");
      exit(1);
    }

    /* Bulk test */
    start_time = hyplnkExampleReadTime();
    for (i = 0, token = 0; i < hyplnk_EXAMPLE_NTOKENS; i++) {
      if (hyplnkExampleIOCycle (&dataBuffer, bufferThroughHypLnk, ++token,
                                  hyplnk_EXAMPLE_BULK_TOKEN_WAIT_LIMIT)) {
        System_printf ("Failed to pass token %08x from remote to local\n", token);
        exit(1);
      }
    }
    total_time = hyplnkExampleReadTime() - start_time;

    System_printf ("=== this is not an optimized example ===\n");
    System_printf ("Link Speed is %d * %s Gbps\n", 
                   hyplnk_EXAMPLE_MAX_LANES, hyplnk_EXAMPLE_LINK_SPEED);
    System_printf ("Passed %d tokens round trip (read+write through hyplnk) in %d Mcycles\n", 
                   hyplnk_EXAMPLE_NTOKENS, (unsigned int)(total_time/1000000));
    System_printf ("Approximately %d cycles per round-trip\n", 
                   (unsigned int)(total_time/hyplnk_EXAMPLE_NTOKENS));
    System_printf ("=== this is not an optimized example ===\n");

    System_printf ("Checking statistics\n");
    hyplnkExampleCheckOneStat (hyplnk_LOCATION_LOCAL, "after passing tokens", 0);
    hyplnkExampleCheckOneStat (hyplnk_LOCATION_REMOTE, "after passing tokens", 0);
  }
  /* #if just suppresses compiler warning when set to run forever */
#if hyplnk_EXAMPLE_NITERS > 0
  System_printf ("Test Completed successfully\n");
#endif
}


