바로전에 인터럽트 계층에 대해 설명했다. 
왜냐하면 운영체제안에 타이머를 작동시키려고하는데 이를 외부인터럽트 즉,IRQ를 통해서 처리하려고했기때문이다
그럼 오늘은 이러한 IRQ로 넘어온 인터럽트를 처리하기 위해 PXA255에서 제공하는 타이머 레지스터에 대해 알아보자
PXA255에서는 OSMR, OIER, OSCR, OSSR 네 개의 레지스터로 OS타이머를 설정할 수 있다. 

OSMR은 OS timer Match Register로서  OSMR0 부터 OSMR3R까지 네개가 존재하며 32비트의 값을 입력할수 있다. 이름처럼 어떤 값과 같은지 비교하는 레지스터이다. 이후 OSCR에 설정된 값이랑 비교된다. 
OSCR은 OS timer Counter Register로서 무언가 헤아리는 레지스터 즉, 0부터시작해서 1초가지나면 3686400 이 되는 레지스터이다.
OIER은 OS timer Intrrupt Enable Register로서 OSMR에 셋팅된 값이 OSCR과 같더라도 OIER에서 허용해주지 않으면 인터럽트가 발생하지 않는다. OSMR0부터 3까지 있으므로 OIER은 0부터 3까지 4비트를 셋팅할 수 있다. 
OSSR은 OS timer Status Register로서 OIER까지 확인을 거쳐 발생한 인터럽트의 해당 비트를 셋팅한다. OIER처럼 0-3까지 OSMR에 따라 셋팅된다. 또한 OSSR은 인터럽트를 클리어하는 역할도 한다. 해당비트에 1로 셋팅이되면 발생되었던 인터럽트가 클리어되어 (즉, 여기서 우리는 ICPR과 ICIP가 클리어 될것이다.) 다음 인터럽트를 발생시킬 준비를 한다. 

이상 이론적인 내용이고, 그럼 여기서 내가 짜야될 코드가 무엇인가 보면,.,.. 
OSMR1에 3686400이라는 32비트 값을 입력하고, OIER에 1번비트에 1을 셋팅한다(우리가 타이머로 사용할 레지스터는 OSMR1이므로) 이후 OSSR 1번비트에 1로 셋팅한다. 






AND

오늘 정리하는 부분은 이전의 swi 핸들러작성에 이어 IRQ핸들러를  OS타이머를 구현해 보며 작성하는 것이다. 
여기서 os 타이머의 필요성은 이해했고, OS타이머를 만들기위해 인터럽트를 이용할 것인데 이러한 인터럽트는 이전의 소프트웨어적인 인터럽트와는 달라서 외부 인터럽트이다. 따라서 IRQ 또는 FIQ로 처리하는데, 본 운영체제에서는 IRQ만들 사용하기 때문에 IRQ exception 으로 타이머를 구현할 것이다. 
따라서 먼저 인터럽트를 이용해야 하기 때문에 인터럽트에 대한 처리가 어떻게 이루어 지는지 인터럽트 컨트롤러 계층에 대해 알아야 한다. 
pxa255의 인터럽트 컨트롤러 블록 다이어그램은 데이터시트를 보면 나와있고
각 레지스터들의 기능들에 대해 알아보겠다. 데이터시트를 옆에 두고 보면 이해가 된다. 
ICLR : level 레지스터  즉, 인터럽트가 IRQ인지 FIQ인지 결정한다. 22개의 레지스터중 해당비트가 0이면  IRQ, 1이면 FIQ이다.
ICCR: control 레지스터 . 마지막 한 비트만 설정가능한데 이는 DIM 비트로서 disable idle mode라는 뜻이다.
이 부분을 이해하려면 ICCR[DIM]=0 & idle mode='1' 부분을 잘 봐야한다.  얼핏보면 헷갈린다. 해석하면 ICCR[dim]비트가 0이면 참, 1이면 거짓이고 idle mode는 1이면 참, 0이면 거짓이다. 가운데 & 연산자는 알다시피 둘다 참일때만 참인값을 내보낸다.다시말해서 ICCR[DIM]=0 & idle mode='1' 일때만 1이라는 참의 값을 낸다는 것이다. 하지만 포인트는 우리는 ICCR[DIM]비트를 0또는1로 컨트롤 하는 목적으로 써야한다는 것이다. 이해를 돕기위해 idle mode는 알아서 바뀐다고 생각하고 (idle 일때도 있고 아닐때도 있으니까 알아서 바뀌게 내버려둬...) idle mode 를 무시할지 말아야할 지만 결정하면 되는 것이다. 왜? ? ? 왜냐하면 ICCR의 목적은 idle mode의 변화로 인해 ICMR이 제 기능을 하도록 지켜주는 것이다.  
즉, idle mode가 0 이든 1이든 신경쓰지 않고 ICMR에 셋팅된 인터럽트만 받겠다고 하면(ICMR을 최고로 존중해주겠다고하면)ICCR[dim]을 1로 셋팅하면된다. 그리고 idle mode = 0일때에만(ICMR을 반 만 지켜주는....) ICMR에 셋팅된 인터럽트를 받고 싶으면 ICCR[dim]을 0으로 셋팅하면된다. 하지만 이때는 idle mode=1일때 ICMR을 무시하고 아무 인터럽트 발생시에도 다 통과시킨다 는 점을 기억하자

