//
// Created by lydxh on 24-11-27.
//

#ifndef HW_LIB_TICKS_H
#define HW_LIB_TICKS_H
/*============================ INCLUDES ======================================*/
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif
/*============================ MACROS ========================================*/


/*!
 * \addtogroup gHelper 4 Helper
 * @{
 */

// for IAR
#undef __IS_COMPILER_IAR__
#if defined(__IAR_SYSTEMS_ICC__)
#   define __IS_COMPILER_IAR__                  1
#endif

// for arm compiler 5
#undef __IS_COMPILER_ARM_COMPILER_5__
#if ((__ARMCC_VERSION >= 5000000) && (__ARMCC_VERSION < 6000000))
#   define __IS_COMPILER_ARM_COMPILER_5__       1
#endif


//for arm compiler 6

#undef __IS_COMPILER_ARM_COMPILER_6__
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
#   define __IS_COMPILER_ARM_COMPILER_6__       1
#endif
#undef __IS_COMPILER_ARM_COMPILER__
#if defined(__IS_COMPILER_ARM_COMPILER_5__) && __IS_COMPILER_ARM_COMPILER_5__   \
 || defined(__IS_COMPILER_ARM_COMPILER_6__) && __IS_COMPILER_ARM_COMPILER_6__
#   define __IS_COMPILER_ARM_COMPILER__         1
#endif

// for clang
#undef  __IS_COMPILER_LLVM__
#if defined(__clang__) && !__IS_COMPILER_ARM_COMPILER_6__
#   define __IS_COMPILER_LLVM__                 1
#else

// for gcc
#   undef __IS_COMPILER_GCC__
#   if defined(__GNUC__) && !(defined(__IS_COMPILER_ARM_COMPILER__)           \
 || defined(__IS_COMPILER_LLVM__)                   \
 || defined(__IS_COMPILER_IAR__))
#       define __IS_COMPILER_GCC__              1
#   endif

#endif


#ifdef __PERF_COUNT_PLATFORM_SPECIFIC_HEADER__
#   include __PERF_COUNT_PLATFORM_SPECIFIC_HEADER__
#endif

#if defined(__clang__)
#   pragma clang diagnostic push
#   pragma clang diagnostic ignored "-Wunknown-warning-option"
#   pragma clang diagnostic ignored "-Wreserved-identifier"
#   pragma clang diagnostic ignored "-Wdeclaration-after-statement"
#   pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#   pragma clang diagnostic ignored "-Wgnu-statement-expression"
#   pragma clang diagnostic ignored "-Wunused-but-set-variable"
#   pragma clang diagnostic ignored "-Wshadow"
#   pragma clang diagnostic ignored "-Wshorten-64-to-32"
#   pragma clang diagnostic ignored "-Wcompound-token-split-by-macro"
#   pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
#elif defined(__IS_COMPILER_ARM_COMPILER_5__)
#   pragma diag_suppress 550
#elif defined(__IS_COMPILER_GCC__)
#   pragma GCC diagnostic push
#   pragma GCC diagnostic ignored "-Wpedantic"
#   pragma GCC diagnostic ignored "-Wunused-variable"
#   pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#   pragma GCC diagnostic ignored "-Wformat="
#endif

#ifndef __PLOOC_VA_NUM_ARGS_IMPL
#   define __PLOOC_VA_NUM_ARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, \
                                    _12, _13, _14, _15, _16, __N, ...)      __N
#endif

#ifndef __PLOOC_VA_NUM_ARGS
#define __PLOOC_VA_NUM_ARGS(...)                                                \
            __PLOOC_VA_NUM_ARGS_IMPL( 0,##__VA_ARGS__,16,15,14,13,12,11,10,9,   \
                                      8,7,6,5,4,3,2,1,0)
#endif

#ifndef UNUSED_PARAM
#   define UNUSED_PARAM(__VAR)     (void)(__VAR)
#endif

#ifndef MIN
#   define MIN(__a, __b)  ((__a) <= (__b) ? (__a) : (__b))
#endif

#ifndef MAX
#   define MAX(__a, __b)  ((__a) >= (__b) ? (__a) : (__b))
#endif

/*!
 * \brief an attribute for static variables that no initialisation is required
 *        in the C startup process.
 */
