/**
 * \file IfxCpu.h
 * \brief CPU  basic functionality
 * \ingroup IfxLld_Cpu
 *
 * \version iLLD_1_0_1_11_0
 * \copyright Copyright (c) 2019 Infineon Technologies AG. All rights reserved.
 *
 *
 *                                 IMPORTANT NOTICE
 *
 *
 * Use of this file is subject to the terms of use agreed between (i) you or 
 * the company in which ordinary course of business you are acting and (ii) 
 * Infineon Technologies AG or its licensees. If and as long as no such 
 * terms of use are agreed, use of this file is subject to following:


 * Boost Software License - Version 1.0 - August 17th, 2003

 * Permission is hereby granted, free of charge, to any person or 
 * organization obtaining a copy of the software and accompanying 
 * documentation covered by this license (the "Software") to use, reproduce,
 * display, distribute, execute, and transmit the Software, and to prepare
 * derivative works of the Software, and to permit third-parties to whom the 
 * Software is furnished to do so, all subject to the following:

 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer, must
 * be included in all copies of the Software, in whole or in part, and all
 * derivative works of the Software, unless such copies or derivative works are
 * solely in the form of machine-executable object code generated by a source
 * language processor.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.

 *
 *
 * \defgroup IfxLld_Cpu_Std_Core Cpu Core Functions
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_Interrupt Interrupt Utility Functions
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_Cache Cache Management Functions
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_PerformanceCounter Performance Counter Functions
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_Synchronization Synchronization Functions
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_Utility Cpu Utility Functions
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_Enum Enumerations
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_DataStructures Data Structures
 * \ingroup IfxLld_Cpu_Std
 */

#ifndef IFXCPU_H
#define IFXCPU_H 1

/******************************************************************************/
/*----------------------------------Includes----------------------------------*/
/******************************************************************************/

#include "_Impl/IfxCpu_cfg.h"
#include "IfxSrc_reg.h"
#include "IfxScu_reg.h"
#include "IfxStm_reg.h"
#include "_Impl/IfxScu_cfg.h"
#include "_Utilities/Ifx_Assert.h"
#include "Scu/Std/IfxScuWdt.h"
#include "Scu/Std/IfxScuCcu.h"

/******************************************************************************/
/*-----------------------------------Macros-----------------------------------*/
/******************************************************************************/

/** \brief Convert local DSPR address to global DSPR address which can be accessed from the SRI bus.
 * Use this macro to convert a local DSPR address (in segment 0xd00.....) to
 * a global DSPR address (in segment 0x700....., 0x600....., 0x500..... downwards) depending on
 * the CPU number.
 * Example usage:
 *  \code
 *     dmaChConfig.sourceAddress      = IFXCPU_GLB_ADDR_DSPR(IfxCpu_getCoreId(), &sourceBuffer[i][0]);
 *     dmaChConfig.destinationAddress = IFXCPU_GLB_ADDR_DSPR(IfxCpu_getCoreId(), &destinationBuffer[i][0]);
 *  \endcode
 */
#define IFXCPU_GLB_ADDR_DSPR(cpu, address) ((((((unsigned)(address) & 0xF0000000) == 0xD0000000) ? ((((unsigned)(address) & 0x000fffff) | 0x70000000) - ((cpu) * 0x10000000)) : (unsigned)(address))))

/** \brief Convert local PSPR address to global PSPR address which can be accessed from the SRI bus.
 * Use this macro to convert a local PSPR address (in segment 0xc......) to
 * a global PSPR address (in segment 0x701....., 0x601....., 0x501..... downwards) depending on
 * the CPU number.
 *
 *   Example usage:
 *   \code
 *     dmaChConfig.sourceAddress      = IFXCPU_GLB_ADDR_PSPR(IfxCpu_getCoreId(), &sourceBufferInPsprMemory);
 *     dmaChConfig.destinationAddress = IFXCPU_GLB_ADDR_PSPR(IfxCpu_getCoreId(), &destinationBufferInPsprMemory);
 *   \endcode
 */
#define IFXCPU_GLB_ADDR_PSPR(cpu, address) ((((unsigned)(address) & 0x000fffff) | 0x70100000) - ((cpu) * 0x10000000))

/******************************************************************************/
/*------------------------------Type Definitions------------------------------*/
/******************************************************************************/

/** \brief Lock type Spin lock
 */
typedef unsigned int IfxCpu_spinLock;

/** \brief Lock type Mutex lock
 */
typedef unsigned int IfxCpu_mutexLock;

/** \brief Event used for synchronisation.
 */
typedef unsigned int IfxCpu_syncEvent;

/******************************************************************************/
/*--------------------------------Enumerations--------------------------------*/
/******************************************************************************/

/** \addtogroup IfxLld_Cpu_Std_Enum
 * \{ */
/** \brief Enumeration for the Cpu mode
 */
typedef enum
{
    IfxCpu_CoreMode_halt,
    IfxCpu_CoreMode_run,
    IfxCpu_CoreMode_idle,
    IfxCpu_CoreMode_sleep,
    IfxCpu_CoreMode_stby,
    IfxCpu_CoreMode_unknown
} IfxCpu_CoreMode;