적어놓으니 이해가 잘된다ㅎ
ICMR: mask 레지스터, 통과시키고 싶은 인터럽트를 1로 셋팅한다. 22개의 비트중 각 비트마다 특정 인터럽트가 바인딩되어있         다
ICPR: pending 레지스터 . 인터럽트 발생시 무조건 기록한다. ICMR처럼 비트마다 특정 인터럽트가 바인딩 되어 있다. 
ICIP: IRQ 로 발생한 특정 인터럽트 비트를 기록
ICFP:FIQ 로 발생한 특정 인터럽트 비트를 기록 

인터럽트번호별 할당된 내용.

이를 바탕으로 정리해보자.
그러니까,  처음으로 인터럽트가 감지되면 해당 인터럽트를 ICPR에 무조건 기록한다. 그리고 이렇게 인터럽트가 감지되었을때, ICCR[DIM]=0 & idle mode = 1 이거나 ICMR의 해당 비트가 셋팅되어 있으면 다음단계로 이동 , ICLR에 의해 해당비트가 0이면IRQ, 1이면 FIQ인지 결정하게 되고, IRQ면 ICIP, FIQ이면 ICFP에 기록한 후 인터럽트가 프로세서로 전달하게 된다. 

그럼 여기서 내가 프로그래밍 해줘야 될게 무엇이냐?... 
 ICLR에서 IRQ인지 FIQ인지 해당비트를 셋팅해주고, 
 ICMR에서도 허용할 인터럽트비트를 활성화 시킨다. 
 ICCR[DIM]을 1로 셋팅한다. 그래야 우리가 원하는 ICMR에 허용된 비트만 받아들이게된다. 















AND

일단 이거 부터 읽고 이해해야한다. 

그리고 다음 소스를 보자.
.global navilnux_swiHandler
navilnux_swiHandler:
1;        stmfd   sp!,{r0-r12,r14}
2;        mrs      r1,spsr
3;        stmfd   sp!,{r1}
4;        bl        swiHandler
5;        ldmfd   sp!,{r1}
6;        msr     spsr_cxsf,r1
7;        ldmfd   sp!,{r0-r12,pc}^
============================
1; stmfd 는 아래와 같은 stack자료 구조가 있을때 , 예로 높은 주소 0x0c부터 낮은주소 0x00으로 와 같이 스택이 자라는 것이라고 한다. full descending이므로  stmdb와 같다. 감소하고 채우고. 이렇게 이해하면 쉽다. ㅋ 
sp d(감소) 한다 b(전에) data input 
난 여지껏 높은 주소가 아래이고 낮은 주소가  가장 위(limit)가 되어 높은 주소에서 낮은 주소로 , limit를 향해서 채워나가는 것이 스택이라고 생각했는데,  높은 주소로도 ,또는  낮은 주소로도 자랄 수 있다는 것을 알게 되었다. 
 하지만 보통 스택은  full descending으로 구현되며 위에서 보듯이 높은 주소에서 낮은 주소로 감소하고 채우고 방법을 많이 사용하게 된다. 
여기서 중요 ascending 은 sp!,{r0-r12,r14}에서 r0부터 작업하지만, descending은 r14부터 작업한다. 즉,~!~!
stmfd sp!,{r0-r12,r14} 은 sp가 4byte씩 감소하면서 r14채우고 , 감소하고 r12채우고, 감소하고 r11채우고.... 이런 식이 되는것이다. 그림으로 보면 이런식이다. 



