Skip to content

Commit c2c80f9

Browse files
committed
wait_us optimization
As the timer code became more generic, coping with initialization on demand, and variable width and speed us_ticker_api implementations, wait_us has gradually gotten slower and slower. Some platforms have reportedly seen overhead of wait_us() increase from 10µs to 30µs. These changes should fully reverse that drop, and even make it better than ever. Add fast paths for platforms that provide compile-time information about us_ticker. Speed and code size is improved further if: * Timer has >= 2^32 microsecond range, or better still is 32-bit 1MHz. * Platform implements us_ticker_read() as a macro * Timer is initialised at boot, rather than first use The latter initialisation option is the default for STM, as this has always been the case.
1 parent 64575fe commit c2c80f9

File tree

10 files changed

+159
-26
lines changed

10 files changed

+159
-26
lines changed

TESTS/mbed_hal/us_ticker/main.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ void us_ticker_info_test()
3434
TEST_ASSERT(p_ticker_info->frequency >= 250000);
3535
TEST_ASSERT(p_ticker_info->frequency <= 8000000);
3636
TEST_ASSERT(p_ticker_info->bits >= 16);
37+
38+
#ifdef US_TICKER_PERIOD_NUM
39+
TEST_ASSERT_UINT32_WITHIN(1, 1000000 * US_TICKER_PERIOD_DEN / US_TICKER_PERIOD_NUM, _ticker_info->frequency);
40+
TEST_ASSERT_UINT32_EQUAL(US_TICKER_MASK, ((uint64_t)1 << p_ticker_info->bits) - 1);
41+
#endif
3742
}
3843

3944
utest::v1::status_t test_setup(const size_t number_of_cases)

hal/mbed_us_ticker_api.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,48 @@
1616
*/
1717

1818
#include <stddef.h>
19+
#include "platform/mbed_atomic.h"
1920
#include "hal/us_ticker_api.h"
2021

2122
#if DEVICE_USTICKER
2223

24+
#if !MBED_CONF_TARGET_INIT_US_TICKER_AT_BOOT
25+
bool _us_ticker_initialized;
26+
#endif
27+
2328
static ticker_event_queue_t events = { 0 };
2429

2530
static ticker_irq_handler_type irq_handler = ticker_irq_handler;
2631

32+
// We provide these wrapper to either:
33+
// a) not actually initialize and free, because we want it to
34+
// always run,
35+
// b) note that it's currently running, for wait_us() to peek
36+
static void do_us_ticker_init()
37+
{
38+
#if !MBED_CONF_TARGET_INIT_US_TICKER_AT_BOOT
39+
us_ticker_init();
40+
core_util_atomic_store_bool(&_us_ticker_initialized, true);
41+
#endif
42+
}
43+
44+
static void do_us_ticker_free()
45+
{
46+
#if !MBED_CONF_TARGET_INIT_US_TICKER_AT_BOOT
47+
core_util_atomic_store_bool(&_us_ticker_initialized, false);
48+
us_ticker_free();
49+
#endif
50+
}
51+
2752
static const ticker_interface_t us_interface = {
28-
.init = us_ticker_init,
53+
.init = do_us_ticker_init,
2954
.read = us_ticker_read,
3055
.disable_interrupt = us_ticker_disable_interrupt,
3156
.clear_interrupt = us_ticker_clear_interrupt,
3257
.set_interrupt = us_ticker_set_interrupt,
3358
.fire_interrupt = us_ticker_fire_interrupt,
3459
.get_info = us_ticker_get_info,
35-
.free = us_ticker_free,
60+
.free = do_us_ticker_free,
3661
.runs_in_deep_sleep = false,
3762
};
3863

