@@ -31,12 +31,6 @@ void z_arm_exc_exit(void);
3131
3232#define TICKLESS (IS_ENABLED(CONFIG_TICKLESS_KERNEL))
3333
34- /* VAL value above which we assume that a subsequent COUNTFLAG
35- * overflow seen in CTRL is real and not an artifact of wraparound
36- * timing.
37- */
38- #define VAL_ABOUT_TO_WRAP 8
39-
4034static struct k_spinlock lock ;
4135
4236static u32_t last_load ;
@@ -90,41 +84,34 @@ static volatile u32_t overflow_cyc;
9084 */
9185static u32_t elapsed (void )
9286{
93- u32_t val , ctrl1 , ctrl2 ;
94-
95- /* SysTick is infuriatingly racy. The counter wraps at zero
96- * automatically, setting a 1 in the COUNTFLAG bit of the CTRL
97- * register when it does. But reading the control register
98- * automatically resets that bit, so we need to save it for
99- * future calls. And ordering is critical and race-prone: if
100- * we read CTRL first, then it is possible for VAL to wrap
101- * after that read but before we read VAL and we'll miss the
102- * overflow. If we read VAL first, then it can wrap after we
103- * read it and we'll see an "extra" overflow in CTRL. And we
104- * want to handle multiple overflows, so we effectively must
105- * read CTRL first otherwise there will be no way to detect
106- * the double-overflow if called at the end of a cycle. There
107- * is no safe algorithm here, so we split the difference by
108- * reading CTRL twice, suppressing the second overflow bit if
109- * VAL was "about to overflow".
110- */
111- ctrl1 = SysTick -> CTRL ;
112- val = SysTick -> VAL ;
113- ctrl2 = SysTick -> CTRL ;
114-
115- /* overflow_cyc is reset to zero by
116- * - _init()
117- * - _isr()
118- * - _set_timeout()
87+ u32_t val1 = SysTick -> VAL ; /* A */
88+ u32_t ctrl = SysTick -> CTRL ; /* B */
89+ u32_t val2 = SysTick -> VAL ; /* C */
90+
91+ /* SysTick behavior: The counter wraps at zero automatically,
92+ * setting the COUNTFLAG field of the CTRL register when it
93+ * does. Reading the control register automatically clears
94+ * that field.
95+ *
96+ * If the count wrapped...
97+ * 1) Before A then COUNTFLAG will be set and val1 >= val2
98+ * 2) Between A and B then COUNTFLAG will be set and val1 < val2
99+ * 3) Between B and C then COUNTFLAG will be clear and val1 < val2
100+ * 4) After C we'll see it next time
101+ *
102+ * So the count in val2 is post-wrap and last_load needs to be
103+ * added if and only if COUNTFLAG is set or val1 < val2.
119104 */
120- overflow_cyc += ( ctrl1 & SysTick_CTRL_COUNTFLAG_Msk ) ? last_load : 0 ;
121- if ( val > VAL_ABOUT_TO_WRAP ) {
122- int wrap = ctrl2 & SysTick_CTRL_COUNTFLAG_Msk ;
105+ if (( ctrl & SysTick_CTRL_COUNTFLAG_Msk )
106+ || ( val1 < val2 ) ) {
107+ overflow_cyc += last_load ;
123108
124- overflow_cyc += (wrap != 0 ) ? last_load : 0 ;
109+ /* We know there was a wrap, but we might not have
110+ * seen it in CTRL, so clear it. */
111+ (void )SysTick -> CTRL ;
125112 }
126113
127- return (last_load - val ) + overflow_cyc ;
114+ return (last_load - val2 ) + overflow_cyc ;
128115}
129116
130117/* Callout out of platform assembly, not hooked via IRQ_CONNECT... */
0 commit comments