/** \brief Performance conunter modes
 */
typedef enum
{
    IfxCpu_CounterMode_normal = 0,  /**< \brief Normal counter mode:the counter increments on their respective triggers */
    IfxCpu_CounterMode_task   = 1   /**< \brief Normal counter mode:additional gating control from the debug unit which allows the data gathered in the performance counters to be filtered by some specific criteria */
} IfxCpu_CounterMode;

/** \} */

/******************************************************************************/
/*-----------------------------Data Structures--------------------------------*/
/******************************************************************************/

/** \addtogroup IfxLld_Cpu_Std_DataStructures
 * \{ */
/** \brief counter
 */
typedef struct
{
    uint32  counter;        /**< \brief Counter value */
    boolean overlfow;       /**< \brief sticky overlfow */
} IfxCpu_Counter;

/** \} */

/** \addtogroup IfxLld_Cpu_Std_DataStructures
 * \{ */
/** \brief Performance counter result
 */
typedef struct
{
    IfxCpu_Counter instruction;       /**< \brief Instruction counter */
    IfxCpu_Counter clock;             /**< \brief CPU clock counter */
    IfxCpu_Counter counter1;          /**< \brief Multi counter 1 */
    IfxCpu_Counter counter2;          /**< \brief Multi counter 2 */
    IfxCpu_Counter counter3;          /**< \brief Multi counter 3 */
} IfxCpu_Perf;

/** \} */

/** \addtogroup IfxLld_Cpu_Std_Core
 * \{ */

/******************************************************************************/
/*-------------------------Inline Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief API to get the address for CPU HW module register memory map
 * \param cpu Resource index of the CPU
 * \return CPU module register address
 */
IFX_INLINE Ifx_CPU *IfxCpu_getAddress(IfxCpu_ResourceCpu cpu);

/** \brief API to get core id of the CPU of the caller.
 * Caution: Core id of the cpu's may not be continguous and shouldn't be used to index cpu.
 * Use IfxCpu_getCoreIndex() to get cpu no.
 * \return Resource index of the CPU.
 */
IFX_INLINE IfxCpu_Id IfxCpu_getCoreId(void);

/** \brief API to get cpu index of the caller CPU.
 * Note: This api can be used whereever cpu no/index is needed.
 * \return Resource index of the CPU.
 */
IFX_INLINE IfxCpu_ResourceCpu IfxCpu_getCoreIndex(void);

/** \brief API to initialize the context save area of the CPU where this is called.
 *
 * This API can initialize the CSA of the host CPU where this API is called. This API
 * shall not be used to initialize the CSA of another CPU
 * \param csaBegin Pointer to start of context save area
 * \param csaEnd Pointer to end of context save area
 * \return None
 */
IFX_INLINE void IfxCpu_initCSA(uint32 *csaBegin, uint32 *csaEnd);

/** \brief Set/Clear safety task identifier (PSW.S) on current CPU
 * \return None
 */
IFX_INLINE void IfxCpu_setSafetyTaskIdentifier(boolean safetyId);

/** \brief Triggers Software Reset
 * \return None
 */
IFX_INLINE void IfxCpu_triggerSwReset(void);

/******************************************************************************/
/*-------------------------Global Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief API to get current mode of CPU
 * \param cpu Pointer to the CPU HW module (register memory map)
 * \return Current mode of the CPU
 */
IFX_EXTERN IfxCpu_CoreMode IfxCpu_getCoreMode(Ifx_CPU *cpu);

/** \brief API to get current mode of CPU
 * \param cpu Pointer to the CPU HW module (register memory map)
 * \return Resource index of the CPU
 */
IFX_EXTERN IfxCpu_ResourceCpu IfxCpu_getIndex(Ifx_CPU *cpu);

/** \brief API to set mode of the CPU
 * \param cpu Pointer to the CPU HW module (register memory map)
 * \param mode CPU mode to be set by this API
 * \return Success status of the activity (setting the core mode).
 * \retval TRUE: If the activity successfully be performed.
 * \retval FALSE: If the activity can't be performed.
 */
IFX_EXTERN boolean IfxCpu_setCoreMode(Ifx_CPU *cpu, IfxCpu_CoreMode mode);

/** \brief API to set the program counter for the CPU specified.
 * \param cpu Pointer to the CPU HW module (register memory map)
 * \param programCounter Program counter value to be set
 * \return success status of the activity (setting program counter value).
 * \retval TRUE: If the activity successfully be performed.
 * \retval FALSE: If the activity can't be performed
 */
IFX_EXTERN boolean IfxCpu_setProgramCounter(Ifx_CPU *cpu, uint32 programCounter);

/** \brief API to set the program counter for the CPU specified and start the CPU
 * \param cpu Pointer to the CPU HW module (register memory map)
 * \param programCounter Program counter value to start the CPU
 * \return success status of the activity (setting program counter value).
 * \retval TRUE: If the activity successfully be performed.
 * \retval FALSE: If the activity can't be performed
 */
