Introduction
Scope
The goal of this application note will be to understand when a E_OS_LIMIT error occurs and how to solve it.
Many of the RTA-OS API calls return an error code at runtime which tells you whether the OS detected an error during the execution of the API call or not.
Each API call that returns an error code will return a different set of values depending on what type of errors can occur when the call is made.
Error code 4, E_OS_LIMIT, means that a task has been activated whilst still running or that the activation count has hit the limit of activation specified by configuration. So, the requested activation is ignored.
It's entirely an application issue and the error hook is the right way to trap that. The extra error hook information can be configured and the API call information will be obtained.
How to find out the offending task
Centralized Error Handling - the ErrorHook()
The common way of checking errors from either standard or extended status builds is to use the error hook which provides a “catch all” error handler. If the ErrorHook is enabled, then it is called by RTA-OS when any API call is about to return an error code that is not E_OK. The error code is passed into the ErrorHook routine and you can use it to work out which error has occurred. If you enable the error hook then you must provide an implementation. If you do not provide an implementation then your program will not link correctly. For example:
FUNC(void, OS_CALLOUT_CODE) ErrorHook(StatusType status) { switch (status) { case E_OS_ACCESS: /* Handle error then return. */ break; case E_OS_LIMIT: /* Terminate. */ ShutdownOS(status); default: break; } }
In this case, you need to know which API call resulted in the error being generated and also knowing which parameters were passed to the API call when it failed, this helps you to debug a problem.
This information is available at run-time by configuring advanced error logging. From the tool you have to set to TRUE the Call Error Hook, Record Service ID and Record Parameters as shown here:
Configuring Advanced Error Logging three levels of detail are available:
- Do not record the service details (default)
- Record the API name only.
- Record the API name and the associated parameters.
When error logging is enabled, RTA-OS provides a set of macros for accessing the name and the associated parameters of the API call that caused the error. You can find out which API call caused the error using the OSErrorGetServiceId() macro. This macro returns an OSServiceIdType of the form OSServiceId_<API name>.
So if the E_OS_LIMIT is called by an ActivateTask(), OSErrorGetServiceId will return OSServiceId_ActivateTask.
The parameters to the API call are available using macros in the form:
OSError_<API Name>_<API Parameter Name>
A macro is defined for each parameter of each API call.
Using the ActivateTask() example again, OSError_ActivateTask_TaskId will return the TaskId parameter passed to ActivateTask().
This additional error logging information can be usefully incorporated into the ErrorHook() code. The macros for obtaining the API name and the associated parameters should only be used from within the Error Hook. The values they represent do not persist outside the scope of the hook.
For example the implementation could be:
FUNC(void, OS_CALLOUT_CODE) ErrorHook(StatusType status) { OSServiceIdType callee; switch (status) { case E_OS_LIMIT: /* API call called with invalid handle. */ callee = OSErrorGetServiceId(); switch (callee) { case OSServiceId_ActivateTask: /* Handle error. */ break; case OSServiceId_ChainTask: /* Handle error. */ break; case OSServiceId_SetRelAlarm: /* Handle error. */ break; default: break; } /* Terminate. */ ShutdownOS(); default: break; } }
In-line Error Handling
An alternative to the ErrorHook() is to check the API return codes in-line with calling. This means that you can build some degree of run-time fault tolerance into your application. This may be useful if you want to check for error conditions that can occur in the Standard status so in case of an E_OS_LIMIT returned by a ActivateTask(). The example shows you how this can be done:
TASK(FaultTolerant){ /* Do some work */ if (ActivateTask(HelperTask) != E_OK) { /* Handle error during task activation. */ } TerminateTask(); }
Causes of this error
Incorrect TaskActivationCount
An E_OS_LIMIT error will trigger if a task receives an activation request while a previously activated task is either still running or in the queue and the activation request count goes over the activation count limit. So, it is important to allow for enough time for the task to run before it's activated again.
A task may need to have an activation count limit greater than 1, if there is communication between tasks, which is a "manual activation".
An example of needing to have an activation count limit greater than 1, is when receiving a transient burst of CAN frames. Where the time to run the task that processes the CAN frames is slower than the activations of this task (CAN frames arriving rapidly over a short period to trigger multiple activations).
Timer running too fast
If the Task activations are triggered by a schedule table it is likely the task that is generating the error is:
- being interrupted by a fast running task that has a higher priority;
- the interrupting task is the running task and it is running too quickly;
- too many operations are being performed in the task that is being interrupted so that it cannot be scheduled in the given time frame.
Tasks taking too long
If the Task activations are triggered by a schedule table it is likely the task that is generating the error is:
- not enough time is being given to the task that is being interrupted;
- the task does not have enough time to execute before being re-activated (it could be taking too long to execute, or other higher priority tasks could be taking too long).
So, if the task does not have queued activations (BCC1, ECC1, ECC2 tasks) and it is already running and another task activation is requested, then the additional request is lost and an E_OS_LIMIT error is raised.
If a system is running slower than expected, could be that you are trying to do more work within a certain time frame than you are able to. It could be that the task is being preempted too often, so you could raise the priority. It could be that some tasks are taking too long and hence this task isn't getting time.
How to calculate the correct TaskActivationCount
The number of the activation task count depends on your application, on how many times you are going to activate the task at the same time, therefore there is no specific rule for this.
So if you expect that this task will be activated n times at the same time, you should assign the count to n.
For example:
Let's suppose that the SWC "Client_SWC" has the runnable "RTEEv_Runnable_Step()" mapped to "Appl_Task_10ms_Core2".
This runnable triggers 2 events "Event1" and "Event2" via two "Server Call Point" which perform the operation "CommandRequest":
RTEEv_Runnable_Step() { Rte_Call_Client_SWC_Event1_CommandRequest(xx); Rte_Call_Client_SWC_Event2_CommandRequest(xx); }
From the other side, the SWC is connected to a "Server" port which starts a Runnable entity mapped to the task "CS_Task_Core0". So, in "Rte.c" you'll see something like:
Rte_Call_Client_SWC_Event1_CommandRequest(xx) { SetEvent1(); ActivateTask(CS_Task_Core0); } Rte_Call_Client_SWC_Event2_CommandRequest(xx) { SetEvent2(); ActivateTask(CS_Task_Core0); }
Now since "RTEEv_Runnable_Step" is mapped to "Appl_Task_10ms_Core2" this means that the runnable can activate "CS_Task_Core0" 2 times.
In Conclusion, the number of activation of "CS_Task_Core0" depends on your application code, in this example n will be 2.