사전 임베디드
개념

임베디드

gabury1

더 큰 기계 안에 붙박이로 들어가는 컴퓨터입니다. 그 기계가 하는 일 하나만 맡습니다. 사람이 프로그램을 새로 깔아 가며 쓰는 컴퓨터가 아닙니다. 세탁기 안에도 자동차 안에도 이런 컴퓨터가 들어 있습니다.

쉽고 빠른 이해

더 큰 기계 안에 붙박이로 들어가 그 기계가 하는 일 하나만 맡는 컴퓨터입니다. 전자레인지의 버튼과 표시창 뒤에도 이런 컴퓨터가 있어서, 조리 시간을 세고 문이 닫혔는지만 봅니다.

범용 컴퓨터를 기계마다 통째로 넣으면 부피와 전력을 그만큼 잡아먹습니다. 하는 일을 미리 정해 두면 필요 없는 것을 덜어낼 수 있어서 이렇게 만듭니다.

도는 방식은 이렇습니다.

  1. 평소에는 안에 미리 넣어 둔 프로그램 하나가 돕니다.
  2. 물리 장치에서 신호가 오면 하던 일을 멈추고 그 신호를 처리하는 정해진 자리로 건너뜁니다.
  3. 처리가 끝나면 멈췄던 자리로 돌아가 하던 일을 잇습니다.

대가는 정해 둔 일 밖의 다른 일은 못 한다는 것입니다. 하는 일이 자주 바뀌면 범용 컴퓨터를 쓰는 쪽이 낫고, 하나로 굳어 있으면 임베디드로 갑니다. 나중에 다른 일을 시키려면 프로그램을 새로 만들어 다시 심어야 합니다.

상세

전자레인지 앞판의 숫자 버튼과 표시창을 떠올리면 됩니다. 그 안에도 계산하는 부품이 들어 있습니다.

임베디드 시스템은 더 큰 기계나 장치 안에 들어가 정해진 기능만 맡는 컴퓨터 시스템입니다. 정의는 이 한 문장에서 끝납니다. 안에 들어간다는 자리와 정해진 기능만 맡는다는 역할, 둘이 같이 있어야 합니다. 표제어 「임베디드」는 이 임베디드 시스템을 줄여 부르는 이름이고, 이 문서에서는 둘을 같은 뜻으로 씁니다.

안을 들여다보면 계산을 하는 프로세서 하나에 물리 장치와 신호를 주고받는 통로가 붙어 있습니다. 신호를 받거나 내보내는 입출력 핀, 다른 장치와 정해진 방식으로 주고받는 통신 버스, 시간을 세는 타이머 같은 것들입니다.

범용 컴퓨터와 갈리는 자리는 셋입니다.

범용 컴퓨터 임베디드 시스템
하는 일 쓰는 사람이 그때그때 정합니다 만들 때 미리 정해집니다
프로그램 사용자가 새로 깔거나 지웁니다 대개 만들 때 넣은 것으로 끝납니다
시간 결과만 맞으면 되는 일이 많습니다 정해진 시각 안에 응답해야 하는 경우가 많습니다

세 번째 자리가 붙는 이유는 임베디드가 대개 물리 장치에 붙어 있기 때문입니다. 모터를 멈추는 신호가 늦게 나가면 모터는 이미 지나가 있습니다.

돌아가는 방식은 이렇습니다. 평소에는 안에 미리 넣어 둔 프로그램 하나가 돕니다. 물리 장치에서 신호가 오면 프로세서는 하던 일을 멈추고 그 신호를 처리하는 정해진 루틴으로 건너뜁니다. 정상 흐름을 멈추고 다른 자리로 건너뛰는 것을 예외라고 부르고, 그 예외가 바깥 장치에서 온 신호로 일어나면 인터럽트라고 부릅니다. 처리가 끝나면 멈췄던 자리로 돌아갑니다. 건너뛸 자리(진입 주소)들을 모아 둔 목록을 벡터 테이블이라고 부릅니다.

임베디드는 작은 컴퓨터가 아니라 전용 컴퓨터입니다. 크기로 가르면 손바닥만 한 범용 컴퓨터가 임베디드가 되어 버립니다. 가르는 것은 크기가 아니라 그 컴퓨터가 어디에 들어가 무엇을 맡느냐 입니다.

하는 일이 자주 바뀌거나 여러 프로그램을 한꺼번에 돌려야 하면 범용 컴퓨터를 그대로 쓰는 쪽이 낫습니다. 하는 일이 하나로 굳어 있고 자리나 전력이 빠듯하면 임베디드로 갑니다. 나중에 다른 일을 시키려면 프로그램을 새로 만들어 다시 심어야 하는 경우가 많습니다.

배경