#ifndef PERF_NOINIT
#   if     defined(__IS_COMPILER_ARM_COMPILER_5__)
#       define PERF_NOINIT   __attribute__(( section( ".bss.noinit"),zero_init))
#   elif   defined(__IS_COMPILER_ARM_COMPILER_6__)
#       define PERF_NOINIT   __attribute__(( section( ".bss.noinit")))
#   elif   defined(__IS_COMPILER_IAR__)
#       define PERF_NOINIT   __no_init
#   elif   (defined(__IS_COMPILER_GCC__) || defined(__IS_COMPILER_LLVM__)) && !defined(__APPLE__)
#       define PERF_NOINIT   __attribute__(( section( ".bss.noinit")))
#   else
#       define PERF_NOINIT
#   endif
#endif


#undef __CONNECT2
#undef __CONNECT3
#undef __CONNECT4
#undef __CONNECT5
#undef __CONNECT6
#undef __CONNECT7
#undef __CONNECT8
#undef __CONNECT9

#undef CONNECT2
#undef CONNECT3
#undef CONNECT4
#undef CONNECT5
#undef CONNECT6
#undef CONNECT7
#undef CONNECT8
#undef CONNECT9

#undef CONNECT

#undef __MACRO_EXPANDING
#define __MACRO_EXPANDING(...)                      __VA_ARGS__

#define __CONNECT2(__A, __B)                        __A##__B
#define __CONNECT3(__A, __B, __C)                   __A##__B##__C
#define __CONNECT4(__A, __B, __C, __D)              __A##__B##__C##__D
#define __CONNECT5(__A, __B, __C, __D, __E)         __A##__B##__C##__D##__E
#define __CONNECT6(__A, __B, __C, __D, __E, __F)    __A##__B##__C##__D##__E##__F
#define __CONNECT7(__A, __B, __C, __D, __E, __F, __G)                           \
                                                    __A##__B##__C##__D##__E##__F##__G
#define __CONNECT8(__A, __B, __C, __D, __E, __F, __G, __H)                      \
                                                    __A##__B##__C##__D##__E##__F##__G##__H
#define __CONNECT9(__A, __B, __C, __D, __E, __F, __G, __H, __I)                 \
                                                    __A##__B##__C##__D##__E##__F##__G##__H##__I

#define ALT_CONNECT2(__A, __B)              __CONNECT2(__A, __B)
#define CONNECT2(__A, __B)                  __CONNECT2(__A, __B)
#define CONNECT3(__A, __B, __C)             __CONNECT3(__A, __B, __C)
#define CONNECT4(__A, __B, __C, __D)        __CONNECT4(__A, __B, __C, __D)
#define CONNECT5(__A, __B, __C, __D, __E)   __CONNECT5(__A, __B, __C, __D, __E)
#define CONNECT6(__A, __B, __C, __D, __E, __F)                                  \
                                            __CONNECT6(__A, __B, __C, __D, __E, __F)
#define CONNECT7(__A, __B, __C, __D, __E, __F, __G)                             \
                                            __CONNECT7(__A, __B, __C, __D, __E, __F, __G)
#define CONNECT8(__A, __B, __C, __D, __E, __F, __G, __H)                        \
                                            __CONNECT8(__A, __B, __C, __D, __E, __F, __G, __H)
#define CONNECT9(__A, __B, __C, __D, __E, __F, __G, __H, __I)                   \
                                            __CONNECT9(__A, __B, __C, __D, __E, __F, __G, __H, __I)

#define CONNECT(...)                                                            \
            ALT_CONNECT2(CONNECT, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)

#undef __using1
#undef __using2
#undef __using3
#undef __using4
#undef using

#define __using1(__declare)                                                     \
            for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL;          \
                 CONNECT3(__using_, __LINE__,_ptr)++ == NULL;                   \
                )

#define __using2(__declare, __on_leave_expr)                                    \
            for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL;          \
                 CONNECT3(__using_, __LINE__,_ptr)++ == NULL;                   \
                 (__on_leave_expr)                                              \
                )

#define __using3(__declare, __on_enter_expr, __on_leave_expr)                   \
            for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL;          \
                 CONNECT3(__using_, __LINE__,_ptr)++ == NULL ?                  \
                    ((__on_enter_expr),1) : 0;                                  \
                 (__on_leave_expr)                                              \
                )

