or1200移植实时系统Raw-OS(一)

        Raw-OS的官网网站:http://www.raw-os.org/

        这是一个起步不久的make in china的实时操作系统哈~作者是txj牛牛~崇尚开源的多多支持支持txj哈~

        记得我们在移植大多数实时操作系统的时候需要做的工作吗,回想一下最主要的,四个函数的编写,最高优先级任务启动start_first_task(),任务切换函数task_switch(),中断ISR里的任务切换函数int_switch(),时钟ISR函数TickISR()。

        按照我自己的理解解释一下实时操作系统初始化后的工作流程,在内核初始化完成之后,开始执行自行编写的任务程序时,会从就绪任务队列中执行最高优先级任务,然后,在任务的时间轮转片用完之后,会将当前任务调度入任务等待队列,再从就绪任务队列中再次执行最高优先级任务,而时间片的计算就是由时钟ISR执行的。

        然后着重介绍每个函数的工作流程是怎么样的~

        我们首先上Raw-OS的官网将移植的相关说明文档下载下来,具体google之就OK啦,对于做苦逼的技术员来说,google是神器~不解释了。

        在内核启动之初,要从最高优先级的任务先开始运行,但是不只是内核启动之初,系统启动之后进行任务切换时都要调度最高优先级任务运行的,所以就得出结论(稍后总结哈~亲)。


        回想起在学计算机原理课程的时候讲CPU中断,CPU需要做什么工作呢?

        1.CPU跳转到对应一级中断向量地址

        2.将当前CPU的PC、SR、GPRs······寄存器压栈

        3.转入对应的二级中断向量表的ISR执行自定义的中断操作

        4.ISR返回后将先前压栈的PC、SR、GPRs恢复

        5.返回中断时的PC继续执行


        当OS进行任务切换的时候都存在类似的过程,在Kernel建立任务的时候,为每个任务都建立属于自己的任务堆栈,所以在任务切换的时候就进行类似于响应中断的过程

        将当前任务压栈,然后从最高优先级任务从堆栈中恢复出来······具体再仔细想想每次任务切换的过程,当前任务压栈,下一任务从堆栈恢复,压栈出栈,压栈出栈······不要想不通做傻事了~少年~


        现在就直接具体化任务切换函数:

        1.堆栈指针自减一个帧的大小(128Bytes+128Bytes)

        2.保存程序计数器(EPCR)

        3.保存状态字(ESR)

        4.保存所有寄存器r2-r31

        5.然后读取最高优先级任务地址

        6.读取最高优先级任务的第一个变量的值(任务使用的堆栈的入口地址),然后存放入r1寄存器(openrisc架构的帧结构定义为堆栈寄存器SP)

        7.然后调用任务切换的钩子函数(可选)

        8.读取最高优先级任务的第一个变量的值(最高优先级任务堆栈),读取当前优先级任务的第一个变量的值(当前优先级任务堆栈),然后将最高优先级任务堆栈值存入当前优先级任务堆栈值

        9.将最高优先级任务堆栈指针值赋值r1(SP)

        10.恢复程序计数器(EPCR)、状态字(ESR)

        11.恢复所有寄存器r2-r31

        12.堆栈指针自加一个帧的大小(128Bytes+128Bytes)

        13.返回


        这里要说明一下,这个流程是把在中断中进行任务切换的流程也包括在内,至于中断进行的任务切换稍后说明一下,这里理解最基本的任务切换先。

        代码:

/*
------------------------------
            port_task_switch
------------------------------
 Description :
 This routine switches between two different tasks.
 The task state of one is saved on its kernel stack.
 Then the state of the other is restored from its kernel stack.

 There maybe memory management hardware issues

 Finally, we can return to the second task, via the 'return'.

 Includes raw_int_switch
------------------------------
 Uses:
------------------------------
*/