IFX_EXTERN boolean IfxCpu_startCore(Ifx_CPU *cpu, uint32 programCounter);

/** \} */

/** \addtogroup IfxLld_Cpu_Std_Interrupt
 * \{ */

/******************************************************************************/
/*-------------------------Inline Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief API to get the status of global interrupt enable (ICR.IE) for the CPU which calls this API
 * This API provides the status of CPU where this API is called
 * \return Status of global interrupt enable bit.
 * \retval TRUE: Global interrupts enabled.
 * \retval FALSE: Global interrupts disabled
 */
IFX_INLINE boolean IfxCpu_areInterruptsEnabled(void);

/** \brief API to disable global interrupt and return the previous status.
 *
 * This API can be used only to disable the global interrupts of caller CPU. It cannot be
 * used for this activity towards other CPUs
 * \return Previous status of global interrupt enable bit.
 * \retval TRUE: Previously, global interrupts enabled.
 * \retval FALSE: Previously, global interrupts disabled
 */
IFX_INLINE boolean IfxCpu_disableInterrupts(void);

/** \brief API to enable global interrupt.
 * This API simply enables the global interrupt.
 * \return None
 */
IFX_INLINE void IfxCpu_enableInterrupts(void);

/** \brief Disable the Global Interrupt
 * \return None
 */
IFX_INLINE void IfxCpu_forceDisableInterrupts(void);

/** \brief API to restore global interrupt with that of the passed parameter.
 *
 * This API can be used only to disable the global interrupts of caller CPU. It cannot be
 * used for this activity towards other CPUs
 * \param enabled Previous status of the global interrupt enable bit
 * \return None
 */
IFX_INLINE void IfxCpu_restoreInterrupts(boolean enabled);

/** \} */

/** \addtogroup IfxLld_Cpu_Std_Cache
 * \{ */

/******************************************************************************/
/*-------------------------Inline Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief API to enable/ disable the data cacheability for selected segments
 * With this API cacheability for one or more segment can be enabled/disabled for the CPU core where this API is called.
 * \Note This API is to be called only if the PCACHE or DCACHE are not enabled before
 * \param segmentNumberMask Mask where bitfield 0 represents segment 0 and bitfield 16 represent segment F.
 * \param enable TRUE: to enable the cacheability for selected segment, FALSE: to disable.
 * \return None
 */
IFX_INLINE void IfxCpu_enableSegmentSpecificDataAccessCacheability(uint16 segmentNumberMask, boolean enable);

/** \brief API to enable/ disable the instruction cacheability for selected segments
 * With this API cacheability for one or more segment can be enabled/disabled for the CPU core where this API is called.
 * \Note This API is to be called only if the PCACHE or DCACHE are not enabled before
 * \param segmentNumberMask Mask where bitfield 0 represents segment 0 and bitfield 16 represent segment F.
 * \param enable TRUE: to enable the cacheability for selected segment, FALSE: to disable.
 * \return None
 */
IFX_INLINE void IfxCpu_enableSegmentSpecificInstructionAccessCacheability(uint16 segmentNumberMask, boolean enable);

/** \brief API to invalidate the program cache
 * \return None
 */
IFX_INLINE void IfxCpu_invalidateProgramCache(void);

/** \brief API to determine if an address is in a cachable or non-cachable Flash/LMU section
 * \param address Address
 * \return Status TRUE/FALSE
 */
IFX_INLINE boolean IfxCpu_isAddressCachable(void *address);

/** \brief API to enable or bypass the data cache for the CPU which calls this API.
 *
 * This API can be used only to enable or bypass the data cache of caller CPU. It cannot be
 * used for this activity towards other CPUs
 * \param enable Command to enable or bypass the data cache
 * TRUE: Enable the data cache.
 * FALSE: Bypass the data cache.
 * \return None
 */
IFX_INLINE void IfxCpu_setDataCache(boolean enable);

/** \brief API to enable or bypass the program cache for the CPU which calls this API.
 *
 * This API can be used only to enable or bypass the program cache of caller CPU. It cannot be
 * used for this activity towards other CPUs
 * \param enable Command to enable or bypass the program cache.
 * TRUE: Enable the program cache.
 * FALSE: Bypass the program cache
 * \return None
 */
IFX_INLINE void IfxCpu_setProgramCache(boolean enable);

/** \} */

/** \addtogroup IfxLld_Cpu_Std_PerformanceCounter
 * \{ */

/******************************************************************************/
/*-------------------------Inline Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief API to read the clock counter for the CPU which calls this API.
 *
 * This API can be used to read clock counter of only the caller CPU. It cannot be
 * used for this activity towards other CPUs.
 * \return Counter value. 0 to 0x7FFFFFFF.
 */
IFX_INLINE uint32 IfxCpu_getClockCounter(void);

/** \brief API to get sticky overflow bit of clock counter for the CPU, which calls this API.
 *
 * This API can be used to get sticky overflow bit of clock counter of only the caller CPU.
 * It cannot be used for this activity towards other CPUs.
 * This API also clears the sticky overflow after the read. While reading the sticky bit this API disables
 * the counter for short time. (otherwise sticky bit cannot be cleared). This API shall be used after
 * reading the counter
 * \return Status of sticky overflow bit.
 * \retval TRUE: Sticky overflow bit is set.
 * \retval FALSE: Sticky overflow bit is reset
 */
