mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #5419 from kjbracey-arm/kernel_ticks
Add absolute millisecond tick count to RTOS classespull/6026/head
commit
3d815de0fc
|
@ -20,6 +20,7 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
#include "rtos/ConditionVariable.h"
|
||||
#include "rtos/Kernel.h"
|
||||
#include "rtos/Thread.h"
|
||||
|
||||
#include "mbed_error.h"
|
||||
|
@ -27,7 +28,6 @@
|
|||
|
||||
namespace rtos {
|
||||
|
||||
|
||||
ConditionVariable::Waiter::Waiter(): sem(0), prev(NULL), next(NULL), in_list(false)
|
||||
{
|
||||
// No initialization to do
|
||||
|
@ -64,6 +64,24 @@ bool ConditionVariable::wait_for(uint32_t millisec)
|
|||
return timeout;
|
||||
}
|
||||
|
||||
bool ConditionVariable::wait_until(uint64_t millisec)
|
||||
{
|
||||
uint64_t now = Kernel::get_ms_count();
|
||||
|
||||
if (now >= millisec) {
|
||||
// Time has already passed - standard behaviour is to
|
||||
// treat as a "try".
|
||||
return wait_for(0);
|
||||
} else if (millisec - now >= osWaitForever) {
|
||||
// Exceeds maximum delay of underlying wait_for -
|
||||
// spuriously wake after 49 days, indicating no timeout.
|
||||
wait_for(osWaitForever - 1);
|
||||
return false;
|
||||
} else {
|
||||
return wait_for(millisec - now);
|
||||
}
|
||||
}
|
||||
|
||||
void ConditionVariable::notify_one()
|
||||
{
|
||||
MBED_ASSERT(_mutex.get_owner() == Thread::gettid());
|
||||
|
|
|
@ -120,7 +120,7 @@ class ConditionVariable : private mbed::NonCopyable<ConditionVariable> {
|
|||
public:
|
||||
/** Create and Initialize a ConditionVariable object
|
||||
*
|
||||
* @note You may call this function from ISR context.
|
||||
* @note You cannot call this function from ISR context.
|
||||
*/
|
||||
ConditionVariable(Mutex &mutex);
|
||||
|
||||
|
@ -150,6 +150,39 @@ public:
|
|||
*/
|
||||
void wait();
|
||||
|
||||
/** Wait for a notification until specified time
|
||||
*
|
||||
* @param millisec absolute end time referenced to Kernel::get_ms_count()
|
||||
* @return true if a timeout occurred, false otherwise.
|
||||
*
|
||||
* @note - The thread calling this function must be the owner of the
|
||||
* ConditionVariable's mutex and it must be locked exactly once
|
||||
* @note - Spurious notifications can occur so the caller of this API
|
||||
* should check to make sure the condition they are waiting on has
|
||||
* been met
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* mutex.lock();
|
||||
* uint64_t end_time = Kernel::get_ms_count() + COND_WAIT_TIMEOUT;
|
||||
*
|
||||
* while (!condition_met) {
|
||||
* if (cond.wait_until(end_time)) {
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* if (condition_met) {
|
||||
* function_to_handle_condition();
|
||||
* }
|
||||
*
|
||||
* mutex.unlock();
|
||||
* @endcode
|
||||
*
|
||||
* @note You cannot call this function from ISR context.
|
||||
*/
|
||||
bool wait_until(uint64_t millisec);
|
||||
|
||||
/** Wait for a notification or timeout
|
||||
*
|
||||
* @param millisec timeout value or osWaitForever in case of no time-out.
|
||||
|
@ -164,15 +197,12 @@ public:
|
|||
* Example:
|
||||
* @code
|
||||
* mutex.lock();
|
||||
* Timer timer;
|
||||
* timer.start();
|
||||
*
|
||||
* bool timed_out = false;
|
||||
* uint32_t time_left = TIMEOUT;
|
||||
* while (!condition_met && !timed_out) {
|
||||
* timed_out = cond.wait_for(time_left);
|
||||
* uint32_t elapsed = timer.read_ms();
|
||||
* time_left = elapsed > TIMEOUT ? 0 : TIMEOUT - elapsed;
|
||||
* while (!condition_met) {
|
||||
* cond.wait_for(MAX_SLEEP_TIME);
|
||||
* if (!condition_met) {
|
||||
* do_other_work_while_condition_false();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* if (condition_met) {
|
||||
|
@ -190,7 +220,7 @@ public:
|
|||
*
|
||||
* @note - The thread calling this function must be the owner of the ConditionVariable's mutex
|
||||
*
|
||||
* @note This function may be called from ISR context.
|
||||
* @note You cannot call this function from ISR context.
|
||||
*/
|
||||
void notify_one();
|
||||
|
||||
|
@ -198,13 +228,13 @@ public:
|
|||
*
|
||||
* @note - The thread calling this function must be the owner of the ConditionVariable's mutex
|
||||
*
|
||||
* @note This function may be called from ISR context.
|
||||
* @note You cannot call this function from ISR context.
|
||||
*/
|
||||
void notify_all();
|
||||
|
||||
/** ConditionVariable destructor
|
||||
*
|
||||
* @note You may call this function from ISR context.
|
||||
* @note You cannot call this function from ISR context.
|
||||
*/
|
||||
~ConditionVariable();
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "rtos/Kernel.h"
|
||||
|
||||
#include "mbed.h"
|
||||
|
||||
namespace rtos {
|
||||
|
||||
uint64_t Kernel::get_ms_count() {
|
||||
// CMSIS-RTOS 2.1.0 and 2.1.1 differ in the time type. We assume
|
||||
// our header at least matches the implementation, so we don't try looking
|
||||
// at the run-time version report. (There's no compile-time version report)
|
||||
|
||||
// 2.1.0 uint64_t osKernelGetTickCount(void), not documented as callable from ISR (but RTX does allow)
|
||||
// 2.1.1 uint32_t osKernelGetTickCount(void), callable from ISR
|
||||
// 2.1.x who knows? We assume could go back to uint64_t
|
||||
if (sizeof osKernelGetTickCount() == sizeof(uint64_t)) {
|
||||
return osKernelGetTickCount();
|
||||
} else /* assume 32-bit */ {
|
||||
// Based on suggestion in CMSIS-RTOS 2.1.1 docs, but with reentrancy
|
||||
// protection for the tick memory. We use critical section rather than a
|
||||
// mutex, as hopefully this method can be callable from interrupt later -
|
||||
// only thing currently preventing it is that pre CMSIS RTOS 2.1.1, it's
|
||||
// not defined as safe.
|
||||
// We assume this is called multiple times per 32-bit wrap period (49 days).
|
||||
static uint32_t tick_h, tick_l;
|
||||
|
||||
core_util_critical_section_enter();
|
||||
// The 2.1.1 API says this is legal from an ISR - we assume this means
|
||||
// it's also legal with interrupts disabled. RTX implementation kind
|
||||
// of conflates the two.
|
||||
uint32_t tick32 = osKernelGetTickCount();
|
||||
if (tick32 < tick_l) {
|
||||
tick_h++;
|
||||
}
|
||||
tick_l = tick32;
|
||||
uint64_t ret = ((uint64_t) tick_h << 32) | tick_l;
|
||||
core_util_critical_section_exit();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef KERNEL_H
|
||||
#define KERNEL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace rtos {
|
||||
/** \addtogroup rtos */
|
||||
/** @{*/
|
||||
|
||||
/** Functions in the Kernel namespace control RTOS kernel information. */
|
||||
namespace Kernel {
|
||||
|
||||
/** Read the current RTOS kernel millisecond tick count.
|
||||
The tick count corresponds to the tick count used by the RTOS for timing
|
||||
purposes. It increments monotonically from 0 at boot, hence effectively
|
||||
never wraps. If the underlying RTOS only provides a 32-bit tick count,
|
||||
this method expands it to 64 bits.
|
||||
@return RTOS kernel current tick count
|
||||
@note mbed OS always uses millisecond RTOS ticks, and this could only wrap
|
||||
after half a billion years
|
||||
@note You cannot call this function from ISR context.
|
||||
*/
|
||||
uint64_t get_ms_count();
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
} // namespace rtos
|
||||
#endif
|
||||
|
||||
/** @}*/
|
|
@ -20,6 +20,7 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
#include "rtos/Mutex.h"
|
||||
#include "rtos/Kernel.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "mbed_error.h"
|
||||
|
@ -58,11 +59,30 @@ osStatus Mutex::lock(uint32_t millisec) {
|
|||
}
|
||||
|
||||
bool Mutex::trylock() {
|
||||
if (osMutexAcquire(_id, 0) == osOK) {
|
||||
_count++;
|
||||
return trylock_for(0);
|
||||
}
|
||||
|
||||
bool Mutex::trylock_for(uint32_t millisec) {
|
||||
osStatus status = lock(millisec);
|
||||
if (status == osOK) {
|
||||
return true;
|
||||
} else {
|
||||
}
|
||||
|
||||
MBED_ASSERT(status == osErrorTimeout || status == osErrorResource);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mutex::trylock_until(uint64_t millisec) {
|
||||
uint64_t now = Kernel::get_ms_count();
|
||||
|
||||
if (now >= millisec) {
|
||||
return trylock();
|
||||
} else if (millisec - now >= osWaitForever) {
|
||||
// API permits early return
|
||||
return trylock_for(osWaitForever - 1);
|
||||
} else {
|
||||
return trylock_for(millisec - now);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
29
rtos/Mutex.h
29
rtos/Mutex.h
|
@ -78,11 +78,36 @@ public:
|
|||
|
||||
/** Try to lock the mutex, and return immediately
|
||||
@return true if the mutex was acquired, false otherwise.
|
||||
@note equivalent to trylock_for(0)
|
||||
|
||||
@note This function cannot be called from ISR context.
|
||||
@note You cannot call this function from ISR context.
|
||||
*/
|
||||
bool trylock();
|
||||
|
||||
/** Try to lock the mutex for a specified time
|
||||
@param millisec timeout value or 0 in case of no time-out.
|
||||
@return true if the mutex was acquired, false otherwise.
|
||||
@note the underlying RTOS may have a limit to the maximum wait time
|
||||
due to internal 32-bit computations, but this is guaranteed to work if the
|
||||
wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
|
||||
the lock attempt will time out earlier than specified.
|
||||
|
||||
@note You cannot call this function from ISR context.
|
||||
*/
|
||||
bool trylock_for(uint32_t millisec);
|
||||
|
||||
/** Try to lock the mutex until specified time
|
||||
@param millisec absolute timeout time, referenced to Kernel::get_ms_count()
|
||||
@return true if the mutex was acquired, false otherwise.
|
||||
@note the underlying RTOS may have a limit to the maximum wait time
|
||||
due to internal 32-bit computations, but this is guaranteed to work if the
|
||||
wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
|
||||
the lock attempt will time out earlier than specified.
|
||||
|
||||
@note You cannot call this function from ISR context.
|
||||
*/
|
||||
bool trylock_until(uint64_t millisec);
|
||||
|
||||
/** Unlock the mutex that has previously been locked by the same thread
|
||||
@return status code that indicates the execution status of the function:
|
||||
@a osOK the mutex has been released.
|
||||
|
@ -90,7 +115,7 @@ public:
|
|||
@a osErrorResource the mutex was not locked or the current thread wasn't the owner.
|
||||
@a osErrorISR this function cannot be called from the interrupt service routine.
|
||||
|
||||
@note This function cannot be called from ISR context.
|
||||
@note You cannot call this function from ISR context.
|
||||
*/
|
||||
osStatus unlock();
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
#include "rtos/Semaphore.h"
|
||||
#include "rtos/Kernel.h"
|
||||
#include "platform/mbed_assert.h"
|
||||
|
||||
#include <string.h>
|
||||
|
@ -57,6 +58,20 @@ int32_t Semaphore::wait(uint32_t millisec) {
|
|||
}
|
||||
}
|
||||
|
||||
int32_t Semaphore::wait_until(uint64_t millisec) {
|
||||
uint64_t now = Kernel::get_ms_count();
|
||||
uint32_t timeout;
|
||||
|
||||
if (now >= millisec) {
|
||||
return wait(0);
|
||||
} else if (millisec - now >= osWaitForever) {
|
||||
// API permits early return
|
||||
return wait(osWaitForever - 1);
|
||||
} else {
|
||||
return wait(millisec - now);
|
||||
}
|
||||
}
|
||||
|
||||
osStatus Semaphore::release(void) {
|
||||
return osSemaphoreRelease(_id);
|
||||
}
|
||||
|
|
|
@ -67,6 +67,18 @@ public:
|
|||
*/
|
||||
int32_t wait(uint32_t millisec=osWaitForever);
|
||||
|
||||
/** Wait until a Semaphore resource becomes available.
|
||||
@param millisec absolute timeout time, referenced to Kernel::get_ms_count()
|
||||
@return number of available tokens, before taking one; or -1 in case of incorrect parameters
|
||||
@note the underlying RTOS may have a limit to the maximum wait time
|
||||
due to internal 32-bit computations, but this is guaranteed to work if the
|
||||
wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
|
||||
the acquire attempt will time out earlier than specified.
|
||||
|
||||
@note You cannot call this function from ISR context.
|
||||
*/
|
||||
int32_t wait_until(uint64_t millisec);
|
||||
|
||||
/** Release a Semaphore resource that was obtain with Semaphore::wait.
|
||||
@return status code that indicates the execution status of the function:
|
||||
@a osOK the token has been correctly released.
|
||||
|
|
|
@ -353,6 +353,35 @@ osStatus Thread::wait(uint32_t millisec) {
|
|||
return osDelay(millisec);
|
||||
}
|
||||
|
||||
osStatus Thread::wait_until(uint64_t millisec) {
|
||||
// CMSIS-RTOS 2.1.0 and 2.1.1 differ in the time type, which we determine
|
||||
// by looking at the return type of osKernelGetTickCount. We assume
|
||||
// our header at least matches the implementation, so we don't try looking
|
||||
// at the run-time version report. (There's no compile-time version report)
|
||||
if (sizeof osKernelGetTickCount() == sizeof(uint64_t)) {
|
||||
// CMSIS-RTOS 2.1.0 has a 64-bit API. The corresponding RTX 5.2.0 can't
|
||||
// delay more than 0xfffffffe ticks, but there's no limit stated for
|
||||
// the generic API.
|
||||
return osDelayUntil(millisec);
|
||||
} else {
|
||||
// 64-bit time doesn't wrap (for half a billion years, at last)
|
||||
uint64_t now = Kernel::get_ms_count();
|
||||
// Report being late on entry
|
||||
if (now >= millisec) {
|
||||
return osErrorParameter;
|
||||
}
|
||||
// We're about to make a 32-bit delay call, so have at least this limit
|
||||
if (millisec - now > 0xFFFFFFFF) {
|
||||
return osErrorParameter;
|
||||
}
|
||||
// And this may have its own internal limit - we'll find out.
|
||||
// We hope/assume there's no problem with passing
|
||||
// osWaitForever = 0xFFFFFFFF - that value is only specified to have
|
||||
// special meaning for osSomethingWait calls.
|
||||
return osDelay(millisec - now);
|
||||
}
|
||||
}
|
||||
|
||||
osStatus Thread::yield() {
|
||||
return osThreadYield();
|
||||
}
|
||||
|
|
|
@ -360,7 +360,10 @@ public:
|
|||
*/
|
||||
static osEvent signal_wait(int32_t signals, uint32_t millisec=osWaitForever);
|
||||
|
||||
/** Wait for a specified time period in millisec:
|
||||
/** Wait for a specified time period in milliseconds
|
||||
Being tick-based, the delay will be up to the specified time - eg for
|
||||
a value of 1 the system waits until the next millisecond tick occurs,
|
||||
leading to a delay of 0-1 milliseconds.
|
||||
@param millisec time delay value
|
||||
@return status code that indicates the execution status of the function.
|
||||
|
||||
|
@ -368,6 +371,22 @@ public:
|
|||
*/
|
||||
static osStatus wait(uint32_t millisec);
|
||||
|
||||
/** Wait until a specified time in millisec
|
||||
The specified time is according to Kernel::get_ms_count().
|
||||
@param millisec absolute time in millisec
|
||||
@return status code that indicates the execution status of the function.
|
||||
@note not callable from interrupt
|
||||
@note if millisec is equal to or lower than the current tick count, this
|
||||
returns immediately, either with an error or "osOK".
|
||||
@note the underlying RTOS may have a limit to the maximum wait time
|
||||
due to internal 32-bit computations, but this is guaranteed to work if the
|
||||
delay is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
|
||||
it may return with an immediate error, or wait for the maximum delay.
|
||||
|
||||
@note You cannot call this function from ISR context.
|
||||
*/
|
||||
static osStatus wait_until(uint64_t millisec);
|
||||
|
||||
/** Pass control to next thread that is in state READY.
|
||||
@return status code that indicates the execution status of the function.
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#define RTOS_H
|
||||
|
||||
#include "mbed_rtos_storage.h"
|
||||
#include "rtos/Kernel.h"
|
||||
#include "rtos/Thread.h"
|
||||
#include "rtos/Mutex.h"
|
||||
#include "rtos/RtosTimer.h"
|
||||
|
|
Loading…
Reference in New Issue