[STM32H7] Chapter 9 RTX5 task runs in privileged or non privileged mode

Forum original address (continuously updated): [anfulai] RTX5 kernel tutorial, which adopts CMSIS-RTOS V2 packaging layer, has been updated and released to Chapter 9 (2021-12-27) - STM32H7 - tough guy embedded Forum - Powered by Discuz!

Chapter 9} RTX5 tasks run in privileged or non privileged mode

The tutorial in this chapter explains an important knowledge point of RTX5 operation mode, privileged mode and non privileged mode. Some materials or books call the non privileged mode user mode.

catalogue

9.1 important tips for beginners

9.2} RTX5 task privilege level knowledge point description

9.2.1} which registers are inaccessible in non privileged mode

9.2.2 how to initialize core peripheral registers in non privileged mode

9.2.3 how to switch between two modes for Cortex-M3/M4/M7 kernel

9.2.4} setting method of RTX5 task privilege level

9.3} in depth understanding of RTX5 task privilege level

9.4 experimental routine description

9.5} summary

9.1 important tips for beginners

RTX5 uses the non privilege level mode. Never open the Event Recorder, because DWT calls require privilege level.

9.2} RTX5 task privilege level knowledge point description

For beginners, just remember the knowledge points in this section. If you want to have an in-depth understanding, you still need to take some time to study the authoritative Cortex-M3/M4/M7 guide.

For chips using Cortex-M3/M4/M7 kernel, RTX operating system can let tasks run in privileged or non privileged mode, which are the characteristics of Cortex-M3/M4/M7 kernel itself.

In privilege level mode, users can access and configure system control registers, such as NVIC interrupt controller. However, if it is in the non privileged mode, the system control register is not allowed to be accessed. Once accessed, it will lead to hardware exceptions.

  •   Unprivileged:

The non privileged level plays the role of protecting user tasks and preventing users from accessing and modifying system registers in any task. Improper operation will cause system crash.

  •   Privileged:

Privilege level, in which the user can access and modify the system control register in any task.

With this basic understanding, there are four questions to be solved.

9.2.1} which registers are inaccessible in non privileged mode

For Cortex-M3/M4/M7 cores, all core peripheral registers can only be accessed at the privilege level. Which are core peripheral registers? For STM32, you need to read the programming manual. All registers of these core peripherals need to be accessed at the privilege level:

The core peripherals are mainly MPU, NVIC, SCB and STK. Some beginners have to ask, how do you know that the registers of these kernel peripherals can only be accessed at the privilege level? Here is the answer. We can open any register:

 

You can check whether other registers of MPU, NVIC, SCB and STK can be accessed at the privilege level according to the above method.

In addition to the core peripheral registers, the special function registers of Cortex-M3/M4/M7 cores cannot be accessed at the non privileged level. The special function registers mainly include the following registers:

  • Program status register group (PSRs or xPSR)
  • Interrupt mask register group (PRIMASK, FAULTMASK, and BASEPRI)
  • CONTROL register

For SPI, USART, USB and other peripheral registers mentioned in the reference manual, they can be accessed at the non privileged level.

9.2.2 how to initialize core peripheral registers in non privileged mode

If the user sets the tasks of the RTX operating system to run in the non privileged mode, where should the core peripheral registers be initialized? There are mainly the following two methods:

  • Using SVC (Supervisor Call) soft interrupt will be explained in detail in later chapters.
  • Initialize the core peripherals before initializing and starting RTX multitasking.

9.2.3 how to switch between two modes for Cortex-M3/M4/M7 kernel

The special function registers in Cortex-M3/M4/M7 include:

  • Program status register group (PSRs or xPSR)
  • Interrupt mask register group (PRIMASK, FAULTMASK, and BASEPRI)
  • CONTROL register

The CONTROL register CONTROL is used to set the switching between privileged level and non privileged level. The CONTROL register is defined as follows:

 

 CONTROL[0]

This bit is allowed to be written only when operating at the privilege level. Once entering the user level, the only way to return to the privilege level is to trigger a soft interrupt, and then the service routine rewrites the bit.

The CONTROL register is also operated by MRS and MSR instructions:

MRS   R0,     CONTROL

MSR   CONTROL,  R0

9.2.4} setting method of RTX5 task privilege level

The setting method of RTX task privilege level is relatively simple. Check the configuration wizard of RTX system, as shown in Figure 9.1:

Figure 9.1 RTX Configuration Wizard

  •   Run in privileged mode

This parameter is used to set privilege level and non privilege level. Selecting the radio box means that the enabled task works in privilege level mode, and canceling the radio box means that the task works in non privilege level mode.

9.3} in depth understanding of RTX5 task privilege level

The knowledge points in this section are difficult for beginners to understand. They can only be better understood after accumulating some experience. However, it is recommended to read them.

To deeply understand the privilege level of Cortex-M3/M4/M7 kernel, we have to talk about two operation modes. Cortex-M3/M4 supports two operation modes, which are:

  • Handler mode, interrupt mode, simply means that the exception service program is in interrupt mode.
  • Thread mode, which simply means that programs other than exception service programs are in thread mode.