기계 하나에 정해진 일 하나를 시키려는 쪽이 먼저 있었습니다. 그 일에는 계산이 필요합니다. 그 계산기를 사람이 옆에 두고 돌려 줄 수는 없습니다. 기계와 같이 움직여야 하기 때문입니다. 범용 컴퓨터를 통째로 갖다 붙이는 길도 부피와 전력을 그만큼 요구합니다.

그래서 그 일만 하는 계산기를 기계 안에 넣어 버렸습니다. 하는 일을 미리 정해 두면 필요 없는 것을 다 덜어낼 수 있습니다. 기계 안에 박혀 있다는 자리 때문에 이 이름이 붙었습니다.

초기 사례로 꼽히는 것이 아폴로 유도 컴퓨터입니다. 이 컴퓨터는 아폴로 우주선이라는 더 큰 기계 안에 들어갔습니다. 맡은 것은 유도와 항법과 제어에 필요한 계산이었고, 그 결과를 유도 계통과 다른 우주선 계통에 신호로 보냈습니다.

예시

Zephyr 의 스레드 생성 호출

Zephyr 는 자원이 제한된 시스템과 임베디드 시스템에서 쓰도록 설계된 작은 커널을 바탕으로 한 운영체제입니다. 이 커널에서 스레드를 만드는 호출은 이렇게 생겼습니다.

C
k_thread_create(&my_thread_data, my_stack_area,
                K_THREAD_STACK_SIZEOF(my_stack_area),
                my_entry_point, NULL, NULL, NULL,
                my_priority, 0, K_NO_WAIT);

인자 자리에는 NULL 이 세 번 이어서 들어갑니다. 마지막 자리는 K_NO_WAIT 입니다. 이 한 줄 안에 스레드가 쓸 메모리 자리(my_stack_area)와 시작할 함수(my_entry_point)와 우선순위 (my_priority)가 전부 코드에 박혀 있습니다. 프로그램을 새로 짜지 않는 한 이 값들은 실행 중에 안 바뀝니다. 같은 문서는 컴파일 시점에 K_THREAD_DEFINE 을 불러 스레드를 선언하는 방법도 함께 적습니다.

같은 프로젝트의 하드웨어 지원 문서는 주변장치를 갈래로 나눠 이름을 적습니다.

갈래 적힌 이름
통신 CAN(Controller Area Network, 컨트롤러 영역 네트워크) · I2C(Inter-Integrated Circuit, 집적회로 간 통신) 버스 · I3C(Improved Inter-Integrated Circuit, 개선된 집적회로 간 통신) 버스 · SPI(Serial Peripheral Interface, 직렬 주변장치 인터페이스) 버스 · UART(Universal Asynchronous Receiver-Transmitter, 범용 비동기 송수신기)
GPIO 와 입력 GPIO(General-Purpose Input/Output, 범용 입출력)
시스템과 진단 DMA(Direct Memory Access, 직접 메모리 접근)
타이밍 워치독

Cortex-M 의 벡터 테이블

Arm 은 지원하는 컴파일러마다 startup_device 라는 템플릿 파일을 제공합니다. 그 칩을 실제로 만드는 회사(실리콘 벤더)는 이 파일을 가져다 자기 장치에 맞는 인터럽트 벡터를 모두 채워 넣어야 합니다. 아직 안 채운 자리에는 더미 핸들러 — 아무 일도 안 하고 바로 돌아오는 처리 루틴 — 를 가리키는 weak 함수가 대신 들어갑니다. weak 함수는 같은 이름의 함수가 따로 정의되면 그쪽에 자리를 내주는 함수입니다. 실리콘 벤더가 진짜 처리 루틴을 같은 이름으로 새로 적으면 그걸로 바뀝니다.

아래 표는 그 벡터 테이블의 일부입니다. IRQn 은 각 항목에 매겨진 번호이고, 표에 나온 값이 전부 음수인 것은 이 넷이 장치별 인터럽트가 아니라 프로세서 자신에 딸린 예외이기 때문입니다.

예외 벡터 핸들러 함수 IRQn 값
NonMaskableInt_IRQn NMI_Handler -14
HardFault_IRQn HardFault_Handler -13
PendSV_IRQn PendSV_Handler -2
SysTick_IRQn SysTick_Handler -1

이 벡터 테이블은 대개 프로그램 메모리의 맨 앞에 놓입니다. 인터럽트 벡터 재배치를 쓰면 RAM(Random Access Memory, 임의 접근 메모리)으로 옮길 수 있습니다. 시작 코드에서 벡터 테이블의 주소를 가리키는 심볼은 __Vectors 입니다. 그 시작 주소를 담는 레지스터는 SCB->VTOR 입니다. SCB(System Control Block, 시스템 제어 블록)는 프로세서 설정을 모아 둔 레지스터 묶음이고, VTOR(Vector Table Offset Register, 벡터 테이블 오프셋 레지스터)는 그 안에서 벡터 테이블의 시작 주소를 담는 자리입니다.

