Skip to content

Commit 2fbbf0a

Browse files
authored
Merge pull request #2666 from stevew817/bugfix/EFM32_us_timer
[EFM32] Microsecond ticker optimization
2 parents 6bfcb3c + c4c1ba2 commit 2fbbf0a

File tree

1 file changed

+68
-51
lines changed
  • hal/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32

1 file changed

+68
-51
lines changed

hal/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/us_ticker.c

Lines changed: 68 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @file us_ticker.c
33
*******************************************************************************
44
* @section License
5-
* <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b>
5+
* <b>(C) Copyright 2016 Silicon Labs, http://www.silabs.com</b>
66
*******************************************************************************
77
*
88
* SPDX-License-Identifier: Apache-2.0
@@ -42,33 +42,29 @@
4242

4343
static uint8_t us_ticker_inited = 0; // Is ticker initialized yet
4444

45-
static volatile uint32_t ticker_cnt = 0x2ff00; //Internal overflow count, used to extend internal 16-bit counter to (MHz * 32-bit)
46-
static volatile uint16_t ticker_int_rem = 0; //Timer match value for user interrupt
45+
static volatile uint32_t ticker_cnt = 0; //Internal overflow count, used to extend internal 16-bit counter to (MHz * 32-bit)
4746
static volatile uint32_t ticker_int_cnt = 0; //Amount of overflows until user interrupt
4847
static volatile uint8_t ticker_freq_mhz = 0; //Frequency of timer in MHz
48+
static volatile uint32_t ticker_top_us = 0; //Amount of us corresponding to the top value of the timer
4949

5050
void us_ticker_irq_handler_internal(void)
5151
{
52+
/* Handle timer overflow */
53+
if (TIMER_IntGet(US_TICKER_TIMER) & TIMER_IF_OF) {
54+
ticker_cnt++;
55+
if(ticker_cnt >= ((uint32_t)ticker_freq_mhz << 16)) ticker_cnt = 0;
56+
TIMER_IntClear(US_TICKER_TIMER, TIMER_IF_OF);
57+
}
58+
5259
/* Check for user interrupt expiration */
5360
if (TIMER_IntGet(US_TICKER_TIMER) & TIMER_IF_CC0) {
54-
if (ticker_int_rem > 0) {
55-
TIMER_CompareSet(US_TICKER_TIMER, 0, ticker_int_rem);
56-
ticker_int_rem = 0;
57-
TIMER_IntClear(US_TICKER_TIMER, TIMER_IF_CC0);
58-
} else if (ticker_int_cnt > 0) {
61+
if (ticker_int_cnt > 0) {
5962
ticker_int_cnt--;
6063
TIMER_IntClear(US_TICKER_TIMER, TIMER_IF_CC0);
6164
} else {
6265
us_ticker_irq_handler();
6366
}
6467
}
65-
66-
/* Handle timer overflow */
67-
if (TIMER_IntGet(US_TICKER_TIMER) & TIMER_IF_OF) {
68-
ticker_cnt++;
69-
if(ticker_cnt >= (((uint32_t)ticker_freq_mhz) << 16)) ticker_cnt = 0;
70-
TIMER_IntClear(US_TICKER_TIMER, TIMER_IF_OF);
71-
}
7268
}
7369

7470
void us_ticker_init(void)
@@ -104,6 +100,9 @@ void us_ticker_init(void)
104100
/* Set prescaler */
105101
US_TICKER_TIMER->CTRL = (US_TICKER_TIMER->CTRL & ~_TIMER_CTRL_PRESC_MASK) | (prescaler << _TIMER_CTRL_PRESC_SHIFT);
106102

103+
/* calculate top value */
104+
ticker_top_us = (uint32_t) 0x10000 / ticker_freq_mhz;
105+
107106
/* Select Compare Channel parameters */
108107
TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT;
109108
timerCCInit.mode = timerCCModeCompare;
@@ -117,15 +116,16 @@ void us_ticker_init(void)
117116
NVIC_EnableIRQ(US_TICKER_TIMER_IRQ);
118117

119118
/* Set top value */
120-
TIMER_TopSet(US_TICKER_TIMER, 0xFFFF);
119+
TIMER_TopSet(US_TICKER_TIMER, (ticker_top_us * ticker_freq_mhz) - 1);
121120

122121
/* Start TIMER */
123122
TIMER_Enable(US_TICKER_TIMER, true);
124123
}
125124

