2009.2.28
其实之前很早就看过uC/OS II这个操作系统。而且当时也看了一些材料。但是后来自己头脑发昏,以为搞嵌入式就一定要搞Linux或winCE,再加个TCP/IP之类的才是。最后吃力不讨好。反而自信心受重挫。最终呢,还是回归原始,回归最简单,最本质的那些东西。
这次呢,打算把uC/OS II这个操作系统移植到周立功公司的EASYARM2104上面。为什么会有这样的想法呢。其实本来是想自己实现一个操作系统的。一个简单的内核也行。也是呢,找了一大堆的编写操作系统的资料,还有一些国内外开源的嵌入式实时操作系统。所以,当时我们科研小组在做第二次报告的时候,我选的题目就是FDGKos的设计。我做完报告之后,吴老师说,我之前很多的师兄都有想实现一个系统的想法,但是,谈何容易呢。它要求一个人有很高的编程水平,及对硬件、操作系统原理十分了解才行。我后来反复的思考了吴老师说的这几句话。也不是没有道理的。那好。我不一步到位。我先把uC/OS II在LPC2104上面玩转了再说。
开始干活了才知道。原来一切都没有自己想象的那么容易。我很快了,花了几天时间,把绍贝贝学生翻译的那本书给啃了一遍。然后迫不及待的想运行起来。要运行起来,当然得先移植才行啊。这移植的工作量也不小啊。我想啊,没事,也当一次练兵的机会。周立功写的书里面有移植笔记,我先按着他的笔记,把代码敲进去。写入flash运行之后发现,我这程序,在系统初始化之后,创建了两个任务,但是这些任务只有刚开始执行以下,碰到有OSTimeDly()函数的时候,一调度,就没有办法回到那两个任务里面来。而是一直执行空闲任务。如果任务里面延迟函数用周立功写的DelayNS()函数的话,有没有问题。 这个问题一直困惑了我好久,也一直没有办法解决。代码了一遍又一遍,代码也读了一遍又一遍。最后火了。直接建立一个ucos for lpc210x的工程模板。一看,没有问题。那看来我之前写的那段程序里面有某一部分还有问题。可以是哪个变量定义不正确,也有可能少了一些关键的语句,而这语句关系着系统的正确运行。我找了老半天也找不出来是为什么。最后火了。一个一个文件的对比。我还不信找不出来。结果还真的找到了问题的关键所在。原来是我在移植的过程中,少做了一步。就是程序与芯片的相关中断源的挂接,使芯片在产生相应的中断后会调用相应的处理程序的这一步。这一步一般需要做两个事情:
- 增加汇编接口的支持。 XXX_handler HANDLER XXX_Exception
- 初始化向量中断控制器。
其中包括向量中断和时钟中断。这两个方面代码加上之后,就uC/OS II 就乖乖的正常运行了。
为什么我会忽略了这些呢。只有一个原因:我对中断还不是非常清楚。不明白他的重要性。故,还得重新复习下向量中断控制器这一内容。然后呢,将不按照周立功的移植过程,安装uC/OS II官方网站上,针对ARM7TDMI的标准移植过程再重新移植一下。
这里面涉及到一些问题,比较基本的,做做笔记。
- uC/OS II有哪些特点? 可裁剪,可以固化,实时的抢占式的多任务内核,它基本上用ANSI C编写的。包含一小部分的汇编代码。
- 移植uC/OS II需要具备哪些知识?
- 对目标体系结构要有很深的了解;
- 对OS原理要有较深的了解;
- 对所使用的编译器要有较深入的了解;
- 对需要移植的操作系统要有相当的了解;
- 对具体使用的芯片也要有一定的了解。
- 移植过程中要编写哪些文件?
- OS_CPU.H
- OS_CPU_A.ASM
- OS_CPU_C.C
- INCLUDES.H
- OS_CFG.H
- 还要对startup.s irq.s target.c做相应的修改。
- 移植uC/OS II 到 ARM7 为何使用SWI软件中断异常接口?
- 带T的ARM7处理器核具有两个指令集,用户任务还可以使用两种处理器模式:用户模式和系统模式,组合起来就有4种方式,各种方式对系统资源有不同的控制权。为了是底层接口函数与处理器状态无关,同时在任务调用相应的函数不需要知道函数位置,本移植使用软件中断指令SWI作为底层接口,使用不同功能号的区分不同的函数。用软件中断作为操作系统的底层接口就需要在C语言中使用SWI指令。
- ADS中关键之__swi的具体用法?
- __swi(0x02) void OS_ENTER_CRITICAL(void);
- 当执行这条指令是,相当于调用功能号为0x02的软件中断。之后的函数可以向一般的函数一样有参数,也有返回值。
- 移植过程中为什么不用ADS中的__irq关键字?
- 虽然ADS可以使用__irq关键字声明一个函数是用来处理中断的,从而避免使用汇编代码,但是在uC/OS II中不能这样处理。这是因为使用C语言无法确保堆栈的结构,而RTOS必须使堆栈保持一定结构。
- 移植代码为何要增加ChangeToSYSMode()、ChangeToUSRMode()、TaskIsARM()、TaskIsThumb()这四个函数?它们如何使用?
- 处理器模式转换函数及用于改变任务建立时默认的指令集。在相应任务建立后但还没有运行时调用。
To Be Continue......
2009.3.2
今天早上 ,再把中断控制器那节巩固了一下。在看了一些ARM汇编的语法,周立功移植的代码里面很多就可以看得懂了。或者说,终于看的更明白了。好不容易啊。这边有几个要说的:
- irq.s 里面我本来看的云里雾里的,后来发现,其实就是一个带参数的宏汇编。也就明白了整个中断的过程。周立功的移植过程中,把IRQ中断,时钟中断放在初始化第一个任务的时候,调用targetinit()的时候初始化的。而之前出现的问题:任务切换之后 ,无法切换回来一直在运行空闲进程其实就是由于刚开始没有初始化这两个中断引起的。现在明白了。
- 关于__swi关键字,我查了些资料。现在终于明白整个中断的过程。参见我保存的另外一篇关于 的文章。
To Be Continue.......
2009.3.3
今天的一个任务是自己动手编写EASYARM的启动程序,并移植uC/OS II. 一个目的是为之后自己编写OS做好铺垫。
一、 在初始化cpu堆栈的时候,碰到这样一个疑问:指令MSR cpsr_c,#0xd3 中的cpsr_c跟cpsr有什么不一样?查了一下资料,疑问解开!
MSR - Load specified fields of the CPSR or SPSR with an immediate constant, or from the contents of a general-purpose register.
Syntax:
MSR{cond} _, #immed_8r MSR{cond} _, Rm where: cond is an optional condition code. is either CPSR or SPSR. specifies the field or fields to be moved. can be one or more of:
c control field mask byte (PSR[7:0]) x extension field mask byte (PSR[15:8]) s status field mask byte (PSR[23:16) f flags field mask byte (PSR[31:24]). immed_8r is an expression evaluating to a numeric constant. The constant must correspond to an 8-bit pattern rotated by an even number of bits within a 32-bit word. Rm is the source register.
C 控制域屏蔽字节(psr[7:0])
X 扩展域屏蔽字节(psr[15:8])
S 状态域屏蔽字节(psr[23:16])
F 标志域屏蔽字节(psr[31:24])
常用于MRS或MSR指令,用于psr中的值转移到寄存器或把寄存器的内容加载到psr中.
如:
MSR CPSR_c,#0xd3
二、在看周立功的初始化代码时,__user_initial_stackheap: 这个函数的作用我不清楚,从字面上猜,应该是初始化堆(heap)。在之后的代码里面,没有看到相关的调用,那是不是没有什么用呢?我可以不用吗? 查了下资料,发现与BL __main有关。初始化代码的最后一个语句就是:BL __main 注释说,这样编译器就看可以直接跳转到主程序的Main里面了。它跳转之后会有一些操作,然后才会跳到主程序的main里面,而其中的操作就有一个是__user_initial_stackheap. 对于其中的作用,可以参考以下资料:
函数原型:
__value_in_regs struct __initial_stackheap __user_initial_stackheap(unsigned R0, unsigned SP, unsigned R2, unsigned SL);
函数 __user_initial_stackheap 所返回的参数:
r0:heap基地址;
r1:堆栈基地址;
r2:heap长度限制值(需要的话);
r3:堆栈长度限制值。
如果不在这个函数中设置R1,那么在 Reset init 中的 SP 会被继承下来。
通常有两种情况需要这个函数,当你使用 Scatter loading 时:
1.如果你的 Heap 和 Stack 不在同一块存储器区域中;
2.的 Heap 基址比 Stack 基址大。
此时需要用这个函数来指明,否则Link时或程序运行会出错。
下面是对示例函数__user_initial_stackheap() 进行移植的一个例子:
__value_in_regs struct __initial_stackheap __user_initial_stackheap(
unsigned R0, unsigned SP, unsigned R2, unsigned SL)
{
struct __initial_stackheap config;
config.heap_base = (unsigned int) 0x11110000;
// config.stack_base = SP; // optional
return config;
}
因为__user_initial_stackheap() 函数的全部执行效果就是返回一些数值,所
以只要符合接口的调用标准,直接用汇编来实现看起来更加直观一些:
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR r0,0x11110000
MOV pc,lr
如果不对这个函数进行移植,编译过程中将使用缺省的设置,这个设置适用
于ARM 公司的Integrator 系列平台。
(注意:ARM 的编译/连接工具链也提供了绕过库函数来设置运行时存储器模型
的方法,请参阅ARM 公司其他的相关文档。)
编写的bootloader程序,成功运行。不过还没添加中断模块,后面的路还很长,加油。
三、LDM, STM指令格式如下:
LDM{cond}<模式> Rn{!},reglist{^}
指令格式中,寄存器Rn为基址寄存器,装有传送数据的初始地址,Rn不允许为R15.后缀'!'表示最后的地址回写到Rn中。后缀“^"不允许在用户模式或系统模式下使用。若在LDM指令且寄存器列表中包含有PC时使用,那么除了正常的多寄存器传送外,将SPSR也拷贝到CPSR中,这可用于异常处理返回。使用后缀“^”进行数据传送且寄存器列表不包含PC时,加载/存储的是用户模式的寄存器,而不是当前模式的寄存器。 明白了,这两个后缀,这个中断服务子程序的汇编接口就很容易看懂了。
$IRQ_Label
SUB LR, LR, #4 ; Calculate the returning address 计算返回地址
STMFD SP!, {R0-R3, R12, LR} ; Protects the task environments 保存任务环境
MRS R3, SPSR ; Protects the status variable 保存状态
STMFD SP, {R3,LR}^ ; Protects SPSR and SP in user status, Notice: DO NOT write back.
; If the SP is written back, it should be adjusted to its appropriate value later.
NOP
SUB SP, SP, #4*2
MSR CPSR_c, #(NoInt | SYS32Mode) ; Switch to the System Mode 切换到系统模式
BL $IRQ_Exception_Function ; call the C interrupt handler funtion 调用c语言的中断处理程序
MSR CPSR_c, #(NoInt | IRQ32Mode) ; Switch bak to IRQ mode 切换回irq模式
LDMFD SP, {R3,LR}^ ; Recover SPSR and SP in user status, Notic: DO NOT write back.
; If the SP is written back, it should be adjusted to its appropriate value later.
MSR SPSR_cxsf, R3
ADD SP, SP, #4*2 ;
LDMFD SP!, {R0-R3, R12, PC}^
不过还是有个不明白,就是,之上的STMFD SP, {R3,LR}^ 指令,为什么不能回写呢?
四、郁闷,从下午写了段代码,实现定时控制LED闪烁。采用中断的方式。一直调到晚上现在,都没有办法调试出来。最后没辙了,把书上的例子一个一个的敲进去执行,反复对比。最终发现了问题的关键所在:1、移植的时候,也就是板刚开始初始化都是禁止中断的。这才导致一直都没有中断产生。2、定时器的分频值,我设置的太大了,T0PR = 99, T0MR0 = 1105920, 这样其实在我改过了第一个错误之后,是程序是没有问题的,只是现在是要间隔99秒才会闪烁一次。也就是说,闪烁的频率太低了,以至于我认为它没有闪烁,程序没有正确执行。把分频值调成0就可以了。 小小的一个细节,没有搞好,弄我的整个进度都延迟了好久,本来这个下午就要完成的,现在弄了一整天。效率太低了。其实这些细节的东西,才真正反映你掌握的情况,这个只能说明,我还是没学透。对整个过程还不是很清晰。好了,不说了,抱怨无用,现在休息,明天继续,明天开始要移植uC/OS II .
To Be Continue.........
3.10 大好天气!
1.OSTaskStkInit()
这个函数,要保存任务传递参数pdata,任务起始地址,处理器状态字,中断返回地址还要保存处理器的寄存器。
用户的C编译器不同,决定着使用寄存器传递参数pdata还是使用堆栈传递。这个事前要了解清楚该编译器。
移植时要了解处理器的文档,了解好堆栈的增长方式,以及堆栈指针指向像一个可以使用的堆栈还是上次入栈的数据。
2, 对这段语句非常不解:
LDR R0, =OS_TCBCur ; OSTCBCur->OSTCBStkPtr = SP;
LDR R1, [R0]
STR SP, [R1]
我觉得这样就可以了:
LDR R0, =OS_TCBCur ; OSTCBCur->OSTCBStkPtr = SP;
STR SP, [R0]
3,任务堆栈结构与中断、任务切换堆栈结构的区别与联系。各个模式的堆栈?
4,程序编译通过了,但是调试的时候,执行到osinit()这个函数的时候,一直会跑飞。不知道是什么原因。都搞了一个晚上了。一点进展都没有。郁闷啊。。。看来只能明天继续了。
To Be Continue....
3.11 又下雨了,真的不喜欢下雨天。
早上调试了一下,发现我这程序发生预取异常,所以来是会重启,可能是昨天在编写代码的时候,由于对堆栈的不熟悉而埋下的祸根。
晚上很兴奋,因为有了很大的进展。果然如我早上所想的那样,在软中断的时候,入栈出栈除了问题,导致,早上程序一运行到
OS_ENTER_CRITICAL()的时候,就出问题,晚上回来查了下,发现问题:
SoftwareInterrupt
- LDR SP,StackSVC ;set the stack
- STMFD SP!,{R0-R3,R12,LR} ;早上问题的所在!
- MOV R1,SP
- LDR R0,[LR,#-4] ;get the swi_num
- BIC R0,R0,#0xFF000000
- ;CMP R0,#1
- ;LDRLO PC,=OSCtxSw
- BL SWI_Exception
- LDMFD SP!,{R0-R3,R12,PC}^
StackSVC DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4) |
早上的问题是在入栈的时候,我使用:STMFD SP!,{R0-R12,PC}
这里面犯了两个错误:其一,应该把LR寄存器入栈,而我却把PC入栈。后面在获取中断号的时候,是针对LR寄存器惊醒操作的。其二,入栈之后的偏移量没注意,也就是说,如果我用原来的语句,就不能用LDR R0,[LR,#-4] 来获得中断号,这个还要进行相应的调整才行。
修改之后,在OS_TASK_IDLE_HOOK()函数里面添加了LED灯闪烁的语句,结果,LED等一直亮。调试通过。因为现在还没创建其他的任务,整个系统都在运行空闲任务。
接着,我又加了一个任务。发现有出现预取异常。我估计可能在OSCtxSw()函数里面还有想之前堆栈的问题。因为这里面涉及到任务的调度切换操作。不过现在很迟了,明天再调试咯。嘿嘿,回宿舍。
To be continue......
3.12
今天又出现了数据终止异常,郁闷,都不知道要怎么调试才好。调试能力有待于提高啊!
To be continue......
3.13
今天看看会不会有什么进展。
重新翻阅了jean J labrosse的书关于移植的那一章,上面说OSCtxSw()函数即任务级的切换是通过软件中断来实现,看来我之前那样移植还是有点问题。重新修改下。
之前对周立功的这个任务切换的函数还不是完全读懂,下午终于对这个函数完全理解了。
OSIntCtxSw
;下面为保存任务环境
LDR R2, [SP, #20] ;获取PC
LDR R12, [SP, #16] ;获取R12
MRS R0, CPSR
MSR CPSR_c, #(NoInt | SYS32Mode)
MOV R1, LR
STMFD SP!, {R1-R2} ;保存LR,PC
STMFD SP!, {R4-R12} ;保存R4-R12
MSR CPSR_c, R0
LDMFD SP!, {R4-R7} ;获取R0-R3
ADD SP, SP, #8 ;出栈R12,PC
MSR CPSR_c, #(NoInt | SYS32Mode)
STMFD SP!, {R4-R7} ;保存R0-R3
LDR R1, =OsEnterSum ;获取OsEnterSum
LDR R2, [R1]
STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum
;保存当前任务堆栈指针到当前任务的TCB
LDR R1, =OSTCBCur
LDR R1, [R1]
STR SP, [R1]
BL OSTaskSwHook ;调用钩子函数
;OSPrioCur <= OSPrioHighRdy
LDR R4, =OSPrioCur
LDR R5, =OSPrioHighRdy
LDRB R6, [R5]
STRB R6, [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!, {R4, R5} ;CPSR,OsEnterSum
;恢复新任务的OsEnterSum
LDR R3, =OsEnterSum
STR R4, [R3]
MSR SPSR_cxsf, R5 ;恢复CPSR
LDMFD SP!, {R0-R12, LR, PC }^ ;运行新任务
- 前半段为什么这么做,是因为任务切换的时候其实是调用了软件中断,软件中断事先会把R0-R3,LR,CPSR都保存起来。但是是保存在管理模式下SVC,但我们想要的操作是把它保存在任务堆栈里面,也就是要在系统模式SYS下保存,这两个模式涉及到的堆栈是不一样的,所以才会做之上那么麻烦的操作,而不是一个语句STMFD SP!,{R0-R12,LR,PC}就可以搞定的,而我之前就是这么干的。所以,要搞清楚当前模式堆栈与当前任务堆栈的区别和联系。
- 移植的官方教程上面说,任务级的调度要用软件中断来显示,而我一直没有用软件中断,想直接用一个宏来实现,这也是造成我一直都么有办法调试成功的原因之一。
- 系统复位或软件中断的时候,处理器是出于管理模式的。而要切换到系统模式之前,一定要使处理器处于某种异常模式,如管理模式。这点修订了,我上面说的 ,任务调度虽然没有用软中断实现,但是最初也是可以实现的。
- 还有,对其中的两次堆栈指针的调整,要明白其中的原理:第一个ADD SP, SP, #8,这个指令是因为软中断跳转到相应的处理函数之后不会返回,要用这个办法,把已经过期的寄存器值忽略。第二个:ADD SP, SP, #68,这个指令是因为在管理模式下高优先级就绪的任务的任务堆栈弹出来之后,用户模式或系统模式下的SP值也要做相应的调整。因为内容过期了。之所以这么做是因为两个模式下的SP、lR寄存器是不一样的。
- 经过今天的调试,发现,这其中还涉及到很多问题。任何一个细小的问题,没有处理好,系统就没有办法正常运行。真叫人头大啊。
To be continue......
3.14 大晴天,我喜欢。
一、中午调试了一下,发现,任务调度的时候,程序没有跳转到相应的就绪任务上,而是直接返回。最后,跟踪软件中断代码,发现,原来我两个任务的堆栈是一样的,所以,他调度了之后,马上返回来了。唉,又是粗心,害的我搞了那么久。不过现在还有一个问题就是,空闲任务,如果系统里面没有创建其他的任务的话,程序会调度到空闲任务那里,而如果有创建其他的任务的话,就没有办法正确的调度过去。我跟踪了一下寄存器,发现:在调度函数里面,
y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to HPT ready to run */
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
我设置最低优先级为60,程序跑到这里,Y应该是0x07, 而OSPrioHighRdy 最后得到的值应该是:0x3c,但是调试的时候发现,这个值是0x38. 也就是说,OSRdyTbl[y]等于0了。
出现了这样的问题,所以在要切换到空闲程序的时候,就出问题了,也就是出现了,我之前发现的预取中止异常和数据终止异常。
一直不懂为什么会出现这样的问题,我就直接把最高优先级设置成24,也就是,刚好最后的OSRdyTbl值为0。呵呵,只能这样了,将错就错。
这样,任务的切换总算是勉强可以了。那接下来要做的事情就是中断调度函数的调试咯。
二、晚上调试的时候发现了一个很奇怪的问题,我发现系统运行起来之后,OSRunning的值还是0,而不是1,我跟踪到OSStart()函数下面的调用的最高优先级任务的汇编函数。这里面最先涉及到对它赋值为1.
LDR R0,=OSRunning ;OSRunning = 1
MOV R1,#1
STR R1,[R0]
单步调试发现,执行完了,OSRunning值还是0,甚是纳闷。跟踪一下内存,发现地址是:0x40001A26. 一切就明白了,原来是内存没有对齐,最后一个语句没有办法对其进行赋值。因为在ARM里面地址是按字对齐的,地址是以0,4,8,C这样结尾的,而这个6结尾的地址当然俄米有办法正确的赋值咯。所以,只要对最后一个语句稍微修改一下就可以了。 改成:STRB R1,[R0] 或者:STRH R1,[R0]
To be continue........
3.15 大晴天。
今天要调试的问题是,中断之后的调度,可以调度到正确的任务上,但是,该任务无法正确的运行下去,会跑飞。我在想,有没有可能是之前的任务切换的时候,没有把当前模式下的LR寄存器保存好。以至于调度回来,程序没有办法正确的跳转。
但不管怎么样,移植就到今天为止吧!后面还有很多事情要做,没有办法在移植方面花那么多时间。 现在时间宝贵啊。
今天一整天的进度很慢,还是没有移植成功,不过,已经有很大的进步了。我发现问题是处在我编写的IRQ中断服务子程序上面。明天再修改下,看行不行。最后再做一个总结。
To be continue.....
3.16. 也是大晴天啊。
今天终于把程序调通了,uc/os II正常的在开发板上面跑起来。真不容易啊。看着那一闪一闪的LED等,激动之情油然而生。
回想整个过程历经了半个月(16天)。算算,进度还是很慢的。不过又不得花那么多时间。
总结一下,整个移植过程中的所得:
- 深入理解ARM汇编、并熟练使用。
- 进一步了解ADS1.2这个开发环境的使用
- 对ARM整个体系结构有了深入的理解。
- 对操作系统原理有了更深入的理解。尤其是任务的切换里面的cpu上下文的保护。
- 对uC/OS II 更加熟悉。
其实想想,之前如果基础比较好的话,这个过程,估计也就两三天的事情。
很多东西还真的要自己动手写一下,才会比较深刻的理解它。之前看得懂,并不代表你掌握了它。
其实整个过程最麻烦的就是对任务切换过程中cpu上下文的保护,你要对整个过程非常清晰,才能写对。
虽然移植完毕,但是,我编写的这个移植程序还不是非常稳定,还需要进一步改进,但是我不能再花时间和精力在这里了,后面还有很多事情要做呢。接下来要编写一个Shell,编写中间件:数据队列和串口驱动,然后试着与上位机用MODbus通信。上位机上还得编一个小程序。
其实很多东西都有现成的,别人已经做好的,如果在企业里面做一个产品的话,可以拿来主义,直接拿来用,但是作为一个学生,还是要自己编写,作为自己的学习历程。从而真正的掌握它。
The end..
附上我移植的程序:
1、boot.s
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; uC/OS-II Porting for LPC2104
; Compiler: ADS 1.2
; By Alexander Hwang
;
; Attention:
;
; Thumb mode is not considered yet.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
IMPORT InitTargetBeforeMain
IMPORT SoftwareInterrupt
EXPORT StackSVC
EXPORT StackUSR
;;defne the length of all kinds of stacks
SVC_STACK_LENGTH EQU 512
UND_STACK_LENGTH EQU 0
FIQ_STACK_LENGTH EQU 0
IRQ_STACK_LENGTH EQU 512
ABT_STACK_LENGTH EQU 0
SYS_STACK_LENGTH EQU 512
;;define the cpu mode
MODE_NOINT_SVC EQU 0xD3
MODE_NOINT_UND EQU 0xDB
MODE_NOINT_IRQ EQU 0xD2
MODE_NOINT_FIQ EQU 0xD1
MODE_NOINT_SYS EQU 0xDF
MODE_NOINT_ABT EQU 0xD7
CODE32
AREA vectors,CODE,READONLY
ENTRY
RESET
LDR PC,ResetAddress
LDR PC,UndefineAddr
LDR PC,SwiAddress
LDR PC,PrefetchAddr
LDR PC,DataAbortAddr
DCD 0xb9205f80
LDR PC,[pc,#-0xff0]
LDR PC,FIQAddress
ResetAddress DCD ResetInit
UndefineAddr DCD UndefineHANDLER
SwiAddress DCD SoftwareInterrupt
PrefetchAddr DCD PrefetchHANDLER
DataAbortAddr DCD DataAbortHANDLER
NoUse DCD 0
IRQ_Addr DCD 0
FIQAddress DCD FIQHANDLER
UndefineHANDLER
B UndefineHANDLER
PrefetchHANDLER
B PrefetchHANDLER
DataAbortHANDLER
B DataAbortHANDLER
FIQHANDLER
B FIQHANDLER
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ResetInit ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ResetInit
BL InitStack
B InitTargetBeforeMain
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; InitStack ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
InitStack
MOV R0,LR
MSR CPSR_c,#MODE_NOINT_SVC
LDR SP,StackSVC
MSR CPSR_c,#MODE_NOINT_UND
LDR SP,StackUND
MSR CPSR_c,#MODE_NOINT_FIQ
LDR SP,StackFIQ
MSR CPSR_c,#MODE_NOINT_IRQ
LDR SP,StackIRQ
MSR CPSR_c,#MODE_NOINT_ABT
LDR SP,StackABT
MSR CPSR_c,#MODE_NOINT_SYS
LDR SP,=StackUSR
MOV PC,R0
StackSVC DCD SVCStackSpace + (SVC_STACK_LENGTH - 1) * 4
StackUND DCD UNDStackSpace + (UND_STACK_LENGTH - 1) * 4
StackFIQ DCD FIQStackSpace + (FIQ_STACK_LENGTH - 1) * 4
StackIRQ DCD IRQStackSpace + (IRQ_STACK_LENGTH - 1) * 4
StackABT DCD ABTStackSpace + (ABT_STACK_LENGTH - 1) * 4
;StackSYS DCD SYSStackSpace + (SYS_STACK_LENGTH - 1) * 4
__user_initial_stackheap
LDR R0,=bottom_of_heap
MOV PC,LR
AREA MyStacks,DATA,ALIGN=2
;;程序使用编译器分配的空间作为堆栈而不是按照通常的做法把堆栈分配到RAM
;;的顶端,这样做有两个好处:
;; 1,不必知道RAM顶端的位置,增加了移植性。
;; 2,编译器给出的占用RAM空间的大小就是实际占用的大小,便于控制RAM的分配
;; 有个缺点就是分配太小了,引起程序执行不正常。要注意!
SVCStackSpace SPACE SVC_STACK_LENGTH * 4
UNDStackSpace SPACE UND_STACK_LENGTH * 4
FIQStackSpace SPACE FIQ_STACK_LENGTH * 4
IRQStackSpace SPACE IRQ_STACK_LENGTH * 4
ABTStackSpace SPACE ABT_STACK_LENGTH * 4
;SYSStackSpace SPACE SYS_STACK_LENGTH * 4
AREA Heap,DATA
bottom_of_heap SPACE 1
AREA MyStacks,DATA
StackUSR
END
2、os_cpu_a.s
;Define the size of the SVC Mode stack size.
SVC_STACK_LEGTH EQU 32
MODE_NOINT_SVC EQU 0xD3
MODE_NOINT_UND EQU 0xDB
MODE_NOINT_IRQ EQU 0xD2
MODE_NOINT_FIQ EQU 0xD1
MODE_NOINT_SYS EQU 0xDF
MODE_NOINT_ABT EQU 0xD7
CODE32
AREA |subr|, CODE, READONLY
IMPORT OSTCBCur
IMPORT OSTCBHighRdy
IMPORT OSPrioCur
IMPORT OSPrioHighRdy
IMPORT OSTaskSwHook
IMPORT OSRunning
IMPORT OSIntEnter
IMPORT StackSvc
EXPORT OSStartHighRdy
EXPORT OSIntCtxSw
EXPORT SoftwareInterrupt
SoftwareInterrupt
MRS R2,SPSR
MOV R3,LR
MSR CPSR_c,#MODE_NOINT_SYS
STMFD SP!,{R3}
STMFD SP!,{R0-R12,LR}
STMFD SP!,{R2}
B OSIntCtxSw
OSIntCtxSw
MSR CPSR_c,#MODE_NOINT_SYS ;Save SP to TCB Block.
LDR R1, =OSTCBCur
LDR R1, [R1]
STR SP, [R1]
BL OSTaskSwHook ;Call OSTaskSwHook
;OSPrioCur <= OSPrioHighRdy
LDR R4, =OSPrioCur
LDR R5, =OSPrioHighRdy
LDRB R6, [R5]
STRB R6, [R4]
;OSTCBCur <= OSTCBHighRdy
LDR R6, =OSTCBHighRdy
LDR R6, [R6]
LDR R4, =OSTCBCur
STR R6, [R4]
;Get new stack
LDR R4, [R6]
ADD SP, R4, #64 ;POP CPSR,R0-R12,LR,SP
LDR LR, [SP, #-8]
MSR CPSR_c, #MODE_NOINT_SVC ;Jump to SVC mode
MOV SP, R4 ;Set the stack
LDMFD SP!, {R5} ;CPSR
MSR SPSR_cxsf, R5 ;Pop CPSR
LDMFD SP!, {R0-R12, LR, PC }^ ;Run the new task.
OSStartHighRdy
MSR CPSR_c, #MODE_NOINT_SYS
;OSRunning +=1
LDR R4, =OSRunning
MOV R5, #1
STRB R5, [R4]
BL OSTaskSwHook ;Call the Hook Fuction
LDR R6, =OSTCBHighRdy
LDR R6, [R6]
LDR R4, [R6]
ADD SP, R4, #64 ;POP CPSR,R0-R12,LR,SP
LDR LR, [SP, #-8]
MSR CPSR_c, #MODE_NOINT_SVC ;Jump to SVC mode
MOV SP, R4 ;Set the stack
LDMFD SP!, {R5} ;CPSR
MSR SPSR_cxsf, R5 ;Pop CPSR
LDMFD SP!, {R0-R12, LR, PC }^ ;Run the new task.
EXPORT OSGetSR
OSGetSR
MRS R0,CPSR
ORR R1,R0,#0x80
MSR CPSR_c,R1
BX LR
EXPORT OSPutSR
OSPutSR
MSR CPSR_c,R0
BX LR
END
;/**********************************************************
;** End Of File
;***********************************************************/
3、OS_cpu.h
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned short INT16U;
typedef signed short INT16S;
typedef unsigned int INT32U;
typedef signed int INT32S;
typedef float FP32;
typedef double FP64;
typedef INT32U OS_STK;
typedef INT16U OS_CPU_SR;
OS_CPU_SR OSGetSR(void);
void OSPutSR(OS_CPU_SR cpu_sr);
#define OS_CRITICAL_METHOD 3
#define OS_ENTER_CRITICAL() (cpu_sr=OSGetSR())
#define OS_EXIT_CRITICAL() (OSPutSR(cpu_sr))
__swi(0x00) void OS_TASK_SW(void); /* Task Switch */
//void __swi(0x01) _OSStartHighRdy(void); /* start the highest task */
//void __swi(0x02) OS_ENTER_CRITICAL(void); /* 关中断 */
//void __swi(0x03) OS_EXIT_CRITICAL(void); /* 开中断 */
//__swi(0x02) void OS_ENTER_CRITICAL(void);
//__swi(0x03) void OS_EIXT_CRITICAL(void);
#define OS_STK_GROWTH 1
#define OS_CPU_EXT extern
//#define OS_TASK_SW() OSCtxSw()
4、os_cpu_c.c
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_data, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
INT32U task_addr;
opt = opt; /* 'opt' is not used, prevent warning */
stk = ptos; /* Load stack pointer */
task_addr = (INT32U)task & ~1;
*(stk) = (INT32U)task_addr; /* Entry Point */
*(--stk) = (INT32U)0x14141414L; /* R14 (LR) */
*(--stk) = (INT32U)0x12121212L; /* R12 */
*(--stk) = (INT32U)0x11111111L; /* R11 */
*(--stk) = (INT32U)0x10101010L; /* R10 */
*(--stk) = (INT32U)0x09090909L; /* R9 */
*(--stk) = (INT32U)0x08080808L; /* R8 */
*(--stk) = (INT32U)0x07070707L; /* R7 */
*(--stk) = (INT32U)0x06060606L; /* R6 */
*(--stk) = (INT32U)0x05050505L; /* R5 */
*(--stk) = (INT32U)0x04040404L; /* R4 */
*(--stk) = (INT32U)0x03030303L; /* R3 */
*(--stk) = (INT32U)0x02020202L; /* R2 */
*(--stk) = (INT32U)0x01010101L; /* R1 */
*(--stk) = (INT32U)p_data; /* R0 : argument */
*(--stk) = (INT32U)ARM_SVC_MODE_ARM; /* CPSR, ARM-mode */
return (stk);
}
5、IRQ.s
;;define the cpu mode
MODE_NOINT_SVC EQU 0xD3
MODE_NOINT_UND EQU 0xDB
MODE_NOINT_IRQ EQU 0xD2
MODE_NOINT_FIQ EQU 0xD1
MODE_NOINT_SYS EQU 0xDF
MODE_NOINT_ABT EQU 0xD7
IMPORT OSIntExit
IMPORT StackUSR
IMPORT OSIntEnter
CODE32
AREA IRQ,CODE,READONLY
MACRO
$IRQ_Handler HANDLER $IRQ_Exception_Function
EXPORT $IRQ_Handler
IMPORT $IRQ_Exception_Function
$IRQ_Handler
SUB LR,LR,#4
STMFD SP!,{R0-R3,R12,LR}
MSR CPSR_c,#MODE_NOINT_SYS ;Jump to the SYS mode
LDR SP,=StackUSR
STMFD SP!,{LR} ;Save the content.
STMFD SP!,{R0-R12,LR}
MSR CPSR_c,#MODE_NOINT_IRQ ;Save the CPSR And LR
MRS R2,SPSR
STMFD SP!,{R2}
MOV R3,LR
;LDR R3,[SP,#20]
MSR CPSR_c,#MODE_NOINT_SYS
STMFD SP!,{R2}
STR R3,[SP,#4*16]
MSR CPSR_c,#MODE_NOINT_IRQ ;Jump to the IRQ mode
BL OSIntEnter
BL $IRQ_Exception_Function
BL OSIntExit
MSR CPSR_c,#MODE_NOINT_IRQ
LDMFD SP!,{R2}
MSR SPSR_cxsf,R2
LDMFD SP!,{R0-R3,R12,PC}^
MEND
Timer0_Handler HANDLER Timer0_Exception
;IRQ_Handler HANDLER IRQ_Exception
END
;/*****************************************************
;** End Of File
;******************************************************/
6、target.c
#include "config.h"
extern void Timer0_Exception(void);
extern void app_main(void);
extern int old_main(void);
extern void Timer0_Handler(void);
void Timer0Init(void)
{
T0IR = 0xffffffff;
T0TC = 0;
//T0PR = 0;
T0TCR = 0x01;
T0MCR = 0x03;
T0MR0 = Fpclk;
//T0TCR = 0x03;
}
/**********************************************************
** Function name: VICInit
**
** Descriptions: Initialize the Interrupt Vevtor Controller
**
** input parameters: None
** Returned value: None
**
** Used global variables: None
** Calling modules: None
**
** Created by: Chenmingji
** Created Date: 2004/02/02
**----------------------------------------------------------
** Modified by:
** Modified date:
**----------------------------------------------------------
**************************************************************/
void VICInit(void)
{
VICIntEnClr = 0xffffffff;
//VICDefVectAddr = (uint32)IRQ_Handler;
VICVectAddr0 = (uint32)Timer0_Handler;
VICVectCntl0 = (0x20 | 0x04);
VICIntEnable = 1 << 4;
}
void TargetInit(void)
{
VICInit();
Timer0Init();
}
/*
void TargetInit(void)
{
VICIntSelect = 0x00000000;
VICVectCntl0 = 0x20 | 4;
VICVectAddr0 = (uint32)Time0_Handler;
VICIntEnable = 1 << 4;
T0MCR = 0x03;
T0MR0 = Fpclk;
T0TCR = 0x03;
T0TCR = 0x01;
}
*/
void InitTargetBeforeMain(void)
{
MAMCR = 2;
/* set up the clock. */
PLLCON = 1;
VPBDIV = 0;
PLLCFG = 0x23;
PLLFEED = 0xAA;
PLLFEED = 0x55;
while((PLLSTAT & (1 << 10)) == 0);
PLLCON = 3;
PLLFEED = 0xAA;
PLLFEED = 0x55;
/* initial VIC */
VICIntEnClr = 0xffffffff;
VICVectAddr = 0;
VICIntSelect = 0;
/* initial Time0 */
T0IR = 0xffffffff;
T0TC = 0;
T0PR = 0;
/* then jump to main() */
old_main();
//app_main();
}