2008년 8월 2일 토요일

ARM9TDMI 프로세서의 5단계 동작

■ Fetch
pc가 가르키는 메모리 주소로부터 명령어를 읽는다. 이 명령어는 코어로 로드된후, 코어 파이프라인의 다음 단계로 진행된다.
■ Decode
이전 사이클에서 읽어들인 명령어를 해독한다. 만약 이전 단계에서 입력 오퍼랜드들이 사용되지 않았다면 레지스터 뱅크에서 입력 오퍼랜드들도 읽어들인다.
■ ALU
이전 사이클에서 해독한 명령어를 실행한다. 이 명령어는 pc-8(ARM 상태) 또는 pc-4(Thumb 상태)의 주소에서 읽는 것이다. ALU는 보통 데이터 처리 연산을 위한 결과값을 계산하거나 로드, 스토어, 분기 연산을 위한 주소값을 계산한다. 어떤 명령어들은 이 단계에서 여러 사이클을 소모 할 수 있다. 예를 들어, 다중 로드-스토어 명령어와 레지스터 기반의 시프트 연산을 여러 ALU 사이클을 소모한다.
■LS1
로드-스토어 명령어에 의해 규정된 데이터를 읽거나 저장한다. 로드-스토어 명령어가 아니라면 이 단계는 영향을 미치지 않는다.
■LS2
바이트 또는 하프워프 로드 명령어에 의해 로드된 데이터를 제로-또는 부호- 확장한다. 8비트 바이트 또는 16비트 하프워드를 로드하는 명령어가 아니라면 이 단계는 영향을 미치지 않는다.

2008년 7월 30일 수요일

ARM 효과적인 루프문의 코딩

■ 0으로 다운카운트를 하는 루트를 사용하도록 한다. 그러면 컴파일러는 최종값을 저장하기 위해 레지스터를 할당할 필요가 없으며, 0과 비교하는 작업에 비용이 들지 않는다.

■ 디폴트로 unsigned 루트 카운터를 사용하고, 반복 조건으로 i>0보다는 i!=0을 사용하도록 한다. 그러면 루프 오버헤드는 명령어 2개로 줄어든다.

■루프가 적어도 한 번 이상 실행된다면 for문보다는 do-while 문을 사용하도록 한다. 이것은 컴파일러가 루프 카운터가 0인지 아닌지를 체크할 필요성을 없애준다.

■ 루프 오버헤드를 줄어여 하는 중요한 루프문은 언롤링시키도록 한다. 그렇다고 언롤링을 너무 맣이 하지는 않도록 한다. 만약 루프 오버헤드가 전체의 일정 비율만큼 작아진다면, 언롤링은 코드사이즈를 증가시키고 캐시 성능에 좋지 않은 영향을 끼칠 것이다.

■배열에서 요소(element)들의 수는 4나 8의 배수가 되도록 정렬시키도록 한다. 그러면 배열 요소를 추가해야 할 지에 대해 고민하지 않고 루프를 2,4,8배로 쉽게 언롤링 시킬수 있다.

2008년 7월 28일 월요일

ARM 스택 명령어

ARM 아키텍처는 스택 오퍼레이션을 수행하기 위해 다중-레지스터 전송 명령어를 사용하고 있다. 팝(pop) 동작(스택에서 데이터를 제거하는 일)을 위해 LDM 명령어를 사용하며, 푸시(push) 동작(스택에 데이터를 집어넣는 일)을 위해서는 STM 명령어를 사용한다. 스택을 사용할 때에는 스택을 메모리 위쪽으로 증가시킬 지 아래쪽으로 증가시킬 지를 결정해야 한다. 스택은 ascending(A)이나 descending(D) 중 하나이다. ascending 스택은 메모리의 상위 주소 방향으로 스택이 자라는 것을 말하며, descending 스택은 메모리의 하위 주소 방향으로 스택이 자라는 것을 의미한다.

full 스택(F)을 사용하는 경우, 스택 포인터 sp는 마지막으로 사용된 위치의 주소(예를 들어, sp는 스택상에 마지막 아이템을 가르킴)를 가르키고 있다. 반면 empty 스택(E)은 sp가 처음 사용되지 않을 영역의 주소(예를 들어, 스택의 마지막 아이템 다음의 빈공간)를 가르키고 있다.

스택을 처리하기 위해 보번해야 되는, 스택 베이스, 스택 포인터, 스택 리미트의 3개의 인자가 있다. 스택 베이스는 메모리에서 스택의 시작 위치를 나타낸다. 스택 포인터는 초기에는 스택 베이스를 가리키지만, 데이터가 스택으로 들어가면 스택 포인터는 메모리의 하단으로 이동하여 스택의 가장 맨 위를 계속 가르키고 있다. 스택 포인터가 스택 리미트를 넘어가면 스택 오벼플로우 에러가 발생된다.

2008년 7월 21일 월요일

ARM 프로세서 모드

프로세서 모드는 어떤 레지스터가 활성화 되고, cpsr 레지스터를 액세스 할 수 있는 권리를 갖게 될 지를 결정한다. 각 프로세서 모드는 특권 모드일 수도 있고 일반 모드일 수도 있다. 특권모드란 CPSR를 완전히 읽고 쓸 수 있는 모드를 말한다. 반면, 일반 모드에서는 cpsr의 제어 필드는 읽기만 가능하고, 상태 플래그는 읽고 쓰기가 모드 가능하다.
프로세서 모드에는 6개의 특권모드(abort, FIQ, IRQ, supervisor, system, undefined)와 하나의 일반모드(user),즉 전체적으로 7개의 모드가 있다.
프로세서는 메모리 액세스가 실패했을 경우, abort모드로 진입한다. FIQ와 IRQ모도는 ARM 프로세서에서 사용할 수 있는 2가지의 인터럽트 레벨을 위한 모드이다.
supervisor 모드는 프로세서에 리셋이 걸렸을 때에 진입하는 모드로, 일반적으로 운영체제 커널이 동작하는 모드이다. system 모드는 user 모드의 특수한 버전으로 cpsr을 완전히 읽고 쓸 수 있다. undefined 모드는 프로세서가 정의되지 않은 명령어나 지원되지 않은 명령어를 만났을 때에 진입하는 모드이고, user 모드는 프로그램과 어플리케이션을 위해 사용되는 모드이다.

2008년 7월 17일 목요일

ARM 인터럽트 컨트롤러

주변 장치나 디바이스가 실행되려고 할 때, 이것은 프로세서에게 인터럽트를 발생시킨다. 인터럽트 컨트롤러란 인터럽트 컨트롤러 레지스터 안에 있는 해당 비트를 1로 설정함으로써 특정 시간에 어떤 주변 장치나 디바이스가 프로세서에게 인터럽트를 발생시킬수 있도록 할 지에 대한 소프트웨어 정책을 제공한다.

ARM 프로세서에서 사용할 수 있는 2가지 인터럽트 컨트롤러로는 표준 인터럽트 컨트롤러와 백터 인터럽트 컨트롤러(VIC, Vector Interrupt Controller)가 있다.

표준 인처럽트 컨트롤러는 외부 다바이스가 서비스를 요청하면, 그 인터럽트 신호를 프로세서 코어에게 보낸다. 이 컨트롤러는 각 디바이스나 디바이스 세트의 요청을 무시할지, 수렴할 지에 대해 프로그램적으로 설정할 수 있다. 인터럽트 핸들러는 인터럽트 컨트롤러 안에 있는 디바이스 비드맵 레지스터를 읽은 후 어떤 디바이스가 서비스를 요청 했는지를 결정한다.

VIC는 표준 인터럽트 컨트롤러보다 강력한 기능을 가지고, 인터럽트에 우선순위를 부여하여 어떤 디바이스가 인터럽트를 발생시켰는지를 쉽게 결정할 수 있다. VIC는 운선순위와 각 인터럽트에 대한 핸들러 주소를 살펴본 후, 새로운 인터럽트가 현재 실행되고 있는 인터럽트 핸들러보다 우선순위가 높은 경우에만 프로세서에게 인터럽트 신호를 보낸다. VIC는 다비이스를 위한 핸들러 주소를 읽어들이기 위해 표준 인터럽트 익셉션 핸들러를 호출하거나 디바이스를 위해 직접 핸들러로 분기한다.

2008년 7월 16일 수요일

ARM 버스

임베디드 시스템은 x86 PC를 위해 설계된 것과는 다른 버스 체계를 사용하고 있다. 임베디드 디바이스는 칩 내부 보스를 사용하는데, 이 버스는 칩 내부에 위치하여, 다른 주변 장치가 ARM 코어와 내부에서 연결될 수 있도록 해준다.

버스에 연결될 수 있는 디바이스에는 두 종류가 있다. ARM 프로세서 코어는 버스 마스터이다. 버스 마스터란 다른 디바이스로 데이터를 전송할 수 있는 논리 장치를 말한다. 주변 장치는 버스 슬레이브처럼 동작하는데, 이는 버스 마스터로부터 전송 요청이 있을 때에만 동작한다.

버스 아키텍처는 두 단계로 구성되어 있다. 하나는 전기적인 특성과 버스폭(16, 32, 64비트)를 담당하는 물리적인 단계이며, 다른 하나는 프로토콜 처리 단계이다. 프로토콜이란 츠로세서와 주변 장치간의 통신을 담당하는 논리적인 규칙이다.

RISC의 특징

RISC란 매우 빠른 속도로 한 클럭 안에 실행될 수 있는 간결하면서도 막강한 명령어들을 가지고 있는 아키텍처를 말한다. RISC는 하드웨어에 의해 수행되는 명령어들의 복잡도를 줄이는 것을 목표로 하고 있는데, 그 이유는 하드웨어보다는 소프트웨어에 유연성과 기능성을 제공하는 것이 보다 유리하기 때문이다. 결과적으로 RISC는 컴퍼일러에 더욱 의존하게 되었다. RISC는 다음 4가지의 설계 방식을 기본으로 한다.

1. 명령어
RISC 프로세서는 상대적으로 적은 수의 명령어군을 가지고 있다. 컴파일러와 프로그래머는 간단한 명령어 몇 개를 조합하여 복잡한 연산들을 구현하다.

2. 파이프라인
명령어는 파이프라인에 의해 병렬로 실행될 수 있는 작은 단계로 쪼게져 실행된다. 이상적인 파이프라인에서는 한 사이클에 각 명령어를 한 단계씩 앞서 처리하고, 명령어들을 한 파이프라인 단계에서 해독될 수 있다.

