微内核之多任务定时切换

/###################################################################

任务目标 :通过定时器产生任务切换,其中swi来实现多个任务之间切换

实验平台 : 本实验是基于S3C2440上实现

/####################################################################

工作原理 :

在运行任务程序的时候通过定时器产生中断,进入任务优先级调度,选择优先级最高的任务去执行;其中各个任务之间通过SWI软中断来实现相互切换。
难点:在中断模式和SVC模式下任务栈信息保存及恢复,同时在SVC模式下把将要运行的任务栈信息从该任务栈里恢复到寄存器中,最后跳转到用户态执行任务。
基本流程:

图片.png

实现步骤:

请参考微内核之任务切换。http://www.jianshu.com/p/7792cdc4fd5f

增加与修改部分bug:

主要增加定时器中断以及任务函数寄存器信息完整保存,使每次任务执行时被中断后能正确恢复该任务的信息,各个任务被中断还能从中断点继续执行。

1、IRQ模式下任务栈信息保存

*IRQ模式中断处理函数

/*#####################################################
 * IRQ中断被监测到,则下面行为被执行:
 * R14_irq = 将被执行的下一条指令地址+4;
 * SPSR_irq = CPSR
 * CPSR[4:0] = 0b10010 ; Enter Supervisor mode 
 * CPSR[5] = 0         ; Execute in ARM state 
 * CPSR[6] is unchanged 
 * CPSR[7] = 1         ;Disable normal interrupts 
 * CPSR[8] = 1         ; Disable Imprecise Data Aborts (v6 only)
 * CPSR[9] = CP15_reg1_EEbit ; Endianness on exception entry 
 * 执行irq处理函数后返回原程序,可以使用如下指令:
 * SUBS PC,R14,#4 ; SPSR_irq中的值自动存储到CPSR中
#######################################################*/    
HandleIRQ:

   SUB LR, LR, #4                             /*  计算返回地址,中断模式LR保存被中断指令的下一条指令 */
  
    STMDB   SP!,    { LR }                    /*  被中断模式下的指令下一条指令 */
    STMDB   SP!,    { R0-R14}^                /* ^表示保存user模式下R0-R14,只针对usr模式 */ 
                     
    LDR  R0,=IRQ_STACK          /* 保存irq模式下的SP栈,用于切换到SVC模式下*/
    STR SP, [R0]                        /*  恢复保存的寄存器值,用于调试  */

    LDR R0, =LR_VAR                     /*  保存IRQ模式下的LR,用于调试  */
    STR R14, [R0]   

    LDR R0, =pCurTcb               /* 获取当前任务栈的起始地址 */
    LDR R2, [R0]        
    MRS R0, SPSR               
    STR R0,[R2],#4          /* SPSR存进当前任务栈里 */
    STR R0,[R2],#4          /* CPSR存进当前任务栈里 */ 

    MOV R1, SP
    MOV R3,#16              /* 把IRQ模式下保存寄存器数量R0-R14 */  
 
/*  把IRQ模式下保存寄存器数量R0-R15复制到当前任务栈里  */

IRQ_STACK_COPY:             

    LDR R0,[R1],#4      
    STR R0,[R2],#4      
    SUBS R3,R3,#1       
    BNE IRQ_STACK_COPY                      
        
    LDR LR, =int_return             /* 设置调用ISR即EINT_Handle函数后的返回地址 
    LDR PC, =ISR_Handle             /* 调用中断服务函数,在interrupt.c中 */

int_return:
   LDMIA   SP!,    { R0-R14 }^     /* ^表示把中断模式下SP栈里寄存器恢复到usr模式下对应的寄存器中 */
   LDMIA   SP!,    { PC }^         /* 中断返回, ^表示将spsr的值复制到cpsr */

2、SVC模式下任务栈信息保存

*SVC模式中断处理函数

