Raspberry Pi Picoの仕様書を読んでみる(3)

引き続き、RP2040のマニュアルを読んでみます。
今回はRP2040の特徴的な機能である、PIO(プログラマブルIO)について見ていきます。

最初に、PIOとはどんなことができるものか、イメージを説明します。

例えばI2Cでの送信を考えてみてください。この機能を利用する側は、I2Cアドレスと送りたいデータを用意します。ハードウェアI2Cインターフェースがある場合には、データをレジスタに書き込んでやると、SCLでクロックが出力され、SDAでデータが1ビットずつ送り出されていきます。

この同じ処理を、GPIOをソフトウェアで叩く、いわゆる「ビットバンギング」というというやり方でソフトウェアのみで実装することもできます。ただしその場合、処理中はメインCPUが占有されてしまいます。

PIOは、このビットバンキングによる通信プロトコルのソフトウェア実装を行うための、専用の小規模なシステムです。PIOにはステートマシンという小さなRISC CPUが4つ内蔵されており、ステートマシンのためのプログラムを書くことにより、プロトコルを実装します。

多くのインターフェースは、複数の信号線を必要とします。RP2040のPIOは4つのステートマシンを持っており、各ステートマシンがGPIOの入出力を処理します。1つのPIOで、合計で最大32本のGPIOを使ったインターフェースを作ることができます。GPIOへのアクセスは、あらかじめPIOのレジスタで「ベース」となるGPIOを指定し、ステートマシンの命令ではベースからの相対値でGPIOを指定する、というやり方で行います。

次の図が PIO の全体構成図です。これは二つあるPIOの一つを表しており、4つのステートマシンと、それぞれについて入力と出力のFIFOが繋がっています。

ステートマシンのためのプログラムは「インストラクションメモリ」に入っています。 インストラクションメモリは32ワードで、4つのステートマシンが共有しています。各ステートマシンに別個の部分を実行させる(受信のコードと送信のコードというように)こともできますし、同一のコードを実行させることもできます。

最大で32ステップのソフトウェアというと、とても短いように思われるかもしれませんが、プロトコルの実装というのは、パラレルデータをシリアルデータに変換する比較的単純なループ処理がほとんどです。PIOを使って、I2SやSDIOのほか、VGA(抵抗器でDACを作れば)なども実装することができるそうです。

次の図はステートマシンの中身です。入力と出力のシフトレジスタ、汎用のスクラッチレジスターXとY、プログラムカウンタ、動作速度を制御するためのクロックディバイダ、それに命令を実行するコントロールロジックが入っています。

利用できる命令は次の9つです。

JMP  (条件)分岐
WAIT 一定の条件が成立するまで停止
IN   受信シフトレジスタへビット列を読み込む
OUT  送信シフトレジスタからビット列を書き出す
PUSH 受信シフトレジスタから受信FIFOへデータを読み出す
PULL 送信FIFOから送信シフトレジスタへデータを書き出す
MOV  レジスタ等の間でデータをコピーする
IRQ  割り込みフラグの読み書き
SET  GPIOピンやスクラッチレジスタに即値を書き込む

一般的な CPU と違い、データを読み書きするためのメモリ空間は無く、読み書きの対象はレジスタやGPIOの値になります。次の図はFIFOとシフトレジスタとGPIOの関係を表したものです。

また、命令は基本的に1サイクルで実行されますが、実行時に余計にサイクルを消費させるための「delay」、および 実行時に命令のメインとなる機能と並行してGPIOを操作することができる「side-set」という属性を、すべての命令が持つことができます。delayがあるのでNOP命令は必要なく、side-setは、クロックとデータのように、複数の信号線が連携して動作する状況を効率的に制御できます。

プログラムカウンタは、アドレス31の命令を実行したらアドレス0にジャンプし、0から31までの命令をループ実行します。さらに「Program wrapping」という仕組みがあり、ループ範囲をPIOのレジスタで指定することができるようになっています。つまり、プログラムカウンタが特定の値になったら、あらかじめ指定したアドレスへ自動的にジャンプさせることができます。ステートマシンのプログラムは無限ループになるはずなので、32個しかないインストラクションメモリをJMPで消費するのを避けるための工夫だそうです。

こういった様々な機能をサポートするアセンブラ「picoasm」が用意されています。また様々なプロトコルを実装したライブラリも提供されます。

データシートには、 全二重SPIや、電子工作でおなじみのNeoPixel(WS2812)のドライバ【→こちらの記事で解説】など、様々なサンプルが掲載されています。JMP命令のside-setにある、XレジスタまたはYレジスタをデクリメントする機能を使って、ステートマシンには無い加算命令を実現するサンプルまであります。もっとも、32bitの加算をするのに1分くらいかかるそうですが。

RP2040のペリフェラルは比較的簡素(最低限と言ってもいいくらい)ですが、PIOがあることで応用範囲は大きく広がりそうです。

コメント