/* task switch function */
port_task_switch:
        /* l.sys exception for now so we are in supervisor mode */
        /* exception  - recover pc from epcr */       
		
        l.mfspr r3,r0,SPR_EPCR_BASE     /* save program counter that was put in exception register */
        l.sw    PT_PC(r1),r3
        l.mfspr r3,r0,SPR_ESR_BASE      /* save status register that was put in exception register */
        l.sw    PT_SR(r1),r3
	
        STORE_CONTEXT
	
					/* Store current stack pointer   */
        LOAD_SYMBOL_2_GPR(r3,raw_task_active)            /* r3= &raw_task_active */
        l.lwz   r3,0(r3)                /* r3 = &raw_task_active->task_stack */
        l.sw    0(r3),r1                /* raw_task_active->task_stack = SP */

/* task switch in interrupt routine */
raw_int_switch:
	/* call the raw_task_switch_hook */
	
	/*  */
        LOAD_SYMBOL_2_GPR(r2,high_ready_obj)        /* r2= &high_ready_obj */
        l.lwz   r2,0(r2)                                                /* r2 = &high_ready_obj->task_stack  */
        LOAD_SYMBOL_2_GPR(r3,raw_task_active)            /* r3= &raw_task_active */
        l.sw    0(r3),r2                /* &raw_task_active->task_stack = r2 */

        l.lwz   r1,0(r2)               /* sp = *(&high_ready_obj->task_stack) */
        
        l.lwz   r2,PT_PC(r1)                /* load context for task to be resumed */
        l.mtspr r0,r2,SPR_EPCR_BASE
        
        l.lwz   r2,PT_SR(r1)
        l.mtspr r0,r2,SPR_ESR_BASE

        RESTORE_CONTEXT

        l.addi  r1,r1,STK_FRAME_SIZE	
        l.rfe
        l.nop

        还要说明一下的就是openrisc定义的寄存器使用规则和帧结构的组成,在or1200 spces文档里有很详尽的描述

        寄存器使用规则,需要注意的就是列表中的寄存器的特殊用法:

        

         帧结构:

        而在帧结构组成的下面有一段话解释了为什么要求任务或者中断进行压栈时的帧的大小(128Bytes+128Bytes)

or1200移植实时系统Raw-OS(一)_第1张图片

        大概任务切换的流程的概念就有了,想象一下如果把Kernel开始运行最高优先级任务当做是任务切换的话,调度任务简单来讲就是入栈出栈······入栈出栈······所以运行最高优先级任务就是当前任务入栈,最高优先级任务出栈,但是Kernel开始是没有当前任务运行的,所以最高优先级任务就只存在任务出栈的步骤。


        好,又直接总结启动最高优先级任务的具体步骤

        1.调用任务切换的钩子函数(可选)

        2.读取最高优先级任务的第一个变量的值(最高优先级任务堆栈)

        3.将最高优先级任务堆栈指针值赋值r1(SP)

        4.恢复程序计数器(SPR_EPCR_BASE)、状态字(SPR_ESR_BASE)

        5.恢复所有寄存器r2-r31

        6.堆栈指针自加一个帧的大小(128Bytes+128Bytes)

        7.返回

        NOTE:这个和中断任务切换函数流程是一样的,所以在开始最高优先级任务的时候就要恢复堆栈,但是我们任务一开始哪里会有压栈过的任务堆栈,没压栈何来的出栈。所以,就必须要在开始最高优先级任务的时候要自己模拟一个压栈过的堆栈让任务恢复


    代码:

