OpenVINOTM,给你看得见的未来!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » STM32G070RB(三)Demo2:MPU使用,Privileged与Unp

共2条 1/1 1 跳转至

STM32G070RB(三)Demo2:MPU使用,Privileged与Unprivileged

菜鸟
2021-01-25 19:39:14    评分

G0系列主要是用来替代F0系列的产品,所以从Cortex M0到Cortex M0+算是一个升级。Cortex M0与Cortex M0+的区别很细微,很多不注意细节的工程师可能还说不出来两者到底有什么差别。其实ARM公司当初推出Cortex M0的初衷就是替换8bit,16bit的产品,但是很多市场的变化带来嵌入式软件复杂度持续增加。所以Cortex M0被删除的一些功能在Cortex M0+上又补回来了。当然其中很多Cortex M0+新增的功能都是Optional,即可实现也可不实现。 对设计软件的工程师而言Cortex M0+相对于Cortex M0的改变有如下几点比较重要:


1,流水线从三级变成了两级,这是为功耗与中断响应速度而考虑的改进。

2,VTOR可选,Cortex M0的中断向量是不能有偏移量的,这点设计过Bootloader的程序员会感受很深。

3,MPU可选,做过RTOS的任务间隔离的程序员会感受很深。

4,两级CPU权限:privileged和unprivileged两种权限状态。 Cortex M0也有这两种权限,只是两种权限完全一样,所以等于没有。


这里做个Demo展示一下MPU与两级内核状态的使用。设计目的:有一个数组,在用户态仅仅允许读,在内核态可读可写。这种设计在Cortex M0上基本上不可能。在Cortex M0+的芯片如STM32G0F70上,利用MPU和CPU权限可以轻易做到。


首先声明数组,配置MPU, 注意用的是数组声明是绝对定位:

#define ARRAY_ADDRESS_START (0x20002000UL)

#define ARRAY_SIZE MPU_REGION_SIZE_256B

#define ARRAY_REGION_NUMBER MPU_REGION_NUMBER3


uint8_t PrivilegedReadOnlyArray[32] __attribute__((at(ARRAY_ADDRESS_START)));


void MPU_Config(void) {

MPU_Region_InitTypeDef MPU_InitStruct = {0};


HAL_MPU_Disable();


/* Configure RAM region as Region N°0, 256KB of size and R/W region */

MPU_InitStruct.Enable = MPU_REGION_ENABLE;

MPU_InitStruct.BaseAddress = EXAMPLE_RAM_ADDRESS_START;

MPU_InitStruct.Size = EXAMPLE_RAM_SIZE;

MPU_InitStruct.AccessPermission = portMPU_REGION_READ_WRITE;

MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;

MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;

MPU_InitStruct.Number = EXAMPLE_RAM_REGION_NUMBER;

MPU_InitStruct.SubRegionDisable = 0x00;

MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;


HAL_MPU_ConfigRegion(&MPU_InitStruct);


/* Configure FLASH region as REGION N°1, 1MB of size and R/W region */

MPU_InitStruct.BaseAddress = EXAMPLE_FLASH_ADDRESS_START;

MPU_InitStruct.Size = EXAMPLE_FLASH_SIZE;

MPU_InitStruct.Number = EXAMPLE_FLASH_REGION_NUMBER;


HAL_MPU_ConfigRegion(&MPU_InitStruct);


/* Configure Peripheral region as REGION N°2, 512MB of size, R/W and Execute

Never region */

MPU_InitStruct.BaseAddress = EXAMPLE_PERIPH_ADDRESS_START;

MPU_InitStruct.Size = EXAMPLE_PERIPH_SIZE;

MPU_InitStruct.Number = EXAMPLE_PERIPH_REGION_NUMBER;

MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;


HAL_MPU_ConfigRegion(&MPU_InitStruct);


/* Enable MPU (any access not covered by any enabled region will cause a fault) */

HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

}


void MPU_AccessPermConfig(void) {

MPU_Region_InitTypeDef MPU_InitStruct = {0};


HAL_MPU_Disable();


MPU_InitStruct.Enable = MPU_REGION_ENABLE;

MPU_InitStruct.BaseAddress = ARRAY_ADDRESS_START;

MPU_InitStruct.Size = ARRAY_SIZE;

MPU_InitStruct.AccessPermission = MPU_REGION_PRIV_RW_URO;

MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;

MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;

MPU_InitStruct.Number = ARRAY_REGION_NUMBER;

MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;

MPU_InitStruct.SubRegionDisable = 0x00;

MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;


HAL_MPU_ConfigRegion(&MPU_InitStruct);


/* Enable MPU (any access not covered by any enabled region will cause a fault) */

HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

}


主函数中调用上述函数以使能MPU,并且切换至用户状态:

MPU_Config();

MPU_AccessPermConfig();

__set_CONTROL(THREAD_MODE_UNPRIVILEGED | SP_MAIN);

__ISB();


接下来实验读写权限:

printf("%s %u\n", __func__, __LINE__);

printf("%s %u %02X\n", __func__, __LINE__, PrivilegedReadOnlyArray[0]);

这句没毛病,只是读。

这句就会引起HardFault, 因为在用户状态这个数组是不允许写的。

PrivilegedReadOnlyArray[0] = (uint8_t)HAL_GetTick();

printf("%s %u %02X\n", __func__, __LINE__, PrivilegedReadOnlyArray[0]);

那么要写怎么办?

答案就是使用SVC指令进入内核权限。

asm_svc_2(HAL_GetTick());

printf("%s %u %02X\n", __func__, __LINE__, PrivilegedReadOnlyArray[0]);

注意asm_svc_2这个是汇编语言写的函数,要放在另外的.s文件中,详情直接看后文的代码下载连接。

;Supervisor Call 2

ALIGN

asm_svc_2 FUNCTION

EXPORT asm_svc_2

SVC #2

bx lr

ENDP

注意要将SVC_Handler的声明改成有参数输入,生成的代码是没有参数输入的。按照AAPS的调用规约,这个函数可以有输入参数。为了求简便,没有区分SVC号,实际工程中这样就比较浪费SVC号了。

void SVC_Handler(uint32_t input){

PrivilegedReadOnlyArray[0] = (uint8_t)input;

}

这样就可以达到设计目的了。

本贴属于转载,如有侵权请联系删除




关键词: STM32G070RB     板子     芯片     内核     Nucl    

高工
2021-01-25 22:45:14    评分
2楼

感谢分享


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]