2026년 3월 19일 목요일

Embedded Linux Programming with STM32MP257F-DK board(Episode V)

거의 6년만에 STM32MP 보드를 다시 하나 입수했다(이번에는 STM32MP257F-DK 보드다). 이번 시간 부터는 앞으로 몇차례에 걸쳐서, STM32MP257F-DK 보드 기반 embedded linux와 관련한 다양한 주제를 다뤄 보고자 한다. 이번이 그 다섯번째 시간이다. 😎

 
The 5th episode

목차
9. OpenAMP와 Cortex-M33 CPU 기반 Firmware 돌려 보기
10. Application 돌려 보기 - WireGuard Rust, Qt6 Application, ROS2
References

KeywordsSTM32MP257F-DK, booting flow, u-boot, linux kernel, device tree, yocto project, Buildroot, Rust kernel module, STM32CubeIDE, Zephyr RTOS, PySide6

Embedded linux(echosystem)는 점점 더 복잡해져 가고 있다. 이중 삼중의 bootloader(거기에 TrustZone까지), device tree로 무장한 linux kernel, kernel에 합류한 Rust, build system의 대세 Yocto project ! 어느 하나 무시할 수 없는 것들 뿐이다. 이번 시간에는 Coprocessor인 Cortex-M33에 Zephyr RTOS를 올린 후, Main processor(Cortex-A35) 상의 Linux와 통신하는 내용을 다뤄 보고자 한다. 😋

Alpine skiing~
(마치 두개의 이기종 CPU가 앞 다투어 booting하는 형국이랄까~ ㅋㅋ)

9. OpenAMP와 Cortex-M33 CPU 기반 Firmware 돌려 보기
이번 장에서는 Cortex-M33 CPU에서 동작하는 firmware에 관한 이야기를 해 보고자 한다. 이미 앞 장에서 여러 차례 언급한 바와 같이, stm32mp257f SoC(정확하게는 STM32MP257FAK3)는 Arm Cortex-A35 CPU core와 Cortex-M33 CPU core가 공존하는 구조이다. Arm Cortex-A35 CPU core에는 Linux kernel이 돌고 있으며, Cortex-M33 CPU core에는 RTOS or No OS(Bare Metal) 형태의 firmware를 올릴 수가 있다.

[그림 9.1] stm32mp257f block diagram [출처 - 참고문헌 10]

Cortex-M33 CPU 관련 주요 스펙을 살펴 보면 다음과 같다.

[그림 9.2] stm32mp257f Cortex-M33 스펙 [출처 - 참고문헌 13]

내용을 보니, 아쉽게도 board 상에 표출된 주변 장치(peripheral)가 GPIO input button 1개와 LED 1개 뿐인 듯하다. 😂

9.1 OpenAMP 기반 이기종 CPU core 간의 통신
STM32MP257F-DK 보드에서 메인 프로세서인 Cortex-A35(Linux 환경)와 코프로세서인 Cortex-M33(Zephyr RTOS 또는 STM32Cube 기반 펌웨어) 간의 제어 및 데이터 통신은 주로 OpenAMP (Open Asymmetric Multi-Processing) 표준 아키텍처를 기반으로 구현된다.
[그림 9.3] STM32MP257F SoC과 OpenAMP

OpenAMP는 아래와 같이 크게 3가지 요소로 이루어져 있다. 💢

구성 요소 1. 펌웨어 로드 및 생명주기 관리를 담당하는 Linux Remoteproc Framework

리눅스 커널은 remoteproc (Remote Processor) 서브시스템을 사용하여 Cortex-M33 코어의 생명주기(부팅, 펌웨어 로드, 중지, 상태 모니터링 등)를 총괄적으로 통제한다.

  • 디바이스 트리 바인딩: 리눅스 커널의 디바이스 트리에는 m33_rproc 노드(compatible = "st,stm32mp2-m33")가 정의되어 있다. 이 노드에는 M33 코어가 펌웨어를 실행할 전용 메모리 영역(RETRAM, SRAM 등)과 통신을 위한 공유 메모리 주소 공간이 명시적으로 매핑된다.
  • 사용자 공간 제어: 리눅스의 sysfs 가상 파일 시스템 경로인 /sys/class/remoteproc/remoteprocX/state에 애플리케이션이나 셸 스크립트가 start 또는 stop 명령을 기록하는 방식으로 M33 펌웨어의 실행을 런타임에 동적으로 시작하고 제어할 수 있다.

[그림 9.4] OpenAMP Architecture(1) [출처 - 참고문헌 21]

구성 요소 2. 데이터 통신 채널 확립에 필요한 RPMsg와 VirtIO(가상화 큐)

두 코어 간의 실제 데이터 송수신은 RPMsg (Remote Processor Messaging) 프로토콜과 가상화 큐인 VirtIO 기술을 통해 이루어진다.

  • 리소스 테이블(Resource Table): Zephyr RTOS나 STM32 펌웨어 바이너리(.elf)를 컴파일할 때, 내부에는 반드시 .resource_table이라는 특수한 구조체 섹션이 포함되어야 한다.
  • 공유 메모리 동기화: 리눅스의 remoteproc 드라이버가 M33 펌웨어를 메모리에 적재할 때 이 리소스 테이블을 파싱한다. 이를 통해 두 코어가 메시지 큐를 주고받을 공유 메모리(VirtIO Ring)의 물리적 위치와 통신 엔드포인트(Endpoint) 설정 정보를 상호 동기화하게 된다.