/*
------------------------------
        raw_start_first_task
------------------------------
 Description :
   Starts the highest priority task that is available to run

 raw_start_first_task() MUST:
  a) Call raw_task_switch_hook() then,
  b) Set OSRunning to TRUE,
  c) Switch to the HPT

------------------------------
 Uses :
------------------------------
*/
raw_start_first_task:
        /* call the raw_task_switch_hook */

        /* set OSRunning == TRUE */
        /* raw_task_active = high_ready_obj */
	
        LOAD_SYMBOL_2_GPR(r3,raw_task_active)            /* r3= &raw_task_active */
        l.lwz   r3,0(r3)                /* r3 = &raw_task_active->task_stack */
        l.lwz   r1,0(r3)               /* sp = *(&raw_task_active->task_stack) */

        l.lwz   r2,PT_PC(r1)                /* load context for task to be resumed */
        l.mtspr r0,r2,SPR_EPCR_BASE
        l.lwz   r2,PT_SR(r1)
        l.mtspr r0,r2,SPR_ESR_BASE

        RESTORE_CONTEXT

        l.addi  r1,r1, STK_FRAME_SIZE
        l.rfe
        l.nop

        往下走,OS的任务切换都是靠系统Ticks来计时切换的,所以必须有这样一个功能,OS在某一个时间点执行Ticks累加,每个Tick是一个时间片,然后程序中的任务根据自己的时间片得以执行,当任务自己限定的Ticks用尽后,OS执行任务调度。所以,这个系统定时靠device的Timer或者Ticker外设来进行计时,很不幸的是or1200里只有一个Timer,所以只有牺牲掉它来做定时咯,至于如果程序需要用到硬件定时器的话,可以自己在or1200内部自己添加Timer。

        好,Ticks计数是放在Timer or Ticks’ ISR进行的,看上面总结的CPU中断流程,很容易得到其进入ISR的步骤:

        1.堆栈指针自减一个帧的大小(128Bytes+128Bytes)

        2.保存程序计数器(EPCR)

        3.保存状态字(ESR)

        4.保存所有寄存器r2-r31

        5.跳转到OS定义的enter_interrupt(判断中断嵌套)

        6.读取全局int_nesting(中断嵌套变量)

        7.if IntNesting==1,将最当前优先级任务堆栈指针值赋值r1(SP),else不保存当前优先级任务堆栈指针值

        8.清除定时器中断标志

        9.重新打开CPU中断(可选)

        10.调用时钟节拍函数Timer or Ticks’ ISR

        11.调用中断退出函数finish_int

        12.读取最高优先级任务的第一个变量的值(最高优先级任务堆栈)

        13.将最高优先级任务堆栈指针值赋值r1(SP)

        14.恢复程序计数器(SPR_EPCR_BASE)、状态字(SPR_ESR_BASE)

        15.恢复所有寄存器r2-r31

        16.堆栈指针自加一个帧的大小(or1200为128bytes)

        17.返回


        代码:

/*
------------------------------
        TickISR
------------------------------
 Description :

------------------------------
 Uses :
------------------------------
*/
TickISR:
        l.mfspr r3,r0,SPR_EPCR_BASE     /* save program counter that was put in exception register */
        l.sw    PT_PC(r1),r3
        l.mfspr r3,r0,SPR_ESR_BASE      /* save status register that was put in exception register */
        l.sw    PT_SR(r1),r3		
	
        STORE_CONTEXT
	
        /* Jump to timer tick handler written by C */
        l.jal raw_enter_interrupt
        l.nop

        LOAD_SYMBOL_2_GPR(r2,raw_int_nesting)
        l.lbz   r3,0(r2)             /* r3 raw_int_nesting */	

        /* if (raw_int_nesting == 1) raw_task_active->task_stack = sp */	
        l.sfeqi r3,1
        l.bnf	nest_not_one
        l.nop

        LOAD_SYMBOL_2_GPR(r4,raw_task_active)			 /* r4= &raw_task_active */
        l.lwz	r5,0(r4)				/* r3 = &raw_task_active->task_stack  */
        l.sw	0(r5),r1				/* raw_task_active->task_stack = SP */

