Merge pull request 'FixedTimeslotTask FreeRTOS improvements' (#117) from KSat/fsfw:mueller_FixedTimeslotTaskImprovements into master

This commit is contained in:
Steffen Gaisser 2020-07-14 11:45:41 +02:00
commit 639e61cebf
2 changed files with 113 additions and 70 deletions

View File

@ -1,6 +1,7 @@
#include <framework/serviceinterface/ServiceInterfaceStream.h>
#include "FixedTimeslotTask.h" #include "FixedTimeslotTask.h"
#include <framework/serviceinterface/ServiceInterfaceStream.h>
uint32_t FixedTimeslotTask::deadlineMissedCount = 0; uint32_t FixedTimeslotTask::deadlineMissedCount = 0;
const size_t PeriodicTaskIF::MINIMUM_STACK_SIZE = configMINIMAL_STACK_SIZE; const size_t PeriodicTaskIF::MINIMUM_STACK_SIZE = configMINIMAL_STACK_SIZE;
@ -18,16 +19,19 @@ FixedTimeslotTask::~FixedTimeslotTask() {
void FixedTimeslotTask::taskEntryPoint(void* argument) { void FixedTimeslotTask::taskEntryPoint(void* argument) {
//The argument is re-interpreted as FixedTimeslotTask. The Task object is global, so it is found from any place. // The argument is re-interpreted as FixedTimeslotTask. The Task object is
// global, so it is found from any place.
FixedTimeslotTask *originalTask(reinterpret_cast<FixedTimeslotTask*>(argument)); FixedTimeslotTask *originalTask(reinterpret_cast<FixedTimeslotTask*>(argument));
// Task should not start until explicitly requested /* Task should not start until explicitly requested,
// in FreeRTOS, tasks start as soon as they are created if the scheduler is running * but in FreeRTOS, tasks start as soon as they are created if the scheduler
// but not if the scheduler is not running. * is running but not if the scheduler is not running.
// to be able to accommodate both cases we check a member which is set in #startTask() * To be able to accommodate both cases we check a member which is set in
// if it is not set and we get here, the scheduler was started before #startTask() was called and we need to suspend * #startTask(). If it is not set and we get here, the scheduler was started
// if it is set, the scheduler was not running before #startTask() was called and we can continue * before #startTask() was called and we need to suspend if it is set,
* the scheduler was not running before #startTask() was called and we
* can continue */
if (!originalTask->started) { if (not originalTask->started) {
vTaskSuspend(NULL); vTaskSuspend(NULL);
} }
@ -58,11 +62,6 @@ ReturnValue_t FixedTimeslotTask::startTask() {
ReturnValue_t FixedTimeslotTask::addSlot(object_id_t componentId, ReturnValue_t FixedTimeslotTask::addSlot(object_id_t componentId,
uint32_t slotTimeMs, int8_t executionStep) { uint32_t slotTimeMs, int8_t executionStep) {
if (objectManager->get<ExecutableObjectIF>(componentId) != nullptr) { if (objectManager->get<ExecutableObjectIF>(componentId) != nullptr) {
if(slotTimeMs == 0) {
// FreeRTOS throws a sanity error for zero values, so we set
// the time to one millisecond.
slotTimeMs = 1;
}
pst.addSlot(componentId, slotTimeMs, executionStep, this); pst.addSlot(componentId, slotTimeMs, executionStep, this);
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
@ -81,8 +80,9 @@ ReturnValue_t FixedTimeslotTask::checkSequence() const {
} }
void FixedTimeslotTask::taskFunctionality() { void FixedTimeslotTask::taskFunctionality() {
// A local iterator for the Polling Sequence Table is created to find the start time for the first entry. // A local iterator for the Polling Sequence Table is created to find the
auto slotListIter = pst.current; // start time for the first entry.
FixedSlotSequence::SlotListIter slotListIter = pst.current;
//The start time for the first entry is read. //The start time for the first entry is read.
uint32_t intervalMs = slotListIter->pollingTimeMs; uint32_t intervalMs = slotListIter->pollingTimeMs;
@ -90,32 +90,68 @@ void FixedTimeslotTask::taskFunctionality() {
TickType_t xLastWakeTime; TickType_t xLastWakeTime;
/* The xLastWakeTime variable needs to be initialized with the current tick /* The xLastWakeTime variable needs to be initialized with the current tick
count. Note that this is the only time the variable is written to explicitly. count. Note that this is the only time the variable is written to
After this assignment, xLastWakeTime is updated automatically internally within explicitly. After this assignment, xLastWakeTime is updated automatically
vTaskDelayUntil(). */ internally within vTaskDelayUntil(). */
xLastWakeTime = xTaskGetTickCount(); xLastWakeTime = xTaskGetTickCount();
// wait for first entry's start time // wait for first entry's start time
vTaskDelayUntil(&xLastWakeTime, interval); if(interval > 0) {
vTaskDelayUntil(&xLastWakeTime, interval);
}
/* Enter the loop that defines the task behavior. */ /* Enter the loop that defines the task behavior. */
for (;;) { for (;;) {
//The component for this slot is executed and the next one is chosen. //The component for this slot is executed and the next one is chosen.
this->pst.executeAndAdvance(); this->pst.executeAndAdvance();
if (pst.slotFollowsImmediately()) { if (not pst.slotFollowsImmediately()) {
//Do nothing // Get the interval till execution of the next slot.
} else { intervalMs = this->pst.getIntervalToPreviousSlotMs();
// we need to wait before executing the current slot interval = pdMS_TO_TICKS(intervalMs);
//this gives us the time to wait:
intervalMs = this->pst.getIntervalToPreviousSlotMs();
interval = pdMS_TO_TICKS(intervalMs);
vTaskDelayUntil(&xLastWakeTime, interval);
//TODO deadline missed check
}
checkMissedDeadline(xLastWakeTime, interval);
// Wait for the interval. This exits immediately if a deadline was
// missed while also updating the last wake time.
vTaskDelayUntil(&xLastWakeTime, interval);
}
} }
} }
void FixedTimeslotTask::checkMissedDeadline(const TickType_t xLastWakeTime,
const TickType_t interval) {
/* Check whether deadline was missed while also taking overflows
* into account. Drawing this on paper with a timeline helps to understand
* it. */
TickType_t currentTickCount = xTaskGetTickCount();
TickType_t timeToWake = xLastWakeTime + interval;
// Tick count has overflown
if(currentTickCount < xLastWakeTime) {
// Time to wake has overflown as well. If the tick count
// is larger than the time to wake, a deadline was missed.
if(timeToWake < xLastWakeTime and
currentTickCount > timeToWake) {
handleMissedDeadline();
}
}
// No tick count overflow. If the timeToWake has not overflown
// and the current tick count is larger than the time to wake,
// a deadline was missed.
else if(timeToWake > xLastWakeTime and currentTickCount > timeToWake) {
handleMissedDeadline();
}
}
void FixedTimeslotTask::handleMissedDeadline() {
#ifdef DEBUG
sif::warning << "FixedTimeslotTask: " << pcTaskGetName(NULL) <<
" missed deadline!\n" << std::flush;
#endif
if(deadlineMissedFunc != nullptr) {
this->deadlineMissedFunc();
}
}
ReturnValue_t FixedTimeslotTask::sleepFor(uint32_t ms) { ReturnValue_t FixedTimeslotTask::sleepFor(uint32_t ms) {
vTaskDelay(pdMS_TO_TICKS(ms)); vTaskDelay(pdMS_TO_TICKS(ms));
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;

View File

@ -1,26 +1,27 @@
#ifndef POLLINGTASK_H_ #ifndef FRAMEWORK_OSAL_FREERTOS_FIXEDTIMESLOTTASK_H_
#define POLLINGTASK_H_ #define FRAMEWORK_OSAL_FREERTOS_FIXEDTIMESLOTTASK_H_
#include <framework/devicehandlers/FixedSlotSequence.h> #include <framework/devicehandlers/FixedSlotSequence.h>
#include <framework/tasks/FixedTimeslotTaskIF.h> #include <framework/tasks/FixedTimeslotTaskIF.h>
#include <framework/tasks/Typedef.h> #include <framework/tasks/Typedef.h>
extern "C" { #include <freertos/FreeRTOS.h>
#include "FreeRTOS.h" #include <freertos/task.h>
#include "task.h"
}
class FixedTimeslotTask: public FixedTimeslotTaskIF { class FixedTimeslotTask: public FixedTimeslotTaskIF {
public: public:
/** /**
* @brief The standard constructor of the class. * Keep in mind that you need to call before vTaskStartScheduler()!
* * A lot of task parameters are set in "FreeRTOSConfig.h".
* @details This is the general constructor of the class. In addition to the TaskBase parameters, * @param name Name of the task, lenght limited by configMAX_TASK_NAME_LEN
* the following variables are passed: * @param setPriority Number of priorities specified by
* * configMAX_PRIORITIES. High taskPriority_ number means high priority.
* @param (*setDeadlineMissedFunc)() The function pointer to the deadline missed function that shall be assigned. * @param setStack Stack size in words (not bytes!).
* * Lower limit specified by configMINIMAL_STACK_SIZE
* @param getPst The object id of the completely initialized polling sequence. * @param overallPeriod Period in seconds.
* @param setDeadlineMissedFunc Callback if a deadline was missed.
* @return Pointer to the newly created task.
*/ */
FixedTimeslotTask(const char *name, TaskPriority setPriority, FixedTimeslotTask(const char *name, TaskPriority setPriority,
TaskStackSize setStack, TaskPeriod overallPeriod, TaskStackSize setStack, TaskPeriod overallPeriod,
@ -28,16 +29,18 @@ public:
/** /**
* @brief The destructor of the class. * @brief The destructor of the class.
* * @details
* @details The destructor frees all heap memory that was allocated on thread initialization for the PST and * The destructor frees all heap memory that was allocated on thread
* the device handlers. This is done by calling the PST's destructor. * initialization for the PST and the device handlers. This is done by
* calling the PST's destructor.
*/ */
virtual ~FixedTimeslotTask(void); virtual ~FixedTimeslotTask(void);
ReturnValue_t startTask(void); ReturnValue_t startTask(void);
/** /**
* This static function can be used as #deadlineMissedFunc. * This static function can be used as #deadlineMissedFunc.
* It counts missedDeadlines and prints the number of missed deadlines every 10th time. * It counts missedDeadlines and prints the number of missed deadlines
* every 10th time.
*/ */
static void missedDeadlineCounter(); static void missedDeadlineCounter();
/** /**
@ -46,13 +49,14 @@ public:
static uint32_t deadlineMissedCount; static uint32_t deadlineMissedCount;
ReturnValue_t addSlot(object_id_t componentId, uint32_t slotTimeMs, ReturnValue_t addSlot(object_id_t componentId, uint32_t slotTimeMs,
int8_t executionStep); int8_t executionStep) override;
uint32_t getPeriodMs() const; uint32_t getPeriodMs() const override;
ReturnValue_t checkSequence() const; ReturnValue_t checkSequence() const override;
ReturnValue_t sleepFor(uint32_t ms) override;
ReturnValue_t sleepFor(uint32_t ms);
protected: protected:
bool started; bool started;
TaskHandle_t handle; TaskHandle_t handle;
@ -60,32 +64,35 @@ protected:
FixedSlotSequence pst; FixedSlotSequence pst;
/** /**
* @brief This attribute holds a function pointer that is executed when a deadline was missed. * @brief This attribute holds a function pointer that is executed when
* * a deadline was missed.
* @details Another function may be announced to determine the actions to perform when a deadline was missed. * @details
* Currently, only one function for missing any deadline is allowed. * Another function may be announced to determine the actions to perform
* If not used, it shall be declared NULL. * when a deadline was missed. Currently, only one function for missing
* any deadline is allowed. If not used, it shall be declared NULL.
*/ */
void (*deadlineMissedFunc)(void); void (*deadlineMissedFunc)(void);
/** /**
* @brief This is the entry point in a new polling thread. * @brief This is the entry point for a new task.
* * @details
* @details This method, that is the generalOSAL::checkAndRestartPeriod( this->periodId, interval ); entry point in the new thread, is here set to generate * This method starts the task by calling taskFunctionality(), as soon as
* and link the Polling Sequence Table to the thread object and start taskFunctionality() * all requirements (task scheduler has started and startTask()
* on success. If operation of the task is ended for some reason, * has been called) are met.
* the destructor is called to free allocated memory.
*/ */
static void taskEntryPoint(void* argument); static void taskEntryPoint(void* argument);
/** /**
* @brief This function holds the main functionality of the thread. * @brief This function holds the main functionality of the thread.
* * @details
* * Core function holding the main functionality of the task
* @details Holding the main functionality of the task, this method is most important. * It links the functionalities provided by FixedSlotSequence with the
* It links the functionalities provided by FixedSlotSequence with the OS's System Calls * OS's System Calls to keep the timing of the periods.
* to keep the timing of the periods.
*/ */
void taskFunctionality(void); void taskFunctionality(void);
void checkMissedDeadline(const TickType_t xLastWakeTime,
const TickType_t interval);
void handleMissedDeadline();
}; };
#endif /* POLLINGTASK_H_ */ #endif /* FRAMEWORK_OSAL_FREERTOS_FIXEDTIMESLOTTASK_H_ */