关于s3c2440的SWI,如何设置r0

关于s3c2440的SWI,如何设置r0,第1张

推荐一:OS_CPU.H

1、定义与编译器无光的数据类型

只是按照不同的编译器编写对应的数据类型的typedef 对应于ARM7的数据类型的编写如下

typedef unsigned char BOOLEAN/* 布尔变量*/

typedef unsigned char INT8U /* 无符号8位整型变量*/

typedef signed char INT8S /* 有符号8位整型变量*/

typedef unsigned short INT16U/* 无符号16位整型变量*/

typedef signed short INT16S/* 有符号16位整型变量*/

typedef unsigned int INT32U/* 无符号32位整型变量*/

typedef signed int INT32S/* 有符号32位整型变量*/

typedef float FP32 /*单精度浮点数(32Bit)*/

typedef double FP64 /*双精度浮点数(64Bit)*/

/*在上面定义的数据类型中按照ARM7的堆栈宽度选择INT32U*/

typedef INT32U OS_STK/* 堆栈是32位宽度*/

接下来一部分是为了兼容低版本UCOS的数据类型所编写的代码,在UCOS-II中暂不考虑

2 与处理器相关的代码

先定义中断的实现方式,预先设定的中断方式有三种,在ARM7中设置为方式 2

#define OS_CRITICAL_METHOD 2/*选择开,关中断的方式 */

接下来的一段是我暂时还没有完全搞懂的一部分,只知道是设定了12个软件中断的函数,当调用这

些函数之前都会执行对应中断号的事情。具体的看到后面应该能完全搞懂软件中断的实现方式,

该段代码在后面的文件中会有具体的解释,这里暂时不看

定义堆栈的生长方式,ARM7内核支持两种生长方式,但是ADS的C语言编译器只支持从上往下的生

长方式,因此:

#define OS_STK_GROWTH 1 /* 堆栈是从上往下长的,0-从下往上的生长方式 */

最后几行分别定义了用户模式01和系统模式1f以及IRQ中断禁止的指令80三个立即数,方便调用.

还有两个预定义往后看应该知道作用,暂不考虑,不是很重要.

软中断:

中断不返回形式:

void _swi(swi_num) swi_name(arguments)

返回一个结果到R0中

int _swi(swi_num) swi_name(arguments)

最多可以返回四个结果R0-R3到一个结构struct type中

type(返回类型) _value_in_regs(返回多个结果的修饰符) _swi(swi_num) swi_name(arguments)

在ARM中实现软中断的方法我在blog里面搜了很多文章也没有看到讲的通俗一点的,还是自己看

ARM的移植代码吧首先定义了一堆软中断的中断号,其中0和1的中断服务子程序是用汇编编写的,

其他的都是在c语言编写的中断服务子程序SWI_Exception中。

__swi(0x00) void OS_TASK_SW(void)

/* 任务级任务切换函数 */

__swi(0x01) void _OSStartHighRdy(void)

/* 运行优先级最高的任务*/

__swi(0x02) void OS_ENTER_CRITICAL(void)

/* 关中断 */

__swi(0x03) void OS_EXIT_CRITICAL(void)

/* 开中断 */

__swi(0x40) void *GetOSAddr(int Index)

/* 获取系统服务函数入口*/

__swi(0x41) void *GetUsrAddr(int Index)

/* 获取自定义服务函数入口 */

__swi(0x42) void OSISRBegin(void)

/* 中断开始处理*/

__swi(0x43) int OSISRNeedSwap(void)

/* 判断中断是否需要切换*/

__swi(0x80) void ChangeToSYSMode(void)

/* 任务切换到系统模式 */

__swi(0x81) void ChangeToUSRMode(void)

/* 任务切换到用户模式 */

__swi(0x82) void TaskIsARM(INT8U prio)

/* 任务代码是ARM代码 */

__swi(0x83) void TaskIsTHUMB(INT8U prio)

/* 任务代码是THUMB */

比如在程序运行到调用OS_TASK_SW(void)函数时,就产生软件中断,然后就进入中断服务子程序,

按照什么指令走呢?恩,就按照下面这个代码,这个代码是将软件中断异常处理程序挂接到内核

的作用的,是在启动代码中实现的:

LDR PC,SWI_Addr

SWI_Addr DCD SoftwareInterrupt

因此当产生软中断之后PC就跳到了SoftwareInterrupt,这时就算真正进入了软件异常中断处理部

分了,然后就是执行下面的汇编代码SoftwareInterrupt

LDR SP, StackSvc

/*重新设置堆栈指针*/

STMFD SP!,

