MicroC/OS-II Structure 경희대학교 컴퓨터공학과 조 진 성
Contents Introduction Kernel Structure Task Management Time Management Event Control Block Semaphore Management Mutual Exclusive Semaphore Event Flags Management Message Mail Box Management Message Queue Management Memory Management MicroC/OS-II Porting
Introduction
Introduction MicroC/OS-II 개요 1992년 Jean J. Labrosse가 만든 실시간 운영체제 학교나 개인의 교육과 같은 비상업적 목적에 한해 자유로이 사용 가능한 공개소스로서, 소스코드의 수정 및 커널의 내부 구조를 이해하기 용이함. 용도 : 각종 장비 개발이 가능 공식 사이트: http://www.ucos-ii.com(소스 다운로드 가능) 상업적인 목적에 사용될 경우 라이센스를 따로 얻어야 함.
Introduction (Cont’d) MicroC/OS-II는 Multitasking이 가능한 Preemptive Real time Kernel MicroC/OS-II는 태스크를 64개까지 관리할 수 있다. 최상위,최하위 4개는 예약된 우선순위이므로 56개의 Task 사용가능 엄격하게 관리할 수 있다면 OS_LOWEST_PRIO를 제외하고 모든 우선순위 사용 가능하다. Portable 이식성이 높은 ANSI-C로 작성되었고, 일부 프로세서에 의존적인 부분만 어셈블리어로 작성됨. 8Bit, 16Bit, 32Bit 및 64Bit, DSP로도 Porting할 수 있다. Realiable 안정-결정적인 시스템에 사용할 수 있을 정도로 강인하고 안전한 운영체제 FAA(Federal Aviation Administration)에서 승인됨.(2000년 7월) Romable Scalable
Introduction (Cont’d) 임베디드 운영체제로써 대표적인 비-상용 공개형 커널 신뢰성과 안정성을 가진다. 작은 사이즈 – 많은 시스템에 적용가능 작은 임베디드 시스템에 탑재 가능하며 임베디드 시스템 중에서도 강력한 네트워크가 필요한 곳과 높은 성능 시스템에 사용하는 것이 적합 (TCP/IP 스택 및 GUI환경 라이센스 판매) 프로젝트에 따른 소스 코드의 절약이 가능 스택체크, 처리시간 체크, Mailbox, Queue, Semaphore, Memory partition 등의 시스템 서비스 제공 인터럽트 관리 태스크의 수행을 일시 중지하거나 재개가 가능하다. 인터럽트 중첩(up to 255 levels deep) 단점 초기 개발 투자와 라이센스 등의 후처리 문제로 인해 마음대로 사용하거나 배포에 어려움이 있음
Kernel Structure
uC/OS-II File Structure Kernel Structure uC/OS-II File Structure
Kernel Structure (Cont’d) uC/OS-II 핵심 서비스 Enabled when set to 1 in OS_CFG.H OS_ENTER_CRITICAL() OS_EXIT_CRITICAL() OSInit() OSStart() OSIntEnter() OSIntExit() OSSchedLock() OS_SCHED_LOCK_EN OSSchedUnlock() OSVersion()
Kernel Structure (Cont’d) Critical Section, OS_ENTER_CRITICAL() & OS_EXIT_CRITICAL() MicroC/OS-II는 OS_ENTER_CRITICAL()과 OS_EXIT_CRITICAL()라는 매크로를 사용하여 Interrupt를 비활성화/활성화한다. OS_ENTER_CRITICAL()과 OS_EXIT_CRITICAL()은 Critical Section을 감싸기 위해 항상 다음과 같이 쌍으로 사용한다. OS_ENTER_CRITICAL()과 OS_EXIT_CRITICAL()은 서로 다른 세 가지 방법으로 구현된다. (OS_CPU.H에 정의한 OS_CRITICAL_METHOD상수를 통해 사용) 1.OS_ENTER_CRITICAL()와 OS_EXIT_CRITICAL()에 인터럽트를 비활성화/ 활성화하는 프로세서 명령을 정의하여 사용 2.인터럽트 비활성화/활성화 상태를 Stack에 저장하여 사용 3.프로세서 상태 워드의 값을 C함수 안의 지역변수에 저장하는 기능을 가진 컴파일러를 사용할 경우 . OS_ENTER_CRITICAL(); /* uC/OS-II critical code section */ OS_EXIT_CRITICAL();
Kernel Structure (Cont’d) Task – Infinite loop function MicroC/OS-II는 Task를 64개까지 관리할 수 있다. MicroC/OS-II는 8개의 예약된 태스크 중 Idle, CPU 사용률 계산 Task를 사용 중 작은 숫자일수록 더 높은 우선순위.(ex. 1 -> 최상위 우선순위) Task 우선순위를 통해 Task를 식별한다. void YourTask (void *pdata) { for (;;){ /* USER CODE */ Call one of uC/OS-II’s services: OSFlagPend(); OSMboxPend(); OSMutexPend(); OSQPend(); OSSemPend(); OSTaskDel(OS_PRIO_SELF); OSTaskSuspend(OS_PRIO_SELF); OSTimeDly(); OSTimeDlyHMSM(); }
Kernel Structure (Cont’d) Task 상태
Kernel Structure (Cont’d) Task Control Block Task 생성시 각 Task 별로 Task control block인 OS_TCB를 할당 받는다. OS_TCB는 RAM에 상주하면서 태스크의 상태를 관리한다. 주요 변수 설명 OSTCBStkPtr : Task의 스택 꼭대기를 가리키는 포인터. 어셈블리어 코드의 문맥 전환 코드 부분에서 접근하는 유일한 필드. 맨 위에 배치하는 것이 어셈블리 언어에서 접근하도록 하는데 좋음. OSTCBStat : Task의 상태, 이 값들은 ucos.c에 있음 OSTCBDly : Task가 지연될 필요가 있을 때나 일정한 timeout을 두고 이벤트 발생을 기다릴 때 사용. OSTCBX, OSTCBY, OSTCBBitX, OSTCBBitY : Task가 생성되거나 우선순위가 바뀔 때 사용 OSTCBNext, OSTCBPrev : OS_TCB를 양방향으로 링크하는데 사용. OSTCBEventPtr : Event control block의 포인터
Kernel Structure (Cont’d) Task Control Block typedef struct os_tcb { OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */ #if OS_TASK_CREATE_EXT_EN > 0 void *OSTCBExtPtr; /* Pointer to user definable data for TCB */ OS_STK *OSTCBStkBottom; /* Pointer to bottom of stack */ INT32U OSTCBStkSize; /* Size of task stack (in number of stack) */ INT16U OSTCBOpt; /* Task options as passed by OSTaskCreateExt() */ INT16U OSTCBId; /* Task ID (0..65535) */ #endif struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */ struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */ #if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0) || (OS_MUTEX_EN > 0) OS_EVENT *OSTCBEventPtr; /* Pointer to event control block */ #if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) void *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost()
Kernel Structure (Cont’d) Task Control Block #if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) #if OS_TASK_DEL_EN > 0 OS_FLAG_NODE *OSTCBFlagNode; /* Pointer to event flag node */ #endif OS_FLAGS OSTCBFlagsRdy; /* Event flags that made task ready to run */ INT16U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event INT8U OSTCBStat; /* Task status */ INT8U OSTCBPrio; /* Task priority (0 == highest, 63 == lowest)*/ INT8U OSTCBX; /* Bit position in group corresponding to task priority (0..7) */ INT8U OSTCBY; /* Index into ready table corresponding to task priority */ INT8U OSTCBBitX; /*Bit mask to access bit position in ready table */ INT8U OSTCBBitY; /*Bit mask to access bit position in ready group */ BOOLEAN OSTCBDelReq; /* Indicates whether a task needs to delete */ } OS_TCB;
Kernel Structure (Cont’d) Task Control Block [0] [1] OSTCBNext OSTCBNext OSTCBNext [2] [3] . OSTCBNext OSTCBNext OSTCBNext OSTCBNext [61] [62] [63] 가장 높은 우선 순위 OSTCBHighRdy 현재 실행 중인 타스크 OSTCBCur 지역 변수 ptcb OSTCBList OS_TCB OS_TCB OS_TCB OSTCBNext OSTCBNext OSTCBNext OSTCBPrev OSTCBPrev OSTCBPrev OSTCBFreeList OS_TCB OS_TCB OS_TCB 자유 리스트 OSTCBNext OSTCBNext OSTCBNext
Kernel Structure (Cont’d) Ready List 두 개의 변수 OSRdyGrp과 OSRdyTbl[ ]로 Ready 상태의 Task를 관리 현재 ready 상태인 Task를 효율적을 검색 OSRdyGrp Task의 우선순위에 의해 그룹화 한 그룹 당 8개의 우선순위(Task ID)를 가짐 OSRdyGrp내의 각 비트는 해당 그룹의 Ready 여부를 표시 OSRdyTbl[ ] 지정된 우선순위의 각 태스크가 Ready 인지 여부를 표시
Kernel Structure (Cont’d) Ready List
Kernel Structure (Cont’d) 태스크를 준비상태로 만들기 준비 리스트에서 태스크 삭제 준비 리스트에서 최상위 우선순위 찾기 OSRdyGrp |= OSMapTbl[prio >> 3]; OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07]; if((OSRdyTbl[prio>>3] &= ~OSMapTbl[prio & 0x07]) == 0) OSRdyGrp &= ~OSMapTbl[prio >> 3]; y = OSUnMapTbl[OSRdyGrp]; x = OSUnMapTbl[OSRdyTbl[y]]; prio = (y<<3) + x;
Kernel Structure (Cont’d) Ready List에서 최상위 우선순위 태스크 찾기
Kernel Structure (Cont’d) Task Scheduling MicroC/OS-II는 준비상태의 Task중 항상 가장 높은 우선순위의 태스크를 실행 Task 수준의 Scheduling은 OS_Sched() 함수에 의해 이뤄진다. ISR 수준의 Scheduling은 OSIntExit() 함수에 의해 이뤄진다. void OS_Sched (void) { #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr; #endif INT8U y; OS_ENTER_CRITICAL(); if ((OSIntNesting == 0) && (OSLockNesting == 0)) { y = OSUnMapTbl[OSRdyGrp]; OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]); if (OSPrioHighRdy != OSPrioCur) { OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; OSCtxSwCtr++; OS_TASK_SW(); } OS_EXIT_CRITICAL();
Kernel Structure (Cont’d) Task 레벨 Context Switching OS_TASK_SW() 호출할 때 현재 Task 문맥 저장
Kernel Structure (Cont’d) 현재 Task 재실행 void OSCtxSw (void) { PUSH R1, R2, R3 and R4 onto the current stack; OSTCBCur->OSTCBStkPtr = SP; OSTCBCur = OSTCBHighRdy; SP = OSTCBHighRdy->OSTCBStkPtr; POP R4, R3, R2 and R1 from the new stack; Execute a return from interrupt instruction; }
Kernel Structure (Cont’d) Scheduler 잠그기/풀기 OSSchedLock()과 OSSchedUnlock()함수를 이용하여 Scheduler를 잠그고 푼다. MicroC/OS-II는 255단계까지 Scheduling 잠금을 지원한다. OSLockNesting이 0이 될 때 Scheduler가 풀린다. void OSSchedLock (void) { #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr; #endif if (OSRunning == TRUE) { OS_ENTER_CRITICAL(); if (OSLockNesting < 255) { OSLockNesting++; } OS_EXIT_CRITICAL();
Kernel Structure (Cont’d) Scheduler 잠그기/풀기 void OSSchedUnlock (void) { #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr; #endif if (OSRunning == TRUE) { OS_ENTER_CRITICAL(); if (OSLockNesting > 0) { OSLockNesting--; if ((OSLockNesting == 0) && (OSIntNesting == 0)) { OS_EXIT_CRITICAL(); OS_Sched(); } else { }
Kernel Structure (Cont’d) Idle Task / OS_TaskIdle() MicroC/OS-II는 항상 Idle Task를 생성한다. Idle Task는 다른 Task가 실행하지 않을 때 실행한다. Idle Task는 항상 최하위 우선순위(OS_LOWEST_PRIO)가 배정된다. 응용 프로그램이 Idle Task를 삭제할 수 없다. void OS_TaskIdle (void *pdata) { #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr; #endif pdata = pdata; for (;;) { OS_ENTER_CRITICAL(); OSIdleCtr++; OS_EXIT_CRITICAL(); OSTaskIdleHook(); }
Kernel Structure (Cont’d) 통계 Task / OS_TaskStat() MicroC/OS-II는 실행 통계를 내주는 태스크를 포함하고 있다. OS_CFG.H에 있는 OS_TASK_STAT_EN 설정상수를 1로 설정하면 Task가 생성 통계 Task는 CPU 사용률을 1초 마다 %로 계산한다. 통계 Task를 사용한다면 시스템 초기화시 처음으로 생성된 Task에서 통계 Task를 호출하도록 해야 한다. void main (void) { OSInit(); /* Initialize uC/OS-II */ /* Install uC/OS-II's context switch vector */ /* Create your startup task (for sake of discussion, TaskStart()) */ OSStart(); /* Start multitasking */ } void TaskStart (void *pdata) { /* Install and initialize uC/OS-II’s ticker */ OSStatInit(); /* Initialize statistics task */ /* Create your application task(s) */ for (;;) { /* Code for TaskStart() goes here! */ }
Kernel Structure (Cont’d) 통계 Task
Kernel Structure (Cont’d) 통계 Task 초기화 / OSStatInit() OSStatInit()은 Idle Task외에 아무 태스크도 실행되지 않는 상태에서 Idle 카운터(OSIdleCtr) 값을 얼마나 증가 할 수 있는지 알아내는 일을 한다. void OSStatInit (void) { #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr; #endif OSTimeDly(2); OS_ENTER_CRITICAL(); OSIdleCtr = 0L; OS_EXIT_CRITICAL(); OSTimeDly(OS_TICKS_PER_SEC); OSIdleCtrMax = OSIdleCtr; OSStatRdy = TRUE; }
Kernel Structure (Cont’d) 통계 Task void OS_TaskStat (void *pdata) { #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr; #endif INT32U run; INT32U max; INT8S usage; pdata = pdata; while (OSStatRdy == FALSE){ OSTimeDly(2 * OS_TICKS_PER_SEC); } max = OSIdleCtrMax / 100L; for (;;) { OS_ENTER_CRITICAL(); OSIdleCtrRun = OSIdleCtr; run = OSIdleCtr; OSIdleCtr = 0L; OS_EXIT_CRITICAL(); if (max > 0L) { usage = (INT8S)(100L - run / max); if (usage >= 0) { OSCPUUsage = usage; } else { OSCPUUsage = 0; } max = OSIdleCtrMax / 100L; OSTaskStatHook(); OSTimeDly(OS_TICKS_PER_SEC);
Kernel Structure (Cont’d) MicroC/OS-II의 Interrupt MicroC/OS-II에서는 어셈블리로 Interrupt Service Routine (ISR)을 작성해야 하나 C컴파일러가 Inline 어셈블러를 지원한다면 C소스코드에 ISR을 작성 해도 된다. Interrupt 관련 함수 OSIntEnter(), OSIntExit(), OSIntCtxSW() YourISR: Save all CPU registers; Call OSIntEnter() or, increment OSIntNesting directly; if (OSIntNesting == 1) { OSTCBCur->OSTCBStkPtr = SP; } Clear interrupting device; Re-enable interrupts (optional) Execute user code to service ISR; Call OSIntExit(); Restore all CPU registers; Execute a return from interrupt instruction; 의사코드
Kernel Structure (Cont’d) MicroC/OS-II의 Interrupt
Kernel Structure (Cont’d) Clock Tick / OSTimeTick() MicroC/OS-II에서 타임 아웃 기능과 시간지연 기능 등을 위해 Clock Tick을 사용 Multitasking을 시작한 뒤 즉 반드시 OSStart()를 호출한 뒤에 Clock Tick Interrupt를 활성화 해야 한다. Clock Tick 관련 함수 OSTimeTick(), OSTickISR() void OSTickISR(void) { Save processor registers; Call OSIntEnter() or increment OSIntNesting; if (OSIntNesting == 1) { OSTCBCur->OSTCBStkPtr = SP; } Post a 'dummy' message (e.g. (void *)1) to the tick mailbox; Call OSIntExit(); Restore processor registers; Execute a return from interrupt instruction;
Kernel Structure (Cont’d) MicroC/OS-II 초기화 MicroC/OS-II의 시스템 서비스를 사용하기 위해서는 가장 먼저 OSInit()를 호출 다음 장의 그림은 OSInit()을 호출해서 초기화한 MicroC/OS-II의 모든 변수와 자료구조를 나타내고 있고 OS_CFG.H에 있는 #define상수를 다음과 같이 설정했다고 가정한다. OS_TASK_STAT_EN을 1로 설정 OS_FLAG_EN을 1로 설정 OS_LOWEST_PRIO를 63으로 설정 OS_MAX_TASKS를 62로 설정
Kernel Structure (Cont’d) MicroC/OS-II 초기화
Kernel Structure (Cont’d) MicroC/OS-II 초기화
Kernel Structure (Cont’d) MicroC/OS-II 시작 void main (void) { OSInit(); /* Initialize uC/OS-II */ . Create at least 1 task using either OSTaskCreate() or OSTaskCreateExt(); OSStart(); /* Start multitasking! OSStart() will not return */ } void OSStart (void) { INT8U y; INT8U x; if (OSRunning == FALSE) { y = OSUnMapTbl[OSRdyGrp]; x = OSUnMapTbl[OSRdyTbl[y]]; OSPrioHighRdy = (INT8U)((y << 3) + x); OSPrioCur = OSPrioHighRdy; OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; OSTCBCur = OSTCBHighRdy; OSStartHighRdy(); }
Kernel Structure (Cont’d) MicroC/OS-II 시작
Task Management
Task Management Task Management void YourTask (void *pdata) { for (;;) { /* USER CODE */ Call one of uC/OS-II's services: OSFlagPend(); OSMboxPend(); OSMutexPend(); OSQPend(); OSSemPend(); OSTaskSuspend(OS_PRIO_SELF); OSTimeDly(); OSTimeDlyHMSM(); } void YourTask (void *pdata) { /* USER CODE */ OSTaskDel(OS_PRIO_SELF); }
Task Management (Cont’d) Task 생성 / OSTaskCreate() or OSTaskCreateExt() Task는 Multitasking을 시작하기 전에 초기화 코드에서 생성하거나, Multitasking을 시작한 뒤 실행 중인 다른 태스크가 생성할 수도 있다. Multitasking을 시작하기 전 (OSStart() 호출 이전), 최소한 하나 이상의 Task를 생성 해야만 한다. 참고) Interrupt Service Routine내에서는 Task를 생성할 수 없다. OSTaskCreate( void (*task) (void *pd), void *pdata, OS_STK *ptos, INT8U prio ) 첫 번째 전달인자: 생성하고자 하는 Task의 시작번지 두 번째 전달인자: 생성하려는 Task로 넘겨줄 전달인자 세 번째 전달인자: 생성하고자 하는 Task에 할당한 Stack사용 시작 번지 네 번째 전달인자: Task의 우선 순위 OSTaskCreateExt() 함수는 전달인자가 9개이며, 이는 참고서적을 통해서 확인해보기 바란다.
Task Management (Cont’d) Task 생성 / OSTaskCreate() or OSTaskCreateExt() INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio) { #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr; #endif void *psp; INT8U err; #if OS_ARG_CHK_EN > 0 if (prio > OS_LOWEST_PRIO) { return (OS_PRIO_INVALID); } OS_ENTER_CRITICAL(); if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { OSTCBPrioTbl[prio] = (OS_TCB *)1; OS_EXIT_CRITICAL(); psp = (void *)OSTaskStkInit(task, pdata, ptos, 0); err = OS_TCBInit(prio, psp, (void *)0, 0, 0, (void *)0, 0); if (err == OS_NO_ERR) { OSTaskCtr++; if (OSRunning == TRUE) { OS_Sched(); } } else { OSTCBPrioTbl[prio] = (OS_TCB *)0; return (err); return (OS_PRIO_EXIST);
Task Management (Cont’d) Task Stack 각 Task는 고유한 Stack공간이 있어야 하며, 이는 OS_STK타입으로 정의하며 연속된 메모리공간을 확보해야 한다. Stack을 위한 공간은 정적으로(컴파일 할 때) 할당하거나 동적으로 (실행시) 할당할 수 있다. static OS_STK MyTaskStack[stack_size]; OS_STK MyTaskStack[stack_size]; Static Stack OS_STK *pstk;pstk = (OS_STK *)malloc(stack_size); if (pstk != (OS_STK *)0) { /* Make sure malloc() has enough space */ Create the task; } Using malloc() to allocate stack space for a task
Task Management (Cont’d) Task Stack Stack을 위한 공간은 C컴파일러의 malloc()함수를 사용해서 동적으로 할당할 수 있지만 메모리 단편화(Fragmentation)현상이 발생할 수 있음으로 유의 Free heap (3Kb) A (1Kb) B (1Kb) C (1Kb) Free(1Kb) 할당 전 할당 후 A, C 반환 후 (단편화 발생)
Task Management (Cont’d) Task Stack 사용방향 스택을 하위 메모리서 상위 매모리 순서로 쓸 경우(OS_STK_GROWTH = 0) OS_STK TaskStk[TASK_STK_SIZE]; OSTaskCreate(task, pdata, &TaskStk[0], prio); 스택을 상위 메모리서 하위 메모리 순서로 쓸 경우(OS_STK_GROWTH = 1) OSTaskCreate(task, pdata, &TaskStk[TASK_STK_SIZE-1], prio); 상, 하향 방식 스택을 모두 지원하는 코드 #if OS_STK_GROWTH == 0 #else #endif
Task Management (Cont’d) Stack 점검 / OSTaskStkChk() Task에서 실제로 얼마만큼의 Stack공간을 사용하는지 검사 검사결과를 바탕으로 Stack공간에 필요이상의 메모리를 할당하는 것을 방지 전달 인자 prio : 태스크 우선순위 pdata : OS_STK_DATA 타입의 구조체를 가리키는 포인터. 스택에서 사용한 바이트 수, 사용하지 않는 바이트 수 (uCOS_II.H 참조)
Task Management (Cont’d) Stack 점검 / OSTaskStkChk()
Task Management (Cont’d) Task 삭제 요청 / OSTaskDelReq() 공유자원을 소유하고 있는 Task로 하여금 자원을 반환하고, 스스로를 삭제하도록 알려 주는 방식의 함수 Task 삭제를 요청하는 함수와 삭제되는 함수 양쪽에서 모두 OSTaskDelReq() 함수를 호출해야 한다. 전달 인자 prio : 삭제 요구를 하기 위한 우선 순위 void RequestorTask (void *pdata) { INT8U err; pdata = pdata; for (;;) { /* Application code */ if ('TaskToBeDeleted()' needs to be deleted) { while (OSTaskDelReq(TASK_TO_DEL_PRIO) != OS_TASK_NOT_EXIST) { OSTimeDly(1); } Task를 스스로 삭제하도록 요청하는 코드
Task Management (Cont’d) Task 삭제 요청 / OSTaskDelReq() OSTaskDel() 실행 과정 조건 검사 : Idle Task 삭제 시도 여부, 삭제 태스크의 존재 여부 OS_TCB 삭제 Ready list 로부터 삭제 MailBox, Queue, Semaphore에 대기중인 OS_TCB 삭제 지연 카운트를 0으로 설정 : Task가 인터럽트 재개시까지 tick ISR이 준비되지 않게 하기 위해 OSTCBStat 플래그를 OS_STAT_RDY로 설정 사용자 정의 확장 TCB를 반납 OSTaskDelHook() Task 카운터 감소 우선 순위 테이블에서 삭제 OSTCBList에서 시작하는 OS_TCB리스트에서 삭제 free OS_TCB list로 OS_TCB 반환 스케줄러를 호출 : OSTaskDel() 실행 중 ISR에 의해 생성된 높은 우선순위의 태스크가 있을 시
Task Management (Cont’d) Task 삭제 요청 / OSTaskDelReq() void TaskToBeDeleted (void *pdata) { INT8U err; pdata = pdata; for (;;) { /* Application code */ if (OSTaskDelReq(OS_PRIO_SELF) == OS_TASK_DEL_REQ) { Release any owned resources; De-allocate any dynamic memory; OSTaskDel(OS_PRIO_SELF); } else { } 자신을 삭제하도록 요청하는 Task
Task Management (Cont’d) Task 우선순위 변경 / OSTaskChangePrio() 우선순위를 Run Time시에 이 함수를 이용하여 동적으로 변경할 수 있다. 주의) 새로 지정한 우선순위를 이미 사용하고 있지 않아야 한다. 전달인자 Oldprio: 이전 우선순위 Newprio: 새로 지정하는 우선순위 우선순위 변화 과정 Task가 실행 준비가 되면 ready list에서 제거되고 Task가 새 우선 순위를 할당 받으면 ready list에 놓여진다. Task가 실행 준비가 되지 않으면 태스크는 새 우선 순위에서 실행될 준비가 되지 않는다. Task가 이벤트 발생을 기다리면 태스크는 event waiting list에서 제거되고 새 우선 순위의 waiting list에 놓여진다. Task가 OS_TCB 체인에서 제거되어 새 우선 순위를 받는 동안 OSTimeTick()에 의하여 태스크가 실행 준비가 되는 것을 방지한다. 인터럽트가 enabled면 OS_TCB의 OSTCBX, OSTCBBitX, OSTCBY, OSTCBBitY가 재계산 된다.
Task Management (Cont’d) Task 일시 중단 / OSTaskSuspend() 태스크를 일시 중단하는 함수. 주의) 이 함수에 의해 중단된 태스크는 OSTaskResume 함수를 통해서만 중단상태에서 빠져 나올 수 있다. 주의) 이벤트(메시지, 세마포어, 큐 등)를 대기중인 태스크에 대해 이 함수를 호출하면 이벤트가 발생했을 경우 처리할 수 없을 것이다. 전달인자 prio: 중단하려는 태스크의 우선순위. OSTaskSuspend() 실행 과정 해당 Task가 Idle 상태인지 검사 해당 Task의 priority가 유효한지 검사 해당 Task가 존재하면 실행 Ready List에서 삭제 OS_TCB의 OS_STAT_SUSPEND 플래그를 설정한다. 스케줄러를 호출한다.
Task Management (Cont’d) Task 일시 중단 / OSTaskResume() 일시 중단한 태스크를 재실행시킨다. 주의) 일시 중단한 태스크를 재실행할 수 있는 유일한 함수(OSTaskSuspend) 전달인자 prio: 재실행하고자 하는 태스크의 우선순위. OSTaskResume() 실행 과정 해당 Task가 Idle 상태인지 검사 OSTCBStat 필드(OS_TCB)의 OS_STAT_SUSPEND를 해제 OSTCBDly를 0으로 설정 : 시간 종료를 기다리고 있지 않은 Task이므로 재개된 Task가 현재 Task보다 더 높은 우선순위를 가지면 스케줄러를 호출한다.
Task Management (Cont’d) Task 정보 알아내기 / OSTaskQuery() 자기 자신이나 다른 응용 Task에 대한 정보를 알아낼 수 있다. 해당 태스크의 TCB 사본을 얻어온다. 주의) 복사본의 정보를 사용할 때, 특히 OSTCBNext, OSTCBPrev 같은 태스크 컨트롤 블록 내의 링크 포인터가 가르키는 블록을 주의한다. 전달인자 prio: 상태를 얻고자 하는 태스크의 우선순위 Pdata: 태스크에 대한 상태를 저장할 태스크 구조체 void MyTask (void *pdata) { OS_TCB MyTaskData; pdata = pdata; for (;;) { /* User code */ err = OSTaskQuery(10, &MyTaskData); /* Examine error code .. */ }
Time Management
Time Management Time Management Time management configuration constants in OS_CFG.H. uC/OS-II Time Management Service Enabled when set to 1 in OS_CFG.H OSTimeDly() OSTimeDlyHMSM() OS_TIME_DLY_HMSM_EN OSTimeDlyResume() OS_TIME_DLY_RESUME_EN OSTimeGet() OS_TIME_GET_SET_EN OSTimeSet()
Time Management (Cont’d) Task 지연 / OSTimeDly() 사용자가 지정한 Clock tick(1~65535) 동안 태스크를 지연할 수 있는 기능을 제공한다. 실행 중이던 태스크 다음으로 우선순위가 높은 태스크를 실행 지정시간이 끝나거나 OSTimeDlyResume()함수가 호출 시 준비상태가 된다. 전달인자 Ticks: 지연 Clock tick
Time Management (Cont’d) Task 지연 / OSTimeDly()
Time Management (Cont’d) Task 지연 / OSTimeDlyHMSM() 시간을 틱 단위가 아닌 시(H), 분(M), 초(S), 그리고 밀리 초(m) 단위로 지정할 수 있다. uC/OS-II에서는 최대 256시간까지 지연이 가능. OS_TIME_DLY_HMSM_EN == 1 일 경우 사용가능. (OS_CFG.H) 전달인자 Hours: 지연할 시간 Minutes: 지연할 분 Seconds: 지연할 초 Milli: 지연할 밀리 초
Time Management (Cont’d) 지연된 Task의 재개 / OSTimeDlyResume() 지연하고 있는 Task를 재개하는 기능을 제공 지정된 지연 시간이 만료되는 것 말고, 다른 Task가 시간지연을 취소해서 지연중인 Task를 다시 준비상태로 만든다. 이 함수는 65,535틱 이상의 시간지연을 재개할 수 없다. 지연시간이 끝나기를 기다려야 한다. OS_TIME_DLY_RESUME_EN == 1 일 때 사용가능 전달인자 prio: 재개할 Taks의 우선순위
Time Management (Cont’d) 시스템 시간 / OSTimeGet(), OSTimeSet() MicroC/OS-II는 Clock Tick Interrupt가 발생할 때마다 32bit 카운터 값을 증가 카운터 값은 처음 OSStart()함수를 호출해서 멀티캐스팅을 시작할 때 0으로 초기화되어 증가하기 시작하고, 4,294,967,295 Tick이후에 다시 0이 된다. OSTimeGet(): 카운터의 현재 값을 가져온다. OSTimeSet(): 카운터의 값을 바꾼다.
Event Control Block
Event Control Block Event Control Block Task 와 ISR은 Event Control Block(ECB) 이라는 Kernel Object를 사용해서 Task로 신호를 보낸다.
Event Control Block (Cont’d) Event Control Block Structure typedef struct { INT8U OSEventType; /* Event type */ INT8U OSEventGrp; /* Group for wait list */ INT16U OSEventCnt; /* Count (when event is a semaphore) */ void *OSEventPtr; /* Ptr to message or queue structure */ INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* Wait list for event to occur */ } OS_EVENT;
Event Control Block (Cont’d) Wait List
Event Control Block (Cont’d) Wait List Operation Making a task wait for an event. pevent->OSEventGrp |= OSMapTbl[prio >> 3]; pevent->OSEventTbl[prio >> 3] |= OSMapTbl[prio & 0x07]; Removing a task from a wait list. if ((pevent->OSEventTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0) { pevent->OSEventGrp &= ~OSMapTbl[prio >> 3]; } Finding the highest priority task waiting for the event. y = OSUnMapTbl[pevent->OSEventGrp]; x = OSUnMapTbl[pevent->OSEventTbl[y]]; prio = (y << 3) + x;
Event Control Block (Cont’d) Wait List Example
Event Control Block (Cont’d) List of Free ECBs
Event Control Block (Cont’d) Event Control Block 초기화 / OS_EventWaitListInit() Semaphore, Mutex, Mailbox, Message Queue를 생성할 때 공통적으로 호출하는 함수이다. 이 함수는 해당 Event Control Block에 대기하고 있는 Task가 하나도 없도록 해준다. void OS_EventWaitListInit (OS_EVENT *pevent) { INT8U *ptbl; pevent->OSEventGrp = 0x00; ptbl = &pevent->OSEventTbl[0]; #if OS_EVENT_TBL_SIZE > 0 *ptbl++ = 0x00; #endif #if OS_EVENT_TBL_SIZE > 1 #if OS_EVENT_TBL_SIZE > 2 #if OS_EVENT_TBL_SIZE > 3 *ptbl++ = 0x00; #endif #if OS_EVENT_TBL_SIZE > 4 #if OS_EVENT_TBL_SIZE > 5 #if OS_EVENT_TBL_SIZE > 6 #if OS_EVENT_TBL_SIZE > 7 *ptbl = 0x00; }
Event Control Block (Cont’d) Task를 준비상태로 만들기 / OS_EventTaskRdy() 이 함수는 Event Control Block에 대기 중인 Task 중에서 가장 우선순위가 높은 Task를 준비상태로 만드는 것이다. Task를 이벤트 대기 상태로 만들기 / OS_EventTaskWait() 이 함수는 현재 Task를 MicroC/OS-II의 준비 리스트에서 삭제하고, 해당 Event Control Block의 대기 리스트에 삽입한다. 타임아웃으로 Task를 준비상태로 만들기 / OS_EventTO() 지정된 시간 안에 이벤트를 받지 못한 경우에 호출하는 것이다. OSTimeTick()이 Task를 준비상태로 만들 경우, OSSemPend(), OSMboxPend(), OSQPend() 함수에서 호출한다.
Semaphore Management
Semaphore Management Semaphore Management Semaphore configuration constants in OS_CFG.H uC/OS-II Semaphore Service Enabled when set to 1 in OS_CFG.H OSSemAccept() OS_SEM_ACCEPT_EN OSSemCreate() OSSemDel() OS_SEM_DEL_EN OSSemPend() OSSemPost() OSSemQuery() OS_SEM_QUERY_EN
Semaphore Management (Cont’d) Task, ISR, Semaphore의 관계
Semaphore Management (Cont’d) Semaphore 생성 / OSSemCreate() 이 함수를 사용하여 Semaphore를 생성할 수 있는데, 생성시 0 ~ 65535 사이의 Semaphore 초기값을 지정해야 한다. 이벤트가 발생한 것을 알려주는 목적으로 Semaphore를 사용할 경우 Semaphore의 초기값을 0을 지정한다. 공유자원에 대한 액세스를 제어할 목적으로 사용할 경우 Semaphore 의 초기값을 1을 준다. 전달인자 cnt: Semaphore를 사용하기 위해 초기화할 값
Semaphore Management (Cont’d) OSSemCreate()이 리턴하기 직전의 ECB
Semaphore Management (Cont’d) Semaphore 삭제 / OSSemDel() Semaphore를 삭제하려면 그 Semaphore를 액세스할 수 있는 모든 Task를 먼저 삭제해야 할 것이다. Semaphore 대기 / OSSemPend() Semaphore를 기다리는 함수이다. 전달인자 Pevent: 해당 Semaphore의 ECB을 가리키는 포인터. Timeout: 옵션 타임아웃 기간(Clock Tick 단위). Err: 에러코드를 저장할 메모리를 가리키는 포인터.
Semaphore Management (Cont’d) Semaphore 반환 / OSSemPost() Semaphore를 반환하는 함수이다. 전달인자 Pevent: 해당 Semaphore의 ECB을 가리키는 포인터. 대기없이 Semaphore 얻기 / OSSemAccept() Semaphore가 가용하지 않을 경우 Task를 대기하지 않도록 하면서 Semaphore를 얻을 수 있도록 하는 함수 Pevent: 해당 Semaphore의 ECB을 가리키는 포인터
Semaphore Management (Cont’d) Semaphore 상태 얻기 / OSSemQuery() Semaphore로 사용하는 ECB의 현재 상태를 얻을 수 있도록 한다. 전달인자 Pevent: 해당 Semaphore의 ECB을 가리키는 포인터 Pdata: Semaphore에 대한 정보를 저장할 구조체를 가리키는 포인터
Mutual Exclusive Semaphore
Mutual Exclusive Semaphore Task가 자원에 대한 독점적인 액세스를 얻고자 할 때 사용한다. Mutex는 MicroC/OS-II가 일반적으로 제공하는 Semaphore 메커니즘에 부가적인 기능을 추가한 Binary Semaphore이다. 우선순위 전도 문제를 줄이기 위해 사용 Mutex configuration constants in OS_CFG.H uC/OS-II Mutex Service Enabled when set to 1 in OS_CFG.H OSMutexAccept() OS_MUTEX_ACCEPT_EN OSMutexCreate() OSMutexDel() OS_MUTEX_DEL_EN OSMutexPend() OSMutexPost() OSMutexQuery() OS_MUTEX_QUERY_EN
Event Flags Management
Event Flags Management 그룹의 현재 이벤트 상태를 나타내는 비트열 이벤트 상태를 나타내는 비트가 켜지거나 꺼지기를 기다리는 Task들의 리스트 Event Flags는 Task를 여러 개의 이벤트 발생에 대해 동기화 할 필요가 있을 때 사용 Event Flags configuration constants in OS_CFG.H uC/OS-II Event Flags Service Enabled when set to 1 in OS_CFG.H OSFlagAccept() OS_FLAG_ACCEPT_EN OSFlagCreate() OSFlagDel() OS_FLAG_DEL_EN OSFlagPend() OSFlagPost() OSFlagQuery() OS_FLAG_QUERY_EN
Event Flags Management (Cont’d) Event Flags Service OSFlagCreat( ) OSFlagDel( ) OSFlagPost( ) OSFlagAccept( ) OSFlagPend( ) OSFlagQuery( ) 태스크 태스크 ISR ISR OSFlagPost( ) OSFlagAccept( ) OSFlagQuery( )
Event Flags Management (Cont’d) Event Flags Group Structure typedef struct { /* Event Flag Group */ INT8U OSFlagType; /* Should be set to OS_EVENT_TYPE_FLAG */ void *OSFlagWaitList; /* Pointer to first NODE of task waiting on event flag*/ OS_FLAGS OSFlagFlags; /* 8, 16 or 32 bit flags */ } OS_FLAG_GRP; Event Flags Group Node Structure typedef struct { /* Event Flag Wait List Node */ void *OSFlagNodeNext; /* Pointer to next NODE in wait list */ void *OSFlagNodePrev; /* Pointer to previous NODE in wait list */ void *OSFlagNodeTCB; /* Pointer to TCB of waiting task */ void *OSFlagNodeFlagGrp; /* Pointer to Event Flag Group */ OS_FLAGS OSFlagNodeFlags; /* Event flag to wait on */ INT8U OSFlagNodeWaitType; /* Type of wait: */ } OS_FLAG_NODE;
Event Flags Management (Cont’d) Event Flags Group, Event Flags Node, TCB 사이의 관계 ● OS_EVENT_ TYPE_FLAG AND 또는 OR NULL OS_FLAG_GRP OS_FLAG_NODE
Event Flags Management (Cont’d) Event Flags Group 생성 / OSFlagsCreate() Event Flags Group을 생성한다. Event Flags Group 삭제 / OSFlagsDel() Event Flags Group을 삭제한다. Event 대기 / OSFlagPend() Event Flags Group에서 특정 이벤트를 기다린다. Event Flags Group에 있는 이벤트 켜고 끄기 / OSFlagPost() Event Flags Group에 있는 비트를 켜고 끈다. Event Flags Group 정보얻기 / OSFlagQuery() Event Flags Group의 현재 값을 얻을 수 있다.
Message Mailbox
Message Mailbox Management Message Mailbox는 MicroC/OS-II에서 제공하는 Kernel Object로써, Task 또는 ISR에서 다른 Task로 포인터 변수를 전송하는 역할을 한다. Mailbox configuration constants in OS_CFG.H MicroC/OS-II Mail Box Service Enabled when set to 1 in OS_CFG.H OSMboxAccept() OS_MBOX_ACCEPT_EN OSMboxCreate() OSMboxDel() OS_MBOX_DEL_EN OSMboxPend() OSMboxPost() OS_MBOX_POST_EN OSMboxPostOpt() OS_MBOX_POST_OPT_EN OSMboxQuery() OS_MBOX_QUERY_EN
Message Mailbox Management (Cont’d) Task, ISR, Mailbox 사이의 관계
Message Mailbox Management (Cont’d) Mailbox 생성 / OSMboxCreate() 이벤트의 발생을 알리는 목적으로 Mailbox를 사용할 경우, Mailbox를 NULL 포인터로 초기화해야 한다. 공유자원 액세스 제어의 목적으로 사용하고자 할 때는 NULL이 아닌 값으로 Mailbox를 초기화한다. 전달인자 Msg: Mailbox에 송부하고자 하는 메시지에 대한 포인터
Message Mailbox Management (Cont’d) OSMboxCreate()이 리턴하기 직전의 ECB
Message Mailbox Management (Cont’d) Mailbox 삭제 / OSMboxDel() Mailbox를 삭제하려면 그 Mailbox를 액세스할 수 있는 모든 Task를 먼저 삭제해야 할 것이다. Mailbox에서 메시지 대기 / OSMboxPend() Mailbox에 메시지가 도착하기를 기다린다. 전달인자 Pevent: 해당 Mailbox의 ECB에 대한 포인터 Timeout: 옵션 타임아웃 값(Clock Tick 단위) err: 에러코드를 저장할 메모리를 가리키는 포인터
Message Mailbox Management (Cont’d) Mailbox로 메시지 보내기 / OSMboxPost() Mailbox로 메시지를 전송한다. 전달인자 Pevent: 해당 Mailbox의 ECB에 대한 포인터 Msg: 보내려는 메시지에 대한 포인터 Mailbox로 메시지 보내기 / OSMboxPostOpt() Mailbox에 대기 중인 모든 Task에게 메시지를 Broadcast할 수 있는 기능 추가 Timeout: 옵션 타임아웃 값(Clock Tick 단위)
Message Mailbox Management (Cont’d) 대기없이 메시지 얻기 / OSMboxAccept() 호출 Task를 대기상태로 만들지 않고 메시지를 받을 수 있다. 전달인자 Pevent: 해당 Mailbox의 ECB에 대한 포인터 Mailbox의 상태얻기 / OSMboxQuery() Mailbox로 사용하는 ECB의 현재 상태를 얻을 수 있다. Pdata: Message Mailbox에 대한 정보를 저장할 구조체를 가리키는 포인터
Message Queue Mailbox
Message Queue Management Message Queue는 포인터 크기의 변수를 Task나 ISR에서 다른 Task로 전달할 수 있도록 하는 Kernel Object이다.이때, 사용하는 포인터는 일반적으로 응용 프로그램에서 정의하는 ‘메시지’ 자료형을 가르킨다. Message queue configuration constants in OS_CFG.H. uC/OS-II Event Flag Service Enabled when set to 1 in OS_CFG.H OSQAccept() OS_Q_ACCEPT_EN OSQCreate() OSQDel() OS_Q_DEL_EN OSQFlush() OS_Q_FLUSH_EN OSQPend() OSQPost() OS_Q_POST_EN OSQPostFront() OS_Q_POST_FRONT_EN OSQPostOpt() OS_Q_POST_OPT_EN OSQQuery() OS_Q_QUERY_EN
Message Queue Management (Cont’d) Task, ISR, Message Queue의 관계
Message Queue Management (Cont’d) Message Queue Structure
Message Queue Management (Cont’d) List of free queue control blocks
Message Queue Management (Cont’d) A message queue as a circular buffer of pointers
Message Queue Management (Cont’d) Message Queue 생성 / OSQCreate() Message Queue를 생성할 경우, 먼저 Message Queue가 메시지를 보관하는데 사용할 메모리를 할당한 뒤 OSQCreate()함수에 넘겨줘야 한다. 전달인자 Start: 큐에서 메시지 저장 영역의 기준 어드레스를 가리키는 포인터 저장 영역은 다음과 같이 void형 포인터의 배열로 선언해야 한다. void *MessageStorage[size] Size: 저장 영역의 요소 수 Message Queue 삭제 / OSQDel () Message Queue를 삭제할 경우, 그 Message Queue를 액세스할 수 있는 모든 Task를 먼저 삭제해야 할 것이다.
Message Queue Management (Cont’d) Message Queue 대기 / OSQPend() Message Queue에 메시지가 도착하기를 기다린다. 전달인자 Pevent: 해당 큐의 ECB을 가리키는 포인터 Timeout: 옵션 타임아웃 기간(Clock Tick 단위) Err: 에러코드를 저장할 메모리를 가리키는 포인터 FIFO방식으로 큐에 메시지 보내기 / OSQPost() FIFO방식으로 Message Queue에 메시지를 넣는다. Msg: 전송할 메시지를 가리키는 포인터. NULL을 지정하면 안된다.
Message Queue Management (Cont’d) LIFO방식으로 큐에 메시지 보내기 / OSQPostFront() LIFO방식으로 Message Queue에 메시지를 넣는다. 전달인자 Pevent: 해당 큐의 ECB을 가리키는 포인터 Msg: 전송할 메시지를 가리키는 포인터. NULL을 지정하면 안된다. FIFO 또는 LIFO방식으로 큐에 메시지 보내기 / OSQPostOpt() OSQPostOpt()는 OSQPost() 와 OSQPostFront() 를 하나로 대치할 수 있는 함수이다. 이 함수는 큐에 대기 중인 모든 Task로 메시지를 Broadcast할 수 있다.
Message Queue Management (Cont’d) 대기 없이 큐에서 메시지 얻기 / OSQAccept() 큐에서 메시지를 받을 수 있다. OSQPend()와 큐가 비어있는 경우라도 Task를 대기 상태로 만들지 않는 것이 다르다. 전달인자 Pevent: 해당 큐의 ECB을 가리키는 포인터 Message Queue 비우기 / OSQFlush() Message Queue안의 메시지를 모두 버리고, 막 생성된 큐 상태로 만든다. Message Queue의 상태 얻기 / OSQQuery() Message Queue의 현재 상태를 가져올 수 있도록 한다. Pdata: Message Queue에 대한 정보를 저장할 구조체를 가리키는 포인터
Message Queue Management (Cont’d)
Memory Management
Memory Management Memory Management 응용 프로그램에서 동적으로 메모리를 할당하고 해제하는데 malloc()과 free()함수를 사용할 수 있다.그러나 이는 메모리 단편화 현상을 초래한다. MicroC/OS-II에서는 malloc()과 free()함수 대신 연속된 메모리 공간으로 구성된 파티션에서 고정 크기의 메모리 블록을 할당할 수 있는 다른 방법을 제공한다. Memory management configuration constants in OS_CFG.H uC/OS-II Memory Service Enabled when set to 1 in OS_CFG.H OSMemCreate() OSMemGet() OSMemPut() OSMemQuery() OS_MEM_QUERY_EN
Memory Queue Management (Cont’d) Memory Control Block Memory Control Block Data Structure Typedef struct { void *OSMemAddr; // 메모리 파티션의 처음을 가르키는 포인터 void *OSMemFreeList; // 자유 메모리 블록 리스트를 가르키는 포인터 INT32U OSMemBlkSize; // 각 메모리 블록의 바이트 단위 크기 INT32U OSMemNBlks; // 이 파티션에서의 총 메모리 바이트 수 INT32U OSMemNFree; // 이 파티션에서 남아있는 자유 메모리 블록 수 } OS_MEM;
Memory Queue Management (Cont’d) Memory Partition Memory Partition 여러 개의 Memory Partition
Memory Queue Management (Cont’d) Free Memory Control Block List
Memory Queue Management (Cont’d) 파티션 생성 / OSMemCreate() 메모리 파티션을 생성하는 함수 밑의 소스는 32바이트 블록이 100개 있는 메모리 파티션을 생성하는 예 OS_MEM *CommTxBuf; INT8U CommTxPart[100][32]; void main (void) { INT8U err; OSInit(); . CommTxBuf = OSMemCreate(CommTxPart, 100, 32, &err); OSStart(); }
Memory Queue Management (Cont’d) OSMemCreate()으로 생성한 메모리 파티션
Memory Queue Management (Cont’d) 메모리 블록 할당 / OSMemGet() 생성된 메모리 파티션으로부터 메모리 블록을 할당받는다. 전달인자 Pmem: 메모리 파티션 컨트롤 블록을 가리키는 포인터 Err: 에러 코드를 저장할 메모리를 가리키는 포인터 메모리 블록의 반환 / OSMemPut() 응용 프로그램에서 메모리 블록의 사용을 마치면 처음 할당받은 파티션으로 해당 블록을 반환해야 한다. Pblk: 해제하려는 메모리 블록을 가리키는 포인터
Memory Queue Management (Cont’d) 메모리 파티션의 상태 얻기 / OSMemQuery() 메모리 파티션에서 사용한 메모리 블록 수와 자유 메모리 블록 수를 알아낸다. 전달인자 Pmem: 메모리 파티션 컨트롤 블록을 가리키는 포인터 Pdata: 메모리 파티션에 대한 정보를 저장할 구조체를 가리키는 포인터
Memory Queue Management (Cont’d) 메모리 파티션의 사용
MicroC/OS-II Porting
MicroC/OS-II Porting Porting Porting 관점에서의 MicroC/OS-II Real Time kernel을 마이크로 프로세서나 마이크로 컨트롤러에서 사용할 수 있도록 하는 작업을 말한다. 즉, MicroC/OS-II를 다른 프로세서에서 사용하기 위한 일반적인 작업임. Porting 관점에서의 MicroC/OS-II 대부분의 MicroC/OS-II의 코드는 이식성을 고려하여 C언어로 작성되어 있음. 일부는 프로세서 의존적인 C코드와 어셈블리어로 작성되어 있음. MicroC/OS-II를 사용할 수 있는 프로세서의 사항 재진입을 지원하는 코드를 생성할 수 있는 C컴파일러 C언어에서 인터럽트 비활성화, 활성화 지원 인터럽트 지원 및 일정한 주기로 발생하는 타이밍 인터럽트를 제공 프로세서 수준에서 지원하는 적정크기의 하드웨어 스택 기능 스택 포인터 또는 레지스터의 내용을 스택이나 메모리로 저장하고 가져올 수 있는 프로세서 명령어
MicroC/OS-II Porting (Cont’d) uC/OS-II File Structure
MicroC/OS-II Porting (Cont’d) 프로세서 의존적인 파일 파일명 설명 OS_CPU.H 프로세서 의존적인 혹은 구현방법에 의존적인 #define문과 매크로, 형 정의 OS_CPU_A.ASM 프로세서에 의존적이면서 C언어로는 작성할 수 없는 저수준(Low-Level)함수나 매크로를 작성하는 어셈블리 파일 이 파일을 수정하기 위해서는 해당 프로세서와 어셈블리에 대한 지식이 있어야 한다. OS_CPU_C.C 프로세서에 의존적이면서 C언어로 작성할 수 있는 함수나 매크로를 작성하는 C 소스 파일 응용 프로그램 의존적인 파일 파일명 설명 OS_CFG.H MicroC/OS-II의 모든 기능을 응용 프로그램에 따라 설정하는 파일 INCLUDES.H 모든 C소스에서 사용하는 마스터 헤더 파일로써, 필요한 모든 헤더 파일은 모두 이 파일에 포함되어 있으므로 어떤 헤더 파일을 포함할지 신경 쓸 필요가 없다.
MicroC/OS-II Porting (Cont’d) OS_CPU.H 프로세서, 컴파일러의 종류에 따라 C언어에서 사용하는 변수타입의 크기가 다르므로 이식성을 보장해 주는 변수 타입을 정의한다. Ex) 16비트 프로세서의 경우, Integer타입의 변수는 16비트 크기 이지만 32비트 프로세서의 경우, Integer타입의 변수는 32비트 크기이다. typedef unsigned char BOOLEAN; typedef unsigned char INT8U; /* 무부호 8비트 데이터 */ typedef signed char INT8S; /* 부호있는 8비트 데이터 */ typedef unsigned int INT16U; /* 무부호 16비트 데이터 */ typedef signed int INT16S; /* 부호있는 16비트 데이터 */ typedef unsigned long INT32U; /* 무부호 32비트 데이터 */ typedef signed long INT32S; /* 부호있는 32비트 데이터 */ typedef float FP32; /* 단정도 부동소수 데이터 */ typedef double FP64; /* 배정도 부동소수 데이터 */ typedef unsigned int OS_STK; /* 각 스택 요소는 16비트 크기임 */
MicroC/OS-II Porting (Cont’d) OS_CPU.H OS_ENTER_CRITICAL() 과 OS_EXIT_CRITICAL() MicroC/OS-II는 Critical Section에 들어가기 전에 인터럽트를 비활성화하고 코드 수행을 마친 뒤 다시 활성화하는 방법을 사용 OS_CPU.H 에는 OS_ENTER_CRITICAL()과 OS_EXIT_CRITICAL()을 구현하는 세 가지 방법이 포함되어 있지만 이들 중 한 가지만 선택해서 사용하면 된다. { . OS_ENTER_CRITICAL(); /* MicroC/OS-II Critical Section Code */ OS_EXIT_CRITICAL(); }
MicroC/OS-II Porting (Cont’d) OS_ENTER_CRITICAL() 과 OS_EXIT_CRITICAL() OS_CRITICAL_METHOD == 1 OS_ENTER_CRITICAL(): 프로세서의 인터럽트 비활성화 명령 수행 OS_EXIT_CRITICA(): 프로세서의 인터럽트 활성화 명령 수행 문제점: 인터럽트가 비활성화된 상태에서 MicroC/OS-II함수를 호출할 경우 함수가 종료해서 리턴할 때 인터럽트는 활성화 상태가 된다. OS_CRITICAL_METHOD == 2 OS_ENTER_CRITICAL(): 인터럽트 비활성화 상태를 스택에 저장 OS_EXIT_CRITICAL(): 스택에서 꺼내 이전 상태로 복귀하도록 처리 #define OS_ENTER_CRITICAL() disable_int() /* 인터럽트 비활성화 */ #define OS_EXIT_CRITICAL() enable_int() /* 인터럽트 활성화 */ #define OS_ENTER_CRITICAL() \ asm(“ PUSH PSW”); \ asm(“ DI”); #define OS_EXIT_CRITICAL() \ asm(“POP PSW”);
MicroC/OS-II Porting (Cont’d) OS_ENTER_CRITICAL() 과 OS_EXIT_CRITICAL() OS_CRITICAL_METHOD == 3 몇몇 컴파일러는 현재 PSW의 값을 읽어서 C함수에서 선언한 지역변수에 저장할 수 있는 확장 기능을 제공한다. #define OS_ENTER_CRITICAL() \ cpu_sr = get_processor_psw(); disalble_interrupts(); #define OS_EXIT_CRITICAL() \ set_processor_psw(cpu_sr);
MicroC/OS-II Porting (Cont’d) OS_CPU_C.C 프로세서에 의존적이지만 C언어로 작성하는 것이 가능한 함수나 매크로를 작성하는 파일로써, 아래의 10개의 간단한 C함수를 작성해야 하지만 필수 함수는 OSTaskStkInit() 뿐이다. OSTaskStkInit() OSTaskCreateHook() OSTaskDelHook() OSTaskSwHook() OSTaskIdleHook() OSTaskStatHook() OSTimeTickHook() OSInitHookBegin() OSInitHookEnd() OSTCBInitHook()
MicroC/OS-II Porting (Cont’d) OSTaskStkInit() OSTaskStkInit() 함수는 OSTaskCreate() 함수나 OSTaskCreateExt()함수에서 생성하고자 하는 태스크의 스택 프레임을 인터럽트가 발생했을 때 처럼 초기화하기 위해 호출한다. OS_STK *OSTaskStkInit ( void (*task) (void *pd), void *pdata, OS_STK *ptos, INT16U opt ) { (1) 전달인자(pdata)와 함께 함수를 호출한 것처럼 만들어준다 (2) ISR 벡터 모사; (3) 모든 레지스터의 내용을 스택에 저장한 것처럼 만들어 준다. (4) 마지막으로 스택 포인터가 가리키고 있는 위치를 리턴한다. } OSTaskStkInit() 함수 의사코드
MicroC/OS-II Porting (Cont’d) OS_CPU_A.ASM 프로세서에 의존적이면서 저수준의 함수나 매크로가 정의되어 있는 어셈블리 파일로써, 아래의 4개의 간단한 어셈블리 함수를 포함한다. 컴파일러가 인라인 어셈블리 기능을 지원한다면, 이 4개의 함수들을 별도의 어셈블리 파일이 아닌 C소스 파일로 작성할 수도 있다. OSStartHighRdy() OSCtxSw() OSIntCtxSw() OSTickISR()
MicroC/OS-II Porting (Cont’d) OSStartHighRdy() OSStartHighRdy() 함수는 OSStart()에서 현재 우선 순위가 가장 높은 Task를 수행하기 위해 호출하는 함수이다. 아래의 의사코드는 실제 해당 프로세서의 어셈블리어로 작성되어야 한다. void OSStartHighRdy() { (1) 사용자 정의 함수 OSTaskSwHook()을 호출한다; (2) OSRunning = TRUE; (3) 재실행할 Task의 스택 포인터를 얻어온다: Stack pointer = OSTCBHighRdy->OSTCBStkPtr; (4) 새 Task의 스택으로부터 모든 레지스터를 복구한다; (5) 인터럽트 복귀 명령을 실행한다; }
MicroC/OS-II Porting (Cont’d) OSCtxSw() Task 수준 문맥 전환 작업은 소프트웨어 인터럽트 명령이나 프로세서에 따라서는 TRAP명령어로 수행한다. 이때 ISR, TRAP 핸들러 혹은 예외 핸들러가 OSCtxSw()를 실행하도록 설정해야 한다. OSCtxSw()함수는 수행 중인 Task에서 더 높은 우선 순위의 Task를 실행 가능한 상태로 만드는 MicroC/OS-II서비스 함수를 호출한 경우에 호출된다. Void OSCtxSw(void) { (1) 프로세서 레지스터 저장; (2) 현재 Task의 스택 포인터를 현재 Task의 TCB에 저장 OSTCBCur->OSTCBStkPtr = 스택 포인터; (3) OSTaskSwHook(); (4) OSTCBCur = OSTCBHighRdy; (5) OSPrioCur = OSPrioHighRdy; (6) 재실행할 Task의 스택 포인터 복구: 스택 포인터 = OSTCBHighRdy->OSTCBStkPtr; (7) 재실행할 Task의 스택으로부터 프로세서 레지스터 복구; (8) 인터럽트 복귀 명령 스택; }
MicroC/OS-II Porting (Cont’d) OSTickISR() MicroC/OS-II은 시간 지연 기능과 타임아웃 기능 구현을 위해 일정한 주기의 타이머를 사용한다. Void OSTickISR(void) { (1) 프로세서 레지스터 저장; (2) OSIntEnter() 호출 또는 OSIntNesting 값을 1 증가; (3) if( OSIntNesting == 1) { OSTCBCur->OSTCBStkPtr = 스택 포인터; } (4) 타이머 인터럽트 발생장치 클리어; (5) 인터럽트 재활성화( 선택사항); (6) OSTimeTick(); (7) OSIntExit(); (8) 프로세서 레지스터 복구; (9) 인터럽트 복귀 명령 실행;
MicroC/OS-II Porting (Cont’d) OSIntCtxSw() OSIntCtxSw()함수는 OSIntExit()함수에서 ISR종료시 문맥전환을 위해 호출 Void OSIntCtxSw(void) { (1) 사용자 정의 함수 OSTaskSwHook()을 호출한다; (2) OSTCBCur = OSTCBHighRdy(); (3) OSPrioCur = OSPrioHighRdy(); (4) 재실행할 Task의 스택 포인터 복구: 스택 포인터 = OSTCBHighRdy->OSTCBStkPtr; (5) 재실행할 Task의 스택으로부터 프로세서 레지스터 복구; (6) 인터럽트 복귀 명령 실행 }