FreeRTOS and RT-Thread for Interrupt and Critical Zone Processing

Keywords: IoT stm32 RTOS RTT

1. Management of interruptions

Freertos manages system interrupts by manipulating BASEPRI registers, and the management priority is defined in the header file FreeRTOSConfig.h

#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5

Setting the maximum priority that FreeRTOS can manage does not belong to the FreeRTOS pipeline, and interrupt service functions below this priority can safely call the api of freertos, and api cannot be called above this priority (freertos cannot manage)

RT-Thread Interrupts are managed by manipulating the PRIMASK registers, directly closing all interrupts except NMI FAULT and hard FAULT

2. Treatment of critical zone

Processing methods for Freertos:

When entering the critical zone, turn off the interrupts at the management level and the number of entries uxCriticalNesting plus 1. At this point, the interrupts on the MCU and the system scheduler are closed except those whose priority is greater than the set threshold (systick has the lowest interrupt priority).

Priority interrupts for SYSTICK are set in xPortStartScheduler() in port.c

/* Make PendSV and SysTick the lowest priority interrupts. */

portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;//Set minimum interrupt

portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;

Freertos Enter Critical Zone

void vPortEnterCritical( void )
{
	portDISABLE_INTERRUPTS();
	uxCriticalNesting++;

	/* This is not the interrupt safe version of the enter critical function so
	assert() if it is being called from an interrupt context.  Only API
	functions that end in "FromISR" can be used in an interrupt.  Only assert if
	the critical nesting count is 1 to protect against recursive calls if the
	assert function also uses a critical section. */
	if( uxCriticalNesting == 1 )
	{
		configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
	}
}

On exit, subtract 1 from the number of entries uxCriticalNesting until you exit the outermost nesting (uxCriticalNesting is 0), then set Allow management interrupts to allow the system scheduler to work

void vPortExitCritical( void )
{
	configASSERT( uxCriticalNesting );
	uxCriticalNesting--;
	if( uxCriticalNesting == 0 )
	{
		portENABLE_INTERRUPTS();
	}
}

Processing method for RTT:

When entering the critical zone, the system shuts down an interrupt (closing all interrupts except NMI FAULT and hard FAULT) immediately after adding the number of entries rt_scheduler_lock_nest to 1. The system resources consumed here are negligible and have little impact on other interrupts in the system.

void rt_enter_critical(void)
{
    register rt_base_t level;

    /* disable interrupt */
    level = rt_hw_interrupt_disable(); // Use only to prevent access conflicts

    /*
     * the maximal number of nest is RT_UINT16_MAX, which is big
     * enough and does not check here
     */
    rt_scheduler_lock_nest ++;  // When not 0, the scheduler does not enable rt_schedule to determine if the value is 0
    
    /* enable interrupt */
    rt_hw_interrupt_enable(level);
}

After entering the critical zone, the system shuts down the scheduler so that tasks in the critical zone are not preempted and interrupted by other tasks.

void rt_schedule(void)
{
    rt_base_t level;
    struct rt_thread *to_thread;
    struct rt_thread *from_thread;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    /* check the scheduler is enabled or not */
    if (rt_scheduler_lock_nest == 0) // Critical segment protection judgment
    {
    // Execute Thread Schedule Switch
    }
}

Exit critical zone, similar to enter critical zone, closes the total interrupt on entry, minus 1 entry count, and determines if a task needs to be scheduled once before turning on the total interrupt

/**
 * This function will unlock the thread scheduler.
 */
void rt_exit_critical(void)
{
    register rt_base_t level;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    rt_scheduler_lock_nest --;
    if (rt_scheduler_lock_nest <= 0)
    {
        rt_scheduler_lock_nest = 0;
        /* enable interrupt */
        rt_hw_interrupt_enable(level);

        if (rt_current_thread)
        {
            /* if scheduler is started, do a schedule */
            rt_schedule();
        }
    }
    else
    {
        /* enable interrupt */
        rt_hw_interrupt_enable(level);
    }
}

Overall, freertos handles critical zones in a more detailed manner, attributable to system-managed interrupts and the shutdown of the scheduler until it exits the critical zone, with no impact on the execution of high-priority interrupts in the system.

It is important to understand that RTT does not rely solely on interruptions of interest to process critical zones; it enters critical zones by not focusing on tasks until only one task is executed.

It handles critical zones directly, simply shutting down the system dispatcher (called dispatcher lock), and if an interrupt signal is generated after the dispatcher locks, the system can still respond to the interrupt. However, if other interrupts are not real-time enough, the interrupt lock can be used to enter the critical zone rt_interrupt_enter, and all interrupts and dispatchers are closed.

Posted by Warmach on Sat, 18 Sep 2021 19:26:01 -0700