BBBのPRUの使い方

概要

AM3358プロセッサは、ARMコアとは独立して、100MHzのクロックで動作する2つのPRU(Programable Realtime Unit)を備えており、リアルタイム処理が必要な場合に利用することができる。

GPIOについては、PRUとの接続はマルチプレクサで自由に選べるようにはなっていないので、PRU0/1それぞれで利用できるピンをあらかじめ調べて入出力信号を接続する必要がある。PRUからはinput/outputがそれぞれ独立でレジスタR30, R31の各ビットに割り当てられている。
GPIOのみならず、TimerやADCなどのペリフェラルもPRUから利用することができる。こちらの詳細についてはTechnical Reference Manualを参照されたい。

セットアップ

kernel 5.10.168-ti-r72を前提とする。基本的にkernel依存は無いはずだが、RpMsgが4.19.94-ti-r42だとうまく動作しなかった(デバイスファイルが出来ない)ので、怪しい場合はkernel updateを試してみると良いだろう。

PRU-ICSSライブラリ

pru-software-support-packageというのをダウンロードすると、ライブラリやヘッダファイル一式が含まれている。

もしくは、BBBには以下のディレクトリに既にSDK(pru-software-support-packageパッケージ)が入っているのでこれを用いるのが簡単です。
/usr/lib/ti/pru-software-support-package

CCSと呼ばれる開発環境(IDE)もあるようだが試していない。この辺、いくつかライブラリがあったり、新旧がわからなかったりで少し紛らわしい。私が用いた方法が必ずしも正ということもなくなっているかもしれないので注意してほしい。(2024/3/1現在)

ubootの設定

/boot/uEnv.txtで次のような設定を行う。kernelが異なる場合は適切なバージョンを用いること。
また、PRUとは直接関係ないのだが、テストプログラムではTSC-ADCモジュールを用いるので、OSのADCドライバをdisableする設定を行っておく。

uboot_overlay_pru=/lib/firmware/AM335X-PRU-RPROC-4-19-TI-00A0.dtbo
disable_uboot_overlay_adc=1

使い方

PRU FWのコンパイル

コンパイラはclpruを用いてARM上でクロスコンパイルを行う。
コンパイルしている中で、AM335x_PRU_intc_rscTbl.cmdスクリプトでmapファイルというのを作っている。リンクするときに必要なようだがよく理解していないので、pru-software-support-packageに含まれるexampleからそのままコピーして利用した。詳細はMakefileを見てほしい。

PRU FWのロードと実行

作成したPRU FWはあらかじめ/lib/firmwareにインストールしておく。
制御は/sys/class/remoteproc/remoteproc*以下のデバイスファイルで行う。remoteproc1がPRU1, remoteproc2がPRU2である。remoteproc0はARM用なので用いない。
これらのディレクトリが作成されていない場合、uEnv.txtの設定に問題がある。

firmwareというデバイスファイルにファイル名を書き込むことでfirmwareのロードを、stateファイルでstop/startを行う。

実行例

root@beaglebone:~# echo stop > /sys/class/remoteproc/remoteproc1/state
root@beaglebone:~# cat /sys/class/remoteproc/remoteproc1/state
offline
root@beaglebone:~# echo pru_fw.out >/sys/class/remoteproc/remoteproc1/firmware
root@beaglebone:~# cat /sys/class/remoteproc/remoteproc1/firmware
pru_fw.out
root@beaglebone:~# echo start > /sys/class/remoteproc/remoteproc1/state
root@beaglebone:~# cat /sys/class/remoteproc/remoteproc1/state
running

またこの時、messagesに次のような出力がある。

[411349.645076] remoteproc remoteproc1: stopped remote processor 4a334000.pru
[411404.509061] remoteproc remoteproc1: powering up 4a334000.pru
[411404.509795] remoteproc remoteproc1: Booting fw image pru_fw.out, size 96696

PRU-ARMホスト間通信

ホストとPRU間の通信は、RpMsgというのを使う実装がTIから提供されていて、前日のpru-software-support-packageに含まれているので、これを用いる。

PRUのプログラムが実行されると、程なく/dev/rpmsg_pru30が生成されるので、これをオープンしてホスト側は読み書きを行う。PRU FWのstart直後にデバイスファイルをオープンしようとすると失敗するので、waitを入れるか、デバイスファイルの生成を待っつ必要がある。

PRU側のFirmwareでは、手順に従って初期化を行った後、pru_rpmsg_send/pru_rpmsg_receiveで通信を行う。連続的にメッセージを送ってオーバーフローさせてしまうと、以降まともに動作しなくなるので、対話的に通信を行う用途に向いている。

また、メッセージ交換用のバッファサイズRPMSG_MESSAGE_SIZE=496が、include/pru_rpmsg.h内で宣言されていて、これを超えるサイズを転送しようとするとOSがハングするの場合があるので注意。

試しに、ヘッダファイル内を次のように修正してrpmsg_lib.libをビルドしなおしてみたが、/dev/rpmsg_pru30からのread待ちとなってしまい、大きなサイズの転送は動作しなかった。そんなに単純な話ではないようだ。
より大きなデータを交換するには、複数回に分ける必要がありそう。

/* The maximum size of the buffer (including the header) */
//#define RPMSG_BUF_SIZE              512
#define RPMSG_BUF_SIZE              1024
/* The size of the buffer header */
#define RPMSG_HEADER_SIZE           16
/* The maximum size of the buffer message */
#define RPMSG_MESSAGE_SIZE          (RPMSG_BUF_SIZE-RPMSG_HEADER_SIZE)

テストプログラム

flow chart

AM3358内臓のTSC_ADCモジュールを用いて、A/D変換したデータをPRUで刈り取って、128個づつOS側に転送するテストプログラムを作成した。
TSC_ADCモジュールの使い方についての詳細はこちらにまとめている。
PRUを用いてTSC_ADCのFIFOバッファの監視や読み込みを行うことで、Overrunを抑止しつつFIFOバッファの容量を超える容量のサンプリングが可能となっている。

ソースはこちら

スループットの確認

折角なので、参考までにテストプログラムをアレンジしたもので、PRU-ホスト間転送を含めたサンプリングレートがどの程度になるか調べてみた。

PRU側のプログラムで、oneshot modeで128回計測し、ホスト側にまとめて転送する方法では、126kSPSでの計測が可能であることがわかった。

root@beaglebone:~/PRU_ADC# time ./userspace
12800256 samplings

real    1m41.599s
user    0m1.170s
sys     0m7.382s
PRU側のプログラムで、oneshot modeで128回x3ch計測し、ホスト側に3回に分けてて転送する方法では、66kSPSでの計測が可能であることがわかった。
root@beaglebone:~/PRU_ADC# time ./userspace
12800256 samplings

real    3m13.524s
user    0m0.613s
sys     0m7.896s

PRU側のプログラムで、continuous modeで計測をし、FIFOに値が入り次第PRUで配列にコピーすることを繰り返して128個の計測結果を取得して、128個のデータをホストに転送する方法では771kSPSでの計測が可能であった。

root@beaglebone:~/PRU_ADC# time ./userspace
128000256 samplings

real    2m46.237s
user    0m1.045s
sys     0m25.239s

continuous modeに比べると数倍遅いものの、Ooneshotモードにおいても案外高速にサンプリングできることがわかった。3ch分まとめて取得してからホスト側に転送する方法だと、通信と並行して計測している時間が少ないので、通信のオーバーヘッドが目立ってくるようだ。