#define __using4(__dcl1, __dcl2, __on_enter_expr, __on_leave_expr)              \
            for (__dcl1, __dcl2, *CONNECT3(__using_, __LINE__,_ptr) = NULL;     \
                 CONNECT3(__using_, __LINE__,_ptr)++ == NULL ?                  \
                    ((__on_enter_expr),1) : 0;                                  \
                 (__on_leave_expr)                                              \
                )

#define using(...)                                                              \
                CONNECT2(__using, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)


#undef __with2
#undef __with3
#undef with

#define __with1(__addr)                                                         \
            using(__typeof__(*__addr) *_=(__addr))

#define __with2(__type, __addr)                                                 \
            using(__type *_=(__addr))
#define __with3(__type, __addr, __item)                                         \
            using(__type *_=(__addr), *__item = _, _=_,_=_ )

#define with(...)                                                               \
            CONNECT2(__with, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)

#undef _

#ifndef dimof
#   define dimof(__array)          (sizeof(__array)/sizeof(__array[0]))
#endif


#define SAFE_NAME(__NAME)   CONNECT3(__,__NAME,__LINE__)

#undef foreach2
#undef foreach3
#undef foreach

#define foreach1(__array)                                                       \
            using(__typeof__(__array[0]) *_ = __array)                          \
            for (   uint_fast32_t SAFE_NAME(count) = dimof(__array);            \
                    SAFE_NAME(count) > 0;                                       \
                    _++, SAFE_NAME(count)--                                     \
                )

#define foreach2(__type, __array)                                               \
            using(__type *_ = __array)                                          \
            for (   uint_fast32_t SAFE_NAME(count) = dimof(__array);            \
                    SAFE_NAME(count) > 0;                                       \
                    _++, SAFE_NAME(count)--                                     \
                )

#define foreach3(__type, __array, __item)                                       \
            using(__type *_ = __array, *__item = _, _ = _, _ = _ )              \
            for (   uint_fast32_t SAFE_NAME(count) = dimof(__array);            \
                    SAFE_NAME(count) > 0;                                       \
                    _++, __item = _, SAFE_NAME(count)--                         \
                )

#define foreach(...)                                                            \
            CONNECT2(foreach, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)

#ifndef safe_atom_code
#   define safe_atom_code()                                                     \
            using(  ticks_global_interrupt_status_t SAFE_NAME(temp) =           \
                        ticks_port_disable_global_interrupt(),                  \
                    ticks_port_resume_global_interrupt(SAFE_NAME(temp)))
#endif


#ifndef __WEAK
#   define __WEAK __attribute__((weak))
#endif

#ifndef __STATIC_INLINE
#   define __STATIC_INLINE static inline
#endif

#ifndef __IRQ_SAFE
#   define __IRQ_SAFE                                                           \
            using(  ticks_global_interrupt_status_t SAFE_NAME(temp) =           \
                        ticks_port_disable_global_interrupt(),                  \
                    ticks_port_resume_global_interrupt(SAFE_NAME(temp)))
#endif

#ifndef __perf_counter_printf__
#   define __perf_counter_printf__      printf
#endif

/* deprecated macro for backward compatibility */
#define user_code_insert_to_systick_handler                                     \
            ticks_port_insert_to_system_timer_insert_ovf_handler

#if __PLOOC_VA_NUM_ARGS() != 0
#warning Please enable GNU extensions, it is required by __cycleof__() and \
__super_loop_monitor__()
#endif

#if defined(__PERF_COUNTER_CFG_USE_SYSTICK_WRAPPER__)                           \
 && (!defined(__TICKS_USE_PORTING__)                                         \
 || (defined(__TICKS_USE_PORTING__) && (0 == __TICKS_USE_PORTING__)))

#   if defined(__IS_COMPILER_ARM_COMPILER_5__) && __IS_COMPILER_ARM_COMPILER_5__
#       pragma import(__ensure_systick_wrapper)
#   elif    (defined(__GNUC__) || defined(__clang__))                           \
        &&  (!defined(__IS_COMPILER_IAR__) || !__IS_COMPILER_IAR__)
__asm(".global __ensure_systick_wrapper\n\t");
#   endif

#endif

