ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Device Tree란? 하드웨어의 청사진, Device Tree 완벽 해부
    임베디드 개발 환경 이야기 2025. 9. 15. 14:35

    우리는 부트로더가 커널을 깨우는 과정을 살펴보았습니다.
    그런데 여기서 한 가지 의문이 생깁니다.
    잠에서 막 깨어난 리눅스 커널은 자신이 올라가 있는 이 보드에 어떤 하드웨어 장치들이, 어디에, 어떻게 연결되어 있는지 어떻게 알 수 있을까요?

    과거에는 이 모든 하드웨어 정보가 커널 소스 코드 안에 C 언어로 덕지덕지 붙어있었습니다.
    이는 끔찍한 비극을 낳았죠.
    오늘은 이 비극을 해결하기 위해 등장한 영웅, 디바이스 트리(Device Tree)에 대해 깊이 있게 알아보겠습니다.


    Part 1. 디바이스 트리는 왜 태어났을까?

    디바이스 트리가 등장하기 전, ARM 아키텍처의 리눅스 커널은 '보드 파일(Board File)'이라는 C 코드(arch/arm/mach-xxx/board-yyy.c)를 통해 각 보드의 하드웨어 정보를 기술했습니다.

    과거의 비극: "보드 하나당 커널 소스 하나"

    A라는 보드에 I2C 센서가 추가되면, 개발자는 A보드용 board-A.c 파일을 수정하고 커널 전체를 다시 컴파일해야 했습니다.
    A보드와 거의 똑같지만 버튼 위치만 다른 A-1 보드가 나오면, board-A.c를 복사해서 board-A-1.c를 만들었습니다.

    이 방식의 문제점은 명확했습니다.

    1. 커널 소스의 오염: 수천 개의 보드 파일이 커널 소스 코드에 섞여 들어가면서 커널이 비대해지고 지저분해졌습니다.
    2. 유지보수의 어려움: 비슷한 코드가 여기저기 중복되었고, 작은 하드웨어 변경에도 커널 전체를 다시 빌드해야 하는 비효율이 발생했습니다.
    3. 확장성의 한계: 새로운 보드가 나올 때마다 커널에 새로운 보드 파일을 추가해야만 했습니다.

    이러한 문제를 해결하기 위해, "하드웨어에 대한 설명"을 "커널 소스 코드"로부터 분리하자는 아이디어가 나왔고, 이것이 바로 디바이스 트리입니다.


    Part 2. 디바이스 트리란 무엇인가?

    디바이스 트리(Device Tree, DT)는 시스템의 하드웨어 구성을 설명하는 데이터 구조입니다.
    이름처럼 나무(Tree)와 같은 계층 구조로 하드웨어 정보를 표현합니다.

    비유: 건물의 '설계 도면' (Blueprint)

    • 커널: 새로 부임한 '건물 관리인'
    • 하드웨어: 관리해야 할 '건물'
    • 디바이스 트리: 건물의 층별 구조, 각 방의 용도(CPU, 메모리), 전기 배선(I2C, SPI 버스), 연결된 장치(센서, 스크린) 등이 상세히 그려진 '설계 도면'

    유능한 건물 관리인(커널)은 세상의 모든 건물 구조를 외우고 있을 필요가 없습니다. 새로운 건물(보드)에 부임할 때, 그 건물의 설계 도면(디바이스 트리)만 건네받으면 모든 것을 파악하고 관리할 수 있습니다.

    디바이스 트리의 생명주기: DTS → DTC → DTB

    디바이스 트리는 3단계의 생명주기를 가집니다.

    1. .dts (Device Tree Source): 사람이 작성하는 소스 파일

      • 개발자가 텍스트 편집기로 작성하는, 사람이 읽고 이해할 수 있는 하드웨어 설명 파일입니다.
      • .dtsi (Device Tree Source Include): .dts 파일에 포함(#include)되는 파일입니다. 여러 보드에서 공통으로 사용되는 부분(예: SoC 자체의 기능)을 .dtsi 파일로 만들어두면, 각 보드의 .dts 파일에서는 보드 고유의 설정만 기술하면 되므로 재사용성이 높아집니다.
    2. DTC (Device Tree Compiler): 소스를 바이너리로 변환하는 컴파일러

      • dtsdtsi 소스 파일들을 기계가 이해할 수 있는 바이너리 형식으로 변환하는 컴파일러입니다.
    3. .dtb (Device Tree Blob): 커널이 사용하는 바이너리 파일

      • DTC가 만들어낸 최종 결과물입니다. 이진 데이터 덩어리(Blob) 형태이며, 부트로더가 이 파일을 메모리에 올려 커널에 전달합니다.

    소스(.dts) → 컴파일(DTC) → 바이너리(.dtb)

    디바이스 트리 문법(Syntax) 맛보기

    .dts 파일은 간단한 문법을 가집니다. 아래는 가상의 AVN 보드를 위한 .dts 파일의 예시입니다.

    /dts-v1/;
    #include "my-soc.dtsi" // 1. SoC 공통 설정 포함
    
    / { // 2. 루트(Root) 노드
        model = "My Awesome AVN Board";
        compatible = "my-vendor,my-avn-board"; // 보드 정보
    
        memory@40000000 { // 메모리 노드
            device_type = "memory";
            reg = <0x40000000 0x80000000>; // 시작 주소, 크기
        };
    };
    
    &i2c0 { // 3. my-soc.dtsi에 정의된 i2c0 노드를 '참조'하여 내용 추가
        status = "okay"; // i2c0 버스 활성화
    
        touchscreen@38 { // 4. i2c0 버스에 연결된 터치스크린 노드
            compatible = "vendor-x,super-touch"; // 5. 호환성 문자열 (가장 중요!)
            reg = <0x38>; // I2C 장치 주소
            interrupt-parent = <&gpio1>;
            interrupts = <10 2>; // GPIO 1의 10번 핀을 인터럽트로 사용
        };
    };
    
    • #include: SoC 제조사에서 제공한 공통 설정 파일(my-soc.dtsi)을 포함합니다.
    • 루트 노드 /: 트리의 최상위 시작점입니다.
    • &i2c0: 레이블(label) 참조. .dtsi에 이미 정의된 i2c0 노드를 가리켜, 그 내용을 수정하거나 자식 노드를 추가할 때 사용합니다.
    • 노드(Node): 각 하드웨어 장치나 버스를 표현합니다. 노드이름@유닛주소 형식으로 작성합니다.
    • 프로퍼티(Property): 노드의 속성을 key = value 형태로 정의합니다.
      • compatible: 디바이스 트리에서 가장 중요한 프로퍼티! "이 하드웨어는 'vendor-x,super-touch'와 호환된다"고 알려주는 문자열입니다. 커널은 이 문자열을 보고, 자신에게 등록된 드라이버 중 "나는 'vendor-x,super-touch'를 담당하는 드라이버야" 라고 말하는 드라이버를 찾아 연결해줍니다. 이것이 바로 하드웨어와 드라이버를 연결하는 핵심 고리입니다.
      • reg: 레지스터 주소, 메모리 범위 등 주소 정보를 담습니다.
      • interrupts: 장치가 사용하는 인터럽트 정보를 담습니다.
      • status: 장치를 활성화("okay")하거나 비활성화("disabled")할 때 사용합니다.

    Part 3. 커널은 디바이스 트리를 어떻게 사용할까?

    부트로더(U-Boot)가 부팅 시 .dtb 파일을 메모리의 특정 주소에 로드합니다.

    부트로더가 커널을 실행시키면서, 커널에게 메모리에 있는 dtb 파일의 주소를 알려줍니다.

    커널은 초기에 이 dtb 파일을 읽고 파싱하여, 메모리 내에 하드웨어 정보를 담은 트리 구조를 만듭니다.

    이후 커널은 이 트리 구조를 순회하며 각 노드의 compatible 프로퍼티를 확인합니다.

    해당 compatible 값과 일치하는 드라이버를 찾아, 드라이버의 초기화 함수(probe)를 호출합니다.

    이때 디바이스 트리에 있던 reg, interrupts 등의 정보가 드라이버에 전달됩니다.

    드라이버는 전달받은 정보를 바탕으로 실제 하드웨어를 초기화하고 제어하기 시작합니다.

    정리하며: 디바이스 트리는 개발자에게 어떤 의미인가?

    디바이스 트리는 단순한 설정 파일을 넘어, 임베디드 리눅스 개발의 패러다임을 바꿨습니다.
    하드웨어 설정이 '코드'에서 '데이터'로: 이제 I2C 주소를 바꾸거나 새로운 장치를 활성화하기 위해 커널을 다시 컴파일할 필요가 없습니다.
    .dts 파일만 수정하고, 몇 초 만에 .dtb 파일만 다시 컴파일하여 타겟 보드에 넣어주면 됩니다.
    이러한 과정으로 개발 속도가 비약적으로 향상됩니다.

    하나의 커널, 여러 개의 보드: 이제 동일한 커널 바이너리(Image 파일)를 하드웨어 구성이 약간씩 다른 여러 보드에서 사용할 수 있습니다.
    부팅 시 각 보드에 맞는 .dtb 파일만 제공해주면 되기 때문입니다.

    명확한 하드웨어 정보: .dts 파일은 그 자체로 시스템 하드웨어에 대한 훌륭한 문서 역할을 합니다.

    '성능과 안정성' 엔지니어에게 디바이스 트리는 디버깅의 핵심 도구입니다.

    "왜 터치스크린이 동작하지 않지?" 라는 문제가 발생했을 때, 가장 먼저 확인할 곳은 바로 디바이스 트리 파일입니다.
    status가 okay로 되어 있는지, reg 주소는 올바른지, interrupts 설정은 정확한지 등을 확인함으로써 문제의 원인을 찾아갈 수 있습니다.

    또한, 실행 중인 시스템의 /proc/device-tree 디렉토리를 통해 커널이 현재 어떻게 하드웨어를 인식하고 있는지도 직접 확인할 수 있습니다.

    이제 우리는 커널이 자신의 '설계 도면'을 어떻게 읽고 해석하여 거대한 시스템을 지휘하는지 알게 되었습니다.

Designed by Tistory.