Zephyr(RTOS)を使って、コア間通信をしてみました。
最近のSoC内のコアはヘテロジニアス(異種混合)化が進み、種類・数ともに多いです。
RISC-VとARMを使って、コア間通信で分散処理をテストした内容を紹介します
RISC-VとARMをIPC(コア間通信)で繋げてみた。
Zephyr(RTOS)を使って、コア間通信をしてみました。
最近のSoC内のコアはヘテロジニアス(異種混合)化が進み、種類・数ともに多いです。

RISC-VとARMを使って、コア間通信で分散処理をテストした内容を紹介します

この内容をZephyr Project Meetup: Nagoya, Japanで報告させていただきました。
その際の発表資料です。
●後日掲載予定。
GitHub
GitHubのリンク先は下記です
概要
Nordicの最新SoCである nRF54L15(またはnRF5340など)のマルチコア構成を活かした、ユニークなデモアプリケーションを作成したので紹介します。
このプロジェクトでは、Zephyr RTOS と Edge Impulse を組み合わせて、以下のシステムを構築しました。
- Hostコア (ARM):加速度センサの値を読み取り、AIモデルでジェスチャーを推論。
- Remoteコア (RISC-V):Hostコアから推論結果を受け取り、PWM (Pulse Width Modulation) でLEDの明るさを滑らかに制御。
単にLEDをON/OFFするのではなく、推論されたジェスチャーの種類に応じて「明るさ(デューティ比)」を変化させることで、より細やかな制御をマルチコア間で行うデモとなっています。
システム構成
システムの概要は以下の通りです。
- ハードウェア: Nordic nRF54L15 DK (または nRF5340 DK)
- OS: Zephyr RTOS (nRF Connect SDK)
- MLライブラリ: Edge Impulse C++ SDK
- 通信プロトコル: IPC (Backend:
icmsg)
処理の流れ:
- Host側: 加速度センサ (I2C) からデータを取得 → Edge Impulseで推論 → 結果(ラベルとスコア)をIPCで送信。
- Remote側: IPCメッセージを受信 → 文字列を解析 → ジェスチャーに応じて PWMのデューティ比 を変更してLEDを駆動。