#ifndef __ticks_sync_barrier__

/* default implementation */
#if defined(__clang__) || __IS_COMPILER_GCC__
#   define __ticks_sync_barrier__(...)      __sync_synchronize()
#else
#   define __ticks_sync_barrier__(...)
#endif

#endif
/*! @} */

/*============================ MACROFIED FUNCTIONS ===========================*/

/*!
 * \addtogroup gBasic 1 Basic
 * @{
 */

/*!
 * \brief measure the cycle count of a given code segment
 * \param[in] __STR a description string for the measurement
 * \param[in] ... an optional code segement, in which we can read the measured
 *                result from __cycle_count__.
 * \details Here is an example:
    E.g.
    \code
        __cycleof__("printf") {
            printf("hello world\r\n");
        }
    \endcode
 */
#define __cycleof__(__STR, ...)                                                 \
            using(int64_t _ = get_system_ticks(), __cycle_count__ = _,          \
                {__ticks_sync_barrier__();},                                    \
                {                                                               \
                __ticks_sync_barrier__();                                       \
                _ = get_system_ticks() - _ - g_nOffset;                         \
                __cycle_count__ = _;                                            \
                if (__PLOOC_VA_NUM_ARGS(__VA_ARGS__) == 0) {                    \
                    __perf_counter_printf__("\r\n");                            \
                    __perf_counter_printf__("-[Cycle Report]");                 \
                    __perf_counter_printf__(                                    \
                        "------------------------------------\r\n");            \
                    __perf_counter_printf__(                                    \
                        __STR " total cycle count: %ld [%08lx]\r\n",            \
                            (long)_, (long)_);                                  \
                } else {                                                        \
                    __VA_ARGS__                                                 \
                };                                                              \
            })

/*!
 * \brief measure the cpu usage for a given code segment and print out the
 *        result in percentage.
 * \param[in] __CNT generate result on every given iterations
 * \param[in] ... an optional code segement, in which we can read the measured
 *                result from __usage__ which is a float value.
 * \details Here is an example, 50% cpu time:
    E.g.
    \code
        while (1) {
            __cpu_usage__(100) {
                delay_us(5000);
            }
            delay_us(5000);
        }
    \endcode
 */
#define __cpu_usage__(__CNT, ...)                                               \
    static int64_t SAFE_NAME(s_lTimestamp) = 0, SAFE_NAME(s_lTotal) = 0;        \
    static uint32_t SAFE_NAME(s_wLoopCounter) = (__CNT);                        \
    using(float __usage__ = 0, ({                                               \
    if (0 == SAFE_NAME(s_wLoopCounter)) {                                       \
        __usage__ = (float)((double)SAFE_NAME(s_lTotal)                         \
                        / (double)(     get_system_ticks()                      \
                                  -     SAFE_NAME(s_lTimestamp)));              \
        __usage__ *= 100.0f;                                                    \
        SAFE_NAME(s_lTimestamp) = 0;                                            \
        SAFE_NAME(s_lTotal) = 0;                                                \
        if (__PLOOC_VA_NUM_ARGS(__VA_ARGS__) == 0) {                            \
            __perf_counter_printf__("CPU Usage %3.2f%%\r\n", (double)__usage__);\
        } else {                                                                \
            __VA_ARGS__                                                         \
        }                                                                       \
    }                                                                           \
    if (0 == SAFE_NAME(s_lTimestamp)) {                                         \
        SAFE_NAME(s_lTimestamp) = get_system_ticks();                           \
        SAFE_NAME(s_wLoopCounter) = (__CNT);                                    \
    }                                                                           \
    start_task_cycle_counter();}),                                              \
    ({SAFE_NAME(s_lTotal) += stop_task_cycle_counter();                         \
    SAFE_NAME(s_wLoopCounter)--;}))

#define __cpu_time__    __cpu_usage__

/*!
 * \addtogroup gBasicTimerService 1.2 Timer Service
 * \ingroup gBasic
 * @{
 */

/*!
 * \brief should not use
 */
#define ticks_is_time_out_ms0()         true

/*!
 * \brief set an alarm with given period in ms and check the status
 *
 * \param[in] __ms a time period in millisecond
 * \param[in] __timestamp_ptr an optional timestamp holder
 * \param[in] __auto_reload whether starting next period after a timeout event
 *
 * \return bool whether it is timeout
 */