IFX_INLINE boolean IfxCpu_getClockCounterStickyOverflow(void);

/** \brief API to read the instruction counter for the CPU which calls this API.
 *
 * This API can be used to read instruction counter of only the caller CPU. It cannot be
 * used for this activity towards other CPUs
 * \return Counter value. 0 to 0x7FFFFFFF.
 */
IFX_INLINE uint32 IfxCpu_getInstructionCounter(void);

/** \brief API to get sticky overflow bit of Instruction counter for the CPU, which calls this API.
 *
 * This API can be used to get sticky overflow bit of Instruction counter of only the caller CPU.
 * It cannot be used for this activity towards other CPUs.
 * This API also clears the sticky overflow after the read. While reading the sticky bit this API disables
 * the counter for short time. (otherwise sticky bit cannot be cleared). This API shall be used after
 * reading the counter
 * \return Status of sticky overflow bit.
 * \retval TRUE: Sticky overflow bit is set.
 * \retval FALSE: Sticky overflow bit is reset
 */
IFX_INLINE boolean IfxCpu_getInstructionCounterStickyOverflow(void);

/** \brief API to read the performance counter for the CPU which calls this API.
 * \param address Address
 * \return counter value
 */
IFX_INLINE uint32 IfxCpu_getPerformanceCounter(uint16 address);

/** \brief API to get sticky overflow bit of performance counter for the CPU, which calls this API.
 * This is generic function to get sticky overflow bit of any performance counters
 * \param address Address
 * \return Status
 */
IFX_INLINE boolean IfxCpu_getPerformanceCounterStickyOverflow(uint16 address);

/** \brief Reset and start instruction, clock and multi counters
 *
 * Reset and start CCNT, ICNT, M1CNT, M2CNT, M3CNT. the overflow bits are cleared.
 * \param mode Counter mode
 * \return None
 */
IFX_INLINE void IfxCpu_resetAndStartCounters(IfxCpu_CounterMode mode);

/** \brief API to enable or disable performance counter for the CPU which calls this API.
 *
 * This API can be used to enable or disable performance counter of only the caller CPU. It cannot be
 * used for this activity towards other CPUs.
 * \param enable enable Command to enable or disable the performance counter.
 * TRUE: Enable the performance counter.
 * FALSE: Disable the performance counter
 * \return None
 */
IFX_INLINE void IfxCpu_setPerformanceCountersEnableBit(uint32 enable);

#if !((defined(__cplusplus)) && (defined(__TASKING__)))
/** \brief Stop instruction and clock counters, return their values
 *
 * Stop CCNT, ICNT, M1CNT, M2CNT, M3CNT and return their values;
 *  \Note The CCTRL is reset to 0, for more accurate measurements and has to be initialized again before strating the next performance measurement.
 * \return Performance counter result
 */
IFX_INLINE IfxCpu_Perf IfxCpu_stopCounters(void);
#endif

/** \brief API to update clock counter for the CPU which calls this API.
 *
 * This API can be used to update clock counter of only the caller CPU. It cannot be
 * used for this activity towards other CPUs.
 * \param count Counter value to be updated. 0 to 0x7FFFFFFF
 * \return None
 */
IFX_INLINE void IfxCpu_updateClockCounter(uint32 count);

/** \brief API to update Instruction counter for the CPU which calls this API.
 *
 * This API can be used to update Instruction counter of only the caller CPU. It cannot be
 * used for this activity towards other CPUs.
 * \param count Counter value to be updated. 0 to 0x7FFFFFFF
 * \return None
 */
IFX_INLINE void IfxCpu_updateInstructionCounter(uint32 count);

/** \brief API to update performance counter for the CPU which calls this API.
 * This is generic function to update any of the performance counters
 * \param address Address
 * \param count Count
 * \return None
 */
IFX_INLINE void IfxCpu_updatePerformanceCounter(uint32 address, uint32 count);

/** \} */

/** \addtogroup IfxLld_Cpu_Std_Synchronization
 * \{ */

/******************************************************************************/
/*-------------------------Global Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief API to acquire the mutex (binary semaphore).
 *
 * This API can be used to acquire/get the mutex.
 * \param lock lock pointer
 * \return TRUE : lock acquired successfully. FALSE: Failed to acquire the lock
 *
 * \code
 *    IfxCpu_mutexLock resourceLock;
 *    boolean flag = IfxCpu_acquireMutex(&resourceLock);
 *    if (flag){
 *      // critical section
 *      IfxCpu_releaseMutex(&resourceLock);
 *    }
 * \endcode
 *
 */
IFX_EXTERN boolean IfxCpu_acquireMutex(IfxCpu_mutexLock *lock);

