




已阅读5页,还剩39页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Acoral移植何为操作系统移植?操作系统移植就是将操作系统在某一特定的cpu或soc芯片上运行起来,这一部分大部分是和硬件相关的,需要针对具体cpu或芯片进行有区别的代码编写。acoral的移植我们称为PAL,有两个部分,一是项目移植(PRJ),一是硬件移植(HAL)。项目移植:不同的开发平台(比如Window下的ADS,KEIL,IAR,Linux下的MAKFILE),编译器,汇编器不一样,所以c语言代码可能有点兼容性问题(但基本兼容,只是一些扩展性能不兼容,比如inline,增加段相关操作),汇编就更加不一样了,因此需要针对这些开发平台编写专门的汇编代码,同时实现一很小量的c语言的扩展属性。硬件移植(HAL):硬件移植就是针对不同目标板编写相关代码,不同的开发板,资源不一样,具体体现在不同架构处理器,指令集不一样,相同架构处理器,不同系列,寄存器资源不一样。项目移植和硬件移植两者,硬件移植是重点,因为项目相关性,主要是一些规则不一样,比如GNU的汇编标号要加”:”,而ADS的汇编标号不用加”:”,GNU的变量导出是”.global”,而ADS中是”EXPORT”.因此只要实现硬件移植,只需修改部分规则即完成了项目移植。下面我们就来详细说下硬件移植。我们知道,我们在做移植时,往往有个疑问,那就是我该实现哪些东西?操作系统与硬件相关的往往体现如下几个方方面。1)启动 这个大家肯定很清楚,不同的cpu肯定不一样,2)中断系统 大家可以很容易知道,不同cpu的芯片的中断机制肯定不同,比如中断优先级,中断屏蔽,开关中断,时钟,肯定要对这部分进行针对处理。3) 任务切换这个是操作系统的灵魂,有了这个,系统才能称的上是操作系统。这部分也是和硬件很相关的,因为任务切换的主体是环境,而环境就是cpu的各种寄存器,比如x86是ax,bx等,而arm是r0,r1等,这肯定是和具体硬件严重相关。4)内存比如内存控制器初始化,MMU映射,内存的大小,地址。 有了上面其实还是不够的,上面只是从功能和需求出发,能不能有一个模板来让移植的人不再“孤单”,踏着前人的痕迹悠闲前进呢?有,那就是将这些与硬件相关的功能部件抽象化,内核层提出需求,移植的时候实现提供这些需求品就可以了,这种方式的专业术语叫硬件抽象,这一个层可以叫做硬件抽象层HAL。这种硬件抽象对用户而言,很透明很有头绪,因此对于与硬件相关的移植部分,我们也采用hal框架,将需要移植的部分抽象成接口,用户只要根据具体平台实现这些接口即可在平台上运行acoral. Acoral的移植hal层的接口按功能可分为如下几类:1.启动接口 必须要有一个名为start的文件,这个文件包含启动代码,做一些简单的初始化后就转到c语言入口函数acoral_start.2.内存相关硬件抽象层接口:HAL_HEAP_START:堆内存起始地址:我们知道不同平台,内存的大小及地址分配情况基本上不一样,因此在移植的时候需要向内核层的内存模块告知这些信息。这个接口是一个变量。HAL_HEAP_END:堆内存起始地址同上HAL_MEM_INIT:内存初始化 这主要对相关内存控制器进行相关初始化,如果启动时内存的初始化不用修改,则在此可以不做处理。其接口形式HAL_MEM_INIT(),无返回值。3.线程相关硬件抽象层接口HAL_START_OS:操作系统线程开始运行系统最开始的线程切入接口,只有一个参数,是要切换的线程的堆栈指针,它往往等于HAL_SWITCH_TO。接口形式:HAL_START_OS(&prev-stack),参数为线程堆栈指针变量的地址,无返回。HAL_SWITCH_TO:线程切入接口 线程环境下切入到指定线程接口,只有一个参数,是要切换的线程的堆栈指针。接口形式:HAL_SWITCH_TO(&prev-stack),参数为线程堆栈指针变量的地址,无返回。HAL_CONTEXT_SWITCH:线程切换接口线程环境下切换接口。接口形式:HAL_CONTEXT_SWITCH(&prev-stack,&next-stack)两个参数,就是要切换的两个线程的堆栈指针变量的地址。HAL_INTR_SWITCH_TO:中断中线程切入接口 在中断环境下切入到指定线程的接口, 调用方式,HAL_INTR_SWITCH_TO(&thread-tack),只有一个参数,是要切换的线程的堆栈指针变量地址。HAL_INTR_CTX_SWITCH:中断中线程切换接口在中断环境下线程切换接口,HAL_INTR_CTX_SWITCH(&prev-stack,&next-stack),两个参数,就是要切换的两个线程的堆栈指针变量的地址。 也许你注意到,为啥要有中断和线程环境之分,这是因为在中断环境下,中断硬件系统可能已经保存了部分线程的环境,因此切换需做特殊处理,当然有些平台 HAL_SWITCH_TO,HAL_INTR_SWITCH_TO就是一样的,比如stm3210,他们的实现就是一样的。HAL_STACK_INIT:线程堆栈初始化,线程创建时模拟线程的环境。接口形式:HAL_STACK_INIT(stack,route,exit,args),四个参数1) 堆栈指针变量地址2) 线程执行函数3) 线程退出函数4) 线程参数无返回值HAL_SCHED_INIT:调度初始化,初始化和调度相关的标识,比如是否需要调度标志need_sched,调度锁。接口形式:HAL_SCHED_INIT(),无参数,无返回。HAL_SCHED_BRIDGE:调度中转桥,这个接口时为了中断硬件抽象层接口:HAL_INTR_ENTRY:硬件相关的中断入口函数,所有要交给内核层中断系统处理的中断都会进入此函数,此函进行简单处理后读取中断向量号,然后调用内核层的中断处理函数,内核层函数返回后,要调用中断退出操作函数acoral_intr_exit(),然后中断退出。接口形式:HAL_INTR_ENTRY,无参数,无返回值。HAL_INTR_INIT:中断初始化中断初始化,顾名思义就是对中断进行初始化,一般会牵涉到中断模式,中断优先级,中断屏蔽等寄存器的初始化,及中断的各种操作函数的初始化,比如中断响应,中断屏蔽,中断开启接口,比如acoral_set_intr_ack(i,hal_intr_ack),acoral_set_intr_mask(i,hal_intr_mask)等。其接口形式HAL_INTR_INIT(),无返回值。HAL_INTR_SPECIAL:这个是在初始化后要调用的接口,有些平台需要在初始化后执行一些特殊化的初始化操作。比如HAL_INTR_INIT初始化进行的是通用操作,而HAL_INTR_SPECIAL是特殊操作,一般不用实现。其接口形式:HAL_INTR_SPECAIL(),无参数,无返回值。HAL_INTR_SET_ENTRY: 设置内核层中断入口函数,接口形式HAL_INTR_SET_ENTRY(isr),只有一个参数,为函数指针,无返回值。这个设置好后,hal层的中断入口函数HAL_INTR_ENTRY进行简单处理后会调用此函数。HAL_INTR_ENABLE:HAL_INTR_DISABLE: 所有中断开启禁止接口,用于禁止和开启所有中断,这种实现有几种方式.1) 直接使用相关cpu的状态寄存器实现中断开关,比如arm9的cpsr的irq,firq位就可以用来开关所有中断。2) 使用中断屏蔽寄存器,有些soc芯片的中断屏蔽寄存器带有屏蔽所有中断的功能,即使没有这种功能,一个一个屏蔽所有中断位也是可以实现的。一般使用第一种,这种方式简单,语句少,效率高,对于没有第一种支持的,可以考虑用第二种。接口形式:HAL_INTR_ENABLE():HAL_INTR_DISABLE()都无参数,无返回值。HAL_INTR_ATTACH:这个针对实时中断特殊设计的,该接口的功能是直接将中断处理函数放到响应的中断向量表中,这样中断来了后,其处理函数直接被调用,而不是经过HAL_INTR_ENTRY-intr_c_entry-中断处理函数,这个接口一般为空,因为只有向量模式的中断才具备这种实时特性,因而只有在支持向量模式中断的处理器,且用户需要好的中断响应时,才需实现这个接口。HAL_INTR_DISABLE_SAVE:带保存处理器状态的关中断接口,在使用HAL_INTR_DISABLE相同方式关中断前,会保存当前处理器的状态,最后会返回当前的此处理器状态,至少是包含了中断状态。接口形式:HAL_INTR_DISABLE_SAVE(),会返回一个acoral_sr变量的值HAL_INTR_RESTORE:恢复传入的参数的值来恢复处理器状态,至少会包含中断状态。接口形式:HAL_INTR_RESTORE(sr),一个参数,即要恢复的处理器状态,无返回值。HAL_INTR_MAX,HAL_INTR_MIN,HAL_TRANSLATE_VECTOR:最小中断向量号,最大中断向量号。这个最小中断向量号不一定为0,我们说的这些中断,都是需要交给内核层中断处理的中断,对于内核层不处理的中断就不定义,直接在HAL层处理。比中断向量0为数据异常中断,这个中断明显不交给内核层来管理,直接在底层处理,故最小中断向量号从1开始,这个对应的内核层的中断向量号为0,因此需要真正中断号转换内核层中断号的接口,这个接口就是HAL_TRANSLATE_VECTOR。HAL_GET_INTR_NESTING: 获取中断嵌套状态。我们知道在只有在最后一层中断返回时才可以进行调度,但是由于允许中断嵌套,中断返回时就必须判断是否是最后一层中断返回。因此可以通过一个变量来存取中断嵌套数。HAL_INTR_NESTING_DEC: 减少中断嵌套数HAL_INTR_NESTING_INC: 获取中断嵌套状态时钟硬件抽象层接口:HAL_TICKS_INTR:时钟中断向量号,变量。HAL_TICKS_INIT():Ticks滴答时钟初始化,我们知道在操作系统中,滴答时钟是非常重要的,也是调度的一个激发源,它是一个中断,每隔一定时间就会触发一次中断,用来计时的,线程的延时函数就是要利用ticks时钟,主要初始化滴答时钟中断相关的寄存器,同时可能需要重新给此中断赋值操作函数。硬件板子抽象层接口:HAL_BOARD_INIT: 板子初始化,主要对cpu,i/o口,一些设备的初始化,当然这里可以不进行相关初始化,可以在驱动程序里对设别进行初始化,不过有些状态不定的设备会带来一些问题,那么在这里初始化时比较好的。接口形式:HAL_BOARD_INIT()无参数,无返回值。多核(CMP)硬件抽象层接口这个只有在用户想在多核芯片支持多核SMP运行才需要实现,对于单核不需要实现,对于多核单独运行的应用也不用实现。HAL_CORE_CPU_INIT: 主核初始化,就是对主核私有的一些数据也行进行初始化,其实这个函数基本上是空的,因为主核在进入acoral_start里,调用了各种函数,这些函数就已经初始化了主核的大部分甚至所有数据。HAL_FOLLOW_CPU_INIT: 次核初始化,主要对次核的私有数据初始化,比如有些私有中断寄存器等,比如pb11mpcore中断,0-32的中断相关寄存器就是私有的,必须自己的核心才能访问,同时需要初始化自己的各种状态的堆栈。接口形式:HAL_FOLLOW_CPU_INIT,无参数,无返回值。HAL_CPU_IS_ACTIVE: 某一cpu是否激活,主要是重启时用的,对于第一次启动,除主核外都没有激活。何为激活呢?激活就是cpu核已经初始化了,在执行内核映像代码。接口形式:HAL_CPU_IS_ACTIVE(cpu),参数为整形,表示获取哪个cpu的激活状态。HAL_PREPARE_CPUS: 主核为激活次核做准备的接口,主要有:为次核准备开始代码,有些是将代码拷贝到指定地址(比如blackfin51),有些是在寄存器中指定开始代码地址(PB11MPCORE)。同时可能要为次核分配临时堆栈,这个可以作为参数传递给次核,次核将自己的堆栈指向分配好的地址即可。接口形式:HAL_PREPARE_CPUS(),无参数,无返回。HAL_START_CPU:激活某一cpu,让其运行:让cpu运行有两种形式:1. 次核没有执行过任何代码,激活就是让其执行指定代码。2. 次核是执行过代码的,只不过开始时处于一种过渡状态,要么是空循环状态,要么是一种特殊的类似standby,Sleep状态。激活一般是通过核间中断来实现的,又或者是又相关启动寄存器。接口形式:HAL_START_CPU(cpu),一个参数,要激活的cpu编号,无返回值。HAL_IPI_SEND:向指定cpu发送核间中断,关键看,这个是核间通信的基础,,接口形式:HAL_IPI_SEND (cpu,vector),两个参数,第一个为目标cpu,第二个参数为核间中断向量号,无返回值。HAL_IPI_SEND_CPUS:向某一cpu组发送核间中断,这个实现与平台很有关系,对于不支持向多个核发送相同中断的,可以使用for循环调用HAL_IPI_SEND实现。接口形式:HAL_IPI_SEND_CPUS (cpulist,vector),两个参数,第一个为目标cpu位图,每位代表一个cpu,第二个参数为核间中断向量号,无返回值。HAL_IPI_SEND_ALL:向所有核心发送中断,这个实现与平台很有关系,对于不支持向所有核发送相同中断的,可以使用for循环调用HAL_IPI_SEND实现。接口形式:HAL_IPI_SEND_ALL (vector),一个参数,为核间中断向量号,无返回值。HAL_WAIT_ACK:等待次核初始化响应,这个可以用变量实现,也可以用初始化为锁状态的自旋锁实现。接口形式:HAL_WAIT_ACK ()无参数,无返回值。HAL_CMP_ACK:次核响应主核,对应HAL_WAIT_ACK。接口形式:HAL_CMP_ACK (),无参数,无返回值。自旋锁实现:这部分只有多核情况下才需实现。HAL_SPIN_LOCK:抢占自旋锁HAL_SPIN_UNLOCK:释放自旋锁。HAL_SPIN_TRYLOCK:尝试抢占自旋锁,如果失败则立刻返回。原子操作:HAL_ATOMIC_INIT:原子量初始化。HAL_ATOMIC_READ:原子读操作HAL_ATOMIC_SET:原子赋值操作HAL_ATOMIC_INC:原子递增操作HAL_ATOMIC_ADD原子加法操作HAL_ATOMIC_DEC原子递减操作HAL_ATOMIC_SUB原子减操作上面是按功能分的。按是否通用分,又可以分为通用HAL接口,专用HAL接口:通用HAL接口,既然是通用,那就是说,用户可不用实现,它是和硬件无直接联系的HAL接口,但不是所有针对所有cpu芯片都要使用这些接口,只是说所有平台可以使用,但在有些soc芯片的配合下,可以有更有效率的实现,这时就可以不适用通用HAL层的实现,针对相关芯片进行优化实现。通用HAL层接口如下:中断嵌套相关的:HAL_INTR_NESTING_INITHAL_GET_INTR_NESTINGHAL_INTR_NESTING_DECHAL_INTR_NESTING_INCHAL_ENTER_CRITICALHAL_EXIT_CRITICALHAL_INTR_SPECIALHAL_INTR_ATTACH和调度相关的:HAL_START_OSHAL_SCHED_BRIDGEHAL_INTR_EXIT_BRIDGE原子操作:HAL_ATOMIC_INITHAL_ATOMIC_READHAL_ATOMIC_SETHAL_ATOMIC_INCHAL_ATOMIC_ADDHAL_ATOMIC_DECHAL_ATOMIC_SUB具体实现如下:#define HAL_START_OS(stack) HAL_SWITCH_TO(stack)#define HAL_INTR_NESTING_INIT() hal_intr_nesting_init_comm()#define HAL_GET_INTR_NESTING() hal_get_intr_nesting_comm()#define HAL_INTR_NESTING_DEC() hal_intr_nesting_dec_comm()#define HAL_INTR_NESTING_INC() hal_intr_nesting_inc_comm()#define HAL_ENTER_CRITICAL() (cpu_sr = HAL_INTR_DISABLE_SAVE()#define HAL_EXIT_CRITICAL() (HAL_INTR_RESTORE(cpu_sr)#define HAL_INTR_SPECIAL()#define HAL_INTR_ATTACH(vecotr,isr)#define HAL_SCHED_BRIDGE() hal_sched_bridge_comm()#define HAL_INTR_EXIT_BRIDGE() hal_intr_exit_bridge_comm()#define HAL_ATOMIC_INIT(v) #define HAL_ATOMIC_READ(v) (v)-val)#define HAL_ATOMIC_SET(i,v) (v)-val) = (i)#define HAL_ATOMIC_INC(v) hal_atomic_add_comm(1,v)#define HAL_ATOMIC_DEC(v) hal_atomic_sub_comm(1,v)#define HAL_ATOMIC_ADD(i,v) hal_atomic_add_comm(i,v)#define HAL_ATOMIC_SUB(i,v) hal_atomic_sub_comm(i,v)它们的实现如下:/*= *initialize the nesting *中断嵌套初始化*=*/void hal_intr_nesting_init_comm()acoral_u32 i;for(i=0;i0)intr_nestingcpu-;/*= *Incrise the nesting *增加中断嵌套数*=*/void hal_intr_nesting_inc_comm() intr_nestingacoral_current_cpu+;void hal_sched_bridge_comm() acoral_sr cpu_sr;HAL_ENTER_CRITICAL();acoral_real_sched();HAL_EXIT_CRITICAL();void hal_intr_exit_bridge_comm() acoral_sr cpu_sr;HAL_ENTER_CRITICAL();acoral_real_intr_sched();HAL_EXIT_CRITICAL();void hal_atomic_add_comm(int i, acoral_atomic_t *v)acoral_sr sr;sr=HAL_INTR_DISABLE_SAVE();v-val=v-val+i;HAL_INTR_RESTORE(sr);void hal_atomic_sub_comm(int i, acoral_atomic_t *v)acoral_sr sr;sr=HAL_INTR_DISABLE_SAVE();v-val=v-val-i;HAL_INTR_RESTORE(sr);对于单核必须要实现的接口如下:HAL_STACK_INITHAL_CONTEX_SWITCHHAL_SWITCH_TOHAL_INTR_CTX_SWITCHHAL_INTR_SWITCH_TOHAL_INTR_INITHAL_SET_INTR_ENTRYHAL_INTR_DISABLEHAL_INTR_ENABLEHAL_INTR_DISABLE_SAVEHAL_INTR_RESTOREHAL_INTR_MAXHAL_INTR_MINHAL_TRANSLATE_VECTORHAL_HEAP_STARTHAL_HEAP_ENDHAL_MEM_INITHAL_TICKS_INTRHAL_TICKS_INITHAL_BOARD_INIT如果是要多核:则要支持更多接口:HAL_CORE_CPU_INITHAL_FOLLOW_CPU_INITHAL_PREPARE_CPUSHAL_START_CPUHAL_CPU_IS_ACTIVEHAL_CMP_ACKHAL_WAIT_ACKHAL_IPI_SENDHAL_IPI_SEND_CPUSHAL_IPI_SEND_ALL注意:上面的接口必须要实现,但是可以置为空。也就说可以通过如下方式:#define HAL_MEM_INIT 尽管为空,但不能没有。下面再说下移植文件要求:.h必须要要有两个头文件:即在pal/$(BOARD/include目录下,比如pal/board/stm3210/includehal_port.h,hal_undef.hhal_port.h应该包含上面两个文件以外的所有.h头文件hal_undef.h:我们前面提到过,上面hal通用接口,移植的时候可以不实现,但是如果想优化,可以自己实现,这样就有重定义的问题了,因为hal_comm.h已经实现了,如果再定义,根据宏的规则,是最后一个有效,因此如果想要自己的实现覆盖公共的定义,就要将我们的定义放在后面,这就是我们需要独立出hal_undef.h的原因,这个在头文件在hal.h是在hal_comm.h之后包含的。那如何覆盖hal_comm.h的定义呢?下面以stm3210这块板子来说明:由于stm3210的中断调度函数使用的pendsv中断,而这个中断优先级最低,因此肯定是最后一层中断时才执行,因此不需要中断嵌套标志,可以将这部分全部滞空。因此可以在hal_undef.h如下定义:#undef HAL_SCHED_BRIDGE()#undef HAL_INTR_EXIT_BRIDGE()#undef HAL_INTR_NESTING_INIT() #undef HAL_GET_INTR_NESTING() #undef HAL_INTR_NESTING_DEC() #undef HAL_INTR_NESTING_INC() #undef HAL_START_OS(stack) #define HAL_INTR_NESTING_INIT() #define HAL_GET_INTR_NESTING() 0#define HAL_INTR_NESTING_DEC() #define HAL_INTR_NESTING_INC() 这样我们自定义的接口就生效了。必须要有start名字的启动文件,后缀根据不同编译器不同,对于gnu的gcc,应该是start.s或start.S,对于armcc则是start.asm,为什么要规定启动代码文件名字,主要是这部分代码必须在映像文件最开始的部分,这样它才能算是启动代码啊。为了可配置,acoral的hal相关的接口有些规范:如果是汇编实现的接口,则直接使用上面的接口名,如果是c实现的,则推荐使用小写,然后用宏转向,比如HAL_INTR_INIT,由于这个接口都可以用c语言实现,因此名字为hal_intr_init,然后用如下宏转换即可:#define HAL_INTR_INIT() hal_intr_init()当然你也可以直接在c语言里使用HAL_INTR_INIT这个函数名。下面以s3c2410的移植为例说下acoral的移植:首先是HAL移植:建立文件结构:s3c2410基于arm公司的arm9开发的,因此在pal/board下建立S3C2410目录。然后在S3C2410目录下建立include src目录。然后在include目录建立hal_port.h,hal_undef.h两个文件.Src建立一个start.s文件:启动接口书写start.S文件,这个可以借鉴别人已经实现的开始启动代码,比如vivi,uboot等bootloader.主要要做哪些事呢?1) 中断向量Arm9是非向量模式的中断系统,irq中断只有一个入口,由于我们要将中断引入HAL_INTR_ENTRY,因此在start.S就要将HAL_INTR_ENTRY放到IRQ向量的处理程序里。_ENTRY:bResetHandlerb HandleIRQ handler for IRQ interruptHandleIRQ:ldr pc,=HAL_INTR_ENTRY2) 复位函数ResetHandlerResetHandler: disable watch dog timermovr1, #0x53000000movr2, #0x0strr2, r1 disable all interruptsmovr1, #INT_CTL_BASEmovr2, #0xffffffffstrr2, r1, #oINTMSKldrr2, =0x7ffstrr2, r1, #oINTSUBMSK initialise system clocksmovr1, #CLK_CTL_BASEmvnr2, #0xff000000strr2, r1, #oLOCKTIMEmovr1, #CLK_CTL_BASEmovr2, #M_DIVNstrr2, r1, #oCLKDIVNmrcp15, 0, r1, c1, c0, 0 read ctrl registerorrr1, r1, #0xc0000000 Asynchronousmcrp15, 0, r1, c1, c0, 0 write ctrl registermovr1, #CLK_CTL_BASEldr r2, =vMPLLCON clock user setstrr2, r1, #oMPLLCONblmemsetupbl InitStacksadr r0,_ENTRYldr r1,_text_startcmp r0,r1blne copy_self sldr r0,_bss_startldr r1,_bss_endbl mem_clearldr pc,=acoral_startb .关看门狗,修改时钟(可采用默认时钟),初始化sdram控制器,加载自己(如果在nandflash,norflash等存储设备上),初始化堆栈,切换模式(我们选择sys系统模式),然后跳转到acoral的c语言启动函数acoral_start。HAL接口移植:线程相关HAL接口:由于是线程相关的,在include目录下新建hal_thead.h头文件,并修改hal_port.h,将hal_thread.h包含进来(#include “hal_thread.h”)。src目录下新建hal_thread_c.c,hal_thread_s.c文件。HAL_SWITCH_TO:线程切入,如何切入呢?切入就是环境恢复啊,只需将线程堆栈中的保护的环境恢复到对应的寄存器即可。故实现如下:HAL_SWITCH_TO: ldr sp,r0 取得新上下文指针(传进来的参数) ldmfd sp!,r0 msr cpsr,r0 恢复新cpsr,这个不能用spsr,因为sys,user模式没有SPSRldmfd sp!, r0-r12,lr,pc 恢复寄存器,HAL_CONTEXT_SWITCH:任务切换,肯定要先保存旧的线程的上下文环境(寄存器),换出新的线程的上下文环境(寄存器)。HAL_CONTEXT_SWITCH: stmfd sp!,lr 保存PC(1) stmfd sp!,r0-r12,lr 保存寄存器和LR mrs r4,CPSR stmfd sp!,r4 保存cpsr str sp,r0 保存旧上下文栈指针到prev-stack(2) ldr sp,r1 取得新上下文指针(3) ldmfd sp!, r0 msr cpsr,r0 恢复新cpsr,这个不能用spsr,因为sys,user模式没有SPSRldmfd sp!, r0-r12,lr,pc 恢复寄存器,(4)(1)(2)是保存prev旧的线程的上下文环境。(3)(4)是换出next新的线程的上下文环境。HAL_INTR_SWITCH_TO: 这个只是HAL_INTR_CTX_SWITCH的下半部分实现,大家看HAL_INTR_CTX_SWITCH就可以了。HAL_INTR_CTX_SWITCH:中断环境下线程切换和线程环境下线程切换有啥区别呢?区别在于中断环境下线程切入函数调用后不能立即切换到新的线程,因为中断必须执行完了才能执行新的线程,要不进入新的线程,相当于中断被终止了,没法复原中断,就比如中断模式的堆栈,因为中断没有完全执行完,中断的堆栈没有回收,这个肯定是不能容忍的,因此要想一个办法,在中断完全退出时才真正进入新的线程。同时中断环境下旧的线程的环境无法直接获取,运行到中断任务切换函数时,处理器寄存器的值已经不是旧的线程的寄存器了,别破坏掉了。因此在中断入口就得保存旧的线程的环境这有两种方式:1) 刚进入中断时就将旧的线程的保存到旧的线程的堆栈,这种方式在退出中断时需要切换线程情况下效果很好,但是如果不是,则要弹出旧的线程的堆栈,这样就比较麻烦,且其实大部分中断不会切换。2) 刚进入中断时,将旧的线程的上下文环境(寄存器)保存到中断的堆栈中,而在线程切换时从中断模式栈顶拷贝copy环境到旧的线程的堆栈,这样虽然要复杂些,但是在不需要切换时,中断退出简单。Acoral针对s3c2410采取第二种方式,如图:实现如下:HAL_INTR_CTX_SWITCH: stmfd sp!,r2-r12,lr 保存正在服务的中断上下文 以下几行把旧的线程prev的上下文从正在服务的中断栈顶转移到其栈中 ldr r2,=IRQ_stack 取irq栈基址,这里存放着被中断线程的上下文(1) ldmea r2!,r3-r10 按递增式空栈方式弹栈,结果: r2-1=LR_irq-r10,被中断线程的PC+4 r2-2=r12-r9,被中断线程的r12 r2-3=r11-r8,被中断线程的PC . r2-8=r6-r3,被中断线程的r6 sub r10,r10,#4 中断栈中的LR_irq-4=PC以下三句就是取出old_vm的SP_sys,只能通过stmfd指令间接取 mov r11,sp下一句不能用SP,故先拷贝到r11 stmfd r11!,sp 被中断线程的SP_sys压入正在服务的中断栈中,带有表示去sys模式下对应的寄存器 ldmfd r11!,r12 从正在服务的中断栈中读取 SP_sys-R12 stmfd r12!,r10 保存 PC_sys stmfd r12!,lr 保存 lr_sys stmfd r12!,r3-r9 保存被中断线程的r12-r6到它的栈中 ldmea r2!,r3-r9 读被中断线程的r5-r0-r9-r4,SPSR_irq-r3,递增式空栈 stmfd r12!,r3-r9 保存被中断线程的r5-r0,CPSR_sys到它的栈中 str r12,r0 换出的上下文的栈指针-old_sp(2) 以下几行把new_vm的上下文copy到IRQ栈顶 与递减式满栈对应,此时IRQ栈用递增式空栈的方式访问 ldr r12,r1 读取需换入的栈指针(3) ldmfd r12!,r3-r11 读取换入线程的CPSR_sys-r3 读取换入线程的r0-r7-r4-r11 stmea r2!,r3-r11 保存换入线程的CPSR_sys-SPSR_irq, r0-r7到IRQ栈 ldmfd r12!,r3-r7 读取换入线程的r8-r12-r3-r7 stmea r2!,r3-r7 保存换入线程的r8-r12到IRQ栈 ldmfd r12!,lr 恢复换入线程的LR_sys到寄存器中 ldmfd r12!,r3 读取换入线程的PC-r3 add r3,r3,#4 模拟IRQ保存被中断上下文PC的方式:PC+4-LR_irq stmea r2!,r3 保存换入线程的LR_irq到IRQ栈 就是将r12赋值给sp,因为无法通过mov,所以要 stmfd r12!,r12 读取SP_sys到r12 ldmfd r12!,sp 恢复SP_sys mov r0,r0 无论是否操作当前状态的SP,操作sp后,不能立即执行函数 返回指令,否则返回指令的结果不可预知。 ldmfd sp!,r2-r12,pc(4)(1)(2)保存prev旧的线程的上下文环境(3)(4)恢复next新的线程的上下文环境HAL_STACK_INIT:S3c2410的线程环境是r0r14及cpsr,故在堆栈初始化时就得压入这么多寄存器来模拟线程的环境,为了方便修改和操作,用一个数据结构表示环境。typedef struct acoral_u32 cpsr;acoral_u32 r0;acoral_u32 r1;acoral_u32 r2;acoral_u32 r3;acoral_u32 r4;acoral_u32 r5;acoral_u32 r6;acoral_u32 r7;acoral_u32 r8;acoral_u32 r9;acoral_u32 r10;acoral_u32 r11;acoral_u32 r12;acoral_u32 lr;acoral_u32 pc;hal_ctx_t;由于c语言实现了这个接口,名称定为hal_stack_init由于这个是线程相关的接口,头文件定为hal_thread.h,源码定为hal_thread_c.c。用宏转换:#define HAL_STACK_INIT(stack,route,exit,args) hal_stack_init(stack,route,exit,args) void hal_stack_init(acoral_u32 *stk,void (*route)(),void (*exit)(),void *args)hal_ctx_t *ctx=*stk;ctx-;(1)ctx=(acoral_u32 *)ctx+1;(2)ctx-r0=(acoral_u32)args;(3)ctx-r1=1;(4)ctx-r2=2;ctx-r3=3;ctx-r4=4;ctx-r5=5;ctx-r6=6;ctx-r7=7;ctx-r8=8;ctx-r9=9;ctx-r10=10;ctx-r11=11;ctx-r12=12;ctx-lr=(acoral_u32)exit;ctx-pc=(acoral_u32)route;(5)ctx-cpsr=0x0000001fL;(6) *stk=ctx;(7) (1) 堆栈是向下生长的,结构体是向上的,获得模拟的环境的基地址(2) 调整4个字节,传进来的堆栈指针的内存本身就可以容纳一个数据的。(3) 压入参数环境(4) 压入寄存器的值,为了调试的时候方便识别出堆栈,将这些寄存器的值按1N赋值。(5) 压入线程执行函数
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025停车场转让合同协议书
- 2025年餐厅管理承包合同模板
- 沛县舞阳桥市政工程施工组织设计
- 2024年医用磁共振成像设备项目投资申请报告代可行性研究报告
- 2025年度担保合同
- DB65T 3671-2014 无公害农产品 玉米1000kg-667㎡栽培技术规程
- 千伏换流站工程项目管理实施规划施工组织设计x
- 汽车买卖合同(青岛)
- 企业用工合同.【三篇】
- 本章复习与测试说课稿-2025-2026学年高中生物苏教版必修1分子与细胞-苏教版
- 维克多高中英语3500词汇
- 工业控制系统安全风险评估(工控安全大检查)
- NY 5052-2001无公害食品海水养殖用水水质
- TSZUAVIA 009.4-2019 多旋翼无人机系统实验室环境试验方法 第4部分:低温试验
- 现状调查培训课件
- 初级社会工作实务全本课件
- 电气试验标准化作业指导书
- 养老机构行政值班查房记录表格
- 练习太极拳的三个阶段
- 华为供应商质量管理体系考察报告(全)
- 冶金工业清洁生产的主要途径(共82页).ppt
评论
0/150
提交评论