perf_counter.h 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  1. /****************************************************************************
  2. * Copyright 2022 Gorgon Meducer (Email:embedded_zhuoran@hotmail.com) *
  3. * *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); *
  5. * you may not use this file except in compliance with the License. *
  6. * You may obtain a copy of the License at *
  7. * *
  8. * http://www.apache.org/licenses/LICENSE-2.0 *
  9. * *
  10. * Unless required by applicable law or agreed to in writing, software *
  11. * distributed under the License is distributed on an "AS IS" BASIS, *
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
  13. * See the License for the specific language governing permissions and *
  14. * limitations under the License. *
  15. * *
  16. ****************************************************************************/
  17. #ifndef __PERFORMANCE_COUNTER_H__
  18. #define __PERFORMANCE_COUNTER_H__
  19. /*============================ INCLUDES ======================================*/
  20. #include <stdbool.h>
  21. #include <stdint.h>
  22. #include <stddef.h>
  23. #include "cmsis_compiler.h"
  24. #ifdef __cplusplus
  25. extern "C" {
  26. #endif
  27. /*============================ MACROS ========================================*/
  28. /*!
  29. * \addtogroup gBasic 1 Basic
  30. * @{
  31. */
  32. #define __PERF_COUNTER_VER_MAJOR__ 2
  33. #define __PERF_COUNTER_VER_MINOR__ 2
  34. #define __PERF_COUNTER_VER_REVISE__ 4
  35. #define __PERF_COUNTER_VER_STR__ ""
  36. #define __PER_COUNTER_VER__ (__PERF_COUNTER_VER_MAJOR__ * 10000ul \
  37. +__PERF_COUNTER_VER_MINOR__ * 100ul \
  38. +__PERF_COUNTER_VER_REVISE__)
  39. /*! @} */
  40. /*!
  41. * \addtogroup gHelper 4 Helper
  42. * @{
  43. */
  44. // for IAR
  45. #undef __IS_COMPILER_IAR__
  46. #if defined(__IAR_SYSTEMS_ICC__)
  47. # define __IS_COMPILER_IAR__ 1
  48. #endif
  49. // for arm compiler 5
  50. #undef __IS_COMPILER_ARM_COMPILER_5__
  51. #if ((__ARMCC_VERSION >= 5000000) && (__ARMCC_VERSION < 6000000))
  52. # define __IS_COMPILER_ARM_COMPILER_5__ 1
  53. #endif
  54. //for arm compiler 6
  55. #undef __IS_COMPILER_ARM_COMPILER_6__
  56. #if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
  57. # define __IS_COMPILER_ARM_COMPILER_6__ 1
  58. #endif
  59. #undef __IS_COMPILER_ARM_COMPILER__
  60. #if defined(__IS_COMPILER_ARM_COMPILER_5__) && __IS_COMPILER_ARM_COMPILER_5__ \
  61. || defined(__IS_COMPILER_ARM_COMPILER_6__) && __IS_COMPILER_ARM_COMPILER_6__
  62. # define __IS_COMPILER_ARM_COMPILER__ 1
  63. #endif
  64. // for clang
  65. #undef __IS_COMPILER_LLVM__
  66. #if defined(__clang__) && !__IS_COMPILER_ARM_COMPILER_6__
  67. # define __IS_COMPILER_LLVM__ 1
  68. #else
  69. // for gcc
  70. # undef __IS_COMPILER_GCC__
  71. # if defined(__GNUC__) && !( defined(__IS_COMPILER_ARM_COMPILER__) \
  72. || defined(__IS_COMPILER_LLVM__) \
  73. || defined(__IS_COMPILER_IAR__))
  74. # define __IS_COMPILER_GCC__ 1
  75. # endif
  76. #endif
  77. #if defined(__clang__)
  78. # pragma clang diagnostic push
  79. # pragma clang diagnostic ignored "-Wunknown-warning-option"
  80. # pragma clang diagnostic ignored "-Wreserved-identifier"
  81. # pragma clang diagnostic ignored "-Wdeclaration-after-statement"
  82. # pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
  83. # pragma clang diagnostic ignored "-Wgnu-statement-expression"
  84. # pragma clang diagnostic ignored "-Wunused-but-set-variable"
  85. # pragma clang diagnostic ignored "-Wshadow"
  86. # pragma clang diagnostic ignored "-Wshorten-64-to-32"
  87. # pragma clang diagnostic ignored "-Wcompound-token-split-by-macro"
  88. # pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
  89. #elif defined(__IS_COMPILER_ARM_COMPILER_5__)
  90. # pragma diag_suppress 550
  91. #elif defined(__IS_COMPILER_GCC__)
  92. # pragma GCC diagnostic push
  93. # pragma GCC diagnostic ignored "-Wpedantic"
  94. # pragma GCC diagnostic ignored "-Wunused-variable"
  95. # pragma GCC diagnostic ignored "-Wunused-but-set-variable"
  96. # pragma GCC diagnostic ignored "-Wformat="
  97. #endif
  98. #ifndef __PLOOC_VA_NUM_ARGS_IMPL
  99. # define __PLOOC_VA_NUM_ARGS_IMPL( _0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11, \
  100. _12,_13,_14,_15,_16,__N,...) __N
  101. #endif
  102. #ifndef __PLOOC_VA_NUM_ARGS
  103. #define __PLOOC_VA_NUM_ARGS(...) \
  104. __PLOOC_VA_NUM_ARGS_IMPL( 0,##__VA_ARGS__,16,15,14,13,12,11,10,9, \
  105. 8,7,6,5,4,3,2,1,0)
  106. #endif
  107. #ifndef UNUSED_PARAM
  108. # define UNUSED_PARAM(__VAR) (void)(__VAR)
  109. #endif
  110. #undef __CONNECT2
  111. #undef __CONNECT3
  112. #undef __CONNECT4
  113. #undef __CONNECT5
  114. #undef __CONNECT6
  115. #undef __CONNECT7
  116. #undef __CONNECT8
  117. #undef __CONNECT9
  118. #undef CONNECT2
  119. #undef CONNECT3
  120. #undef CONNECT4
  121. #undef CONNECT5
  122. #undef CONNECT6
  123. #undef CONNECT7
  124. #undef CONNECT8
  125. #undef CONNECT9
  126. #undef CONNECT
  127. #undef __MACRO_EXPANDING
  128. #define __MACRO_EXPANDING(...) __VA_ARGS__
  129. #define __CONNECT2(__A, __B) __A##__B
  130. #define __CONNECT3(__A, __B, __C) __A##__B##__C
  131. #define __CONNECT4(__A, __B, __C, __D) __A##__B##__C##__D
  132. #define __CONNECT5(__A, __B, __C, __D, __E) __A##__B##__C##__D##__E
  133. #define __CONNECT6(__A, __B, __C, __D, __E, __F) __A##__B##__C##__D##__E##__F
  134. #define __CONNECT7(__A, __B, __C, __D, __E, __F, __G) \
  135. __A##__B##__C##__D##__E##__F##__G
  136. #define __CONNECT8(__A, __B, __C, __D, __E, __F, __G, __H) \
  137. __A##__B##__C##__D##__E##__F##__G##__H
  138. #define __CONNECT9(__A, __B, __C, __D, __E, __F, __G, __H, __I) \
  139. __A##__B##__C##__D##__E##__F##__G##__H##__I
  140. #define ALT_CONNECT2(__A, __B) __CONNECT2(__A, __B)
  141. #define CONNECT2(__A, __B) __CONNECT2(__A, __B)
  142. #define CONNECT3(__A, __B, __C) __CONNECT3(__A, __B, __C)
  143. #define CONNECT4(__A, __B, __C, __D) __CONNECT4(__A, __B, __C, __D)
  144. #define CONNECT5(__A, __B, __C, __D, __E) __CONNECT5(__A, __B, __C, __D, __E)
  145. #define CONNECT6(__A, __B, __C, __D, __E, __F) \
  146. __CONNECT6(__A, __B, __C, __D, __E, __F)
  147. #define CONNECT7(__A, __B, __C, __D, __E, __F, __G) \
  148. __CONNECT7(__A, __B, __C, __D, __E, __F, __G)
  149. #define CONNECT8(__A, __B, __C, __D, __E, __F, __G, __H) \
  150. __CONNECT8(__A, __B, __C, __D, __E, __F, __G, __H)
  151. #define CONNECT9(__A, __B, __C, __D, __E, __F, __G, __H, __I) \
  152. __CONNECT9(__A, __B, __C, __D, __E, __F, __G, __H, __I)
  153. #define CONNECT(...) \
  154. ALT_CONNECT2(CONNECT, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)
  155. #undef __using1
  156. #undef __using2
  157. #undef __using3
  158. #undef __using4
  159. #undef using
  160. #define __using1(__declare) \
  161. for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL; \
  162. CONNECT3(__using_, __LINE__,_ptr)++ == NULL; \
  163. )
  164. #define __using2(__declare, __on_leave_expr) \
  165. for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL; \
  166. CONNECT3(__using_, __LINE__,_ptr)++ == NULL; \
  167. (__on_leave_expr) \
  168. )
  169. #define __using3(__declare, __on_enter_expr, __on_leave_expr) \
  170. for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL; \
  171. CONNECT3(__using_, __LINE__,_ptr)++ == NULL ? \
  172. ((__on_enter_expr),1) : 0; \
  173. (__on_leave_expr) \
  174. )
  175. #define __using4(__dcl1, __dcl2, __on_enter_expr, __on_leave_expr) \
  176. for (__dcl1, __dcl2, *CONNECT3(__using_, __LINE__,_ptr) = NULL; \
  177. CONNECT3(__using_, __LINE__,_ptr)++ == NULL ? \
  178. ((__on_enter_expr),1) : 0; \
  179. (__on_leave_expr) \
  180. )
  181. #define using(...) \
  182. CONNECT2(__using, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)
  183. #undef __with2
  184. #undef __with3
  185. #undef with
  186. #define __with2(__type, __addr) \
  187. using(__type *_=(__addr))
  188. #define __with3(__type, __addr, __item) \
  189. using(__type *_=(__addr), *__item = _, _=_,_=_ )
  190. #define with(...) \
  191. CONNECT2(__with, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)
  192. #undef _
  193. #ifndef dimof
  194. # define dimof(__array) (sizeof(__array)/sizeof(__array[0]))
  195. #endif
  196. #define SAFE_NAME(__NAME) CONNECT3(__,__NAME,__LINE__)
  197. #undef foreach2
  198. #undef foreach3
  199. #undef foreach
  200. #define foreach2(__type, __array) \
  201. using(__type *_ = __array) \
  202. for ( uint_fast32_t SAFE_NAME(count) = dimof(__array); \
  203. SAFE_NAME(count) > 0; \
  204. _++, SAFE_NAME(count)-- \
  205. )
  206. #define foreach3(__type, __array, __item) \
  207. using(__type *_ = __array, *__item = _, _ = _, _ = _ ) \
  208. for ( uint_fast32_t SAFE_NAME(count) = dimof(__array); \
  209. SAFE_NAME(count) > 0; \
  210. _++, __item = _, SAFE_NAME(count)-- \
  211. )
  212. #define foreach(...) \
  213. CONNECT2(foreach, __PLOOC_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)
  214. #ifndef safe_atom_code
  215. # define safe_atom_code() \
  216. using( uint32_t SAFE_NAME(temp) = \
  217. ({ uint32_t SAFE_NAME(temp2)=__get_PRIMASK(); \
  218. __disable_irq(); \
  219. SAFE_NAME(temp2);}), \
  220. __set_PRIMASK(SAFE_NAME(temp)))
  221. #endif
  222. #ifndef __IRQ_SAFE
  223. # define __IRQ_SAFE \
  224. using( uint32_t SAFE_NAME(temp) = \
  225. ({ uint32_t SAFE_NAME(temp2)=__get_PRIMASK(); \
  226. __disable_irq(); \
  227. SAFE_NAME(temp2);}), \
  228. __set_PRIMASK(SAFE_NAME(temp)))
  229. #endif
  230. #ifndef __perf_counter_printf__
  231. # define __perf_counter_printf__ printf
  232. #endif
  233. #if __PLOOC_VA_NUM_ARGS() != 0
  234. #warning Please enable GNU extensions, it is required by __cycleof__() and \
  235. __super_loop_monitor__()
  236. #endif
  237. #if defined(__PERF_COUNTER_CFG_USE_SYSTICK_WRAPPER__)
  238. # if defined(__IS_COMPILER_ARM_COMPILER_5__) && __IS_COMPILER_ARM_COMPILER_5__
  239. # pragma import(__ensure_systick_wrapper)
  240. # elif (defined(__GNUC__) || defined(__clang__)) \
  241. && (!defined(__IS_COMPILER_IAR__) || !__IS_COMPILER_IAR__)
  242. __asm(".global __ensure_systick_wrapper\n\t");
  243. # endif
  244. #endif
  245. /*! @} */
  246. /*============================ MACROFIED FUNCTIONS ===========================*/
  247. /*!
  248. * \addtogroup gBasic 1 Basic
  249. * @{
  250. */
  251. /*!
  252. * \brief measure the cycle count of a given code segment
  253. * \param[in] __STR a description string for the measurement
  254. * \param[in] ... an optional code segement, in which we can read the measured
  255. * result from __cycle_count__.
  256. * \details Here is an example:
  257. E.g.
  258. \code
  259. __cycleof__("printf") {
  260. printf("hello world\r\n");
  261. }
  262. \endcode
  263. */
  264. #define __cycleof__(__STR, ...) \
  265. using(int64_t _ = get_system_ticks(), __cycle_count__ = _, \
  266. _=_, { \
  267. _ = get_system_ticks() - _ - g_nOffset; \
  268. __cycle_count__ = _; \
  269. if (__PLOOC_VA_NUM_ARGS(__VA_ARGS__) == 0) { \
  270. __perf_counter_printf__("\r\n"); \
  271. __perf_counter_printf__("-[Cycle Report]"); \
  272. __perf_counter_printf__( \
  273. "--------------------------------------------\r\n"); \
  274. __perf_counter_printf__( \
  275. __STR " total cycle count: %ld [%016lx]\r\n", \
  276. (long)_, (long)_); \
  277. } else { \
  278. __VA_ARGS__ \
  279. }; \
  280. })
  281. /*!
  282. * \brief measure the cpu usage for a given code segment and print out the
  283. * result in percentage.
  284. * \param[in] __CNT generate result on every given iterations
  285. * \param[in] ... an optional code segement, in which we can read the measured
  286. * result from __usage__ which is a float value.
  287. * \details Here is an example, 50% cpu time:
  288. E.g.
  289. \code
  290. while (1) {
  291. __cpu_time__(100) {
  292. delay_us(5000);
  293. }
  294. delay_us(5000);
  295. }
  296. \endcode
  297. */
  298. #define __cpu_time__(__CNT, ...) \
  299. static int64_t SAFE_NAME(s_lTimestamp) = 0, SAFE_NAME(s_lTotal) = 0; \
  300. static uint32_t s_wLoopCounter = (__CNT); \
  301. using(float __usage__ = 0, ({ \
  302. if (0 == s_wLoopCounter) { \
  303. __usage__ = (float)((double)SAFE_NAME(s_lTotal) \
  304. / (double)( get_system_ticks() \
  305. - SAFE_NAME(s_lTimestamp))); \
  306. __usage__ *= 100.0f; \
  307. SAFE_NAME(s_lTimestamp) = 0; \
  308. SAFE_NAME(s_lTotal) = 0; \
  309. if (__PLOOC_VA_NUM_ARGS(__VA_ARGS__) == 0) { \
  310. __perf_counter_printf__("CPU Usage %3.2f%%\r\n", (double)__usage__); \
  311. } else { \
  312. __VA_ARGS__ \
  313. } \
  314. } \
  315. if (0 == SAFE_NAME(s_lTimestamp)) { \
  316. SAFE_NAME(s_lTimestamp) = get_system_ticks(); \
  317. s_wLoopCounter = (__CNT); \
  318. } \
  319. start_task_cycle_counter();}), \
  320. ({SAFE_NAME(s_lTotal) += stop_task_cycle_counter(); \
  321. s_wLoopCounter--;}))
  322. /*!
  323. * \addtogroup gBasicTimerService 1.2 Timer Service
  324. * \ingroup gBasic
  325. * @{
  326. */
  327. /*!
  328. * \brief should not use
  329. */
  330. #define perfc_is_time_out_ms0() true
  331. /*!
  332. * \brief set an alarm with given period in ms and check the status
  333. *
  334. * \param[in] __ms a time period in millisecond
  335. * \param[in] __timestamp_ptr an optional timestamp holder
  336. * \param[in] __auto_reload whether starting next period after a timeout event
  337. *
  338. * \return bool whether it is timeout
  339. */
  340. #define perfc_is_time_out_ms3(__ms, __timestamp_ptr, __auto_reload) \
  341. ({ static int64_t SAFE_NAME(s_lTimestamp); (void)SAFE_NAME(s_lTimestamp); \
  342. __perfc_is_time_out(perfc_convert_ms_to_ticks(__ms), \
  343. (__timestamp_ptr), (__auto_reload));})
  344. /*!
  345. * \brief set an alarm with given period in ms and check the status
  346. *
  347. * \param[in] __ms a time period in millisecond
  348. * \param[in] __timestamp_ptr an optional timestamp holder
  349. *
  350. * \return bool whether it is timeout
  351. */
  352. #define perfc_is_time_out_ms2(__ms, __timestamp_ptr) \
  353. perfc_is_time_out_ms3((__ms), (__timestamp_ptr), true)
  354. /*!
  355. * \brief set an alarm with given period in ms and check the status
  356. *
  357. * \param[in] __ms a time period in millisecond
  358. * \param[in] __timestamp_ptr an optional timestamp holder
  359. *
  360. * \return bool whether it is timeout
  361. */
  362. #define perfc_is_time_out_ms1(__ms) \
  363. perfc_is_time_out_ms3((__ms), &SAFE_NAME(s_lTimestamp), true)
  364. /*!
  365. * \brief set an alarm with given period in ms and check the status
  366. *
  367. * \param[in] __ms a time period in millisecond
  368. * \param[in] ... an optional timestamp holder
  369. * \param[in] ... an optional indicator for whether starting next period after a timeout event
  370. *
  371. * \return bool whether it is timeout
  372. */
  373. #define perfc_is_time_out_ms(...) \
  374. CONNECT2(perfc_is_time_out_ms, __PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \
  375. (__VA_ARGS__)
  376. /*!
  377. * \brief set an alarm with given period in us and check the status
  378. *
  379. * \param[in] __us a time period in microsecond
  380. * \param[in] __timestamp_ptr an optional timestamp holder
  381. * \param[in] __auto_reload whether starting next period after a timeout event
  382. *
  383. * \return bool whether it is timeout
  384. */
  385. #define perfc_is_time_out_us3(__us, __timestamp_ptr, __auto_reload) \
  386. ({ static int64_t SAFE_NAME(s_lTimestamp); (void)SAFE_NAME(s_lTimestamp); \
  387. __perfc_is_time_out(perfc_convert_us_to_ticks(__us), \
  388. (__timestamp_ptr), (__auto_reload));})
  389. /*!
  390. * \brief set an alarm with given period in us and check the status
  391. *
  392. * \param[in] __us a time period in microsecond
  393. * \param[in] __timestamp_ptr an optional timestamp holder
  394. *
  395. * \return bool whether it is timeout
  396. */
  397. #define perfc_is_time_out_us2(__us, __timestamp_ptr) \
  398. perfc_is_time_out_us3((__us), (__timestamp_ptr), true)
  399. /*!
  400. * \brief set an alarm with given period in us and check the status
  401. *
  402. * \param[in] __us a time period in microsecond
  403. * \param[in] __timestamp_ptr an optional timestamp holder
  404. *
  405. * \return bool whether it is timeout
  406. */
  407. #define perfc_is_time_out_us1(__us) \
  408. perfc_is_time_out_us3((__us), &SAFE_NAME(s_lTimestamp), true)
  409. /*!
  410. * \brief set an alarm with given period in us and check the status
  411. *
  412. * \param[in] __us a time period in microsecond
  413. * \param[in] ... an optional timestamp holder
  414. * \param[in] ... an optional indicator for whether starting next period after a timeout event
  415. *
  416. * \return bool whether it is timeout
  417. */
  418. #define perfc_is_time_out_us(...) \
  419. CONNECT2(perfc_is_time_out_us, __PLOOC_VA_NUM_ARGS(__VA_ARGS__)) \
  420. (__VA_ARGS__)
  421. /*! @} */
  422. /*! @} */
  423. /*!
  424. * \addtogroup gRTOS 2 RTOS Support
  425. * @{
  426. */
  427. #define __super_loop_monitor__(__N, ...) \
  428. using( \
  429. struct { \
  430. int64_t lStart; \
  431. int64_t lTaskUsedCycles; \
  432. int64_t lTimeElapsed; \
  433. } __cpu_usage__ = {.lStart = get_system_ticks()}) \
  434. using(int SAFE_NAME(cnt) = (__N)) \
  435. for(start_task_cycle_counter();; ({ \
  436. if (!(--SAFE_NAME(cnt))) { \
  437. __cpu_usage__.lTimeElapsed \
  438. = get_system_ticks() - __cpu_usage__.lStart - g_nOffset; \
  439. __cpu_usage__.lTaskUsedCycles = stop_task_cycle_counter(); \
  440. \
  441. if (__PLOOC_VA_NUM_ARGS(__VA_ARGS__) == 0) { \
  442. __perf_counter_printf__( \
  443. "%s CPU Usage %2.3f%%\r\n", __func__, \
  444. (float)((double)__cpu_usage__.lTaskUsedCycles * 100.0 / \
  445. (double)__cpu_usage__.lTimeElapsed)); \
  446. } else { \
  447. __VA_ARGS__; \
  448. } \
  449. SAFE_NAME(cnt) = (__N); \
  450. __cpu_usage__.lStart = get_system_ticks(); \
  451. start_task_cycle_counter(); \
  452. }; \
  453. }))
  454. /*============================ TYPES =========================================*/
  455. typedef struct {
  456. int64_t lStart;
  457. int64_t lUsedTotal;
  458. int32_t nUsedRecent;
  459. uint16_t hwActiveCount;
  460. uint16_t : 15;
  461. uint16_t bEnabled : 1;
  462. } task_cycle_info_t;
  463. typedef struct task_cycle_info_agent_t task_cycle_info_agent_t;
  464. struct task_cycle_info_agent_t {
  465. task_cycle_info_t *ptInfo;
  466. task_cycle_info_agent_t *ptNext;
  467. task_cycle_info_agent_t *ptPrev;
  468. };
  469. /*! @} */
  470. /*============================ GLOBAL VARIABLES ==============================*/
  471. extern volatile int64_t g_lLastTimeStamp;
  472. extern volatile int32_t g_nOffset;
  473. /*============================ LOCAL VARIABLES ===============================*/
  474. /*============================ PROTOTYPES ====================================*/
  475. /*!
  476. * \addtogroup gBasicTicks 1.1 Ticks APIs
  477. * \ingroup gBasic
  478. * @{
  479. */
  480. /*!
  481. * \brief get the elapsed cycles since perf_counter is initialised
  482. * \return int64_t the elpased cycles
  483. */
  484. __attribute__((noinline))
  485. extern int64_t get_system_ticks(void);
  486. #ifdef __PERF_CNT_USE_LONG_CLOCK__
  487. /*! \note the prototype of this clock() is different from the one defined in
  488. *! time.h. As clock_t is usually defined as unsigned int, it is
  489. *! not big enough in Cortex-M system to hold a time-stamp. clock()
  490. *! defined here returns the timestamp since the begining of main()
  491. *! and its unit is clock cycle (rather than 1ms). Hence, for a system
  492. *! running under several hundreds MHz or even 1GHz, e.g. RT10xx from
  493. *! NXP, it is very easy to see a counter overflow as clock_t is
  494. *! defined as uint32_t in timer.h.
  495. *! Since we are not allowed to change the defintion of clock_t in
  496. *! official header file, i.e. time.h, I use a compatible prototype
  497. *! after I checked the AAPCS spec. So, the return of the clock() is
  498. *! int64_t, which will use the R0 to store the lower 32bits and R1
  499. *! to store the higher 32bits. When you are using the prototype from
  500. *! timer.h, caller will only take the lower 32bits stored in R0 and
  501. *! the higher 32bits stored in R1 will be ignored.
  502. *!
  503. *! If you want to use the non-overflow version of this clock(), please
  504. *! 1) define the MACRO: __PERF_CNT_USE_LONG_CLOCK__ in your project
  505. *! and 2) do not include system header file <time.h>
  506. *!
  507. */
  508. #if !defined(__IS_COMPILER_IAR__)
  509. __attribute__((nothrow))
  510. #endif
  511. __attribute__((noinline))
  512. extern int64_t clock(void);
  513. #endif
  514. /*!
  515. * \brief try to set a start pointer for the performance counter
  516. */
  517. __STATIC_INLINE
  518. void start_cycle_counter(void)
  519. {
  520. g_lLastTimeStamp = get_system_ticks();
  521. }
  522. /*!
  523. * \brief calculate the elapsed cycle count since the last start point
  524. * \note you can have multiple stop_cycle_counter following one start point
  525. * \return int32_t the elapsed cycle count
  526. */
  527. __STATIC_INLINE
  528. int64_t stop_cycle_counter(void)
  529. {
  530. int64_t lTemp = (get_system_ticks() - g_lLastTimeStamp);
  531. return lTemp - g_nOffset;
  532. }
  533. /*! @} */
  534. /*!
  535. * \addtogroup gBasicTimerService 1.2 Timer Service
  536. * \ingroup gBasic
  537. * @{
  538. */
  539. /*!
  540. * \brief get the elapsed milliseconds since perf_counter is initialised
  541. * \return int32_t the elapsed milliseconds
  542. */
  543. extern int32_t get_system_ms(void);
  544. /*!
  545. * \brief get the elapsed microsecond since perf_counter is initialised
  546. * \return int32_t the elapsed microsecond
  547. */
  548. extern int32_t get_system_us(void);
  549. /*!
  550. * \brief delay specified time in microsecond
  551. * \param[in] nUs time in microsecond
  552. */
  553. extern void delay_us(int32_t nUs);
  554. /*!
  555. * \brief delay specified time in millisecond
  556. * \param[in] nMs time in millisecond
  557. */
  558. extern void delay_ms(int32_t nMs);
  559. /*!
  560. * \brief convert ticks of a reference timer to millisecond
  561. *
  562. * \param[in] lTick the tick count
  563. * \return int64_t the millisecond
  564. */
  565. extern
  566. int64_t perfc_convert_ticks_to_ms(int64_t lTick);
  567. /*!
  568. * \brief convert millisecond into ticks of the reference timer
  569. *
  570. * \param[in] wMS the target time in millisecond
  571. * \return int64_t the ticks
  572. */
  573. extern
  574. int64_t perfc_convert_ms_to_ticks(uint32_t wMS);
  575. /*!
  576. * \brief convert ticks of a reference timer to microsecond
  577. *
  578. * \param[in] lTick the tick count
  579. * \return int64_t the microsecond
  580. */
  581. extern
  582. int64_t perfc_convert_ticks_to_us(int64_t lTick);
  583. /*!
  584. * \brief convert microsecond into ticks of the reference timer
  585. *
  586. * \param[in] wUS the target time in microsecond
  587. * \return int64_t the ticks
  588. */
  589. extern
  590. int64_t perfc_convert_us_to_ticks(uint32_t wUS);
  591. /*!
  592. * \brief set an alarm with given period and check the status
  593. *
  594. * \param[in] lPeriod a time period in ticks
  595. * \param[in] plTimestamp a pointer points to an int64_t integer, if NULL is
  596. * passed, an static local variable inside the function will be used
  597. * \param[in] bAutoReload whether starting next period after a timeout event.
  598. * \return bool whether it is timeout or not
  599. */
  600. extern
  601. bool __perfc_is_time_out(int64_t lPeriod, int64_t *plTimestamp, bool bAutoReload);
  602. /*! @} */
  603. /*!
  604. * \addtogroup gRTOS 2 RTOS Support
  605. * @{
  606. */
  607. #if defined(__PERF_CNT_USE_RTOS__)
  608. /*! \brief initialize the default virtual cycle counter for the current task
  609. */
  610. extern void init_task_cycle_counter(void);
  611. /*! \brief check whether the task stack canary is safe or not
  612. * \retval false likely to be a stack-overflow
  613. * \retval true task stack is safe
  614. */
  615. extern
  616. bool perfc_check_task_stack_canary_safe(void);
  617. /*! \brief provide cycle information for target task
  618. * \details Support RTOS List:
  619. * - RTX5
  620. * - RT-Thread
  621. * - ThreadX
  622. * - FreeRTOS
  623. *
  624. * \return task_cycle_info_t* the cycle info object passed to this function
  625. */
  626. extern task_cycle_info_t * get_rtos_task_cycle_info(void);
  627. /*!
  628. * \brief intialize a given task_cycle_info_t object and enable it before
  629. * registering it.
  630. * \return task_cycle_info_t* the cycle info object passed to this function
  631. */
  632. extern task_cycle_info_t *init_task_cycle_info(task_cycle_info_t *ptInfo);
  633. /*! \brief enable a given task_cycle_info_t object
  634. *
  635. * \param[in] ptInfo the address of target task_cycle_info_t object
  636. * \return bool previous status
  637. */
  638. extern bool enable_task_cycle_info(task_cycle_info_t *ptInfo);
  639. /*! \brief disable a given task_cycle_info_t object
  640. *
  641. * \param[in] ptInfo the address of target task_cycle_info_t object
  642. * \return bool previous status
  643. */
  644. extern bool disable_task_cycle_info(task_cycle_info_t *ptInfo);
  645. /*! \brief resume the enabled status of a given task_cycle_info_t object
  646. *
  647. * \param[in] ptInfo the address of target task_cycle_info_t object
  648. * \param[in] bEnabledStatus the previous status
  649. */
  650. extern
  651. void resume_task_cycle_info(task_cycle_info_t *ptInfo, bool bEnabledStatus);
  652. /*!
  653. * \brief register a global virtual cycle counter agent to the current task
  654. * \param[in] ptInfo the address of target task_cycle_info_t object
  655. * \param[in] ptAgent an list node for the task_cycle_info_t object
  656. * \note the ptAgent it is better to be allocated as a static variable, global
  657. * variable or comes from heap or pool
  658. *
  659. * \return task_cycle_info_agent_t* the agent passed to this function
  660. */
  661. extern
  662. task_cycle_info_agent_t *register_task_cycle_agent(
  663. task_cycle_info_t *ptInfo,
  664. task_cycle_info_agent_t *ptAgent);
  665. /*!
  666. * \brief remove a global virtual cycle counter agent from the current task
  667. * \param[in] ptAgent the list node currently in use
  668. * \return task_cycle_info_agent_t* the agent passed to this function
  669. */
  670. extern
  671. task_cycle_info_agent_t *
  672. unregister_task_cycle_agent(task_cycle_info_agent_t *ptAgent);
  673. /*! \brief reset and start the virtual cycle counter for the current task
  674. *
  675. * \param[in] ptInfo the target task_cycle_info_t object
  676. */
  677. __attribute__((noinline))
  678. extern void __start_task_cycle_counter(task_cycle_info_t *ptInfo);
  679. /*! \brief calculate the elapsed cycle count for current task since the last
  680. * start point
  681. *
  682. * \note you can call stop_cycle_counter() multiple times following one
  683. * start_task_cycle_counter()
  684. *
  685. * \param[in] ptInfo the target task_cycle_info_t object
  686. *
  687. * \note When ptInfo is NULL, it returns current task cycle info, when ptInfo
  688. * is non-NULL, it returns the total used cycles of the specified
  689. * task_cycle_info_t object.
  690. *
  691. * \return int64_t the elapsed cycle count.
  692. */
  693. __attribute__((noinline))
  694. extern int64_t __stop_task_cycle_counter(task_cycle_info_t *ptInfo);
  695. #define start_task_cycle_counter(...) \
  696. __start_task_cycle_counter((NULL,##__VA_ARGS__))
  697. #define stop_task_cycle_counter(...) \
  698. __stop_task_cycle_counter((NULL,##__VA_ARGS__))
  699. #elif !defined(__IMPLEMENT_PERF_COUNTER)
  700. # define start_task_cycle_counter(...) start_cycle_counter()
  701. # define stop_task_cycle_counter(...) stop_cycle_counter()
  702. # define init_task_cycle_counter()
  703. # define register_task_cycle_agent(...)
  704. # define unregister_task_cycle_agent(...)
  705. # define init_task_cycle_info(...) (NULL)
  706. # define enable_task_cycle_info(...) (false)
  707. # define disable_task_cycle_info(...) (false)
  708. # define resume_task_cycle_info(...)
  709. # define perfc_check_task_stack_canary_safe() (false)
  710. #endif
  711. /*! @} */
  712. /*!
  713. * \addtogroup gBasic 1 Basic
  714. * @{
  715. */
  716. /*----------------------------------------------------------------------------*
  717. * Please ignore the following APIs unless you have encountered some known *
  718. * special conditions *
  719. *----------------------------------------------------------------------------*/
  720. /*! \brief initialise cycle counter service
  721. * \note - don't forget to tell the function whether the systick is already
  722. * used by user applications.
  723. * Don't worry, this cycle counter service won't affect your existing
  724. * systick service.
  725. *
  726. * \note - Usually the perf_counter can initialise itself with the help of
  727. * __attribute__((constructor(255))), this works fine in Arm Compiler
  728. * 5 (armcc), Arm Compiler 6 (armclang), arm gcc and llvm. It doesn't
  729. * work for IAR. So, when you are using IAR, please call this function
  730. * manually to initialise the perf_counter service.
  731. *
  732. * \note - Perf_counter library assumes that:
  733. * 1. Your project has already using SysTick
  734. * 2. It assumes that you have already implemented the SysTick_Handler
  735. * 3. It assumes that you have enabled the exception handling for
  736. * SysTick.
  737. * If these are not the case, please:
  738. * 1. Add an empty SysTick_Handler to your project if you don't have
  739. * one
  740. * 2. Make sure you have the SysTick Exception handling enabled
  741. * 3. And call function init_cycle_counter(false) if you doesn't
  742. * use SysTick in your project at all.
  743. *
  744. * \param[in] bIsSysTickOccupied A boolean value which indicates whether SysTick
  745. * is already used by user application.
  746. */
  747. extern void init_cycle_counter(bool bIsSysTickOccupied);
  748. /*!
  749. * \brief a system timer handler inserted to the SysTick_Handler
  750. *
  751. * \note - if you are using a compiler other than armcc or armclang, e.g. iar,
  752. * arm gcc etc, the systick_wrapper_ual.o doesn't work with the linker
  753. * of your target toolchain as it use the $Super$$ which is only supported
  754. * by armlink. For this condition, you have to manually put this function
  755. * into your existing SysTick_Handler to make the perf_counter library
  756. * work.
  757. *
  758. * \note - if you are using Arm Compiler 5 (armcc) or Arm Compiler 6 (armclang)
  759. * you do NOT have to insert this function into your SysTick_Handler,
  760. * the systick_wrapper_ual.s will do the work for you.
  761. */
  762. extern void user_code_insert_to_systick_handler(void);
  763. /*!
  764. * \brief update perf_counter as SystemCoreClock has been updated.
  765. */
  766. extern void update_perf_counter(void);
  767. /*!
  768. * \brief prepare for reconfiguration of SysTick timer.
  769. *
  770. * \note some systems (e.g. FreeRTOS) might reconfigure the systick timer to
  771. * fulfil the requirement of their feature. To support this, just
  772. * before the reconfiguration, please call this function in order
  773. * to make the perf_counter works correctly later.
  774. *
  775. * \note after the reconfiguration, please call update_perf_counter() to apply
  776. * the changes to perf_counter.
  777. *
  778. * \note this function will stop the SysTick, clear the pending bit and set
  779. * the Load register and Current Value register to zero.
  780. */
  781. extern void before_cycle_counter_reconfiguration(void);
  782. /*! @} */
  783. /*!
  784. * \addtogroup gBenchmark 3 Benchmark
  785. * @{
  786. */
  787. #ifdef __PERF_COUNTER_COREMARK__
  788. /*!
  789. * \brief entry for coremark
  790. */
  791. void coremark_main(void);
  792. #endif
  793. /*! @} */
  794. //#if defined(__clang__)
  795. //# pragma clang diagnostic pop
  796. //#elif defined(__IS_COMPILER_GCC__)
  797. //# pragma GCC diagnostic pop
  798. //#endif
  799. #ifdef __cplusplus
  800. }
  801. #endif
  802. #endif