/******************************************************************************
 * SWI中断进入到SVC(超级用户模式),当SWI被执行时,下面行为被执行:
 * R14_svc = SWI指令的下一条指令;
 * SPSR_svc = CPSR
 * CPSR[4:0] = 0b10011 ; Enter Supervisor mode 
 * CPSR[5] = 0         ; Execute in ARM state 
 * CPSR[6] is unchanged 
 * CPSR[7] = 1         ;Disable normal interrupts 
 * CPSR[8] is unchanged 
 * CPSR[9] = CP15_reg1_EEbit ; Endianness on exception entry 
 * 执行SWI函数后返回原程序,可以使用如下指令:
 * MOVS PC,R14 ; SPSR_svc中的值自动存储到CPSR中
 ******************************************************************************/

HandleSWI:

    STMDB   sp!,    {R14}           /* svc模式下R14(LR)保存user模式下的下一条指令地址PC; */
    LDR R0, =LR_VAR                 /* 保存SVC模式下的LR,用于调试 */
    STR R14, [R0]
    
    STMDB   sp!,    { r0-r14}^      /* ^表示user模式下的R0-R14到SVC模式的SP中,只针对user模式 */                 

    MRS     R0, SPSR                /* 不同模式之间切换,cpsr被保存在SPSR中 */    
    STMDB   sp!, {R0}               /* CPSR位置 */    
    STMDB   sp!, {R0}               /* SPSR位置 */    


    LDR R0, =SVC_STACK              /* 保存SVC模式下的SP栈,用于恢复和调试 */
    STR sp, [R0]


    /******************************************************************************
    * 下面代码用于监测irq中断触发的SWI中断,以便跳转到taskSched函数执行最高优先级任务
    ******************************************************************************/

    sub  R0, R14, #4                 /* lr -4 为指令SWI XXX的地址,低24位是软件中断号 */
    nop
    ldr  R1,[R0,#0]                  /* lr -4 为指令SWI XXX的地址,低24位是软件中断号 */
    nop
    bic  R1,R1,#0XFF000000           /* 取得arm指令的低24位立即数 */
    cmp  R1,#255                     /* 判断24位立即数,如果是255,调用timer0_taskSched函数 */
    beq  timer0_taskSched

    /******************************************************************************
    * 把寄存器值保存到当前任务栈里,把SVC模式下保存的当前任务栈信息保存到当前任务栈
    * tasknTcb中。以备下次调用时在恢复该任务的栈信息,是否可以直接存在栈里呢?
    ******************************************************************************/

    LDR   R0, =pCurTcb            /* 获取当前任务栈的起始地址 */
    LDR   R1, [R0] 

    CMP   R1,#0                     /* 把pCurTcb值==0;说明是taskStart函数;跳转到taskBefore处执行 */
    BEQ   taskBefore
    
    LDR   R0,=SVC_STACK             /* 恢复异常时保存SVC模式下的SP栈指针到R2 */
    LDR   R2,[R0] 
    
    MOV   R3,#18                    /* 把SVC模式下栈信息保存到当前任务栈地址,保存SPSR/CPSR/R0-R14寄存器 */    
STACK_COPY: 
    LDR   R0,[R2],#4 
    STR   R0,[R1],#4 
    SUBS  R3,R3,#1 
    BNE   STACK_COPY 
    
taskBefore:

    sub lr, lr, #4                  /* lr -4 为指令SWI XXX的地址,低24位是软件中断号 */
    ldr R3,[lr,#0]                  /* lr -4 为指令SWI XXX的地址,低24位是软件中断号 */
    bic R3,R3,#0XFF000000           /* 取得arm指令的低24位立即数 */
    
    cmp R3,#0                       /* 判断24位立即数,如果是0,调用usrModeSpSetup函数 */
    beq  usrModeSpSetup

    cmp R3,#1                       /* 判断24位立即数,如果是1,调用task1Tcb函数 */
    ldr R0,=task1Tcb
    ldr R0,[R0]
    beq  taskTcbSwitch

    cmp R3,#2                       /* 判断24位立即数,如果是2,调用task2Tcb函数 */
    ldr R0,=task2Tcb
    ldr R0,[R0]
    beq  taskTcbSwitch

    cmp R3,#3                       /* 判断24位立即数,如果是3,调用task3Tcb函数 */
    ldr R0,=task3Tcb
    ldr R0,[R0]
    beq  taskTcbSwitch

    bne  END 
taskTcbSwitch:
    ldr pc, =taskSwitch

/******************************************************************************
* 建立用户模式栈空间
******************************************************************************/
usrModeSpSetup:
    mrs R0, CPSR                    /* Read the CPSR */
    bic R0, R0, #0xdF               /* Clear the mode irq fiq bits  */
    orr R0, R0, #0x10               /* Set the mode bits to FIQ mode */
    msr cpsr_c, R0                  /* 进入用户态,并设置用户态的SP,使能中断 */
    ldr sp, =0x33e00000             /* 设置用户栈指针起始值 */
    ldr R0, =USER_STACK                 /* 保存IRQ模式下的LR,用于调试 */
    str sp, [R0]

    ldr pc, =rootTask               /* rootTask开始用户模式 */

timer0_taskSched:
    ldr pc, =taskSched              /* 在SVC模式下进入taskSched函数调度函数 */
            
END:
    
    movne R0, #-1                   /* 没有该软中断对应函数,出错返回-1 */


3、任务切换函数taskswitch修改


/***********************************************************************************
函数功能: 实现任务切换,调用新得任务执行,并把控制权交给新任务.
入口参数: pTcb: 即将运行的任务的TCB指针.
返 回 值: none.
***********************************************************************************/


int taskSwitch(TCB * pTcb)
{

    /****************************************************************************** 
    * 即将运行任务的寄存器组地址, 汇编语言通过这个变量恢复寄存器 
    ******************************************************************************/
    nextTaskSp = &pTcb->strStackReg;

    /* 即将运行任务的TCB指针 */
    pCurTcb = pTcb;
    __asm__(

    /******************************************************************************
    * 获取将要运行任务的栈信息并运行新任务
    ******************************************************************************/
    " LDR    R0, =nextTaskSp \n\t"
    " LDR    R1, [R0] \n\t"

    " LDMIA  R1!, {R0} \n\t"      /* Spsr位置 */
    " MSR    SPSR, R0  \n\t"
    " LDMIA  R1!, {R0} \n\t"      /* cpsr位置 */

    " MOV  R13, R1 \n\t"          /* 开始恢复原用户态任务栈信息 */

    " LDMIA  R13!, {R0-R12}^ \n\t"  /* ^表示把任务栈里R0-R14恢复到user模式下的R0-R14中,只针对用户模式哦 */
    " ADD R13,R13,#0x4 \n\t"        /* 跳过恢复用户态的R13  */
    
    " LDMIA  R13!, {R14}^ \n\t"     /* ^表示把任务栈里R0-R14恢复到user模式下的R0-R14中,只针对用户模式哦 */

    " nop \n\t"
    " LDMIA  R13!, {R14} \n\t"      /* ^表示把任务栈里R15恢复到SVC模式下的R14中,只针对用户模式哦 */
    " nop \n\t"

    " LDR R13, =SVC_STACK \n\t"     /* 保存SVC模式下的SP栈,用于恢复和调试 */  
    " LDR R13, [R13] \n\t"  
    " ADD R13,R13,#0x48 \n\t"       /* 恢复SVC模式下的SP栈;这里可以释放掉SVC模式SP栈,因为要跳到用户模式 */

    " MOVS   R15,  R14 \n\t"        /* 进入用户模式, SVC模式下的SPSR值恢复到USR模式下的CPSR中 */
 
);
    return 0;
}


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容