/*
 *-----------------------------------------------------------------------------
 * The confidential and proprietary information contained in this file may
 * only be used by a person authorised under and to the extent permitted
 * by a subsisting licensing agreement from ARM Limited.
 *
 *            (C) COPYRIGHT 2010-2017   ARM Limited or its affiliates.
 *                ALL RIGHTS RESERVED
 *
 * This entire notice must be reproduced on all copies of this file
 * and copies of this file may only be made by a person if such person is
 * permitted to do so under the terms of a subsisting license agreement
 * from ARM Limited.
 *
 *      SVN Information
 *
 *      Checked In          : $Date: $
 *
 *      Revision            : $Revision: 366887 $
 *
 *      Release Information : SSE050-r0p1-00rel0
 *-----------------------------------------------------------------------------
 */

/*
  A test to check the functionality of Embedded Trace Macrocell (ETM)
*/
#include "SSE050.h"
#include <stdio.h>
#include "uart_stdout.h"
#include "SSE050_driver.h"
// general defines such as test_pass, and design configuration
#include "config_id.h"

#define HW32_REG(ADDRESS)  (*((volatile unsigned long  *)(ADDRESS)))
#define HW16_REG(ADDRESS)  (*((volatile unsigned short *)(ADDRESS)))
#define HW8_REG(ADDRESS)   (*((volatile unsigned char  *)(ADDRESS)))
#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))
#define ROM_ETM_COMPONENT  0xFFF42003
#define ROM_TPIU_COMPONENT 0xFFF41003
#define ETMIDR_REG_VALUE   0x4114F253
#define ETMIDR_OFFSET      0x11E4
#define ETMCR_OFFSET       0x1000
#define ETMSR_OFFSET       0x1010
#define ETMLAR_OFFSET      0x1FB0
#define ETMLSR_OFFSET      0x1FB4
#define ETMTRIGGER_OFFSET  0x1008
#define TPIU_CSPSR         0x4
#define TPIU_FFCR          0x304
#define TPIU_SPPR          0xF0
#define ETM_COMP_OFFSET    0x014
#define TPIU_COMP_OFFSET   0x10

void HardFault_Handler_c(unsigned int * hardfault_args, unsigned lr_value);
//
// Prototypes
//

void Start(void);
void Stop(void);

// Indirect the start and stop calls to avoid optimisation
void (*volatile Start_p) (void);
void (*volatile Stop_p) (void);

/* Global variables */
volatile int temp_data;
volatile uint32_t Errors = 0;
volatile uint32_t ExpectHardFault = 0;
volatile uint32_t HardFaultTaken = 0;
volatile uint32_t IntTaken = 0;
volatile uint32_t SleepOnExitTest = 0;

int          err_code = 0;
__IO uint32_t testdata = 0;

typedef void (* FuncPtr)(void);