/** \brief API to unlock the mutex .
 *
 * This API can be used to unlock the previously acquired mutex
 * \param lock lock pointer
 * \return None
 *
 * \code
 *    IfxCpu_mutexLock resourceLock;
 *    boolean flag = IfxCpu_acquireMutex(&resourceLock);
 *    if (flag){
 *      // critical section
 *      IfxCpu_releaseMutex(&resourceLock);
 *    }
 * \endcode
 *
 */
IFX_EXTERN void IfxCpu_releaseMutex(IfxCpu_mutexLock *lock);

/** \brief API to unlock the resource .
 *
 * This API can be used to unlock the previously acquired lock
 * \param lock lock pointer
 * \return None
 */
IFX_EXTERN void IfxCpu_resetSpinLock(IfxCpu_spinLock *lock);

/** \brief API to lock the resource in spin mode with the given timeout.
 *
 * This API can be used to spin lock for the lock for the given timeout period.
 * \param lock lock pointer
 * \param timeoutCount loop counter value used for timeout to acquire lock
 * \return TRUE : lock acquired successfully. FALSE: Failed to acquire the lock
 *
 * \code
 *    IfxCpu_spinLock resourceLock;
 *    boolean flag = IfxCpu_setSpinLock(&resourceLock, 0xFFFF);
 *    if (flag){
 *      // critical section
 *      IfxCpu_resetSpinLock(&resourceLock);
 *    }
 * \endcode
 *
 */
IFX_EXTERN boolean IfxCpu_setSpinLock(IfxCpu_spinLock *lock, uint32 timeoutCount);

/** \} */

/** \addtogroup IfxLld_Cpu_Std_Utility
 * \{ */

/******************************************************************************/
/*-------------------------Global Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief API to get random value
 * \param seed Pointer to seed value
 * \return random value
 */
IFX_EXTERN uint32 IfxCpu_getRandomValue(uint32 *seed);

/** \brief API to get random value with in the range
 * \param seed Pointer to seed value
 * \param min minimum range value
 * \param max maximum range value
 * \return random value
 */
IFX_EXTERN uint32 IfxCpu_getRandomValueWithinRange(uint32 *seed, uint32 min, uint32 max);

/** \brief This function waits till all the cores have set their corresponding bits in the event. This function along with
 * IfxCpu_emitEvent() are used to achieve the synchronisation between the configured cores. By default
 * "IFXCPU_CFG_ALLCORE_DONE" macro defined for all the cores. In case the user wants to check for
 * synchronisation among the required cores, the macro can be redefined with the value accroing to the
 * CORE_ID register.
 * The IfxCpu_emitEvent() is to be used in the Main functions of the Cores where the user wants to check for synchronisation.
 *
 * e.g:
 * 1. Check for synchronisation between core 0 and core 5
 * # define 0x41U
 * 2. Check for synchronisation between core 0 to core 5
 * # define 0x5FU
 *
 * Note:
 * Core id values read from CORE_ID register will be as shown below. The value indicates the position of the bit needs to be set while building the macro.
 * Core 0: 0
 * Core 1: 1
 * Core 2: 2
 * Core 3: 3
 * Core 4: 4
 * Core 5: 6
 * \param event Synchronous Event
 * \param timeoutMilliSec timeout in millisec
 * \return Error condition
 *
 * The functions IfxCpu_waitEvent and IfxCpu_emitEvent are used to achieve synchronisation between all cores (i.e individual cores wait till all cores have reached the synchronisation point). The IfxCpu_waitEvent returns 1 incase a timeout occurs.
 *
 * \code
 * // Global variable. preferably located in  shared memory.
 * IfxCpu_syncEvent event;
 * boolean errorVal;
 *
 * // Below code should be repeated in Each core
 *
 * // Upon reaching Synchonisation point
 * IfxCpu_emitEvent(&event);
 * errorVal = IfxCpu_waitEvent(&event, timeoutMilliSec); // timeoutMilliSec is timeout value to wait
 *
 * \endcode
 *
 */
IFX_EXTERN boolean IfxCpu_waitEvent(IfxCpu_syncEvent *event, uint32 timeoutMilliSec);

/** \brief This function sets a bit corresponding to the core in the event.
 * \param event Synchronous Event
 * \return None
 *
 * A coding example can be found in \ref IfxCpu_waitEvent
 *
 */
IFX_EXTERN void IfxCpu_emitEvent(IfxCpu_syncEvent *event);

/** \} */

/******************************************************************************/
/*---------------------Inline Function Implementations------------------------*/
/******************************************************************************/

IFX_INLINE boolean IfxCpu_areInterruptsEnabled(void)
{
    Ifx_CPU_ICR reg;
    reg.U = __mfcr(CPU_ICR);
    return reg.B.IE != 0;
}


IFX_INLINE boolean IfxCpu_disableInterrupts(void)
{
    boolean enabled;
    enabled = IfxCpu_areInterruptsEnabled();
    __disable();
    __nop();
    return enabled;
}


IFX_INLINE void IfxCpu_enableInterrupts(void)
{
    __enable();
}