The purpose of Cortex-M3/M4 kernel to realize these two operations is to distinguish between ordinary application code and exception service program. The following is the relationship between two operation modes and two privilege levels:

  • The difference between interrupt and exception

In the field of ARM programming, any event that interrupts the sequential execution of the program is called exception. In addition to external interrupts, when an instruction executes an "illegal operation", or accesses a prohibited memory interval, fault s caused by various errors, and unshielded interrupts occur, the execution of the program will be interrupted. These conditions are collectively referred to as exceptions. Exceptions and interrupts can also be mixed in a loose context. In addition, the program code can also actively request to enter the abnormal state (commonly used in system call).

When the processor is in the thread state, you can use either the privilege level or the user level; On the other hand, the handler pattern is always privilege level. After system reset, the processor enters thread mode + privilege level.

Code under privilege level can enter user level by setting CONTROL[0]. Regardless of any exception caused by any reason, the processor will run its service routine at the privilege level. After the exception is returned, the system will return to the level where the exception was generated. Code at the user level can no longer attempt to modify CONTROL[0] to return to the privilege level. It must pass an exception handler to modify CONTROL[0] before it can get the privilege level after returning to thread mode. The following figure shows the switching between privilege level thread mode and user level thread mode:

Some simple applications do not require user level threading mode, as shown in the following figure:

Treating the code as privilege level and user level separately is conducive to making the architecture of CM3/CM4 more secure and robust. For example, when a user program code has a problem, it will not become a black sheep, because user level code prohibits writing special function registers and NVIC interrupt registers. In addition, if it is also equipped with MPU, the protection is more powerful, and it can even prevent user code from accessing memory areas that do not belong to it.

In order to avoid the damage of the system stack due to the wrong use of the application, we can assign a stack to the application to prevent it from sharing the stack of the operating system kernel. Under this management system, the user code running in thread mode uses PSP, while the exception service routine uses MSP. The switching between these two stack pointers is intelligent and automatic, and is handled by hardware at the beginning and end of exception service.

As mentioned earlier, CONTROL is responsible for the selection of privilege level and stack pointer. When CONTROL[0]=0, only processor mode conversion occurs at the beginning and end of exception handling, as shown in the following figure.

However, if CONTROL[0]=1 (thread mode + user level), the processor mode and privilege will change at the beginning and end of the interrupt response, as shown in the figure below.

CONTROL[0] can only be accessed at the privilege level. If a user level program wants to enter the privilege level, it usually uses a "system service call instruction (SVC)" to trigger the "SVC exception". The service routine of the exception can modify the CONTROL[0] according to the specific situation.

I'll tell you so much about the operation mode and privilege level. I'll explain the RTX task switching in detail later (this chapter will be matched when the RTX5 tutorial is upgraded later).

9.4 experimental routine description

Supporting examples:

V7-405_RTX5 Task Unprivileged Mode

Purpose of the experiment:

  1. Learn the privilege level and non privilege level settings of RTX.

Experiment content:

  1. Press K1 key to print serial port.
  2. Press the K2 key to force the NVIC operation, which will enter the hardware exception.
  3. The functions of each task are as follows:

AppTaskUserIF task: key message processing.

Apptask: the LED flashes.

AppTaskMsgPro task: message processing.

AppTaskStart task: start the task, which is also the highest priority task. Here, key scanning is realized.

osRtxTimerThread task: timer task, not used yet.

Serial port printing information:

Baud rate 115200, data bit 8, parity bit none, stop bit 1.

RTX configuration:

The details of the RTX configuration wizard are as follows:

   System Configuration

  •   Global Dynamic Memory size

Global dynamic memory, set to 32KB here.

  •   Kernel Tick Frequency

The system clock beat is set to 1KHz here.

  Thread Configuration

  •   Default Thread Stack size

The default task stack size is 1024 bytes

RTX5 task debugging information:

Programming:

Task stack size allocation:

All configurations are independent, and RTX5 default configuration is not used:

/*
**********************************************************************************************************
											 variable
**********************************************************************************************************
*/
/* Task property settings */
const osThreadAttr_t ThreadStart_Attr = 
{
	/* not used */
//	.cb_mem = &worker_thread_tcb_1,
//	.cb_size = sizeof(worker_thread_tcb_1),
//	.stack_mem = &worker_thread_stk_1[0],
//	.stack_size = sizeof(worker_thread_stk_1),
//	.priority = osPriorityAboveNormal,
//	.tz_module = 0
	
	.name = "osRtxStartThread",
	.attr_bits = osThreadDetached, 
	.priority = osPriorityHigh4,
	.stack_size = 2048,
};

const osThreadAttr_t ThreadMsgPro_Attr = 
{
	.name = "osRtxMsgProThread",
	.attr_bits = osThreadDetached, 
	.priority = osPriorityHigh3,
	.stack_size = 1024,
};

