/**
 * @file tpl_machine_arm_generic.c
 *
 * @section descr File description
 *
 * common routines and variables for generic ARM platform
 *
 * @section copyright Copyright
 *
 * Trampoline OS
 *
 * Trampoline is copyright (c) IRCCyN 2005+
 * Copyright ESEO for function and data structures documentation and ARM port
 * Trampoline is protected by the French intellectual property law.
 *
 * This software is distributed under the Lesser GNU Public Licence
 *
 * @section infos File informations
 *
 * $Date$
 * $Rev$
 * $Author$
 * $URL$
 */
#include "tpl_machine.h"
#include "tpl_machine_interface.h"
#include "tpl_os_application_def.h"
#include "tpl_os_definitions.h"
#include "tpl_os.h"
#if WITH_MEMORY_PROTECTION == YES
#include "tpl_memory_protection.h"
#endif
#if WITH_AUTOSAR == YES
#include "tpl_as_definitions.h"
#endif
#include "tpl_os_interrupt.h"

/*
 * Include the tpl_cortex_defenitions.h generated header that set the
 * priority of various Handlers
 */
#include "tpl_cortex_definitions.h"

#define OS_START_SEC_VAR_8BIT
#include "tpl_memmap.h"

tpl_bool tpl_isr2_disabled = FALSE;

#define OS_STOP_SEC_VAR_8BIT
#include "tpl_memmap.h"

#define OS_START_SEC_CODE
#include "tpl_memmap.h"

extern void decPeriode(void);
extern void test_toggle(void);

#define OS_STOP_SEC_CODE
#include "tpl_memmap.h"


#define API_START_SEC_CODE
#include "tpl_memmap.h"

#if TASK_COUNT > 0
extern FUNC(void, OS_CODE) CallTerminateTask(void);
#endif
#if ISR_COUNT > 0
extern FUNC(void, OS_CODE) CallTerminateISR2();
#endif

#define API_STOP_SEC_CODE
#include "tpl_memmap.h"

#define OS_START_SEC_CODE
#include "tpl_memmap.h"

/*
 * Generated functions prototypes
 */
extern FUNC(void, OS_CODE) tpl_init_external_interrupts();
extern FUNC(void, OS_CODE) tpl_init_it_priority();

FUNC (void, OS_CODE) tpl_init_machine_generic (void)
{
#if WITH_MEMORY_PROTECTION == YES
  tpl_init_mp();
#endif
}

FUNC (void, OS_CODE) tpl_init_machine_specific (void)
{
  tpl_set_systick_timer();
  tpl_init_external_interrupts();
  tpl_init_it_priority();
  /*
   * Set the SVC priority to KERNEL_PRIO
   */
  NVIC_SetPriority(SVCall_IRQn, KERNEL_PRIO_UNSHIFTED);
  /*
   * Switch to use PSP, unprivileged state
   */
  __set_CONTROL(0x3);
  /*
   * Instruction SynchronizationBarrier
   * Execute ISB after changing CONTROL (architectural recommendation)
   */
  __ISB();
}

/*
 * tpl_init_context initialize a context to prepare a task to run.
 * It sets up the stack and the entry point
 *
 * Initializing the context consists in the initializing the context structure.
 * The initialization of r4 to r11 is not mandatory but the PSP is:
 * +------------------+
 * | R4               | <- CTX_GPR4
 * +------------------+
 * | R5               | <- CTX_GPR5
 * +------------------+
 * | R6               | <- CTX_GPR6
 * +------------------+
 * | R7               | <- CTX_GPR7
 * +------------------+
 * | R8               | <- CTX_GPR8
 * +------------------+
 * | R9               | <- CTX_GPR9
 * +------------------+
 * | R10              | <- CTX_GPR10
 * +------------------+
 * | R11              | <- CTX_GPR11
 * +------------------+
 * | PSP (R13)        | <- CTX_PSP
 * +------------------+
 * And the initialization of the exception frame.
 * The initialization of r0 to r3 and r12 is not mandatory but the LR, the PC
 * and the xPSR is:
 * +-------------------------------+
 * | R0                            | <- PSP
 * +-------------------------------+
 * | R1                            | <- PSP+4
 * +-------------------------------+
 * | R2                            | <- PSP+8
 * +-------------------------------+
 * | R3                            | <- PSP+12
 * +-------------------------------+
 * | R12                           | <- PSP+16
 * +-------------------------------+
 * | LR (aka R14)                  | <- PSP+20
 * +-------------------------------+
 * | Return Address (saved PC/R15) | <- PSP+24
 * +-------------------------------+
 * | xPSR (bit 9 = 1)              | <- PSP+28
 * +-------------------------------+
 */

#define GPR_ON_EXCEPTION_FRAME  5 /* registers r0-r3 and r12 */
#define LR_IDX                  5
#define PC_IDX                  6
#define xPSR_IDX                7