IFX_INLINE void IfxCpu_enableSegmentSpecificDataAccessCacheability(uint16 segmentNumberMask, boolean enable)
{
    uint32 cpu_pmaVal;
    uint16 checkRestrictionMask;
    uint32 coreIndex   = IfxCpu_getCoreIndex();
    uint16 wdtPassword = IfxScuWdt_getCpuWatchdogPasswordInline(&MODULE_SCU.WDTCPU[coreIndex]);

    /*resolve the restrictions*/
    /*In PMA0 Segment-C and Segment[7-CoreID] must have the same value */
    checkRestrictionMask = ((uint16)1 << (7 - coreIndex)) | ((uint16)1 << 0xC);

    if ((segmentNumberMask & checkRestrictionMask) != 0)
    {
        segmentNumberMask |= checkRestrictionMask;
    }

    cpu_pmaVal = __mfcr(CPU_PMA0);                                                              /* Read the CPU_PMA0 */

    cpu_pmaVal = enable ? (cpu_pmaVal | segmentNumberMask) : (cpu_pmaVal & ~segmentNumberMask); /* enable or disable the corresponding bitfield */

    /*The CPU_PMA registers are ENDINIT protected*/
    IfxScuWdt_clearCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
    /*When changing the value of the CPU_PMAx registers both the instruction and data caches should be invalidated*/
    /*Write to PMA0 register for selecting the cacheability for data cache*/
    __dsync();      /* DSYNC instruction should be executed immediately prior to the MTCR*/
    __mtcr(CPU_PMA0, cpu_pmaVal);
    __isync();      /* ISYNC instruction executed immediately following MTCR */
    IfxScuWdt_setCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
}


IFX_INLINE void IfxCpu_enableSegmentSpecificInstructionAccessCacheability(uint16 segmentNumberMask, boolean enable)
{
    uint32 cpu_pmaVal;
    uint16 checkRestrictionMask;
    uint32 coreIndex   = IfxCpu_getCoreIndex();
    uint16 wdtPassword = IfxScuWdt_getCpuWatchdogPasswordInline(&MODULE_SCU.WDTCPU[coreIndex]);

    /*resolve the restrictions*/
    /*In PMA1 Segment-D and Segment[7-CoreID] must have the same value */
    checkRestrictionMask = ((uint16)1 << (7 - coreIndex)) | ((uint16)1 << 0xD);

    if ((segmentNumberMask & checkRestrictionMask) != 0)
    {
        segmentNumberMask |= checkRestrictionMask;
    }

    cpu_pmaVal = __mfcr(CPU_PMA1);                                                              /* Read the CPU_PMA1 */

    cpu_pmaVal = enable ? (cpu_pmaVal | segmentNumberMask) : (cpu_pmaVal & ~segmentNumberMask); /* enable or disable the corresponding bitfield */

    /*The CPU_PMA registers are ENDINIT protected*/
    IfxScuWdt_clearCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
    /*When changing the value of the CPU_PMAx registers both the instruction and data caches should be invalidated*/
    /*Write to PMA1 register for selecting the cacheability for data cache*/
    __dsync();      /* DSYNC instruction should be executed immediately prior to the MTCR */
    __mtcr(CPU_PMA1, cpu_pmaVal);
    __isync();      /* ISYNC instruction executed immediately following MTCR */
    IfxScuWdt_setCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
}


IFX_INLINE void IfxCpu_forceDisableInterrupts(void)
{
    __disable();
    __nop();
}


IFX_INLINE Ifx_CPU *IfxCpu_getAddress(IfxCpu_ResourceCpu cpu)
{
    Ifx_CPU *module;

    if (cpu < IfxCpu_ResourceCpu_none)
    {
        module = (Ifx_CPU *)IfxCpu_cfg_indexMap[cpu].module;
    }
    else
    {
        module = NULL_PTR;
    }

    return module;
}


IFX_INLINE uint32 IfxCpu_getClockCounter(void)
{
    return IfxCpu_getPerformanceCounter(CPU_CCNT);
}


IFX_INLINE boolean IfxCpu_getClockCounterStickyOverflow(void)
{
    return IfxCpu_getPerformanceCounterStickyOverflow(CPU_CCNT);
}


IFX_INLINE IfxCpu_Id IfxCpu_getCoreId(void)
{
    Ifx_CPU_CORE_ID reg;
    reg.U = __mfcr(CPU_CORE_ID);
    return (IfxCpu_Id)reg.B.CORE_ID;
}


IFX_INLINE IfxCpu_ResourceCpu IfxCpu_getCoreIndex(void)
{
    Ifx_CPU_CORE_ID reg;
    reg.U = __mfcr(CPU_CORE_ID);
    return (IfxCpu_ResourceCpu)reg.B.CORE_ID;
}


IFX_INLINE uint32 IfxCpu_getInstructionCounter(void)
{
    return IfxCpu_getPerformanceCounter(CPU_ICNT);
}


IFX_INLINE boolean IfxCpu_getInstructionCounterStickyOverflow(void)
{
    return IfxCpu_getPerformanceCounterStickyOverflow(CPU_ICNT);
}


