0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Shrike-LiteでAtCoder問題を解く(27):4bitパラレル通信を作る(1)

0
Last updated at Posted at 2026-08-04

はじめに

これまでの連載では、RP2040とFPGAの間の通信にSPIを使ってきました。

SPI通信は扱いやすく、Shrike-Liteにも最初から利用できる仕組みが用意されています。

連載記事の第15回では、MicroPython側の送受信バッファを再利用し、複数byteをまとめて転送しました。

さらにSPIクロックを4MHzまで上げ、現在の構成で安定して利用できる範囲まで通信帯域を広げました。

Shrike-LiteでAtCoder問題を解く(15):RP2040側でSPI転送を高速化する

しかし、ABC469Cのように、RP2040とFPGAの間で大量のデータを受け渡す問題では、まだSPI転送がボトルネックになる可能性があります。

そこで今回は、SPIとは別の通信方式として、4bitパラレル転送を実装してみます。

利用するのは、Shrike-Lite基板上でRP2040とFPGAを接続している4bitの信号線です。

いつも通り、ジャンパ線や外付け回路は使いません。

4bitなら速いのか

SPIは、基本的にデータを1bitずつ転送します。

これに対して4bitパラレル通信なら、1回の転送で4bitを送れます。

同じ速さで信号を切り替えられるなら、単純にはSPIより多くのデータを転送できそうです。

しかし、実際の通信速度は配線の本数だけでは決まりません。

RP2040には内蔵のSPI通信用ハードウェアが組み込まれています。

MicroPythonにも、このSPIハードウェアを利用するためのAPIがあります。

SPIでは、専用ハードウェアとMicroPython APIをそのまま利用するだけで、FIFOやバースト転送を含む最適化済みの通信機構が働きます。

4MHzというクロックだけを見る以上に、初めから得られる実効性能は高いのです。

これに対して、今回の4bitパラレル通信はほぼイチからの自作です。

データ線を4本用意しただけでは通信できません。

通信開始の合図、クロック生成、通信方向の切り替え、byteと4bitデータの変換なども、自分で用意する必要があります。

4bitパラレル通信というアイデアは成立しそうですが、本当に4MHz SPIを超える通信帯域を確保できるかは、実際に作って測定するまで分かりません。

4本のDATA線を交互に使う

Shrike-Liteには、RP2040とFPGAを接続する4bitの信号線があります。

これを送信用と受信用に2本ずつ分けると、一方向あたり2bitしか使えません。

そこで、同じ4本のDATA線を送信と受信で交互に使う半二重通信にしました。

主な信号は次の三つです。

信号 役割
DATA 4bitのデータを転送する
REQ RP2040から通信開始を要求する
CLK 4bitデータを受け渡すタイミングを示す

最初にRP2040がREQを送ります。

その後、DATA線をRP2040からFPGAへの送信に使います。

要求データの送信が終わるとDATA線の方向を切り替え、今度はFPGAからRP2040へ応答を返します。

同じDATA線を双方が同時に駆動すると問題になるため、方向切り替えの間には、どちらもDATA線を駆動しない期間を設けます。

詳細なクロック波形や各信号の切り替え条件については、今回は省略します。

重要なのは、

4本のDATA線を、要求と応答で交互に使う

という基本方針です。

RP2040のPIOを使う

MicroPythonからGPIOを直接操作する方法でも、通信そのものは実現できます。

ただし、Pythonコードで、

  1. DATA線へ値を設定する
  2. CLKを切り替える
  3. 次の4bitを用意する
  4. 再びCLKを切り替える

という処理を繰り返すと、Python側の処理時間が大きくなります。

これでは、せっかくDATA線を4本にしても、十分な通信帯域を得られません。

そこで利用するのが、RP2040のPIOです。

PIOは、小さな専用プログラムによってGPIOを自動制御できる仕組みです。

CPUがGPIOを1回ずつ操作しなくても、PIO State Machineが一定のタイミングでDATA線やCLKを切り替えられます。

さらに、PIOのFIFOとメモリの間のデータ転送にはDMAを利用できます。

役割分担は、おおまかに次のようになります。

処理 担当
packetの準備と通信開始 MicroPython
DATAとCLKの制御 PIO
メモリとPIO FIFO間の転送 DMA
データ受信と応答生成 FPGA

SPIとは違い、RP2040には、4bitパラレル通信専用の周辺回路があるわけではありません。

しかし、PIOとDMAという汎用ハードウェアを組み合わせれば、自作の通信エンジンとして利用できそうです。

実装Phase 1からPhase 4まで

最初からPIOとDMAを組み合わせたわけではありません。

通信方式を確認しながら、少しずつ構成を変更しました。

Phase 試したこと 確認したこと
Phase 1 MicroPythonからGPIOを直接操作 4bit半二重通信の基本動作
Phase 2 複数byteのburst転送 packet単位の連続送受信
Phase 3 PIOを使った信号制御 GPIO操作をハードウェアへ移せること
Phase 4 PIOとDMAを組み合わせる MicroPythonから連続通信できること

それぞれのコードはこの記事には掲載しません。

興味がある方は以下を覗いてみてください。

第27回コード全文

Phase 1:まずはGPIOで動かす

最初は、MicroPythonからGPIOを直接操作しました。

速度よりも、REQ、CLK、DATAによる半二重通信そのものが成立するかを確認するためです。

FPGAへデータを送り、FPGA側で処理した結果を同じ4本のDATA線から受け取ります。

通信方向の切り替えや、双方がDATA線を駆動しない時間も、この段階で確認しました。

Phase 2:複数byteをまとめて送る

次に、1byteだけではなく、複数byteを連続して送受信できるようにしました。

packetの内容をFPGA側のメモリへ保存し、その後、結果をまとめて返します。

