이제 실제 gdb를 활용하여 앞서 설명한대로 동작하는지 점검해보자.
앞서 설명했을 때 IRQ_Interrupt가 걸리면 ARM에서 PC는 강제적으로 0x18로 jump를 하게 된다고 했다.
vector_start:
LDR PC, reset_handler_addr //0x00 Reset
LDR PC, undef_handler_addr //0x04 Undefined Instruction
LDR PC, svc_handler_addr //0x08 SVC exception
LDR PC, pftch_abt_handler_addr //0x0c Prefetch Abort
LDR PC, data_abt_handler_addr //0x10 Data Abort
B . //0x14 Not used
LDR PC, irq_handler_addr //0x18 IRQ Interrupt
LDR PC, fiq_handler_addr //0x1C FIQ Interrupt
따라서 Debugger에 breakpoint를 pc = 0x18에 걸고 걸리는지 확인한다.
(gdb) c
Continuing.
Breakpoint 7, vector_start () at boot/Entry.S:17
17 LDR PC, irq_handler_addr //0x18 IRQ Interrupt
(gdb) p $pc
$18 = (void (*)()) 0x18 <vector_start+24>
Interrupt가 걸렸을 때 PC가 강제적으로 0x18로 변경되었다.
irq_handler_addr: .word Irq_Handler 이므로
next를 하면 자동으로 Irq_Handler로 jump를 하게된다.
__attribute__ ((interrupt ("IRQ"))) void Irq_Handler(void)
{
Hal_interrupt_run_handler();
}
void Hal_interrupt_run_handler(void)
{
uint32_t interrupt_num = GicCpu->interruptack.bits.InterruptID;
if (sHandlers[interrupt_num] != NULL)
{
sHandlers[interrupt_num]();
}
GicCpu->endofinterrupt.bits.InterruptID = interrupt_num;
}
IRQ_Handler는 다시 Hal_interrupt_run_handler를 호출하게 되고
여기서 Hal_interrupt_run_handler는 무슨 interrupt가 걸렸는지(InterruptID) 가지고 온다.
GicCpu 메모리를 확인해보자.
(gdb) p *GicCpu
$21 = {cpucontrol = {all = 1, bits = {Enable = 1, reserved = 0}}, prioritymask = {all = 240,
bits = {Reserved = 0, Prioritymask = 15, reserved = 0}}, binarypoint = {all = 0, bits = {
Binarypoint = 0, reserved = 0}}, interruptack = {all = 44, bits = {InterruptID = 44,
CPUsourceID = 0, reserved = 0}}, endofinterrupt = {all = 0, bits = {InterruptID = 0,
CPUsourceID = 0, reserved = 0}}, runninginterrupt = {all = 0, bits = {Reserved = 0,
Priority = 0, reserved = 0}}, highestpendinter = {all = 44, bits = {InterruptID = 44,
CPUsourceID = 0, reserved = 0}}}
(gdb)
InterruptID가 44인데 44는 UART_INTERRUPT0 = 44 를 의미한다(Hal_uart_init 함수 내부)
따라서 함수의 interrupt_num = 44 가되고
(gdb) p sHandlers[44]
$30 = (InterHdlr_fptr) 0x680 <interrupt_handler>
이므로 interrupt_handler를 호출하여 uart data를 terminal에 쓰게 된다.
함수 실행이 끝나면 LR -4로 return 하면서 모드도 IRQ Mode에서 다시 SYS Mode로 복귀한다.
Irq_Handler () at boot/Handler.c:8
8 }
5: /x $cpsr = 0x600001d2 //함수 끝나기전 IRQ 모드
main () at boot/Main.c:24
24 while(true);
5: /x $cpsr = 0x6000015f // Main 함수
정리하면
1. interrupt(IRQ)가 발생하면 강제로 PC = (void (*))0x18로 변경한다.
2. pc = *0x18에는 Irq_Handler 함수 포인터 값이 있으므로 Irq_Handler로 호출한다.
3. Irq_Handler에서 Hal_interrupt_run_handler 호출한다.
4. Hal_interrupt_run_handler 에서 Interrupt ID를 가지고 어떤 함수를 실행 할 건지 판단한다.
5. sHandlers[44] 의 값은 interrupt_handler 함수 포인터값이 등록되어 있으므로 inetrrupt_handler 함수를 실행한다.
6. Interrupt_handler를 통해 Uart 수신값을 송신한다.
7. 0x00000200 <+24>: ldm sp!, {r0, r1, r2, r3, r4, r11, r12, pc}^ CPSR, SP, PC 값을 전부 복구한다. (SYS모드)
8. interrupt 전 실행 코드 계속 실행한다.
'Firmware > RTOS' 카테고리의 다른 글
임베디드 OS 개발 프로젝트 10(TASK) (0) | 2020.03.05 |
---|---|
임베디드 OS 개발 프로젝트 9(Timer) (0) | 2020.02.16 |
임베디드 OS 개발 프로젝트 7(Uart Interrupt) (0) | 2020.02.15 |
임베디드 OS 개발 프로젝트 6(UART 송수신) (0) | 2020.01.27 |
임베디드 OS 개발 프로젝트 5 (0) | 2020.01.26 |