AM3358プロセッサは、ARMコアとは独立して、100MHzのクロックで動作する2つのPRU(Programable Realtime Unit)を備えており、リアルタイム処理が必要な場合に利用することができる。
GPIOについては、PRUとの接続はマルチプレクサで自由に選べるようにはなっていないので、PRU0/1それぞれで利用できるピンをあらかじめ調べて入出力信号を接続する必要がある。PRUからはinput/outputがそれぞれ独立でレジスタR30, R31の各ビットに割り当てられている。
GPIOのみならず、TimerやADCなどのペリフェラルもPRUから利用することができる。こちらの詳細については
kernel 5.10.168-ti-r72を前提とする。基本的にkernel依存は無いはずだが、RpMsgが4.19.94-ti-r42だとうまく動作しなかった(デバイスファイルが出来ない)ので、怪しい場合はkernel updateを試してみると良いだろう。
pru-software-support-packageというのをダウンロードすると、ライブラリやヘッダファイル一式が含まれている。
もしくは、BBBには以下のディレクトリに既にSDK(pru-software-support-packageパッケージ)が入っているのでこれを用いるのが簡単です。
/usr/lib/ti/pru-software-support-package
CCSと呼ばれる開発環境(IDE)もあるようだが試していない。この辺、いくつかライブラリがあったり、新旧がわからなかったりで少し紛らわしい。私が用いた方法が必ずしも正ということもなくなっているかもしれないので注意してほしい。(2024/3/1現在)
/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
コンパイラはclpruを用いてARM上でクロスコンパイルを行う。
コンパイルしている中で、AM335x_PRU_intc_rscTbl.cmdスクリプトでmapファイルというのを作っている。リンクするときに必要なようだがよく理解していないので、pru-software-support-packageに含まれるexampleからそのままコピーして利用した。詳細はMakefileを見てほしい。
作成した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間の通信は、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)
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.382sPRU側のプログラムで、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分まとめて取得してからホスト側に転送する方法だと、通信と並行して計測している時間が少ないので、通信のオーバーヘッドが目立ってくるようだ。