perf_counter v1.9.8
A dedicated performance counter for Cortex-M Systick. It shares the SysTick with users' original SysTick function(s) without interfering with it.
perf_counter.c
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
18/*============================ INCLUDES ======================================*/
19#include <stdint.h>
20#include <stdbool.h>
21#include <string.h>
22#include "cmsis_compiler.h"
23
24#define __IMPLEMENT_PERF_COUNTER
25#include "perf_counter.h"
26
27#if defined(__IS_COMPILER_GCC__)
28# pragma GCC diagnostic ignored "-Wattributes"
29#endif
30
31
32/*============================ MACROS ========================================*/
33#ifndef PERF_CNT_COMPENSATION_THRESHOLD
34# define PERF_CNT_COMPENSATION_THRESHOLD 16
35#endif
36
37#ifndef PERF_CNT_DELAY_US_COMPENSATION
38# define PERF_CNT_DELAY_US_COMPENSATION 90
39#endif
40
41
42/* IO definitions (access restrictions to peripheral registers) */
43#ifdef __cplusplus
44 #define __I volatile
45#else
46 #define __I volatile const
47#endif
48#define __O volatile
49#define __IO volatile
51/* following defines should be used for structure members */
52#define __IM volatile const
53#define __OM volatile
54#define __IOM volatile
56/* Memory mapping of Core Hardware */
57#define SCS_BASE (0xE000E000UL)
58#define SysTick_BASE (SCS_BASE + 0x0010UL)
59#define SCB_BASE (SCS_BASE + 0x0D00UL)
61#define SysTick ((SysTick_Type *) SysTick_BASE )
62#define SCB ((SCB_Type *) SCB_BASE )
64/* SysTick Control / Status Register Definitions */
65#define SysTick_CTRL_COUNTFLAG_Pos 16U
66#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos)
68#define SysTick_CTRL_CLKSOURCE_Pos 2U
69#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos)
71#define SysTick_CTRL_TICKINT_Pos 1U
72#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos)
74#define SysTick_CTRL_ENABLE_Pos 0U
75#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/)
77/* SysTick Reload Register Definitions */
78#define SysTick_LOAD_RELOAD_Pos 0U
79#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/)
81/* SysTick Current Register Definitions */
82#define SysTick_VAL_CURRENT_Pos 0U
83#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/)
85/* SysTick Calibration Register Definitions */
86#define SysTick_CALIB_NOREF_Pos 31U
87#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos)
89#define SysTick_CALIB_SKEW_Pos 30U
90#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos)
92#define SysTick_CALIB_TENMS_Pos 0U
93#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/)
97#define SCB_ICSR_PENDSTCLR_Pos 25U
98#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos)
100#define SCB_ICSR_PENDSTSET_Pos 26U
101#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos)
104#define MAGIC_WORD_AGENT_LIST_VALID 0x8492A53C
105#define MAGIC_WORD_CANARY 0xDEADBEEF
106
107/*============================ MACROFIED FUNCTIONS ===========================*/
108/*============================ TYPES =========================================*/
109
113typedef struct
114{
115 __IOM uint32_t CTRL;
116 __IOM uint32_t LOAD;
117 __IOM uint32_t VAL;
118 __IM uint32_t CALIB;
119} SysTick_Type;
120
124typedef struct
125{
126 __IM uint32_t CPUID;
127 __IOM uint32_t ICSR;
128 __IOM uint32_t VTOR;
129 __IOM uint32_t AIRCR;
130 __IOM uint32_t SCR;
131 __IOM uint32_t CCR;
132 __IOM uint8_t SHP[12U];
133 __IOM uint32_t SHCSR;
134 __IOM uint32_t CFSR;
135 __IOM uint32_t HFSR;
136 __IOM uint32_t DFSR;
137 __IOM uint32_t MMFAR;
138 __IOM uint32_t BFAR;
139 __IOM uint32_t AFSR;
140 __IM uint32_t PFR[2U];
141 __IM uint32_t DFR;
142 __IM uint32_t ADR;
143 __IM uint32_t MMFR[4U];
144 __IM uint32_t ISAR[5U];
145 uint32_t RESERVED0[5U];
146 __IOM uint32_t CPACR;
147} SCB_Type;
148
149struct __task_cycle_info_t {
150 task_cycle_info_t tInfo;
151 int64_t lLastTimeStamp;
153 uint32_t wMagicWord;
154} ;
155
156
157/*============================ GLOBAL VARIABLES ==============================*/
158extern uint32_t SystemCoreClock;
159
160/*============================ LOCAL VARIABLES ===============================*/
161volatile int64_t g_lLastTimeStamp = 0;
162volatile int32_t g_nOffset = 0;
163volatile static int32_t s_nUSUnit = 1;
164volatile static int32_t s_nMSUnit = 1;
165volatile static int32_t s_nMSResidule = 0;
166volatile static int32_t s_nSystemMS = 0;
167
168volatile static int64_t s_lSystemClockCounts = 0;
169
170
171/*============================ PROTOTYPES ====================================*/
172/*============================ IMPLEMENTATION ================================*/
173/*============================ INCLUDES ======================================*/
174
175__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
176{
177 if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
178 {
179 return (1UL); /* Reload value impossible */
180 }
181
182 //__IRQ_SAFE {
183 SysTick->CTRL = 0;
184
185 SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
186 //NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
187 SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
188 SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
189 SysTick_CTRL_TICKINT_Msk |
190 SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
191 //SCB->ICSR = SCB_ICSR_PENDSTCLR_Msk;
192 //}
193 return (0UL); /* Function successful */
194}
195
197{
198 uint32_t wLoad = SysTick->LOAD + 1;
199 s_lSystemClockCounts += wLoad;
200
201 // update system ms counter
202 s_nMSResidule += wLoad;
203 int32_t nMS = s_nMSResidule / s_nMSUnit;
204 s_nMSResidule -= nMS * s_nMSUnit;
205 s_nSystemMS += nMS;
206}
207
208__WEAK
209void __perf_os_patch_init(void)
210{
211}
212
213
215{
216 s_nUSUnit = SystemCoreClock / 1000000ul;
217 s_nMSUnit = SystemCoreClock / 1000ul;
218
219 __IRQ_SAFE {
220 g_lLastTimeStamp = get_system_ticks();
221 g_nOffset = get_system_ticks() - g_lLastTimeStamp;
222 }
223}
224
225
226void init_cycle_counter(bool bIsSysTickOccupied)
227{
228 __IRQ_SAFE {
229 if (!bIsSysTickOccupied) {
230 SysTick_Config(0x01000000); // use the longest period
231 }
232 SCB->ICSR = SCB_ICSR_PENDSTCLR_Msk;
233 }
234
236 s_lSystemClockCounts = 0; // reset system cycle counter
237 s_nSystemMS = 0; // reset system millisecond counter
238
239#if defined(__PERF_COUNTER_CFG_USE_SYSTICK_WRAPPER__)
240#if defined(__IS_COMPILER_ARM_COMPILER_5__) \
241 || defined(__IS_COMPILER_ARM_COMPILER_6__) \
242 || defined(__IS_COMPILER_GCC__) \
243 || defined(__IS_COMPILER_LLVM__)
244 extern void __ensure_systick_wrapper(void);
245 __ensure_systick_wrapper();
246#endif
247#endif
248
249 __perf_os_patch_init();
250}
251
256__STATIC_INLINE int32_t check_systick(void)
257{
258 int32_t nTemp = (int32_t)SysTick->LOAD - (int32_t)SysTick->VAL;
259
260 /* Since we cannot stop counting temporarily, there are several
261 * conditions which we should take into consideration:
262 * - Condition 1: when assigning nTemp with the register value (LOAD-VAL),
263 * the underflow didn't happen but when we check the PENDSTSET bit,
264 * the underflow happens, for this condition, we should not
265 * do any compensation. When this happens, the (LOAD-nTemp) is
266 * smaller than PERF_CNT_COMPENSATION_THRESHOLD (a small value) as
267 * long as LOAD is bigger than (or equals to) the
268 * PERF_CNT_COMPENSATION_THRESHOLD;
269 * - Condition 2: when assigning nTemp with the register value (LOAD-VAL),
270 * the VAL is zero and underflow happened and the PENDSTSET bit
271 * is set, for this condition, we should not do any compensation.
272 * When this happens, the (LOAD-nTemp) is equals to zero.
273 * - Condition 3: when assigning nTemp with the register value (LOAD-VAL),
274 * the underflow has already happened, hence the PENDSTSET
275 * is set, for this condition, we should compensate the return
276 * value. When this happens, the (LOAD-nTemp) is bigger than (or
277 * equals to) PERF_CNT_COMPENSATION_THRESHOLD.
278 * The following code implements an equivalent logic.
279 */
280 if (SCB->ICSR & SCB_ICSR_PENDSTSET_Msk){
281 if (((int32_t)SysTick->LOAD - nTemp) >= PERF_CNT_COMPENSATION_THRESHOLD) {
282 nTemp += SysTick->LOAD + 1;
283 }
284 }
285
286 return nTemp;
287}
288
289#if defined(__IS_COMPILER_IAR__)
290__attribute__((constructor))
291#else
292__attribute__((constructor(255)))
293#endif
294void __perf_counter_init(void)
295{
296 init_cycle_counter(true);
297}
298
299
300void delay_us(int32_t nUs)
301{
302 int64_t lUs = (int64_t)nUs * (int64_t)s_nUSUnit;
303 int32_t iCompensate = g_nOffset > PERF_CNT_DELAY_US_COMPENSATION
304 ? g_nOffset
305 : PERF_CNT_DELAY_US_COMPENSATION;
306
307 if (lUs <= iCompensate) {
308 return ;
309 }
310
311 lUs -= iCompensate;
312
313 lUs += get_system_ticks();
314 while(get_system_ticks() < lUs);
315}
316
317
318void delay_ms(int32_t nMs)
319{
320 int64_t lUs = (int64_t)nMs * (int64_t)s_nMSUnit;
321 int32_t iCompensate = g_nOffset > PERF_CNT_DELAY_US_COMPENSATION
322 ? g_nOffset
323 : PERF_CNT_DELAY_US_COMPENSATION;
324
325 if (lUs <= iCompensate) {
326 return ;
327 }
328
329 lUs -= iCompensate;
330
331 lUs += get_system_ticks();
332 while(get_system_ticks() < lUs);
333}
334
356#if !defined(__IS_COMPILER_IAR__)
357__attribute__((nothrow))
358#endif
359__attribute__((noinline))
360int64_t clock(void)
361{
362 int64_t lTemp = 0;
363
364 __IRQ_SAFE {
365 lTemp = check_systick() + s_lSystemClockCounts;
366 }
367
368 return lTemp;
369}
370
371
372__attribute__((noinline))
373int64_t get_system_ticks(void)
374{
375 int64_t lTemp = 0;
376
377 __IRQ_SAFE {
378 lTemp = check_systick() + s_lSystemClockCounts;
379 }
380
381 return lTemp;
382}
383
384int32_t get_system_ms(void)
385{
386 int32_t nTemp = 0;
387
388 __IRQ_SAFE {
389 nTemp = s_nSystemMS + (check_systick() + s_nMSResidule) / s_nMSUnit;
390 }
391
392 return nTemp;
393}
394
395
396__WEAK
398{
399 return NULL;
400}
401
403{
404 struct __task_cycle_info_t * ptRootAgent =
405 (struct __task_cycle_info_t *)get_rtos_task_cycle_info();
406 if (NULL == ptRootAgent) {
407 return ;
408 }
409
410 memset(ptRootAgent, 0, sizeof(struct __task_cycle_info_t));
411
412 ptRootAgent->tList.ptInfo = &(ptRootAgent->tInfo);
413 ptRootAgent->tInfo.lStart = get_system_ticks();
414 ptRootAgent->wMagicWord = MAGIC_WORD_CANARY;
415}
416
418{
419 do {
420 if (NULL == ptInfo) {
421 break;
422 }
423
424 memset(ptInfo, 0, sizeof(task_cycle_info_t));
425
426 ptInfo->bEnabled = true;
427 } while(0);
428
429 return ptInfo;
430}
431
433{
434 if (NULL == ptInfo) {
435 return false;
436 }
437 bool bOrig;
438 __IRQ_SAFE {
439 bOrig = ptInfo->bEnabled;
440 ptInfo->bEnabled = true;
441 }
442 return bOrig;
443}
444
446{
447 if (NULL == ptInfo) {
448 return false;
449 }
450 bool bOrig;
451 __IRQ_SAFE {
452 bOrig = ptInfo->bEnabled;
453 ptInfo->bEnabled = false;
454 }
455 return bOrig;
456}
457
458void resume_task_cycle_info(task_cycle_info_t *ptInfo, bool bEnabledStatus)
459{
460 if (NULL == ptInfo) {
461 return;
462 }
463
464 ptInfo->bEnabled = bEnabledStatus;
465}
466
467
470{
471 __IRQ_SAFE {
472 do {
473 if (NULL == ptAgent || NULL == ptInfo) {
474 break;
475 }
476
477 struct __task_cycle_info_t * ptRootAgent =
478 (struct __task_cycle_info_t *)get_rtos_task_cycle_info();
479 if (NULL == ptRootAgent) {
480 break;
481 }
482
483 ptRootAgent->wMagicWord = MAGIC_WORD_AGENT_LIST_VALID;
484
485 ptAgent->ptInfo = ptInfo;
486
487 // push to the stack
488 do {
489 // set next-list
490 ptAgent->ptNext = ptRootAgent->tList.ptNext;
491 ptRootAgent->tList.ptNext = ptAgent;
492
493 // set prev-list
494 ptAgent->ptPrev = &(ptRootAgent->tList);
495 if (NULL != ptAgent->ptNext) {
496 ptAgent->ptNext->ptPrev = ptAgent;
497 }
498 } while(0);
499
500 } while(0);
501 }
502
503 return ptAgent;
504}
505
508{
509 __IRQ_SAFE {
510 do {
511 if (NULL == ptAgent) {
512 break;
513 }
514
515 task_cycle_info_agent_t *ptPrev = ptAgent->ptPrev;
516 if (NULL == ptPrev) {
517 break; /* this should not happen */
518 }
519 if (ptPrev->ptNext != ptAgent) {
520 // already removed
521 break;
522 }
523
525 ptPrev->ptNext = ptAgent->ptNext;
526
527 if (NULL != ptAgent->ptNext) {
528 // remove agent from the prev-list
529 ptAgent->ptNext->ptPrev = ptPrev;
530 }
531
532 ptAgent->ptNext = NULL;
533 ptAgent->ptPrev = NULL;
534
535 } while(0);
536 }
537
538 return ptAgent;
539}
540
541
542void __on_context_switch_in(uint32_t *pwStack)
543{
544 struct __task_cycle_info_t *ptRootAgent = (struct __task_cycle_info_t *)pwStack;
545 uint64_t dwTimeStamp = get_system_ticks();
546
547 ptRootAgent->lLastTimeStamp = dwTimeStamp;
548 ptRootAgent->tInfo.hwActiveCount++;
549
550 if (MAGIC_WORD_AGENT_LIST_VALID == ptRootAgent->wMagicWord) {
551 // update all agents
552 task_cycle_info_agent_t *ptAgent = ptRootAgent->tList.ptNext;
553 while(NULL != ptAgent) {
554 if (NULL != ptAgent->ptInfo) {
555 if (ptAgent->ptInfo->bEnabled) {
556 ptAgent->ptInfo->hwActiveCount++;
557 }
558 }
559 ptAgent = ptAgent->ptNext;
560 }
561 }
562}
563
564void __on_context_switch_out(uint32_t *pwStack)
565{
566 struct __task_cycle_info_t *ptRootAgent = (struct __task_cycle_info_t *)pwStack;
567 int64_t lCycleUsed = get_system_ticks() - ptRootAgent->lLastTimeStamp - g_nOffset;
568
569 ptRootAgent->tInfo.nUsedRecent = lCycleUsed;
570 ptRootAgent->tInfo.lUsedTotal += lCycleUsed;
571
572 if (MAGIC_WORD_AGENT_LIST_VALID == ptRootAgent->wMagicWord) {
573 // update all agents
574 task_cycle_info_agent_t *ptAgent = ptRootAgent->tList.ptNext;
575 while(NULL != ptAgent) {
576 if (NULL != ptAgent->ptInfo) {
577 if (ptAgent->ptInfo->bEnabled) {
578 ptAgent->ptInfo->nUsedRecent = lCycleUsed;
579 ptAgent->ptInfo->lUsedTotal += lCycleUsed;
580 }
581 }
582 ptAgent = ptAgent->ptNext;
583 }
584 }
585}
586
587__attribute__((noinline))
588void __start_task_cycle_counter(task_cycle_info_t *ptInfo)
589{
590 struct __task_cycle_info_t * ptRootAgent =
591 (struct __task_cycle_info_t *)get_rtos_task_cycle_info();
592 if (NULL == ptRootAgent) {
593 return ;
594 }
595
596 __IRQ_SAFE {
597 ptRootAgent->lLastTimeStamp = get_system_ticks();
598 ptRootAgent->tInfo.lUsedTotal = 0;
599
600 if (NULL != ptInfo) {
601 ptInfo->lUsedTotal = 0;
602 ptInfo->bEnabled = true;
603 }
604 }
605}
606
607__attribute__((noinline))
608int64_t __stop_task_cycle_counter(task_cycle_info_t *ptInfo)
609{
610 struct __task_cycle_info_t * ptRootAgent =
611 (struct __task_cycle_info_t *)get_rtos_task_cycle_info();
612 if (NULL == ptRootAgent) {
613 return 0;
614 }
615
616 int64_t lCycles = 0;
617
618 __IRQ_SAFE {
619 int64_t lCycleUsed = get_system_ticks() - ptRootAgent->lLastTimeStamp - g_nOffset;
620 ptRootAgent->tInfo.lUsedTotal += lCycleUsed;
621
622 if (NULL != ptInfo) {
623 if (ptInfo->bEnabled) {
624 ptInfo->nUsedRecent = lCycleUsed;
625 ptInfo->lUsedTotal += lCycleUsed;
626 ptInfo->bEnabled = false;
627 }
628
629 lCycles = ptInfo->lUsedTotal;
630 } else {
631 lCycles = ptRootAgent->tInfo.lUsedTotal;
632 }
633 }
634
635 return lCycles;
636}
637
void user_code_insert_to_systick_handler(void)
a system timer handler inserted to the SysTick_Handler
Definition: perf_counter.c:196
void update_perf_counter(void)
update perf_counter as SystemCoreClock has been updated.
Definition: perf_counter.c:214
void delay_ms(int32_t nMs)
delay specified time in millisecond
Definition: perf_counter.c:318
int64_t get_system_ticks(void)
get the elapsed cycles since perf_counter is initialised
Definition: perf_counter.c:373
void init_cycle_counter(bool bIsSysTickOccupied)
initialise cycle counter service
Definition: perf_counter.c:226
void delay_us(int32_t nUs)
delay specified time in microsecond
Definition: perf_counter.c:300
int32_t get_system_ms(void)
get the elapsed milliseconds since perf_counter is initialised
Definition: perf_counter.c:384
bool disable_task_cycle_info(task_cycle_info_t *ptInfo)
disable a given task_cycle_info_t object
Definition: perf_counter.c:445
task_cycle_info_agent_t * register_task_cycle_agent(task_cycle_info_t *ptInfo, task_cycle_info_agent_t *ptAgent)
register a global virtual cycle counter agent to the current task
Definition: perf_counter.c:468
void init_task_cycle_counter(void)
initialize the default virtual cycle counter for the current task
Definition: perf_counter.c:402
void resume_task_cycle_info(task_cycle_info_t *ptInfo, bool bEnabledStatus)
resume the enabled status of a given task_cycle_info_t object
Definition: perf_counter.c:458
__WEAK task_cycle_info_t * get_rtos_task_cycle_info(void)
provide cycle information for target task
Definition: perf_counter.c:397
bool enable_task_cycle_info(task_cycle_info_t *ptInfo)
enable a given task_cycle_info_t object
Definition: perf_counter.c:432
task_cycle_info_agent_t * unregister_task_cycle_agent(task_cycle_info_agent_t *ptAgent)
remove a global virtual cycle counter agent from the current task
Definition: perf_counter.c:507
task_cycle_info_t * init_task_cycle_info(task_cycle_info_t *ptInfo)
intialize a given task_cycle_info_t object and enable it before registering it.
Definition: perf_counter.c:417