[그림 9.5] RPMsg Virtio Flow - from master to remote [출처 - 참고문헌 20]

[그림 9.6] RPMsg Virtio Flow - from remote to master [출처 - 참고문헌 20]

[그림 9.7] OpenAMP의 RPMsg header 정의 [출처 - 참고문헌 18]

구성 요소 3. 하드웨어 동기화 및 시그널링을 담당하는 IPCC와 Mailbox

실제 데이터 페이로드 자체는 시스템의 공유 메모리 공간에 저장되지만, 상대 코어에 "새로운 데이터가 도착했다"는 사실을 지연 없이 즉각적으로 알리기 위해 IPCC (Inter-Processor Communication Controller)라는 STM32 전용 하드웨어 방식을 사용한다.

  • 메일박스(Mailbox) 컨트롤러: IPCC 하드웨어는 여러 채널(예: STM32MP257F의 경우 IPCC1에 16개 채널 제공)을 갖춘 메일박스 역할을 수행하며, 리눅스와 M33 코어 사이에 논블로킹(non-blocking) 방식으로 하드웨어 인터럽트를 안전하게 발생시킨다.
  • 드라이버 계층: 리눅스 커널 내에서는 drivers/mailbox/stm32-ipcc.c 드라이버가 IPCC 제어 레지스터를 직접 조작하여 상대 코어에 시그널을 보내고 데이터를 인출(Retrieve)할 수 있도록 지원한다.
[그림 9.8] OpenAMP Architecture(2) [출처 - 참고문헌 19]

Cortex-M33에 올라간 Zephyr RTOS 내부에서 애플리케이션이 rpmsg_send()와 같은 API를 호출하여 데이터를 전송하면, 데이터는 지정된 공유 메모리 버퍼(VirtIO)에 써진다(write). 직후 M33 코어는 하드웨어 IPCC 레지스터를 건드려 리눅스(A35) 측으로 인터럽트를 발생시킨다.

리눅스 커널의 Mailbox/IPCC 드라이버는 이 인터럽트를 수신하고 상위의 RPMsg 버스 드라이버로 메시지를 올려보내며, 최종적으로 사용자 공간에 /dev/ttyrpmsgX와 같은 가상 TTY 문자 장치 노드를 생성한다. 리눅스 유저 애플리케이션은 이 파일 노드에 단순한 읽기/쓰기 작업을 수행함으로써 복잡한 내부 과정을 몰라도 M33 코어와 원활하게 양방향 통신을 수행할 수 있게 된다.
아래 그림은 지금까지 설명한 OpenAMP의 개념을 STM32MP 기준으로 정리한 것인데, 매우 복잡한 느낌이다. 😓
[그림 9.9] STM32MP Linux remoteproc framework [출처 - 참고문헌 11]

마지막으로, OpenAMP library가 통합된 Linux kernel image와 RTOS or No OS(Bare Metal) image를 만드는 대략적인 과정을 정리해 보면 다음과 같다.

[그림 9.10] OpenAMP가 통합된 linux firmware image 생성 과정 [출처 - 참고문헌 18]


[그림 9.11] OpenAMP가 통합된 RTOS/No OS firmware image 생성 과정 [출처 - 참고문헌18]

9.2 Zephyr RTOS 환경 설정하기
이번 절에서는 Cortex-M33에 Zephyr RTOS를 올리기 위한 준비 과정을 소개해 보고자 한다. 사실 Zephyr RTOS와 관련해서는 이전 시간에 이미 여러 차례 소개한 바가 있다.


그때도 그랬지만, 이제는 정말로 대세 중의 대세가 된 느낌이다. 😍


[그림 9.12] 높게 날아 오른 Zephyr RTOS [출처 - 참고문헌 17]


최신 Zephyr RTOS(4.3 version)는 Ubuntu 24.04 LTS를 기준으로 하고 있다. 따라서 Ubuntu 22.04 LTS에 환경 구성 시, 아래와 같은 문제가 발생한다.

<Python3 버젼 관련 문제>
1) Ubuntu 22.04 LTS는 GNOME에서 Python 3.10.x를 기본으로 사용한다.
2) Zephyr RTOS latest version은 Python 3.12.x를 기본으로 사용한다.
  -> 만일, Zephyr를 위해서 Python 3.12 버젼 설치 후, 시스템을 재부팅하게 되면, GNOME terminal 등이 뜨지 않는 문제(GNOME에 이런 저런 문제 발생)가 발생하게 된다.
------------------------------------------------------------------

어쨌든 Zephyr RTOS latest version을 위해서는 pyhon 3.12는 반드시 필요한 것이니, 우선 먼저 아래와 같이 설치하도록 한다.

<Ubuntu 22.04 LTS에 python3.12 version 설치>
$ sudo apt update && sudo apt upgrade -y $ sudo apt install software-properties-common -y $ sudo add-apt-repository ppa:deadsnakes/ppa
$ sudo apt update $ sudo apt install python3.12
-> 설치 후, 기존 버젼(3.10.x)을 그대로 유지하는게 중요하다.

이후, (아래 내용을 참조하여) Zephyr RTOS를 설치하도록 하자.

<최초 Zephyr RTOS 4.3 설치 방법>
chyi@earth:~$ mkdir zephyrproject2
chyi@earth:~$ python3.12 -m venv ~/zephyrproject2/.venv

