mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #6534 from c1728p9/rtos_suspend
Update idle loop to reduce calls to suspendpull/6715/merge
commit
675528b6c0
|
|
@ -25,6 +25,7 @@
|
||||||
#include "greentea-client/test_env.h"
|
#include "greentea-client/test_env.h"
|
||||||
#include "unity.h"
|
#include "unity.h"
|
||||||
#include "utest.h"
|
#include "utest.h"
|
||||||
|
#include "ticker_api.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "rtx_lib.h"
|
#include "rtx_lib.h"
|
||||||
|
|
@ -44,7 +45,9 @@ private:
|
||||||
Semaphore _sem;
|
Semaphore _sem;
|
||||||
virtual void handler()
|
virtual void handler()
|
||||||
{
|
{
|
||||||
increment_tick();
|
core_util_critical_section_enter();
|
||||||
|
_increment_tick();
|
||||||
|
core_util_critical_section_exit();
|
||||||
_sem.release();
|
_sem.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,6 +57,11 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SysTimerTest(const ticker_data_t *data) :
|
||||||
|
SysTimer(data), _sem(0, 1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual ~SysTimerTest()
|
virtual ~SysTimerTest()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -64,6 +72,65 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
timestamp_t mock_ticker_timestamp;
|
||||||
|
|
||||||
|
void mock_ticker_init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t mock_ticker_read()
|
||||||
|
{
|
||||||
|
return mock_ticker_timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mock_ticker_disable_interrupt()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void mock_ticker_clear_interrupt()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void mock_ticker_set_interrupt(timestamp_t timestamp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void mock_ticker_fire_interrupt()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const ticker_info_t *mock_ticker_get_info()
|
||||||
|
{
|
||||||
|
static const ticker_info_t mock_ticker_info = {
|
||||||
|
.frequency = 1000000,
|
||||||
|
.bits = 32
|
||||||
|
};
|
||||||
|
return &mock_ticker_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker_interface_t mock_ticker_interface = {
|
||||||
|
.init = mock_ticker_init,
|
||||||
|
.read = mock_ticker_read,
|
||||||
|
.disable_interrupt = mock_ticker_disable_interrupt,
|
||||||
|
.clear_interrupt = mock_ticker_clear_interrupt,
|
||||||
|
.set_interrupt = mock_ticker_set_interrupt,
|
||||||
|
.fire_interrupt = mock_ticker_fire_interrupt,
|
||||||
|
.get_info = mock_ticker_get_info,
|
||||||
|
};
|
||||||
|
|
||||||
|
ticker_event_queue_t mock_ticker_event_queue;
|
||||||
|
|
||||||
|
const ticker_data_t mock_ticker_data = {
|
||||||
|
.interface = &mock_ticker_interface,
|
||||||
|
.queue = &mock_ticker_event_queue
|
||||||
|
};
|
||||||
|
|
||||||
|
void mock_ticker_reset()
|
||||||
|
{
|
||||||
|
mock_ticker_timestamp = 0;
|
||||||
|
memset(&mock_ticker_event_queue, 0, sizeof mock_ticker_event_queue);
|
||||||
|
}
|
||||||
|
|
||||||
/** Test tick count is zero upon creation
|
/** Test tick count is zero upon creation
|
||||||
*
|
*
|
||||||
* Given a SysTimer
|
* Given a SysTimer
|
||||||
|
|
@ -79,26 +146,29 @@ void test_created_with_zero_tick_count(void)
|
||||||
/** Test tick count is updated correctly
|
/** Test tick count is updated correctly
|
||||||
*
|
*
|
||||||
* Given a SysTimer
|
* Given a SysTimer
|
||||||
* When @a update_tick method is called immediately after creation
|
* When the @a suspend and @a resume methods are called immediately after creation
|
||||||
* Then the tick count is not updated
|
* Then the tick count is not updated
|
||||||
* When @a update_tick is called again after a delay
|
* When @a suspend and @a resume methods are called again after a delay
|
||||||
* Then the tick count is updated
|
* Then the tick count is updated
|
||||||
* and the number of ticks incremented is equal TEST_TICKS - 1
|
* and the number of ticks incremented is equal TEST_TICKS - 1
|
||||||
* When @a update_tick is called again without a delay
|
* When @a suspend and @a resume methods are called again without a delay
|
||||||
* Then the tick count is not updated
|
* Then the tick count is not updated
|
||||||
*/
|
*/
|
||||||
void test_update_tick(void)
|
void test_update_tick(void)
|
||||||
{
|
{
|
||||||
SysTimerTest st;
|
mock_ticker_reset();
|
||||||
TEST_ASSERT_EQUAL_UINT32(0, st.update_tick());
|
SysTimerTest st(&mock_ticker_data);
|
||||||
|
st.suspend(TEST_TICKS * 2);
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(0, st.resume());
|
||||||
TEST_ASSERT_EQUAL_UINT32(0, st.get_tick());
|
TEST_ASSERT_EQUAL_UINT32(0, st.get_tick());
|
||||||
us_timestamp_t test_ticks_elapsed_ts = st.get_time() + DELAY_US;
|
|
||||||
|
|
||||||
while (st.get_time() <= test_ticks_elapsed_ts) {}
|
st.suspend(TEST_TICKS * 2);
|
||||||
TEST_ASSERT_EQUAL_UINT32(TEST_TICKS - 1, st.update_tick());
|
mock_ticker_timestamp = DELAY_US;
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(TEST_TICKS - 1, st.resume());
|
||||||
TEST_ASSERT_EQUAL_UINT32(TEST_TICKS - 1, st.get_tick());
|
TEST_ASSERT_EQUAL_UINT32(TEST_TICKS - 1, st.get_tick());
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL_UINT32(0, st.update_tick());
|
st.suspend(TEST_TICKS * 2);
|
||||||
|
TEST_ASSERT_EQUAL_UINT32(0, st.resume());
|
||||||
TEST_ASSERT_EQUAL_UINT32(TEST_TICKS - 1, st.get_tick());
|
TEST_ASSERT_EQUAL_UINT32(TEST_TICKS - 1, st.get_tick());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,12 +180,13 @@ void test_update_tick(void)
|
||||||
*/
|
*/
|
||||||
void test_get_time(void)
|
void test_get_time(void)
|
||||||
{
|
{
|
||||||
SysTimerTest st;
|
mock_ticker_reset();
|
||||||
|
SysTimerTest st(&mock_ticker_data);
|
||||||
us_timestamp_t t1 = st.get_time();
|
us_timestamp_t t1 = st.get_time();
|
||||||
|
|
||||||
wait_us(DELAY_US);
|
mock_ticker_timestamp = DELAY_US;
|
||||||
us_timestamp_t t2 = st.get_time();
|
us_timestamp_t t2 = st.get_time();
|
||||||
TEST_ASSERT_UINT64_WITHIN(DELAY_DELTA_US, DELAY_US, t2 - t1);
|
TEST_ASSERT_EQUAL_UINT64(DELAY_US, t2 - t1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Test cancel_tick
|
/** Test cancel_tick
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
#if DEVICE_LOWPOWERTIMER
|
#if DEVICE_LOWPOWERTIMER
|
||||||
|
|
||||||
#include "hal/lp_ticker_api.h"
|
#include "hal/lp_ticker_api.h"
|
||||||
|
#include "mbed_critical.h"
|
||||||
#include "rtx_core_cm.h"
|
#include "rtx_core_cm.h"
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "rtx_lib.h"
|
#include "rtx_lib.h"
|
||||||
|
|
@ -43,6 +44,14 @@ namespace internal {
|
||||||
|
|
||||||
SysTimer::SysTimer() :
|
SysTimer::SysTimer() :
|
||||||
TimerEvent(get_lp_ticker_data()), _start_time(0), _tick(0)
|
TimerEvent(get_lp_ticker_data()), _start_time(0), _tick(0)
|
||||||
|
{
|
||||||
|
_start_time = ticker_read_us(_ticker_data);
|
||||||
|
_suspend_time_passed = true;
|
||||||
|
_suspended = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SysTimer::SysTimer(const ticker_data_t *data) :
|
||||||
|
TimerEvent(data), _start_time(0), _tick(0)
|
||||||
{
|
{
|
||||||
_start_time = ticker_read_us(_ticker_data);
|
_start_time = ticker_read_us(_ticker_data);
|
||||||
}
|
}
|
||||||
|
|
@ -61,23 +70,30 @@ void SysTimer::setup_irq()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysTimer::schedule_tick(uint32_t delta)
|
void SysTimer::suspend(uint32_t ticks)
|
||||||
{
|
{
|
||||||
insert_absolute(_start_time + (_tick + delta) * 1000000ULL / OS_TICK_FREQ);
|
core_util_critical_section_enter();
|
||||||
|
|
||||||
|
schedule_tick(ticks);
|
||||||
|
_suspend_time_passed = false;
|
||||||
|
_suspended = true;
|
||||||
|
|
||||||
|
core_util_critical_section_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysTimer::cancel_tick()
|
bool SysTimer::suspend_time_passed()
|
||||||
{
|
{
|
||||||
|
return _suspend_time_passed;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SysTimer::resume()
|
||||||
|
{
|
||||||
|
core_util_critical_section_enter();
|
||||||
|
|
||||||
|
_suspended = false;
|
||||||
|
_suspend_time_passed = true;
|
||||||
remove();
|
remove();
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t SysTimer::get_tick()
|
|
||||||
{
|
|
||||||
return _tick & 0xFFFFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t SysTimer::update_tick()
|
|
||||||
{
|
|
||||||
uint64_t new_tick = (ticker_read_us(_ticker_data) - _start_time) * OS_TICK_FREQ / 1000000;
|
uint64_t new_tick = (ticker_read_us(_ticker_data) - _start_time) * OS_TICK_FREQ / 1000000;
|
||||||
if (new_tick > _tick) {
|
if (new_tick > _tick) {
|
||||||
// Don't update to the current tick. Instead, update to the
|
// Don't update to the current tick. Instead, update to the
|
||||||
|
|
@ -88,9 +104,34 @@ uint32_t SysTimer::update_tick()
|
||||||
}
|
}
|
||||||
uint32_t elapsed_ticks = new_tick - _tick;
|
uint32_t elapsed_ticks = new_tick - _tick;
|
||||||
_tick = new_tick;
|
_tick = new_tick;
|
||||||
|
|
||||||
|
core_util_critical_section_exit();
|
||||||
return elapsed_ticks;
|
return elapsed_ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SysTimer::schedule_tick(uint32_t delta)
|
||||||
|
{
|
||||||
|
core_util_critical_section_enter();
|
||||||
|
|
||||||
|
insert_absolute(_start_time + (_tick + delta) * 1000000ULL / OS_TICK_FREQ);
|
||||||
|
|
||||||
|
core_util_critical_section_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SysTimer::cancel_tick()
|
||||||
|
{
|
||||||
|
core_util_critical_section_enter();
|
||||||
|
|
||||||
|
remove();
|
||||||
|
|
||||||
|
core_util_critical_section_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SysTimer::get_tick()
|
||||||
|
{
|
||||||
|
return _tick & 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
us_timestamp_t SysTimer::get_time()
|
us_timestamp_t SysTimer::get_time()
|
||||||
{
|
{
|
||||||
return ticker_read_us(_ticker_data);
|
return ticker_read_us(_ticker_data);
|
||||||
|
|
@ -100,8 +141,10 @@ SysTimer::~SysTimer()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysTimer::set_irq_pending()
|
void SysTimer::_set_irq_pending()
|
||||||
{
|
{
|
||||||
|
// Protected function synchronized externally
|
||||||
|
|
||||||
#if (defined(NO_SYSTICK))
|
#if (defined(NO_SYSTICK))
|
||||||
NVIC_SetPendingIRQ(mbed_get_m0_tick_irqn());
|
NVIC_SetPendingIRQ(mbed_get_m0_tick_irqn());
|
||||||
#else
|
#else
|
||||||
|
|
@ -109,15 +152,25 @@ void SysTimer::set_irq_pending()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysTimer::increment_tick()
|
void SysTimer::_increment_tick()
|
||||||
{
|
{
|
||||||
|
// Protected function synchronized externally
|
||||||
|
|
||||||
_tick++;
|
_tick++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysTimer::handler()
|
void SysTimer::handler()
|
||||||
{
|
{
|
||||||
set_irq_pending();
|
core_util_critical_section_enter();
|
||||||
increment_tick();
|
|
||||||
|
if (_suspended) {
|
||||||
|
_suspend_time_passed = true;
|
||||||
|
} else {
|
||||||
|
_set_irq_pending();
|
||||||
|
_increment_tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
core_util_critical_section_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ class SysTimer: private mbed::TimerEvent, private mbed::NonCopyable<SysTimer> {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
SysTimer();
|
SysTimer();
|
||||||
|
SysTimer(const ticker_data_t *data);
|
||||||
virtual ~SysTimer();
|
virtual ~SysTimer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -56,6 +57,34 @@ public:
|
||||||
*/
|
*/
|
||||||
static void setup_irq();
|
static void setup_irq();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set wakeup time and schedule a wakeup event after delta ticks
|
||||||
|
*
|
||||||
|
* After suspend has been called the function suspend_time_passed
|
||||||
|
* can be used to determine if the suspend time has passed.
|
||||||
|
*
|
||||||
|
* @param delta Ticks to remain suspended
|
||||||
|
*/
|
||||||
|
void suspend(uint32_t delta);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the suspend time has passed
|
||||||
|
*
|
||||||
|
* @return true if the specified number of ticks has passed otherwise false
|
||||||
|
*/
|
||||||
|
bool suspend_time_passed();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exit suspend mode and return elapsed ticks
|
||||||
|
*
|
||||||
|
* Due to a scheduling issue, the number of ticks returned is decremented
|
||||||
|
* by 1 so that a handler can be called and update to the current value.
|
||||||
|
* This allows scheduling restart successfully after the OS is resumed.
|
||||||
|
*
|
||||||
|
* @return the number of elapsed ticks minus 1
|
||||||
|
*/
|
||||||
|
uint32_t resume();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedule an os tick to fire
|
* Schedule an os tick to fire
|
||||||
*
|
*
|
||||||
|
|
@ -77,17 +106,6 @@ public:
|
||||||
*/
|
*/
|
||||||
uint32_t get_tick();
|
uint32_t get_tick();
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the internal tick count
|
|
||||||
*
|
|
||||||
* @return The number of ticks incremented
|
|
||||||
*
|
|
||||||
* @note Due to a scheduling issue, the number of ticks returned is decremented
|
|
||||||
* by 1 so that a handler can be called and update to the current value.
|
|
||||||
* This allows scheduling restart successfully after the OS is resumed.
|
|
||||||
*/
|
|
||||||
uint32_t update_tick();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the time
|
* Get the time
|
||||||
*
|
*
|
||||||
|
|
@ -97,10 +115,12 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void handler();
|
virtual void handler();
|
||||||
void increment_tick();
|
void _increment_tick();
|
||||||
static void set_irq_pending();
|
static void _set_irq_pending();
|
||||||
us_timestamp_t _start_time;
|
us_timestamp_t _start_time;
|
||||||
uint64_t _tick;
|
uint64_t _tick;
|
||||||
|
bool _suspend_time_passed;
|
||||||
|
bool _suspended;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -95,21 +95,24 @@ uint32_t OS_Tick_GetInterval (void) {
|
||||||
|
|
||||||
static void default_idle_hook(void)
|
static void default_idle_hook(void)
|
||||||
{
|
{
|
||||||
uint32_t elapsed_ticks = 0;
|
uint32_t ticks_to_sleep = osKernelSuspend();
|
||||||
|
os_timer->suspend(ticks_to_sleep);
|
||||||
|
|
||||||
core_util_critical_section_enter();
|
bool event_pending = false;
|
||||||
uint32_t ticks_to_sleep = svcRtxKernelSuspend();
|
while (!os_timer->suspend_time_passed() && !event_pending) {
|
||||||
if (ticks_to_sleep) {
|
|
||||||
os_timer->schedule_tick(ticks_to_sleep);
|
|
||||||
|
|
||||||
sleep();
|
core_util_critical_section_enter();
|
||||||
|
if (osRtxInfo.kernel.pendSV) {
|
||||||
|
event_pending = true;
|
||||||
|
} else {
|
||||||
|
sleep();
|
||||||
|
}
|
||||||
|
core_util_critical_section_exit();
|
||||||
|
|
||||||
os_timer->cancel_tick();
|
// Ensure interrupts get a chance to fire
|
||||||
// calculate how long we slept
|
__ISB();
|
||||||
elapsed_ticks = os_timer->update_tick();
|
|
||||||
}
|
}
|
||||||
svcRtxKernelResume(elapsed_ticks);
|
osKernelResume(os_timer->resume());
|
||||||
core_util_critical_section_exit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(FEATURE_UVISOR)
|
#elif defined(FEATURE_UVISOR)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue