Switching Tasks in ThreadX RTOS

Using the QEMU simulator to outline the ThreadX task scheduling process.0. Build ExampleCreate Task 0 and Task 1

#include "tx_api.h"
#include "tx_thread.h"
// Task 0 entry
void    thread_0_entry(ULONG thread_input){
    TX_THREAD       *current_thread;
    thread_0_counter++;
    TX_THREAD_GET_CURRENT(current_thread);
    tx_thread_terminate(current_thread);
}
// Task 1 entry
void    thread_1_entry(ULONG thread_input){
    while(1)
    {
        /* Increment the thread counter.  */
        thread_1_counter++;
        while(1);
    }}
// Create tasks
void    tx_application_define(void *first_unused_memory){
    CHAR    *pointer = TX_NULL;
    /* Create a byte memory pool from which to allocate the thread stacks.  */
    tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE);
    /* Put system definition stuff in here, e.g. thread creates and other assorted       create information.  */
    /* Allocate the stack for thread 0.  */
    tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
    /* Create the main thread.  */
    tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,              pointer, DEMO_STACK_SIZE,             1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);
    /* Allocate the stack for thread 1.  */
    tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
    /* Create threads 1 and 2. These threads pass information through a ThreadX        message queue.  It is also interesting to note that these threads have a time       slice.  */
    tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1,              pointer, DEMO_STACK_SIZE,             16, 16, 4, TX_AUTO_START);
}

1. From the termination of Task 0’s thread_0_entry to Task 1’s while(1)Switching Tasks in ThreadX RTOSCode Walkthrough:

//thead0 context Task 0 state from TX_READY -> TX_TERMINATED
_tx_thread_terminate    thread_ptr -> tx_thread_state =  TX_TERMINATED;
    /* Call actual thread suspension routine.  */
    _tx_thread_system_suspend(thread_ptr);//(Step 1)
    /* Check for preemption.  */
    _tx_thread_system_preempt_check();
        /* Pickup thread pointer.  */
        TX_THREAD_GET_CURRENT(current_thread)
        /* Pickup the next execute pointer.  */
        thread_ptr =  _tx_thread_execute_ptr;
        if (current_thread != thread_ptr)
        {
            _tx_thread_system_return();//(Step 2)cpu porting layer impls(M3 implementation)
        }

Step 1, Preparing for Task SwitchingThe current task state is set to terminated (TX_TERMINATED), and it searches for other tasks of the same priority in the current priority linked list. If there are no other tasks to execute in the current priority queue, it first clears the corresponding bit in _tx_thread_priority_maps for that priority, and then quickly finds the corresponding priority queue using a bitmap, saving it to the global pointer _tx_thread_execute_ptr

/* Calculate the lowest bit set in the priority map. */
TX_LOWEST_SET_BIT_CALCULATE(priority_map, priority_bit)
/* Setup the next highest priority variable.  */
_tx_thread_highest_priority =  base_priority + ((UINT) priority_bit);
_tx_thread_execute_ptr =  _tx_thread_priority_list[_tx_thread_highest_priority];

Switching Tasks in ThreadX RTOSStep 2, Completing the Task Switching ActionThe task switching action is completed through the _tx_thread_system_return interface, which assigns the following global pointer: _tx_thread_current_ptr = _tx_thread_execute_ptr, and restores the context from the target task stack.Different processors implement the _tx_thread_system_return interface according to their characteristics, triggering context switching, as shown in the M3 interface implementation

// M3 implementation
__attribute__( ( always_inline ) ) static inline void _tx_thread_system_return_inline(void){
unsigned int interrupt_save;
    /* Set PendSV to invoke ThreadX scheduler.  */
    *((volatile ULONG *) 0xE000ED04) = ((ULONG) 0x10000000);
    if (__get_ipsr_value() == 0)
    {
        interrupt_save = __get_interrupt_posture();
#ifdef TX_PORT_USE_BASEPRI
        __set_basepri_value(0);
#else
        __enable_interrupts();
#endif
        __restore_interrupt(interrupt_save);
    }}

Then it enters the PENDSV exception entry, returning from PENDSV to the target task Thread 1Switching Tasks in ThreadX RTOSNote: The above task entry refers to the entry of the task business-related code. In the ThreadX system definition, when a task starts executing, it always enters from a unified entry (_tx_thread_shell_entry) and then enters the user-defined entry (thread_entry) in a callback manner.Switching Tasks in ThreadX RTOS

Leave a Comment