一种嵌入式双操作系统架构中外设资源动态迁移的研究与实现*
2022-01-24崔振礼
崔振礼,罗 宇
(国防科技大学计算机学院,湖南 长沙 410073)
1 引言
随着大规模集成电路的发展,嵌入式设备的性能越来越强,功能越来越丰富,应用场景也越来越复杂,在以智能制造和物联网为主题的“工业4.0”概念被提出后,各应用领域对嵌入式操作系统的要求也越来越高。复杂的应用场景要求嵌入式操作系统在具有丰富功能性的同时支持实时性,但是在单操作系统中,丰富的功能性会不可避免地导致较多的运行任务和复杂的调度算法,难以达到实时性要求,而为了保证实时性,系统通常会最小化任务数量,尽量减少一些不必要的功能,所以单操作系统难以同时兼具两者[1]。双操作系统架构正好可以满足这一要求。在双操作系统架构中,通用操作系统GPOS(General-Purpose Operating System)和实时操作系统RTOS(Real-Time Operating System)部署在同一嵌入式平台,其中GPOS 提供复杂任务开发功能,满足系统的功能需求,RTOS 提供精确的实时控制功能,满足系统的实时性需求[2]。
由于双操作系统运行于同一硬件平台,如何合理地配置有限的外设资源是双操作系统架构面临的重要问题。通过虚拟化技术,双操作系统可以实现外设资源共享,但是虚拟化软件层会带来额外的性能开销,对于嵌入式设备来说,这种开销是不容忽视的。而在非虚拟化条件下,操作系统实例互相隔离,独占私有的外设资源,既可以充分发挥性能,又可以提升信息安全性[3]。华为海思Hi3559AV100 SOC的双操作系统架构采用非虚拟化方式,系统分别运行于不同的CPU,外设资源按照功能需求分成2部分由双操作系统分别单独占有,不能共享使用。采用上述方式时,外设资源配置方案在操作系统源代码中被静态指定,系统部署之后便无法调整。搭载了该双操作系统架构的嵌入式设备在运行时,如果某端系统发生了故障,其上运行的与外设交互的任务便无法继续,从而影响到整个系统的正常运行,轻则造成设备功能失效、失控或损坏,重则影响用户的健康安全。
本文基于上述问题提出一种外设资源在双操作系统运行期间动态迁移的解决方案。在某端系统出现故障之后,把配置到该端的外设资源迁移到另一端系统,启动相应的用户处理程序,控制外设继续工作,最大限度地保证用户和设备的安全,避免损失。最后在EVM3559A嵌入式开发平台上对所提方案的可行性和可靠性进行实验验证。
2 Hi3559AV100双操作系统架构简介
2.1 Hi3559AV100 SOC简介
Hi3559AV100是华为海思面向智能网络监控摄像头、3D/VR摄像头、视频会议终端、智能工业机器人、智能无人机、实时护理系统终端等领域设计的专业8K ultra HD mobile camera SOC,它提供了8K30/4K120广播级图像质量的数字视频录制,支持4K sensor输入,H.265编码输出或影视级的raw数据输出,多路全景硬件拼接,并集成了高性能图像信号处理器ISP(Image Signal Processor),为用户提供了卓越的图像处理能力;提供高效且丰富的计算资源,集成了1个由双核A53和双核A73组成的四核CPU、1个单核A53、1个M7、1个GPU、1个四核数字信号处理器DSP(Digital Signal Processor)和2个神经网络推理引擎NNIE(Neural Network Inference Engine),可根据应用场景制定合适的业务部署方案;采用先进的12 nm低功耗工艺和小型化封装,在缩小产品体积的同时减少功耗[4];支持单操作系统和多操作系统(最多4个)部署方案,以满足不同的业务需求。
2.2 双操作系统架构
Hi3559AV100提供Linux+LiteOS的双操作系统部署方案,Linux作为GPOS运行非实时任务,部署在双核A53和双核A73组成的四核CPU上,LiteOS作为RTOS运行实时任务,部署在单核A53上,双操作系统之间共享内存,并通过共用系统总线访问外设资源。Linux作为主流嵌入式通用操作系统,其使用广泛,性能高效,运行稳定,功能强大,对硬件平台适配性好,对外围设备兼容性强,由于其开源特性,可根据具体需求进行裁剪,还可根据特殊需求实现定制化。LiteOS是华为针对物联网领域推出的轻量级物联网实时操作系统,具备轻量级(最小内核尺寸仅为6 KB)、高实时、高稳定性、低功耗、互联互通、组件丰富和快速开发等关键能力[5]。Hi3559AV100双操作系统架构如图1所示。
Figure 1 Hi3559AV100 dual operating systems architecture图1 Hi3559AV100双操作系统架构
3 外设资源动态迁移
外设资源动态迁移的实现基于外设中断与CPU核的动态绑定,即改变外设中断的响应CPU,由该CPU上部署的操作系统进行处理。
3.1 Hi3559AV100外设中断处理流程
在Hi3559AV100中,外设的中断由通用中断控制器GIC(Generic Interrupt Controller)负责管理,GIC是联系外设中断和CPU的桥梁,也是各CPU之间中断互联的通道。GIC共支持3种中断类型[6]:
(1)软中断SGI(Software-Generated Interrupt):SGI为软件可以触发的中断,统一编号为0~15,用于各个CPU核之间的通信;
(2)私有外设中断PPI(Private Peripheral Interrupt):PPI为每个CPU核的私有外设中断,统一编号为 16~31。例如,每个CPU核的Local Timer产生的中断就是通过 PPI 发送给CPU核的;
(3)外设中断SPI(Shared Peripheral Interrupt):SPI 是外部设备产生的中断,所有CPU核共用,统一编号为32~1 019,如Global Timer、UART和GPIO产生的中断。
由于本文研究内容只与SPI相关,所以忽略SGI和PPI。Hi3559AV100使用的是GIC-400,最多支持8个CPU接口,其与外设、CPU连接情况如图2所示。
Figure 2 GIC connection with peripherals and CPU图2 GIC与外设、CPU的连接
由图2可知,CPU核与GIC之间有2条连接线,分别是快速中断请求线FIQ(Fast Interrupt Request)与中断请求线IRQ(Interrupt ReQuest),FIQ用于安全模式,IRQ用于非安全模式[6]。当外设中断发送至GIC时,GIC会把收集来的中断缓存,然后从中选择优先级最高的中断请求发送至该外设中断绑定的CPU,CPU接收到外设中断信号后,读GIC相应的寄存器得到中断号,然后开始处理中断。
3.2 GIC通用中断控制器工作原理
GIC内部由2个部件组成:分发器(Distribut- or)和 CPU 接口(CPU Interface)。
分发器的主要作用是检测各个中断源的状态,控制各个中断源的行为,分发各个中断源产生的中断事件到设定的CPU接口上。虽然分发器可以管理多个中断源,但是它总是把优先级最高的中断请求送往CPU接口。分发器对中断的控制包括[6]:
(1)中断使能或禁用控制。分发器对中断的控制分成2个级别,一个级别是对全局中断的控制,一旦禁能了全局中断,那么任何中断源产生的中断事件都不会被传递到CPU接口;另外一个级别是针对各个中断源进行控制,禁用某一个中断源会导致该中断事件不会分发到CPU接口,但不影响其他中断源产生的中断事件的分发。
(2)将当前优先级最高的中断事件分发到预先绑定的CPU接口。
(3)优先级控制。
(4)中断属性设定,例如是电平触发还是边沿触发。
CPU接口是GIC与CPU核之间的连接接口,它的主要作用有[6]:
(1)使能或者禁能CPU接口向连接的CPU核提交中断事件。
(2)应答中断,改变中断的状态。CPU核会向CPU接口应答中断,中断一旦被应答,分发器就会把该中断的状态从等待状态修改成活跃状态。
(3)接收中断处理完毕的通知。
(4)设定优先级掩码。通过优先级掩码可以屏蔽掉一些优先级比较低的中断,这些中断不会通知到CPU核。
(5)设定中断抢占的策略。
(6)在多个中断事件同时到来的时候,选择一个优先级最高的中断发送到CPU接口。
由分发器和CPU接口的功能可知,中断分发到哪个CPU是分发器根据预先绑定的CPU接口决定的,而外设中断与CPU接口的绑定是通过向GIC分发器部件的中断分发目的寄存器GICD_ITARGETSRn写CPU接口掩码实现的[6]。在分发器分发中断事件时,首先根据GICD_ITARGETSRn寄存器找到该中断绑定的CPU接口掩码,然后根据掩码把中断分发到相应的接口。GIC分发器的初始化是在LiteOS启动过程中完成的,其初始化代码[7]如下所示:
#define WRITE_UINT32(Val,Addr)(*((volatile UINT32 *)((UINT32)(Addr)))=(Val))
void platform_gic_dist_init(void *base,UINT32irq_start){
…
//256 interrupts at most
UINT32max_irq=256;
/*irq_map:An array filled with CPU mask,indicates which CPU the IRQ is sent to*/
UINT32 *irq_dist=(UINT32 *)irq_map;
…
//write CPU mask to GIC related register
for(i=32;i WRITE_UINT32(*irq_dist,base+ARM_GIC_DIST_TARGET+i); irq_dist++; } } 由代码可以看出,GIC设置为最多支持256个中断;irq_map定义为数组,数组元素为外设中断绑定的CPU接口的掩码(8 bit,无符号字符类型),数组元素的序号与某个外设中断一一对应,例如数组序号8对应UART2的中断,irq_map[8]的值表示UART2的中断绑定的CPU接口的掩码;从偏移量32开始(0~15为软中断,16~31为私有外设中断),外设中断绑定的CPU接口的掩码被写到了GIC分发器部件的GICD_ITARGETSRn寄存器(地址为base+ARM_GIC_DIST_TARGET)中,且一次循环写4个掩码值。 CPU接口的掩码及其与CPU核的连接关系如表1所示。 Table 1 Mask of the CPU interface and its connection to the CPU core irq_map数组[7]定义如下所示: #define TO_A53MP0 1<<0x00 #define TO_A53MP1 1<<0x01 #define TO_A73MP0 1<<0x02 #define TO_A73MP1 1<<0x03 #define TO_A53UP_ 1<<0x04 const UCHARirq_map[256-32]={ … /*UART0,UART1,UART2*/ TO_A53MP0,TO_A53UP_,TO_A53MP0, /*SPI0*/ TO_A53MP0 … } 在源代码中先为CPU核掩码作宏定义。由于Linux运行在双核A53和双核A73组成的四核CPU上,LiteOS运行在单核A53上,所以当irq_map数组元素值为TO_A53MP0、TO_A53MP1、TO_A53MP2或者TO_A53MP3时表示对应的中断由Linux负责处理,数组元素为TO_A53UP_时表示对应的中断由LiteOS负责处理。 由GIC的工作原理可知,如果外设中断在系统运行期间可以动态绑定到不同的CPU接口,就可以改变接收该中断CPU核,从而实现外设资源在系统间动态迁移的目的。而外设中断与CPU接口的绑定关系是由GIC分发器的GICD_ITARGETSRn寄存器中存放的CPU接口掩码确定的[8],所以可以通过修改此寄存器中相应地址的值来改变外设中断绑定的CPU接口。 以外设UART2为例,把外设资源动态迁移实现思路分为3步: Step1确定GIC中GICD_ITARGETSRn寄存器的地址; Step2根据UART2中断在irq_map数组中对应的序号和Step 1中找到的地址,确定存放UART2中断绑定的CPU接口的掩码地址; Step3把Step 2确定的地址中的值修改为要绑定的CPU接口的掩码。 由于LiteOS未使用虚拟地址机制,所以可通过物理地址直接访问GICD_ITARGETSRn寄存器的内容。通过阅读LiteOS系统源码得到系统基地址为0x1F101000,GICD_ITARGETSRn地址偏移量为0x800,所以Step 1中GICD_ITARGETSRn寄存器的地址为0x1F101800。由于UART2中断在irq_map数组中对应的序号为8,所以存放UART2中断绑定的CPU接口的掩码地址为0x1F101800+32+8,此地址处的默认值为0x10,即CPU接口4的掩码,然后将该地址处的值修改为双核A53或者双核A73的核所连接的CPU接口的掩码,即可实现UART2从LiteOS到Linux的动态迁移。LiteOS应用程序中实现该功能的代码如下(以迁移到双核A53 的核0为例)所示: #define BASE 0x1F101000 #define GICD_ITARGETSRn 0x800 #define WRITE_UCHAR(Val,Addr) (*((volatile UCHAR*)((UINT32)(Addr)))=(Val)) static voiduart2_tomp(){ … WRITE_UCHAR(BASE+ GICD_ITARGETSRn+40,TO_A53MP0); … } Linux使用了虚拟地址机制,所以在Linux中访问寄存器要先把它的物理地址转换成相应的虚拟地址。Linux提供了物理内存的镜像设备文件/dev/mem,用于访问物理内存,由于ARM架构采用I/O端口与物理内存统一编址方式,所以可以通过访问内存地址的方式访问I/O设备。Linux应用程序中实现该功能的代码如下所示: #define BASE 0x1F101000 #define GICD_ITARGETSRn 0x800 static voiduart2_tomp(){ … intfd=open(“/dev/mem”,O_RDWR|O_SYNC); void *virt_base=mmap(NULL,1000, PROT_READ|PROT_WRITE,MAP_SHARED,fd,BASE); *((UCHAR*)( BASE+GICD_ITARGETSRn+40))=TO_A53MP0; … } 本文通过实验验证本文所提方案是否可行,迁移过程是否可靠。实验采用的硬件平台是广州英码信息科技有限公司生产的搭载了Hi3559AV100 SOC的EVM3559A嵌入式开发板,该开发板主要硬件配置信息如表2所示。 Table 2 Main hardware configuration information on EVM3559A 测试程序分2部分,分别运行于LiteOS和Linux中,模拟单核A53故障或者LiteOS 宕机导致其上运行的任务无法继续执行时,外设资源动态迁移到Linux中。通过HeartBeat原理[9]实现Linux对LiteOS的故障检测,当Linux端测试程序在指定时间内未收到LiteOS端测试程序定时通过处理器间通信IPCM(Internal Processor Communication Message)发送的心跳信息时,便认为LiteOS发生了故障,启动外设资源迁移程序及相应的用户处理程序,进行非实时降级处理,继续控制外设运行。 在本实验中以外设UART2为测试设备。LiteOS端测试程序每隔1 s向Linux发送心跳信息,在程序运行20 s后停止发送,Linux端测试程序等待心跳信息超时时间设置为2 s。为了方便观察实验结果,两端测试程序在接收到UART2数据后输出至调试窗口。测试程序总体流程如图3所示。 Figure 3 Overall flow chart of the test program图3 测试程序总体流程 在2台电脑上分别安装USB转串口驱动,其中1台作为测试电脑,安装由C#编写的串口数据发送程序,该程序每隔1 s向串口发送“‘This is a test statement numbered’+消息序号”的测试数据,其中消息序号从0开始,依次递增1,另一台作为调试电脑,安装串口调试工具。测试电脑通过USB转串口线连接到EVM3559A的UART2接口,调试电脑通过USB线连接至EVM3559A的micro USB调试串口。实验环境连接情况如图4所示。 Figure 4 Connection of experimental environment图4 实验环境连接情况 测试程序均部署为开机自启,后台运行。EVM3559A上电后,双操作系统启动,测试程序自动运行,UART2中断默认由LiteOS负责处理。 此时LiteOS端测试程序等待接收UART2数据,并定期向Linux发送心跳消息。打开测试电脑上的串口数据发送程序,开始发送测试数据,程序运行情况如图5所示。 Figure 5 Running of the serial data sending program图5 串口数据发送程序运行情况 LiteOS端测试程序开始接收到UART2数据,并把数据打印输出至调试窗口,接收情况如图6所示。 Figure 6 Test program receiving UART2 data in LiteOS 图6 LiteOS端测试程序接收UART2数据 LiteOS端测试程序运行20 s之后,停止发送心跳信息,2 s后Linux端测试程序等待接收心跳信息超时,启动UART2迁移程序,开始接收UART2串口数据,并打印输出至调试窗口。接收情况如图7所示。 Figure 7 Test program receiving UART2 data in Linux 图7 Linux端测试程序接收UART2数据 由运行结果可知,Linux端测试程序可以接收到UART2数据,实现了UART2在双操作系统运行期间的动态迁移,满足可行性要求。此外,通过改变串口数据传输参数和传输频率进行了多次实验,实验结果表明,在迁移前后,2端测试程序接收到的串口测试数据的序号是连续的,未发生数据丢失,具有高可靠性。 双操作系统架构是嵌入式系统的发展趋势,如何在系统间配置外设资源是该架构面临的重要问题。本文基于华为海思Hi3559AV100 SOC双操作系统架构,研究其外设资源在系统间的配置原理,分析此种外设配置方式在应用中存在的问题,提出一种利用外设中断动态绑定CPU核的方式来实现外设资源在系统运行期间动态迁移的解决方案,并利用EVM3559A嵌入式开发平台对该方案的可行性和可靠性进行了实验验证。 此外,该解决方案还可应用于外设资源的灵活部署、节能省耗、负载动态平衡等场景。3.3 具体实现
4 实验验证
4.1 测试程序设计
4.2 实验环境搭建
4.3 实验过程及结果
5 结束语