chyi@earth:~$ source ~/zephyrproject2/.venv/bin/activate
(.venv) chyi@earth:~$ pip install west

(.venv) chyi@earth:~$ west init ~/zephyrproject2

(.venv) chyi@earth:~$ cd ~/zephyrproject2
(.venv) chyi@earth:~/zephyrproject2$ west update
...
(.venv) chyi@earth:~/zephyrproject2$ west zephyr-export

(.venv) chyi@earth:~/zephyrproject2$ west packages pip --install
...
((.venv) ) chyi@earth:~/zephyrproject2$ cd zephyr/

((.venv) ) chyi@earth:~/zephyrproject2/zephyr$ west sdk install
 -> 여기에서 아래와 같은 errorr가 발생하는데, 이를 해결해 보자.
Found '/home/chyi/zephyrproject2/zephyr/SDK_VERSION', installing version 0.17.4.
Fetching Zephyr SDK list...
ERROR: CMake Error at /usr/share/cmake-3.22/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
  Could NOT find Python3: Found unsuitable version "3.10.12", but required is
  at least "3.12" (found /home/chyi/zephyrproject2/.venv/bin/python3, found
  components: Interpreter)
Call Stack (most recent call first):
  /usr/share/cmake-3.22/Modules/FindPackageHandleStandardArgs.cmake:592 (_FPHSA_FAILURE_MESSAGE)
  /usr/share/cmake-3.22/Modules/FindPython/Support.cmake:3180 (find_package_handle_standard_args)
  /usr/share/cmake-3.22/Modules/FindPython3.cmake:490 (include)
  cmake/modules/python.cmake:41 (find_package)
  cmake/modules/user_cache.cmake:30 (include)
  cmake/modules/extensions.cmake:5 (include)
  cmake/modules/FindZephyr-sdk.cmake:41 (include)
  scripts/west_commands/sdk/listsdk.cmake:9 (find_package)

FATAL ERROR: Command '['/usr/bin/cmake', '-P', '/home/chyi/zephyrproject2/zephyr/scripts/west_commands/sdk/listsdk.cmake']' returned non-zero exit status 1.

<다른 터미널 창에서 아래 명령 실행>
$ cd ~/zephyrproject2/.venv/bin
chyi@earth:~/zephyrproject2/.venv/bin$ ls -l python3*
lrwxrwxrwx 1 chyi chyi 16  3월 16 15:33 python3 -> /usr/bin/python3
lrwxrwxrwx 1 chyi chyi  7  3월 16 15:33 python3.12 -> python3

chyi@earth:~/zephyrproject2/.venv/bin$ rm python3
chyi@earth:~/zephyrproject2/.venv/bin$ ln -s /usr/bin/python3.12 python3

chyi@earth:~/zephyrproject2/.venv/bin$ ls -l python3*
lrwxrwxrwx 1 chyi chyi 19  3월 17 12:11 python3 -> /usr/bin/python3.12
lrwxrwxrwx 1 chyi chyi  7  3월 16 15:33 python3.12 -> python3

<다시 원래 터미널 창으로 돌아와서>
((.venv) ) chyi@earth:~/zephyrproject2/zephyr$ west sdk install
...
All done.
((.venv) ) chyi@earth:~/zephyrproject2/zephyr$ 

이후, west build 명령으로 build 작업을 진행한다.

(익히 잘 알고 있는 것 처럼) 가상 환경을 종료하고자 할 경우에는 아래와 같이 해 주면 된다.

((.venv) ) chyi@earth:~/zephyrproject2/zephyr$ deactivate
-----------------------------------------------------------------------------------------------------------------

<Zephyr RTOS 4.3 가상 환경 재 진입하기>
 -> 가상 환경 재 진입 시에는 아래의 명령만으로 충분하다.

chyi@earth:~$ source ~/zephyrproject2/.venv/bin/activate
((.venv) ) chyi@earth:~$ cd zephyrproject2/zephyr/

9.3 Zephyr RTOS image 올리기#1 - blinky example
(본격적으로) 이번 절에서는 Zephyr RTOS용 firmware image를 하나 만들어 Cortex-M33에 올리는 과정을 살펴 보도록 하자. 

제일 먼저 시험해 볼 예제는, 가장 만만한 LED blinking 코드가 되겠다. 😋

