下面是在Micrium公司网站上下载的基于ADuC7027平台运行的uCOS-II操作系统代码的简单分析。
一、中断分析
当CPU收到中断信号在本指令执行完后立即响应中断,其响应过程如下:
CPU收到中断信号→当前指令执行完成→程序寄存器PC根据中断种类跳入中断向量表中→根据中断向量表进入中断服务函数.
uCOS-II的中断向量表代码如下:
PRESERVE8
AREA RESET, CODE, READONLY
ARM
Vectors
LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, Prefetch_Addr
LDR PC, Abort_Addr
NOP
LDR PC, IRQ_Addr
LDR PC, FIQ_Addr
Reset_Addr DCD Reset_Handler
Undef_Addr DCD OS_CPU_ARM_ExceptUndefInstrHndlr
SWI_Addr DCD OS_CPU_ARM_ExceptSwiHndlr
Prefetch_Addr DCD OS_CPU_ARM_ExceptPrefetchAbortHndlr
Abort_Addr DCD OS_CPU_ARM_ExceptDataAbortHndlr
NOP
IRQ_Addr DCD OS_CPU_ARM_ExceptIrqHndlr
FIQ_Addr DCD OS_CPU_ARM_ExceptFiqHndlr
中断服务函数OS_CPU_ARM_ExceptIrqHndlr的代码如下:
AREA CODE, CODE, READONLY
CODE32
OS_CPU_ARM_ExceptIrqHndlr
SUB LR, LR, #4 ; LR offset to return from this exception: -4.
STMFD SP!, {R0-R12, LR} ; Push working registers.
MOV R3, LR ; Save link register.
MOV R0, #OS_CPU_ARM_EXCEPT_IRQ
B OS_CPU_ARM_ExceptHndlr
OS_CPU_ARM_ExceptHndlr的代码如下:
AREA CODE, CODE, READONLY
CODE32
OS_CPU_ARM_ExceptHndlr
MRS R1, SPSR ; Save CPSR (i.e. exception's SPSR).
;DETERMINE IF WE INTERRUPTED A TASK OR ANOTHER LOWER PRIORITY EXCEPTION:
;SPSR.Mode = SVC : task,
;SPSR.Mode = FIQ, IRQ, ABT, UND : other exceptions,
;SPSR.Mode = USR : *unsupported state*.
AND R2, R1, #OS_CPU_ARM_MODE_MASK
CMP R2, #OS_CPU_ARM_MODE_SVC
BNE OS_CPU_ARM_ExceptHndlr_BreakExcept
OS_CPU_ARM_ExceptHndlr_BreakExcept的代码如下:
OS_CPU_ARM_ExceptHndlr_BreakExcept
MRS R2, CPSR ; Save exception's CPSR.
; Change to SVC mode & disable interruptions.
MSR CPSR_c, #(OS_CPU_ARM_CONTROL_INT_DIS | OS_CPU_ARM_MODE_SVC)
; HANDLE NESTING COUNTER:
LDR R3, __OS_IntNesting ; OSIntNesting++;
LDRB R4, [R3]
ADD R4, R4, #1
STRB R4, [R3]
MSR CPSR_cxsf, R2 ; RESTORE INTERRUPTED MODE.
; EXECUTE EXCEPTION HANDLER:
LDR R3, __OS_CPU_ExceptHndlr ; OS_CPU_ExceptHndlr(except_type = R0);
MOV LR, PC
BX R3
; Change to SVC mode & disable interruptions.
MSR CPSR_c, #(OS_CPU_ARM_CONTROL_INT_DIS | OS_CPU_ARM_MODE_SVC)
; HANDLE NESTING COUNTER:
LDR R3, __OS_IntNesting ; OSIntNesting--;
LDRB R4, [R3]
SUB R4, R4, #1
STRB R4, [R3]
MSR CPSR_cxsf, R2 ; RESTORE INTERRUPTED MODE.
; RESTORE OLD CONTEXT:
LDMFD SP!, {R0-R12, PC}^ ; Pull working registers and return from exception.
OS_CPU_ExceptHndlr函数的代码如下:
void OS_CPU_ExceptHndlr (CPU_DATA except_type)
{
CPU_FNCT_VOID pfnct;
CPU_INT32U irq_sig;
CPU_INT32U irq_en;
CPU_INT08U byte_low;
CPU_INT08U byte_mid;
CPU_INT08U byte_high;
CPU_INT08U src;
/* If this exception is an IRQ. */
if (except_type == OS_CPU_ARM_EXCEPT_IRQ) {
irq_en = IRQEN;
irq_sig = IRQSIG;
irq_sig &= irq_en;
/* Handle 1st byte ... */
byte_low = (CPU_INT08U)(irq_sig & 0xFF);
while (byte_low != 0) {
src = BSP_UnMapTbl[byte_low];
if (src > 0) {
pfnct = BSP_IntVectTbl[src];
if (pfnct != (CPU_FNCT_VOID)0) {
(*pfnct)();
}
}
byte_low &= ~(1 << src);
}
/* Handle 2nd byte ... */
byte_mid = (CPU_INT08U)((irq_sig >> 8) & 0xFF);
while (byte_mid != 0) {
src = BSP_UnMapTbl[byte_mid];
pfnct = BSP_IntVectTbl[src + 8];
if (pfnct != (CPU_FNCT_VOID)0) {
(*pfnct)();
}
byte_mid &= ~(1 << src);
}
/* Handle 3rd byte ... */
byte_high = (CPU_INT08U)((irq_sig >> 16) & 0xFF);
while (byte_high != 0) {
src = BSP_UnMapTbl[byte_high];
pfnct = BSP_IntVectTbl[src + 16];
if (pfnct != (CPU_FNCT_VOID)0) {
(*pfnct)();
}
byte_high &= ~(1 << src);
}
} else {
/* Infinite loop on other exceptions. */
/* Should be replaced by other behavior (reboot, etc.) */
while (DEF_TRUE) {
;
}
}
}
BSP_IntVectTbl表是记录中断服务函数的地址指针,其声明形式如下:
typedef void (*CPU_FNCT_VOID)(void);
static CPU_FNCT_VOID BSP_IntVectTbl[24];
二、 时钟分析
#define MAIN_OSC_FRQ 41780000L
CPU_INT32U BSP_CPU_ClkFreq (void)
{
CPU_INT08U clk_div_bits;
CPU_INT08U clk_div;
CPU_INT32U clk_freq;
clk_div_bits = (POWCON & 0x07);
clk_div = 1 << clk_div_bits;
clk_freq = MAIN_OSC_FRQ / clk_div;
return (clk_freq);
}
static void BSP_Tmr_TickInit (void)
{
CPU_INT32U clk_freq;
BSP_IntVectSet(BSP_IRQ_TIMER0, Tmr_TickISR_Handler);
T0CON = DEF_BIT_06 /* Periodic mode. */
| DEF_BIT_03; /* Prescaler = 256. */
clk_freq = BSP_CPU_ClkFreq();
T0LD = clk_freq / 256 / OS_TICKS_PER_SEC; /* Assign load value. */
T0CON |= DEF_BIT_07; /* Enable timer. */
BSP_IntEn(BSP_IRQ_TIMER0);
}
三、任务切换
任务切换是通过改变程序寄存器的值使高优先级的任务获得CPU使用权的。当然,任务切换之前需要保存前一任务的现场,恢复即将执行任务的现场。任务切换代码如下:
AREA CODE, CODE, READONLY
CODE32
OSCtxSw
; SAVE CURRENT TASK'S CONTEXT:
STMFD SP!, {LR} ; Push return address, //满栈递减,将链接寄存器内容拷入堆栈中
STMFD SP!, {LR}
STMFD SP!, {R0-R12} ; Push registers, //将寄存器内容拷入堆栈中
MRS R0, CPSR ; Push current CPSR,
TST LR, #1 ; See if called from Thumb mode,
ORRNE R0, R0, #OS_CPU_ARM_CONTROL_THUMB ; If yes, set the T-bit.
STMFD SP!, {R0}
LDR R0, __OS_TCBCur ; OSTCBCur->OSTCBStkPtr = SP;
LDR R1, [R0]
STR SP, [R1]
LDR R0, __OS_TaskSwHook ; OSTaskSwHook();
MOV LR, PC
BX R0
LDR R0, __OS_PrioCur ; OSPrioCur = OSPrioHighRdy;
LDR R1, __OS_PrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, __OS_TCBCur ; OSTCBCur = OSTCBHighRdy;
LDR R1, __OS_TCBHighRdy
LDR R2, [R1]
STR R2, [R0]
LDR SP, [R2] ; SP = OSTCBHighRdy->OSTCBStkPtr;
; RESTORE NEW TASK'S CONTEXT:
LDMFD SP!, {R0} ; Pop new task's CPSR,
MSR SPSR_cxsf, R0
LDMFD SP!, {R0-R12, LR, PC}^ ; Pop new task's context.
一般的任务都会在无限循环中增加OSTimeDly()函数,用于时间延迟,但此函数中也调用了OS_Sched()将CPU使用权切换到就绪表中任务优先级最高的任务中,此过程的实现主要是调用上面函数的结果。