hal/us_ticker_api.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,21 @@ extern "C" {
4141
*
4242
* @see hal_us_ticker_tests
4343
*
44+
* # Compile-time optimization macros
45+
*
46+
* To permit compile-time optimization, particularly of wait_us, the following macros should
47+
* be defined by a target's device.h:
48+
*
49+
* US_TICKER_PERIOD_NUM, US_TICKER_PERIOD_DEN: These denote the ratio (numerator, denominator)
50+
* of the ticker period to a microsecond. For example, an 8MHz ticker would have NUM = 1, DEN = 8;
51+
* a 1MHz ticker would have NUM = 1, DEN = 1; a 250kHz ticker would have NUM = 4, DEN = 1.
52+
* Both numerator and denominator must be 16 bits or less.
53+
*
54+
* US_TICKER_MASK: The value mask for the ticker - eg 0x07FFFFFF for a 27-bit ticker.
55+
*
56+
* If any are defined, all 3 must be defined, and the macros are checked for consistency with
57+
* us_ticker_get_info by test ::us_ticker_info_test.
58+
4459
* @{
4560
*/
4661

@@ -74,6 +89,7 @@ extern "C" {
7489
* Verified by ::ticker_fire_now_test
7590
* * The ticker operations ticker_read, ticker_clear_interrupt, ticker_set_interrupt and ticker_fire_interrupt
7691
* take less than 20us to complete - Verified by ::ticker_speed_test
92+
* * The ticker operations ticker_init and ticker_read are atomic.
7793
*
7894
* # Undefined behavior
7995
* * Calling any function other than ticker_init before the initialization of the ticker
@@ -210,7 +226,7 @@ void us_ticker_free(void);
210226
* }
211227
* @endcode
212228
*/
213-
uint32_t us_ticker_read(void);
229+
uint32_t (us_ticker_read)(void);
214230

215231
/** Set interrupt for specified timestamp
216232
*

platform/mbed_retarget.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,9 @@ extern "C" FILEHANDLE PREFIX(_open)(const char *name, int openflags)
483483
if (!mbed_sdk_inited) {
484484
mbed_copy_nvic();
485485
mbed_sdk_init();
486+
#if DEVICE_USTICKER && MBED_CONF_TARGET_INIT_US_TICKER_AT_BOOT
487+
us_ticker_init();
488+
#endif
486489
mbed_sdk_inited = 1;
487490
}
488491
#endif

platform/mbed_sdk_boot.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <stdlib.h>
2020
#include <stdint.h>
2121
#include "cmsis.h"
22+
#include "hal/us_ticker_api.h"
2223

2324
/* This startup is for mbed 2 baremetal. There is no config for RTOS for mbed 2,
2425
* therefore we protect this file with MBED_CONF_RTOS_PRESENT
@@ -82,6 +83,9 @@ void _platform_post_stackheap_init(void)
8283
{
8384
mbed_copy_nvic();
8485
mbed_sdk_init();
86+
#if DEVICE_USTICKER && MBED_CONF_TARGET_INIT_US_TICKER_AT_BOOT
87+
us_ticker_init();
88+
#endif
8589
}
8690

8791
#elif defined (__GNUC__)
@@ -92,6 +96,9 @@ void software_init_hook(void)
9296
{
9397
mbed_copy_nvic();
9498
mbed_sdk_init();
99+
#if DEVICE_USTICKER && MBED_CONF_TARGET_INIT_US_TICKER_AT_BOOT
100+
us_ticker_init();
101+
#endif
95102
software_init_hook_rtos();
96103
}
97104

@@ -107,6 +114,9 @@ int __wrap_main(void)
107114
int __low_level_init(void)
108115
{
109116
mbed_copy_nvic();
117+
#if DEVICE_USTICKER && MBED_CONF_TARGET_INIT_US_TICKER_AT_BOOT
118+
us_ticker_init();
119+
#endif
110120
return 1;
111121
}
112122

platform/mbed_wait_api.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
#ifndef MBED_WAIT_API_H
2626
#define MBED_WAIT_API_H
2727

28+
#include "platform/mbed_atomic.h"
29+
#include "device.h"
30+
2831
#ifdef __cplusplus
2932
extern "C" {
3033
#endif
@@ -115,6 +118,48 @@ void wait_us(int us);
115118
*/
116119
void wait_ns(unsigned int ns);
117120

121+
/* Optimize if we know the rate */
122+
#if DEVICE_USTICKER && defined US_TICKER_PERIOD_NUM
123+
void _wait_us_ticks(uint32_t ticks);
124+
void _wait_us_generic(unsigned int us);
125+
126+
/* Further optimization if we know us_ticker is always running */
127+
#if MBED_CONF_TARGET_INIT_US_TICKER_AT_BOOT
128+
#define _us_ticker_is_initialized true
129+
#else
130+
extern bool _us_ticker_initialized;
131+
#define _us_ticker_is_initialized core_util_atomic_load_bool(&_us_ticker_initialized)
132+
#endif
133+
134+
#if US_TICKER_PERIOD_DEN == 1 && (US_TICKER_MASK * US_TICKER_PERIOD_NUM) >= 0xFFFFFFFF
135+
/* Ticker is wide and slow enough to have full 32-bit range - can always use it directly */
136+
#define _us_is_small_enough(us) true
137+
#else
138+
/* Threshold is determined by specification of us_ticker_api.h - smallest possible
139+
* time range for the us_ticker is 16-bit 8MHz, which gives 8192us. This also leaves
140+
* headroom for the multiplication in 32 bits.
141+
*/
142+
#define _us_is_small_enough(us) ((us) < 8192)
143+
#endif
144+
145+
/* Speed optimisation for small wait_us. Care taken to preserve binary compatibility */
146+
inline void _wait_us_inline(unsigned int us)
147+
{
148+
/* Threshold is determined by specification of us_ticker_api.h - smallest possible
149+
* time range for the us_ticker is 16-bit 8MHz, which gives 8192us. This also leaves
150+
* headroom for the multiplication in 32 bits.
151+
*/
152+
if (_us_is_small_enough(us) && _us_ticker_is_initialized) {
153+
const uint32_t ticks = ((us * US_TICKER_PERIOD_DEN) + US_TICKER_PERIOD_NUM - 1) / US_TICKER_PERIOD_NUM;
154+
_wait_us_ticks(ticks);
155+
} else {
156+
_wait_us_generic(us);
157+
}
158+
}
159+
160+
#define wait_us(us) _wait_us_inline(us)
161+
#endif // Known-rate, initialised timer
162+
118163
#ifdef __cplusplus
119164
}
120165
#endif

platform/mbed_wait_api_no_rtos.c

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@
1919
#include "platform/mbed_toolchain.h"
2020
#include "platform/mbed_wait_api.h"
2121

22+
#include "hal/us_ticker_api.h"
23+
#include "hal/ticker_api.h"
24+
2225
// This implementation of the wait functions will be compiled only
2326
// if the RTOS is not present.
2427
#ifndef MBED_CONF_RTOS_PRESENT
2528

26-
#include "hal/us_ticker_api.h"
27-
2829
void wait(float s)
2930
{
3031
wait_us(s * 1000000.0f);
@@ -35,24 +36,55 @@ void wait_ms(int ms)
3536
wait_us(ms * 1000);
3637
}
3738

39+
#endif // #ifndef MBED_CONF_RTOS_PRESENT
40+
41+
// This wait_us is used by both RTOS and non-RTOS builds
42+
/* The actual time delay may be 1 less usec */
43+
44+
#if DEVICE_USTICKER
45+
46+
#if defined US_TICKER_PERIOD_NUM
47+
/* Real definition for binary compatibility with binaries not using the new macro */
48+
void (wait_us)(int us)
49+
{
50+
wait_us(us);
51+
}
52+
53+
/* External definition for the inline function */
54+
extern void _wait_us_inline(unsigned int us);
55+
56+
void _wait_us_ticks(uint32_t ticks)
57+
{
58+
const uint32_t start = us_ticker_read();
59+
while (((us_ticker_read() - start) & US_TICKER_MASK) < ticks);
60+
}
61+
62+
void _wait_us_generic(unsigned int us)
63+
#else
3864
void wait_us(int us)
65+
#endif
3966
{
40-
#if DEVICE_USTICKER
67+
// Generic version using full ticker, allowing for initialization, scaling and widening of timer
4168
const ticker_data_t *const ticker = get_us_ticker_data();
42-
uint32_t start = ticker_read(ticker);
69+
const uint32_t start = ticker_read(ticker);
4370
while ((ticker_read(ticker) - start) < (uint32_t)us);
44-
#else // fallback to wait_ns for targets without usticker
45-
while (us > 1000) {
46-
us -= 1000;
47-
wait_ns(1000000);
71+
}
72+
73+
#else // DEVICE_USTICKER
74+
75+
// fallback to wait_ns for targets without usticker
76+
void wait_us(int us)
77+
{
78+
while (us > 1024) {
79+
us -= 1024;
80+
wait_ns(1024000);
4881
}
4982
if (us > 0) {
5083
wait_ns(us * 1000);
5184
}
52-
#endif // DEVICE_USTICKER
5385
}
5486

55-
#endif // #ifndef MBED_CONF_RTOS_PRESENT
87+
#endif // DEVICE_USTICKER
5688

5789
// This wait_ns is used by both RTOS and non-RTOS builds
5890

platform/mbed_wait_api_rtos.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,5 @@ void wait_ms(int ms)
6363
}
6464
}
6565

