FreeRTOS Advanced Article 3---start scheduler

Source: Internet
Author: User

Using FreeRTOS, one of the most basic program architectures is as follows:

int main (void) {      necessary initialization work;    Create task 1;    Create Task 2;    ...   Vtaskstartscheduler ();  /* Start Scheduler *    /while (1);   }

when the task is created, the static variable pointer PXCURRENTTCB (see section 7th, "FreeRTOS advanced 2---freertos Task creation Analysis ") points to the highest-priority readiness task. However, the task does not work at this point because there is a critical next step: Start the FreeRTOS scheduler.

The scheduler is the core of the FreeRTOS operating system, primarily responsible for task switching, which is to identify the highest-priority readiness tasks and get the CPU running power. The scheduler does not run automatically, and it needs to be artificially started.

The API function Vtaskstartscheduler () is used to start the scheduler, which creates an idle task, initializes some static variables, and, most important, initializes the system beat timer and sets the corresponding interrupt, then starts the first task. This article is used to analyze the process of starting the scheduler, as in the previous article, the boot scheduler also involves hardware features (such as System beat timer initialization, etc.), so this article still takes the CORTEX-M3 architecture as an example.

The source of the API function for the boot scheduler Vtaskstartscheduler () is as follows:

void Vtaskstartscheduler (void) {basetype_t xreturn; statictask_t *pxidletasktcbbuffer= NULL;     stacktype_t *pxidletaskstackbuffer= null;uint16_t usidletaskstacksize =tskidle_stack_size; /* If you use static memory to allocate task stacks and task TCB, you need to pre-define task memory and task TCB space for idle tasks */#if (configsupport_static_allocation = = 1) {Vapplicationge    Tidletaskmemory (&pxidletasktcbbuffer, &pxidletaskstackbuffer, &usidletaskstacksize); #endif/*configsupport_static_allocation */* Create idle tasks, use the lowest priority */Xreturn =xtaskgenericcreate (Prvidletask, "IDL E ", Usidletaskstacksize, (void *) NULL, (tskidle_priority | portprivilege_bit), &xidletaskhandle,     Pxidletaskstackbuffer,pxidletasktcbbuffer, NULL); The IF (Xreturn = = Pdpass) {/* First closes the interrupt to ensure that the beat timer interrupt does not occur at or before the call to Xportstartscheduler (). When the first task starts, the interrupt is restarted */Portdisab               Le_interrupts ();       /* Initialize static variable */xnexttaskunblocktime = Portmax_delay;        xschedulerrunning = Pdtrue;         Xtickcount = (ticktype_t) 0U; /* If Macro ConfiggeneraTe_run_time_stats is defined to indicate the use of the run time statistics function, the following macro must be defined to initialize a base timer/counter. */Portconfigure_timer_for_run_time_stats (); /* Set the system beat timer, which is related to the hardware characteristics and is therefore placed on the porting layer. */if (Xportstartscheduler ()! = Pdfalse) {/* If the scheduler is running correctly, it will not be executed here    , the function does not return */} else {/* Only when the task invokes the API function Xtaskendscheduler (), it is executed here. */}} else    {/* Execution here indicates that the kernel does not start, possibly because the stack space is not enough */Configassert (Xreturn); }/* Prevent compiler warning */(void) Xidletaskhandle;}

This API function first creates an idle task, the idle task uses the lowest priority (level 0), the task handle of the idle task is stored in the static variable Xidletaskhandle, and the API function Xtaskgetidletaskhandle () can be called to get the idle task handle.

If the task is created successfully, the interrupt is closed (it will be interrupted again at the end of the scheduler start), some static variables are initialized, and then the function Xportstartscheduler () is called to start the system beat timer and start the first task. Since setting the system tick timer involves hardware features, the function Xportstartscheduler () is provided by the porting layer, with different hardware architectures, and the code for this function is not the same.

For the CORTEX-M3 schema, the implementation of function Xportstartscheduler () is as follows:

basetype_t Xportstartscheduler (void) {#if (configassert_defined = = 1) {volatile uint32_tuloriginalpriority        ; /* Interrupt Priority Register 0:IPR0 */volatile uint8_t * Constpucfirstuserpriorityregister = (uint8_t *) (portnvic_ip_registers_of        Fset_16 +portfirst_user_interrupt_number);         Volatile uint8_tucmaxpriorityvalue;       /* This large piece of code is used to determine the highest ISR priority, and it is safe to invoke API functions that end with FROMISR in this ISR or lower priority ISR./* Save the interrupt priority value because the register (IPR0) is covered below */         uloriginalpriority = *pucfirstuserpriorityregister; /* Determine the number of priority digits that are valid.       First write to all bits 1, and then read out, because the invalid priority bit read out to 0, and then count the number of how many 1, you can know how many priority. */*pucfirstuserpriorityregister= Portmax_8_bit_value;         Ucmaxpriorityvalue = *pucfirstuserpriorityregister; /* Redundant code to prevent users from improperly setting the RTOs to mask interrupt priority values */ucmaxsyscallpriority =configmax_syscall_interrupt_priority &AMP;UCMAXPRIORITYV         Alue;        /* Calculate the maximum priority group Value */Ulmaxprigroupvalue =portmax_prigroup_bits; while ((Ucmaxpriorityvalue &porttop_bit_of_byte) ==porttop_bIt_of_byte) {ulmaxprigroupvalue--;        Ucmaxpriorityvalue <<= (uint8_t) 0x01;       } ulmaxprigroupvalue <<=portPRIGROUP_SHIFT;         Ulmaxprigroupvalue &=portPRIORITY_GROUP_MASK;    /* Restore the value of the IPR0 register */*pucfirstuserpriorityregister= uloriginalpriority;   #endif/*conifgassert_defined */* Sets the PENDSV and Systick interrupts to the lowest priority */Portnvic_syspri2_reg |=PORTNVIC_PENDSV_PRI;     Portnvic_syspri2_reg |=portnvic_systick_pri;     /* Start the system beat timer, i.e. the Systick timer, initialize the interrupt cycle and enable the timer */Vportsetuptimerinterrupt ();     /* Initialize critical section nesting counter */uxcriticalnesting = 0;     /* Start the first task */Prvstartfirsttask (); /* never come here! */return 0;}

From the source, you can see that the beginning of a large segment is redundant code. Because CORTEX-M3 's interrupt priority is somewhat counterintuitive: the larger the CORTEX-M3 interrupt priority value, the lower the priority. The task priority of FreeRTOS is the opposite: the higher the priority value, the greater the priority. According to official statistics, using FreeRTOS on CORTEX-M3 hardware, the vast majority of problems are set incorrectly on the priority setting. Therefore, in order to make freertos more robust, FreeRTOS's authors deliberately added redundant code when writing CORTEX-M3 architecture porting layer code. For a detailed CORTEX-M3 architecture interrupt priority setting, refer to the article "FreeRTOS series 7th---cortex-m kernel uses freertos special Considerations ".

In the CORTEX-M3 architecture, FreeRTOS uses three exceptions for task initiation and task switching: SVC, PENDSV, and Systick. The SVC (System service Call) is used for task initiation, some operating systems do not allow the application to access the hardware directly, but rather by providing some system service functions through the SVC to invoke, PENDSV (can suspend the system call) is used to complete the task switching, its greatest feature is if the current priority is higher than the interrupt is running , the PENDSV is deferred until the high-priority interrupt execution is complete; Systick is used to generate the system tick clock, which provides a time slice, and if multiple tasks share the same priority, each time the Systick is interrupted, the next task will get a slice. For a detailed description of SVC and PENDSV anomalies, the "anomalies" section of the book "Cortex-m3 authoritative Guide" is recommended.

The PENDSV and Systick exception priority is set to the lowest, so that task switching does not interrupt an interrupt service program, the interrupt service program is not delayed, which simplifies the design and facilitates the stability of the system.

The next call to function Vportsetuptimerinterrupt () sets the Systick timer interrupt period and enables the timer to run this function is relatively simple, is to set the corresponding register Systick hardware.

Then there is a key function that is Prvstartfirsttask(), which is used to start the first task. Let's look at the source code first:

__asm void Prvstartfirsttask (void) {    PRESERVE8/     * CORTEXT-M3 hardware, 0xe000ed08 address at vtor (vector table offset) register, storage vector table start Address */    Ldr r0, =0xe000ed08        ldr r0, [r0]/    * Take out the first item in the vector table, the first item of the vector table stores the initial value of the main stack pointer MSP */    LDR r0, [r0]/        * Save stack address to main stack pointer */    MSR MSP, R0/    * Enable global interrupt */    Cpsie i    cpsie F    DSB    ISB/    * Call SVC to start first task */< C17/>svc 0    nop    NOP}

The program begins with a few lines of code that resets the value of the main stack pointer MSP, indicating that the MSP pointer is FreeRTOS taken over from this point, and it is important to note that the interrupt for the CORTEX-M3 hardware also uses the MSP pointer. After enabling the interrupt, use the assembly instruction Svc 0 to trigger the SVC interrupt and complete the task of starting the first one. Let's take a look at the SVC Interrupt Service function:

__asm void Vportsvchandler (void) {    PRESERVE8     LDR R3, =PXCURRENTTCB/   * PXCURRENTTCB point to the highest-priority readiness task TCB */    LDR R1, [R3]/            * Gets the task TCB address */    LDR r0, [R1]/            * Gets the first member of the task TCB, which is the current stack top Pxtopofstack */    ldmia r0!, { R4-R11}/     * out of the stack, r4~r11 the register out of the stack */    msr PSP, R0/             * Latest stack top pointer to the thread stack pointer psp */    ISB    mov r0, #0    msr Basepri, R0    orrr14, #0xd           /* Here 0x0d means: Return to threading mode, stack operation from the process stack, return to thumb status */    BX R14}

We have already known the pointer PXCURRENTTCB through the article we created in the previous article about the task. This is the only global variable defined in TASKS.C, which points to the highest-priority ready task TCB. We know that the core function of FreeRTOS is to ensure that the highest priority readiness task gets CPU privileges, so it can be said that the task that the pointer points to is either running or is about to run (the scheduler shuts down), so this variable is named PXCURRENTTCB.

According to "FreeRTOS advanced 2---freertos Task creation Analysis " We can see that when a task is created, it initializes its task stack as if it were a task switch, as shown in 1-1. For the CORTEX-M3 architecture, you need to go to the stack xpsr, PC, LR, R12, R3~r0, R11~R4, where R11~R4 needs to be artificially stacked, and other registers are automatically loaded into the stack by hardware. The register PC is initialized to the task function pointer vtask_a, so that when a task is switched on, task a gets CPU control, the task function vtask_a is out of the stack to the PC register, then the task A's code is executed, and the LR register is initialized to the function pointer Prvtaskexiterror, This is an error handling function provided by the migration layer.

The task TCB struct member Pxtopofstack represents the stack top of the current stack, which points to the last item in the stack, so in the diagram it points to the R4,TCB struct another member Pxstack represents the starting position of the stack, so in the diagram it points to the beginning of the stack.


Figure 1-1: Task stack distribution after task creation

Therefore, the SVC Interrupt service function starts by using global pointer PXCURRENTTCB to get the first task TCB to start, thus obtaining the top pointer of the task's current stack stack. First, the R4~R11 registers the stack, assigns the latest stack top pointer to the thread stack pointer psp, and then cancels the interrupt masking. Here, whenever an interruption occurs, it can be responded to.

The interrupt service function is returned by the following two-sentence assembly. In the CORTEX-M3 schema, the value of R14 determines the pattern returned from the exception, where R14 the last four bits of the bitwise OR upper 0x0d, representing the stack operation from the process stack upon return, returning to thread mode, and returning to the thumb state.

        Orr R14, #0xd               bx R14

After the BX R14 instruction is executed, the hardware automatically registers the register XPSR, PC, LR, R12, r3~r0 out of the stack, then task A's task function pointer vtask_a will be out of the stack to the PC pointer, thereby starting to perform task A.

At this point, the scheduler formally began to work.

FreeRTOS Advanced Article 3---start scheduler

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.