2、 编辑链接文件,导出SAFERTOS需要的符号
SAFERTOS需要使用链接文件中定义的段和变量符号,设置MPU区域保护内核代码和数据。需要的段和符号可查阅portmpu.h文件。
内核函数段名为kernel_func,内核数据段为kernel_data,GCC中,内核函数和数据通过段属性放到相应段中。
内核函数通常紧随向量表放置。内核数据被放置RAM中的某个位置,需符合MPU对齐需要。
链接文件还需导出段起始和结束符号,ROM及RAM的起始地址、结束地址及大小。
在portmpu.h中,需要下列符号
·lnkStartFlashAddress
· lnkEndFlashAddress
· lnkStartKernelFunc
· lnkEndKernelFunc
· lnkStartKernelData
· lnkEndKernelData
· lnkRAMEnd(RTOS_Demo_Debug_memory.ld)
· lnkRAMStart(RTOS_Demo_Debug_memory.ld)
3、安装SAFERTOS需要的中断和异常
K6xxF移植中需要SysTick,PendSV,SVC中断,这些函数使用CMSIS定义的实现处理,相应的处理入口位于默认的向量表位置,RTOS可以命名自己的异常处理函数。
在SAFERTOSConfig.h中,通过#defining 实现SAFERTOS异常处理替代CMSIS定义,但SAFERTOS库文件无法修改,可以重新定义startup文件向量表中的CMSIS名称。
工程代码中,向量表定义位于startup_mk64f12.c文件,在该文件中插入:
/* SAFERTOS system tick, SVC and PendSV handlers */
#define SysTick_Handler vTaskProcessSystemTickFromISR
#define SVC_Handler vSafeRTOSSVCHandler
#define PendSV_Handler vSafeRTOSPendSVHandler
与FreeRTOS不同,SAFERTOS在启动时检查向量表条目,如果它们不存在或不正确,则拒绝运行。
4、内核hook函数
SAFERTOS提供了有限的hook函数,其中最主要的是Error Hook函数。在系统检测到不可恢复错误时,进入安全的错误状态。例如检测到被破坏的TCB或堆栈溢出,将调用error hook。项目中,error hook是一个简单的无限循环(位于HookFunctions.c文件)。
5、内核任务、内核配置及启动
除hook函数的地址外,还需将堆栈及TCB的地址和大小传给空闲任务和timer任务(可选),内核任务还需MPU参数,timer 命令队列等信息。SAFERTOS通过一个专用结构将参数传给专用的API来配置内核。应用从FreeRTOS迁移到SAFERTOS最复杂的部分是配置并启动调度器。
内核配置(包含内核任务堆栈和TCB)信息,放在单独的SafeRTOSConfig.c文件中。
在内核启动的每个阶段,当配置结构被传递到内核配置函数后,内核启动,API将返回相应的错误代码。函数返回的错误代码,可以在内核include目录的projdefs.h文件中查找含义。该文件列出了所有错误代码信息。
对于示例应用,还需使用一个“备用”MPU区域,建立一个全局MPU区域,以访问板载LED的GPIO,这样我们就不必提升相应的任务权限以使用GPIO资源。在SafeRTOSConfig.c中,通过xMPUConfigureGlobal Region()调用设置了“全局”MPU区域,允许读写GPIO寄存器 (实际上,该区域包含所有的外设地址空间)。
6、应用任务TCB和堆栈
为每个应用任务设置堆栈和TCB,填充其它任务参数。
在FreeRTOS中,创建任务所需的6个参数直接传递给xTaskCreate()函数,函数将返回新创建任务的句柄或pdFALL错误信息。在SAFERTOS中,任务参数更多,通过一个指向移植特定的xTaskParameters结构参数,传递给xTaskCreate(),第二个参数接收新创建任务的句柄,函数将返回pdPASS或错误码。
在示例中可以看到,FreeRTOS与SAFERTOS的任务名称和参数各不相同。SAFERTOS任务参数中还有每个任务的特权级别和MPU区域设置。第一步我们使所有任务都运行在特权级别,所以目前不需要额外的MPU参数,这些参数设置为0/null。
7、更新调用的API
针对每个API调用,检查并修改SAFERTOS对应的函数名称及返回值。
8、类型更新
除API函数名称不同外,移植层定义的类型名也有区别。可以借助IDE的search&replace功能替换。
二、修改应用任务为非特权模式
我们已经通过一个“全局MPU域”允许所有任务访问使用的GPIO寄存器。任务被转换为非特权模式后,还需要有哪些访问权限?
将某个任务标记为unprivileged,然后加载并运行应用程序,如果应用最终进入MPU fault处理程序中。可以检查调试器的寄存器以识别故障地址,可以尝试使用调试器强制从处理程序返回,快速识别导致MPU故障的指令。
示例中,每个LED任务需要访问共享的“brightness request”数组,处理访问该数据的互斥量,互斥量buffer仅由内核代码访问,任务如何实现访问共享RAM region?
在链接文件中创建一个命名段,导出开始地址和大小符号,添加放置属性来指定函数或变量的定位,在GCC中如下:
__attribute__ ( ( section ( “section_name”)))
最后,使用链接文件导出的段符号来定义MPU域,添加到任务MPU参数中,以获得访问权限。注意,导出的linker符号为地址,在声明其为extern变量后,我们可以获取符号的地址,或者将其声明为数组类型,数组名将是其地址。