66-
/* The actual time delay may be 1 less usec */
67-
void wait_us(int us)
68-
{
69-
if (us > 10000) {
70-
MBED_WARNING(MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_UNKNOWN),
71-
"wait_us blocks deep sleep, wait_ms recommended for long delays\n");
72-
}
73-
const ticker_data_t *const ticker = get_us_ticker_data();
74-
uint32_t start = ticker_read(ticker);
75-
while ((ticker_read(ticker) - start) < (uint32_t)us);
76-
}
77-
7866
#endif // #if MBED_CONF_RTOS_PRESENT
7967

rtos/TARGET_CORTEX/mbed_boot.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include <stdlib.h>
5959

6060
#include "cmsis.h"
61+
#include "hal/us_ticker_api.h"
6162
#include "mbed_toolchain.h"
6263
#include "mbed_boot.h"
6364
#include "mbed_error.h"
@@ -75,6 +76,9 @@ void mbed_init(void)
7576
mbed_mpu_manager_init();
7677
mbed_cpy_nvic();
7778
mbed_sdk_init();
79+
#if DEVICE_USTICKER && MBED_CONF_TARGET_INIT_US_TICKER_AT_BOOT
80+
us_ticker_init();
81+
#endif
7882
mbed_rtos_init();
7983
}
8084

targets/targets.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@
4646
"tickless-from-us-ticker": {
4747
"help": "Run tickless from the microsecond ticker rather than the low power ticker. Running tickless off of the microsecond ticker improves interrupt latency on targets which use lpticker_delay_ticks",
4848
"value": false
49+
},
50+
"init-us-ticker-at-boot": {
51+
"help": "Initialize the microsecond ticker at boot rather than on first use, and leave it initialized. This speeds up wait_us in particular.",
52+
"value": false
4953
}
5054
}
5155
},
@@ -1880,7 +1884,8 @@
18801884
},
18811885
"overrides": {
18821886
"deep-sleep-latency": 3,
1883-
"tickless-from-us-ticker": true
1887+
"tickless-from-us-ticker": true,
1888+
"init-us-ticker-at-boot": true
18841889
},
18851890
"device_has": [
18861891
"USTICKER",

0 commit comments

Comments
 (0)