mirror of
https://github.com/GorgonMeducer/perf_counter.git
synced 2025-02-07 19:34:18 +08:00
found some racing condition and fixed them
- fixed the racing condition - introduce a minimal systick LOAD restriction - add some comments
This commit is contained in:
parent
6ec7874669
commit
2e465bc4c2
@ -155,26 +155,21 @@
|
|||||||
/*============================ LOCAL VARIABLES ===============================*/
|
/*============================ LOCAL VARIABLES ===============================*/
|
||||||
/*============================ PROTOTYPES ====================================*/
|
/*============================ PROTOTYPES ====================================*/
|
||||||
|
|
||||||
/* Function: initialise cycle counter service
|
|
||||||
* and 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.
|
|
||||||
*/
|
|
||||||
extern void init_cycle_counter(bool bSysTickIsOccupied);
|
|
||||||
|
|
||||||
/* Function : start_time
|
|
||||||
This function will be called right before starting the timed portion of the benchmark.
|
|
||||||
Implementation may be capturing a system timer (as implemented in the example code)
|
|
||||||
or zeroing some system parameters - e.g. setting the cpu clocks cycles to 0.
|
|
||||||
*/
|
|
||||||
extern void start_cycle_counter(void);
|
|
||||||
|
|
||||||
/* Function : stop_time
|
|
||||||
This function will be called right after ending the timed portion of the benchmark.
|
/*! \brief try to set a start pointer for the performance counter
|
||||||
Implementation may be capturing a system timer (as implemented in the example code)
|
*! \retval false the LOAD register is too small
|
||||||
or other system parameters - e.g. reading the current value of cpu cycles counter.
|
*! \retval true performance counter starts
|
||||||
*/
|
*/
|
||||||
|
extern bool start_cycle_counter(void);
|
||||||
|
|
||||||
|
/*! \brief calculate the elapsed cycle count since the last start point
|
||||||
|
*!
|
||||||
|
*! \note you can have multiple stop_cycle_counter following one start point
|
||||||
|
*!
|
||||||
|
*! \return the elapsed cycle count.
|
||||||
|
*/
|
||||||
extern int32_t stop_cycle_counter(void);
|
extern int32_t stop_cycle_counter(void);
|
||||||
|
|
||||||
/* Function : delay specified us with the help from systick
|
/* Function : delay specified us with the help from systick
|
||||||
@ -207,4 +202,55 @@ __attribute__((nothrow))
|
|||||||
extern int64_t clock(void);
|
extern int64_t clock(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*
|
||||||
|
* Please ignore the following APIs unless you have encountered some known *
|
||||||
|
* special conditions *
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief initialise cycle counter service
|
||||||
|
*! and 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:
|
||||||
|
*! a. Your project has already using SysTick
|
||||||
|
*! b. It assumes that you have already implemented the SysTick_Handler
|
||||||
|
*! c. It assumes that you have enabled the exception handling for
|
||||||
|
*! SysTick.
|
||||||
|
*! If these are not the case, please:
|
||||||
|
*! a. Add an empty SysTick_Handler to your project if you don't have
|
||||||
|
*! one
|
||||||
|
*! b. Make sure you have the SysTick Exception handling enabled
|
||||||
|
*! c. And call function init_cycle_counter(false) if you doesn't
|
||||||
|
*! use SysTick in your project at all.
|
||||||
|
*!
|
||||||
|
*! \param bSysTickIsOccupied A boolean value which indicates whether SysTick
|
||||||
|
*! is already used by user application.
|
||||||
|
*/
|
||||||
|
extern void init_cycle_counter(bool bSysTickIsOccupied);
|
||||||
|
|
||||||
|
|
||||||
|
/*! \note if you are using a compiler rather 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 user_code_insert_to_systick_handler(void);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Binary file not shown.
@ -22,6 +22,10 @@
|
|||||||
#include "cmsis_compiler.h"
|
#include "cmsis_compiler.h"
|
||||||
#include "perf_counter.h"
|
#include "perf_counter.h"
|
||||||
|
|
||||||
|
#ifndef PERF_CNT_COMPENSATION_THRESHOLD
|
||||||
|
# define PERF_CNT_COMPENSATION_THRESHOLD 16
|
||||||
|
#endif
|
||||||
|
|
||||||
/*============================ MACROS ========================================*/
|
/*============================ MACROS ========================================*/
|
||||||
/* IO definitions (access restrictions to peripheral registers) */
|
/* IO definitions (access restrictions to peripheral registers) */
|
||||||
/**
|
/**
|
||||||
@ -192,6 +196,14 @@ void user_code_insert_to_systick_handler(void)
|
|||||||
s_lSystemClockCounts += wLoad;
|
s_lSystemClockCounts += wLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! \brief initialise cycle counter service
|
||||||
|
*! and 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.
|
||||||
|
*! \param bSysTickIsOccupied A boolean value which indicates whether SysTick
|
||||||
|
*! is already used by user application.
|
||||||
|
*/
|
||||||
void init_cycle_counter(bool bSysTickIsOccupied)
|
void init_cycle_counter(bool bSysTickIsOccupied)
|
||||||
{
|
{
|
||||||
if (!bSysTickIsOccupied) {
|
if (!bSysTickIsOccupied) {
|
||||||
@ -205,16 +217,20 @@ void init_cycle_counter(bool bSysTickIsOccupied)
|
|||||||
__ensure_systick_wrapper();
|
__ensure_systick_wrapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Function : start_time
|
/*! \brief try to start the performance counter
|
||||||
This function will be called right before starting the timed portion of the benchmark.
|
*! \retval false the LOAD register is too small
|
||||||
Implementation may be capturing a system timer (as implemented in the example code)
|
*! \retval true performance counter starts
|
||||||
or zeroing some system parameters - e.g. setting the cpu clocks cycles to 0.
|
|
||||||
*/
|
*/
|
||||||
void start_cycle_counter(void)
|
bool start_cycle_counter(void)
|
||||||
{
|
{
|
||||||
|
if (SysTick->LOAD < PERF_CNT_COMPENSATION_THRESHOLD) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
safe_atom_code(){
|
safe_atom_code(){
|
||||||
s_nCycleCounts = (int32_t)SysTick->VAL - (int32_t)SysTick->LOAD;
|
s_nCycleCounts = (int32_t)SysTick->VAL - (int32_t)SysTick->LOAD;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \note this function should only be called when irq is disabled
|
/*! \note this function should only be called when irq is disabled
|
||||||
@ -225,22 +241,41 @@ static __attribute__((always_inline)) int32_t check_systick(void)
|
|||||||
{
|
{
|
||||||
int32_t nTemp = (int32_t)SysTick->LOAD - (int32_t)SysTick->VAL;
|
int32_t nTemp = (int32_t)SysTick->LOAD - (int32_t)SysTick->VAL;
|
||||||
|
|
||||||
/*! \note here is a corner case: SysTick->VAL is zero and SysTick Pending bit is set.
|
/*! \note Since we cannot stop counting temporarily, there are several
|
||||||
*! we should check this corner condition with (nTemp != SysTick->LOAD)
|
*! conditions which we should take into consideration:
|
||||||
|
*! Condition one: when assign nTemp with the register value (LOAD-VAL),
|
||||||
|
*! the underflow didn't happen and when we check the PENDSTSET bit,
|
||||||
|
*! the underflow happens, for this condition, we should not
|
||||||
|
*! do any compensation. When this happens, the (LOAD-nTemp) is
|
||||||
|
*! smaller than PERF_CNT_COMPENSATION_THRESHOLD (a big value) as
|
||||||
|
*! long as LOAD is bigger than (or equals to) the
|
||||||
|
*! PERF_CNT_COMPENSATION_THRESHOLD;
|
||||||
|
*! Condition two: when assign nTemp with the register value (LOAD-VAL),
|
||||||
|
*! the VAL is zero and underflow happened and the PENDSTSET bit
|
||||||
|
*! is set, for this condition, we should not do any compensation.
|
||||||
|
*! When this happens, the (LOAD-nTemp) is equals to zero.
|
||||||
|
*! Condition Three: when initialising nTemp with the register value
|
||||||
|
*! VAL, the underflow has already happened, hence the PENDSTSET
|
||||||
|
*! is set, for this condition, we should compensate the return
|
||||||
|
*! value. When this happens, the (LOAD-nTemp) is bigger than (or
|
||||||
|
*! equals to) PERF_CNT_COMPENSATION_THRESHOLD.
|
||||||
|
*! The following code implements an equivalent logic.
|
||||||
*/
|
*/
|
||||||
if ( (SCB->ICSR & SCB_ICSR_PENDSTSET_Msk)
|
if (SCB->ICSR & SCB_ICSR_PENDSTSET_Msk){
|
||||||
&& (nTemp != SysTick->LOAD)) {
|
if (((int32_t)SysTick->LOAD - nTemp) >= PERF_CNT_COMPENSATION_THRESHOLD) {
|
||||||
nTemp += SysTick->LOAD;
|
nTemp += SysTick->LOAD;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nTemp;
|
return nTemp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Function : stop_time
|
/*! \brief calculate the elapsed cycle count since the last start point
|
||||||
This function will be called right after ending the timed portion of the benchmark.
|
*!
|
||||||
Implementation may be capturing a system timer (as implemented in the example code)
|
*! \note you can have multiple stop_cycle_counter following one start point
|
||||||
or other system parameters - e.g. reading the current value of cpu cycles counter.
|
*!
|
||||||
*/
|
*! \return the elapsed cycle count.
|
||||||
|
*/
|
||||||
int32_t stop_cycle_counter(void)
|
int32_t stop_cycle_counter(void)
|
||||||
{
|
{
|
||||||
int32_t nTemp = 0;
|
int32_t nTemp = 0;
|
||||||
|
@ -155,26 +155,21 @@
|
|||||||
/*============================ LOCAL VARIABLES ===============================*/
|
/*============================ LOCAL VARIABLES ===============================*/
|
||||||
/*============================ PROTOTYPES ====================================*/
|
/*============================ PROTOTYPES ====================================*/
|
||||||
|
|
||||||
/* Function: initialise cycle counter service
|
|
||||||
* and 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.
|
|
||||||
*/
|
|
||||||
extern void init_cycle_counter(bool bSysTickIsOccupied);
|
|
||||||
|
|
||||||
/* Function : start_time
|
|
||||||
This function will be called right before starting the timed portion of the benchmark.
|
|
||||||
Implementation may be capturing a system timer (as implemented in the example code)
|
|
||||||
or zeroing some system parameters - e.g. setting the cpu clocks cycles to 0.
|
|
||||||
*/
|
|
||||||
extern void start_cycle_counter(void);
|
|
||||||
|
|
||||||
/* Function : stop_time
|
|
||||||
This function will be called right after ending the timed portion of the benchmark.
|
/*! \brief try to set a start pointer for the performance counter
|
||||||
Implementation may be capturing a system timer (as implemented in the example code)
|
*! \retval false the LOAD register is too small
|
||||||
or other system parameters - e.g. reading the current value of cpu cycles counter.
|
*! \retval true performance counter starts
|
||||||
*/
|
*/
|
||||||
|
extern bool start_cycle_counter(void);
|
||||||
|
|
||||||
|
/*! \brief calculate the elapsed cycle count since the last start point
|
||||||
|
*!
|
||||||
|
*! \note you can have multiple stop_cycle_counter following one start point
|
||||||
|
*!
|
||||||
|
*! \return the elapsed cycle count.
|
||||||
|
*/
|
||||||
extern int32_t stop_cycle_counter(void);
|
extern int32_t stop_cycle_counter(void);
|
||||||
|
|
||||||
/* Function : delay specified us with the help from systick
|
/* Function : delay specified us with the help from systick
|
||||||
@ -207,4 +202,55 @@ __attribute__((nothrow))
|
|||||||
extern int64_t clock(void);
|
extern int64_t clock(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------*
|
||||||
|
* Please ignore the following APIs unless you have encountered some known *
|
||||||
|
* special conditions *
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief initialise cycle counter service
|
||||||
|
*! and 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:
|
||||||
|
*! a. Your project has already using SysTick
|
||||||
|
*! b. It assumes that you have already implemented the SysTick_Handler
|
||||||
|
*! c. It assumes that you have enabled the exception handling for
|
||||||
|
*! SysTick.
|
||||||
|
*! If these are not the case, please:
|
||||||
|
*! a. Add an empty SysTick_Handler to your project if you don't have
|
||||||
|
*! one
|
||||||
|
*! b. Make sure you have the SysTick Exception handling enabled
|
||||||
|
*! c. And call function init_cycle_counter(false) if you doesn't
|
||||||
|
*! use SysTick in your project at all.
|
||||||
|
*!
|
||||||
|
*! \param bSysTickIsOccupied A boolean value which indicates whether SysTick
|
||||||
|
*! is already used by user application.
|
||||||
|
*/
|
||||||
|
extern void init_cycle_counter(bool bSysTickIsOccupied);
|
||||||
|
|
||||||
|
|
||||||
|
/*! \note if you are using a compiler rather 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 user_code_insert_to_systick_handler(void);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user