int main (void)
{
  uint32_t tmp;

  UartStdOutInit();  // Initialize UART0 for printf (retargeting)

  // Connect the indirected functions. This ensures the start()
  // and stop() calls won't be inlined, so their addresses can
  // be used for DWT matching
  Start_p = &Start;
  Stop_p  = &Stop;

  //Test banner message
  puts("\nSSE-050 - Embedded Trace Macrocell test - revision $Revision: 366887 $\n");

  temp_data = 0;

   if(EXPECTED_ETM)
    {
      // Enable Trace and TPIU
      puts ("Enable Trace and TPIU core debug DEMCR write");
      CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA;

      tmp = TPIU->PID3;
      tmp = (tmp<<8) | TPIU->PID2;
      tmp = (tmp<<8) | TPIU->PID1;
      tmp = (tmp<<8) | TPIU->PID0;
      MSG(("TPIU Peripheral ID is %08x. Part %03x, Revision %x\n",tmp,tmp&0xFFF, (tmp>>20)& 0xF));

      // Configure TPIU for trace port mode, 4 bits
      TPIU->CSPSR = (1<<3);
      TPIU->SPPR = TPIU_PIN_TRACEPORT;

      // Enable synchronisation counter for TPIU
      DWT->CTRL |= DWT_CTRL_CYCCNTENA | DWT_CTRL_SYNCTAP24;

      // Enable ETM
      tmp = ETM->PID3;
      tmp = (tmp<<8) | ETM->PID2;
      tmp = (tmp<<8) | ETM->PID1;
      tmp = (tmp<<8) | ETM->PID0;
      MSG(("ETM  Peripheral ID is %08x. Part %03x, Revision %x\n",tmp,tmp&0xFFF, (tmp>>20)& 0xF));
      ETM->LAR = CS_UNLOCK;

      // Power Up ETM and initialise registers
      ETM->CR &= ~ETM_CR_PWRDN;
      ETM->CR |= ETM_CR_PROGBIT | ETM_CR_ETMEN | ETM_CR_TSEN;
      ETM->FFLR  = 0;                           // FifoFull not used
      ETM->CNTRLDVR1 = 0;                       // Counter1 not used
      ETM->TSEVR = ETM_EVT_NOTA | ETM_EVT_TRUE; // timestamp event never
      ETM->TRIGGER = ETM_EVT_A  | ETM_EVT_DWT0; // Trigger on DWT0
      ETM->TEEVR = ETM_EVT_A | ETM_EVT_TRUE;    // Trace Always
      ETM->TRACEIDR = SSE050_ETM_ID;            // 7 bit trace ID. Must be valid

      // At least there is one DWT comparator, use as trigger
      DWT->COMP0 = (uint32_t) &testdata; // testdata address
      DWT->MASK0 = 0x2;                  // Mask(0xFFFFFFFC) for Start address
      DWT->FUNCTION0 = DWT_FUNC_TRIG_WR; // DWT_ETM_TRIG_WR, Function set to Trigger on data write.

      // if EXPECTED_WPT = 4, 4 DWT comparators exist,
      // use COMP1 and COMP2 to set TraceEnable Start/Stop Embedded ICE control register
      if (EXPECTED_WPT == 4)
      {
        MSG(("Program DWT_COMP for START-STOP address"));
        ETM->TECR1 |= ETM_TECR1_USE_SS;           // TraceEnable address range comparators
        ETM->TESSEICR  = (ETM_TESSEICR_ICE2 << ETM_TESSEICR_STOP) | ETM_TESSEICR_ICE1;
                      // initialize the TraceEnable Start/Stop Embedded ICE control register

        // set DWT start/stop address
        DWT->COMP1 = (uint32_t)&Start; // start address
        DWT->MASK1 = 0x2;              // Mask(0xFFFFFFFC) for Start address
        DWT->COMP2 = (uint32_t)&Stop;  // stop address
        DWT->MASK2 = 0x2;              // Mask(0xFFFFFFFC) for Stop address
        DWT->FUNCTION1 = DWT_FUNC_TRIG_PC; // Function set to Trigger on PC match
        DWT->FUNCTION2 = DWT_FUNC_TRIG_PC; // Function set to Trigger on PC match
      }
      else
      {
        ETM->TECR1 &= ~ETM_TECR1_USE_SS;
        ETM->TESSEICR = 0x0;
      }

      // Clear programming bit
      MSG(("Tracing is now enabled\n"));
      ETM->CR &= ~ETM_CR_PROGBIT;

       Start_p();
      DWT->COMP3 = (uint32_t) &testdata; // testdata address
      // Ensure we use normal SLEEP - SLEEPDEEP should be clear
       SCB->SCR &= ~(1UL << 2);

        // Ensure timer interrupt is not pending before the test
        NVIC_ClearPendingIRQ(TIMER0_IRQn);

        // Set timer 0 to decrement from 0xF0 with internal clock,
        // interrupt enabled
        SSE050_timer_Init_IntClock(SSE050_TIMER0, 0xF0, 1);

        // Enable Interrupts
        NVIC_EnableIRQ(TIMER0_IRQn);

                  // Wait For Interrupt
        __WFI();  // Enter sleep mode
            // Wakeup when timer interrupt is triggered

        // Stop the timer and disable interrupt generation
        SSE050_timer_StopTimer(SSE050_TIMER0);
        SSE050_timer_DisableIRQ(SSE050_TIMER0);

       // Disable Interrupt and Interrupt Source
       NVIC_DisableIRQ(TIMER0_IRQn);

      // Generate timestamp
      __ISB();


      // check ETM status register start/stop bit.
      // It should be set if EXPECTED_WPT == 4 and start/stop is programmed
      // otherwise, it should be clear.
      tmp = ETM->SR & ETM_SR_SSTOP;
      if (EXPECTED_WPT == 4 ){
        if (tmp == 0) {
          MSG(("ERROR - ETM status register start/stop bit is not set.\n"));
          Errors++;
        } else {
          MSG((" CONFIG USED\n"));
          MSG((" DWT_COMP0 = TRIGGER\n"));
          MSG((" DWT_COMP1 = START ADDRESS\n"));
          MSG((" DWT_COMP2 = END_ADDRESS\n"));
        }
      } else {
        if (tmp != 0) {
          MSG(("ERROR - ETM status register start/stop bit is not clear 0x%x.\n", tmp));
          Errors++;
        }
        MSG((" USED CONFIG\n"));
        MSG((" DWT_COMP0 = TRIGGER\n"));
      }

      // Trace stop address has been set to the address of Function Stop(),
      // therefore, the ETM trace stops here.
      Stop_p();

      // Disable Interrupt
      NVIC_DisableIRQ(PORT0_ALL_IRQn);

      // Set programming bit to stop trace
      ETM->CR |= ETM_CR_PROGBIT;
      // Allow trace to drain. Have at most 32 bytes from ETM
      tmp = 3000;
      while(tmp !=0)
      {
        tmp --;
        if ((ETM->SR & ETM_SR_PROGBIT) !=0)
        {
          tmp = 0;
        }
      }
      if ((ETM->SR & ETM_SR_PROGBIT) ==0) {
        MSG(("ERROR - ETM has not acknowledged Progbit.\n"));
        MSG(("        Status Register %8x\n",ETM->SR));
        Errors++;
      }
      else
      {
        // allow time for 6 bytes in tpiu to drain
        testdata = 50;
        while(testdata !=0)
        {
          testdata--;
        }
        // Ensure tpiu is drained by forcing toggle of bit[24]
        DWT->CYCCNT = 1<<24;
        DWT->CYCCNT = 0;
        MSG(("ETM stopped and drained\n"));
      }
      if (Errors > 0) {
        printf ("\n** TEST FAILED **\n");
      } else {
        printf ("\n** TEST PASSED **\n");
      }


    }
    else
    {
      // Read ETM ID register, it should return '0' otherwise test will fail
      tmp = ETM->PID0;
      if (tmp != 0) {
        MSG(("** CHECK CONFIG : EXPECTED ETM not set, ETMPIDR0 is non-zero\n"));
        printf ("\n** TEST FAILED **\n");
      } else {
        MSG(("** EXPECTED ETM not set, ETMPIDR0 is zero \n"));
        printf ("\n** TEST PASSED **\n");
      }
    }
  UartEndSimulation();

  return Errors;
}


