mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-12-10 18:24:11 +01:00
442 lines
11 KiB
C
442 lines
11 KiB
C
|
/**
|
||
|
******************************************************************************
|
||
|
* @file pios_thread.c
|
||
|
* @author Tau Labs, http://taulabs.org, Copyright (C) 2014
|
||
|
* @addtogroup PIOS PIOS Core hardware abstraction layer
|
||
|
* @{
|
||
|
* @addtogroup PIOS_Thread Thread Abstraction
|
||
|
* @{
|
||
|
* @brief Abstracts the concept of a thread to hide different implementations
|
||
|
*****************************************************************************/
|
||
|
/*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful, but
|
||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||
|
* for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License along
|
||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
*/
|
||
|
|
||
|
#include "pios.h"
|
||
|
#include "pios_thread.h"
|
||
|
|
||
|
#if !defined(PIOS_INCLUDE_FREERTOS) && !defined(PIOS_INCLUDE_CHIBIOS)
|
||
|
#error "pios_thread.c requires PIOS_INCLUDE_FREERTOS or PIOS_INCLUDE_CHIBIOS"
|
||
|
#endif
|
||
|
|
||
|
#if defined(PIOS_INCLUDE_FREERTOS)
|
||
|
|
||
|
#include "FreeRTOS.h"
|
||
|
#include "task.h"
|
||
|
#include "queue.h"
|
||
|
#include "semphr.h"
|
||
|
|
||
|
// portTICK_RATE_MS is in [ms/tick].
|
||
|
// See http://sourceforge.net/tracker/?func=detail&aid=3498382&group_id=111543&atid=659636
|
||
|
#define TICKS2MS(t) ((t) * (portTICK_RATE_MS))
|
||
|
#define MS2TICKS(m) ((m) / (portTICK_RATE_MS))
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Creates a thread.
|
||
|
*
|
||
|
* @param[in] fp pointer to thread function
|
||
|
* @param[in] namep pointer to thread name
|
||
|
* @param[in] stack_bytes stack size in bytes
|
||
|
* @param[in] argp pointer to argument which will be passed to thread function
|
||
|
* @param[in] prio thread priority
|
||
|
*
|
||
|
* @returns instance of @p struct pios_thread or NULL on failure
|
||
|
*
|
||
|
*/
|
||
|
struct pios_thread *PIOS_Thread_Create(void (*fp)(void *), const char *namep, size_t stack_bytes, void *argp, enum pios_thread_prio_e prio)
|
||
|
{
|
||
|
struct pios_thread *thread = pios_malloc(sizeof(struct pios_thread));
|
||
|
|
||
|
if (thread == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
thread->task_handle = (uintptr_t)NULL;
|
||
|
|
||
|
if (xTaskCreate(fp, (const char *)namep, stack_bytes / 4, argp, prio, (xTaskHandle *)&thread->task_handle) != pdPASS) {
|
||
|
pios_free(thread);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return thread;
|
||
|
}
|
||
|
|
||
|
#if (INCLUDE_vTaskDelete == 1)
|
||
|
/**
|
||
|
*
|
||
|
* @brief Destroys an instance of @p struct pios_thread.
|
||
|
*
|
||
|
* @param[in] threadp pointer to instance of @p struct pios_thread
|
||
|
*
|
||
|
*/
|
||
|
void PIOS_Thread_Delete(struct pios_thread *threadp)
|
||
|
{
|
||
|
if (threadp == NULL) {
|
||
|
vTaskDelete(NULL);
|
||
|
} else {
|
||
|
vTaskDelete((xTaskHandle)threadp->task_handle);
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
#error "PIOS_Thread_Delete requires INCLUDE_vTaskDelete to be defined 1"
|
||
|
#endif /* (INCLUDE_vTaskDelete == 1) */
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Returns the current system time.
|
||
|
*
|
||
|
* @returns current system time
|
||
|
*
|
||
|
*/
|
||
|
uint32_t PIOS_Thread_Systime(void)
|
||
|
{
|
||
|
return (uint32_t)TICKS2MS(xTaskGetTickCount());
|
||
|
}
|
||
|
|
||
|
#if (INCLUDE_vTaskDelay == 1)
|
||
|
/**
|
||
|
*
|
||
|
* @brief Suspends execution of current thread at least for specified time.
|
||
|
*
|
||
|
* @param[in] time_ms time in milliseconds to suspend thread execution
|
||
|
*
|
||
|
*/
|
||
|
void PIOS_Thread_Sleep(uint32_t time_ms)
|
||
|
{
|
||
|
if (time_ms == PIOS_THREAD_TIMEOUT_MAX) {
|
||
|
vTaskDelay(portMAX_DELAY);
|
||
|
} else {
|
||
|
vTaskDelay((portTickType)MS2TICKS(time_ms));
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
#error "PIOS_Thread_Sleep requires INCLUDE_vTaskDelay to be defined 1"
|
||
|
#endif /* (INCLUDE_vTaskDelay == 1) */
|
||
|
|
||
|
#if (INCLUDE_vTaskDelayUntil == 1)
|
||
|
/**
|
||
|
*
|
||
|
* @brief Suspends execution of current thread for a regular interval.
|
||
|
*
|
||
|
* @param[in] previous_ms pointer to system time of last execution,
|
||
|
* must have been initialized with PIOS_Thread_Systime() on first invocation
|
||
|
* @param[in] increment_ms time of regular interval in milliseconds
|
||
|
*
|
||
|
*/
|
||
|
void PIOS_Thread_Sleep_Until(uint32_t *previous_ms, uint32_t increment_ms)
|
||
|
{
|
||
|
portTickType temp = MS2TICKS(*previous_ms);
|
||
|
|
||
|
vTaskDelayUntil(&temp, (portTickType)MS2TICKS(increment_ms));
|
||
|
*previous_ms = TICKS2MS(temp);
|
||
|
}
|
||
|
#else
|
||
|
#error "PIOS_Thread_Sleep requires INCLUDE_vTaskDelayUntil to be defined 1"
|
||
|
#endif /* (INCLUDE_vTaskDelayUntil == 1) */
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Returns stack usage of a thread.
|
||
|
*
|
||
|
* @param[in] threadp pointer to instance of @p struct pios_thread
|
||
|
*
|
||
|
* @return stack usage in bytes
|
||
|
*
|
||
|
*/
|
||
|
uint32_t PIOS_Thread_Get_Stack_Usage(struct pios_thread *threadp)
|
||
|
{
|
||
|
#if (INCLUDE_uxTaskGetStackHighWaterMark == 1)
|
||
|
/* @note: This will fail when FreeRTOS TCB structure changes. */
|
||
|
return uxTaskGetStackHighWaterMark((xTaskHandle)threadp->task_handle) * 4;
|
||
|
|
||
|
#else
|
||
|
return 1024;
|
||
|
|
||
|
#endif /* (INCLUDE_uxTaskGetStackHighWaterMark == 1) */
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Returns runtime of a thread.
|
||
|
*
|
||
|
* @param[in] threadp pointer to instance of @p struct pios_thread
|
||
|
*
|
||
|
* @return runtime in milliseconds
|
||
|
*
|
||
|
*/
|
||
|
uint32_t PIOS_Thread_Get_Runtime(struct pios_thread *threadp)
|
||
|
{
|
||
|
#if (INCLUDE_uxTaskGetRunTime == 1)
|
||
|
return uxTaskGetRunTime((xTaskHandle)threadp->task_handle);
|
||
|
|
||
|
#else
|
||
|
(void)threadp;
|
||
|
return 0;
|
||
|
|
||
|
#endif /* (INCLUDE_uxTaskGetRunTime == 1) */
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Suspends execution of all threads.
|
||
|
*
|
||
|
*/
|
||
|
void PIOS_Thread_Scheduler_Suspend(void)
|
||
|
{
|
||
|
vTaskSuspendAll();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Resumes execution of all threads.
|
||
|
*
|
||
|
*/
|
||
|
void PIOS_Thread_Scheduler_Resume(void)
|
||
|
{
|
||
|
xTaskResumeAll();
|
||
|
}
|
||
|
|
||
|
#elif defined(PIOS_INCLUDE_CHIBIOS)
|
||
|
|
||
|
#define ST2MS(n) (((((n) - 1UL) * 1000UL) / CH_FREQUENCY) + 1UL)
|
||
|
|
||
|
/**
|
||
|
* Compute size that is at rounded up to the nearest
|
||
|
* multiple of 8
|
||
|
*/
|
||
|
static uint32_t ceil_size(uint32_t size)
|
||
|
{
|
||
|
const uint32_t a = sizeof(stkalign_t);
|
||
|
|
||
|
size = size + (a - size % a);
|
||
|
return size;
|
||
|
}
|
||
|
/**
|
||
|
* ChibiOS stack expects alignment (both start and end)
|
||
|
* to 8 byte boundaries. This makes sure to allocate enough
|
||
|
* memory and return an address that has the requested size
|
||
|
* or more with these constraints.
|
||
|
*/
|
||
|
static uint8_t *align8_alloc(uint32_t size)
|
||
|
{
|
||
|
// round size up to at nearest multiple of 8 + 4 bytes to guarantee
|
||
|
// sufficient size within. This is because PIOS_malloc only guarantees
|
||
|
// uintptr_t alignment which is 4 bytes.
|
||
|
size = size + sizeof(uintptr_t);
|
||
|
uint8_t *wap = PIOS_malloc(size);
|
||
|
|
||
|
// shift start point to nearest 8 byte boundary.
|
||
|
uint32_t pad = ((uint32_t)wap) % sizeof(stkalign_t);
|
||
|
wap = wap + pad;
|
||
|
|
||
|
return wap;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Creates a thread.
|
||
|
*
|
||
|
* @param[in] fp pointer to thread function
|
||
|
* @param[in] namep pointer to thread name
|
||
|
* @param[in] stack_bytes stack size in bytes
|
||
|
* @param[in] argp pointer to argument which will be passed to thread function
|
||
|
* @param[in] prio thread priority
|
||
|
*
|
||
|
* @returns instance of @p struct pios_thread or NULL on failure
|
||
|
*
|
||
|
*/
|
||
|
struct pios_thread *PIOS_Thread_Create(void (*fp)(void *), const char *namep, size_t stack_bytes, void *argp, enum pios_thread_prio_e prio)
|
||
|
{
|
||
|
struct pios_thread *thread = PIOS_malloc_no_dma(sizeof(struct pios_thread));
|
||
|
|
||
|
if (thread == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
#ifdef SIM_POSIX
|
||
|
if (stack_bytes < PIOS_THREAD_STACK_SIZE_MIN) {
|
||
|
stack_bytes = PIOS_THREAD_STACK_SIZE_MIN;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Use special functions to ensure ChibiOS stack requirements
|
||
|
stack_bytes = ceil_size(stack_bytes);
|
||
|
uint8_t *wap = align8_alloc(stack_bytes);
|
||
|
if (wap == NULL) {
|
||
|
PIOS_free(thread);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
thread->threadp = chThdCreateStatic(wap, stack_bytes, prio, (msg_t (*)(void *))fp, argp);
|
||
|
if (thread->threadp == NULL) {
|
||
|
PIOS_free(thread);
|
||
|
PIOS_free(wap);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
#if CH_USE_REGISTRY
|
||
|
thread->threadp->p_name = namep;
|
||
|
#endif /* CH_USE_REGISTRY */
|
||
|
|
||
|
return thread;
|
||
|
}
|
||
|
|
||
|
#if (CH_USE_WAITEXIT == TRUE)
|
||
|
/**
|
||
|
*
|
||
|
* @brief Destroys an instance of @p struct pios_thread.
|
||
|
*
|
||
|
* @param[in] threadp pointer to instance of @p struct pios_thread
|
||
|
*
|
||
|
*/
|
||
|
void PIOS_Thread_Delete(struct pios_thread *threadp)
|
||
|
{
|
||
|
if (threadp == NULL) {
|
||
|
chThdExit(0);
|
||
|
} else {
|
||
|
chThdTerminate(threadp->threadp);
|
||
|
chThdWait(threadp->threadp);
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
#error "PIOS_Thread_Delete requires CH_USE_WAITEXIT to be defined TRUE"
|
||
|
#endif /* (CH_USE_WAITEXIT == TRUE) */
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Returns the current system time.
|
||
|
*
|
||
|
* @returns current system time
|
||
|
*
|
||
|
*/
|
||
|
uint32_t PIOS_Thread_Systime(void)
|
||
|
{
|
||
|
return (uint32_t)ST2MS(chTimeNow());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Suspends execution of current thread at least for specified time.
|
||
|
*
|
||
|
* @param[in] time_ms time in milliseconds to suspend thread execution
|
||
|
*
|
||
|
*/
|
||
|
void PIOS_Thread_Sleep(uint32_t time_ms)
|
||
|
{
|
||
|
if (time_ms == PIOS_THREAD_TIMEOUT_MAX) {
|
||
|
chThdSleep(TIME_INFINITE);
|
||
|
} else {
|
||
|
chThdSleep(MS2ST(time_ms));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Suspends execution of current thread for a regular interval.
|
||
|
*
|
||
|
* @param[in] previous_ms pointer to system time of last execution,
|
||
|
* must have been initialized with PIOS_Thread_Systime() on first invocation
|
||
|
* @param[in] increment_ms time of regular interval in milliseconds
|
||
|
*
|
||
|
*/
|
||
|
void PIOS_Thread_Sleep_Until(uint32_t *previous_ms, uint32_t increment_ms)
|
||
|
{
|
||
|
systime_t future = MS2ST(*previous_ms) + MS2ST(increment_ms);
|
||
|
|
||
|
chSysLock();
|
||
|
systime_t now = chTimeNow();
|
||
|
int mustDelay =
|
||
|
now < MS2ST(*previous_ms) ?
|
||
|
(now < future && future < MS2ST(*previous_ms)) :
|
||
|
(now < future || future < MS2ST(*previous_ms));
|
||
|
if (mustDelay) {
|
||
|
chThdSleepS(future - now);
|
||
|
}
|
||
|
chSysUnlock();
|
||
|
*previous_ms = ST2MS(future);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Returns stack usage of a thread.
|
||
|
*
|
||
|
* @param[in] threadp pointer to instance of @p struct pios_thread
|
||
|
*
|
||
|
* @return stack usage in bytes
|
||
|
*/
|
||
|
uint32_t PIOS_Thread_Get_Stack_Usage(struct pios_thread *threadp)
|
||
|
{
|
||
|
#if CH_DBG_FILL_THREADS
|
||
|
uint32_t *stack = (uint32_t *)((size_t)threadp->threadp + sizeof(*threadp->threadp));
|
||
|
uint32_t *stklimit = stack;
|
||
|
while (*stack ==
|
||
|
((CH_STACK_FILL_VALUE << 24) |
|
||
|
(CH_STACK_FILL_VALUE << 16) |
|
||
|
(CH_STACK_FILL_VALUE << 8) |
|
||
|
(CH_STACK_FILL_VALUE << 0))) {
|
||
|
++stack;
|
||
|
}
|
||
|
return (stack - stklimit) * 4;
|
||
|
|
||
|
#else
|
||
|
return 0;
|
||
|
|
||
|
#endif /* CH_DBG_FILL_THREADS */
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Returns runtime of a thread.
|
||
|
*
|
||
|
* @param[in] threadp pointer to instance of @p struct pios_thread
|
||
|
*
|
||
|
* @return runtime in milliseconds
|
||
|
*
|
||
|
*/
|
||
|
uint32_t PIOS_Thread_Get_Runtime(struct pios_thread *threadp)
|
||
|
{
|
||
|
chSysLock();
|
||
|
|
||
|
uint32_t result = threadp->threadp->ticks_total;
|
||
|
threadp->threadp->ticks_total = 0;
|
||
|
|
||
|
chSysUnlock();
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Suspends execution of all threads.
|
||
|
*
|
||
|
*/
|
||
|
void PIOS_Thread_Scheduler_Suspend(void)
|
||
|
{
|
||
|
chSysLock();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @brief Resumes execution of all threads.
|
||
|
*
|
||
|
*/
|
||
|
void PIOS_Thread_Scheduler_Resume(void)
|
||
|
{
|
||
|
chSysUnlock();
|
||
|
}
|
||
|
|
||
|
#endif /* defined(PIOS_INCLUDE_CHIBIOS) */
|