-
Notifications
You must be signed in to change notification settings - Fork 7.6k
micros() overflow fix #267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The micros() function uses the cycle count register of the CPU. This function divides the cycle count by the number of cycles in a microsecond and returns that value. The problem is when the cycle count overflows at the 32bit maximum value, the microsecond value will also overflow. Many applications expect the microsecond value not to overflow before it reaches 2^32. This function now keeps track of cycle count overflows, and take them into account when calculating the microseconds.
Added a comment to describe the function.
that is true, given that you call micros often enough :) else it will again go out of sync. |
Why not call gettimeofday instead? This will fail if the CPU frequency is not constant. |
@igrr I remember having issues with gettimeofday a while back and resorted to the current code. Will look into it again. Things change on both fronts as we go :) I wonder how many unnecessary workarounds we will find with time. |
@igrr tested |
Ok, I see. Will add a cheaper "get number of microseconds since boot" function... |
How about we calculate overflow count in a thread and keep the time accurate. The reading of ccounter and incrementing overflow is cheap. |
OK the following code costs 0.2us. If no one has arguments, I'll commit it. uint32_t IRAM_ATTR micros()
{
static uint32_t lccount = 0;
uint32_t ccount;
static uint32_t overflow = 0;
__asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) );
if(ccount < lccount){
overflow += 4294967295 / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
}
lccount = ccount;
return overflow + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ);
} |
You may possibly want to replace that integer constant with a UINT32_MAX, but aside from that it's fine (with the above mentioned caveat about calling micros often enough). If you add a call to micros to the idle hook, there should be no issues at all. |
Yup :) gave it a go in another task and... havoc :D looking into better solution. |
maybe something like this: portMUX_TYPE microsMux = portMUX_INITIALIZER_UNLOCKED;
uint32_t IRAM_ATTR micros()
{
static uint32_t lccount = 0;
static uint32_t overflow = 0;
uint32_t ccount;
__asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) );
if(ccount < lccount){
if(xPortInIsrContext()){
portENTER_CRITICAL_ISR(µsMux);
overflow += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
portEXIT_CRITICAL_ISR(µsMux);
} else {
portENTER_CRITICAL(µsMux);
overflow += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
portEXIT_CRITICAL(µsMux);
}
}
lccount = ccount;
return overflow + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ);
} |
maybe makes the IRAM pointless? |
FYI you can use There is still a race condition in the code above. Consider the following case:
How bad is grabbing |
@igrr yeah I though of that and was looking for a way to maybe do it without portMUX_TYPE microsMux = portMUX_INITIALIZER_UNLOCKED;
uint32_t IRAM_ATTR micros()
{
static uint32_t lccount = 0;
static uint32_t overflow = 0;
uint32_t ccount;
portENTER_CRITICAL_ISR(µsMux);
__asm__ __volatile__ ( "rsr %0, ccount" : "=a" (ccount) );
if(ccount < lccount){
overflow += UINT32_MAX / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
}
lccount = ccount;
portEXIT_CRITICAL_ISR(µsMux);
return overflow + (ccount / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ);
} |
Implemented :) |
The micros() function uses the cycle count register of the CPU. This function divides the cycle count by the number of cycles in a microsecond and returns that value. The problem is when the cycle count overflows at the 32bit maximum value, the microsecond value will also overflow. Many applications expect the microsecond value not to overflow before it reaches 2^32.
This function now keeps track of cycle count overflows, and take them into account when calculating the microseconds.