아무것도 몰라요

리눅스

Cross Development Environment(교차 개발 환경)

telomere37 2021. 3. 12. 17:57

Key Words

- Cross-Compiler
- TFTP

1. Cross Development Environment

 

  백준 문제를 풀다 보면 제대로 코드를 작성했는지 돌려보는 환경이 필요하다. C/C++를 사용하였다면 visual studio, xCode 등일 수 있고 Java라면 Eclipse, Intellij가 될 수 있다. 이러한 프로그램들을 'IDE'라고 한다. IDE, Integrated Development Environment, 는 source code editor 뿐만 아니라 debugger, automation tool 등 코딩하기 더 좋은 환경을 제공하는 소프트웨어를 말한다. 이와는 약간 다른 단순히 코드 편집 기능만을 제공하는 visual stuido code, notepad도 있다. 백준 문제의 경우 debugger를 사용 안 하고 푸는 문제들이 많아 편집기만으로도 충분할 것 같아 보인다. 하지만 파일 하나가 아닌 수많은 헤더와 파일이 들어가는 kernel수준의 코드라면 이야기가 다를 것이다. 하물며 가장 단순한 코드 편집 기능도 없는 단순한 문서 편집기만 있다면 더더욱 막막할 것이다.

  임베디드 시스템은 우리가 흔히 사용하는 컴퓨터와 환경이 매우 다르다. 제한된 전력, 제한된 메모리, 제한된 크기 안에 압축해서 목적의 수행을 위해 정교하게 설계되야 된다. 따라서 임베디드 시스템 내부에 컴파일러와 디버거를 넣는 것은 '사치'를 부리는 것과 같다. 따라서 "Cross-Compiler"를 사용하게 되었는데, 이는

 

Compiler 중에서 생성해내는 기계어 코드가, Compiler가 돌아가는 기계에서 돌아가는 것이 아닌 다른 기계에서 돌아갈 수 있는 것을 뜻한다.

 

  같은 단어가 여러번 등장해 어려워 보이지만, 쉽게 말해서 다른 기계에서 돌아가는 코드를 생성해주는 것이다. 컴퓨터는 다 같은 기계 아닌가? 왜 서로 다른 언어가 필요한 것일까? 이는 시간을 거슬러 올라가 RISC와 CISC에 대한 개념을 알아야 이해할 수 있다. RISC(Reduced-Instruction-Set-Architecture)는 말 그대로 Insturction의 수가 적고 더 복잡한 Instruction을 기본 Instruction으로 수행하는 CPU이며 CISC(Complex-Instruction-Set-Architecture)는 명령의 길이가 가변적이며 복잡하다. (둘의 차이, 역사, 그리고 현재 양상까지에 대한 내용은 다음에 컴퓨터 구조와 논리 수업을 들으면서 자세히 알아봐야겠다.) 여기서 요점은 둘이 서로 다른 Instruction을 사용한다는 것이다. 이번에 Apple에서 개발한 M1칩에 대해 들어봤을 것이다. 기존에 휴대폰에 들어가던 ARM architecture를 접목시켜 만들었다고. ARM은 advanced RISC machine의 약자로 RISC구조를 기반으로 한다. 이에 따라서 호환성 문제, 로제타 2의 기능 등 다양한 이야기가 나오고 있다. 이야기가 조금 샜지만, 정리하자면

 

CPU마다 사용할 수 있는 Instruction Set이 다르다.

 

이라고 쉽게 말할 수 있다. 따라서 임베디드 시스템에 들어가는 CPU가 알아들을 수 있게 다른 compiler를 사용해서 실행 파일을 생성하는 것이다. LG gram을 사용해서 intel CPU에 돌아가는 코드를 생성해봤자 M1칩을 가지고 있는 맥북에서 (호환을 지원해주거나 도와주는 프로그램이 없다면) 실행이 안되는 것이다. (실제로 실행하면 어떻게 되는지 궁금하다, 오류 메시지의 내용이라던가...) 따라서 우리는 cross-compiler가 필요하다. 

  이런 근본적인 이유뿐만 아니라 컴퓨터로 코드를 작성함으로서 처음에 언급한 IDE를 사용하여 훨씬 시간과 노력을 단축시킬 수 있다. 그렇다면 이제는 컴퓨터에서 작성한 파일, kernel, 등등을 어떻게 임베디드 보드로 옮길 수 있는지 알아보자.

 

2. File Transfer 

 

아직 완전하게 배우지 못했지만 아직 배운 것까지만 정리하면 다음과 같다. 

 

이제 왜 굳이 파일을 Host(컴퓨터)에서 Target Board(임베디드 시스템)으로 옮겨야 되는 이유는 알았다. 그렇다면 어떻게 옮길 것인가? 아마 Target Board에 따라서, 보내려는 파일에 따라서 등등 다양한 요인이 있겠지만 이번에 실습으로 사용하게 될 "Achro-i.MX6Q"의 경우 크게 2가지 방법이 있다. 하나, USB 포트를 사용한다. 둘, Ethernet(TFTP)을 사용한다. 둘의 가장 큰 차이는 USB 포트로 가져오는 경우 내장 "Flash Memory"에 바로 저장하여 power가 꺼져도 저장된다. 하지만 TFTP로 가져온 파일은 RAM에만 저장이 되고 Flash Memory에 따로 저장하지 않는 이상 power가 나가면 사라진다. 이를 방지하기 위해 Flash Memory에 저장하는 것을 Fusing이라고 한다. 정석적인 방법은 TFTP로 파일을 받아와 실행시켜보고, 정상적인 작동을 확인 후 Fusing을 한다. Flash memory를 포함한 대부분의 memory는 영구적인 것이 아닌 수명이 존재한다. (이를 연장하기 위해 Wear leveling과 같은 기술들이 사용되곤 한다) 따라서 USB 포트를 사용하여 계속 fusing을 하게 되면 기계의 수명을 단축할 수 있다. 하지만 이는 번거로운 과정을 거친다는 이유로 이번 실습에서는 아마 하지 않을 것 같다. 

 

3. Boot Loader

 

좀 뜬금 없지만 수업의 내용을 기준으로 끊어서 Boot Loader까지 짚고 넘어가고 싶다. 방학 때 Linux kernel build를 하기 위해 끙끙거리던 중 들었던 익숙한 단어 중 하나다. 컴퓨터 부팅의 흐름을 살펴보면 ROM이나 Flash에 저장되어 있는 BIOS(Basic Input/Output System)이 실행이 되고 이는 Boot loader를 실행시키게 된다. 이때 Hardware initialization(POST와 같은)이 이루어지고 커널 이미지를 찾기 위해 SDD, CDROM, USB 등의 저장장치로 간다. 이때 MBR(Master Boot Record)를 실행하게 되고 원하는 커널의 이미지를 선택한 다음 DRAM에 올리는 것이다. Linux를 사용할 때 가장 흔하게 사용되는 Boot Loader는 "LILO(LInux LOader)"와 "GRUB(GRand Unified Bootloader)"가 있으며 이들은 흔히 x86 system에서 사용된다. (ARM도 호환이 되는 것 같다) 이러한 Boot 흐름은 우리의 컴퓨터 같은 ROM도 있고 SSD나 HDD도 있고, partition도 여러 개 있고, 제법 복잡한 환경에서의 흐름이다. 그렇다면 우리가 공부하는 Target Board에서의 흐름은 어떨까? U-boot(Universal Boot Loader)라는 boot loader를 사용하며 ROM BIOS가 아닌 Flash ROM에서 읽어와서 실행한다. 이에 대한 내용은 다음에 이어서 한다.