/*
 * This file is part of the CmBacktrace Library.
 *
 * Copyright (c) 2016-2020, Armink, <armink.ztl@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * 'Software'), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * 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 AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Function: It is the macro definition head file for this library.
 * Created on: 2016-12-15
 */

#ifndef _CMB_DEF_H_
#define _CMB_DEF_H_

#include <cmb_cfg.h>
#include <stdint.h>
#include <stdlib.h>

/* library software version number */
#define CMB_SW_VERSION                "1.4.2"

#define CMB_CPU_ARM_CORTEX_M0             0
#define CMB_CPU_ARM_CORTEX_M3             1
#define CMB_CPU_ARM_CORTEX_M4             2
#define CMB_CPU_ARM_CORTEX_M7             3
#define CMB_CPU_ARM_CORTEX_M33            4

#define CMB_OS_PLATFORM_RTT               0
#define CMB_OS_PLATFORM_UCOSII            1
#define CMB_OS_PLATFORM_UCOSIII           2
#define CMB_OS_PLATFORM_FREERTOS          3
#define CMB_OS_PLATFORM_RTX5              4
#define CMB_OS_PLATFORM_THREADX           5

#define CMB_PRINT_LANGUAGE_ENGLISH        0
#define CMB_PRINT_LANGUAGE_CHINESE        1
#define CMB_PRINT_LANGUAGE_CHINESE_UTF8   2
#define CMB_PRINT_LANGUAGE_CUSTOM         0xFF

/* name max length, default size: 32 */
#ifndef CMB_NAME_MAX
#define CMB_NAME_MAX                      32
#endif

/* print information language, default is English */
#ifndef CMB_PRINT_LANGUAGE
#define CMB_PRINT_LANGUAGE                CMB_PRINT_LANGUAGE_ENGLISH
#endif


#if defined(__ARMCC_VERSION)
    /* C stack block name, default is STACK */
    #ifndef CMB_CSTACK_BLOCK_NAME
    #define CMB_CSTACK_BLOCK_NAME          STACK
    #endif
    /* code section name, default is ER_IROM1 */
    #ifndef CMB_CODE_SECTION_NAME
    #define CMB_CODE_SECTION_NAME          ER_IROM1
    #endif
#elif defined(__ICCARM__)
    /* C stack block name, default is 'CSTACK' */
    #ifndef CMB_CSTACK_BLOCK_NAME
    #define CMB_CSTACK_BLOCK_NAME          "CSTACK"
    #endif
    /* code section name, default is '.text' */
    #ifndef CMB_CODE_SECTION_NAME
    #define CMB_CODE_SECTION_NAME          ".text"
    #endif
#elif defined(__GNUC__)
    /* C stack block start address, defined on linker script file, default is _sstack */
    #ifndef CMB_CSTACK_BLOCK_START
    #define CMB_CSTACK_BLOCK_START         _sstack
    #endif
    /* C stack block end address, defined on linker script file, default is _estack */
    #ifndef CMB_CSTACK_BLOCK_END
    #define CMB_CSTACK_BLOCK_END           _estack
    #endif
    /* code section start address, defined on linker script file, default is _stext */
    #ifndef CMB_CODE_SECTION_START
    #define CMB_CODE_SECTION_START         _stext
    #endif
    /* code section end address, defined on linker script file, default is _etext */
    #ifndef CMB_CODE_SECTION_END
    #define CMB_CODE_SECTION_END           _etext
    #endif
#else
    #error "not supported compiler"
#endif

/* supported function call stack max depth, default is 32 */
#ifndef CMB_CALL_STACK_MAX_DEPTH
#define CMB_CALL_STACK_MAX_DEPTH       32
#endif

/* 
 * The maximum print depth in case of exception prevents
 * too much stack information from printing and insufficient log space
 */
#ifndef CMB_DUMP_STACK_DEPTH_SIZE
#define CMB_DUMP_STACK_DEPTH_SIZE     (16)
#endif

/* system handler control and state register */
#ifndef CMB_SYSHND_CTRL
#define CMB_SYSHND_CTRL                (*(volatile unsigned int*)  (0xE000ED24u))
#endif

/* memory management fault status register */
#ifndef CMB_NVIC_MFSR
#define CMB_NVIC_MFSR                  (*(volatile unsigned char*) (0xE000ED28u))
#endif

/* bus fault status register */
#ifndef CMB_NVIC_BFSR
#define CMB_NVIC_BFSR                  (*(volatile unsigned char*) (0xE000ED29u))
#endif

