| 4.Demo2: MPU使用,Privileged与Unprivileged 
 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, 注意用的是数组声明是绝对定位:
 
 主函数中调用上述函数以使能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_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这个是汇编语言写的函数, 要放在另外的.s文件中,详情直接看后文的代码下载连接.asm_svc_2(HAL_GetTick());                
                printf("%s %u %02X\n", __func__, __LINE__, PrivilegedReadOnlyArray[0]);
 
 注意要将SVC_Handler的声明改成有参数输入, 生成的代码是没有参数输入的. 按照AAPS的调用规约, 这个函数可以有输入参数. 为了求简便, 没有区分SVC号, 实际工程中这样就比较浪费SVC号了.;Supervisor Call 2
        ALIGN
asm_svc_2 FUNCTION    
        EXPORT asm_svc_2
        SVC #2
        bx lr        
        ENDP
 
 |