block-beta
columns 4
  vec["벡터 테이블(__Vectors)"]:1 code["나머지 프로그램 코드"]:3
  ram["RAM(재배치한 벡터 테이블)"]:4

리눅스 디바이스 트리의 보드 노드

디바이스 트리는 보드에 어떤 하드웨어가 어떤 주소에 붙어 있는지를 커널에 알려주는 자료 구조입니다. 하드웨어 하나하나를 노드라는 블록으로 적습니다. 리눅스 커널 문서는 보드 하나를 적은 디바이스 트리 조각을 실물로 보여 줍니다. NVIDIA Tegra Harmony 보드입니다.

dts
chosen {
    bootargs = "console=ttyS0,115200 loglevel=8";
};

chosen 은 부팅 설정을 담는 특별한 노드이고, bootargs 는 그 안에서 커널에 넘기는 부팅 인자입니다. 같은 조각의 다른 노드들에는 compatible 이 되풀이해 나옵니다. compatible 은 그 노드가 어떤 하드웨어와 맞는지 적는 값이고, 커널은 이 값으로 그 장치를 다룰 드라이버를 찾습니다. 문서가 보여 주는 값은 이렇습니다.

compatible 에 적힌 값
"nvidia,harmony", "nvidia,tegra20"
"nvidia,tegra20-soc", "simple-bus"
"nvidia,tegra20-gic"
"nvidia,tegra20-uart"
"nvidia,tegra20-i2s"
"nvidia,tegra20-i2c"

값마다 회사 이름(nvidia)과 칩 이름(tegra20)이 같이 적혀, 같은 회사 제품이어도 어느 세대 칩인지까지 가려냅니다.

같은 조각에는 reg 값 <0x50041000 0x1000> 과 <0x50040100 0x0100> 도 나옵니다. reg 는 그 장치가 메모리에서 차지하는 시작 주소와 크기입니다. 같은 문서는 다른 보드의 예로 compatible = "ti,omap3-beagleboard", "ti,omap3450", "ti,omap3" 도 함께 듭니다 — 보드마다 디바이스 트리를 따로 써야 한다는 뜻입니다.

같은 문서의 다른 대목입니다. 리눅스 커널 문서는 U-Boot 와 kexec 를 비롯한 부트로더들이 디바이스 트리 바이너리를 다음 단계로 넘기는 것과, 부팅 시점에 그 안 내용을 고치는 것을 둘 다 지원한다고 적습니다. 커널에 넘어가는 디바이스 트리 값이 컴파일할 때 정해진 것으로 끝나지 않고, 부트로더 단계에서도 바뀔 수 있다는 뜻입니다.

경계

개인용 컴퓨터에 쓰이는 프로세서로 만든 보드가 있습니다. 그 보드를 큰 장치 안에 넣어 정해진 일만 시키면 임베디드인가. 맞습니다.

Zephyr 공식 소개 문서가 근거를 줍니다. 이 문서는 자기 커널이 자원이 제한된 시스템과 임베디드 시스템에 쓰도록 설계됐다고 밝힙니다. 대상으로 드는 것은 단순한 임베디드 환경 센서와 LED(Light Emitting Diode, 발광 다이오드) 웨어러블입니다. 정교한 임베디드 컨트롤러와 스마트워치와 사물인터넷 무선 애플리케이션까지도 듭니다. 같은 문단이 지원 아키텍처를 이어서 나열합니다. 그 목록에 Arm 과 RISC-V — 이름의 RISC 는 Reduced Instruction Set Computer(축소 명령어 집합 컴퓨터)의 줄임말입니다 — 와 나란히 Intel x86 이 32비트와 64비트로 들어 있습니다.

임베디드 전용으로 설계됐다고 스스로 밝힌 커널이 개인용 컴퓨터 계열 프로세서를 지원 대상에 넣습니다. 부품의 계보가 판정을 정하지 않는다는 뜻입니다. 정하는 것은 그 시스템이 어디에 들어가 무엇을 맡느냐입니다.

관련 항목

도는 바탕

운영체제 · 커널 · 부트로더 · 디바이스 트리

바깥과 잇는 통로

인터럽트 · 벡터 테이블 · GPIO · I2C · I3C · SPI · UART · CAN · DMA

만들어 심는 과정

크로스 컴파일 · 툴체인 · 플래싱 · JTAG(Joint Test Action Group, 합동 테스트 액션 그룹) · 하드웨어 디버깅

쫓기는 제약

실시간성 · 워치독 · 자원 제약

헷갈리는 이웃

사물인터넷 · 사이버물리시스템 · 임베디드 컨트롤러 · 리눅스 커널

다른 이름: embedded · embedded system · 임베디드 시스템 · 내장형 시스템