32byteのpacketを1000回繰り返す試験も行い、連続通信できることを確認しました。

Phase 3:PIOへ移す

GPIOをMicroPythonから直接操作する構成では、Python側の処理が通信速度を制限します。

そこで、DATA線とCLKの制御をPIOへ移しました。

RP2040のCPUはpacketの準備と開始を担当し、実際の信号生成はPIOへ任せます。

この段階で、RP2040側のPIOとFPGA側の内部クロックが異なることも考慮する必要がありました。

Phase 4:DMAを組み合わせる

最後に、PIOのFIFOとメモリ間の転送へDMAを使いました。

MicroPythonから1個ずつデータをPIOへ渡すのではなく、送信データをメモリへ準備し、その後の転送をDMAへ任せます。

受信側も同様に、PIOが取り込んだデータをDMAでメモリへ保存します。

これにより、

  • MicroPython
  • PIO
  • DMA
  • FPGA

を組み合わせた4bit半二重通信が実機で動作しました。

速度も測ってみる

各Phaseでは、動作確認だけでなく処理時間も測定しました。

32byteのpacketを1000回送受信した結果は、次のようになりました。

Phase 構成 処理時間
Phase 2 GPIOによるburst転送 約28.3秒
Phase 3 PIO+MicroPythonによるFIFO操作 約12.3秒
Phase 4 PIO+DMA 約12.7秒

GPIO操作をPIOへ移したPhase 3では、Phase 2の約2.3倍まで高速化しました。

しかし、パラレル通信用クロックを100kHzから4MHzまで上げても、通信全体の処理時間はほとんど変わりませんでした。

PIOが信号を高速に生成できても、MicroPythonからPIOのFIFOへデータを出し入れする処理がボトルネックになっていたためです。

そこでPhase 4では、PIOのFIFOとメモリ間の転送をDMAへ任せました。

その結果、DMAが実際にデータを転送している区間だけを見ると、約9.19Mbpsまで高速化できました。

参考として、これまで使用してきた4MHz SPIは、1byteあたり約2.5µs、通信速度に換算すると約3.2Mbpsです。

測定条件は同じではありませんが、PIO+DMAによる通信区間だけなら、4MHz SPIの約3倍に相当する速度が出ています。

ところが、256byteのpacketを1000回処理した通信全体では、約66.8kbpsに留まりました。これは4MHz SPIのおよそ48分の1です。

DMAによる実データ転送が占める時間は通信全体の1%未満で、残りの99%以上が、packetの準備、byteと4bitデータの変換、DMAの設定、応答の確認などに使われていました。

つまり、4bitの通信路そのものは十分に高速化できましたが、その前後に残っているMicroPythonの処理が、通信全体の性能をほぼ決めていたのです。

MicroPython版が完成した

Phase 4では、MicroPythonからPIOとDMAを設定し、4bitパラレル通信を実行しています。

この時点で、目標としていた通信方式そのものは完成しました。

Shrike-Liteの既設配線だけを使い、RP2040からFPGAへpacketを送り、FPGAから応答を受け取れます。

複数packetの連続試験も通りました。

しかし、MicroPython版のままでは、4MHz SPIと勝負できる通信速度にはまったく届きませんでした。

一方で、PIOとDMAによる通信区間の速度測定では、4bitパラレル通信に十分なポテンシャルがあることも確認できました。

次に行うべきことは、MicroPython側に残っている処理を減らすことです。

本当の勝負はここからです。

C SDKへ移れば素直だが

PIOとDMAを本格的に利用するなら、RP2040のC SDKで専用アプリケーションを作る方法があります。

CからPIOやDMAを直接制御すれば、MicroPythonよりも細かい処理を効率よく実装できます。

しかし、この連載では最初に、

Shrike-LiteはThonnyとMicroPythonから操作する

という縛りルールを決めています。

ここでC SDKの専用アプリケーションへ移行すると、このルールから外れてしまいます。

自分で決めたルールですが、守ることにします。

そこで、ユーザーから見える操作方法はMicroPythonのまま残し、高速化が必要な部分だけをCへ移します。

Cで作成した処理は、MicroPythonのUser C Moduleとしてfirmwareへ組み込みます。

そのfirmwareをUF2としてShrike-Liteへ書き込み、Thonnyから通常のMicroPython moduleとして呼び出します。

つまり、

C SDKのアプリケーションへ置き換えるのではなく、MicroPythonの内部へCで書いた専用通信エンジンを追加する

という方針です。

これなら、ThonnyとMicroPythonから操作するという連載のルールを維持できます。

今回のまとめ

今回は、Shrike-Lite基板上の4bit配線を使った半二重通信を設計し、PIOとDMAを組み合わせたMicroPython版まで実装しました。

PIOとDMAによる通信区間は高速化できましたが、MicroPython側の処理を含む通信全体では、まだ4MHz SPIにまったく歯が立ちません。

一方で、ボトルネックが4bitの信号線ではなく、その前後のMicroPython処理にあることも分かりました。

今後、これらの処理を段階的にCへ移し、最終的にSPIを超えられるか確認します。

次回

次回は、公開されているMicroPythonのソースコードから、Shrike-Lite用firmwareを再構築します。

clean環境でfirmwareをbuildし、User C Moduleを追加します。

その後、データのpack/unpackとDMA処理を段階的にCへ移し、MicroPython版との性能差を確認します。

4bitパラレル通信は、本当に4MHz SPIを超えられるのでしょうか。

お楽しみに。


前回:
Shrike-LiteでAtCoder問題を解く(26):Interlude - ABC469の各問題をFPGA目線で見てみる

次回:
Shrike-LiteでAtCoder問題を解く(28):4bitパラレル通信を作る(2) - Cで高速化する

今回の全コード:
第27回コード全文

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?