const osThreadAttr_t ThreadLED_Attr = 
{
	.name = "osRtxLEDThread",
	.attr_bits = osThreadDetached, 
	.priority = osPriorityHigh2,
	.stack_size = 512,
};

const osThreadAttr_t ThreadUserIF_Attr = 
{
	.name = "osRtxThreadUserIF",
	.attr_bits = osThreadDetached, 
	.priority = osPriorityHigh1,
	.stack_size = 1024,
};

System stack size allocation:

RTX5 initialization:

/*
*********************************************************************************************************
*	Function name: main
*	Function Description: standard c program entry.
*	Formal parameters: None
*	Return value: None
*********************************************************************************************************
*/
int main (void) 
{	
	/* HAL Library, MPU, Cache, clock and other system initialization */
	System_Init();

	/* Time benchmark for closing HAL before kernel startup */
	HAL_SuspendTick();
	
	/* Kernel initialization */
	osKernelInitialize();                                  

	/* Create startup task */
	ThreadIdStart = osThreadNew(AppTaskStart, NULL, &ThreadStart_Attr);  

	/* Turn on multitasking */
	osKernelStart();
	
	while(1);
}

RTX5 task creation:

/*
*********************************************************************************************************
*	Function name: AppTaskCreate
*	Function Description: create application task
*	Formal parameters: None
*	Return value: None
*********************************************************************************************************
*/
static void AppTaskCreate (void)
{
	ThreadIdTaskMsgPro = osThreadNew(AppTaskMsgPro, NULL, &ThreadMsgPro_Attr);  
	ThreadIdTaskLED = osThreadNew(AppTaskLED, NULL, &ThreadLED_Attr);  
	ThreadIdTaskUserIF = osThreadNew(AppTaskUserIF, NULL, &ThreadUserIF_Attr);  
}

Implementation of four RTX tasks:

/*
*********************************************************************************************************
*	Function name: AppTaskUserIF
*	Function Description: key message processing		
*	Formal parameters: None
*	Return value: None
*   Priority: ospriorityhig1 (the smaller the value, the lower the priority, which is opposite to uCOS)
*********************************************************************************************************
*/
void AppTaskUserIF(void *argument)
{
	uint8_t ucKeyCode;

    while(1)
    {
		ucKeyCode = bsp_GetKey();
		
		if (ucKeyCode != KEY_NONE)
		{
			switch (ucKeyCode)
			{
				/* K1 Key press */
				case KEY_DOWN_K1:
					printf("K1 Key Download\r\n");
					
					break;
					
				/* K2 Key press */
				case KEY_DOWN_K2:
					printf("K2 Press the key to set in non privileged mode NVIC Packet, causing the system to enter the hardware exception
HardFault_Handler\r\n");
					HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
					break;

				/* Other key values are not processed */
				default:                     
					break;
			}
		}
		
		osDelay(20);
	}
}

/*
*********************************************************************************************************
*	Function name: AppTaskLED
*	Function Description: LED flashes.
*	Formal parameters: None
*	Return value: None
*   Priority: ospriorityhig2 
*********************************************************************************************************
*/
void AppTaskLED(void *argument)
{
	const uint16_t usFrequency = 200; /* Delay period */
	uint32_t tick;

	/* Get current time */
	tick = osKernelGetTickCount(); 
	
    while(1)
    {
		bsp_LedToggle(2);
		/* Relative delay */
		tick += usFrequency;                          
		osDelayUntil(tick);
    }
}

/*
*********************************************************************************************************
*	Function name: AppTaskMsgPro
*	Function Description: message processing, not used for the time being.
*	Formal parameters: None
*	Return value: None
*   Priority: ospriorityhig3  
*********************************************************************************************************
*/
void AppTaskMsgPro(void *argument)
{
	while(1)
	{
		osDelay(10);
	}	
}

/*
*********************************************************************************************************
*	Function name: AppTaskStart
*	Function Description: start task, which is used here as BSP driver package processing.
*	Formal parameters: None
*	Return value: None
*   Priority: ospriorityhig4  
*********************************************************************************************************
*/
void AppTaskStart(void *argument)
{
	const uint16_t usFrequency = 1; /* Delay period */
	uint32_t tick;
	
	/* Initialize peripherals */
	HAL_ResumeTick();
	bsp_Init();

	/* Create task */
	AppTaskCreate();

	/* Get current time */
	tick = osKernelGetTickCount(); 
	
    while(1)
    {
		/* Programs requiring periodic processing correspond to systick called by bare metal projects_ ISR */
		bsp_ProPer1ms();
		
		/* Relative delay */
		tick += usFrequency;                          
		osDelayUntil(tick);
    }
}

9.5} summary

For beginners, it is easy to make mistakes in privileged and non privileged places. If you don't have this foundation before, it's still a little difficult to understand. It's not urgent. Take your time. A complete understanding also needs a step-by-step process.

Keywords: stm32 ARM

Added by gerrydewar on Thu, 06 Jan 2022 11:42:30 +0200