/* usage fault status register */
#ifndef CMB_NVIC_UFSR
#define CMB_NVIC_UFSR                  (*(volatile unsigned short*)(0xE000ED2Au))
#endif

/* hard fault status register */
#ifndef CMB_NVIC_HFSR
#define CMB_NVIC_HFSR                  (*(volatile unsigned int*)  (0xE000ED2Cu))
#endif

/* debug fault status register */
#ifndef CMB_NVIC_DFSR
#define CMB_NVIC_DFSR                  (*(volatile unsigned short*)(0xE000ED30u))
#endif

/* memory management fault address register */
#ifndef CMB_NVIC_MMAR
#define CMB_NVIC_MMAR                  (*(volatile unsigned int*)  (0xE000ED34u))
#endif

/* bus fault manage address register */
#ifndef CMB_NVIC_BFAR
#define CMB_NVIC_BFAR                  (*(volatile unsigned int*)  (0xE000ED38u))
#endif

/* auxiliary fault status register */
#ifndef CMB_NVIC_AFSR
#define CMB_NVIC_AFSR                  (*(volatile unsigned short*)(0xE000ED3Cu))
#endif

/**
 * Cortex-M fault registers
 */
struct cmb_hard_fault_regs{
  struct {
    unsigned int r0;                     // Register R0
    unsigned int r1;                     // Register R1
    unsigned int r2;                     // Register R2
    unsigned int r3;                     // Register R3
    unsigned int r12;                    // Register R12
    unsigned int lr;                     // Link register
    unsigned int pc;                     // Program counter
    union {
      unsigned int value;
      struct {
#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33)
        unsigned int IPSR : 9;           // Interrupt Program Status register (IPSR)
        unsigned int EPSR : 18;          // Execution Program Status register (EPSR)
        unsigned int APSR : 5;           // Application Program Status register (APSR)
#else
        unsigned int IPSR : 8;           // Interrupt Program Status register (IPSR)
        unsigned int EPSR : 19;          // Execution Program Status register (EPSR)
        unsigned int APSR : 5;           // Application Program Status register (APSR)
#endif
      } bits;
    } psr;                               // Program status register.
  } saved;

  union {
    unsigned int value;
    struct {
      unsigned int MEMFAULTACT    : 1;   // Read as 1 if memory management fault is active
      unsigned int BUSFAULTACT    : 1;   // Read as 1 if bus fault exception is active
#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33)
      unsigned int HARDFAULTACT   : 1;   // Read as 1 if hardfault is active
#else
      unsigned int UnusedBits1    : 1;
#endif
      unsigned int USGFAULTACT    : 1;   // Read as 1 if usage fault exception is active
#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33)
      unsigned int SECUREFAULTACT : 1;   // Read as 1 if secure fault exception is active
      unsigned int NMIACT         : 1;   // Read as 1 if NMI exception is active
      unsigned int UnusedBits2    : 1;
#else
      unsigned int UnusedBits2    : 3;
#endif
      unsigned int SVCALLACT      : 1;   // Read as 1 if SVC exception is active
      unsigned int MONITORACT     : 1;   // Read as 1 if debug monitor exception is active
      unsigned int UnusedBits3    : 1;
      unsigned int PENDSVACT      : 1;   // Read as 1 if PendSV exception is active
      unsigned int SYSTICKACT     : 1;   // Read as 1 if SYSTICK exception is active
      unsigned int USGFAULTPENDED : 1;   // Usage fault pended; usage fault started but was replaced by a higher-priority exception
      unsigned int MEMFAULTPENDED : 1;   // Memory management fault pended; memory management fault started but was replaced by a higher-priority exception
      unsigned int BUSFAULTPENDED : 1;   // Bus fault pended; bus fault handler was started but was replaced by a higher-priority exception
      unsigned int SVCALLPENDED   : 1;   // SVC pended; SVC was started but was replaced by a higher-priority exception
      unsigned int MEMFAULTENA    : 1;   // Memory management fault handler enable
      unsigned int BUSFAULTENA    : 1;   // Bus fault handler enable
      unsigned int USGFAULTENA    : 1;   // Usage fault handler enable
#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33)
      unsigned int SECUREFAULTENA : 1;   // Secure fault handler enable
      unsigned int SECUREFAULTPENDED : 1;   // Secure fault pended; Secure fault handler was started but was replaced by a higher-priority exception
      unsigned int HARDFAULTPENDED   : 1;   // Hard fault pended; Hard fault handler was started but was replaced by a higher-priority exception
#else
      // None
#endif
    } bits;
  } syshndctrl;                          // System Handler Control and State Register (0xE000ED24)

