지금까지 한게.

1. 익셉션 벡터 테이블 만들어서 동작모드별, 태스크 등 메모리 할당해주고 메인함수로 진입하게 구현

2. UART 하드웨어 초기화, 입출력, printf

3. 인터럽트 하드웨어 초기화, HW와 인터럽트 컨트롤러 연결, 익셉션 벡터와 인터럽트 헨들러 연결

4. 타이머 초기화, 딜레이 함수 구현

 

으로 RTOS 구현에 필요한 함수들을 구현했고 이번에는 태스크를 다룬다.

 

TCB task control block

개별 태스크를 추상화한 자료 구조

태스크 = 프로그램

태스크 간 전환시 문제 발생이 없어야 함

 

TCB 구성 요소?

컨텍스트 : 프로그램의 현재 상태 정보

태스크 이름

태스크 번호, 우선순위

 

 

 

 

kernel/task.h

이전에 TASK STACK 사이즈를 64MB로 만들었고,

USR_TACK_STACK_SIZE가 0x100000 = 1024 x 1024 = 1MB이니

최대 스택 개수는 64개가 된다.

 

커널 태스크 컨택스트는  컨텍스트를 추상화해서 상태레지스터 spsr, 범용레지스터 r0-r12, pc 영역을

커널tcb는 (범용레지스터에 있는) 스택 포인터 sp와 (개별 테스크의) 스택 베이스로 스택관련 정보를 저장

 

#ifndef KERNEL_TASK_H_
#define KERNEL_TASK_H_

#include "MemoryMap.h"

#define NOT_ENOUGH_TASK_NUM		0xFFFFFFFF
#define USR_TASK_STACK_SIZE		0x100000
#define MAX_TASK_NUM			(TASK_STACK_SIZE / USR_TASK_STACK_SIZE)

typedef struct KernelTaskContext_t
{
	uint32_t spsr;
	uint32_t r0_r12[13];
	uint32_t pc;
}	KernelTaskContext_t;

typedef struct KernelTcb_t
{
	uint32_t	sp;
	uint8_t*	stack_base;
} KernelTcb_t;

typedef void (*KernelTaskFunc_t)(void);

void Kernel_task_init(void);
uint32_t Kernel_task_create(KernelTaskFunc_t startFunc);

#endif

 

 

 

kernel/task.c

앞서 추상화한 테스크컨텍스와 tcb 구조체를 가지고

커널 테스크 초기화, 생성 구현

 

sAllocated tcp index를 기준으로 이거보다 아래는 할당된거 크면 할당되지 않은거

 

태스크 컨트롤 블록을 64개 배열로 선언하고

각 태스크 컨트롤 블록별 스택 베이스와

스택 포인터를 우선 1. 위에서 패딩을 위해 -4한 지점로 초기화 2. 태스크 컨택스트 크기만큼 아래로 내려간 지점을 SP로 설정

KernelTaskContext_t ctx = (KernelTaskContext_t*)sTask_list[i].sp;로 sp기준으로 윗 부분에 태스크 컨택스트 저장

 

 

#include <stdint.h>
#include <stdbool.h>

#include "ARMv7AR"
#include "task.h"

static KernelTcb_t	sTask_list[MAX_TASK_NUM];
static uint32_t		sAllocated_tcb_index;

void Kernel_task_init(void)
{
	sAllocated_tcb_index = 0;

	for(uint32_t i = 0; i < MAX_TASK_NUM ; i++)
	{
		sTask_list[i].stack_base = (uint8_t*)(TASK_STACK_START + (i * USR_TASK_STACK_SIZE));
		sTask_list[i].sp = (uint32_t)sTask_list[i].stack_base + USR_TASK_STACK_SIZE - 4;

		sTask_list[i].sp -= sizeof(KernelTaskContext_t);
		KernelTaskContext_t* ctx = (KernelTaskContext_t*)sTask_list[i].sp;
		ctx->pc = 0;
		ctx->spsr = ARM_MOD_BIT_SYS;
	}
}

uint32_t Kernel_task_create(KernelTaskFunc_t startFunc)
{
	return NOT_ENOUGH_TASK_NUM;
}

 

위 코드에서 태스크 생성 부분을 제대로 구현하면

 

태스크 생성시 안쓰는 tcb 블록 가져오고 인덱스는 ++

그다음 태스크 리스트 최대 크기를 넘었는지 체크