実装のポイント
Host側:推論とIPC送信
Host側(Main.cpp)では、センサーデータの収集と推論ループを回しています。推論結果が出ると、スコアが最も高いラベルを文字列(例: "flick:0.98")に整形して送信します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 推論結果の送信部分 (Main.cpp) if (best_ix >= 0) { struct data_packet msg; memset(&msg, 0, sizeof(msg)); // "Label:Score" の形式に整形 int len = snprintf((char *)msg.data, sizeof(msg.data), "%s:%.2f", result.classification[best_ix].label, best_val); if (len > 0) { // IPCサービスを使ってRemoteコアへ送信 ipc_service_send(ep, msg.data, len); } } |
Remote側:受信とPWM制御
Remote側(main.c)は、RISC-Vコア(またはNetworkコア)で動作します。IPCでメッセージを受け取ると、その内容に応じて PWMドライバ を使用してLEDの明るさを3段階に制御します。
- idle: デューティ比 0% (消灯)
- flick: デューティ比 50% (半点灯)
- updown: デューティ比 100% (全点灯)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// データ受信時のコールバック (main.c) static void ep_recv(const void *data, size_t len, void *priv) { // 受信データを文字列化 char msg[128]; // ... (コピー処理) ... LOG_INF("Received: %s", msg); uint32_t pulse = 0; bool update = false; // ジェスチャー判定とPWMデューティ比の設定 if (strstr(msg, "idle") != NULL) { pulse = 0; // 0% update = true; } else if (strstr(msg, "flick") != NULL) { pulse = pwm_led0.period / 2; // 50% update = true; } else if (strstr(msg, "updown") != NULL) { pulse = pwm_led0.period; // 100% update = true; } if (update) { // PWMでLEDを駆動 pwm_set_dt(&pwm_led0, pwm_led0.period, pulse); } } |
デモ動画
実際に動作している様子です。ジェスチャー(flickやupdown)を行うと、それに応じてLEDの明るさ(PWM)が変わっているのが分かります。

こちらがnRF54L15-DKの評価ボードでのデモ動画となります。
- Zephyr IPC-Demo with RISC-V + ARM on nRF54L15-DK
以前個人開発したnRF54L15が搭載した、カスタムボードでも問題なく動作しました。
- Zephyr IPC-Demo with RISC-V + ARM on nRF54L15 Custom Board
ログ
Host(ARM)とRemote(RISC-V)側のログを取りましたので、貼り付けておきます。
無事にIPC通信出来ていることが分かります。
Host側のログ例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
[00:12:33.001,322] <inf> host: Predictions (DSP: 39 ms, Classification: 1 ms, Anomaly: 0 ms) [00:12:33.001,349] <inf> host: circle: 0.13281 [00:12:33.001,361] <inf> host: flick: 0.74219 [00:12:33.001,373] <inf> host: idle: 0.07031 [00:12:33.001,385] <inf> host: updown: 0.05078 [00:12:33.001,485] <inf> host: Sent inference result: flick:0.74 [00:12:34.926,868] <inf> host: Predictions (DSP: 39 ms, Classification: 0 ms, Anomaly: 0 ms) [00:12:34.926,893] <inf> host: circle: 0.05469 [00:12:34.926,905] <inf> host: flick: 0.78906 [00:12:34.926,917] <inf> host: idle: 0.12109 [00:12:34.926,929] <inf> host: updown: 0.03516 [00:12:34.927,028] <inf> host: Sent inference result: flick:0.79 [00:12:36.851,436] <inf> host: Predictions (DSP: 39 ms, Classification: 1 ms, Anomaly: 0 ms) [00:12:36.851,461] <inf> host: circle: 0.16797 [00:12:36.851,473] <inf> host: flick: 0.26562 [00:12:36.851,485] <inf> host: idle: 0.50000 [00:12:36.851,497] <inf> host: updown: 0.06641 [00:12:36.851,596] <inf> host: Sent inference result: idle:0.50 [00:12:38.772,037] <inf> host: Predictions (DSP: 39 ms, Classification: 0 ms, Anomaly: 0 ms) [00:12:38.772,062] <inf> host: circle: 0.08203 [00:12:38.772,074] <inf> host: flick: 0.01172 [00:12:38.772,086] <inf> host: idle: 0.03906 [00:12:38.772,098] <inf> host: updown: 0.86328 [00:12:38.772,199] <inf> host: Sent inference result: updown:0.86 [00:12:40.691,609] <inf> host: Predictions (DSP: 39 ms, Classification: 1 ms, Anomaly: 0 ms) [00:12:40.691,634] <inf> host: circle: 0.08203 [00:12:40.691,648] <inf> host: flick: 0.01562 [00:12:40.691,659] <inf> host: idle: 0.03906 [00:12:40.691,671] <inf> host: updown: 0.86328 [00:12:40.691,772] <inf> host: Sent inference result: updown:0.86 [00:12:42.613,134] <inf> host: Predictions (DSP: 39 ms, Classification: 0 ms, Anomaly: 1 ms) [00:12:42.613,159] <inf> host: circle: 0.02734 [00:12:42.613,171] <inf> host: flick: 0.00391 [00:12:42.613,183] <inf> host: idle: 0.05469 [00:12:42.613,195] <inf> host: updown: 0.91406 [00:12:42.613,296] <inf> host: Sent inference result: updown:0.91 [00:12:44.539,739] <inf> host: Predictions (DSP: 40 ms, Classification: 0 ms, Anomaly: 0 ms) [00:12:44.539,764] <inf> host: circle: 0.00391 [00:12:44.539,776] <inf> host: flick: 0.00391 [00:12:44.539,787] <inf> host: idle: 0.19531 [00:12:44.539,799] <inf> host: updown: 0.79687 [00:12:44.539,899] <inf> host: Sent inference result: updown:0.80 [00:12:46.464,274] <inf> host: Predictions (DSP: 39 ms, Classification: 0 ms, Anomaly: 0 ms) [00:12:46.464,299] <inf> host: circle: 0.00781 [00:12:46.464,311] <inf> host: flick: 0.02344 [00:12:46.464,323] <inf> host: idle: 0.91016 [00:12:46.464,335] <inf> host: updown: 0.05469 [00:12:46.464,435] <inf> host: Sent inference result: idle:0.91 [00:12:48.389,822] <inf> host: Predictions (DSP: 39 ms, Classification: 0 ms, Anomaly: 0 ms) [00:12:48.389,848] <inf> host: circle: 0.00000 [00:12:48.389,859] <inf> host: flick: 0.00000 [00:12:48.389,871] <inf> host: idle: 0.98828 [00:12:48.389,883] <inf> host: updown: 0.01172 [00:12:48.389,981] <inf> host: Sent inference result: idle:0.99 |
Remote側のログ例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[00:12:33.001,486] <inf> RISC_V: Received: flick:0.74 [00:12:33.001,502] <inf> RISC_V: PWM: 50% [00:12:34.927,029] <inf> RISC_V: Received: flick:0.79 [00:12:34.927,045] <inf> RISC_V: PWM: 50% [00:12:36.851,597] <inf> RISC_V: Received: idle:0.50 [00:12:36.851,610] <inf> RISC_V: PWM: 0% [00:12:38.772,199] <inf> RISC_V: Received: updown:0.86 [00:12:38.772,216] <inf> RISC_V: PWM: 100% [00:12:40.691,773] <inf> RISC_V: Received: updown:0.86 [00:12:40.691,790] <inf> RISC_V: PWM: 100% [00:12:42.613,297] <inf> RISC_V: Received: updown:0.91 [00:12:42.613,313] <inf> RISC_V: PWM: 100% [00:12:44.539,900] <inf> RISC_V: Received: updown:0.80 [00:12:44.539,917] <inf> RISC_V: PWM: 100% [00:12:46.464,435] <inf> RISC_V: Received: idle:0.91 [00:12:46.464,449] <inf> RISC_V: PWM: 0% [00:12:48.389,982] <inf> RISC_V: Received: idle:0.99 [00:12:48.389,996] <inf> RISC_V: PWM: 0% |
まとめ
Zephyr(RTOS)を使って、コア間通信をしてみました。
最近のSoC内のコアはヘテロジニアス(異種混合)化が進み、種類・数ともに多いです。
RISC-VとARMを使って、コア間通信で分散処理をテストした内容を紹介しました


コメント