2. arm에서는 상태레지스터를 메모리 접근 명령어의 인자로 사용불가 따라서 move to from stack status 명령어를 사용하여 r1에 복사후 
3. 스택에 넣기
4. swi핸들러 가기
5.  밑의 글은 5번 라인의 ldmfd의 내용을 보기위해 한번쯤은 당연히 생각해 보았어야 할 문제다.  밑의 글은 질의 내용인데 여러 사이트에 올렸던 질문내용이다.
///////////////////////////////////

stmfd store multiple full descending
    저장 한꺼번에쭉 꽉차게 주소감소하면서
ldmfd load multiple full descending
   꺼냄 한꺼번에쭉 꽉차게 주소감소하면서

강의를보고 이해하기로 저렇게 이해했습니다. 그러면 stmfd에서 낮은 주소방향으로 스택이 쌓여가다가 ldmfd에서도 또 낮은 주소방향으로 주소 포인터가 이동하면서 꺼내야 되는데 왜 ldmfd에서는 높은 주소방향으로 주소 포인터가 이동하면서 쌓아놨던 스택을 꺼내는 걸까요? store와 load 명령어의 동작 방식 때문에 그렇다면 st와 ld로 인해 위에 d의 의미가 감소가 아니라 증가로 바뀌는 것인가요?
단순히 push pop개념으로 이해하면 그냥 넘어가겠는데, 명령어상 문자상의 의미로는 틀린거같아서요.

ldmfa라고 해야 맞는 명령어이지 않을까요? 
왜 저렇게 쓰는지 밍모르겠어요.

다른 분은 full descending 의미는 store에서 라고 하셨는데 그 말은 st냐 ld에 따라 마지막 d의 의미가 달라진다는 말씀이신 것같은데요. 제가 생각하는게 맞나요?

또 한가지 더요.

       ldmfd는 현재 주소에서 큰  주소 방향으로 이동하는데 ,  주소가 증가하고 데이터 빼고 하는 pop 인가요? .......1
                                ""                                            ,  데이터 빼고 주소가 증가하는 pop 인가요? .......2

1번 이 맞는 것같은데... 확실한지 모르겠네요

/////////////////////////////////////

이 질문에 대한 대답은 이렇다. stmfd와 ldmfd를 명령어 적인 해석 보다는 push pop의 개념으로 이해하라! 

그렇다. 명령어적으로 해석하면 분명 내가 질문했던 내용이 포인트가 될 수 도 있다. 
하지만 이렇게 외우기로 했다. stmfd와 ldmfd를 쌍으로 push pop 으로 외우기로 했다. 
마지막 질문인 1번인지 2번인지는 아직도 모르겠다. 주소이동이니까 이동하고 빼는지 빼고 이동하는지 순서가 있을텐데....
아직까지 미지의 부분이다. 혹시라도 이 글을 읽는 다른 사람이 있다면 알려주길...

6.        msr     spsr_cxsf,r1
2번에서의 설명과 같다.상태레지스터를 메모리 접근 명령어의 인자로 사용이 불가하기 때문에 spsr_cxsf에 r1의 내용을 넣는다 . 즉 spsr은 다시 복원되는 것이다. 근데 여기서 cxsf는 상태레지스터의 4가지 영역을 모두다 복원한다는 것이다. flag, status, extension, control.

7.        ldmfd   sp!,{r0-r12,pc}^
pop명령어로 스택의 포인터값을 4바이트씩 증가시키면서 낮은 주소로 쌓아올렸던 스택값을 빼내서 r0....r12, pc 순서로 까지 복구한다. 그리고 마지막 ^ 기호는 arm어셈블리 주소지정 방식으로 메모리에 있는 값을 레지스트리에 복구하고 pc의 주소로 자동으로 분기하며 동시에 spsr의 값을 cpsr의 값으로 복사한다. 

 

AND

책 4, 5장 관련 전반적인 내용에 관한 정리이다 .
먼저 main.c 에는 
..
__asm__("swi 77");  
..
명령어가 들어있고, entry.S에는
....
_ram_entry:
        bl      main           0xa0008000
        b       _ram_entry   0xa0008004
        b       navilnux_swiHandler    0xa0008008
        b       navilnux_irqHandler     0xa000800c