126125
uint32_t us_ticker_read()
127126
{
128-
uint32_t volatile countH_old, countH, countL;
127+
uint32_t countH_old, countH;
128+
uint16_t countL;
129129

130130
if (!us_ticker_inited) {
131131
us_ticker_init();
@@ -134,65 +134,82 @@ uint32_t us_ticker_read()
134134
/* Avoid jumping in time by reading high bits twice */
135135
do {
136136
countH_old = ticker_cnt;
137-
/* If the counter overflowed while in the IRQ handler for the CC0 interrupt,
138-
* it hasn't had time to update ticker_cnt yet. Take this into account here. */
139137
if (TIMER_IntGet(US_TICKER_TIMER) & TIMER_IF_OF) {
140-
countH_old += 1;
138+
countH_old++;
141139
}
142140
countL = US_TICKER_TIMER->CNT;
143141
countH = ticker_cnt;
144-
/* If the counter overflowed while in the IRQ handler for the CC0 interrupt,
145-
* it hasn't had time to update ticker_cnt yet. Take this into account here. */
146142
if (TIMER_IntGet(US_TICKER_TIMER) & TIMER_IF_OF) {
147-
countH += 1;
143+
countH++;
148144
}
149145
} while (countH_old != countH);
150146

151-
/* Merge upper (mhz * 16-bit) and lower 16-bit into 64bit */
152-
uint64_t count = ((uint64_t)countH << 16) | (uint64_t)countL;
153-
/* Divide by ticker_freq_mhz to get 32-bit 1MHz timestamp */
154-
return (count / ticker_freq_mhz);
147+
/* Timer count value needs to be div'ed by the frequency to get to 1MHz ticks.
148+
* For the software-extended part, the amount of us in one overflow is constant.
149+
*/
150+
return (countL / ticker_freq_mhz) + (countH * ticker_top_us);
155151
}
156152

157153
void us_ticker_set_interrupt(timestamp_t timestamp)
158154
{
159-
int32_t delta = 0, ts = timestamp, time = us_ticker_read();
155+
uint64_t goal = timestamp;
156+
uint32_t trigger;
160157

161158
if((US_TICKER_TIMER->IEN & TIMER_IEN_CC0) == 0) {
162159
//Timer was disabled, but is going to be enabled. Set sleep mode.
163160
blockSleepMode(TIMER_LEAST_ACTIVE_SLEEPMODE);
164161
}
165162
TIMER_IntDisable(US_TICKER_TIMER, TIMER_IEN_CC0);
166163

167-
delta = ts - time;
168-
if(delta <= ticker_freq_mhz) {
169-
delta = ticker_freq_mhz;
170-
timestamp = us_ticker_read() + 0x100;
171-
}
164+
/* convert us delta value back to timer ticks */
165+
goal -= us_ticker_read();
166+
trigger = US_TICKER_TIMER->CNT;
172167

173-
/* Multiply by ticker_freq_mhz to get clock ticks */
174-
delta *= ticker_freq_mhz;
175-
/* Overflowing this doesn't matter, since we only need the lower 16 bits */
176-
ts *= ticker_freq_mhz;
168+
/* Catch "Going back in time" */
169+
if(goal < (50 / (REFERENCE_FREQUENCY / 1000000)) ||
170+
goal >= 0xFFFFFF00UL) {
171+
TIMER_IntClear(US_TICKER_TIMER, TIMER_IFC_CC0);
172+
TIMER_CompareSet(US_TICKER_TIMER, 0, (US_TICKER_TIMER->CNT + 3 > US_TICKER_TIMER->TOP ? 3 : US_TICKER_TIMER->CNT + 3));
173+
TIMER_IntEnable(US_TICKER_TIMER, TIMER_IEN_CC0);
174+
return;
175+
}
177176

178-
/* Split delta between timers */
179-
ticker_int_cnt = (((uint64_t)delta) >> 16) & 0xFFFFFFFF;
180-
ticker_int_rem = ts & 0xFFFF;
177+
/* Cap at 32 bit */
178+
goal &= 0xFFFFFFFFUL;
179+
/* Convert to ticker timebase */
180+
goal *= ticker_freq_mhz;
181181

182-
/* Set compare channel 0 to (current position + lower 16 bits of delta).
183-
* If lower 16 bits is a small number, we a do one compare of (current + lower 16 + 0x8000)
184-
* and then one of (current + lower 16). Else, we simply use (current + lower 16).
182+
/* Note: we should actually populate the following fields by the division and remainder
183+
* of goal / ticks_per_overflow, but since we're keeping the frequency as low
184+
* as possible, and ticks_per_overflow as close to FFFF as possible, we can
185+
* get away with ditching the division here and saving cycles.
185186
*
186-
* When time from lower 16 bits have elapsed, run complete cycles with ticker_int_rem as
187-
* reference ticker_int_cnt times. */
188-
if ((delta & 0xFFFF) < 0x8000 && ticker_int_cnt > 0) {
189-
TIMER_CompareSet(US_TICKER_TIMER, 0, ticker_int_rem + 0x8000);
190-
ticker_int_cnt--;
187+
* "exact" implementation:
188+
* ticker_int_cnt = goal / TIMER_TopGet(US_TICKER_TIMER);
189+
* ticker_int_rem = goal % TIMER_TopGet(US_TICKER_TIMER);
190+
*/
191+
ticker_int_cnt = (goal >> 16) & 0xFFFFFFFF;
192+
193+
/* Set compare channel 0 to (current position + lower 16 bits of target).
194+
* When lower 16 bits match, run complete cycles with ticker_int_rem as trigger value
195+
* for ticker_int_cnt times. */
196+
TIMER_IntClear(US_TICKER_TIMER, TIMER_IFC_CC0);
197+
198+
/* Take top of timer into account so that we don't end up missing a cycle */
199+
/* Set trigger point by adding delta to current time */
200+
if((goal & 0xFFFF) >= TIMER_TopGet(US_TICKER_TIMER)) {
201+
trigger += (goal & 0xFFFF) - TIMER_TopGet(US_TICKER_TIMER);
202+
ticker_int_cnt++;
191203
} else {
192-
TIMER_CompareSet(US_TICKER_TIMER, 0, ticker_int_rem);
193-
ticker_int_rem = 0;
204+
trigger += (goal & 0xFFFF);
194205
}
195-
TIMER_IntClear(US_TICKER_TIMER, TIMER_IFC_CC0);
206+
207+
if(trigger >= TIMER_TopGet(US_TICKER_TIMER)) {
208+
trigger -= TIMER_TopGet(US_TICKER_TIMER);
209+
}
210+
211+
TIMER_CompareSet(US_TICKER_TIMER, 0, trigger);
212+
196213
TIMER_IntEnable(US_TICKER_TIMER, TIMER_IEN_CC0);
197214
}
198215

0 commit comments

Comments
 (0)