((.venv) ) chyi@earth:~/zephyrproject2/zephyr$ west build -p always -b stm32mp257f_dk/stm32mp257fxx/m33 samples/basic/blinky
-- west build: generating a build system
Loading Zephyr default modules (Zephyr base).
-- Application: /home/chyi/zephyrproject2/zephyr/samples/basic/blinky
-- CMake version: 3.22.1
-- Found Python3: /home/chyi/zephyrproject2/.venv/bin/python3 (found suitable version "3.12.13", minimum required is "3.12") found components: Interpreter 
-- Cache files will be written to: /home/chyi/.cache/zephyr
-- Zephyr version: 4.3.99 (/home/chyi/zephyrproject2/zephyr)
-- Found west (found suitable version "1.5.0", minimum required is "0.14.0")
-- Board: stm32mp257f_dk, qualifiers: stm32mp257fxx/m33
-- ZEPHYR_TOOLCHAIN_VARIANT not set, trying to locate Zephyr SDK
-- Found host-tools: zephyr 0.17.4 (/home/chyi/zephyr-sdk-0.17.4)
-- Found toolchain: zephyr 0.17.4 (/home/chyi/zephyr-sdk-0.17.4)
-- Found Dtc: /home/chyi/zephyr-sdk-0.17.4/sysroots/x86_64-pokysdk-linux/usr/bin/dtc (found suitable version "1.7.0", minimum required is "1.4.6") 
-- Found BOARD.dts: /home/chyi/zephyrproject2/zephyr/boards/st/stm32mp257f_dk/stm32mp257f_dk_stm32mp257fxx_m33.dts
-- Generated zephyr.dts: /home/chyi/zephyrproject2/zephyr/build/zephyr/zephyr.dts
-- Generated pickled edt: /home/chyi/zephyrproject2/zephyr/build/zephyr/edt.pickle
-- Generated devicetree_generated.h: /home/chyi/zephyrproject2/zephyr/build/zephyr/include/generated/zephyr/devicetree_generated.h
Parsing /home/chyi/zephyrproject2/zephyr/Kconfig
Loaded configuration '/home/chyi/zephyrproject2/zephyr/boards/st/stm32mp257f_dk/stm32mp257f_dk_stm32mp257fxx_m33_defconfig'
Merged configuration '/home/chyi/zephyrproject2/zephyr/samples/basic/blinky/prj.conf'
Configuration saved to '/home/chyi/zephyrproject2/zephyr/build/zephyr/.config'
Kconfig header saved to '/home/chyi/zephyrproject2/zephyr/build/zephyr/include/generated/zephyr/autoconf.h'
-- Found GnuLd: /home/chyi/zephyr-sdk-0.17.4/arm-zephyr-eabi/arm-zephyr-eabi/bin/ld.bfd (found version "2.38") 
-- The C compiler identification is GNU 12.2.0
-- The CXX compiler identification is GNU 12.2.0
-- The ASM compiler identification is GNU
-- Found assembler: /home/chyi/zephyr-sdk-0.17.4/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc
-- Using ccache: /usr/bin/ccache
-- Found gen_kobject_list: /home/chyi/zephyrproject2/zephyr/scripts/build/gen_kobject_list.py
-- Configuring done
-- Generating done
-- Build files have been written to: /home/chyi/zephyrproject2/zephyr/build
-- west build: building application
[1/156] Preparing syscall dependency handling

[3/156] Generating include/generated/zephyr/version.h
-- Zephyr version: 4.3.99 (/home/chyi/zephyrproject2/zephyr), build: v4.3.0-8239-g91a6b1ec452b
[156/156] Linking C executable zephyr/zephyr.elf
Memory region         Used Size  Region Size  %age Used
           FLASH:       23324 B         8 MB      0.28%
             RAM:        4240 B         8 MB      0.05%
        DDR_CODE:          0 GB         8 MB      0.00%
         DDR_SYS:          0 GB         8 MB      0.00%
        IDT_LIST:          0 GB        32 KB      0.00%
Generating files from /home/chyi/zephyrproject2/zephyr/build/zephyr/zephyr.elf for board: stm32mp257f_dk/stm32mp257fxx/m33
📌 -p always pristine build option(다시 말해 clean build)이다.

Build 결과 파일은 다음과 같다.

((.venv) ) chyi@earth:~/zephyrproject2/zephyr$ ls -l build/zephyr/zephyr.elf
-rwxrwxr-x 1 chyi chyi 621796  3월 16 15:56 build/zephyr/zephyr.elf

참고로, build 과정에서 자동 생성되는 device tree header 파일을 살펴보면 다음과 같다(한번 쯤 내용을 훑어 보는 것이 개발자의 기본 자세 ㅋ). 😋

[그림 9.13] 자동 생성된 device tree file - zephyr/build/zephyr/include/generated/zephyr/devicetree_generated.h

테스트 편의를 위해, LED9(주황색 LED)의 blinking 간격을 1초 -> 5초로 수정해 본다(기존에도 주황색 LED가 1초 간격으로 깜빡이고 있어서, 구분 차원에서 수정하였다).

[그림 9.14] blinky main.c code 수정

자, 이제부터 zephyr.elf file을 구동시켜 보도록 하자.

Cortex-M33 coprocessor에 firmware를 올리는 방법은 아래와 같이 2가지가 있다. 이번 posting에서는 이 중에서 kernel에서 하는 방법을 중심으로 설명하도록 한다.

1) u-boot에서 올리기
2) kernel에서 부팅하면서 올리기 or run-time에 올리기
----------------------------------------------

기존 style대로라면, west flash 명령을 실행하여, zephyr.elf file을 Cortex-M33 flash memory에 write하면 끝나지만, stm32mp257f-dk의 경우는 OpenAMP를 기반으로 zephyr.elf를 loading해 주어야 한다.

Cortex-A35(Linux) -> remoteproc(OpenAMP) -> Cortex-M33(Zephyr)

먼저, elf binary를 target board(Linux OS)로 copy한다.

$ scp /home/chyi/zephyrproject2/zephyr/build/zephyr/zephyr.elf root@192.168.8.227:~/workspace/03172026

이후, target board 상에서 아래와 같은 (remoteproc) 명령을 실행해 주도록 하자.