IFX_INLINE uint32 IfxCpu_getPerformanceCounter(uint16 address)
{
    Ifx_CPU_CCNT ccnt;

    if (address == CPU_CCNT)
    {
        ccnt.U = __mfcr(CPU_CCNT);
    }
    else if (address == CPU_ICNT)
    {
        ccnt.U = __mfcr(CPU_ICNT);
    }
    else if (address == CPU_M1CNT)
    {
        ccnt.U = __mfcr(CPU_M1CNT);
    }
    else if (address == CPU_M2CNT)
    {
        ccnt.U = __mfcr(CPU_M2CNT);
    }
    else if (address == CPU_M3CNT)
    {
        ccnt.U = __mfcr(CPU_M3CNT);
    }

    return ccnt.B.CountValue;
}


IFX_INLINE boolean IfxCpu_getPerformanceCounterStickyOverflow(uint16 address)
{
    Ifx_CPU_CCNT ccnt;

    if (address == CPU_CCNT)
    {
        ccnt.U = __mfcr(CPU_CCNT);
    }
    else if (address == CPU_ICNT)
    {
        ccnt.U = __mfcr(CPU_ICNT);
    }
    else if (address == CPU_M1CNT)
    {
        ccnt.U = __mfcr(CPU_M1CNT);
    }
    else if (address == CPU_M2CNT)
    {
        ccnt.U = __mfcr(CPU_M2CNT);
    }
    else if (address == CPU_M3CNT)
    {
        ccnt.U = __mfcr(CPU_M3CNT);
    }

    return ccnt.B.SOvf;
}


IFX_INLINE void IfxCpu_initCSA(uint32 *csaBegin, uint32 *csaEnd)
{
    uint32  k;
    uint32  nxt_cxi_val = 0;
    uint32 *prvCsa      = 0U;
    uint32 *nxtCsa      = csaBegin;
    uint32  numOfCsa    = (((uint32)csaEnd - (uint32)csaBegin) / 64U);

    for (k = 0; k < numOfCsa; k++)
    {
        nxt_cxi_val = ((uint32)nxtCsa & (0XFU << 28)) >> 12 | ((uint32)nxtCsa & (0XFFFFU << 6)) >> 6;

        if (k == 0)
        {
            __mtcr(CPU_FCX, nxt_cxi_val);   /* store the new pcxi value to LCX */
        }
        else
        {
            *prvCsa = nxt_cxi_val;  /* Store null pointer in last CSA (= very first time!) */
        }

        if (k == (numOfCsa - 3U))
        {
            __mtcr(CPU_LCX, nxt_cxi_val);   /* Last but 2 context save area is pointed in LCX to know if there is CSA depletion */
        }

        prvCsa  = (uint32 *)nxtCsa;
        nxtCsa += 16;           /* next CSA */
    }

    *prvCsa = 0;
}


IFX_INLINE void IfxCpu_invalidateProgramCache(void)
{
    uint16 cpuWdtPassword = IfxScuWdt_getCpuWatchdogPassword();
    {
        IfxScuWdt_clearCpuEndinit(cpuWdtPassword);
        Ifx_CPU_PCON1 pcon1;
        pcon1.U       = __mfcr(CPU_PCON1);
        pcon1.B.PCINV = 1;
        __mtcr(CPU_PCON1, pcon1.U);
        IfxScuWdt_setCpuEndinit(cpuWdtPassword);
    }
}


IFX_INLINE boolean IfxCpu_isAddressCachable(void *address)
{
    uint8 segment = (uint32)address >> 24;
    return ((segment == IFXCPU_CACHABLE_FLASH_SEGMENT) || (segment == IFXCPU_CACHABLE_LMU_SEGMENT)) ? TRUE : FALSE;
}


IFX_INLINE void IfxCpu_resetAndStartCounters(IfxCpu_CounterMode mode)
{
    Ifx_CPU_CCTRL cctrl;
    cctrl.U    = __mfcr(CPU_CCTRL);
    /*Disable the counters */
    cctrl.B.CE = 0;
    __mtcr(CPU_CCTRL, cctrl.U);

    /* reset the counters */
    __mtcr(CPU_CCNT, 0);
    __mtcr(CPU_ICNT, 0);
    __mtcr(CPU_M1CNT, 0);
    __mtcr(CPU_M2CNT, 0);
    __mtcr(CPU_M3CNT, 0);

    /*Enable the counters, set the counter mode */
    cctrl.B.CE = 1;
    cctrl.B.CM = mode;
    __mtcr(CPU_CCTRL, cctrl.U);
}


IFX_INLINE void IfxCpu_restoreInterrupts(boolean enabled)
{
    if (enabled != FALSE)
    {
        __enable();
    }
}


IFX_INLINE void IfxCpu_setDataCache(boolean enable)
{
    uint32 coreIndex   = IfxCpu_getCoreIndex();
    uint16 wdtPassword = IfxScuWdt_getCpuWatchdogPasswordInline(&MODULE_SCU.WDTCPU[coreIndex]);
    /*PCACHE enable steps */
    {                           /* Step 1: Set PCBYP to 0 if cache is enabled */
        IfxScuWdt_clearCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
        Ifx_CPU_DCON0 dcon0;
        dcon0.U       = 0;
        dcon0.B.DCBYP = enable ? 0 : 1; /*depending on the enable bypas bit is reset/set */
        __mtcr(CPU_DCON0, dcon0.U);
        IfxScuWdt_setCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
    }
    /* Step 2: Call Isync */
    __isync();
}