//
// Handlers
//

void HardFault_Handler()
{
  //
  // This test should not generate HardFault
  //
  // Not expecting a fault
  MSG(("Unexpected HardFault - FAIL\n"));
}

void Start()
{
  testdata++;
  return;
}

void Stop()
{
  testdata++;
  return;
}

void GPIO_IRQHandler()
{

  // Disable interrupt
  NVIC_DisableIRQ(PORT0_ALL_IRQn);

}


//
// Timer interrupt handler
//

void TIMER0_Handler(void)
{
  SSE050_timer_ClearIRQ(SSE050_TIMER0);
  // Signal to main code that the INT was taken
  IntTaken++;
  if (SleepOnExitTest==1){
    if (IntTaken==1){
      puts("  Enter sleep...");
      }

    if (IntTaken==2){
      // Disable sleep on exit feature
      SCB->SCR &= ~(1UL << 1);
      // Stop the timer and disable interrupt generation
      SSE050_timer_StopTimer(SSE050_TIMER0);
      SSE050_timer_DisableIRQ(SSE050_TIMER0);
      }
   } else {
    // Stop the timer and disable interrupt generation
    SSE050_timer_StopTimer(SSE050_TIMER0);
    SSE050_timer_DisableIRQ(SSE050_TIMER0);
  }

}