.global navilnux_swiHandler
navilnux_swiHandler:
...
부트로더 에는 이미 swiHandler의 주소가 0xa0008008 가 고정되어 있는상태이고, 커널 이미지상에서도 위에서 보는 바와 같이 고정 시켜뒀다.
일단 arm에 각기다른 모드별로 사용할 수 있는 레지스트리가 다르다.
여기서 총 arm에서 사용할 수 있는 레지스트리는 37개 이다 . 어떻게 알지?.. 직접 세어봐라. 

다만 헷갈리지 말아야 할 부분을 적어보겠다. 
모드별 r0~r12번중 표시 안된건 공유, cpsr(현재 어떤모드인지)은 1개 , spsr(예전에 어떤모드였는지)은 user모드에는 필요없다. r15(pc, 다음에실행될 명령어 주소)는 공유. 기억하자!!

이제 main 함수에서 swi  라는 소프트웨어 인터럽트를 발생시키는 명령어로 인한 ISR진입과정 을  자세히 설명해 보도록 하겠다. swi 는 소프트웨어 인터럽트를 발생시키는 명령어로서 svc모드로 동작하게 된다. 

예를들자. main에서 swi 발생할때 예를 들어 설명하겠다.  

1. main에서 swi 77 명령어가 실행되면 인터럽트 발생!
2. 현재 cpsr을 svc모드의 spsr에 복사
3. r15(pc)를 svc모드의 r14(lr)에 복사
4. cpsr값을  svc모드로 바꾸기
5. r15(pc)값을 exception handler주소로 변경 여기서는 0xa0008008(위에서 부트로더와 커널에서 다~ 지정했다.부트로더에서와 커널에서의 주소지정 방법은 책에..)
========================================
요기까지가 arm코어에서 자동으로 해주는 작업이다. 
뭐... cpsr값을 개발자가 바꿔줘야 할 때도 있지만 swi여기서는 자동!
이 다음부턴 개발자가 핸들러를 짜줘야 하는 부분이다.
========================================
6. r0~r14까지 스택에 백업한다. 왜냐? 각 모드별로 따로 할당되어 있는 레지트스리가 그림에서 보듯이 있는데 svc모드는 따로 할당된게 없지 않은가(공유한다)? 일단 인터럽트 발생해서 넘어왔으니 할일을 해야한다. 그 할일을 하는것이 handler이고 여기서 지정된 곳으로 분기하게 되어 레지스트리를 사용하지 않은가? 그니까 백업해놔야한다. 어디에? 각자 지정된 스택에 (그래서 각자 r13(sp) -따로 있잖아.!!)
만약 FIQ 가 발생하면 r0~r7 , r13,r14 정도만 백업한다. 이렇게 레지스터 몇개 줄이는 것도 시간줄일 수 있다. 우선순위도 다르지만...
7. svc모드의 spsr도 스택에 백업한다.
8. ISR코드 수행한다. 즉, 위의 entry.S 코드에서
.global navilnux_swiHandler
navilnux_swiHandler:
....                             
bl   swiHandler                              <====요기   (main.c 에 함수로 정의되어 있다. bl 을 사용하면 c코드로 넘어감.)
이부분을 수행하게 되는 것이다. 이거 수행할때 레지스트리 r0~~~부터 해서 레지스트리 사용하게된다. 그래서 백업하는것이다.
9 . 작업이 끝난 후 반대로 돌려주면된다. 스택포인터는 spsr을 저장한 다음의 위치를 가리키고 있으므로 스택에서 4바이트 빼서 spsr에 다시 저장한다. arm은 4바이트씩.
10.r0~r14까지 다시 되돌려준다. 

그러니까  main.c에서 swi명령어로 인해 소프트웨어 인터럽트가 발생할 때 ISR 핸들러를 만들어 주는 과정을 요약하면
자동으로 arm코어에서 하는 과정은 놔두고

entry.S에서
.global navilnux_swiHandler
navilnux_swiHandler:
r0-r14내용을 스택에 백업
spsr 내용을 스택에 백업
bl   swiHandler 
스택의 마지막 4바이트를 spsr에 쓰기
스택에서 차례대로 r0-r14쓰기

이렇게 요약할 수 있겠다. 
천재!