<Target board>  .................................. (A)
root@stm32mp2-e3-d2-e5:~# cp ~/workspace/03172026/zephyr.elf /lib/firmware
 -> zephyr.elf file을 /lib/firmware directory로 복사한다.

root@stm32mp2-e3-d2-e5:~# cat /sys/class/remoteproc/remoteproc0/name
m33

root@stm32mp2-e3-d2-e5:~# cat /sys/class/remoteproc/remoteproc0/firmware 
USBPD_DRP_UCSI_CM33_NonSecure_stripped.elf

root@stm32mp2-e3-d2-e5:~# cat /sys/class/remoteproc/remoteproc0/state
running
 -> 이미 Cortex-M33 CPU 상에 firmware(USBPD_DRP_UCSI_CM33_NonSecure_stripped.elf)가 올라가 있다.

root@stm32mp2-e3-d2-e5:~# echo stop > /sys/class/remoteproc/remoteproc0/state
[ 2376.558206] ucsi-stm32g0-i2c 1-0035: i2c write 35, 08 error: -110
[ 2376.596942] remoteproc remoteproc0: stopped remote processor m33
 -> 새로운 firmware를 loading하려면, 기존에 동작중이던 것을 중지시켜야 한다.
📌 테스트를 하다 보니, stop 명령으로 한번에 중지가 안되는 경우가 종종 발생한다.

root@stm32mp2-e3-d2-e5:~# echo /lib/firmware/zephyr.elf >  /sys/class/remoteproc/remoteproc0/firmware
 -> 새로운 firmware 명을 설정하도록 한다.

root@stm32mp2-e3-d2-e5:~# echo start > /sys/class/remoteproc/remoteproc0/state
  -> 새로운 firmware로 다시 시작하도록 한다.
[ 2405.108041] remoteproc remoteproc0: powering up m33
[ 2405.146131] remoteproc remoteproc0: Booting fw image zephyr.elf, size 621796
[ 2405.147650] remoteproc remoteproc0: no resource table found for this firmware
[ 2405.155043] remoteproc remoteproc0: remote processor m33 is now up

OK, LED9가 5초 간격으로 깜빡이는 것을 육안으로 확인할 수가 있다. 😎

[그림 9.15] 주황색 LED가 5초 간격으로 깜빡이는 모습

이번에는 serial console로 확인할 차례다.

$ sudo minicom -D /dev/ttyACM1
  -> 신기하게도 (동시에) /dev/ttyACM0로는 linux가 보이며,  /dev/ttyACM1으로 zephyr firmware 동작 모습이 보인다.

[그림 9.16] zephyr console 출력 모습

참고로, yocto project의 경우,  /usr/local/Cube-M33-examples/STM32MP257F-DK 아래에 Cortex-M33 firmware 관련 내용이 설치되어 있다.

root@stm32mp2-e3-d2-e5:/usr/local/Cube-M33-examples/STM32MP257F-DK/Demonstrations/USBPD_DRP_UCSI# ls -la
total 10
drwxr-xr-x 3 root root 1024 Jan  2 14:57 .
drwxr-xr-x 3 root root 1024 Jan  2 14:57 ..
-rw-r--r-- 1 root root   45 Jan  2 14:57 default.stm32mp257f-dk
-rw-r--r-- 1 root root   45 Jan  2 14:57 default.stm32mp257f-dk-ca35tdcid-ostl
-rwxr-xr-x 1 root root 4294 Jan  2 14:57 fw_cortex_m33.sh
drwxr-xr-x 3 root root 1024 Jan  2 14:57 lib
root@stm32mp2-e3-d2-e5:/usr/local/Cube-M33-examples/STM32MP257F-DK/Demonstrations/USBPD_DRP_UCSI# ls -lR
.:
total 8
-rw-r--r-- 1 root root   45 Jan  2 14:57 default.stm32mp257f-dk
-rw-r--r-- 1 root root   45 Jan  2 14:57 default.stm32mp257f-dk-ca35tdcid-ostl
-rwxr-xr-x 1 root root 4294 Jan  2 14:57
fw_cortex_m33.sh   <---------- 이걸 사용하자.
drwxr-xr-x 3 root root 1024 Jan  2 14:57 lib

./lib:
total 1
drwxr-xr-x 2 root root 1024 Mar 18 04:59 firmware

./lib/firmware:
total 7333
-rwxr-xr-x 1 root root 5159544 Jan  2 14:57 USBPD_DRP_UCSI_CM33_NonSecure.elf
-rw-r--r-- 1 root root  148892 Jan  2 14:57 USBPD_DRP_UCSI_CM33_NonSecure_sign.bin
-rwxr-xr-x 1 root root  148256 Mar 18 04:59 USBPD_DRP_UCSI_CM33_NonSecure_stripped.elf
-rw-r--r-- 1 root root  148892 Jan  2 14:57 USBPD_DRP_UCSI_CM33_NonSecure_stripped_sign.bin

---------------------------------------------------------------------------------------------------------------

이 내용 중에서 fw_cortex_m33.sh script를 이용하면 앞서 (A)에서 설정한 내용을 한방(?)에 처리할 수가 있다. 이와 관련해서는 다음 절에서 자세히 살펴 보기로 하자.