nest_not_one:
        /* Jump to timer tick handler written by C */
        l.jal SysTickHandler
        l.nop

        /*Call raw_finish_int()*/
        l.jal   raw_finish_int
        l.nop	

        LOAD_SYMBOL_2_GPR(r3,high_ready_obj)			 /* r3= &high_ready_obj */
        l.lwz	r3,0(r3)				/* r3= &high_ready_obj->task_stack */
        l.lwz	r1,0(r3)			   /* sp = *(&high_ready_obj->task_stack) */
       
        l.lwz   r2,PT_PC(r1)                /* load context for task to be resumed */
        l.mtspr r0,r2,SPR_EPCR_BASE
        l.lwz   r2,PT_SR(r1)
        l.mtspr r0,r2,SPR_ESR_BASE

        RESTORE_CONTEXT

        l.addi  r1,r1,STK_FRAME_SIZE
        l.rfe
        l.nop          	        
     


        关于汇编,最后还有一个要自己编写的就是User’s ISR,这个和Timer or Ticks’ ISR的步骤是完全一样的,所以不解释咧,直接上代码稍微阅读下就OK了~

/*
------------------------------
        UsrISR
------------------------------
 Description :

------------------------------
 Uses :
------------------------------
*/
UsrISR:
	l.mfspr r3,r0,SPR_EPCR_BASE 	/* save program counter that was put in exception register */
	l.sw	PT_PC(r1),r3
	l.mfspr r3,r0,SPR_ESR_BASE		/* save status register that was put in exception register */
	l.sw	PT_SR(r1),r3		
			
	STORE_CONTEXT
			
	/* Jump to timer tick handler written by C */
	l.jal raw_enter_interrupt
	l.nop

	LOAD_SYMBOL_2_GPR(r2,raw_int_nesting)
	l.lbz	r3,0(r2)			 /* r3 raw_int_nesting */	
		
	/* if (raw_int_nesting == 1) raw_task_active->task_stack = sp */	
	l.sfeqi r3,1
	l.bnf	Unest_not_one
	l.nop
		
	LOAD_SYMBOL_2_GPR(r4,raw_task_active)			 /* r4= &raw_task_active */
	l.lwz	r5,0(r4)				/* r5 = &raw_task_active->task_stack  */
	l.sw	0(r5),r1				/* raw_task_active->task_stack = SP */
		
Unest_not_one:
	/* Jump to timer tick handler written by C */
	l.jal InterruptHandle
	l.nop
		
	/*Call raw_finish_int()*/
	l.jal	raw_finish_int
	l.nop	
		
	LOAD_SYMBOL_2_GPR(r3,high_ready_obj)			 /* r3= &high_ready_obj */
	l.lwz	r3,0(r3)				/* r3= &high_ready_obj->task_stack */
	l.lwz	r1,0(r3)			   /* sp = *(&high_ready_obj->task_stack) */
			   
	l.lwz	r2,PT_PC(r1)				/* load context for task to be resumed */
	l.mtspr r0,r2,SPR_EPCR_BASE
	l.lwz	r2,PT_SR(r1)
	l.mtspr r0,r2,SPR_ESR_BASE
		
	RESTORE_CONTEXT
		
	l.addi	r1,r1,STK_FRAME_SIZE
	l.rfe
	l.nop 

        之前讲到启动最高优先级任务的时候要求自己手动模拟一个压栈过的堆栈让任务恢复,其实这个过程存在每一个任务建立的时候,是不是?进行任务切换时就会恢复任务的堆栈,就必须要有压栈过的堆栈,就必须要求建立任务时存在压栈过的堆栈,要不任务切换时从没压栈过哪里有压栈过的堆栈恢复,对不对?

        所以在任务建立的时候就会存在一个这样的函数,是专门用来模拟任务压栈的,stack_init(),这个函数压栈的顺序要求同汇编里面压栈的顺序是一样的,但是为什么要一样?这个难道还要解释吗,我去······

        那就直接上代码咯~