  union {
    unsigned char value;
    struct {
      unsigned char IACCVIOL    : 1;     // Instruction access violation
      unsigned char DACCVIOL    : 1;     // Data access violation
      unsigned char UnusedBits  : 1;
      unsigned char MUNSTKERR   : 1;     // Unstacking error
      unsigned char MSTKERR     : 1;     // Stacking error
      unsigned char MLSPERR     : 1;     // Floating-point lazy state preservation (M4/M7)
      unsigned char UnusedBits2 : 1;
      unsigned char MMARVALID   : 1;     // Indicates the MMAR is valid
    } bits;
  } mfsr;                                // Memory Management Fault Status Register (0xE000ED28)
  unsigned int mmar;                     // Memory Management Fault Address Register (0xE000ED34)

  union {
    unsigned char value;
    struct {
      unsigned char IBUSERR    : 1;      // Instruction access violation
      unsigned char PRECISERR  : 1;      // Precise data access violation
      unsigned char IMPREISERR : 1;      // Imprecise data access violation
      unsigned char UNSTKERR   : 1;      // Unstacking error
      unsigned char STKERR     : 1;      // Stacking error
      unsigned char LSPERR     : 1;      // Floating-point lazy state preservation (M4/M7)
      unsigned char UnusedBits : 1;
      unsigned char BFARVALID  : 1;      // Indicates BFAR is valid
    } bits;
  } bfsr;                                // Bus Fault Status Register (0xE000ED29)
  unsigned int bfar;                     // Bus Fault Manage Address Register (0xE000ED38)

  union {
    unsigned short value;
    struct {
      unsigned short UNDEFINSTR : 1;     // Attempts to execute an undefined instruction
      unsigned short INVSTATE   : 1;     // Attempts to switch to an invalid state (e.g., ARM)
      unsigned short INVPC      : 1;     // Attempts to do an exception with a bad value in the EXC_RETURN number
      unsigned short NOCP       : 1;     // Attempts to execute a coprocessor instruction
#if (CMB_CPU_PLATFORM_TYPE == CMB_CPU_ARM_CORTEX_M33)
      unsigned short STKOF      : 1;     // Indicates a stack overflow error has occured
      unsigned short UnusedBits : 3;
#else
      unsigned short UnusedBits : 4;
#endif
      unsigned short UNALIGNED  : 1;     // Indicates that an unaligned access fault has taken place
      unsigned short DIVBYZERO0 : 1;     // Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set)
    } bits;
  } ufsr;                                // Usage Fault Status Register (0xE000ED2A)

  union {
    unsigned int value;
    struct {
      unsigned int UnusedBits  : 1;
      unsigned int VECTBL      : 1;      // Indicates hard fault is caused by failed vector fetch
      unsigned int UnusedBits2 : 28;
      unsigned int FORCED      : 1;      // Indicates hard fault is taken because of bus fault/memory management fault/usage fault
      unsigned int DEBUGEVT    : 1;      // Indicates hard fault is triggered by debug event
    } bits;
  } hfsr;                                // Hard Fault Status Register (0xE000ED2C)

  union {
    unsigned int value;
    struct {
      unsigned int HALTED   : 1;         // Halt requested in NVIC
      unsigned int BKPT     : 1;         // BKPT instruction executed
      unsigned int DWTTRAP  : 1;         // DWT match occurred
      unsigned int VCATCH   : 1;         // Vector fetch occurred
      unsigned int EXTERNAL : 1;         // EDBGRQ signal asserted
    } bits;
  } dfsr;                                // Debug Fault Status Register (0xE000ED30)

  unsigned int afsr;                     // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional)
};