9.4 Zephyr RTOS image 올리기#2 - openamp sample
OpenAMP를 사용한다는 것은 이 기종 processor간에 뭔가 역동(?)적인 상호 작용이 필요하다는 얘기이기도 하다. 단순히 LED를 깜빡이게 하는게 목적이 아니라는 말이다. 😋
따라서, 이번에는 Linux kernel module과 Zephyr code에 OpenAMP code가 포함된 예제를 돌려 봄으로써, (OpenAMP를 통해) 이기종 CPU 간에 상호 통신이 어떻게 이루어지는 지를 확인해 보도록 하자.

<시험 내용#1>
           rmsg_client_sample.ko  <-----OpenAMP------>  zephyr_openamp_rsc_table.elf
   (Linux kernel module)                                                       (zephyr firmware image)

아래 내용은 위의 두 code의 위치를 확인하는 차원에서 main code를 capture해 본 것이다.


[그림 9.17] linux-stm32mp/samples/rpmsg/rpmsg_client_sample.c

[그림 9.18] zephyr/samples/subsys/ipc/openamp_rsc_table/src/main_remote.c

양쪽 code를 살짝 들여다 보니, linux kernel module에서 "hello world!" string을 zephyr code쪽으로 던지면, zephyr code는 이를 받아 console로 출력하고, 수신한 내용을 다시 linux kernel module로 되돌려 보내는 지극히 간단한 예제로 보인다. 😋 물론 OpenAMP 기반으로 message가 상호 전달되는 것이므로, 내부 구조까지 상세히 따라 들어가 보면 결코 간단치 만은 아닌 내용이지만 말이다.

위의 Kernel module(rmsg_client_sample.ko)은 (yocto & buildroot 상에서) 이미 build되어 kernel image(정확히는 module 형태로 존재)에 포함된 상태이니, 여기에서는 zephyr code만 build해 보면 될 일이다.

----------------------------------------------------------------
((.venv) ) chyi@earth:~/zephyrproject2/zephyr$ west build -p always -b stm32mp257f_dk/stm32mp257fxx/m33 samples/subsys/ipc/openamp_rsc_table
-- west build: making build dir /home/chyi/zephyrproject2/zephyr/build pristine
-- west build: generating a build system
Loading Zephyr default modules (Zephyr base).
-- Application: /home/chyi/zephyrproject2/zephyr/samples/subsys/ipc/openamp_rsc_table
-- CMake version: 3.22.1
-- Found Python3: /home/chyi/zephyrproject2/.venv/bin/python3.12 (found suitable version "3.12.13", minimum required is "3.12") found components: Interpreter 
-- Cache files will be written to: /home/chyi/.cache/zephyr
-- Zephyr version: 4.3.99 (/home/chyi/zephyrproject2/zephyr)
-- Found west (found suitable version "1.5.0", minimum required is "0.14.0")
-- Board: stm32mp257f_dk, qualifiers: stm32mp257fxx/m33
-- ZEPHYR_TOOLCHAIN_VARIANT not set, trying to locate Zephyr SDK
-- Found host-tools: zephyr 0.17.4 (/home/chyi/zephyr-sdk-0.17.4)
-- Found toolchain: zephyr 0.17.4 (/home/chyi/zephyr-sdk-0.17.4)
-- Found Dtc: /home/chyi/zephyr-sdk-0.17.4/sysroots/x86_64-pokysdk-linux/usr/bin/dtc (found suitable version "1.7.0", minimum required is "1.4.6") 
-- Found BOARD.dts: /home/chyi/zephyrproject2/zephyr/boards/st/stm32mp257f_dk/stm32mp257f_dk_stm32mp257fxx_m33.dts
-- Found devicetree overlay: /home/chyi/zephyrproject2/zephyr/samples/subsys/ipc/openamp_rsc_table/boards/stm32mp257f_dk_stm32mp257fxx_m33.overlay
-- Generated zephyr.dts: /home/chyi/zephyrproject2/zephyr/build/zephyr/zephyr.dts
-- Generated pickled edt: /home/chyi/zephyrproject2/zephyr/build/zephyr/edt.pickle
-- Generated devicetree_generated.h: /home/chyi/zephyrproject2/zephyr/build/zephyr/include/generated/zephyr/devicetree_generated.h

warning: PRINTK (defined at subsys/debug/Kconfig:145) was assigned the value 'n' but got the value
'y'. See http://docs.zephyrproject.org/latest/kconfig.html#CONFIG_PRINTK and/or look up PRINTK in
the menuconfig/guiconfig interface. The Application Development Primer, Setting Configuration
Values, and Kconfig - Tips and Best Practices sections of the manual might be helpful too.

