뭘 알아야 이해를 하지
무더운 여름 철 한참 맛나게 낮잠을 하고 있는데 파리 한 마리가 달콤한 단잠을 깨우네요. 파리를 잡으려고 파리채를 잡으면 어느새 파리는 도망을 가버리고 다시 잠을 자려고 하면 또 깨우네요. 어떻게 해야 할까요? ᄏᄏ
시피유 입장에서 파리가 바로 인터럽트랍니다. 시피유는 특별한 일을 하지 않을 때는 단잠을 자는데 전문 용어로 슬립모드(sleep mode)라고 부르죠. 슬립모드에서 깨어나는 경우는 인터럽트가 들어올 때 깨어나게 되죠. 휴대폰으로 비교하자면 엘시디가 꺼져 있는데 전화가 오거나 전화를 걸기 위해 자판을 누르면 엘시디 화면이 나오면서 시피유의 잠단을 깨우는 거죠. 그러다가 또 아무런 인터럽트를 주지 않으면 슬립모드로 들어가면서 단잠을 잔답니다.
인터럽트는 크게 하드웨어 인터럽트와 소프트웨어 인터럽트가 있답니다. 간단하게 하드웨어 인터럽트는 패리퍼럴 요청에 의해서 발생되는 인터럽트이고, 소프트 인터럽트는 사용자가 프로그램 내에서 발생하도록 설정하는 것이랍니다. 그럼 인터럽트가 발생하면 실제적으로 임베디드 시스템에서는 어떤 일들이 일어 날까요? 일반적으로 슬립상태나 휴대폰으로 열심히 게임을 하고 있다가 전화가 오면 인터럽트가 발생하게 되죠. 이때 현재 게임 상태를 저장한답니다. 전화 통화가 끝나면 즉, 전화 통화 인터럽트 수행이 끝나면 이제 다시 게임 상태로 돌아가게 되죠.
먼저 하드웨어 인터럽트부터 살펴보아요.
어떤 시피유이든 인터럽트를 처리하는 부분을 개발자가 프로그램 해야 하죠. 암 시피유의 경우는 하드웨어 인터럽트 처리를 두 군데서 하는데 아이알큐 인터럽트(IRQ)와 에프아이큐 인터럽트(FIQ)랍니다. 차이점은 인터럽트가 동시에 발생을 했을 때 가장 빨리 처리해야 될 인터럽트 처리를 에프아이큐 인터럽트에서 한답니다. 그래서 빠른 인터럽트 처리는 에프아이큐 인터럽트에서 처리를 하고 일반적인 인터럽트는 아이알큐 인터럽트가 처리 한답니다.
각각 패리퍼럴들의 인터럽트 선을 인터럽트 컨트롤러에 연결을 해서 하드웨어 설계를 끝내고, 개발자가 만든 프로그램을 플래시 메모리에 내장 시키면 임베디드 시스템이 동작을 하죠. 동작 중에 인터럽트들이 발생하면 아이알큐 또는 에프아이큐 모드 중에 하나가 처리 될 텐데, 시피유는 아이알큐 인터럽트와 에프아이큐 인터럽트를 어떻게 구별해서 처리를 할까요? 지금부터 알아 보아요.
부팅 할 때 각 패리퍼럴들을 아이알큐 인터럽트에 연결할지, 에프아이큐 인터럽트에 연결할지 인터럽트 컨트롤러에다 설정을 한답니다. 보통 인터럽트 모드 레지스터(INTERRUPT MODE REGISTER, INTMOD)에서 하죠. 부팅 완료 후 해당 패리퍼럴이 인터럽트 요청하면 인터럽트 컨트롤러에서 정의된 레지스터를 읽어서 시피유쪽으로 아이알큐 또는 에프아이큐 인터럽트 신호를 보내게 되죠. 이때 아이알큐 인터럽트와 에프아이큐 인터럽트는 암코어에서 정의한 고정된 어드레스로 무조건 점프하게 되죠. 이곳은 인터럽트 디스크립터 테이블( Interrupt Descriptor Table) 또는 인터럽트 벡터 테이블(Interrupt Vector Table)이라고 불리는 곳이며, 이곳을 지나 인터럽트를 수행하도록 인터럽트 서비스 루틴(Interrupt Service Routine) 라는 곳으로 가서 실행하게 된답니다.
인터럽트가 발생하면 암코어가 아이알큐 인터럽트 또는 에프아이큐 인터럽트를 감지하고 어셈블리 언어로 만든 Startup.s 파일에서 인터럽트 벡터 테이블(IVT)라는 곳으로 옵니다.
[로우 어드레스 기준의 IVT]
___addr/line__|code_____|label_________|mnemonic________________|comment
SR:00000000|EA00006B b 0x1B4 ; handlerReset
SR:00000004|EA000052 b 0x154 ; handlerUndef
SR:00000008|EA000057 b 0x16C ; SWI interrupt handler
SR:0000000C|EA000062 b 0x19C ; handlerPAbort
SR:00000010|EA00005B b 0x184 ; handlerDAbort
SR:00000014|EAFFFFFE b 0x14 ; handlerReserved
SR:00000018|EA000047 b 0x13C ; handlerIRQ
SR:0000001C|EA000040 b 0x124 ; handlerFIQ
IRQ(0x0000 0018) 와 FIQ(0x0000 001C)는 각 해당 하는 주소로 다시 분기를 하고 난 후 실제적으로 인터럽트를 처리하는 일은 한답니다.
[IRQ에 대한 인터럽트 서비스 루틴]
| IsrIRQ
248| sub sp,sp,#4 ;reserved for PC
249| stmfd sp!,{r8-r9}
|
251| ldr r9,=INTOFFSET
252| ldr r9,[r9]
253| ldr r8,=HandleEINT0
254| add r8,r8,r9,lsl #2
255| ldr r8,[r8
256| str r8,[sp,#8]
257| ldmfd sp!,{r8-r9,pc}
하이 어드레스 기준이라면 당연히 0xFFFF FF18와 0xFFFF FF1C번지가 아이알큐, 에프알큐 인터럽트가 됩니다. 그럼 어떤 기준에 의해서 로우 어드레스와 하이 어드레스가 결정이 될까요?
보통 노어 플래시 메모리를 사용한다면 로우 어드레스 기준이고 낸드 플래시 메모리를 사용한다면 하이 어드레스라고 보셔도 됩니다. 만약 노어 플래시와 낸드 플래시 모두 있는 타겟 보드라면 어떻게 될까요?
하드웨어 디버거를 사용하면 보다 싶게 확인 해 볼 수가 있어요.
"201 유닛"에서 코프로세서에 대해 설명 드렸는데, 눈치 빠르신 분들을 아마 이미 알고 있지 않을까 생각이 드네요. 코프로세서 레지스터(CP15) 윈도우를 열어보면 알 수가 있지요.
CR 00000000 iA 0 nF 0 RR Random V 0x00000000
I Disable R Disable S Disable B Little
C Disable A Disable M Disable
CR레지스터에서 V라고 되어 있는 비트가 있는데, 이 비트를 통해 로우 어드레스인지 하이 어드레인지를 알 수가 있지죠.
두 번째로 소프트웨어 인터럽트랍니다.
인터럽트 벡터 테이블에서 0x0000 0008번지를 소프트웨어 인터럽트(SWI, Software Interrupt)라고 해요. 소프트웨어 인터럽트는 주로 시스템 콜(System Call)을 사용하는 오에스(Operating System)에서 정의한 API랍니다. 하지만 모든 오에스에서 사용되는 것은 아니며 특히 임베디드 시스템에서는 많이 없지요. 대표적인 오에스는 임베디드 리눅스(Embedded Linux)와 윈도우 씨이(Windows CE)랍니다. 이러한 오에스의 특징은 메모리 공간이 가상주소(Virtual address)을 사용하며, 유저 영역과 커널 영역으로 나누어져 있답니다. 유저 영역은 어플리케이션 프로그램이 있는 커널 영역은 오에스가 있답니다. 유저 영역에 있는 어플리케이션은 페리퍼럴을 직접 제어하는 권한이 없도록 설계 되어 있어 반드시 페리퍼럴을 제어하려면 오에스의 도움을 구해야 해요. 바로 시스템 콜이라는 API인 거죠. 유저 영역에 있는 어플리케이션 프로그램 소스에서 시스템 콜이라는 API를 사용하면 소프트웨어 인터럽트가 발생하게 되고 인터럽트 벡터 테이블의 0x0000 0008번지로 와서 인터럽트 서비스 루틴으로 간답니다.
댓글