3.레지스터
RISC 프로세서는 거대한 레지스터군을 가지고 있으며, 이 레지스터에는 데이터와 주소가 저장될 수 있다. 레지스터들은 모든 데이터 처리 동작을 위해 사용되는 빠른 로컬 메모리처럼 동작한다.

4. 로드-스토어(Load-Store) 아키텍처
프로세서는 보통 레지스터 안에 저장되어 있는 데이터를 이용하여 어떤 동작을 수행한다.메모리에서 레지스터로 데이터를 읽어 들일때에는 로드 명령어를, 레지스터에서 메모리로 데이터를 저장할 때에는 스토어 명령어를 사용한다.

2008년 7월 15일 화요일

MOBM(Mini OEM Boot Module)

MOBM(Mini OEM Boot Module)은 NAND Flash를 동작시키기 위한 소스이다. NAND Flash는 NOR Flash 처럼 0x0000_0000 번지에서 실행 할수 없다. NOR Flash는 주소로 구성되어 직접전근을 할 수 있으나, Nand Flash는 명령어 체계를 이용하여 Block과 Page단위로 접근하기 때문에 실행코드가 바로 수행되지 못한다. 또한 NAND Flash의 블력크기는 Small Block인 경우 16KB, Large Block인 경우에는 64KB정도의 크기 밖에 안 된다. 따라서 부트로더의 크기가 200KB 이상인 경우 0번 블럭에 다 복사하지 못한다. MOBM은 부팅시 필요한 기본적인 기능들 만 구현된것을 말한다. 또한 NAND Flash에 있는 loader를 internal SRAM 및 메모리로 복사하고, 제어권을 넘겨주는 역활을 한다.

2008년 7월 11일 금요일

소프트웨어에서의 하드웨어 접근방법

소스트웨서에서 하드웨어는 메모리로 인식된다. 프로그램은 주변기기로부터 값을 읽거나 프로그램에서 처리한 결과를 주변기기로 출력한다. 이러한 동작을 마이크로프로세서 입장에서 본다면 외부에서 데이터가 입력되거나 외부로 데이터를 출력하는 과정이며, 이러한 입출력을 중간에서 연결지어주는 것이 바로 입출력포트이다. 그러므로 프로그램에서는 입출력포트에서 사용할 메모리를 지정하고, 지정된 메모리에 값을 읽거나 쓰면 된다. 데이터 시트는 보드를 구성하는 하드웨어와 마이크로프로세서를 제어하는 정보들을 담고 있다. 특히 어떤 장치를 사용하려면 어떤 레지스터를 등록해야 하고, 어떤 값을 써야 하는지에 대한 정보를 제공받아야 한다.

2008년 7월 10일 목요일

BSP

BSP(board support package) 는 OS 제공자에 의해 제공되는 추가의 컴포넌트이며, 그 주요한 목적은 운영체제와 범용 디비아스 드라이버 사이에 가상 계층을 제공하기 쉽게 하는 것이다. BSP는 OS가 새로운 하드웨어 환경에 더 쉽게 포팅될 수 있도록 만들어준다. 왜냐하면 그 것은 하드웨어에 독립적인 소스 코드와 하드웨어에 의존적은 소스 코드의 시스템 안에 집적해 주기 때문이다. BSP는 하드웨어를 최적화할 수 있는 소프트웨어의 상위 계층에 서브루틴들을 제공하며, 컴파일시 유연성을 제공한다. 이러한 루틴들은 시스템 어플리케이션 소프트웨어의 나머지로부터 컴파일된 디바이스 드라이버 코드를 가르키고 있기 때문에, BSP는 범용 디바이스 드라어버 코드의 런타임 포팅 가능성을 제공한다.BSP는 아키텍처에 특화된 디바이스 드라이버 설정 관리와 범용 디바이스 드라이버에 접근할 수 있는 OS(또는 소프트웨어 상위계층)을 위한 API를 제공한다. BSP는 또한 시스템 안에서의 디바이스 드라이버(하드웨어)와 OS의 초기화를 관리하는 책임을 맡고 있다.

Segmentation

프로세스 내의 모든 다른 종류의 정보는 세그먼트(segment)라고 불리는 가변 크기의 '논리' 메모리 장치로 나누어진다. 세그먼트란 동일한 종류의 정보를 포함하고 있는 한 세트의 논리 어드레스이다. 세그먼트 어드레스는 0에서 시작하는 논리 어드레스 이며, 세그먼트의 베이스 어드레스를 가리키는 세그먼트 번호와 실제 물리 메모리 어드레스를 정의하는 세그먼트 오프셋으로 구성되어 있다. 세그먼트들은 독립적으로 보호된다. 이는 세그먼트들이 공유(다른 프로세스가 그 세그먼트에 접근할 수 있는곳, 읽기 전용, 읽기/쓰기와 같은 접근 속성을 할당받을 수 있다는 것을 의미한다.

대부분의 OS는 전형적으로 프로세스가 세그먼트 내에 5가지 종류의 정보들의 모두 또는 일부 조합을 가질 수 있도록 해준다.
▶ 텍스트(코드) 세그먼트, 데이터 세그먼트, bss(심벌에 의해 시작되는 블럭) 세그먼트, 스택 세그먼트, 힙 세그먼트

● 텍스트(text) 세그먼트 : 소스 코드를 포함하고 있는 메모리 공간
● 데이터(data) 세그먼트 : 소스 코드의 초기화 변수(데이터)를 포함하고 있는 메모리 공간
● bss 세그먼트 : 소스 코드의 초기화 되지 않은 변수(데이터)를 포함하고 있는 정적으로 할당된 메모리 공간
● 스택(stack) 세그먼트 : LIFP(last in, first out)큐로 구조화된 메모리의 일부이다.
● 힙(heap) 세그먼트 : 런타인시 블록 단위로 할당될 수 있는 메모리 영역, 보통 메모리 구역의 자유로운 링크 리스트로 구성

※ 데이터, 텍스트, bss 세그먼트들은 모두 컴파일시에 동일한 크기로 고정되며, 정적 세그먼트와 같다. 이 세가지 세그먼트들은 실행 가능한 파일의 일부이다. 한편 스택과 힙 세그먼트들은 컴파일시 고정되지 않으며, 런타임시에 그 크기가 변경 될수 있다.



2008년 7월 9일 수요일

메모리 관리

커널은 태스크를 통해 임베디드 시스템 안에서 프로그램 코드를 관리한다. 커널은 시스템 내에서 태스크들을 로드하고 실행하는 어떤시스템을 가지고 있어어 한다. CPU는 캐시 또는 RAM안에 있는 태스크 코드를 실행만 하기 때문이다. 동일한 메모리 공간을 고유하는 다중 태스크 코드를 보호하기 위해서는 보안 시스템 메커니즘을 필요로 한다. 또한 OS는 그것들을 관리하고 있는 태스크들과 동일한 메모리 공간 안에 있어야 하기 때문에, 보호 메커니즘은 메모리 안에 있는 그 자신의 코드를 관리하고 자신이 관리하는 태스크 코드로부터 보호 매커니즘을 루틴을 보호해야 한다. 일반적으로 커널의 메모리 관리기능은 다은과 같은 사항들을 포함한다.

● 논리(물리) 메모리와 태스크 메모리 레퍼런스 사이에서 매핑을 관리한다.
● 이용 가능한 메모리 공간으로 어떤 프로세스가 로드될지를 결정한다.
● 시스템을 구성하는 메모리 프로세스를 위한 메모리를 할당 및 해제한다.
● C 언어의 'alloc'과 'dealloc' 함수처럼,(프로세스 내에서) 코드 요청의 메모리 할당 및 해제 또는 특정 버퍼 할당 및 해제 루틴을 지원한다.
● 시스템 컴포넌트들의 메모리 사용을 추적한다.
● 프로세스 메모리 보호를 보장한다.

대부분의 OS 프로세스들은 보통 실행되는 루틴에 따라 kernel mode와 user mode의 두 모두 중 하나에서 동작한다. 커널 루틴들은 미들웨어 또는 어플리케이션과 같은 소프트웨어의 상위 계층들과는 다른 메모리 공간 및 계층 안의 커널모드(관리자모드)에서 동작한다. 전형적으로 소프트웨어의 이러한 상위 계층들은 사용자 모드에서 동작하며, 시스템호출(system call), 커널의 서브루틴으로서의 상위 레벨 인터페이스를 통해서만 커널 모드에서 동작하는 것에 접근이 가능하다.

처리를 위해 RAM으로 로드 될때 다중 프로세스는 동일한 물리 메모리를 공유하고 있기 때문에, 프로세스들이 하나의 물리 메모리 공간의 안팎으로 교체될 때 서로서로에게 의도 하지 않은 영향을 미치지 못하도록 보호 메커니즘이 있어야 한다. 보통 운영체제에 의해 메모리 스와핑이라는 방법으로 해결하는데 이것은 메모리의 일부분이 런타임시 메모리 안팎으로 교체되는것을 말한다.






2008년 7월 5일 토요일

버스

■ 버스란?
버스는 임베디디 보드상에 있는 모든 주요한 컴포넌트들 사이에서, 다양한 데이터 신호들과 어드레스 신호, 그리고 제어신호들(클럭 신호, 요청신호, 응답신호, 데이터 유형등)을 운반해 주는 선들의 모임. 여기서 주요 컴포넌트들이란 I/O 서브시스템들과 메모리 서브시스템, 그리고 주프로세서를 들수 있다.

■ 버스의 종류
1. 시스템 버스(system bus, 메인버스, 로컬버스, 프로세서 메모리 버스)
외부 주 메모리와 캐시를 주 CPU에 연결하고, 브리지들을 다른 버스에 연결하는 것을 말함. 일반적으로 매우 짧고, 고속이며, 관례적인버스이다.
2. 백플레인 버스(backplane bus)
한 버스에 메모리, 주 프로세서, 그리고 I/O를 상호 연결하고 있는 버스
3. I/O 버스
확장 버스, 외부 버스, 호스트 버스라고 불리며, 남아 있는 컴포넌트들을 주 CPU에 연결하거나, 컴포넌트들끼리 연결하거나, 브리지를 통해 시스템버스에 연결하거나, I/O 통신 포트를 통해 임베디드 시스템 그자체에 연결하는등 시스템 버스의 확장 형태처럼 동작한다.

※ 시스템 버스와 I/O 버스 사이의 주요한 차이점은 I/O 버스상에 IRQ(interrupt request) 제어신호의 존재 가능성이다.

2008년 7월 4일 금요일

파이프라인

■ 파이프라인이란?
파이프라인은 RISC 프로세서가 명령어를 실행하기 위해 사용하는 메커니즘이다. 파이프 라인 한 명령어가 해독되어 실행되는 동안, 다음 명령어를 미리 읽어올 수 있도록 함으로써 실행 속도를 향상시켜 준다. 파이프라인은 자동차를 제조하기 위해 특별한 작업을 수행하는 각 단계를 자동차 제조 라인으로 비유할 수도 있다.

■ 3단 파이프라인의 구조
1. Fetch : 메모리에서 명령어를 로드한다.
2. Decode : 실행한 명령어를 해독한다.
3. Execute : 명령어를 처리하고 그 결과를 레지스터에 저장한다.

■ ARM9 5단 파이프라인
Fetch - Decode - Execute - Memory - Write

■ ARM10 6단 파이프라인
Fetch - Issue - Decode - Execute - Memory - Write

인터럽트(interrupt)

■ 인터럽트란?
주 프로세스에 의한 명령어열의 실행 동안 어떤 이벤트에 의해 발생된 신호를 말한다.

■ 인터럽트의 종류

1. 소프트웨어 인터럽트
주 프로세서에 의해 실행되고 있는 현재 명령어열 안에 있는 어떤 명령어에 의해 내부적으로 발생

2. 내부 하드웨어 인터럽트
주 프로세서의 의해 실행되는 현재 명령어열과 관련된 문제 때문에 발생한 이벤트 에 의해 초기화. 오버플로우 또는 0으로의 나눗셈과 같은 잘못된 산술연산, 디버깅 (싱글 스텝, 브레이크 포인트), 유효하지 않는 명령어(오피코드)등과 같은 하드웨어의특징(제한)때문에 발생. 익셉션 또는 트랩이라고 불림

3. 외부 하드웨어 인터럽트
주 CPU가 아닌 하드웨어(예를 들어, 보드 버스, I/O등)에 의해 초기화된 인터럽트 인터럽트를 실제 발생시키는 것은 초기 디바이스 드라이버 코드 안에 잠재적인 인터럽트 소스를 활성화시키거나 비활성화시키는 레지스터 비트를 통해 소프트웨어로 결정

외부 인터럽트의 처리
외부 이벤트에 의해 발생된 인터럽트에서 주 프로세서는 IRQ(interrupt request level) 핀 또는 포트라고 불리는 입력 핀을 통해 외부 중재 하드웨어(ex : 인터럽트 컨트롤러)에연결되어 있거나 인터럽트를 발생시키고자 할 때 주 프로세서에게 신호를 보내는 전용 인터럽트 포트를 가진 보드상의 다른 컴포넌트에 직접연결된다. 이런 종류의 인터럽트들은 레벨 트리거(level-triggered) 또는 에지 트리거(edge-triggered)중 하나의 방법으로 발생한다. 레벨 트리거 인터럽트는 IRQ 신호가 어떤 레벨에 있을때(ex : HIGH or LOW) 초기화된다. 에지 트리거 인터럽트는 IRQ 라인상 변화(LOW 에서 HIGH : 상승에지, HIGH 에서 LOW : 하강에지)가 발생할 때 발생한다.









2008년 7월 1일 화요일

DMA란?

1. DMA의 개념

이러한 DMA 컨트롤러는 입출력장치와 관련된 개념입니다. 일반적으로 이러한 입출력 창치는 직접 시스템 버스에 접속되지 않습니다. 그 이유는 입출력 장치가 다양하고, 각기 전송속도의 차이가 나며, 데이터의 형식과 길이가 다르기 때문입니다. 그래서 입출력 장치를 제어하기 위해서는 입출력 제어기가 필요합니다. 그리고, 입출력 제어기는 입출력 장치의 제어와 타이밍을 조절하는 역할을 하고, CPU와 입출력 장치의 통신을 담당합니다. 또한 데이터의 버퍼링이나 오류를 검출하는 역할을 수행합니다. 물론 CPU는 해당 입출력 제어기를 통하여 입출력 동작을 수행하고요. 그런면에서 DMA는 넓은 의미에서 입출력 제어기의 범위로 이해하셔도 무방합니다.

2. 보통메모리 제어방식

질문자가 말한 보통의 메모리 제어방식은 아마도 인터럽트를 통한 입출력 제어 방식을 의미하는 것 같습니다. 이러한 인터럽트 방식은 I/O동작이 입출력 제어기와 입출력 장치사이에서 진행되는 동안에는 CPU가 다른 작업을 할 수 있습니다.
동작순서를 살펴보면,
1. CPU가 I/O제어기에 명령을 보내고, CPU는 다른 작업을 수행 합니다.
2. 제어기는 I/O 장치를 제어하여 I/O 명령을 수행 합니다.
3. I/O 명령 수행이 완료되면, 제어기는 CPU 로 인터럽트 신호를 보냅니다.
4. CPU는 인터럽트 신호를 받는 즉시 원래의 프로그램으로 돌아와서 수행을 계속한다.

하지만 이런 인터럽트 방식은 많은 문제점이 있는데, 이를 해결하기 위해 다중 인터럽선을 사용하는방식, 데이지 체인 방식, 소프트웨어 폴링 방식 같은 것을 사용하기도 합니다.그러나 인터럽트 방식은 기억장치와 I/O 장치간의 데이터 통신에 CPU가 직접개입을 하여야 하는데, 이로인하여, 이동되는 데이터들이 반드시 CPU를 경유해야 한다는 단점이 있습니다. 이것을 해결하기 위해 등장한 것이 DMA 방식입니다.

3. DMA 방식

이러한 DMA방식은 CPU의 개입없이 I/O장치와 기억장치 사이에 데이터 전송이 일어나므로 이를 직접메모리 제어 방식이라 부르는 것입니다. DMA 커트롤러는 CPU를 사용하지 않으므로 I/O장치의 주소와 연산지정자(읽기/쓰기), 주기억 장치 영역의 시작주소, 전송될 데이터 단어들의 수를 알 수 있도록 구성되어야 합니다. 또한 사이클 훔침이라해서 CPU가 주기억 장치를 엑세스 하지 않는 동안에 시스템의 버스를 사용하는 기능이 필요합니다.
주기억 장치의 데이터 블록을 디스크에 저장하는 DMA과정을 살펴보면,
1. CPU가 DMA 컨트롤러에게 명령을 보냅니다.
2. DMA 는 CPU로 BUS REQ 신호를 보냅니다.
3. CPU가 DMA에세 BUS GRANT 신호를 보냅니다.
4. DMA가 메모리에서 데이터를 읽어 디스크에 저장합니다.<--(요것이 핵심)
5. 전송할 데이터가 남아 있으면, 위의 과정 반복
6. 모든 데이터 전송이 끝나면, CPU에게 INTR신호를 보냄

하지만 이런 DMA제어기를 이용한 I/I데이터 전송의 문제는 지원에 한계가 있으며, 버퍼링을 위한 내부 기억장치가 따로 필요하는 단점이 있습니다.

4. 채널

일반적으로 DMA 제어에서 채널을 의미하는 것은 IOP입니다. IOP는 입출력 프로세서를 의미하는데, 이러한 IOP는 4가지로 구성됩니다.

1. I/O명령을 실행할 수 있는 프로세서
2. 데이터 블록의 임시 저장을 위한 로컬메모리
3. 시스템 버스에 대한 인터페이스 및 버스 마스터 회로
4. I/O버스 중재 회로,

혹은 OS의 디바이스 드라이브 루틴 프로그램들이 IOP에서 실행되기도 합니다.

*PS : 그리고, 32비트 cpu의 경우 DMA방식을 사용하는 이유는 일반적인 입출력 장치가 32비트수를 필요로 하지 않기 때문이기도 합니다. 또한 멀티라는 개념이 등장하면서 이러한 기술들이 활용되었기 때문이기도 합니다.

2008년 6월 23일 월요일

PXA320 Dynamic Memory Controller Summary

1. SDRAM Configuration Register(MDCNFG)
The MDCNFG register contains control bits for configuring the SDRAM with parameters such as tRP, tRCD, tRAS, tRC. Both SDRAM chip selects must use the same parameters.

2. SDRAM Refresh Control Register(MDREFR)
The MDREFR register contains control bits for SDRAM refresh.

3. SDRAM Mode Register Set Configuration Register(MDMRS)
The MDMRS register is used to issue MRS and EMRS commands to SDRAM

4. DDR Hardware Calibration Register(DDR_HCAL)
This register allows hardware to calibrate and set the strobe delay lines.

5. DDR Write Strobe Calibration Register(DDR_WCAL)
This register lets hardware calibration set the write-strobe delay lines.

6. Dynamic Memory Controller Interrupt Enable Register(DMCIER)
The DMCIER is the Interrupt Enable register for all of the dynamic-memory controller interrupts.

7. Dynamic Memory Controller Interrupt Status Register(DMCISR)
The DMCISR is the Intrrrupt Status register for all of the dynamic-memory controller interrupts.

8. Delay Line Status Register(DDR_DLS)
The read-only Delay Line Status register monitors the delay-line values on strobes DQSx. DDR_DLS contains calibration information for controlling the external-bus delay lines and DQSx.

9. External Memory Pin Interface Control Register(EMPI)
The EMPI register controls the external-memory pin-interface(EMPI) module.

10. Rcomp Control Register(RCOMP)
The Rcomp Control register is similar in concept to the SDRAM Refresh Control register, MDREFR. Both registers use a counter clocked off of a 13-MHz clock to initiate events, and both registers contain an identical software mechanism for initiating their respective events.

2008년 6월 18일 수요일

VC.net과 VC 2005에서의 C Run-Time Libraries

VC 2005(visual c++ 8.0)의 경우 Run-Time Libray 함수를 사용하기 위해서 해당 함수의 확인이 필요하다. 이는 기존의 Run-Time Libray 함수들이 재정의 되면서 함수의 형태 및 필요 인수와 반환값의 형태가 바뀐 것들이 많기 때문이다.

VC 2005 Run-Time Library 목록

VC .net Run-Time Library 목록

2008년 6월 17일 화요일

마이크로소프트웨어에서 BSP를 검색할수 있는 곳

Find Board Support Packages

http://www.microsoft.com/windows/embedded/msdn/bspsearch.aspx

Visula Studio 2005 단축키

Ctrl + Tab Edit하고 있는 Child Window 간의 이동
Ctrl + F4 현재 Edit하고 있는 Child Window를 닫기
Ctrl + I 문자열 입력 점진적으로 문자열 찾기 (Incremental Search)
Ctrl + F3 현재 커서에 있는 문자열 찾기 fowared (블록 지정 안 해도 됨)
Shift + F3 현재 커서에 있는 문자열 찾기 backward
F3 찾은 문자열에 대한 다음 문자열로 이동 (Next Search)
Ctrl + H 문자열 찾아 바꾸기 (Replace)
Ctrl + Left/Right Arrow 단어 단위로 이동
Ctrl + Delete 또는 Backspace 단어 단위로 삭제
Ctrl + F2 현재 라인에 북마크 지정/해제
F2 지정된 다음 북마크로 이동
Ctrl + Shift + F2 지정된 모든 북마크를 해제
F9 현재 라인에 Breakpoint를 지정/해제
Ctrl + Shift + F9 현재 Edit하고 있는 소스파일에 지정된 모든 Breakpoint 해제
Ctrl + ] 또는 E '{'괄호의 짝을 찾아줌 ('{'에 커서를 놓고 눌러야 함}
Ctrl + J, K #ifdef 와 #endif의 짝을 찾아줌
Ctrl + L 한 라인을 클립보드로 잘라내기 (Cut)
Ctrl + Shift + L 한 라인을 삭제
Alt + Mouse 블록 설정 세로로 블록 설정하기 (마우스d로)
Ctrl + Shift + F8 세로로 블록 설정하기 (키보드로), 취소할 때는 Esc키를 눌러야 함
블록 설정 -> Tab 선택된 블록의 문자열을 일괄적으로 들여쓰기(Tab) 적용
블록 설정 -> Shift + Tab 선택된 블록의 문자열을 일괄적으로 내어쓰기 적용
Alt + F8 -> Tab 또는 Shift + Tab 들여쓰기 자동 조정 (Tab:들여쓰기, Shift + Tab : 내어쓰기)
Ctrl + T 현재 커서에 있는 변수/함수에 대한 Type이 Tooltip 힌트 창에 나타남
Ctrl + Alt + T 멤버 변수/함수 목록에 대한 팝업 창이 나타남
Ctrl + Shift + T 공백/콤마/파이프/괄호 등을 기준으로 좌우 문자열을 Swap시킴
Ctrl + Shift + 8 문단기호 표시/감추기 : Tab은 ^, Space는 .으로 표시
Ctrl + D 툴바의 찾기 Editbox로 이동
Ctrl + Up/Down Arrow 커서는 고정시키고 화면만 스크롤 시키기
Tab 들여쓰기
Shift+Tab 내어쓰기
F4 다음 에러메세지로 이동
Alt+F8 들여쓰기 자동 조정
Ctrl+F 찾기
Ctrl+F3 현재 커서에 있는 단어로 찾기
Ctrl+H 찾아 바꾸기
F3 다음 단어 찾기
Ctrl+Shift+F3 이전 단어 찾기
Alt+E+I 프로젝트 내의 모든 파일에서 찾기
F2 설정된 북마크로 이동
F9 현재 커서위치에 BreakPoint를 설정/해제
Ctrl+B 브레이크 포인트 관리 메뉴
Ctrl+Spacebar 멤버목록 팝업창
Ctrl+T 현재 커서에 있는 단어의 툴팁정보 보기
Ctrl+U 모두 소문자로
Ctrl+Shift+U 모두 대문자로
Ctrl+Shift+8 Tab은 ^ 로 공백은 . 으로 보여줌

디버깅에 관련된 단축키 하나 더.
변수이름을 적어 놓고 브래이크 포인터의 위치 여부와 관계 없이 변수의 내용을 추적하는 기능이 Watch Window에 들어 있다. 디버그 모드에서 추가하고픈 변수나 등등 앞에 커서를 위치 시킨후 Shift + F9를 누르면 그냥 바로 추가된다.

▼ Browse Information file 이 필요한 단축키
(컴파일 되어야 함)

F12 현재 커서의 함수 정의 부분으로 이동
Shift+F12 현재 커서의 함수 참조 부분으로 이동
Ctrl+Num+ 다음 정의/참조 로 이동
Ctrl+Num- 이전 정의/참조 로 이동

▼ Visual Assist 6.0 설치시 가능한 단축키

Alt+O 현재 파일의 헤더(.h)/구현(.cpp) 파일로 이동
Alt+G 현재 커서의 함수 정의 부분으로 이동
Alt+M 현재 파일의 모든 함수 목록
Ctrl+Shift+V 선택해서 붙여넣기
Alt+← 이전 커서 위치로 이동
Alt+→ 다음 커서 위치로 이동
■ 디버그 단축키

변수이름을 적어 놓고 브래이크 포인터의 위치 여부와 관계 없이 변수의 내용을
추적하는 기능이 Watch Window에 들어 있죠...
그 때 디버그 모드에서 추가하고픈 변수나 등등 앞에 커서를 위치 시킨후
Shift + F9를 누르면 그냥 바로 추가됩니다

■ General Development Settings Default Shortcut Keys
http://msdn.microsoft.com/en-us/library/xte2hh6a(VS.80).aspx

2008년 6월 16일 월요일

[Tip] RETAILMSG로 디버그메시지 출력하기 [펌]

디버깅 하고자하는 파일(*.cpp, *.c)에 다음 두줄을 선언하시고 빌더하시면 됩니다.
(대략 위치는 헤드 파일 선언 이후면 되겠네요.)

#undef DEBUGMSG
#define DEBUGMSG( a, b) RETAILMSG( 1, b)

그러면 해당 파일에 있는 DEBUGMSG의 메시지들이 릴리즈 모드에서도 출력됩니다.
※ 헤드파일(*.h)에는 선언하지 마시기 바랍니다. 여러 파일에 적용되어 메시지 홍수 납니다.
함수 내에 선언하셔서 함수만 적용되도록 하셔도 됩니다.

이런게 가능한 이유는 DEBUGMSG 가 함수가 아니라 define으로 되어 있기 때문입니다.

고수님들은 다 알고 계신 내용을 팁이겠지만 도움되시는 분들이 있으실까봐 올립니다.
역시나 적고 보니 조금 허접하네요. 단 두줄이라니.. ^^;;

--------------------------------------------------------------------------------------------------------------------------------------

참고로.. RETAILMSG의 사용법은 소스를 보시면 많이 있는데 보통 아래와 같습니다.
RETAILMSG( 1, (TEXT("Message \r\n") ));
RETAILMSG( 1, (TEXT("Message : %d \r\n"), count ));
메시지들은 KITL로 연결되면 PB에 출력되며, KITL 연결이 아니면 디버거 시리얼 포트로 출력됩니다.참 RETAILMSG의 첫 인자가 1일때 출력됩니다. ( 1이나 TRUE 일때 )
---------------------------------------------------------------------------------------------------------------------------------------

WinCE에서의 스트링 사용

WinCE에서 스트링을 처리할 때는 기존의 표준 ANSI C 함수(strcpy, strcmp 등)를 사용할 수 없다. WinCE가 유니코드를 사용하기 때문이다. 유니코드라고 해서 특별히 복잡한 것은 없는데 다만 1바이트로 처리하던 문자(ANSI의 경우 표준 아스키코드 128종류문자, 확장 특수문자까지 포함하면 256가지)를 2바이트로 대폭 확장했다는 차이만이 있을 뿐이다. 그렇지만 이 차이로 인해 PC상에서 주로 썼던 친숙한 스티링 함수 등은 전부 쓸 수 없게 되었다. 대신 한글처리나 기타 여러 언어의 문자를 처리함에 있어서 언어버전마다 다른 복잡한 PC윈도 프로그램 보단 통일성 있게 프로그래밍이 가능해졌다.

스트링의 선언
- ANSI의 경우(컴파일러 환경에 _MBCS라는 매크로가 정의된 경우)
char string[10];

- Unicode 의 경우(컴파일러 환경에 UNICODE나 _UNICODE라는 매크로가 정의된 경우)
wchar_t string[10];

두 가지를 통합하는 변수타입도 있는데 다음과 같이 선언해주면 ANSI든 Unicode든 상관없이 컴파일러 옵션에서 알아서 처리한다.
TCHAR string[10];

앞으로는 WinCE 의 스트링을 처리할 때는 꼭 'TCHAR'를 기억하자. 참고로 TCHAR는 다음과 같이 선언되어 있다.
#ifdef _UNICODE
#define TCHAR wchar_t
#endif
#ifdef _MBCS // ANSI의 Ascii 코드 방식일 때
#define TCHAR char
#endif

스트링을 선언과 함께 초기화할 때
- ANSI의 경우
char szString[20] = "I am a girl";

- Unicode의 경우
wchar_t szString[20] = L"I am a girl";

유니코드의 경우 쌍따옴표 앞에서 붙은 'L'이라는 매크로를 붙임으로 해서 쌍따옴표 부분이 유니코드로 변환된다. 하지만 실제로 가장 많이 쓰는 변수형은 다음과 같다.

TCHAR szString[20] = _T("I am a girl");
TCHAR szString[20] = TEXT("I am a girl");

_T()와 TEXT()는 사실 같은 역할을 하는 매크로로 컴파일러 환경이 ANSI로 정의되어 있으며 괄호안에 스트링을 그대로 두어 ANSI코드로 만들고 UNICODE로 되어 있으면 'L'이라는 매크로를 앞에 붙여서 괄호안의 스트링을 유니코드로 만드는 것과 같은 역할을 한다.
즉, 다음과 같은 역할을 하는 것이다.

- ANSI의 경우
TCHAR szString[20] = _T("I am a girl");
-> char szString[20] = "I am a gril";

-Unicode의 경우
TCHAR szString[20] = _T("I am a girl");
-> wchar_t szString[20] = L"I am a girl";

결론적으로 가장 중요하게 강조하고 싶은 저은 TCHAR와 _T("")/TEXT("") 매크로로 프로그램이나 모듈을 짜놓으면 개발환경이 ANSI인지 유니코드인지 상관없이 컴파일이 가능한 코드를 작성할 수 있다. 가급적으로 WinCE에서는 이 매크로들만 사용하기를 강하게 추천하는 바이다.
또 주의해야 할 점은 TCHAR와 _T(""), TEXT("") 같은 통합 매크로를 사용했으면 지속적으로 사용해야지 중간에 char, tchar나 'L' 매크로 등을 소스 중간 중간에 써넣으면 나중에 간혹 환경이 다른 상황에서 컴파일 할 때의 자잘한 수 백 개의 컴파일 에러들이 짜증날 가능성이 높다.

스트링 처리 함수
C 프로그램을 짜다가 보면 아마 가장 많이 사용하는 함수 중의 하나가 strcpy, strlen 등의 스트링 처리 함수일 것이다. 하지만 이런 함수들은 기본적으로 1바이트 차계의 스트링들을 처리하기 위한 함수들이므로 WinCE의 유니코드 환경에서 제대로 작동하지 않는다.
- ANSI에서 스트링 처리 함수
strcpy, strncpy, strlen, strcmp, strncmp 등
- Unicode에서 스트링 처리 함수
wcscpy, wcsncpy, wcsnlen, wcscmp 등

즉 str로 시작되는 접두사 부분을 wcs로 바꾸어 프로그램을 짜면 된다. 또 TCHAR형식처럼 ANSI와 유니코드를 동시에 지원하려면 다음 함수들을 사용하면 되며, 모두 'tchar.h'에 정의되어 있다.
_tcscpy, _tcsncpy, _tcsnlen, _tcscmp 등

스트링 입출력 함수
- ANSI : printf, sprintf, scanf
- Unicode : wprintf, swprintf, wscanf
- TCHAR : _tprintf, _stprintf, _tscanf

실제 사용 예제는 다음과 같다.
{
TCHAR szTemp1[100] = _T("You are my brother.");
TCHAR szTemp2[100] = _T(" And we are the world.");
TCHAR szTemp3[100] ;

_tcscat(szTemp1, szTemp2);
_stprintf(szTemp3, _T("%s -> Merge string"), szTemp1);
MessageBox(NULL, szTemp3, _T("Test"), MB_OK);
}

Win32에서는 sprintf를 쓸 수 없고 wsprintf를 써야만 되는 경우가 있어서 위 소스가 컴파일 에러를 낼 수 도 있다. 이 경우 다음의 사용자 정의 스트링 처리 함수 부분을 참조하자.

사용자 정의 스트링 처리 함수
아무리 TCHAR 형식 스트링이 ANSI/유니코드를 동시 지원한다고는 하지만 실제 프로그래밍을 들어가 보면 사용자 정의 형식의 함수가 필요할 때가 아주 많다. 예를 들어 위 스트링 처리 함수들을 응용하고 재조합하는 함수를 만들다가 보면 중간에 별도의 다른 내용의 함수가 필요할 때가 종종 있다.
예를 들어 다음과 같이 Sample이라는 스트링의 실제 문자 수를 알고 싶다면
TCHAR Sample[10] = _T("Test");

ANSI에서는 strlen()함수, 유니코드에서는 wcslen()함수 등을 쓰면 된다. 혹은 _tcslen함수를 써도 마찬가지다. 근데 만약 알고 싶은 것이 실제 문자수가 아니라 스트링이 몇 바이트인지 알고싶을 때는 _tcslen 함수를 사용해도 알 수 없다.
왜냐하면 _tcslen함수는 무조건 4라는 리턴값을 취할 것이기 때문이다. 유니코드 상태에서 Sample이라는 변수의 "Test"란 스트링이 차지하는 바이트 수는 8이다(4개의 문자 * 유니코드 기본바이트 2). 이 경우는 비교적 간단한 경우라서

_tcslen(Sample) * sizeof(TCHAR)

이런 식으로 리턴값을 얻어내면 되겠지만 모든 소스코드 라인에 이런 귀찮은 표현을 타이핑 하기에도 아주 번거로울 것이다. 이런 경우에 대비하여 아예 my_strbyte()란 함수를 만들어두고 필요할 때마다 사용하면 편할 것이다. 다음과 같이 사용자 정의 함수를 만들면 된다.
inline int my_strbyte( TCHAR *szString )
{
#ifdef _MBCS // ANSI
return strlen(szString);
#endif
#ifdef _UNICODE // Unicode
return wcslen(szString) *2
#endif
}

그리고 어쩌다가 윈도 표준 스트링 함수와 스트링 처리 함수가 충돌하는 경우가 있는데 이런 경우(대표적 예가 _strintf 이다) 다음과 같이 define 형식으로 처리하면서 각각 상황에 맞는 함수를 정의 내릴 수 있다.
// my_stprintf의 등록
#ifdef _MBCS
#define my_stprintf wsprintf
#endif
#ifdef _UNICODE
#define my_stprintf swprintf
#endif

윗 부분을 정의한 다음에 윗 장에서 썼던 예제 소스를 다시 수정하여 넣어보면 적어도 윈도우를 사용하는 어떠한 플랫폼에서도 제대로 컴파일 되고 아무런 문제가 없이 실행됨을 알 수 있다.

다음은 예제 소스이다.
{
TCHAR szTemp1[100] = _T("You are my brother.");
TCHAR szTemp2[100] = _T(" And we are the world.");
TCHAR szTemp3[100] ;

_tcscat(szTemp1, szTemp2);
my_stprintf(szTemp3, _T("%s -> Merge string"), szTemp1);
MessageBox(NULL, szTemp3, _T("Test"), MB_OK);
}

그 외 여러 가지 사용자 정의 함수의 예들을 넣었다. 필자가 초기에 _tcs 계열의 함수들이 미덥지 못해서 만든 함수들인데 비록 모든 함수들이 _tcs(XXX)형식으로 다 있는 것이기는 하지만 예제를 보고 충분히 재활용할 수 있게 소스를 실었다.

// my_strcpy함수: UNICODE와 ANSI 코드 동시 지원
TCHAR inline *my_strcpy(TCHAR* dest, TCHAR* src)
{
#ifdef _MBCS
return strcpy(dest, src);
#endif
#ifdef _UNICODE
return wcscpy(dest, src);
#endif
}

// my_strncpy함수: UNICODE와 ANSI 코드 동시 지원
TCHAR inline *my_strncpy(TCHAR* dest, TCHAR* src, int n)
{
#ifdef _MBCS
return strncpy(dest, src, n);
#endif
#ifdef _UNICODE
return wcsncpy(dest, src, n);
#endif
}

// my_strtok함수: UNICODE와 ANSI 코드 동시 지원
TCHAR inline *my_strtok(TCHAR* buff, TCHAR* token)
{
#ifdef _MBCS
return strtok(buff, token);
#endif
#ifdef _UNICODE
return wcstok(buff, token);
#endif
}

// my_atoi함수: UNICODE와 ANSI 코드 동시 지원
int inline my_atoi(TCHAR* str)
{
#ifdef _MBCS
return atoi(str);
#endif
#ifdef _UNICODE
return _wtoi(str);
#endif
}

// my_strlen함수: UNICODE와 ANSI 코드 동시 지원
int inline my_strlen(TCHAR* str)
{
#ifdef _MBCS
return strlen(str);
#endif
#ifdef _UNICODE
return wcslen(str);
#endif
}
parent.ContentViewer.parseScript('b_388771');

Wince에서 cached와 uncached를 사용하는 이유

CPU Core 내부에 Cache Controller 가 있으면 캐쉬를 인에이블시켜 쓰던데 ...
CPU내부 core에서 CPU의 내부에 있는 Register나 SDRAM 또는 NAND 또는 Address로 접근 가능한 장치를 address mapping 할때 cache와 uncache로 구분해서 사용합니다.
Oemaddress Table에는 physical address와 cached address만 기록하면 cached address에서 0x2000_0000만큼 높은 번지는 Uncached address가 됩니다.

가령 physical 0x000__0000을 cached로 0x8000_0000이면 uncached는 0xa000_0000 이됩니다.
WINCE에서 Cached address는 0x8000_0000 ~0x9FFF_FFFF까지 이고 Uncached는 0xA000_0000 ~ 0xB000_0000까지 입니다.
cached로 사용하면 Core에서 Address를 access하는 순간 바로 해당 주소를 access하는 반면 uncached로 addressing 하면 그 즉시 access하지 않고 지연되어 read/write가 됩다.


Uncached를 주로 사용하는 경우는 다음과 같습니다.
- CPU내부 register - DMA descriptor
- CPU외부의 chip selector 에 연결되어 있는 IO device - SDRAM의 특정 영역(global 영역등 sharing 을 위한 목적)

Cached를 주로 사용하는 경우는 다음과 같습니다.
- OS가 access하는 SDRAM
- 대용량 memory share(display area / audio dma area 등등)

Cache와 Uncached 의 가장 큰 차이점은 속도 입니다. Uncached로 선언된 영역을 access하면 core의 command가 나갈때 마다 실제로 memory를 access하여야하는데 이과정에서 속도가 떨어지게됩니다. 왜냐면 만약 core는 400Mhz로 작동하는데 외부 버스는 100Mhz나 50Mhz로 작동되는 상황을 생각해 보시기 바랍니다

Windows CE 실전가이드 중요내용 요약

플랫폼 빌더 : 마이크로소프트 사에서 제공하는 wince용 이미지를 제공하는 툴

이미지 생성 절차를 이해하기 위해서는 BSP와 Common요소를 반드시 이해

BSP : 몇가지 표준 타겟 시스템을 정해놓고, 해당하는 타겟 시스템에 종속적인 코드들을
담아놓은 환경

common : 타겟 시스템에 종속적인 코드를 제외한 나머지 코드들중 어떤 BSP를 사용하더라도
호환성을 가질수 있는 코드

프로젝트 : 개발자가 직접 생성하는 타겟을 위한 프로그램

Windows CE용 이미지 생성 = BSP + Common + 프로젝트

명령프롬프트 상에서 이미지를 생성하는 절차
1. system 생성과정(Header 생성과정)
2. Bulid Platform 과정
3. Bulild Release 과정
4. Make Image 과정

REG,DAT,DB,BIB
이미지 생성을 하는데 사용되어지는 중요한 구성파일
실제로 이미지 생성 시 사용되는 시기는 Make Image과정때

REG
시스템 레지스트리 파일을 만드는데 사용되어지는 파일
MakeImage과정을 하게 되면
모든 REG 확장자를 가지는 파일들을 통합하여 REGINIT.ini파일 생성
REGINIT.ini 파일은 DEFAULT.fdf파일로 변환
해당 파일을 NK.bin 이미지 파일에 포함되어,
부팅과정에서 형성될 초기 시스템 레지스트리 환경을 구축

PLATFORM.reg : 디바이스 드라이버의 관련정보가 들어 있음

DAT
단축아이콘 등을 원하는 위치에 생성하도록 지시하는 파일
DAT파일 통합 INITOBJ.dat파일생성 이를 NK.bin 이미지 파일에 포함

DB
데이터 베이스 테이블을 생성하도록 지시하는 파일이다.
확장자 통합하여 INITDB.ini를 만듬

BIB
NK.bin으로 압축할 파일들에 대한 정보와 압축이미지의 속성을 결정하는 파일
확장자를 통합하여 CE.bib 파일을 만듬


파일 속성
S : System 속성
H : Hidden 속성
X : 이미지 속에 압축될 때 서명된 정보를 보관하도록 지시
L : 가상메모리 상에서 해당하는 파일이 분리되어 보관되지 않도록 지시

sample BSP생성시 반드시 빌요한 폴더 및 파일
SRC폴더 및 3개의 파일이 존재해야 함
dirs : 플랫폼 빌더의 의해서 BSP가 빌드되는 과정때, 사용될 빌드 대상 폴더정보를 담고 있다.
samplebsp.bat : 특별히 정하고자 하는 BSP_XXX환경변수를 정의
Sources.cmm : SRC폴더 내의 각 Component들이 Build명령어에 의해서 빌드될때 참고

부트로더에서 중요하게 생각하는 부분
CPU 내에서 존재하는 MMU와 캐시의 활성화
리얼타임 클럭등 초기화, 다운로드 경로로 사용될 하드웨어 초기화

EBOOT작성을 도와주기 위해서 제공되어지는 라이브러리 소스의 위치

$(_PUBLICROOT)\COMMON\OCK\DRIVERS\ETHDBG\BLCOMMON

BLCOMMON 제공하는 함수 크게 두가지 = BootloaderMain() + DownloadImage()
전원=> OEM의 StartUp() => BLCOMMON의 BootloaderMain() =>
OEMDeBugInit() : 디버그 모니터용 포트를 초기화 =>
OEMPlatformInit() : 부트로더로서 역활을 수행하기 위해서 필요한 타겟 플랫폼을 초기화 =>
OEMPreDownLoad() : 연결될 호스트를 착기 위해서 BOOTME 패킷을 Broadcast형식으로 내보내려 한다.
=> BLCOMMON의 DownloadImage()
=> OEMReadData()
=> OEMMapMemAddr()
=> OEM의 OEMLaunch()
=> OEM의 StartUp()
이미지 로딩이 끝나면 호스트 이미지의 시작주소로 제어이행

OAL(OEM Adaptation Layer)은 커널과 함께 링크되어 하나의 NK.exe 프로세스 파일을 이루는 요소
WinCE 스레드 단위의 작업 스케줄링을 처러
동일한 시간에 32개의 최대 프로세스가 메모리에 상주하는 것을 허용(WinCE5.0)

액티브슬롯 : 실행중인 모든 프로세스 중에서 현재 실행되고 있는 스레드를 위한 프로세스가 사용
WinCE 커널은 퀀텀타임(기본 CPU선점시간)에 기반을 둔 라운드로빈 방식의 스케줄링을 지원

가상메모리 4GB중 2GB의 공간이 응용프로그램을 위해 사용
커널레벨에서 동작하는 커널 코드와 OAL코드들은 상위 2GB의 공간을 사용

부팅과정에서 OAL은 커널에게 가상메모리 가 실제 어떤 물리메모리 공간으로 매핑돼야 하는지
알리는 OEMAddressTable을 소개하게되는데, 커널은 이 테이블을 사용해서 가상메모리를 물리
메모리공간으로 매핑하도록 MMU를 초기화

디바이스 드라이버 개발자들이 흔히 저리르는 실수가 하나 있다. WinXP와 같은 운영체제를 사용하는
습관에서 비롯된 부분인데, 동일한 프로세스 내의 모든 스레들은 같은 공간을 참조할 수 있다는 것이다.
그러나 이것은 WinCE에서는 불가능하다. 그렇기 때문에 스레드를 생성한 뒤 반드시 해당하는
스레드에게 적절한 퍼미션을 줘야 한다.

커널과 OAL(OEM Adaptation Layer)중에서, 개발자가 코딩해야 하는 부분은 OAL
커널과 OAL은 서로 같이 링크되어 NK.exe라는 프로세스 파일을 형성하게 된다.

커널이 관심을 두고 있는 플랫폼 하드웨어는 그 용도가 정해져 있다.

시간등을 기록하고 읽어오기 위한용도
인터럽트 컨트롤러로 하여금 인터럽트를 금지하거나 허용시키는 용도
지정된 시간마다 타이머 인터럽트가 발생되도록 시도하는 용도
디버그 용도로 사용될 UART,NETCARD등을 다루는 용도

커널이 접근하려는 하드웨어 플랫폼에 대해 이를 접근할수 있도록 지원하는 목적이 OAL의 역할
디바이스 드라이버도 플랫폼에 관심을 두고 있는 모듈이지만 그 용도가 사용자 서비스와
관련된 용도라는 점에서 OAL과 다른 특징을 가짐

OAL필수함수들이 반드시 구현되어야지만 NK.exe가 만들어진다.

StartUp()
이함수가 호출될때는 CPU내의 MMU는 사용되지 않는 상태이어야함
이 함수를 호출할때 파라미터로 사용되는 것인 OEMAddressTable
이테이블은 가상메모리 주소와 실제 메모리 주소와의 매핑 관계를
서술하고 있는 테이블로서 이테이블을 넘겨받아야 커널이 MMU를 초기화하게 된다.

OEMInitDebugSerial() 이 함수를 통해 UART로의 디버그지원함수를 준비할 책임이 있음

OEMInit() 함수를 호출해 플랫폼 하드웨어를 초기화
1플랫폼 하드웨어 초기화
2인터럽트 핸들러 초기화
ARM CPU를 사용하는 경우에는 OAL의 OEMInterruptHandler() 핰수가 보편적인
인터럽트 서비스 루팀으로서의 역할을 수행
3전역변수 초기화


커널은 BIB파일을 통해서 개발자가 알린RAM외에 또다른 RAM의 존재를 확인
이때 호출되는 함수가 OEMGetExtensionDRAM()

커널은 하드웨어 플랫폼으로부터 인터럽트를 가장 먼저 받는 모듈.
그러나 커널은 인터럽트의 구제적인 의미를 파악하지 못하기 때문에 OAL이 제공하는
인터럽트 서비스 루틴의 지원을 받게됨

OAL이 제공하는 ISR의 프로토타입

int OEMISR(unsigend int reserved);


커널과 디바이스 드라이버들이 이해하는 인터럽트를 Interrupt Logical ID라고 부른다.

결국 OAL이 지원하는 ISR은 타겟 시스템에 이미 내장된 하드웨어가 발생하는 인터럽트를
인식하는 역활을 수행하지만, 디바이스 드라이버가 추가적으로 지원하는 Installabe ISR은
복수개의 동일한 인터럽트 벡터를 사용하는 하드웨어를 지원하기 위함이라는 사실을 알아야 함

OEMInterruptEnable()
디바이스 드라이버는 자신이 받고자 하는 하드웨어 인터럽에 대한 Logical ID를 커널에게
전달해 이 인터럽트가 유효한지를 확인하고 해당하는 인터럽트가 발생될수 있는 조건을
만들도록 용처해야하는데 이때 이 함수를 씀

InterruptInitialize()
디바이스 드라이버가 사용하는 인터럽트 Logical ID에 대한 허용요청 함수
커널이 드라이버에게 제공하는 함수

디바이스 드라이버 => 커널 InterrputInitialize()=> OAL의 OEMInterruptEnable()

OEMInterruptDisable()
해당하는 인터럽트를 금지시키는 함수
다바이스 드라이버는 커널함수 InterruptDisable()을 호출하여 자신이 사용하는 인터럽트ID를
중지하려 할때, 커널이 이 사실을 OAL에게 알려주는 용도로 사용된다.

디바이스 드라이버 => 커널 InterruptDisable() => OAL의 OEMInterruptDisable()

OEMInterruptDone()
해당하는 인터럽트의 재개를 허용해달라는 의미
디바이스 드라이버는 자신이 인터럽트를 모두 처리한 뒤 인터럽트의 재개를 부탁해야 하는데,
이때 사용하는 커널함수가 InterruptDone()이다. 커널은 이 함수가 호출되었을때, OAL의
OEMInterruptDone()를 호출하여 해당하는 인터럽트의 재개를 부탁하게 된다.

디바이스 드라이버 => 커널 InterruptDone() => OAL의 OEMInterruptDone()

OEMIoControl()
응용프로그램이나 디바이스 드라이버는 커널함수 KernelIoControl()사용해서 IO컨트롤 코드를 커널에게
보내게 되는데, 이때 커널은 커널 자신이 처리해야 하는 IO컨트롤 코드를 제외한 나머지 IO컨트롤 코드를
OAL의 OEMIoControl()함수로 보내어 처리를 의뢰하게 된다.

IO컨트롤 코드를 정의할때, 주의할 점은 매크로의 2번째 파라미터인 Function값이 반드시
0x800에서 0xFFF사이어야 한다는 것이다.

디바이스 드라이버 => 커널 KernelIoControl()=>OAL의 OEMIoControl()

커널 디버깅을 하기 위해서는 KITL(Kernel Independent Transport Layer)환경을 구축해야 한다.
KITL은 플랫폼 디버그 포트를 다양한 목적으로 서비스될 수 있도록 지원하기 위해서 커널이 구축한
특별한 환경이다.

디바이스 드라이버란 하드웨어를 구동하는 소프트웨어로서 최종 사용자에게 서비스를 제공하는
목적을 가진 모듈을 의미
디바이스 드라이버는 응용프로그램과 달리 항상 누군가에게 호출되어 동작하는 특성을 가지고 있다.

스트림 인터페이스 드라이버를 취하고 있는 드라이버의 종류는 다음과 같다.
시리얼 드라이버, 디스크 드라이버, 오디오 드라업, 패러럴 드라이버,USB호스트 컨트롤러,NDIS.dll(네트워크 관리 드라이버)등. 그 밖에 대부분 개발자가 개발 방법으로 선택하는 대부분의 디바이스 드라이버들이 여기에 속한다. 스트림 인터페이스 드라이버 모델을 취하는 드라이버는 DEVICE.exe 프로세스의 DLL형식으로 동작하게 되며, 그렇기 때문에
항상 이들 드라이버는 DEVICE.exe 프로세스의 문맥 아래에서만 동작


버스 인터페이스 드라이버는 WinCE가 지원하는 특정 버스에 대한 서비스 드라이버들을 들수 있다. 클라이언트 드라이버들에게 서비스를 제공하는 역할을 한다. DEVICE.exe 문맥 아래에서
동작한다. 스트림과는 단지 같은 프로세스 문맥 아래에서 동작하는 공통점을 가질뿐
REGENUM.dll(Legacy BUS), PCI.dll(PCI BUS), USBD.dll(USB BUS), PCMCIA.dll(PCMCIA BUS)등이 있다.

네이티브 인터페이스 드라이버란 드라이버들이 저마다 자신에게 가장 적합한 드라이버 인터페이스를 갖고 있는 드라이버를 의미한다. 네이티브 인터페이스 드라이버는 GWES.exe 프로세스 아래에서 동작하는 드라이버이다. 이들은 DLL 형식으로 작성되어 동작하는 드라이버들과, LIB 형식으로 작성되어 있어서 프로세스(GWES.exe)가 생성될 당시에 같이 링크되는 정적링크 형식을 취하는 드라이버들이다.


PDD(Platform Dependent Driver)
디바이스 드라이버를 개발할 때 응용프로그램과 대화하는 부분에서 다른 소스의 도움을 받고
자신은 이 소스로부터 호출되는 간단한 작업들만 수행하는 드라이버를 의미

DDI(Device Driver Interface)
디바이스 드라이버가 응용프로그램과 대화하는 방법

미니포트 드라이버
부수적으로 프랫폼을 직접 다루는 부분을 지원하는 PDD

MDD(Model Device Driver)
PDD를 사용하는 외부소스나 라이브러리

DDSI(Device Driver Service Provider Interface)
PDD와 MDD와 링크되는 부분

Layered Driver(계층드라이버)
MDD와 PDD형식으로 드라이버를 개발하는 경우

미니포트 드라이버
계층드라이버(MDD, PDD)형식과 의미는 같으나, MDD가 소스나 라이브러리 형태가 아닌 정형화된 드라이버 파일형태(DLL)로 되어 있으며, 이들 MDD DLL은 디바이스 드라이버로서의 DDI를 지원하고, 개발자는 이들 MDD DLL이 직접 로딩하여 다루게 되는 PDD DLL을 만들게 된다.

스트림 드라이버의 조건
1. 디바이스 드라이버로서 가져야 할 접두어 세글자를 정해야 한다.
2. DLL형식으로 작성되야 하며, DDI 함수들(XXX_open,XXX_Read)을 구현해야 한다.
3. 구현된 DLL함수들 중 DDI로 사용되는함수들은 모두 익스포트돼야 한다(DEF 파일사용)
4. 디바이스 드라이버로서 등록되기 위한 적절한 시스템 레지스트리를 가져야 한다.
5. 다바이스 드라이버가 “DEVICE.exe” 프로세스 아래에서 동작하는 것을 유념해야 한다.


사용자 혼란을 줄이기 위해 접두어 세글자를 제공하는 것과 동시에 자신이 어떤 드라이버인지를 소개하는 인터페이스 GUID를 시스템 레지스트리에 같이 등록하는 것이 좋다.

불가피하게 C++ 형식의 DLL로 만드는 경우라면, 이 함수명들은 모두 extern “C”형식으로 정의해서 C++ 맹글링 현상을 피해야 한다.

XXX_Init
DEVICE.exe(장치관리자) 프로세스 공간으로 스트림 인터페이스 드라이버가 등록될 때 호출하는 함수

스트림 인터페이스 드라이버를 등록시킬 때 사용하는 3가지 방법
1. RegisterDevice() 함수 사용
특별히 시스템 레지스트리를 준비하지 않는 상태에서 손쉽게 드라이버를 스트림 인터페이스 드라이버로 등록할 때 사용하는 함수. Dll 드라이버내에 스트림 인터페이스를 위한 함수들이 존재해야 함
2. ActivateDevice() 함수 사용
시스템 레지스트리를 준비한 상태에서만 사용이 가능한 스트림 인터페이스 드라이버 등록함수
3. ActivateDeviceEX() 함수 사용
다양한 변수들을 Active 키 아래에 추가적으로 등록하는 방법을 제공하기 위해 지원되는 함수

XXX_Deinit
등록해제시 호출되어지는 드라이버함수

RegisterDevice() 함수를 사용해 등록한 드라이버는 DeregisterDevice()함수를 사용해 등록해제하게 되며, ActivateDevice(), ActivateDeviceEX() 함수를 사용해 등록한 드라이버는
DeactivateDevice() 함수에 의해 등록해제 된다.

XXX_PowerUp
시스템의 공급되는 파워가 낮은 레벨에서 높은 레벨로 변화를 시도하게 되면, 커널은 디바이스 드라이버들이 이 사실을 인지하도록 지원하기 위한 함수

XXX_PowerDown
시스템의 공급되는 파워가 높은 레벨에서 낮은 레벨로 변화를 시도하게 되면, 커널은 디바이스 드라이버들이 이사실을 인지하도록 지원한다.

XXX_IOControl
응용프로그램과 통신하는 목적으로 사용

IOCTL_POWER_CAPABILITIES
드라이버가 등록되는 과정에서 호출되며, 드라이버가 원하는 전원관리 정보를 받는데 사용

응용프로그램과 통신하는 함수
XXX_Open
응용프로그램은 Win32API인 CreatFile() 함수를 사용해 드라이버에 접근할수 있는 권한을 얻게 되는데, CreateFile() 함수는 적절한 디바이스 드라이버의 XXX_Open()을 호출해 드라이버의 동의를 구하게 된다.

XXX_Read
응용프로그램은 Win32 API인 ReadFile() 함수를 사용해 드라이버로부터 일정량의 데이터를 읽어 올 수 있다. 이때 사용하는 파일 핸들은 반드시 이전에 CreatFile()을 통해 얻은 파일핸들을 사용해야 한다.

XXX_Wirte
응용프로그램은 Win32 API인 WriteFile() 함수를 사용해 드라이버로 일정양의 데이터를 기록하는 요청을 보낼수 있다.

XXX_IOControl
응용프로그램은 Win32 API인 DeviceIoControl() 함수를 사용해 응용프로그램이 2개의 버퍼를 제공함으로써, 하나는 응용프로그램 측에서 드라이버로, 또다른 하나는 드라이버 측에서 응용프로그램으로 데이터를 전달하도록, 즉 양방향으로 사용되는 버퍼를 하나의 DeviceIoControl() 함수를 이용해 사용할수 있다.

XXX_Seek
응용프로그램은 Win32 API인 SetFilePointer() 함수를 사용해, 읽기 혹은 쓰기 작업을 하는 시작 위치를 정의 할수 있다.

XXX_Close
응용프로그램인 Win32 API인 CloseHandle() 함수를 이용해, 사용하던 권한을 반납한다.


스트림 인터페이스 드라이버는 장치관리자(DEVICE.exe) 프로세스 아래에서 동작하는 DLL형식을 갖는 모듈이다. 이들은 장치관리자 프로세스 아래에서 DLL로 동작하는 성질을 가짐과 동시에 자기 자신을 디바이스 드라이버로 동작하도록 등록되어야한다.
필수요건 3가지
1. 스트림 인터페이스를 지원하기 위한 함수들을 가져야한다.(XXX_Init, XXX_Open등)
2. 적당한 시스템 레지스트리에 등록되어 있어서 ActivateDevice()되거나 아니면 누군가에 의해서 RegisterDevice() 되어야 한다.
3. 사용되는 XXX_Init, XXX_Open 등의 함수를 DLL 외부에서 쉽게 찾을수 있도록 각 함수들을
DLL 익스포트 시켜야 하며, 보통은 DEF파일이 사용된다.


실제로 디바이스 드라이버가 바로 호출될 당시에는 커널에 의해 디바이스 드라이버를 호출한 응용프로그램을 접근할 수 있는 권한이 디바이스 드라이버 코드에게 할당된다. 그렇기 때문에 드라이버는 호출한 슬롯상의 응용프로그램 버퍼 주수에 대한 매핑(MapPtrToProcess)후 사용

MapPtrToProcess()
접근하는 주소를 구하는 함수

네이티브 인터페이스 드라이버
네이티브 인터페이스라는 이름에서 이름에서 알수 있듯이, 네이티브 인터페이스를 지향하는 드라이버 작성방법이 모두 동일하지는 않다. 이들은 모두 GWES.exe 프로세스 문맥아래에서 동작하며, 부팅과정을 통해서 메모리에 상주한다.

GWES 문맥 아래에서 동작하는 드라이버에서 하드웨어 접근을 위한 코드를 갖는 방식은 크게
2가지로 나뉜다.
1. 직접 버스에 접근하는 방식
2. DEVICE.exe문맥 아래에서 동작하는 스트림 인터페이스 DDI를 사용하는 클라이언트 드라이버를 호출하는 방식

하드웨어로부터 발생하는 인터럽트는 OAL(OEM Adaptation Layer)에 의해서 적절한 Logical Interrupt ID로 변환되어 커널에게 보고 되는데, 이렇게 보고되는 Logical Interrupt ID에 의해 디바이스 드라이버 상에서 동작하고 있는 인터럽트 서비스 스레드에게 알려진다.

GWES : Graphic Windowing Event Subsystem

디바이스 드라이버는 크게 두가지 방향의 인터페이스를 구현한다. 하나는 응용프로그램으로부터 연결되는 Upper Edge이며, 또 다른 하나는 하드웨어나 버스 인터페이스를 사용하는 Lower Edge이다. 이 중 Upper Edge에 해당하는 부분이 사실상 스트림 혹은 네이티브라는 의미를 갖게 된다. 개발자가 만들 드라이버 소스를 실제로 열어 봤을 때, 본인이 Upper Edge와 Lower Edge모두를 구현하는 경우를 모노리딕(Monolithic Driver)라고 부르며, 그렇지 않고 Upper Edge나 Lower Edge중 하나만을 구현한 경우를 계층 드라이버(Layered Driver)라고 부른다.


마이크로소프트 플랫폼 빌더는 개발자들이 드라이버를 개발하는 데 도움을 주고자 Upper Edge에 해당하는 부분을 제공하는데, 이것을 MDD(Model Device Driver)라고 부른다. 반면 이런 MDD를 사용하는 경우에 개발자가 당당하는 Lower Edge를 PDD(Platform Dependent Driver)라고 부른다. “Platform Dependent”이라는 말 그래도 하드웨어 종속적인 코드만을 구현한다는 의미를 가진다. 이렇게 MDD와 PDD형식을 갖는 계층 드라이버 작성이 있어서 스트림이나 네이티브라는 용어 대신에 MDD와 PDD간의 호출규약이 필요하게 되는데, 이것을 DDSI(Device Driver Service Provider Interface)라고 한다.

미니포트 드라이버는 서로 다른 바이너리 파일 형태를 가진 복수 개의 드라이버들이 어우러져 있는 상태이다.

하드웨어 인터럽트 서비스 스레드 구현방법
모든 종류의 하드웨어 인터럽트는 커널이 가장 먼저 이를 수신하여 OAL의 도움을 받아서 실제로 디바이스 드라이버에게 전달하기 때문이다.

디바이스 드라이버의 IST(Interrupt Service Thread)는 다른 스레드와는 달리 비교적 높은우선순위에서 체류하기 때문에, 대부분 자신에게 할당된 퀀텀 시간을 sleep상태 (WaitForSingleObject)로 설정해둬야 한다. 그러다가 하드웨어 인터럽트를 다를 때만 잠깐 깨어나며, 인터럽트를 모두 다른 뒤에는 다시 Sleep상태로 전화해야 한다.


하드웨어 인터럽트 처리과정

1. IST는 자신이 사용할 하드웨어 인터럽트를 명시하는 Interrupt Logical IDfmf 정한다. 또한 자신이 사용할 Event를 준비한다. 이렇게 ID와 Event를 준비한 다음, 두가지 정보를 커널함수
InterruptInitialize()을 통해서 커널에게 전달

2.커널은 해당하는 인터럽트 ID가 유효한 ID인지를 확인하기 위해서, OAL의 OEMInterruptEnable()함수를 호출한다.

3. 이렇게 호출된 OAL의 OEMInterruptEnable()은 자신이 받은 Logical ID의 유효성을 확인한뒤, 해당하는 하드웨어 인터럽트가 발생 가능하도록 조치한 다음돌아간다. 이제 IST는 대부분의 시간을 WaitForSingleObject(Event) 함수를 사용하여 Event를 기다리게 된다.하드웨어로부터 인터럽트 신호가 발생하면, 이처리는 가장 먼저 커널이 담당하게 된다.

4.커널은 발생한 하드웨어 인터럽트의 Logical ID를 확인ㅇ하기 위해서 OEM의 ISR(Interrupt Service Routine)을 호출한다.ㄴ

5.이때 호출되는 OEMInterruptHandler()함수는 ARM계열의 CPU를 사용하는 플랫폼에서 사용되는 OSL의 ISR함수이름이다. 호출된 OEMInterruptHandler()는 해당하는 하드웨어 인터럽트를 금지시킨뒤 적절한 ID커널에게 리턴한다. 그러면 커널은 리턴된 ID를 사용하여, 관련된 Event를 시그널시킨다.

6. 디바이스 드라이버는 오랜 시간 Sleep 상태에서 머물다가 깨어난 뒤 하드웨어를 접근하여 원하는 데이터를 수신하거나 송신하게 된다. 이렇게 디바이스드라이버가 실제로 인터럽트에 대한 조치를 한다는 것을 유념해야 한다. OAL이나 커널은 인터럽트 허용 및 금지와 관련된 조치만 취한다. 디바이스 드라이버는 적당한 시기에 해당하는 인터럽트 처리 종료 사실을 커널함수 InterruptDone()을 사용해서 알린다.

7.커널은 OAL의 OEMInterruptDone()를 호출하여 해당하는 인터럽트 처리 종료 사실을 OAL에게 알린다. 해당함수는 인터럽트의 금지상태를 해제하게 된다.


저마다의 IST가 자신이 깨어날 조건을 커널에게 알리기 위해서 등록하는 서비스루틴을 Installable ISR이라고 부른다. Installable ISR이 하는 일은 같은 종류의 하드웨어 인터럽트가 공유되는 경우에 실제 인터럽트를 요청한 장치를 찾아서 이것과 관련된 서로 구분된 Interrupt Logical ID를 커널에게 리턴하는 것이 그목적이다.


C 함수는 스택(stack)과 힙(Heap)이 없으면 사용이 불가능하기 때문에 어셈블리 언어로 이 작업들을 완료한 이후에 C언어로 넘어가 작업을 한다.

메모리 사용을 위한 정보를 바꾸기 위해서는 g_oalAddressTable만을 수정해 사용
해당테이블은 oemaddrtab_cfg.inc 파일에 명기되어 있음

EBOOT
부트로더의 목적은 실행이미지를 메모리에 위치시키고, 운영체제를 구동할수 있도록 점프하는 기능을 가지고 있다.
OEM 코드부분에는 시스템의 초기화 부분과 각각의 하드웨어에 대한 콘트롤 함수들이 위치하게 된다.

코드를 분석하는데 있어서 중요한 점은 시작점을 찾아내는 것이다.
실제 시작점은 Sources파일의 상단에 있는 EXEENTRY=StartUP 부분

리셋핸들러의 내용
1. I/D TLB 클리어/캐시 사용중지
2. 위치독 타이머 사용중지
인터럽트 사용중지
클럭 설정
메모리 컨트롤러 초기화
부트로더를 메모리로 복사
3. MMU설정(물리지소를 가상주소로 설정)
4. 스택 포인터 초기화
5. C의 main()함수로 점프

EBOOT의 수행내용은 BootloaderMain()함수에 모두 존재

BootloaderMain() 함수안에 있는 EdbgOutputDebugString 명령에 의해서 디버그 포트로
출력되는 메시지

OEMDebugInit() 함수에서 디버그 포트를 초기화

OEMPlatformInit() 함수에서는 사용하는 플랫폼에 대한초기화 작업을 수행

EBOOT 디렉토리의 boot.bib의 파일에 의해서 EBOOT이미지가 생성

ROM 이미지를 빌드하기 위해서는 ROM 이미지를 만들어내는 툴인 Romimage.exe파일을 사용
이것은 또한 Makeimg.exe라고도 부르는데 .bib 파일에 의해서 메모리에 적재되는 위치가 결정

OAL이란 OEM Adaptation Layer의 약자로 Windows CE의 커널과 하드웨어 사이에서 상호 간에 정보를 교환해 주는 창구역하를 하는것이다. 그렇기 때문에 BSP는 이와 같은 OAL 함수로 대부분이 구성된다.

OAL을 손쉽게 작성하려면 다음의 기본절차를 따르게 된다.
1. OAL을 작성하기 위해서는 BSP안에 특별한 디렉토리를 만든다.
PLATFROM\[BSP이름]\KERNEL\OAL
Dirs 파일에 새로 생성한 디렉토리 이름을 추가한다.
2. 기본적인 OAL함수를 작성한다.
Startup 관련함수
디버그 포트초기화(Debug Port Initialize) 관련함수
시스템 타이머(System Timer) 관련함수
인터럽트 처리함수
3. 추가적인 OAL 함수를 작성한다.
RTC(Real Time Clock)
타이머
확장 메모리

ARM CPU에 대한 초기화 작업은 다음과 같은 일을 한다.
SVC(Supervisor) 모드로 변경
FIQ와 IRQ를 사용하지 못하도록 설정
MMU와 I/D 캐시를 사용하지 못하도록 설정
TLB(Translation Look-aside Buffer)를 지우고 WB(Write Buffer)를 지움
각종 디바이스들의 초기화
메모리 컨트롤러 초기화
인터럽트 컨트로러 초기화
클럭 설정
OEMAddressTable의 물리주소에 대한 기준 주소를 R0 레지스터에 저장
KernelStart로 분기

ARM CPU에 대한 초기화 작업은 시스템이 동작하게끔 하기 위한 일련의 코드들이 들어간다.

커널에 대한 초기화
첫번째 페이지 테이블 초기화
MMU와 캐시를 사용하도록 설정
동작모드 별 스택 초기화
커널이 사용하는 글로벌 데이터 초기화
디버깅 관련 함수를 사용하도록 설정
OEMInit() 함수를 호출
메모리 초기화
기타 초기화

디버그 포트에 관련된 함수
OEMInitDebugSerial()
동작속도, 패리티 비트, 정지비트에 대한 설정
OEMReadDebugByte()
디버그용 포트로부터 한 바이트씩 입력
OEMWriteDebugByte()
디버그 포트로 한 바이트씩 출력
OEMWriteDebugString()
디버그 포트로 문자열을 출력

OEMInit() 함수에서는 펌웨어로 초기화하는 과정이다.
타겟 시스템의 모든 하드웨어에 대한 초기화, KITL 초기화, 커널이 사용하는
전역변수(Global Variable)의 초기화 작업이 수행

인터럽트 처리에 쓰여지는 함수
OEMInterruptEnable()
하드웨어적으로 인터럽트를 사용하도록 설정함
OEMInterruptDisable()
하드웨어적으로 인터럽트를 사용하지 못하도록 설정함
OEMInterruptDone()
인터럽트 처리가 완료된 이후에 다시 인터럽트를 사용하도록 설정함

OALTimerInit 함수
이 함수는 시스템 타이머를 설정하기 위한 함수. 시스템 타이머는
인터럽트 서비스 루틴(ISR, Interrupt Service Routine)을 이용해 인터럽트 등록을
시켜서 사용한다.

WinCE에서는 두가지 과정을 통해서 인터럽트가 처리된다.
그 두가지는 ISR로 불리는 인터럽트 서비스 루틴(Interrupt Service Routine)과 IST로
불리는 인터럽트 서비스 스레드(Interrupt Service Thread)이다. ISR은 복수개의 인터럽트 요청에 대응한다. 인터럽트가 사용 가능한 상태에서 인터럽트를 사용하기 위해서는 ISR에 인터럽트를 등록해야만 사용이 가능하다. 외부에서 인터럽트가 요청이 들어오면 ISR은 해당 인터럽트를 식별하여 어떤 인터럽트인지를 알려준다. 이것이 바로 SYSINTR로 시작되는 인터럽트 ID이다.
커널은 인터럽트 ID를 가지고 해당하는 IST를 수행하게 되는것이다.


인터럽트를 처리를 위해서 해당하는 디바이스 드라이버가 로딩된 이후에 초기화 단계에서 인터럽트를 초기화하는 작업을 수행해야 한다. 이경우에 CreatThread()함수를 이욯하게된다. 이함수는 실행시킬 스레드 함수의 주소를 설정한다. 즉, 해당 스레드의 ID가 발생하는 경우에 실행될 스레드 처리 함수의 주소를 설정한다는 것이다.

인터럽트 초기화단계

1. CreateThread()
2. CreateEvent()
3. KernelloControl()
4. InterruptInitialize()

위와 같은 인터럽트가 초기화 되면, 스레드가 수행되며 인터럽트가 발생되기를 기다리게 되는데, 인터럽트 처리를 위해서 WaitForSingleObject()와 InterruptDone() 함수가 사용된다.