VPOS 분석 - 2.부팅
VPOS_kernel_main() 에서 부터, shell thread 가 수행되기전까지의 설명을 담고 있다.
VPOS_kernel_main
수행에 필요한 queue 와 하드웨어 초기화를 수행한다. 마지막으로 가장 낮은 우선순위를 가지는 shell thread 를 수행하고, 스케줄러를 시작한다.
void VPOS_kernel_main( void )
{
pthread_t p_thread;
vk_init_kdata_struct(); // 앞으로의 수행에 필요한 각 queue 생성 및 초기화
vk_machine_init(); // 하드웨어 설정 초기화
printk("%s\n%s\n%s\n%s\n%s\n%s\n", top_line, version, develop, release, homepage, bottom_line);
pthread_create(&p_thread, NULL, VPOS_SHELL, (void *)NULL); // 기본적인 shell thread 생성
VPOS_start(); // 스케줄러 동작
}
vk_init_kdata_struct
각 queue 를 생성하고 초기화 시킨다. vpos 는 총 0 ~ 31 단계의 우선순위를 가질 수 있다. 그래서 각각 31 개의 queue 를 생성한다.
void vk_init_kdata_struct(void)
{
vk_init_ready_queue(); // ready queue (0 ~ 31개)
vk_init_wait_queue(); // wait queue (0 ~ 31개)
vk_init_tcb_log_queue(); // tcb log queue (0 ~ 31개)
vk_init_dd_table(); // device driver, 총 29 개의 vk_dd_table 생성 및 총 64 개의 vk_idt_table 생성
vk_init_thread_join_table(); // 10 개의 thread_join_table 생성
vk_current_thread= &init_thread; // init thread 를 current thread 로 지정
}
vk_machine_init
각각의 하드웨어 초기화를 한다.
void vk_machine_init(void)
{
vh_serial_init(); // 시리얼 통신을 위한 UART 관련 레지스터 세팅(s3c2410 user manual 참조)
vh_timer_init(); // 타이머 등록 및 인터럽트 언마스크
vh_rtc_init();
vh_lcd_init();
}
vh_timer_init
위에서 vh_timer_init() 의 타이머를 등록시키는 루틴을 살펴보기로 한다.
void vh_timer_init(void)
{
int timer_load_val;
int TIMER = 14;
vu_register_dev(0, "Timer", &timer_device_driver_fops, 14); // file_operations(open, release, read, write, ioctl, interrrupt) 가진 구조체
vh_interrupt_unmask(TIMER);
vh_rTCFG0 = 0x0f00;
vh_rTCFG1 = 0x0000;
timer_load_val = 50000000/(2*16*100);
vh_rTCNTB = timer_load_val;
vh_rTCON = (vh_rTCON & ~0x0700000) | 0x600000;
vh_rTCON = (vh_rTCON & ~0x0700000) | 0x500000;
}
...
...
...
int vu_register_dev(unsigned int dev_number, char *dev_name, struct file_operations *fops, int irq_num)
{
unsigned int i;
i = dev_number; // i = 0
if(vk_dd_table[i].register_dev == 1) { // 이미 dev_number 를 사용하고 있다면
printk("Alreday %s device driver is registered\n", dev_name);
return -1;
}
vk_dd_table[i].register_dev = 1;
vk_dd_table[i].name = dev_name; // Timer
vk_dd_table[i].interrupt_number= irq_num; // 14
vk_dd_table[i].fop_list = fops; // file_operations(open, release, read, write, ioctl, interrrupt)
if(irq_num >= 0) vk_idt_table[irq_num]=dev_number; // vk_idt_table[14] = 0
return 0;
}
위의 코드를 수행 후의 상태는 다음과 같다.
| 구조체 | 변수값 |
| vk_dd_table[0] | register_dev = 1 |
| name = Timer | |
| interrupt_number = 14 | |
| fop_list = file_operations |
| 구조체 | 변수값 |
| vk_idt_table[14] | 0 |
여기서 fop_list 에 등록된 file_operations 은 다음과 같다. 아래에 보면, TIMER 인터럽트가 발생하면, vh_timer_interrupt_handler() 를 호출한다.
struct file_operations timer_device_driver_fops =
{
NULL, // open
NULL, // release
NULL, // read
NULL, // write
NULL, // ioctl
vh_timer_interrupt_handler // interrupt 발생 시 수행 함수
};
주기적으로 TIMER 인터럽트가 발생할 때마다 수행되는 함수는 아래와 같다.
int vh_timer_interrupt_handler(void)
{
vh_rSRCPND = vh_BIT_TIMER; // 레지스터 세팅
vh_rINTPND = vh_BIT_TIMER;
++(vk_current_thread->cpu_tick); // vk_current_thread->cpu_tick = 0 -> 1
if(vk_sched_lock==0) vk_scheduler(); // vk_sched_lock = 0, vk_scheduler() 함수 호출
return 0;
}
등록을 한 후에는 인터럽트를 언마스크 한다.
vh_interrupt_unmask(TIMER); // TIMER = 14
...
...
...
void vh_interrupt_unmask(int unmask_irq_number)
{
vh_rINTMSK &= ~(0x1<<unmask_irq_number); // vh_rINTMSK = 0x4a000008, ~(0x1<<unmask_irq_number) = 0xFFFFBFF
}
s3c2410 user manual 을 보면(p358), 0x4a000008 에 0 은 마스크를 해제, 1 은 마스크를 적용하도록 설정할 수 있다. 여기서는 TIMER 의 인터럽트를 언마스크 해야 하기 때문에, 0x14 의 역수로 설정한다.
이 결과, 스케줄링은 타이머 인터럽트에 의해서 주기적으로 발생되며, 또는 사용자에 의한 함수 호출로 인해 발생할 수 있다는 것을 알았다.
vh_rtc_init
루틴은 다음과 같다. 여기서는 시스템의 년, 월, 일을 설정한다.
void vh_rtc_init(void)
{
int Timetick = 8;
vu_register_dev(8, "Timetick", &timeTick_device_driver_fops, 8); // Timetick 등록
vh_interrupt_unmask(Timetick); // 인터럽트 마스크 해제
//sem_init(&time_sem, 0, 1);
vh_rRTCCON = 0x00;
vh_rTICNT = (1<<7) + 127;
vh_timeInit();
}
위 루틴의 수행 후의 상태는 다음과 같다.
| 구조체 | 변수값 |
| vk_dd_table[8] | register_dev = 1 |
| name = Timetick | |
| interrupt_number = 8 | |
| fop_list = file_operations |
| 구조체 | 변수값 |
| vk_idt_table[14] | 8 |