ps. 2010.9.29일 추가내용
주의 arm은 기본모드가 svc란다. user모드에서 svc로 바뀌는게 아니라 모드의 변경은 없고, isr처리 과정에 중점을 두어 살펴봐야한다.







'OS > navilnux' 카테고리의 다른 글

pxa255 인터럽트 계층 이해하기  (1) 2010.09.17
exception handler 소스 분석!  (0) 2010.09.15
임베디드개발 보드의 LED 활용!  (0) 2010.09.14
2. 개발환경 테스트 에러발생  (0) 2010.09.11
1. 개발환경 구축하기  (0) 2010.09.11
AND

디버거용이다. 코드 사이에 집어넣어  거기까지 잘 동작하는 지 디버거 용으로 쓴다. 이상.!

제공되는 c 코드 보면 그냥 활용할 수 있다.
AND

나빌옹님은 ez-x5보드 위에서의 개발환경과 에뮬로의 환경을 둘다  말해주고 있는데, 앞으로 모든 얘기는 에뮬환경에서의 이야기이다. 에뮬레이터 qemu를 돌리기 위해서는 ez보드에서 제공되는 커널이미지와 , 에뮬레이터에서 돌아가는 gunstix_uboot 부트로더 를 사용하게 된다.
그런데 gumstix_uboot의 부트로더 이미지를 생성하는 과정중 이상한 에러가 발생하였다. 
failed to merge target specific data of file /usr/lib/gcc/arm-linux/3.4.3/libgcc.a(_modsi3.oS)
이걸 고치기위해 검색해 본 결과 
msoft-float 이라는 옵션의 문제였다. 

gumstix_uboot 디렉토리에 가서 grep -nR 'msoft-float' ;;; 이렇게 검색하면 gumstix_uboot/cpu/ 아래 아키텍쳐별로 주루룩 나오는데 

우리가 봐야될거는 pxa폴더이다. 즉, gumstix_uboot/cpu/pxa/config.mk 파일을 열어서 msoft-float라는 글자만 쏙 제거해준다.

그리고gumstix_uboot 로 돌아와서

make distclean
make gumstix_config
make all
을 실행하면 에러가 사라지고 부트로더 이미지 파일의 생성과 함께 평화가 찾아온다.







AND

임베디드는 개발환경 구축할 때가 가장 짜증나는 것 같다. 한번뚫어놓으면 쫙쫙인데 보드에대한 경험이 없어서인지 보드 바뀔따마다 개고생한다. 

하지만 난  보드를 사용하지 않는다. 돈이없다. 보드살돈이... ㅠ (누가 좀 주세요ㅠ)그래서 친절한 나빌옹님의 가르침을 따라 qemu 에뮬을 사용하기로 했다. 선택의 여지가 없었다. ㅋ

나빌옹님은 gcc 3.3.2  버전을 사용하시고, qemu는 0.9.1 사용하시고  실제 개발보드와 qemu를 사용한 방법 모두를 설명하고있다. 
따라서 나도 그대로 gcc 와 qemu를 버전 맞추어 사용하기로 하였다. 

현재 
몇번의 삽질의 결과 보드의 개발사에서 제공하는 툴체인을 활용하는 것이 개발환경에 아주 수월하다 판단하여 툴체인을 그대로 이용하기로 하였다. 툴체인을 쓰게되면 gcc 3.3.2도 알아서 깔아주고 간단하다. 
qemu는 패키지 관리자를 통해서 설치하지 않고 0.9.1 버전의 패키지 설치파일을 찾아서 설치했다. 여러모로 그게 더 간단하였기때문에 ㅋ   요기참고 http://packages.ubuntu.com/hardy/i386/qemu/download

대충됐다. ㅎ 이렇게 해놓으니.... 한결 수월해졌다. 이제 남은건 make뭐 이런거하면서 생기는 에러 잡아서 온전한 hello world를 일단! 일단!! qemu상에서 보는 것이다. 그게 첫번째다. 


AND

기분좋은  책하나를 발견해서 혼자 진행해 보고있다. 
ㅎㅎ 앞으로 진행하면서  막혔던 부분이나 정리가 필요한 부분을 채워야겠다. 
'도전 ! 임베디드 OS 만들기'  나빌레라님이 작성하신 글이 책으로 나왔다. 2년전에... 이제야알다니 ㅠ
AND