RAW_VOID  *port_stack_init(PORT_STACK  *p_stk_base, RAW_U32 stk_size,  RAW_VOID   *p_arg, RAW_TASK_ENTRY p_task)
{
	RAW_U32 *stk;
	RAW_U32 *fp;
	RAW_U32 temp = (RAW_U32)(p_stk_base + stk_size - 32);
	stk = (RAW_U32  *)temp;	
	
	stk--;	
        
	// *stk-- = (RAW_U32)(p_arg);               /* fp+0 -> parameter 0    */
	// fp     = stk;
	// *stk-- = (RAW_U32)fp;                    /* sp+4 -> previous fp    */
	// *stk-- = (RAW_U32)0;                     /* sp+0 -> return address */
         
	*stk-- = (RAW_U32)31;                     /* r31 = 0  */
	*stk-- = (RAW_U32)30;                     /* r30 = 0  */
	*stk-- = (RAW_U32)29;                     /* r29 = 0  */
	*stk-- = (RAW_U32)28;                     /* r28 = 0  */
	*stk-- = (RAW_U32)27;                     /* r27 = 0  */
	*stk-- = (RAW_U32)26;                     /* r26 = 0  */
	*stk-- = (RAW_U32)25;                     /* r25 = 0  */
	*stk-- = (RAW_U32)24;                     /* r24 = 0  */
	*stk-- = (RAW_U32)23;                     /* r23 = 0  */
	*stk-- = (RAW_U32)22;                     /* r22 = 0  */
	*stk-- = (RAW_U32)21;                     /* r21 = 0  */
	*stk-- = (RAW_U32)20;                     /* r20 = 0  */
	*stk-- = (RAW_U32)19;                     /* r19 = 0  */
	*stk-- = (RAW_U32)18;                     /* r18 = 0  */
	*stk-- = (RAW_U32)17;                     /* r17 = 0  */
	*stk-- = (RAW_U32)16;                     /* r16 = 0  */
	*stk-- = (RAW_U32)15;                     /* r15 = 0  */
	*stk-- = (RAW_U32)14;                     /* r14 = 0  */
	*stk-- = (RAW_U32)13;                     /* r13 = 0  */
	*stk-- = (RAW_U32)12;                     /* r12 = 0  */
	*stk-- = (RAW_U32)11;                     /* r11 = 0 return value */
	*stk-- = (RAW_U32)10;                     /* r10 = 0 */
	*stk-- = (RAW_U32)9;                     /* r09 = 0 link register */
	*stk-- = (RAW_U32)8;                     /* r08 = 0 function paramters*/
	*stk-- = (RAW_U32)7;                     /* r07 = 0 */
	*stk-- = (RAW_U32)6;                     /* r06 = 0 */
	*stk-- = (RAW_U32)5;                     /* r05 = 0 */
	*stk-- = (RAW_U32)4;                     /* r04 = 0 */
	*stk-- = (RAW_U32)(p_arg);               /* r03 = arg0 */
	*stk-- = (RAW_U32)fp;                    /* r02 = frame pointer */
	*stk-- = (RAW_U32)(SPR_SR_IEE | SPR_SR_TEE | SPR_SR_SM);         /* status word */
	*stk   = (RAW_U32)(p_task);                /* program counter */
 
	return ((RAW_U32 *)stk);  
}

        还有一点是要注意的,在写任务切换的时候把任务切换放在Syscall exception里面的,也就是任务切换用异常来实现的,所以在定义任务切换这个CONTEXT_SWITCH()宏的时候要注意下

#define  CONTEXT_SWITCH() __asm__ ("l.sys 0"); \
                      				__asm__ ("l.nop");


        到这里先解释完移植的要点,下节再贴出详细的移植步骤,包括用到的外设驱动和移植内核后的测试代码。

        再下节的话基于simple-spi这个ipcore编写SD卡的驱动程序。

        再再下节就移植znFat文件系统上去吧,这个也是国内一位牛人振南兄写的Fat32的解决方案。

        今天先到这里啦,Raw-OS移植好的代码和测试程序都传到资源里面了,下次再见吧~追动漫去~

 
 

你可能感兴趣的:(RAW-OS,openrisc)