Yocto 사용자 정의
개발중 또는 제품화에서 필요한 Yocto 사용자 정의를 정리한다.
SD Card, EMMC의 ROOTFS 크기 조정
imx8mp-evk에서 "imx-image-full"로 이미지를 생성하면 대략 8GB의 rootfs 이미지가 만들어 진다.
이 경우 사용하려는 SD Card나 EMMC의 용량이 8GB 보다 크면, 8GB를 제외한 저장 공간이 사용되지 않는다.
SD Card나 EMMC의 저장 공간을 최대한 사용하려면 아래의 단계를 따른다.
- "imx-image-full"을 사용하여 생성된 이미지를 SD Card나 EMMC에 플래싱(저장)한다.
- 보드를 부팅후 "df -h" 커맨드(크기에 따라 MB나 GB로 출력된다)를 사용하여 저장 공간을 확인한다.
Filesystem의 "/dev/root"의 저장 공간 크기를 확인한다. 이 크기가 실제 rootfs의 크기이다(기본으로 8GB 정도이다). - SD Card나 EMMC의 실제 저장 공간에서 위에서 확인한 크기를 제외한 사용되지 않는 저장 공간을 계산한다.
예로 SD Card나 EMMC가 16GB인 경우 8GB의 사용되지 않는 저장 공간이 있다. - 사용되지 않는 공간보다 적은 크기의 추가 공간의 크기를 결정한다.
추가 공간의 크기는 KB 단위로 설정하게 되어 있다.
여기서는 5GB를 추가한다고 가정한다.
"<build-dir>/conf/local.conf" 파일에 아래 내용을 추가한다(<build-dir>은 빌드 디렉터리이다).
IMAGE_ROOTFS_EXTRA_SPACE = "5242880"
"IMAGE_ROOTFS_EXTRA_SPACE"는 rootfs에 포함할 추가 용량을 나타낸다.
이미지를 다시 빌드하면, 5GB가 추가된 이미지가 생성된다(총 13GB의 rootfs 생성).
1GB는 "1048576"으로 계산된다.
"imx-linux-kirkstone" 이후에 생성된 이미지는 압축되어 있어서 이미지 저장시(플래싱) 시간을 단축할 수 있다.
이전에는 "wic" 확장자를 가진 이미지를 업데이트 했었는데, 이 이미지는 실제 rootfs의 크기와 동일하여 13GB의 이미지 생성시 실제로 파일 크기가 13GB가 생성이 되었다.
요즘은 "zst"로 압축된 이미지가 생성되어 실제 rootfs 크기보다 훨씬 적은 크기의 이미지가 생성이 된다.
21GB의 rootfs를 생성하면 실제 크기는 1.3GB 정도가 나온다.
아마 추가되는 크기는 모두 패팅으로 처리되는 것으로 보여진다.
아래는 "zst"로 생성된 이미지를 "dd" 커맨드를 이용하여 EMMC에 저장하는 커맨드이다.
zstdcat imx-image-full.rootfs.wic.zst | dd of=/dev/mmcblk2 bs=1M status=progress conv=fsync
간단하게 설명하면 "zst"로 압축된 파일을 "zstdcat"로 압축을 해제하여, "dd" 커맨드로 보내서 EMMC로 저장하는 커맨드이다. "wic"로 생성된 이미지를 저장하는 것보다 수배 빠르게 동작한다.
"status=progress"를 추가하면, 작업 진행 상황을 확인할 수 있어서 좋다.
Application 개발 환경 구축 (개발 툴 관련)
Application 개발 환경 구축(개발 툴 관련)과 관련하여 아래 사이트에 정리가 잘 되어 있다.
설명은 i.MX8M-Mini이지만 i.MX8M Plus에도 적용이 가능하다(동일하다고 생각해도 된다).
여기서는 VSCode를 기준으로 설명한다.
VSCode 디버그를 지원하는 rootfs 생성
VSCode 디버그를 지원하는 rootfs를 생성하려면, "<build-dir>/conf/local.conf" 파일에 아래 내용을 추가한다.
EXTRA_IMAGE_FEATURES += " \
tools-debug \
ssh-server-dropbear \
"
"EXTRA_IMAGE_FEATURES"는 이미지에 포함할 추가 기능 목록을 나타낸다(링크에 자세한 내용이 설명되어 있다).
VSCode로 디버깅하려면, 선호하는 SSH 서버(openssh, dropbear 등), gdb 또는 gdbserver가 대상 디바이스에 설치되어 있어야 한다.
- tools-debug : gdb, strace와 같은 디버깅 도구를 설치한다.
- ssh-server-dropbear : Dropbear 최소 기능의 SSH 서버를 설치한다.
rootfs를 생성한 후 대상 디바이스에 이미지를 저장(플래싱)한다.
호스트 PC 환경 설정
대상 호스트 PC에 Ubuntu 20.04가 설치되어 있다고 가정하고 설명한다.
필요한 패키지를 아래와 같이 설치한다(Yocto 이미지를 빌드하는 호스트라면 이미 설치되어 있다고 나올 수 있다).
$ sudo apt-get -y update
$ sudo apt-get -y install build-essential gdb gdb-multiarch git
리눅스용 VScode를 아래와 같이 설치한다.
$ sudo snap install --classic code
VSCode의 확장 기능을 아래와 같이 설치한다.
$ code --install-extension ms-vscode.cpptools
커맨드 실생 후 C/C++과 관련된 확장 기능이 설치된다.
Yocto Toolchain(SDK)을 설치한다(SDK 설치). SDK는 "Yocto Command 정리"의 "SDK 빌드"를 참고하여 빌드 후 생성된 SDK를 설치하면 된다(설치 경로를 꼭 확인한다).
VSCode에서 프로젝트 생성, 컴파일 및 실행
여기서는 OpevCV를 이용하여 카메라 영상을 캡쳐하여 디스플레이하는 단순한 Application을 만들면서 설명한다.
호스트 PC에서 아래와 같이 프로젝트 디렉터리를 생성하고, VSCode를 실행한다.
$ mkdir ~/opencv-test
$ cd ~/opencv-test
$ code .
"opencv-test" 디렉터리를 생성하고 "opencv-test" 디렉터리로 이동 후, VSCode를 실행한다.
아래와 같은 화면이 나온다(Ubuntu를 WSL2 상에서 실행하기 때문에 WSL이라는 문구를 볼 수 있다).
"opencv-test" 디렉터리에 아래 테이블과 같이 파일과 디렉터리를 생성한다.
파일 및 경로 | 설명 |
---|---|
main.cpp | 샘플 프로그램 opencv-test.bin의 소스 코드. |
Makefile | main.cpp를 opencv-test.bin으로 크로스 컴파일하기 위한 Makefile. |
.vscode/settings.json | SDK/toolchain에 대한 전역 환경 변수를 구성하는 VSCode 파일. |
.vscode/tasks.json | 새로운 작업을 재정의하거나 추가하는 VSCode 파일. VSCode 빌드 커맨드가 실행될 때 Makefile이 실행된다. |
.vscode/c_cpp_properties.json | IntelliSense의 포함 경로를 구성하는 VSCode 파일. |
파일이나 디렉터리를 생성하려면, VSCode의 "EXPLORER"에서 마우스 오른쪽을 클릭하고 "New File...", "New Folder..."를 선택한다.
main.cpp의 코드는 아래와 같다.
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(){
// Create a VideoCapture object
VideoCapture cap(2);
// Check if camera opened successfully
if(!cap.isOpened()){
cout << "Error opening video stream" << endl;
return -1;
}
while(1) {
Mat frame;
// Capture frame-by-frame
cap >> frame;
// If the frame is empty, break immediately
if (frame.empty())
break;
// Display the resulting frame
imshow( "Frame", frame );
// Press ESC on keyboard to exit
char c = (char)waitKey(1);
if( c == 27 )
break;
}
// When everything done, release the video capture
cap.release();
// Closes all the frames
destroyAllWindows();
return 0;
}
간단하게 소스 코드를 설명하면, 캡처 디바이스로 "/dev/video2"를 열고 프레임을 캡처하여 화면에 이미지를 보여주는 간단한 코드이다. 사용하는 환경에 맞게 캡처 디바이스를 수정해서 사용하면 된다.
main.cpp를 크로스 컴파일하기 위한 Makefile의 내용은 아래와 같다.
INC_DIRS = -I$(SDKTARGETSYSROOT)/usr/include/opencv4
LIB_DIRS = -L$(SDKTARGETSYSROOT)/usr/lib
LIBS = -lopencv_core
LIBS += -lopencv_videoio
LIBS += -lopencv_highgui
all: main.cpp
$(CXX) $(CXXFLAGS) $(INC_DIRS) $(LIB_DIRS) $(LIBS) main.cpp -g -o opencv-test.bin
clean:
rm -f opencv-test.bin
"INC_DIRS"는 추가할 헤더 파일이 존재하는 디렉터리를 설정한다.
"LIB_DIRS"는 추가할 라이브러리 파일이 존재하는 디렉터리를 설정한다.
"LIBS"는 추가할 라이브러리 파일을 설정한다.
".vscode/settings.json"의 내용은 아래와 같다.
{
"OPENCV_APP": {
/* 대상 디바이스의 IP 주소 */
"TARGET_IP":"192.168.0.16",
/* 프로젝트 설정 */
"PROGRAM":"opencv-test.bin",
/* Yocto SDK Configuration */
"ARCH":"aarch64-poky-linux",
"OECORE_NATIVE_SYSROOT":"/opt/fsl-imx-wayland/6.1-langdale/sysroots/x86_64-pokysdk-linux",
"SDKTARGETSYSROOT": "/opt/fsl-imx-wayland/6.1-langdale/sysroots/armv8a-poky-linux",
/* Yocto SDK Constants */
"CC_PREFIX": "${config:OPENCV_APP.OECORE_NATIVE_SYSROOT}/usr/bin/${config:OPENCV_APP.ARCH}/${config:OPENCV_APP.ARCH}-",
"CXX": "${config:OPENCV_APP.CC_PREFIX}g++ --sysroot=${config:OPENCV_APP.SDKTARGETSYSROOT}",
"CC": "${config:OPENCV_APP.CC_PREFIX}gcc --sysroot=${config:OPENCV_APP.SDKTARGETSYSROOT}",
},
"files.associations": {
"iostream": "cpp",
"ostream": "cpp"
}
}
".vscode/settings.json"에는 대상 디바이스에 대한 정보, Yocto SDK의 toolchain 정보, 기본적인 컴파일 전역 환경 변수를 설정한다.
다른 json 파일에서 "${config:OPENCV_APP.TARGET_IP}"와 같은 구문으로 접근할 수 있다.
전역 변수 | 설명 |
---|---|
TARGET_IP | imx8mp-evk 대상 디바이스의 IP 주소를 설정한다. |
PROGRAM | Makefile에서 생성하는 바이너리 이름과 일치해야 한다. |
ARCH | gcc, g++, gdb 등에서 사용하는 아키텍처 prefix를 설정한다. |
OECORE_NATIVE_SYSROOT | Yocto SDK가 설치된 경로의 environment-setup-armv8a-poky-linux에 의해 설정되는 OECORE_NATIVE_SYSROOT 환경 변수와 동일하게 설정한다. |
SDKTARGETSYSROOT | Yocto SDK가 설치된 경로의 environment-setup-armv8a-poky-linux에 의해 설정되는 SDKTARGETROOT 환경 변수와 동일하게 설정한다. |
CC_PREFIX | toolchain gcc, g++ 등 바이너리에 대한 전체 경로를 설정한다. |
CXX | 크로스 컴파일러 g++ 바이너리와 sysroot의 경로를 설정한다. |
CC | 크로스 컴파일러 gcc 바이너리와 sysroot의 경로를 설정한다. |
".vscode/tasks.json"은 세 개의 root 개체가 있다.
- options : Makefile에서 사용하는 CC, CXX 환경 변수를 정의한다.
- presentation : 모든 task에 대한 VSCode의 통합된 터미널의 동작을 구성한다.
- tasks : task 구성의 나열이다. 아래 내용은 VSCode의 "Run Build Task..."가 실행될 때 Makefile을 실행하는 단일 빌드 작업을 생성한다.
{
"version": "2.0.0",
/* Configure Yocto SDK Constants from settings.json */
"options": {
"env": {
"CXX": "${config:OPENCV_APP.CXX}", /* Used by Makefile */
"CC": "${config:OPENCV_APP.CC}", /* Used by Makefile */
}
},
/* Configure integrated VS Code Terminal */
"presentation": {
"echo": false,
"reveal": "always",
"focus": true,
"panel": "dedicated",
"showReuseMessage": true,
},
"tasks": [
/* Configure Build Task */
{
"label": "build",
"type": "shell",
"command": "make clean; make -j$(nproc)",
"problemMatcher": ["$gcc"]
},
]
}
".vscode/c_cpp_properties.json"은 IntelliSense에 대한 포함 경로를 구성한다.
내용은 아래와 같다.
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"${config:OPENCV_APP.SDKTARGETSYSROOT}/usr/include/**"
]
}
],
"version": 4
}
프로젝트를 빌드하기 전에 Yocto SDK가 설치된 경로의 "environment-setup-armv8a-poky-linux"를 실행해야 한다.
아래와 같이 실행한다. 경로는 설치 위치에 따라 달라진다.
$ source /opt/fsl-imx-wayland/6.1-langdale/environment-setup-armv8a-poky-linux
$ export LDFLAGS=
VSCode에서 프로젝트를 빌드하려면, "Terminal->Run Build Task..."를 실행하거나 "Ctrl + Shift + B" 단축키를 사용한다.
생성된 바이너리를 대상 디바이스에 배포하기 위해, "Terminal->New Terminal"을 실행하거나 "Ctrl + Shift + `" 단축키를 사용한다.
터미널이 실행되면 아래와 같이 scp 커맨드로 생성된 바이너리를 대상 디바이스에 배포한다.
$ scp opencv-test.bin root@192.168.0.16:/home/root
IP 주소는 ".vscode/settings.json"의 TARGET_IP와 동일하다.
대상 디바이스의 터미널에서 배포한 바이너리를 실행한다.
# ./opencv-test.bin
VSCode를 사용한 원격 디버깅
VSCode에서 디버깅을 활성화하기 위해서 ".vscode/tasks.json" 파일의 수정과 "deploy_gdb.sh", ".vscode/launch.json" 파일들이 추가적으로 필요하다.
파일 및 경로 | 설명 |
---|---|
deploy-gdb.sh | gdbserver를 배포하고 실행하는 일반 스크립트 |
.vscode/launch.json | 디버그 구성을 설정하는 VSCode 파일. tasks.json에서 deploy-gdb를 실행. |
.vscode/tasks.json | 디버깅하기 전에 deploy-gdb.sh를 호출하도록 편집. |
deploy-gdb.sh 파일 생성
먼저 프로그램 배포를 위한 일반 스크립트를 추가하고 gdbserver를 실행한다. 스크립트에는 두 가지 인수가 필요하다.
- TARGET_IP : 대상 디바이스의 IP 주소
- PROGRAM : 바이너리 실행 파일의 이름(여기서는 opencv-test.bin)
결국 tasks.json은 각 디버그 세션이 시작될 때 이 스크립트를 실행한다.
아래는 "deploy-gdb.sh"의 내용이다.
#!/bin/bash
readonly TARGET_IP="$1"
readonly PROGRAM="$2"
readonly TARGET_DIR="/home/root"
# Must match startsPattern in tasks.json
echo "Deploying to target"
# kill gdbserver on target and delete old binary
ssh root@${TARGET_IP} "sh -c '/usr/bin/killall -q gdbserver; rm -rf ${TARGET_DIR}/${PROGRAM} exit 0'"
# send the program to the target
scp ${PROGRAM} root@${TARGET_IP}:${TARGET_DIR}
# Must match endsPattern in tasks.json
echo "Starting GDB Server on Target"
# start gdbserver on target
ssh -t root@${TARGET_IP} "sh -c 'cd ${TARGET_DIR}; gdbserver localhost:3000 ${PROGRAM}'"
.vscode/launch.json 파일 생성
VSCode에는 다양한 프로그램 언어를 디버깅할 수 있는 강력한 디버거가 내장되어 있다. 여기서는 C/C++ Extension을 사용하여 디버깅을 구성을 추가한다.
아래는 ".vscode/launch.json"의 내용이다.
{
"version": "0.2.0",
"configurations": [
{
"name": "GDB debug",
"type": "cppdbg",
"request": "launch",
"program": "${config:OPENCV_APP.PROGRAM}",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"console": "integratedTerminal",
"MIMode": "gdb",
"targetArchitecture": "arm64",
"preLaunchTask": "deploy-gdb",
"setupCommands": [{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}],
"miDebuggerPath": "/usr/bin/gdb-multiarch",
"miDebuggerServerAddress": "${config:OPENCV_APP.TARGET_IP}:3000",
}]
}
주요 변수에 대한 세부 정보는 아래 테이블을 참조한다.
변수 | 설명 |
---|---|
miDebuggerServerAddress | 대상 디바이스의 IP 주소와 일치해야 한다. |
program | Makefile에서 생성한 바이너리의 이름과 일치해야 한다. |
preLaunchTask | .vscode/tasks.json의 다음 단계의 task 이름과 일치해야 한다. |
.vscode/tasks.json 파일 편집
".vscode/launch.json"에서는 각 디버깅 세션의 시작부분에서 deploy-gdb 이름의 task를 실행하기 위해 preLaunchTask를 만들었다. 이 task는 ".vscode/tasks.json"에서 새로운 task인 deploy-gdb 추가하여 구성된다.
아래는 최종 ".vscode/tasks.json" 파일의 내용이다.
{
"version": "2.0.0",
/* Configure Yocto SDK Constants from settings.json */
"options": {
"env": {
"CXX": "${config:OPENCV_APP.CXX}", /* Used by Makefile */
"CC": "${config:OPENCV_APP.CC}", /* Used by Makefile */
}
},
/* Configure integrated VS Code Terminal */
"presentation": {
"echo": false,
"reveal": "always",
"focus": true,
"panel": "dedicated",
"showReuseMessage": true,
},
"tasks": [
/* Configure launch.json (debug) preLaunchTask Task */
{
"label": "deploy-gdb",
"isBackground": true,
"problemMatcher":{
"base": "$gcc",
"background": {
"activeOnStart": true,
"beginsPattern": "Deploying to target",
"endsPattern": "Starting GDB Server on Target"
}
},
"type": "shell",
"command": "sh",
"args": [
"deploy-gdb.sh",
"${config:OPENCV_APP.TARGET_IP}",
"${config:OPENCV_APP.PROGRAM}"
],
"dependsOn": ["build"],
},
/* Configure Build Task */
{
"label": "build",
"type": "shell",
"command": "make clean; make -j$(nproc)",
"problemMatcher": ["$gcc"]
},
]
}
아래 테이블은 deploy-gdb 구성을 설명한다.
변수 | 설명 |
---|---|
label | .vscode/launch.json의 preLaunchTask와 일치해야 한다. |
type | 이 task는 shell을 실행한다. |
command | "deploy-gdb.sh 192.168.0.16 opencv-test.bin" 인수로 sh를 실행한다. |
디버깅 세션 실행
위의 단계를 수행하면 아래와 같은 파일들이 있어야 한다.
"Run->Start Debugging"을 선택하거나 "F5"를 눌러서 새로운 디버깅 세션을 시작할 수 있다.
VSCode는 디버그 모드로 전환하고 대상 디바이스에서 gdbserver를 실행하는 gnome-terminal을 실행한다.
사용자 정의 디바이스 트리 적용
기본으로 제공되는 디바이스 트리를 사용하지 않고, 사용자 정의된 디바이스 트리를 사용하는 경우 아래의 절차를 따른다.
imx8mp_evk를 기준으로 설명한다.
- 기본으로 제공되는 디바이스 트리를 참고하여, 사용자 정의 디바이스 트리를 작성한다.
Yocto Project의 "<build-dir>/<work-shared>/<machine>/kernel-source/arch/arm64/boot/dts/freescale/"에서 기본으로 제공되는 디바이스 트리 파일을 찾을 수 있다.
사용자 정의 디바이스 트리 파일은 작성 후 위와 동일한 디렉터리에 있어야 한다. - 같은 디렉터리에 있는 Makefile에 사용자 정의 디바이스 트리 파일을 추가한다.
dtb-$(CONFIG_ARCH_MXC) += <사용자 정의 디바이스 트리 파일 이름>.dtb - "<meta-레이어>/conf/machine/<machine>.conf" 파일에 사용자 정의 디바이스 트리 파일을 추가한다.
"KERNEL_DEVICETREE += "<사용자 정의 디바이스 트리 파일 이름>.dtb""
예로 imx8mp을 사용하는 경우, "source/meta-imx/meta-bsp/conf/machine/imx8_all.conf"에 추가한다. - u-boot에서 defconfig 파일을 수정한다.
Yocto Project의 "<build-dir>/<work>/<machine>-poky-linux/u-boot-imx/<version>-r0/git/configs/imx8mp_evk_defconfig" 파일에서 아래 항목을 수정한다.
CONFIG_DEFAULT_DEVICE_TREE="<사용자 정의 디바이스 트리>"
CONDIF_DEFAULT_FDT_FILE="<사용자 정의 디바이스 트리>.dtb"
위의 절차대로 수정 후 다시 이미지를 빌드한다. u-boot에서 defconfig를 수정하지 않으면, 기본 디바이스 트리 파일을 로드하므로 꼭 수정을 해야 한다.
'NXP i.MX SoC Family > Evaluation Kit for the i.MX 8M Plus' 카테고리의 다른 글
i.MX 8M Plus 개발 환경 구축 - Yocto Command 정리 (0) | 2023.05.03 |
---|---|
i.MX 8M Plus 개발 환경 구축 - U-Boot 사용자 지정 (0) | 2023.05.02 |
i.MX 8M Plus 개발 환경 구축 - 커널 사용자 지정 (0) | 2023.04.24 |
i.MX 8M Plus 개발 환경 구축 - U-Boot 빌드 (0) | 2023.04.07 |
i.MX 8M Plus 개발 환경 구축 - 커널 빌드 (0) | 2022.11.01 |