FUNC(void, OS_CODE) tpl_init_context(
  CONST(tpl_proc_id, OS_APPL_DATA) proc_id)
{
#if WITH_PAINT_REGISTERS == YES || WITH_PAINT_STACK == YES
  VAR(int, AUTOMATIC) i;
#endif

  /* The pointer to the static descriptor of the process */
  CONSTP2CONST(tpl_proc_static, AUTOMATIC, OS_APPL_DATA) the_proc =
    tpl_stat_proc_table[proc_id];

  /* The pointer to the context of the process */
  CONSTP2VAR(arm_core_context, AUTOMATIC, OS_APPL_DATA) l_tpl_context =
    the_proc->context;

  /* The pointer to the stack of the process */
  CONSTP2VAR(tpl_stack_word, AUTOMATIC, OS_APPL_DATA) stack =
    the_proc->stack.stack_zone;

  /* The size of the stack in 32 bits word above the esception frame */
  CONST(uint32, AUTOMATIC) size_of_stack_above_exception_frame =
    (the_proc->stack.stack_size - ARM_CORE_EXCEPTION_FRAME_SIZE) >> 2;

  /* The pointer to the exception frame */
  CONSTP2VAR(tpl_stack_word, AUTOMATIC, OS_APPL_DATA) exception_frame =
    stack + size_of_stack_above_exception_frame;

#if WITH_PAINT_REGISTERS == YES
  /*
   * Set GPRs to the stack pattern to track changes.
   * This is not mandatory, so it is done only if the OIL OS attribute
   * PAINT_REGISTERS is set to TRUE. Otherwise the registers are not
   * initialized.
   */
  l_tpl_context->gpr4 =
  l_tpl_context->gpr5 =
  l_tpl_context->gpr6 =
  l_tpl_context->gpr7 =
  l_tpl_context->gpr8 =
  l_tpl_context->gpr9 =
  l_tpl_context->gpr10 =
  l_tpl_context->gpr11 = OS_STACK_PATTERN;

  /*
   * Paint the registers on the exception frame : r0, r1, r2, r3 and r12
   */
  for (i = 0; i < GPR_ON_EXCEPTION_FRAME; i++)
  {
    exception_frame[i] = OS_STACK_PATTERN;
  }
#endif
  /* sp : setup initial stack pointer.
   * The SP points to (stack_zone + stack_size - ARM_CORE_EXCEPTION_FRAME_SIZE)
   * ARM_CORE_EXCEPTION_FRAME_SIZE is the frame pushed by the core at each exception.
   * This frame consists in pushing xpsr, pc, lr, r12, r3, r2, r1, r0
   *  */
  l_tpl_context->stackPointer = (uint32)exception_frame;

#if WITH_PAINT_STACK == YES
  /*
   * Paint the stack with a pattern. This is not mandatory, so it is done
   * only if the OIL OS attribute PAINT_STACK is set to TRUE. Otherwise the
   * stack above the exception frame is not initialized.
   */
  for (i = 0; i < size_of_stack_above_exception_frame; i++)
  {
    stack[i] = OS_STACK_PATTERN;
  }
#endif

  /* lr
   * Set the return address of the task/isr. This is usefull in case the
   * user forgets to call TerminateTask/TerminateISR
   * MISRA RULE 1,45,85 VIOLATION: the function pointer is used and stored
   * in a 32bits variable, but as the Os is dependant on the target,
   * the behaviour is controlled
   */
#if TASK_COUNT > 0
#if   ISR_COUNT > 0
  /*
   * at least a task and at least an ISR2 exist. So we have to deal with
   * what to put in the return function
   */
  exception_frame[LR_IDX] =
    (IS_ROUTINE == the_proc->type) ?
      (uint32)(CallTerminateISR2) :
      (uint32)(CallTerminateTask) ;
#else
  /*
   * At least a task but no ISR2. The return function is CallTerminateTask
   */
  exception_frame[LR_IDX] = (uint32)(CallTerminateTask);
#endif
#else
#if   ISR_COUNT > 0
  /*
   * No task but at least an ISR2 exists. The return function is CallTerminateISR2
   */
  exception_frame[LR_IDX] = (uint32)(CallTerminateISR2);
#else
  /*
   * Never go there : no task and no ISR
   */
  exception_frame[LR_IDX] = NULL;
#endif
#endif
  /* pc */
  exception_frame[PC_IDX] = (uint32)(the_proc->entry);
  /* xpsr */
  exception_frame[xPSR_IDX] = 0x01000000;

#if WITH_AUTOSAR_STACK_MONITORING == YES && WITH_PAINT_STACK == NO
  (*(uint8 *)(the_proc->stack.stack_zone)) = OS_STACK_PATTERN;
#endif
}

FUNC(uint8, OS_CODE) tpl_check_stack_footprint(CONST(tpl_proc_id, OS_APPL_DATA) proc_id)
{
  uint8 tmp;
  /*to do*/
  tmp=0;
  return tmp;
}

/*
 * tpl_sleep is used by the idle task
 */
void idle_function(void)
{
    while(1);
}

void tpl_init_machine()
{
    tpl_init_machine_generic ();
    tpl_init_machine_specific();
}

void tpl_shutdown ()
{
    /* FIXME: this is does not conform to AUTOSAR OS specifications,
     * should return to main with initial context */
    //DISABLE_FIQ ();
    //DISABLE_IRQ ();
	// remove ITs

  	// spurious events can wake up processor :
	__asm__ (" CPSID	I");

	// we need a loop to ensure sleep
	while(1)
	{
		__asm__ ("   wfi ;"); 	// go to sleep until NMI/HARD FAULT/RESET
	}

    /* TODO : fall into very low consumption mode : all
     * internal CPU clocks are disabled.
     */

    while (1);
}

#define OS_STOP_SEC_CODE
#include "tpl_memmap.h"

/* End of file tpl_machine_arm_generic.c */