IFX_INLINE void IfxCpu_setPerformanceCountersEnableBit(uint32 enable)
{
    Ifx_CPU_CCTRL cctrl;
    cctrl.U    = __mfcr(CPU_CCTRL);
    cctrl.B.CE = enable;
    __mtcr(CPU_CCTRL, cctrl.U);
}


IFX_INLINE void IfxCpu_setProgramCache(boolean enable)
{
    if (enable)
    {                           /* Step 3: Initiate invalidation of current cache contents if any */
        Ifx_CPU_PCON1 pcon1;
        pcon1.U       = 0;
        pcon1.B.PCINV = 1;
        __mtcr(CPU_PCON1, pcon1.U);
    }

    uint32 coreIndex   = IfxCpu_getCoreIndex();
    uint16 wdtPassword = IfxScuWdt_getCpuWatchdogPasswordInline(&MODULE_SCU.WDTCPU[coreIndex]);
    /*PCACHE enable steps */
    {                           /* Step 1: Set PCBYP to 0 if cache is enabled */
        IfxScuWdt_clearCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
        Ifx_CPU_PCON0 pcon0;
        pcon0.U       = 0;
        pcon0.B.PCBYP = enable ? 0 : 1; /*depending on the enable bypass bit is reset/set */
        __mtcr(CPU_PCON0, pcon0.U);
        IfxScuWdt_setCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
    }
    /* Step 2: Call Isync */
    __isync();
}


IFX_INLINE void IfxCpu_setSafetyTaskIdentifier(boolean safetyId)
{
    Ifx_CPU_PSW psw;
    IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, (safetyId == 0 || safetyId == 1));
    psw.U   = __mfcr(CPU_PSW);
    psw.B.S = safetyId;
    __mtcr(CPU_PSW, (uint32)psw.U);
}


#if !((defined(__cplusplus)) && (defined(__TASKING__)))
IFX_INLINE IfxCpu_Perf IfxCpu_stopCounters(void)
{
    IfxCpu_Perf result;
    /*Disable the counters, reset the control reg */
    /* Use inline assembly to ensure constant implementation, and execution of the measurement routines */
    __stopPerfCounters();

    Ifx_CPU_CCNT ccnt;
    ccnt.U                = __mfcr(CPU_CCNT);
    result.clock.counter  = ccnt.B.CountValue;
    result.clock.overlfow = ccnt.B.SOvf;

    Ifx_CPU_ICNT icnt;
    icnt.U                      = __mfcr(CPU_ICNT);
    result.instruction.counter  = icnt.B.CountValue;
    result.instruction.overlfow = icnt.B.SOvf;

    Ifx_CPU_M1CNT m1cnt;
    m1cnt.U                  = __mfcr(CPU_M1CNT);
    result.counter1.counter  = m1cnt.B.CountValue;
    result.counter1.overlfow = m1cnt.B.SOvf;

    Ifx_CPU_M2CNT m2cnt;
    m2cnt.U                  = __mfcr(CPU_M2CNT);
    result.counter2.counter  = m2cnt.B.CountValue;
    result.counter2.overlfow = m2cnt.B.SOvf;

    Ifx_CPU_M3CNT m3cnt;
    m3cnt.U                  = __mfcr(CPU_M3CNT);
    result.counter3.counter  = m3cnt.B.CountValue;
    result.counter3.overlfow = m3cnt.B.SOvf;

    return result;
}
#endif


IFX_INLINE void IfxCpu_triggerSwReset(void)
{
    MODULE_SCU.SWRSTCON.B.SWRSTREQ = 1;

    /* Wait till reset */
    while (1)
    {}
}


IFX_INLINE void IfxCpu_updateClockCounter(uint32 count)
{
    IfxCpu_updatePerformanceCounter(CPU_CCNT, count);
}


IFX_INLINE void IfxCpu_updateInstructionCounter(uint32 count)
{
    IfxCpu_updatePerformanceCounter(CPU_ICNT, count);
}


IFX_INLINE void IfxCpu_updatePerformanceCounter(uint32 address, uint32 count)
{
    IFX_UNUSED_PARAMETER(address);
    Ifx_CPU_CCTRL cctrl;
    boolean       enableBit;
    /*Disable the counters */
    cctrl.U    = __mfcr(CPU_CCTRL);
    enableBit  = cctrl.B.CE;
    cctrl.B.CE = 0;
    __mtcr(CPU_CCTRL, cctrl.U);

    /*Update the counter value */
    count &= ~(1U << 31);       /*clear sticky overflow bit if set */
    __mtcr(address, count);

    /*restore the enable bit */
    cctrl.B.CE = enableBit;
    __mtcr(CPU_CCTRL, cctrl.U);
}


#endif /* IFXCPU_H */