Parsing /home/chyi/zephyrproject2/zephyr/Kconfig
Loaded configuration '/home/chyi/zephyrproject2/zephyr/boards/st/stm32mp257f_dk/stm32mp257f_dk_stm32mp257fxx_m33_defconfig'
Merged configuration '/home/chyi/zephyrproject2/zephyr/samples/subsys/ipc/openamp_rsc_table/prj.conf'
Merged configuration '/home/chyi/zephyrproject2/zephyr/samples/subsys/ipc/openamp_rsc_table/boards/stm32mp257f_dk_stm32mp257fxx_m33.conf'
Configuration saved to '/home/chyi/zephyrproject2/zephyr/build/zephyr/.config'
Kconfig header saved to '/home/chyi/zephyrproject2/zephyr/build/zephyr/include/generated/zephyr/autoconf.h'
-- Found GnuLd: /home/chyi/zephyr-sdk-0.17.4/arm-zephyr-eabi/arm-zephyr-eabi/bin/ld.bfd (found version "2.38") 
-- The C compiler identification is GNU 12.2.0
-- The CXX compiler identification is GNU 12.2.0
-- The ASM compiler identification is GNU
-- Found assembler: /home/chyi/zephyr-sdk-0.17.4/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc
-- libmetal version: 1.9.0 (/home/chyi/zephyrproject2/zephyr/samples/subsys/ipc/openamp_rsc_table)
-- Build type:  
-- Host:    Linux/x86_64
-- Target:  Generic/arm
-- Machine: arm
-- Vendor: none
-- Looking for include file stdatomic.h
-- Looking for include file stdatomic.h - found
-- open-amp version: 1.8.0 (/home/chyi/zephyrproject2/modules/lib/open-amp/open-amp)
-- Host:    Linux/x86_64
-- Target:  Generic/arm
-- Machine: arm
-- C_FLAGS :  -Wall -Wextra
-- Looking for include file fcntl.h
-- Looking for include file fcntl.h - found
-- Using ccache: /usr/bin/ccache
-- Found gen_kobject_list: /home/chyi/zephyrproject2/zephyr/scripts/build/gen_kobject_list.py
-- Configuring done
-- Generating done
-- Build files have been written to: /home/chyi/zephyrproject2/zephyr/build
-- west build: building application
[1/224] Preparing syscall dependency handling

[3/224] Generating include/generated/zephyr/version.h
-- Zephyr version: 4.3.99 (/home/chyi/zephyrproject2/zephyr), build: v4.3.0-8239-g91a6b1ec452b
[224/224] Linking C executable zephyr/zephyr_openamp_rsc_table.elf
Memory region         Used Size  Region Size  %age Used
           FLASH:       72800 B         8 MB      0.87%
             RAM:       21244 B         8 MB      0.25%
        DDR_CODE:          0 GB         8 MB      0.00%
         DDR_SYS:          0 GB         8 MB      0.00%
        IDT_LIST:          0 GB        32 KB      0.00%
Generating files from /home/chyi/zephyrproject2/zephyr/build/zephyr/zephyr_openamp_rsc_table.elf for board: stm32mp257f_dk/stm32mp257fxx/m33

역시 개발자라면,  이번에도 아래 파일들을 한번 쯤 훑어 보는 것이 신상에 해롭지 않을까 싶다. 😋

[그림 9.19] 자동생성된 stm32mp257f_dk_stm32mp257fxx_m33.dts file

[그림 9.20] stm32mp257f_dk_stm32mp257fxx_m33.overlay file


[그림 9.21] 자동생성된 zephyr.dts file

자, build된 elf file을 target board로 copy하도록 하자.

$ scp /home/chyi/zephyrproject2/zephyr/build/zephyr/zephyr_openamp_rsc_table.elf  root@192.168.8.227:~/workspace/03182026

<Target board>
root@stm32mp2-e3-d2-e5:~# modprobe rpmsg_client_sample
 -> 먼저 rmsg_client_sample.ko(linux samples 디렉토리에 있는 예제)를 구동시키도록 하자.

root@stm32mp2-e3-d2-e5:~# lsmod | grep rpmsg
rpmsg_client_sample    16384  0
rpmsg_tty              16384  0
rpmsg_ctrl             12288  0
rpmsg_char             20480  1 rpmsg_ctrl
virtio_rpmsg_bus       24576  0
i2c_rpmsg              16384  0
irq_rpmsg              12288  0

다음으로, 앞서 build한 zephyr_openamp_rsc_table.elf를 구동시킬 차례이다.

root@stm32mp2-e3-d2-e5:~#  cd ~/workspace/03182026
root@stm32mp2-e3-d2-e5:~# cp ./zephyr_openamp_rsc_table.elf /usr/local/Cube-M33-examples/STM32MP257F-DK/Demonstrations/USBPD_DRP_UCSI/lib/firmware/USBPD_DRP_UCSI_CM33_NonSecure_stripped.elf
 -> 내용을 분석해 보니, fw_cortex_m33.sh에서 USBPD_DRP_UCSI_CM33_NonSecure_stripped.elf 파일을 /lib/firmware로 복사 후, 구동 준비를 하는 관계로, 일단은 이렇게 복사를 하도록 한다.

root@stm32mp2-e3-d2-e5:~# cd /usr/local/Cube-M33-examples/STM32MP257F-DK/Demonstrations/USBPD_DRP_UCSI
root@stm32mp2-e3-d2-e5:~# fw_cortex_m33.sh stop
root@stm32mp2-e3-d2-e5:~# fw_cortex_m33.sh start
 -> fw_cortex_m33.sh script를 이용하여 M33에 올라가 있는 firmware를 중지시킨 후, 새로운 firmware로 재구동시킨다.

(이 상태에서) linux console과 zephyr console을 통해 각각 확인해 보니, 서로 rpmsg를 주고 받는 모습이 보인다. 😎

$ sudo minicom -D /dev/ttyACM0

[그림 9.22] linux console - fw_cortex_m33.sh stop -> start

sudo minicom -D /dev/ttyACM1

[그림 9.23] zephyr console - fw_cortex_m33.sh start 시 출력되는 내용

참고로, 위의 zephyr_openamp_rsc_table 예제 코드는 (main 함수에서) 3개의 thread를 띄우고 있는데, 아래 thread를 사용하면, tty를 활용한 또 다른 시험이 가능하다.

