The basic principles of the work of RTOS planners were discussed in the article “Tasks and Planning”. In this article, we look at the features that the Nucleus RTOS offers, as well as in more detail those provided by the Nucleus SE.
Previous articles in the series:
Article # 8. Nucleus SE: Inside and Deployment
Article # 7. Nucleus SE: introduction
Article # 6. Other RTOS services
Article # 5. Interaction between tasks and synchronization
Article # 4. Tasks, context switching and interrupts
Article # 3. Tasks and planning
Article # 2. RTOS: Structure and Real Time
Article # 1. RTOS: introduction.
Since Nucleus RTOS is a full-fledged, well-proven commercial RTOS, we can safely assume that the scheduler was designed in accordance with the requirements of such a product. This complex and flexible operating system provides the developer with a wide range of possibilities for solving virtually any conceivable programming problem in real time.
The scheduler can support an unlimited number of tasks (limited only by available resources) and work with priority management. A task can be assigned a priority from 0 to 255, where 0 is the highest priority and 255 is the lowest. A task has a dynamic priority, that is, it can be changed at run time, either by the task itself or by another. Multiple tasks can be assigned the same priority level. In the extreme case, all tasks can be assigned the same priority, which makes it possible to implement a scheduling policy on the principle of Round Robin and Time-Slice.
If there are several tasks with the same priority, they will be scheduled using the Round Robin algorithm, in the order in which they were prepared. The task needs to pause or transfer control to start the next task. Tasks can also be assigned time slots, which provide a more controlled division of available processor time.
Task scheduling is 100% deterministic, which is to be expected from a similar core. Tasks can also be dynamically created and destroyed, which, thanks to the scheduler, occurs invisibly to the user.
I developed all aspects of the Nucleus SE so that they were generally compatible with the Nucleus RTOS, but at the same time were simpler and more efficient in terms of memory. The scheduler is no exception. It provides many features of the Nucleus RTOS scheduler, but is somewhat limited. Flexibility is achieved through configuration at the time of assembly.
A Nucleus SE application can have a maximum of 16 tasks (and at least one). Although this number could theoretically be increased, the efficiency of the algorithms would be at risk; A number of data structures rely on storing the index number of the task (from 0 to 15) in the nibble (four bits), and they will need to be processed together with the corresponding code.
To achieve a balance between flexibility and simplicity (and size), instead of having one scheduler with multiple capabilities, the Nucleus SE offers a choice of one of four types of scheduler: Run to Completion (RTC), Round Robin (RR), Time-Slice ( TS) and Priority. The scheduler is selected statically, at the time of assembly. Details of each type of scheduler are described below in the “Scheduler Types” section.
Like any other aspect of the Nucleus SE, tasks are static objects. They are determined during configuration and their priority (index) cannot be changed.
As stated above, the Nucleus SE offers a choice of one of four types of schedulers. Like most aspects of the Nucleus SE configuration, this choice is determined by writing to nuse_config.h, the NUSE_SCHEDULER_TYPE parameter must be set accordingly, as shown in this snippet from the configuration file:
No matter which scheduler is selected, its startup code is called immediately after the system initialization. Full details on the initialization of the Nucleus SE will be presented in the next article.
The RTC scheduler is the easiest and most suitable solution if it meets the requirements of the application. Each task must complete its work before performing the return function and allowing the scheduler to perform the next task.
It is not necessary for each task to have a separate stack. All code is written in C, assembly language is not required. Below is the RTC scheduler code in its entirety.
Code is simply an infinite loop that calls each task in turn. The array NUSE_Task_Start_Address [] contains pointers to the external function of each task. The macro PF0 is a simple conversion of a void pointer to a pointer to a void function with no parameters. It is designed to ensure readability of the code.
Conditional compilation is used to enable support for additional functions: NUSE_SUSPEND_ENABLE determines whether tasks can be suspended; NUSE_SCHEDULE_COUNT_SUPPORT determines whether a counter value is required each time a task is scheduled. More information about this can be found in the following article.
If a bit more flexibility is required than provided by the RTC scheduler, an RR scheduler will do. It allows the task to transfer control or pause, and then continue from the same point. Additional overhead, in addition to code complexity and non-portability, is that each task requires its own stack.
The scheduler code consists of two parts. The startup component looks like this:
If support for the initial state of the task is enabled (using the NUSE_INITIAL_TASK_STATE_SUPPOR T parameter, see “Parameters” in the next article), scheduling begins with the first finished task; otherwise, the task with index 0 is used. The context of this task is then loaded using NUSE_Context_Load () . For more information on saving and restoring context, see the “Saving Context” section in the next article.
The second part of the scheduler is the “re-planning” component:
This code is called when the task releases the CPU or pauses.
The code chooses to run a task with the following index and puts the value on NUSE_Task_Next, taking into account whether task suspension is on or not. The macro NUSE_CONTEXT_SWAP () is then used to trigger a context switch using a software interrupt. For more information on saving and restoring context, see the “Saving Context” section in the next article.
The Priority Scheduler in the Nucleus SE, like the other options, is designed to provide the required functionality while being quite simple. As a result, each task has a unique priority; it is impossible to have several tasks with one priority level. The priority is determined by the task index, where 0 is the highest priority level. The index of the task is determined by its place in the array NUSE_Task_Start_Address []. The next article will provide more detailed information on setting up tasks.
Like the RR and TS schedulers, the Priority scheduler has two components. The launch component of the Priority scheduler is the same as that of the RR and TS schedulers, as illustrated above. The rescheduling component is somewhat different:
There is no conditional code that could disable the suspension of tasks, since this feature is mandatory for the priority scheduler; any alternative would be illogical. The NUSE_Reschedule () function takes a parameter that “tells” which task can be scheduled next - new_task. This value is set when rescheduling is called because another task is activated. The index of this task is passed as a parameter. The scheduler can then determine whether to perform a context switch by comparing the value of new_task with the index of the current task (NUSE_Task_Active) . If rescheduling is the result of pausing the task, the parameter will be set to NUSE_NO_TASK , and the scheduler will look for the task with the highest priority.
As a rule, all operating systems have the concept of finding tasks in a certain “state”. Details vary depending on the RTOS. In this article, we will look at how Nucleus RTOS and Nucleus SE use task states.
Nucleus RTOS supports 5 task states.
Source: https://habr.com/ru/post/422615/
All Articles