그러고나서 현재 태스크 컨트롤 블록/컨텍스트의 pc에 태스크 함수 등록하고 이 tcb 블록 인덱스 반환

 

 

#include <stdint.h>
#include <stdbool.h>

#include "ARMv7AR"
#include "task.h"

static KernelTcb_t	sTask_list[MAX_TASK_NUM];
static uint32_t		sAllocated_tcb_index;

void Kernel_task_init(void)
{
	sAllocated_tcb_index = 0;

	for(uint32_t i = 0; i < MAX_TASK_NUM ; i++)
	{
		sTask_list[i].stack_base = (uint8_t*)(TASK_STACK_START + (i * USR_TASK_STACK_SIZE));
		sTask_list[i].sp = (uint32_t)sTask_list[i].stack_base + USR_TASK_STACK_SIZE - 4;

		sTask_list[i].sp -= sizeof(KernelTaskContext_t);
		KernelTaskContext_t* ctx = (KernelTaskContext_t*)sTask_list[i].sp;
		ctx->pc = 0;
		ctx->spsr = ARM_MOD_BIT_SYS;
	}
}

uint32_t Kernel_task_create(KernelTaskFunc_t startFunc)
{
	KernelTcb_t* new_tcb = &sTask_list[sAllocated_tcb_index++];

	if(sAllocated_tcb_index > MAX_TASK_NUM)
	{
		return NOT_ENOUGH_TASK_NUM;
	}

	KernelTaskContext_t* ctx = (KernelTaskContext_t*)new_tcb->sp;
	ctx -> pc = (uint32_t)startFunc;

	return (sAllocated_tcb_index - 1);
}

 

 

태스크 쓰기 위해선 스케줄러와 컨텍스트 스위칭을 구현해야해서 제대로는 못쓰고

대신 더미 태스크 함수를 만들면

boot/Main.c

 

 

#include <stdint.h>
#include <stdbool.h>

#include "HalInterrupt.h"
#include "HalUart.h"
#include "HalTimer.h"

#include "task.h"

#include "stdio.h"
#include "stdlib.h"

static void Hw_init(void);
static void Kernel_init(void);

static void	Printf_test(void);
static void Timer_test(void);

void User_task0(void);
void User_task1(void);
void User_task2(void);

void main(void)
{
	Hw_init();

	uint32_t i = 100;
	while(i--)
	{
		Hal_uart_put_char('N');
	}
	Hal_uart_put_char('\n');
	putstr("Hello World!\n");

	Printf_test();
	Timer_test();
}

static void Hw_init(void)
{
	Hal_interrupt_init();
	Hal_uart_init();
	Hal_timer_init();
}

static void Printf_test(void)
{
	char* str = "printf pointer test";
	char* nullptr = 0;
	uint32_t i = 5;
	uint32_t* sysctrl0 = (uint32_t*)0x10001000;

	debug_printf("%s\b", "Hello printf");
	debug_printf("output string pointer: %s\n", str);
	debug_printf("%s is null pointer, %u number\n", nullptr, 10);
	debug_printf("%u=5\n", i);
	debug_printf("dec=%u hex=%x\n", 0xff, 0xff);
	debug_printf("print zero %u\n", 0);
	debug_printf("SYSCTRL0 %x\n", *sysctrl0);

}

static void Timer_test(void)
{
	while(true)
	{
		debug_printf("current count : %u\n", Hal_timer_get_1ms_counter());
		delay(1000);
	}
}

static void Kernel_init(void)
{
	uint32_t taskId;

	Kernel_task_init();

	taskId = Kernel_task_create(User_task0);
	if (NOT_ENOUGH_TASK_NUM == taskId)
	{
		putstr("Task0 creation fail\n");
	}

	taskId = Kernel_task_create(User_task1);
	if (NOT_ENOUGH_TASK_NUM == taskId)
	{
		putStr("Task1 creation fail\n");
	}

	taskId = Kernel_task_create(User_task2);
	if (NOT_ENOUGH_TASK_NUM == taskId)
	{
		putStr("Task2 creation fail\n");
	}
}

void User_task0(void)
{
	debug_printf("User Task#0\n");

	while(true);
}

void User_task1(void)
{
	debug_printf("User Task#1\n");

	while(true);
}

void User_task2(void)
{
	debug_printf("User Task#2\n");

	while(true);
}

 

 

 

+ Recent posts