#define ticks_is_time_out_ms3(__ms, __timestamp_ptr, __auto_reload)             \
    ({  static int64_t SAFE_NAME(s_lTimestamp);  (void)SAFE_NAME(s_lTimestamp); \
        __ticks_is_time_out(ticks_convert_ms_to_ticks(__ms),                    \
        (__timestamp_ptr), (__auto_reload));})

/*!
 * \brief set an alarm with given period in ms and check the status
 *
 * \param[in] __ms a time period in millisecond
 * \param[in] __timestamp_ptr an optional timestamp holder
 *
 * \return bool whether it is timeout
 */
#define ticks_is_time_out_ms2(__ms, __timestamp_ptr)                            \
            ticks_is_time_out_ms3((__ms), (__timestamp_ptr), true)


/*!
 * \brief set an alarm with given period in ms and check the status
 *
 * \param[in] __ms a time period in millisecond
 * \param[in] __timestamp_ptr an optional timestamp holder
 *
 * \return bool whether it is timeout
 */
#define ticks_is_time_out_ms1(__ms)                                             \
            ticks_is_time_out_ms3((__ms), &SAFE_NAME(s_lTimestamp), true)

/*!
 * \brief set an alarm with given period in ms and check the status
 *
 * \param[in] __ms a time period in millisecond
 * \param[in] ... an optional timestamp holder
 * \param[in] ... an optional indicator for whether starting next period after a timeout event
 *
 * \return bool whether it is timeout
 */
#define ticks_is_time_out_ms(...)                                               \
            CONNECT2(ticks_is_time_out_ms, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))    \
                (__VA_ARGS__)

/*!
 * \brief set an alarm with given period in us and check the status
 *
 * \param[in] __us a time period in microsecond
 * \param[in] __timestamp_ptr an optional timestamp holder
 * \param[in] __auto_reload whether starting next period after a timeout event
 *
 * \return bool whether it is timeout
 */
#define ticks_is_time_out_us3(__us, __timestamp_ptr, __auto_reload)             \
    ({  static int64_t SAFE_NAME(s_lTimestamp); (void)SAFE_NAME(s_lTimestamp);  \
        __ticks_is_time_out(ticks_convert_us_to_ticks(__us),                    \
        (__timestamp_ptr), (__auto_reload));})

/*!
 * \brief set an alarm with given period in us and check the status
 *
 * \param[in] __us a time period in microsecond
 * \param[in] __timestamp_ptr an optional timestamp holder
 *
 * \return bool whether it is timeout
 */
#define ticks_is_time_out_us2(__us, __timestamp_ptr)                            \
            ticks_is_time_out_us3((__us), (__timestamp_ptr), true)


/*!
 * \brief set an alarm with given period in us and check the status
 *
 * \param[in] __us a time period in microsecond
 * \param[in] __timestamp_ptr an optional timestamp holder
 *
 * \return bool whether it is timeout
 */
#define ticks_is_time_out_us1(__us)                                             \
            ticks_is_time_out_us3((__us), &SAFE_NAME(s_lTimestamp), true)

/*!
 * \brief set an alarm with given period in us and check the status
 *
 * \param[in] __us a time period in microsecond
 * \param[in] ... an optional timestamp holder
 * \param[in] ... an optional indicator for whether starting next period after a timeout event
 *
 * \return bool whether it is timeout
 */
#define ticks_is_time_out_us(...)                                               \
            CONNECT2(ticks_is_time_out_us, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))    \
                (__VA_ARGS__)

/*============================ TYPES =========================================*/
typedef struct {
    int64_t lStart;
    int64_t lUsedTotal;
    int32_t nUsedRecent;
    uint16_t hwActiveCount;
    uint16_t                        : 15;
    uint16_t bEnabled: 1;
} task_cycle_info_t;

typedef struct task_cycle_info_agent_t task_cycle_info_agent_t;

struct task_cycle_info_agent_t {
    task_cycle_info_t *ptInfo;
    task_cycle_info_agent_t *ptNext;
    task_cycle_info_agent_t *ptPrev;
};

extern volatile int64_t g_lLastTimeStamp;
extern volatile int32_t g_nOffset;


