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