drivers: systick: Fix Cortex-M systick jumping forward and back again
The existing implementation did not properly handle when `SysTick->VAL` is zero. This caused three subtle edge cases: * val1=0,COUNTFLAG=0,val2=0 This should result in no cycles elapsed, however `(last_load - val2) = last_load`. So an extra `last_load` cycles was returned. * val1=0,COUNTFLAG=0,val2=(last_load-1) This should result in 1 cycle elapsed, however `val1 < val2` so an extra `last_load` cycles was returned. * val1=[2,1,0],COUNTFLAG=1,val2=0 This should result in `last_load` cycles elapsed. However, `last_load * 2` cycles was returned. To fix the calculation, val1 and val2 are first wrapped/realigned from [0:last_load-1] to [1:last_load]. Tidy comments to better reflect the SysTick behaviour and link reference manuals. Signed-off-by: Grant Ramsay <gramsay@enphaseenergy.com>
This commit is contained in:
parent
147cef6660
commit
e032f9520d
1 changed files with 20 additions and 4 deletions
|
@ -101,10 +101,19 @@ static uint32_t elapsed(void)
|
|||
uint32_t ctrl = SysTick->CTRL; /* B */
|
||||
uint32_t val2 = SysTick->VAL; /* C */
|
||||
|
||||
/* SysTick behavior: The counter wraps at zero automatically,
|
||||
* setting the COUNTFLAG field of the CTRL register when it
|
||||
* does. Reading the control register automatically clears
|
||||
* that field.
|
||||
/* SysTick behavior: The counter wraps after zero automatically.
|
||||
* The COUNTFLAG field of the CTRL register is set when it
|
||||
* decrements from 1 to 0. Reading the control register
|
||||
* automatically clears that field. When a timer is started,
|
||||
* count begins at zero then wraps after the first cycle.
|
||||
* Reference:
|
||||
* Armv6-m (B3.3.1) https://developer.arm.com/documentation/ddi0419
|
||||
* Armv7-m (B3.3.1) https://developer.arm.com/documentation/ddi0403
|
||||
* Armv8-m (B11.1) https://developer.arm.com/documentation/ddi0553
|
||||
*
|
||||
* First, manually wrap/realign val1 and val2 from [0:last_load-1]
|
||||
* to [1:last_load]. This allows subsequent code to assume that
|
||||
* COUNTFLAG and wrapping occur on the same cycle.
|
||||
*
|
||||
* If the count wrapped...
|
||||
* 1) Before A then COUNTFLAG will be set and val1 >= val2
|
||||
|
@ -115,6 +124,13 @@ static uint32_t elapsed(void)
|
|||
* So the count in val2 is post-wrap and last_load needs to be
|
||||
* added if and only if COUNTFLAG is set or val1 < val2.
|
||||
*/
|
||||
if (val1 == 0) {
|
||||
val1 = last_load;
|
||||
}
|
||||
if (val2 == 0) {
|
||||
val2 = last_load;
|
||||
}
|
||||
|
||||
if ((ctrl & SysTick_CTRL_COUNTFLAG_Msk)
|
||||
|| (val1 < val2)) {
|
||||
overflow_cyc += last_load;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue