이번 글에서는 프로젝트 옵션에서 hex 또는 binary 형식으로 실행 코드를 생성하는 방법과 command line의 명령어들을 빌드 시 사용하는 방법 등에 대해 알아보고 Static Analysis와 Runtime Checking 기능도 함께 살펴보도록 하겠습니다.

 

들어가며 
지난 글에서는 국내 실시간 운영체제(RTOS) 기술자립을 이끌어가고 있는 NEOS의 적용 사례와 기본 특징에 대해 살펴보았다. NEOS는 다양한 무기체계 개발사업의 여러 부분품에 적용되어 개발 중이고, 체계 개발에 상당한 비중을 차지하는 SW 개발에 있어 국산 RTOS의 중요성을 계속 입증하고 있다.

[그림 1] NEOS 적용 분야

방위사업청의 SW 개발지침에 따라 코딩 규칙(방위사업청 코딩 표준)에 기반한 개발 구현 및 통합 활동을 수행하였고, 정적 분석 도구를 이용하여 코드 분석을 완료하였다. 또한 개발보드 및 실제 무기체계에 적용된 보드를 통해서 다양한 조건의 시험을 완수하였다. 

NEOS를 경험하지 못한 개발자들을 위해서 이번 기고에서는 NEOS의 기본적인 운영체제/개발환경 기능을 설명하고  개발환경 설치 과정과 첫 번째 예제 프로그램을 실행하는 과정을 설명하고자 한다. 이를 통해 NEOS를 사용하고자 하는 개발자들이 대략적인 기능과 환경을 이해하는 데 도움이 되었으면 한다.

 

NEOS 개발환경의 이해
NEOS가 지원하는 CPU 목록 
NEOS는 지금까지 다양한 국방/항공 분야 고객을 위해 BSP 지원 범위를 확대해 왔다. 다음은 NEOS가 지원하는 주요 CPU와 보드를 보여준다.

[그림 2] NEOS 지원 BSP

NEOS 구성 요소 
NEOS는 하드웨어 비의존적인 부분과 하드웨어 의존적인 부분으로 나뉘어 각 기능이 모듈화되어있다. 이중 하드웨어 비의존적인 모듈로는 NEOS의 핵심인 실시간 커널과 다양한 서브 시스템 라이브러리가 있다.

[그림 3] NEOS 아키텍처

반면 하드웨어 의존적인 모듈에는 CPU 코어에 의존적인 ASP(Architecture Support Package)와 보드 장치에 의존적인 BSP(Board Support Package)가 있다.

 

NEOSPACE 통합 개발환경의 기능과 구성 
NEOS 개발자용 통합 개발환경인 NEOSPACE는 오픈 플랫폼인 Eclipse를 기반이고 임베디드 타겟을 위한 크로스 컴파일러(Cross Compiler)로 GNU GCC 4.3.3을 비롯한 툴체인을 포함하고 있다. 아울러 실행 이미지를 네트워크로 다운로드하기 위한 NEOS TFTP 서버 등 유틸리티를 함께 제공한다.

 

호스트 컴퓨터와 타겟 기본 구성 
NEOSPACE는 크로스 툴체인을 이용하여 응용 프로그램을 빌드하고 빌드된 이미지를 이더넷 인터페이스를 통해 다운로드하여 실행하는 기본 구조로 되어 있다. 빌드된 응용 프로그램이 실행하면서 출력되는 메시지는 표준 입출력 장치로 사용되는 시리얼 장치(UART)로 전달되어 시리얼 터미널에서 출력된다. 

[그림 4] NEOS 개발 환경

NDK (NEOS Development Kit) 설치 
NEOS Development Kit은 DVD의 형태로 제공되는데 NEOS RTOS와 NEOSPACE 통합 개발환경, 기타 유틸리티 등이 포함되어 있다. 다음 그림과 같이 간단한 설치과정을 거쳐 호스트 컴퓨터에 NDK를 설치할 수 있다. 

[그림 5] NEOS 설치 절차

 

NDK가 설치된 디렉토리 구성 
NDK를 설치하면 아래와 그림과 같은 파일들이 설치된다.

[그림 6] NDK 설치 파일 구조

Docs 디렉토리 - NEOS 사용자 매뉴얼, NEOS API 문서, IDE 사용자 매뉴얼 등이 있다.
ExtraTools 디렉토리 - 임베디드 시스템 개발에 필요한 TFTP 서버, Tera Term, 라이센스 관리 프로그램 등이 있다.
License 디렉토리 - NDK 라이센스 관리 프로그램이 사용하는 라이센스 파일이 있다.
Neos 디렉토리 - NEOS 헤더, BSP, NEOS 라이브러리, 장치 드라이버 소스 등이 있다.
NEOSPACE  - Eclipse IDE 패키지
Tools  - 빌드를 위한 크로스 툴체인

 

NEOS 프로젝트 타입 
NEOSPACE는 다음 3가지 프로젝트 타입을 지원한다.

NEOS 커널 이미지 프로젝트  - 커널이 포함된 응용 프로그램을 개발하기 위해 사용하는 프로젝트 타입으로 하나의 실행 이미지로 xxx.bin과 xxx.elf 파일 생성
NEOS 응용 프로젝트  - 커널이 포함되지 않고 응용 SW만의 실행 이미지를 개발하기 위해 사용하는 프로젝트 타입으로 xxx.app 파일을 생성하며 NEOS가 이미 동작 중인 타겟보드에 동적 로더로 로딩하여 실행함
NEOS 정적 라이브러리 - 라이브러리 생성 시 사용하는 프로젝트 타입으로 xxx.a로 정적 링크 가능한 라이브러리 이미지 파일 생성

 

첫 번째 프로젝트 생성하기: HelloWorld 
이제 본격적으로 NEOSPACE를 실행하여 첫 번째 HelloWorld 프로젝트를 만들어 본다.
① File -> New -> NEOS Project 선택
② Project Name을 입력하고 NEOS Kernel Image를 선택한 후 Next 클릭

[그림 7] HelloWorld 프로젝트 생성하기 (1)
[그림 8] Hello World 프로젝트 생성하기 (2)

③ Configurations을 선택하고 Next 클릭 
④ BSP(Board Support Package, 사용하는 타겟보드명)를 선택하고 Finish 버튼 클릭.

 

HelloWorld 프로젝트 빌드하기 
생성된 HelloWorld 프로젝트를 아래와 같이 빌드한다.
① 생성된 프로젝트 트리 최상위에 마우스 오른쪽 버튼을 누르고 빌드 모드 선택
② 빌드 모드 설정을 완료하고 Build Project를 눌러 프로젝트 빌드
③ 하단의 Console View에서 빌드 과정 확인 

[그림 9] HelloWorld 프로젝트 빌드하기

HelloWorld 프로젝트 실행 
HelloWorld 프로젝트를 다음과 같은 절차로 실행한다
① ExtraTools 디렉토리의 teraterm-4.77.exe 프로그램 설치
② Tera Term 프로그램을 시작하여 설정 -> 시리얼 포트 메뉴 선택
③ COM 포트를 타겟 보드 환경에 적합하게 설정

[그림 10] HelloWorld 프로젝트 실행 (1)

④ 타겟 보드 전원을 켜고 Tera term을 통해 부트 로더 메시지 출력 확인.
⑤ ExtraTools 디렉토리에 있는 TFTP Server 실행
⑥ 실행된 TFTP 서버의 Menu->Setting 메뉴 선택

[그림 11] HelloWorld 프로젝트 실행 (2)

⑦ TFTP 서버 IP 및 TFTP 디렉토리 경로 지정
⑧ 부트 로더가 실행중인 타깃에서 HelloWorld.bin 이미지를 다운로드 및 실행하는 명령 실행
⑨ 시리얼 콘솔에 NEOS 로고 출력을 확인한다.

[그림 12] HelloWorld 프로젝트 실행 (3)

NEOSPACE를 이용한 디버깅 
NEOSPACE는 응용 프로그램 디버깅을 위해 SW 디버거를 제공한다. SW 디버거는 내부적으로는 GNU GDB를 사용하며 원격 타겟 보드의 디버그 에이전트와 연동하여 타겟의 메모리 및 CPU 레지스터 정보를 읽거나 쓸 수 있게 한다. 

단 SW 디버거의 한계상 “NEOS Application” 프로젝트로 빌드된 응용 타입의 이미지만 디버깅이 가능하고 커널 전체를 포함한 이미지를 전체적으로 디버깅하는 것은 불가능하다. 이를 위해서는 JTAG/BDM 등의 하드웨어 디버거를 사용하여야 한다. 아울러 하드웨어 의존성이 있는 인터럽트를 사용하는 장치 드라이버 등의 디버깅은 정확한 결과를 보장하기 어려운 측면이 있다. 

NEOSPACE를 이용한 SW 디버깅을 아래와 같이 실행할 수 있다. 

[그림 13] NEOS 디버깅 화면

① 먼저 기반이 될 “NEOS Kernel Image” 프로젝트를 생성하고 타겟보드에 로딩하여 실행한다.
② 디버깅할 응용 프로그램을 “NEOS Application” 프로젝트로 생성하여 빌드한다.
③ 디버거 설정화면에서 타겟 보드의 IP 주소를 지정한다.
④ 디버깅 실행 버튼을 눌러 디버깅을 시작한다.
    - 응용 프로그램은 자동적으로 타겟에 다운로드된다.
    - 로드된 응용 프로그램은 NEOS 동적 로더에 의해 실행된다.

⑤ 최초 디버깅 시 기본적으로 응용 프로그램의 main() 함수에서 멈추고 대기한다.

 

NEOS 커널 기능 소개 
앞 장에서는 NEOS 개발환경을 간략히 살펴보고, 프로젝트 생성과 빌드, 디버깅 방법에 대해 살펴보았다. 이 장에서는 그러한 개발환경을 이용하여 응용할 수 있는 NEOS 커널의 각 기능에 대해 개략적으로 소개한다.

 

NEOS 커널 개요  
임베디드 시스템을 위한 실시간 운영체제는 멀티 태스킹 환경과 태스크 간의 통신 및 동기화 기능을 기본적으로 제공한다. 이를 이용하여 시스템 개발자는 전체 응용 SW를 여러 개의 분리된 작업 단위(태스크)로 분할하여 작성한 후, 운영체제가 제공하는 스케줄러를 통해 다중 실행하도록 구성할 수 있다.

또한 태스크 간 통신 기능을 이용하면 생성한 태스크 간에 서로 동기화하여 통신하도록 구성할 수 있다. NEOS 커널은 스레드(Thread)라는 기본 작업 단위를 제공하고, 다수의 스레드를 단일한 메모리 주소 공간을 공유하여 수행시키는 구조로 설계되어 있다. 이를 통해 하드 리얼타임 성능을 보장한다. 

아울러 실시간 운영체제에서 중요한 요소 하나는 하드웨어에서 발생하는 인터럽트 요청을 예측 가능한 방식으로 얼마나 정확하고 신속하게 처리하는가의 여부이다.

이는 임베디드 시스템은 주로 인터럽트를 통해 외부로부터 이벤트를 전달받고, 이를 가장 높은 우선순위로 신속하게 처리해야 하기 때문이다. 인터럽트를 처리하기 위해 NEOS를 비롯한 대부분의 실시간 운영체제 커널은 인터럽트만을 위한 수행 문맥(Context)를 별도로 정의하여 가장 높은 우선순위로 처리한다.

 

스레드 
실시간 시스템 SW는 대개 여러 개의 작업(태스크)을 동시다발적으로 실행하는 형태로 구성된다. 이때 각 스레드는 실시간 스케줄링에 필요한 우선순위(Priority) 값이나 시간 제약(Timing constraints) 값 등을 속성으로 가진다.

효과적인 멀티 스레드 실행 환경을 보장하기 위해 실시간 시스템은 실시간 스케줄러를 사용해 우선순위와 시간 제약 조건을 만족시키도록 스레드를 스케줄링한다. 또한 스레드들은 서로 메시지를 주고 받으며 통신을 하고 동기화를 통해 공유 자원을 사용한다. 

[그림 14] NEOS 상태 천이도

NEOS의 스레드는 API 호출과 스케줄링에 따라 다음 그림의 6가지 상태 간을 천이하며, READY 상태를 갖는 가장 높은 우선순위의 스레드가 실행을 선점하여 실행(RUNNING)하는 방식으로 스케줄링한다. 

 

동기화와 통신 
멀티 스레드 환경을 위해서는 스레드 간의 동기화와 통신을 위한 방법이 요구되며 NEOS 커널은 이를 위해 다음의 방법을 제공한다.

- 상호배제와 동기화를 위한 세마포어
- 스레드 간 통신을 위한 메시지 큐
- 특정 스레드에 이벤트를 전달하기 위한 이벤트 플래그

[그림 15] 바이너리 세마포어 상태 천이도

세마포어는 여러 개의 스레드가 동시에 공유자원에 접근하는 것을 동기화하기 위해 사용되는 커널 객체이다. 전역 변수, 임계 영역(Critical Section)이라 불리는 코드의 일부분, 또는 스레드 간에 공유될 수 있는 모든 자원들이 공유자원이 될 수 있다.

NEOS 커널은 사용되는 목적과 동작 방식에 따라 세 가지 종류의 세마포어를 제공하는데, 그 중 뮤텍스 세마포어에는 잘 알려진 우선순위 역전현상을 방지하는 메커니즘이 적용되어 있다.

- 바이너리 세마포어 (Binary Semaphore)
- 카운팅 세마포어 (Counting Semaphore)
- 뮤텍스 세마포어 (Mutex Semaphore)

아울러 NEOS에서 지원하는 메시지 큐 API를 이용하면, 스레드에 비동기적인 통신 프로토콜을 사용할 수 있다. 비동기적인 통신 프로토콜의 의미는 메시지의 송/수신 스레드가 동시에 상호 작용할 필요가 없다는 것이다.

송신된 메시지는 큐에 전송되고 메시지를 받을 수신 스레드에 의해 수신되기 전까지 큐에서 관리되는데 수신 시점은 비동기적으로 설계된다. 응용 SW 개발자의 의도에 따라 메시지 큐 내에 관리되는 메시지 길이와 개수, 큐의 속성은 가변적으로 설정될 수 있다. 

[그림 16] 메시지 큐 내부 구조

이벤트 플래그는 여러 스레드를 동기화시키는 가장 간단한 방법으로 스레드 간 비트 단위의 통신을 한다. 메시지 큐와는 달리 이벤트 플래그는 특정 이벤트 발생 여부만을 스레드에 알리며 이 이벤트에 해당하는 정보나 데이터는 함께 제공하지 않는다.

이벤트 플래그는 매우 단순하여 스레드 연결 기능을 구현하는데 주로 사용되고, 스레드 연결 기능은 한 스레드가 다른 스레드의 종료를 기다려 종료된 후 수행되는 것이다. 다음은 이벤트 플래그의 특징을 보여준다.

- 스레드는 하나 이상의 이벤트를 동시에 기다릴 수 있음
- 이벤트는 서로 독립적임
- 이벤트는 데이터를 전달하지 않음
- 대상 스레드가 이벤트를 읽어 처리를 수행하기 전에 다시 이벤트가 한 번 이상 보내질 경우 마지막으로 보내진 이벤트만이 유효함

[그림 17] 이벤트 플래그 동작 원리

SW 타이머
SW 타이머는 시스템 시간 값을 계산하여 사용자에게 타이머 서비스를 제공한다. 즉, 사용자가 특정 시간 후에 실행되기를 원하는 함수를 등록하면 SW 타이머의 내부에 저장된 시간 카운트 값은 시스템 클락 인터럽트가 발생할 때마다 감소하여 정확한 시간 후에 등록된 함수를 호출한다. 실행중인 SW 타이머를 정지시키면, 해당 SW 타이머는 즉시 정지하고 그 카운트 값은 삭제된다.

 

인터럽트 처리
인터럽트란 프로그램 실행 중에 하드웨어로부터 발생한 이벤트를 말하고 인터럽트가 발생하면 CPU는 실행중인 작업을 중단하고 발생된 이벤트를 우선 처리한 뒤 기존에 수행 중이던 작업으로 복귀한다. NEOS는 사용자가 장치 IRQ 번호만 알면 쉽게 ISR을 등록하고 인터럽트 관련 기능을 사용하도록 다양한 인터럽트 API들을 제공한다.

[그림 18] 인터럽트 소프트웨어 처리

예외 처리
예외란 프로그램이 실행되는 도중에 프로세서의 정상적인 실행 흐름을 방해하는 모든 이벤트를 말하고 프로세서로 하여금 특정 상태에서 정해진 명령을 수행하도록 한다.

예외는 동기(Synchronous)와 비동기(Asynchronous) 두 가지로 구분된다. 동기식 예외는 프로세서 명령어 실행 중에 발생한 내부 이벤트를 말하고 0으로 나누는 산술연산 또는 필요로 하는 데이터가 접근 불가능한 경우 발생하는 예외가 대표적인 예이다. 

반면에 비동기 예외는 일반적으로 하드웨어 장치에서 발생한 신호들로 인한 외부 이벤트를 말하는데, 인터럽트가 대표적인 예이다. NEOS는 이러한 예외 상황이 발생할 경우 디버깅을 효율적으로 하기 위해 CPU 내부 레지스터 및 함수 Call-BackTrace를 출력하는 기본 핸들러가 등록되어 있고 사용자는 이러한 기본 핸들러를 변경할 수 있다.

또한 기본 핸들러가 호출되고 나서 사용자가 원하는 디버깅 코드를 추가할 수 있는 구조도 제공한다.

 

나가며
이번 글에서는 NEOS 개발환경에 대한 기본적인 소개와 사용방법, NEOS 커널의 기본적인 기능에 대해 살펴보았다. 이어지는 다음 글에서는 NEOS 커널의 각 기능에 대해 한 단계 더 깊이 들어가 세부적인 API와 복합적인 예제 코드를 주제로 두고 좀더 자세하게 설명하는 시간을 갖고자 한다. 

 

글 : 장재형 책임연구원 / NT 개발실 / MDS테크놀로지
자료제공 : MDS테크놀로지 <www.mdstec.com>

이 기사를 공유합니다
저작권자 © 테크월드뉴스 무단전재 및 재배포 금지