-----------------------------------------------------------------------------
    k_thread_create(&thread_tty_data, thread_tty_stack, APP_TTY_TASK_STACK_SIZE,
            app_rpmsg_tty,
            NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
-----------------------------------------------------------------------------

<시험 내용#2>
          /dev/ttyRPMSG0 <-----OpenAMP------>  zephyr_openamp_rsc_table.elf
   (Linux )                                                         (zephyr firmware image)

root@stm32mp2-e3-d2-e5:~# lsmod | grep rpmsg
rpmsg_client_sample    16384  0
rpmsg_tty              16384  0    <---- 이번에는 얘가 사용되는데, 이미 구동되어 있다.
rpmsg_ctrl             12288  0
rpmsg_char             20480  1 rpmsg_ctrl
virtio_rpmsg_bus       24576  0
i2c_rpmsg              16384  0
irq_rpmsg              12288  0

root@stm32mp2-e3-d2-e5:~# cat /dev/ttyRPMSG0 &
  -> 먼저, 이 명령을 실행하여 /dev/ttyRPMSG0로 들어오는 값을 출력할 준비를 해 둔다.

root@stm32mp2-e3-d2-e5:~# echo "Hello Zephyr" > /dev/ttyRPMSG0
  ->   Hello Zephyr string을 /dev/ttyRPMSG0로 내보낸다(zephyr로 내보낸다).
Hello Zephyr

[그림 9.24] linux console - fw_cortex_m33.sh stop -> start

한가지 재밌는 사실은 /dev/ttyRPMSG0를 통해 명령을 전달할 경우, 해당 명령이 zephyr rtos 상에서 실행된 후, 그 결과가 linux로 전달되어 console에 출력된다는 점이다.

9.5 STM32CubeMP2 example 돌려 보기
Cortex-M33 CPU core에 firmware를 올리는 방법은 다양하게 존재한다. 이번 절에서는 STM32CubeMP2 example code를 build하여 올려보는 내용을 소개하고자 한다. 사실 이와 관련해서는 (오래전에) STM32MP157C-DK2 보드를 사용하여 비슷한 내용을 소개한 바가 있다. 따라서, 여기에서는 아래 link를 참조하는 것으로 이를 대신하고자 한다. 😂


참고로, STM32CubeMP2 example codes는 아래 site에서 확인 가능하다.


-------------------------------------------------------------------
이상으로 6차례에 걸쳐서 STM32MP257F-DK 보드를 기반으로 embedded linux와 관련한 다양한 주제를 다루어 보았다. 

1) Hardware spec & Board booting
2) Device Tree & Booting flow 해부
3) Yocto 기반 개발 방법 #1, #2
4) Buildroot 기반 개발 방법
5) 이기종 CPU간의 통신 방법(Linux <- OpenAMP -> Zephyr)

이어지는 posting에서는, 마지막으로 사용자 계층 application에 관한 내용을 다뤄 보고자 한다. 😎


To be continued...


References
[1] https://www.st.com/en/evaluation-tools/stm32mp257f-dk.html
[2] UM3385 - Discovery kit with STM32MP257F MPU - User manual, STMicroelectronics.
[3] Data brief - STM32MP257F-DK, STMicroelectronics.
[4] STM32MP251C/F STM32MP253C/F, STM32MP255C/F STM32MP257C/F Datasheet, STMicroelectronics.
[5] STM32MP257-DK schematic, STMicroelectronics.
[6] RM0457 Reference manual, STM32MP23/25xx advanced Arm®-based 32/64-bit MPUs
[7] https://wiki.st.com/stm32mpu/wiki/Getting_started/STM32MP2_boards/STM32MP257x-DK
[8] https://wiki.st.com/stm32mpu/wiki/STM32MP2_boot_chain_overview
[9] https://wiki.st.com/stm32mpu/wiki/STM32MPU_Distribution_Package
[10] https://www.st.com/en/microcontrollers-microprocessors/stm32mp257f.html
[11] https://wiki.st.com/stm32mpu/wiki/Linux_remoteproc_framework_overview#Remote_processor_boot_through_sysfs
[12] https://wiki.st.com/stm32mpu/wiki/Category:Buildroot-based_Linux_embedded_software
  -> STM32MP documents

[13] https://docs.zephyrproject.org/latest/boards/st/stm32mp257f_dk/doc/index.html
[14] https://slowbootkernelhacks.blogspot.com/2024/11/zephyr-rtos-programming.html
[15] https://slowbootkernelhacks.blogspot.com/2020/11/zephyr-rtos-project.html
[16] https://docs.zephyrproject.org/latest/develop/getting_started/index.html
[17] https://www.zephyrproject.org/wp-content/uploads/2026/01/Zephyr-Overview-20260107.pdf
  -> zephyr RTOS

[18] https://openamp.readthedocs.io/_/downloads/en/latest/pdf/
[19] https://openamp.readthedocs.io/en/latest/
[20] https://static.linaro.org/connect/hkg18/presentations/hkg18-411.pdf
[21] https://www.openampproject.org/docs/whitepapers/Introduction_to_OpenAMPlib_v1.1a.pdf
  -> OpenAMP

[22] https://github.com/bootlin/buildroot-external-st
  -> buildroot

[23] And Google and Gemini~

Slowboot

댓글 없음:

댓글 쓰기