/* assert for developer. */
#define CMB_ASSERT(EXPR)                                                       \
if (!(EXPR))                                                                   \
{                                                                              \
    cmb_println("(%s) has assert failed at %s.", #EXPR, __FUNCTION__);         \
    while (1);                                                                 \
}

/* ELF(Executable and Linking Format) file extension name for each compiler */
#if defined(__CC_ARM) || defined(__CLANG_ARM) || defined(__ARMCC_VERSION)
    #define CMB_ELF_FILE_EXTENSION_NAME          ".axf"
#elif defined(__ICCARM__)
    #define CMB_ELF_FILE_EXTENSION_NAME          ".out"
#elif defined(__GNUC__)
    #define CMB_ELF_FILE_EXTENSION_NAME          ".elf"
#else
    #error "not supported compiler"
#endif

#ifndef cmb_println
    #error "cmb_println isn't defined in 'cmb_cfg.h'"
#endif

#ifndef CMB_CPU_PLATFORM_TYPE
    #error "CMB_CPU_PLATFORM_TYPE isn't defined in 'cmb_cfg.h'"
#endif

#if (defined(CMB_USING_BARE_METAL_PLATFORM) && defined(CMB_USING_OS_PLATFORM))
    #error "CMB_USING_BARE_METAL_PLATFORM and CMB_USING_OS_PLATFORM only one of them can be used"
#elif defined(CMB_USING_OS_PLATFORM)
    #if !defined(CMB_OS_PLATFORM_TYPE)
        #error "CMB_OS_PLATFORM_TYPE isn't defined in 'cmb_cfg.h'"
    #endif /* !defined(CMB_OS_PLATFORM_TYPE) */
    #if (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT)
        #include <rtthread.h>
    #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSII)
        #include <ucos_ii.h>
    #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_UCOSIII)
        #include <os.h>
    #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_FREERTOS)
        #include <FreeRTOS.h>  
        extern uint32_t *vTaskStackAddr(void);/* need to modify the FreeRTOS/tasks source code */
        extern uint32_t vTaskStackSize(void);
        extern char * vTaskName(void);
    #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTX5)
        #include "rtx_os.h"
    #elif (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_THREADX)
        #include "tx_api.h"
        #include "tx_thread.h"
    #else
        #error "not supported OS type"
    #endif /* (CMB_OS_PLATFORM_TYPE == CMB_OS_PLATFORM_RTT) */
#endif /* (defined(CMB_USING_BARE_METAL_PLATFORM) && defined(CMB_USING_OS_PLATFORM)) */

/* include or export for supported cmb_get_msp, cmb_get_psp and cmb_get_sp function */
#if defined(__CC_ARM)
    static __inline __asm uint32_t cmb_get_msp(void) {
        mrs r0, msp
        bx lr
    }
    static __inline __asm uint32_t cmb_get_psp(void) {
        mrs r0, psp
        bx lr
    }
    static __inline __asm uint32_t cmb_get_sp(void) {
        mov r0, sp
        bx lr
    }
#elif defined(__CLANG_ARM)
    __attribute__( (always_inline) ) static __inline uint32_t cmb_get_msp(void) {
        uint32_t result;
        __asm volatile ("mrs %0, msp" : "=r" (result) );
        return (result);
    }
    __attribute__( (always_inline) ) static __inline uint32_t cmb_get_psp(void) {
        uint32_t result;
        __asm volatile ("mrs %0, psp" : "=r" (result) );
        return (result);
    }
    __attribute__( (always_inline) ) static __inline uint32_t cmb_get_sp(void) {
        uint32_t result;
        __asm volatile ("mov %0, sp" : "=r" (result) );
        return (result);
    }
#elif defined(__ICCARM__)
/* IAR iccarm specific functions */
/* Close Raw Asm Code Warning */  
#pragma diag_suppress=Pe940    
    static uint32_t cmb_get_msp(void)
    {
      __asm("mrs r0, msp");
      __asm("bx lr");        
    }
    static uint32_t cmb_get_psp(void)
    {
      __asm("mrs r0, psp");
      __asm("bx lr");        
    }
    static uint32_t cmb_get_sp(void)
    {
      __asm("mov r0, sp");
      __asm("bx lr");       
    }
#pragma diag_default=Pe940  
#elif defined(__GNUC__)
    __attribute__( ( always_inline ) ) static inline uint32_t cmb_get_msp(void) {
        register uint32_t result;
        __asm volatile ("MRS %0, msp\n" : "=r" (result) );
        return(result);
    }
    __attribute__( ( always_inline ) ) static inline uint32_t cmb_get_psp(void) {
        register uint32_t result;
        __asm volatile ("MRS %0, psp\n" : "=r" (result) );
        return(result);
    }
    __attribute__( ( always_inline ) ) static inline uint32_t cmb_get_sp(void) {
        register uint32_t result;
        __asm volatile ("MOV %0, sp\n" : "=r" (result) );
        return(result);
    }
#else
    #error "not supported compiler"
#endif

#endif /* _CMB_DEF_H_ */