__attribute__((noinline))
extern int64_t get_system_ticks(void);


#ifdef __TICKS_USE_LONG_CLOCK__
/*! \note the prototype of this clock() is different from the one defined in
 *!           time.h. As clock_t is usually defined as unsigned int, it is
 *!           not big enough in Cortex-M system to hold a time-stamp. clock()
 *!           defined here returns the timestamp since the begining of main()
 *!           and its unit is clock cycle (rather than 1ms). Hence, for a system
 *!           running under several hundreds MHz or even 1GHz, e.g. RT10xx from
 *!           NXP, it is very easy to see a counter overflow as clock_t is
 *!           defined as uint32_t in timer.h.
 *!           Since we are not allowed to change the defintion of clock_t in
 *!           official header file, i.e. time.h, I use a compatible prototype
 *!           after I checked the AAPCS spec. So, the return of the clock() is
 *!           int64_t, which will use the R0 to store the lower 32bits and R1
 *!           to store the higher 32bits. When you are using the prototype from
 *!           timer.h, caller will only take the lower 32bits stored in R0 and
 *!           the higher 32bits stored in R1 will be ignored.
 *!
 *!           If you want to use the non-overflow version of this clock(), please
 *!           1) define the MACRO: __PERF_CNT_USE_LONG_CLOCK__ in your project
 *!           and 2) do not include system header file <time.h>
 *!
 */
#if !defined(__IS_COMPILER_IAR__)
__attribute__((nothrow))
#endif
__attribute__((noinline))
extern int64_t clock(void);
#endif

/*!
 * \brief try to set a start pointer for the performance counter
 */
static inline
void start_cycle_counter(void) {
    g_lLastTimeStamp = get_system_ticks();
}

/*!
 * \brief calculate the elapsed cycle count since the last start point
 * \note  you can have multiple stop_cycle_counter following one start point
 * \return int32_t the elapsed cycle count
 */
static inline
int64_t stop_cycle_counter(void) {
    int64_t lTemp = (get_system_ticks() - g_lLastTimeStamp);

    return lTemp - g_nOffset;
}




/*! @} */


/*!
 * \addtogroup gBasicTimerService 1.2 Timer Service
 * \ingroup gBasic
 * @{
 */

/*!
 * \brief get the system timer frequency
 * \return uint32_t the system timer frequency in Hz
 */
extern uint32_t ticks_get_systimer_frequency(void);

/*!
 * \brief get the elapsed milliseconds since perf_counter is initialised
 * \return int64_t the elapsed milliseconds
 */
extern int64_t get_system_ms(void);

/*!
 * \brief get the elapsed microsecond since perf_counter is initialised
 * \return int64_t the elapsed microsecond
 */
extern int64_t get_system_us(void);

/*!
 * \brief delay specified time in microsecond
 * \param[in] wUs time in microsecond
 */
extern void delay_us(uint32_t wUs);

/*!
 * \brief delay specified time in millisecond
 * \param[in] wMs time in millisecond
 */
extern void delay_ms(uint32_t nMs);

/*!
 * \brief convert ticks of a reference timer to millisecond
 *
 * \param[in] lTick the tick count
 * \return int64_t the millisecond
 */
extern
int64_t ticks_convert_ticks_to_ms(int64_t lTick);

/*!
 * \brief convert millisecond into ticks of the reference timer
 *
 * \param[in] wMS the target time in millisecond
 * \return int64_t the ticks
 */
extern
int64_t ticks_convert_ms_to_ticks(uint32_t wMS);

/*!
 * \brief convert ticks of a reference timer to microsecond
 *
 * \param[in] lTick the tick count
 * \return int64_t the microsecond
 */
extern
int64_t ticks_convert_ticks_to_us(int64_t lTick);

/*!
 * \brief convert microsecond into ticks of the reference timer
 *
 * \param[in] wUS the target time in microsecond
 * \return int64_t the ticks
 */
extern
int64_t ticks_convert_us_to_ticks(uint32_t wUS);

/*!
 * \brief set an alarm with given period and check the status
 *
 * \param[in] lPeriod a time period in ticks
 * \param[in] plTimestamp a pointer points to an int64_t integer, if NULL is
 *            passed, an static local variable inside the function will be used
 * \param[in] bAutoReload whether starting next period after a timeout event.
 * \return bool whether it is timeout or not
 */