/*保存 R0,R1,R2,R3,R12,LR(R14),注意为什么只保存这几个

寄存器呢,因为R4-R11存储局部变量,编译器自动保护他们*/

MOV R1, SP /* R1指向参数存储位置 */

MRS R3, SPSR /*保存管理模式的状态寄存器*/

TST R3, #T_bit /* 中断前是否是Thumb状态 */

LDRNEH R0, [LR,#-2] /* 若是,取得Thumb状态SWI号*/

BICNE R0, R0, #0xff00 /*THUMB指令SWI功能号为8位 */

LDREQ R0, [LR,#-4] /* 为零即ARM指令取得SWI号 */

BICEQ R0, R0, #0xFF000000

/*在ARM指令集中SWI功能号为24位所以高8位清零r0=SWI号*/

CMP R0, #1 /*

LDRLO PC, =OSIntCtxSw /*疑惑ing */

/* 功能号为0到OSIntCtxSw执行中断任务切换函数 */

LDREQ PC, =__OSStartHighRdy/*SWI为1第一次任务切换*/

BL SWI_Exception/*否则进入c编写的中断函数 */

LDMFD SP!, /*R0-R3,R12,LR出栈 */

StackSvc

DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)

怎么进入c编写的中断服务子程序SWI_Exception呢?通过下面的申明

IMPORT SWI_Exception 软中断异常处理程序

表示将c程序中的该函数挂接到此段汇编代码中,同样的道理

EXPORT __OSStartHighRdy

EXPORT OSIntCtxSw 中断退出时的入口

参见startup.s中的IRQ_Handler

EXPORT SoftwareInterrupt 软中断入口上面的申明是将该段汇编代码挂接到外面,

因此在外部可以直接调用函数名

继续看OS_CPU_A.S的其他部分代码,就是两个软件异常中断处理函数OSIntCtxSw和OSStarHighRdyOSIntCtxSw代码是中断服务子程序使得更高优先级的任务进入就绪状态后,中断返回后需要切换到该任务时调用的,这是被切换的任务的CPU寄存器的值已经在响应中断后存入了堆栈中,因此,这里不需要重复保存了直接切换任务即可,具体过程看代码OSIntCtxSw

下面为保存任务环境当响应软件异常中断后进入了系统模式,在上面的代码中我们可以看到,进入系统模式时保存的堆栈结构从顶到底依次是:R0,R1,R2,R3,R12,LR,而在用户模式中任务的堆栈结构应该是:OsEnterSum,CPSR,RO-12,LR,PC,所以在进行软件中断任务切换之前先要保存原来任务的堆栈结构。

LDR R2, [SP, #20] 获取PC

LDR R12, [SP, #16] 获取R12

MRS R0, CPSRMSR CPSR_c, #(NoInt | SYS32Mode)

MOV R1, LR

STMFD SP!, 保存LR,PC

STMFD SP!,保存R4-R12MSR CPSR_c, R0

LDMFD SP!, 获取R0-R3

ADD SP, SP, #8 出栈R12,PC

MSR CPSR_c, #(NoInt | SYS32Mode)

STMFD SP!, 保存R0-R3

LDR R1, =OsEnterSum 获取OsEnterSum

LDR R2, [R1]

STMFD SP!,保存CPSR,OsEnterSum保存当前任务堆栈指针到当前任务的TCB

LDR R1, =OSTCBCur

LDR R1, [R1]

STR SP, [R1]BL OSTaskSwHook调用钩子函数

OSPrioCur <= OSPrioHighRdy

LDR R4, =OSPrioCur

LDR R5, =OSPrioHighRdy

LDRBR6, [R5]

STRBR6, [R4]

OSTCBCur <= OSTCBHighRdy

LDR R6, =OSTCBHighRdy

LDR R6, [R6]

LDR R4, =OSTCBCur

STR R6, [R4]

OSIntCtxSw_1

获取新任务堆栈指针

LDR R4, [R6]

ADD SP, R4, #68 17寄存器CPSR,OsEnterSum,R0-R12,LR,SP

LDR LR, [SP, #-8]

MSR CPSR_c, #(NoInt | SVC32Mode)进入管理模式

MOV SP, R4 设置堆栈指针LDMFD SP!,CPSR,OsEnterSum

恢复新任务的OsEnterSum

LDR R3, =OsEnterSum

STR R4, [R3]

MSR SPSR_cxsf, R5 恢复CPSR

LDMFD SP!, ^ 运行新任务

__OSStartHighRdy

MSR CPSR_c, #(NoInt | SYS32Mode)调整到管理模式

告诉uC/OS-II自身已经运行

LDR R4, =OSRunning

MOV R5, #1

STRBR5, [R4] 标记多任务运行标记为真BL OSTaskSwHook调用钩子函数,可以运行用户自定义的函数LDR R6, =OSTCBHighRdyR6存有最高优先级的就绪任务的控制块地址

LDR R6, [R6]

B OSIntCtxSw_1 转到前面编写的中断返回函数块的任务跳转部分的代码,因为这两个函数都要用到这部分代码,进入这段代码之前高优先级的就绪任务的任务控制快地址存在R6中。AREASWIStacks, DATA, NOINIT,ALIGN=2

SvcStackSpace SPACE SVC_STACK_LEGTH * 4 管理模式堆栈空间 OSIntCtxSw_1的代码:OSIntCtxSw_1

获取新任务堆栈指针

LDR R4, [R6] 任务控制块的堆栈指针放在R6中,现在放在R4中

ADD SP, R4, #68 17寄存器CPSR,OsEnterSum,R0-R12,LR,SP

LDR LR, [SP, #-8]

MSR CPSR_c, #(NoInt | SVC32Mode)进入管理模式

MOV SP, R4 设置堆栈指针,R4存有没有改动过的堆栈指针LDMFD SP!,CPSR,OsEnterSum

恢复新任务的OsEnterSum

LDR R3, =OsEnterSum

STR R4, [R3]

MSR SPSR_cxsf, R5 恢复CPSR

LDMFD SP!, ^ 运行新任务,恢复现场,异常处理返回;中断返回指令的寄存器列表其中必须包括PC后的^符号,表示这是一条特殊形式的指令。这条指令在从存储器中装载PC的同时,CPSR也得到恢复。这里使用的堆栈指针SP是属于异常模式的寄存器,每个异常模式有自己的堆栈指针。SoftwareInterrupt

LDR SP, StackSvc重新设置堆栈指针

STMFD SP!, 保存寄存器

MOV R1, SP R1指向参数存储位置MRS R3, SPSR

TST R3, #T_bit 中断前是否是Thumb状态

LDRNEH R0, [LR,#-2]是: 取得Thumb状态SWI号

BICNE R0, R0, #0xff00

LDREQ R0, [LR,#-4]否: 取得arm状态SWI号

BICEQ R0, R0, #0xFF000000

r0 = SWI号,R1指向参数存储位置

CMP R0, #1

LDRLO PC, =OSIntCtxSw

LDREQ PC, =__OSStartHighRdy SWI 0x01为第一次任务切换BL SWI_Exception

LDMFD SP!, ^

StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)OSIntCtxSw

下面为保存任务环境

LDR R2, [SP, #20] 获取PC(LR)

LDR R12, [SP, #16] 获取R12

MRS R0, CPSRMSR CPSR_c, #(NoInt | SYS32Mode)

MOV R1, LR

STMFD SP!, 保存LR,PC

STMFD SP!,保存R4-R12MSR CPSR_c, R0

LDMFD SP!, 获取R0-R3

ADD SP, SP, #8 出栈R12,PC

MSR CPSR_c, #(NoInt | SYS32Mode)

STMFD SP!, 保存R0-R3

LDR R1, =OsEnterSum 获取OsEnterSum

LDR R2, [R1]

STMFD SP!,保存CPSR,OsEnterSum保存当前任务堆栈指针到当前任务的TCB

LDR R1, =OSTCBCur

LDR R1, [R1]

STR SP, [R1]BL OSTaskSwHook调用钩子函数

OSPrioCur <= OSPrioHighRdy

LDR R4, =OSPrioCur

LDR R5, =OSPrioHighRdy

LDRBR6, [R5]

STRBR6, [R4] 把OSPrioHighRdy最高优先级的就绪任务传给OSPrioCur

OSTCBCur <= OSTCBHighRdy

LDR R6, =OSTCBHighRdy

LDR R6, [R6]

LDR R4, =OSTCBCur

STR R6, [R4] 将最高优先级的任务控制块指针传给当前任务控制块指针

关于中断和时钟节拍,UCOS-II对于ARM7通用的中断服务程序的汇编与c函数接口如下:MACRO和MEND伪指令用于宏定义,MACRO标识宏定义的开始,MEND标识宏定义的结束。定义之后在程序中就可以通过宏指令多次调用该段代码MACRO

$IRQ_Label HANDLER $IRQ_Exception_EXPORT $IRQ_Label 输出的标号

IMPORT $IRQ_Exception_ 引用的外部标号$IRQ_Label

SUB LR, LR, #4 计算返回地址

STMFD SP!,保存任务环境

MRS R3, SPSR保存状态

STMFD SP, ^ 保存用户状态的R3,SP,LR,注意不能回写

如果回写的是用户的SP,所以后面要调整SP

LDR R2, =OSIntNesting OSIntNesting++

LDRBR1, [R2]

ADD R1, R1, #1

STRBR1, [R2]SUB SP, SP, #4*3

MSR CPSR_c, #(NoInt | SYS32Mode)切换到系统模式

CMP R1, #1

LDREQ SP, =StackUsr

BL $IRQ_Exception_ 调用c语言的中断处理程序MSR CPSR_c, #(NoInt | SYS32Mode)切换到系统模式

LDR R2, =OsEnterSum OsEnterSum,使OSIntExit退出时中断关闭

MOV R1, #1

STR R1, [R2]BL OSIntExitLDR R2, =OsEnterSum 因为中断服务程序要退出,所以OsEnterSum=0

MOV R1, #0

STR R1, [R2]MSR CPSR_c, #(NoInt | IRQ32Mode)切换回irq模式

LDMFD SP, ^ 恢复用户状态的R3,SP,LR,注意不能回写

如果回写的是用户的SP,所以后面要调整SP

LDR R0, =OSTCBHighRdy

LDR R0, [R0]

LDR R1, =OSTCBCur

LDR R1, [R1]

CMP R0, R1ADD SP, SP, #4*3

MSR SPSR_cxsf, R3

LDMEQFD SP!, ^ 不进行任务切换

LDR PC, =OSIntCtxSw 进行任务切换

MEND二:OS_CPU_C.C个文件中要求用户编写10个简单的C函数,但是只有1个函数是必要的,其余的函数必须声明,但不一定要包含任何代码,大致看了一下作用好像是用来调试之类的。唯一要编写的是OSTaskStkInit()OSTaskStkInit()函数的功能是初始化任务的栈结构,任务的堆栈结构与CPU的体系结构、编译器有密切的关联。从ARM的结构可以写出如下的栈结构:程序计数器PC,程序链接器LR,R12-R1,R0用于传递第一个参数pdata,CPSR/SPSR,关中断计数器(用于计算关中断的次数,这样就实现了中断的嵌套),返回的地址指针是指向的最后一个存入的数据,而不是一个空地址。软件中断异常SWI服务程序C语言部分void SWI_Exception(int SWI_Num, int *Regs):参数SWI_Num对应前面文件中定义的功能号,其中0、1号的功能在后面的文件中定义,这里只定义了其他10个功能。2、3分别对应关中断和开中断关中断:MRS R0, SPSR //在软件中断的时候直接对程序状态保存寄存器SPSR *** 作也就是对CPSR的 *** 作

ORR R0, R0, #NoInt//在汇编语言中对寄存器的对应位置位用ORR,清零用BIC

MSR SPSR_c, R0//SPSR_c表示的是只改变SPSR的控制段的8位代码,其他三段_f,_s,_x中标志位在_f段,其他为保留位开中断:MRS R0, SPSR //在开中断中基本与上面相同,只是ORR改成BIC清零

BIC R0, R0, #NoInt

MSR SPSR_c, R由于需要实现中断嵌套,所以只有当关中断的计数器减为0的时候才能够开中断,而且每次关中断的时候该计数器都应该加1。另外,插入汇编语言时用_asm指令。80、81、82、83分别对应系统模式、用户模式、ARM指令集、THUMB指令集系统模式:MRS R0, SPSR

BIC R0, R0, #0x1f //先将控制模式的低5位清零

ORR R0, R0, #SYS32Mode //设置成系统模式的1F

MSR SPSR_c, R0用户模式:MRS R0, SPSR

BIC R0, R0, #0x1f

ORR R0, R0, #USR32Mode //设置成用户模式的10

MSR SPSR_c, R0ARM指令集与THUMB指令集的代码如下:ptcb = OSTCBPrioTbl[Regs[0]]

if (ptcb != NULL)

{

ptcb ->OSTCBStkPtr[1] &= ~(1 <<5)

}ptcb = OSTCBPrioTbl[Regs[0]]

if (ptcb != NULL)

{

ptcb ->OSTCBStkPtr[1] |= (1 <<5)

}昨天就是看到这里,出现了一个意识到是不能忽悠的地方就是UCOS里面的任务控制块OS_TCB的概念,因此今天的任务就是把这部分看看。。。大概回忆了一下昨天晚上的工作,开始今天的工作吧

一点一点来,什么不会就学什么,都不会就都学。。。没有问题只要你肯努力。。。。。。__OSStartHighRdy

MSR CPSR_c, #(NoInt | SYS32Mode) MSR:在ARM中只有MSR能够直接设置状态寄存器CPSR或SPSR,可以是立即数或者源寄存器,NoInt是禁止中断,SYS32Mode是系统模式

告诉uC/OS-II自身已经运行

LDR R4, =OSRunning OSRunning正在运行多任务的标志,=OSRunning是把OSRunning的地址加载到R4,R4里存的是一个地址。。。

MOV R5, #1

STRBR5, [R4] 将R5存储到R4存的地址的变量即OSRunning中,也就是将OSRunning置1BL OSTaskSwHook调用钩子函数,OSTaskSwHook 是用于扩展的,在任务切换的时候执行用户自己定义的功能。LDR R6, =OSTCBHighRdyOSTCBHighRdy指向最高优先级任务的控制块TCB的指针!!!将放指针的地址放到R6中。

LDR R6, [R6] 将R6地址处的数据读出即OSTCBHighRdy的地址放到R6中

B OSIntCtxSw_1 跳转到OSIntCtxSw_1 AREASWIStacks, DATA, NOINIT,ALIGN=2

SvcStackSpace SPACE SVC_STACK_LEGTH * 4 管理模式堆栈空间继续昨天没有看完的代码OSIntCtxSw

下面为保存任务环境

LDR R2, [SP, #20] 获取PC,放入R2

LDR R12, [SP, #16] 获取R12,//R12存的什么东西啊???

MRS R0, CPSRMSR CPSR_c, #(NoInt | SYS32Mode) 进入系统模式并禁止中断

MOV R1, LRR1放LR值

STMFD SP!, 保存LR,PC,将R1,R2存入SP

STMFD SP!,保存R4-R12,将R4-12存入SPMSR CPSR_c, R0 再回到之前的模式

LDMFD SP!, 获取R0-R3

ADD SP, SP, #8 出栈R12,PC

MSR CPSR_c, #(NoInt | SYS32Mode)

STMFD SP!, 保存R0-R3

LDR R1, =OsEnterSum 获取OsEnterSum

LDR R2, [R1]

STMFD SP!,保存CPSR,OsEnterSum保存当前任务堆栈指针到当前任务的TCB

LDR R1, =OSTCBCur

LDR R1, [R1]

STR SP, [R1]BL OSTaskSwHook调用钩子函数

OSPrioCur <= OSPrioHighRdy

LDR R4, =OSPrioCur

LDR R5, =OSPrioHighRdy

LDRBR6, [R5]

STRBR6, [R4]

OSTCBCur <= OSTCBHighRdy

LDR R6, =OSTCBHighRdy

LDR R6, [R6]

LDR R4, =OSTCBCur

STR R6, [R4]

OSIntCtxSw_1

获取新任务堆栈指针

LDR R4, [R6] 把OSTCBHighRdy指向最高优先级任务的控制块TCB的指针给R4

ADD SP, R4, #68 17寄存器:CPSR,OsEnterSum,R0-R12,LR,SP

LDR LR, [SP, #-8] 取出LR放到LR

MSR CPSR_c, #(NoInt | SVC32Mode)进入管理模式并且保持禁止中断

MOV SP, R4 设置堆栈指针LDMFD SP!,CPSR,OsEnterSum。LDMFD数据出栈,放入R4,R5

恢复新任务的OsEnterSum

LDR R3, =OsEnterSum OsEnterSum的地址存入R3

STR R4, [R3] 把R4的值赋给OsEnterSum

MSR SPSR_cxsf, R5 恢复CPSR;在管理模式里是修改SPSR

LDMFD SP!, ^ 运行新任务 ,恢复现场,异常处理返回

在ucos的中断函数中是每次写中断函数都需要OSIntEnter()和OSIntExit() 用来判断中断嵌套层数和进行任务调度,有些情况是这样的,例如有的书上的程序中有RTC中断服务函数,但是并没有调用 *** 作系统的进入中断服务函数OSIntEnter()和退出中断服务函数OSIntExit(),

解释是因为在RTC中断服务函数中没有调用任何 *** 作系统的服务函数(如发送信号量之类的函数),故不需要 *** 作系统干预

下面就上述解释,以源码为依据进行阐述个人理解

void OSIntEnter (void)

{

if (OSRunning == OS_TRUE)

{

if (OSIntNesting <255u)

{

OSIntNesting++//对OSIntNesting变量进行了加1 *** 作

}

}


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12050567.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-20
下一篇 2023-05-20

发表评论

登录后才能评论

评论列表(0条)

保存