はじめに
前回は、ForgeFPGA Workshopを使ってFPGA側のLチカ速度を変えました。
Verilogを変更して、合成して、bitstreamを生成し、Shrike-Liteへ書き込むところまで確認しました。
今回は、もう少しだけ自力実装に近づきます。
Shrike-Liteには、RP2040とFPGAが同じ基板上に載っています。
ただし、同じ基板上に載っているからといって、RP2040とFPGAが自動的に情報を共有できるわけではありません。
RP2040側の状態をFPGA側へ渡すには、基板上で実際につながっている信号線を使う必要があります。
今回は回路図を見て、RP2040からFPGAへつながっているGPIO線を確認し、そのうち1本を使ってON/OFF状態を渡してみます。
やることは、かなり単純です。
RP2040側のGPIOをON/OFFし、その1bit信号をFPGA側で受け取り、FPGA側LEDにそのまま出します。そうすれば二つの青LEDが同時に点滅するように見えるはずです。
今回のFPGAの役割をそれっぽく言えば「1bitバッファ」です。
今回やること
今回の流れは以下です。
今回のゴールは、RP2040側のON/OFF状態をFPGA側へ1bit信号として渡し、FPGA側LEDに反映することです。
RP2040とFPGAの間にある信号線を、自分で選んで、自分で使ってみましょう。
回路図を見て接続線を探す
まず、Shrike-Liteの回路図を確認します。
Shrike-Liteの回路図は、Shrike公式GitHubリポジトリで公開されています。
今回は、以下のPDFを参照します。
PDFを開き、RP2040とFPGAの間がどのようにつながっているかを確認します。
回路図を見ると、RP2040のGPIOとFPGA側GPIOの間に、0Ω抵抗を介した接続があります。
今回注目するのは、次の接続です。
RP2040 GPIO15 ─ R14 0Ω ─ FPGA F_GPIO17
つまり、RP2040の GPIO15 をON/OFFすると、その信号がFPGA側の F_GPIO17 に入るはずです。
今回はこの線を使います。
ForgeFPGA Workshopで新規プロジェクトを作る
前回は、Shrike公式GitHubリポジトリにある既存サンプル examples/led_blink を変更しました。
今回は、ForgeFPGA Workshopで新規プロジェクトを作ってみます。
新規プロジェクトの作成方法は、Shrike公式GitHubリポジトリの Generating your first bitstream に掲載されています。
ただし、Go Configure Software Hubのバージョンアップによって、少し操作が変わっているようです。
手元の Go Configure Software Hub v6.54 で、ForgeFPGA Workshop の Project settings 画面を開くところまで紹介します。
まず Go Configure Software Hubを起動します。ページの上の方にStart New Projectボタンがあるのでクリックします。
Select part number画面が表示されるので SLG47910V (Rev BB) を選択します。
New project画面が表示されるので、プロジェクト名とフォルダの選択をします。
今回のプロジェクト名は、1bit_buffにしました。
次はProject settings画面が出ます。ここはShrike公式の資料に合わせ次のような設定にしてからOKボタンをクリックします。
これで無事にForgeFPGA Workshop画面が立ち上がるはずです。
FPGA側Verilogを書く
今回作成する回路はとてもシンプルです。
FPGA側でやることは、次の1つだけです。
FPGA側入力 RP_in を FPGA側LED出力 LED へそのまま接続する
FPGA側のVerilogは以下です。
(* top *) module main(
(* iopad_external_pin *) output LED,
(* iopad_external_pin *) output LED_en,
(* iopad_external_pin *) input RP_in
);
assign LED_en = 1'b1;
assign LED = RP_in;
endmodule
ForgeFPGA用のVerilogの書き方については、Shrike公式の Verilog style guide が参考になります。
一読しておくと良いかもしれません。
コードがやっていることは単純です。
LED_en はLED出力を常時有効にするため、1'b1 に固定します。
assign LED_en = 1'b1;
そして、RP2040から受け取った入力 RP_in を、そのままLED出力 LED に接続します。
assign LED = RP_in;
今回はクロックを使いません。カウンタも状態機械も使いません。
RP2040側のGPIO状態を、FPGA側LEDへそのまま渡すだけです。
IO Plannerでピン割り当てする
次に、IO PlannerでVerilog上の信号と、FPGA側の実際のピンを対応させます。
今回の対応は以下です。
| Verilog上の信号 | 割り当て先 |
|---|---|
LED |
GPIO16_OUT |
LED_en |
GPIO16_OE |
RP_in |
GPIO17_IN |
LED と LED_en は、前回のFPGA側Lチカでも確認したLED用の信号です。
今回新しく使うのは RP_in です。
回路図上でRP2040の GPIO15 がFPGAの F_GPIO17 につながっているので、Verilog上の RP_in を GPIO17_IN に割り当てます。
似たような名前が並んでいますので設定後はよく確認したほうがいいですね。
合成してbitstreamを生成する
Verilogを書き、IO Plannerでピン割り当てを設定したら、合成してbitstreamを生成します。
今回の回路は、入力をそのまま出力へ渡すだけです。
そのため、合成時に組み合わせ回路に関するwarningが出るかもしれません。私の環境でもいくつかのwarningが出ました。
しかし今回も 0 errors でbitstream生成まで進めば、そのまま実機で確認することにします。
生成されたbitstreamは、Shrike-Lite側から読み込める場所に置きます。
今回は、生成された FPGA_bitstream_MCU.bin を 1bit_buff.bin にリネームして、Shrike-Liteへコピーしました。
RP2040側のmain.pyを実験用に置き換える
FPGA側のbitstreamが準備できたので、次にRP2040側の main.py を実験用に置き換えます。
ここで、Factory Codeの main.py に注意が必要です。
RP2040では、main.py が起動時に自動実行されます。
Factory Codeの main.py の中には blink_all.bin をFPGAへ書き込む命令が含まれています。
つまり、自作したbitstreamを書き込んだつもりでも、電源を入れ直すと、FPGAに blink_all.bin をまた書き込んでしまいます。
今回は自作したFPGA bitstreamを使うため、Factory Codeの main.py はPC側に退避しておき、Shrike-Lite上の main.py を実験用コードに置き換えることにします。
Factory Codeの main.py では、FPGAへ blink_all.bin を書き込んだあと、複数のGPIOをまとめてON/OFFしています。
手元で確認した main.py には、次のようなGPIOリストがありました。
led_pins = [4, 5, 6, 7, 8, 9, 10,
11, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
この中には、今回FPGAとの接続に使いたい GPIO15 が含まれています。また、回路図を見ると基板上の青LEDは GPIO4 がドライブしています。
なので、二つのLEDのON/OFFを行うled_pinsには4と15を設定すればよさそうです。
今回はShrike-Lite上の main.py を次の内容にします。
from machine import Pin
import time
import shrike
shrike.reset()
shrike.flash("1bit_buff.bin")
led_pins = [4, 15]
# Initialize pins as outputs
leds = [Pin(pin, Pin.OUT) for pin in led_pins]
while True:
for led in leds:
led.value(1)
time.sleep(0.3)
for led in leds:
led.value(0)
time.sleep(0.3)
最初に、自作したbitstreamをFPGAへ書き込みます。
shrike.reset()
shrike.flash("1bit_buff.bin")
そのあと、RP2040の GPIO4 と GPIO15 を出力に設定します。
led_pins = [4, 15]
leds = [Pin(pin, Pin.OUT) for pin in led_pins]
そして、GPIO4 と GPIO15 を0.3秒ごとにON/OFFします。
while True:
for led in leds:
led.value(1)
time.sleep(0.3)
for led in leds:
led.value(0)
time.sleep(0.3)
RP2040上で実行されているMicroPythonコードが GPIO4 と GPIO15 をON/OFFするたびに、 GPIO4 に接続されているRP2040用LEDがON/OFFします。また、GPIO15 の出力信号は、ボード上の配線を通ってFPGA側の F_GPIO17 に入ることで、FPGA側LEDも同じようにON/OFFするはずです。
実機で確認する
準備ができたら、Shrike-Liteを動かします。
電源投入後、RP2040側の main.py が自動実行されます。
今回の main.py では、最初に自作bitstreamをFPGAへ書き込み、その後GPIOピンをON/OFFします。
動作の流れは以下です。
RP2040 GPIO15 <- 連動 -> RP2040 GPIO4
↓ ↓
R14 0Ω RP2040側LED
↓
FPGA F_GPIO17
↓
FPGA内の RP_in
↓
LED 出力
↓
FPGA側LED
これで、RP2040側のLED ON/OFF信号をFPGA側へ1bit信号として渡すことができました。
二つのLEDがほぼ同時に点滅を繰り返していることが観察できると思います。
今回のまとめ
今回は、RP2040からFPGAへ1bit信号を渡しました。
RP2040のGPIOをON/OFFする
↓
基板上の接続線を通す
↓
FPGA側入力で受ける
↓
FPGA側LEDへ出す
簡単、かつ一方通行ですが、二つのIC間で1bitの状態を受け渡すことができました。
これで同じ基板に載っているRP2040とFPGAを協調して動かすことができるようになりました。
次回扱うSPIサンプルも、難しい通信方式として見るよりは、この1bit信号の受け渡しの延長だと思うことにします。
線が何本かに増えて、ON/OFFの並びに意味を持たせて、読むタイミングを決める。
まずはそのくらいの気持ちで眺めればよさそうです。
また、回路図を見て、RP2040とFPGAの間につながっている信号線を探し、その線を使ってみたのは良いポイントですね。
FPGAで何かを実装するときは回路図を確認する機会が頻繁にあります。電子回路の動作理解ができなくても、どことどこがどうつながっているのかがわかるだけで、できることが広がるのが見えてきましたね。
次回
今回は、RP2040からFPGAへ1bitのON/OFF状態を渡しました。
次回は、公式SPIサンプルを使って、RP2040とFPGAの間でSPI通信によるデータ送受信を試してみます。
次回が準備編全5回の最終回の予定です。お楽しみに。
前回:準備編③:ForgeFPGA WorkshopでFPGA側のLチカ速度を変える
次回:準備編⑤:公式サンプルからSPI Slave機能を取り込み、AtCoder実装テンプレートを作る