extern
bool __ticks_is_time_out(int64_t lPeriod, int64_t *plTimestamp, bool bAutoReload);

/*!
 * \addtogroup gBasic 1 Basic
 * @{
 */

/*----------------------------------------------------------------------------*
 * Please ignore the following APIs unless you have encountered some known    *
 * special conditions                                                         *
 *----------------------------------------------------------------------------*/

/*! \brief   initialise cycle counter service
 *  \note    - don't forget to tell the function whether the systick is already
 *           used by user applications.
 *           Don't worry, this cycle counter service won't affect your existing
 *           systick service.
 *
 *  \note    - Usually the perf_counter can initialise itself with the help of
 *           __attribute__((constructor(255))), this works fine in Arm Compiler
 *           5 (armcc), Arm Compiler 6 (armclang), arm gcc and llvm. It doesn't
 *           work for IAR. So, when you are using IAR, please call this function
 *           manually to initialise the perf_counter service.
 *
 *  \note    - Perf_counter library assumes that:
 *           1. Your project has already using SysTick
 *           2. It assumes that you have already implemented the SysTick_Handler
 *           3. It assumes that you have enabled the exception handling for
 *              SysTick.
 *           If these are not the case, please:
 *               1. Add an empty SysTick_Handler to your project if you don't have
 *              one
 *               2. Make sure you have the SysTick Exception handling enabled
 *               3. And call function init_cycle_counter(false) if you doesn't
 *              use SysTick in your project at all.
 *
 *  \param[in] bIsSysTickOccupied  A boolean value which indicates whether SysTick
 *           is already used by user application.
 *
 *  \return false Failed to initialize the timer counter, as the timer is not
 *                available or IO error.
 *  \return true initialization is successful.
 */
extern bool init_cycle_counter(bool bIsSysTickOccupied);


/*!
 * \brief a system timer overflow handler
 *
 * \note  - if you are using a compiler other than armcc or armclang, e.g. iar,
 *        arm gcc etc, the systick_wrapper_ual.o doesn't work with the linker
 *        of your target toolchain as it use the $Super$$ which is only supported
 *        by armlink. For this condition, you have to manually put this function
 *        into your existing SysTick_Handler to make the perf_counter library
 *        work.
 *
 * \note  - if you are using Arm Compiler 5 (armcc) or Arm Compiler 6 (armclang)
 *        you do NOT have to insert this function into your SysTick_Handler,
 *        the systick_wrapper_ual.s will do the work for you.
 */
extern void ticks_port_insert_to_system_timer_insert_ovf_handler(void);

/*!
 * \brief update perf_counter as SystemCoreClock has been updated.
 */
extern void update_perf_counter(void);

/*!
 * \brief prepare for reconfiguration of SysTick timer.
 *
 * \note some systems (e.g. FreeRTOS) might reconfigure the systick timer to
 *       fulfil the requirement of their feature. To support this, just
 *       before the reconfiguration, please call this function in order
 *       to make the perf_counter works correctly later.
 *
 * \note after the reconfiguration, please call update_perf_counter() to apply
 *       the changes to perf_counter.
 *
 * \note this function will stop the SysTick, clear the pending bit and set
 *       the Load register and Current Value register to zero.
 */
extern void before_cycle_counter_reconfiguration(void);


#ifndef __ticks_sync_barrier__
#   define __ticks_sync_barrier__(...)         do {__DSB();__ISB();} while(0)
#endif

typedef uint32_t ticks_global_interrupt_status_t;

__STATIC_INLINE
ticks_global_interrupt_status_t ticks_port_disable_global_interrupt(void);
//{
//    ticks_global_interrupt_status_t tStatus = __get_PRIMASK();
//    __disable_irq();
//
//    return tStatus;
//}

__STATIC_INLINE
void ticks_port_resume_global_interrupt(ticks_global_interrupt_status_t tStatus);
//{
//    __set_PRIMASK(tStatus);
//}


//#if defined(__clang__)
//#   pragma clang diagnostic pop
//#elif defined(__IS_COMPILER_GCC__)
//#   pragma GCC diagnostic pop
//#endif

#ifdef __cplusplus
}
#endif
#endif //HW_LIB_TICKS_H
