はじめに
前回は、Factory Codeを確認し、RP2040側の main.py を編集してLチカ速度を変えました。
今回は、もう一つのLED、つまりFPGA側に接続されているLEDの点滅速度を変えてみます。
RP2040側では、MicroPythonスクリプトを変更して実行すればすぐに動作が変わりました。
一方、FPGA側では、Verilogコードを変更したあと、合成してbitstreamを生成し、Shrike-Liteへ書き込む必要があります。
さらに、FPGAではVerilog上の出力信号が、実機上のどのピンに出ているのかも確認する必要があります。
今回は、このFPGA側の開発ループを一周してみます。
今回やること
今回の流れは以下です。
Verilogを変更し、そのLED信号のピン割り当てを確認して、合成とbitstream生成を行い、実機上のFPGAの動作が変わることを確認するところまでをゴールとします。
Factory CodeとFPGA側ソースの関係
前回確認したように、Shrike-LiteをPCに接続すると、USBドライブ上には次のようなファイルが見えました。
USBドライブ
├── blink_all.bin
└── main.py
main.py はRP2040側で動くMicroPythonコードです。
blink_all.bin はFPGAに書き込まれているbitstreamです。
USBドライブ内には、FPGA側のVerilogソース一式が入っていないようです。
そこで今回は、Shrike公式GitHubリポジトリの examples/led_blink に公開されているLチカサンプルを利用します。
FPGA側のLチカ速度を変える
ここからがFPGAらしい作業です。
FPGA側のコードを確認する
今回利用するサンプルプロジェクトは以下です。
examples/led_blink
主に確認するVerilogファイルは以下です。
examples/led_blink/ffpga/src/main.v
このファイルには、FPGA側LEDを制御するためのトップモジュールが書かれています。
(* top *) module blink(
(* iopad_external_pin, clkbuf_inhibit *) input clk,
(* iopad_external_pin *) output LED,
(* iopad_external_pin *) output LED_en,
(* iopad_external_pin *) output clk_en
);
どうやらこの回路では入力が1本 (clk)、出力が3本 (LED, LED_en, clk_en) 準備されているようですね。
そのうち出力の2本 (LED_en, clk_en) は '1' に固定されています。
assign LED_en = 1'b1;
assign clk_en = 1'b1;
LED出力は内部で LED_status と接続されています。LED出力を変えるにはLED_statusを変えればいいですね。
assign LED = LED_status;
Lチカ速度は、カウンタの条件比較値で決めているようです。
クロックごとに counter をカウントアップし、counter が5000万になったら LED_status を反転させ、counter をゼロに戻しています。
if (counter == 50_000_000) begin
LED_status <= !LED_status;
counter <= 32'b0;
end
このサンプルではFPGA内部の50MHzクロックを使っているので、5000万カウントで約1秒です。
そのため、LED_status は約1秒ごとに反転します。
LEDのON/OFFが1秒ごとに切り替わるので、ONから次のONまでの周期としては約2秒になります。
この値を次の節で変更して、点滅速度を変えてみます。
Verilogを変更する
FPGA側のコードを確認したら、点滅速度に関係する値を変更します。
今回は、カウンタの比較値を変更します。
変更前:
if (counter == 50_000_000) begin
LED_status <= !LED_status;
counter <= 32'b0;
end
変更後:
if (counter == 10_000_000) begin
LED_status <= !LED_status;
counter <= 32'b0;
end
値を 10_000_000 にすると、約0.2秒ごとに反転するため、かなり速く点滅するはずです。
変更したら、ファイルを保存します。
ForgeFPGA Workshopで開く
Verilogの変更を保存したら、ForgeFPGA Workshopを起動し、Lチカサンプルのプロジェクトを開きます。
led_blink.ffpga ファイルをダブルクリックしてください。
examples/led_blink/led_blink.ffpga
開いたら、画面上部ツールバーの FPGA Editor をクリックします。
変更対象の main.v ファイルがプロジェクトに含まれていることを確認します。
LED信号のピン割り当てを確認する
Verilogを変更したら、合成へ進む前に、LED 信号のピン割り当てを確認します。
FPGAで実機を動かすときには、Verilog上の入出力信号と、FPGAの物理ピンの対応を確認する必要があります。
マイコンのLチカでは、プログラム上でGPIO番号を指定しますが、FPGAは少し方法が違います。
FPGA側では、Verilog上の出力信号を、プロジェクトに含まれるIO設定でFPGAピンへ割り当てます。
今回の led_blink サンプルでは、外部にジャンパ線をつなぐ必要はありません。
Shrike-Lite上のFPGA側LEDを使います。
公式READMEでは、このサンプルはFPGAのGPIO 16に接続されたオンボードLEDを使うものとして説明されています。
Verilog上の LED 出力
↓
FPGAのGPIO 16
↓
Shrike-Lite上のFPGA側LED
ForgeFPGA Workshopでプロジェクトを開いたら、FPGA Editorの IO Planner でピン割り当てを確認します。
画面上部の IO Planner ボタンをクリックするか、IO Planner タブを開いてください。
確認するポイントは、次のような対応です。
OSC_EN (Output) ⇔ clk_en
OSC_CLK (Input) ⇔ clk
GPIO16_OE [PIN 7] (Output) ⇔ LED_en
GPIO16_OUT [PIN 7] (Output) ⇔ LED
OSC_EN は、SLG47910内蔵の50MHz発振器を有効にするための信号です。
サンプルでは、Verilog上の clk_en を 1 に固定して、内蔵OSCを有効にしています。
OSC_CLK は、FPGAに内蔵の50MHz OSCから出力されるクロック信号です。
この信号がVerilog上の clk 入力につながり、Lチカ用カウンタを動かすクロックとして使われています。
GPIO16_OUT はGPIO16へ出す値で、Verilog上の LED 信号につながっています。
GPIO16_OE はGPIO16の出力有効化に相当する信号で、Verilog上の LED_en 信号につながっています。
合成する
Verilogの変更と LED 信号のピン割り当て確認が終わったら、ForgeFPGA Workshopで合成します。
ForgeFPGA Workshop画面の左下にある Synthesize ボタンを押すと合成が始まります。
合成が成功すると、Synthesize ボタン右側のステータスが緑になります。
合成時には、warningが出ることがあります。
手元の環境での合成では次のような warning が出ました。
WARNING (Synthesis): The network is combinational (run "fraig" or "fraig_sweep").
この連載では、まず動作確認を優先する方針にします。
明らかにエラーで止まっていなければ、warningはメモしておき、必要になったら後で調べることにします。
bitstreamを生成する
合成が通ったら、次はbitstreamを生成します。
bitstreamは、FPGAに書き込む回路構成ファイルです。
ForgeFPGA Workshop画面の左下にある Generate Bitstream ボタンを押すと生成が始まります。
生成が成功すると、Generate Bitstream ボタン右側のステータスが緑になります。
合成の時と同様に warning 表示が出ますが、このまま進みます。
生成が終わると、ForgeFPGA Workshop画面の左上にFPGAの利用リソースレポートが表示されます。
これは合成されたbitstreamを書き込むと、FPGAの内部リソースをどのぐらい使うかのレポートです。
簡単なLチカ回路でも、カウンタや入出力まわりである程度のリソースを使うことがわかりますね。
さて、生成されたbitstreamは、以下のフォルダに入っています。
led_blink\ffpga\build\bitstream
似たようなファイルが並んでいますが、今回はShrike-Lite上のRP2040から書き込むため、FPGA_bitstream_MCU.bin を使います。
このファイルを led_blink\bitstream フォルダにコピーして、分かりやすいように led_blink_fast.bin にリネームしておきます。
Shrike-Liteへ書き込んで実行
さて、いよいよ生成したbitstreamをShrike-Liteへ書き込んで動作を確認します。
Shrike USBドライブに生成したled_blink_fast.binをコピー
↓
ThonnyからMicroPythonでFPGAへの書き込みを実行
↓
書き込みが完了するとFPGA側のLED点滅速度が変わるので確認
コピーが済んだらいよいよFPGAへのbitstream書き込みです。
書き込みはThonnyのシェルから2行だけコマンドを送れば実行されます。
Thonny MicroPython実行例:
import shrike
shrike.flash("led_blink_fast.bin")
以下のような書き込みログが表示されれば書き込みは成功です。
実行ログ:
[shrike_flash] FPGA reset done
[shrike_fpga] Starting FPGA flash...
[shrike_fpga] flashing: led_blink_fast.bin
[shrike_flash] FPGA programming done.
書き込みが完了したら、Shrike-Lite上のFPGA側LEDの点滅速度を確認します。
FPGA側のLED点滅速度も速くなっているはずです。
これで、FPGA側の開発ループも一周できました。
RP2040側とFPGA側の違い
前回と今回で、RP2040側とFPGA側の両方でLチカ速度を変えてみました。
同じLチカでも、手順はかなり違います。
| 項目 | RP2040側 | FPGA側 |
|---|---|---|
| 記述するもの | MicroPython | Verilog |
| 実行方法 | Thonnyからスクリプト実行 | 合成してbitstreamを書き込み |
| 変更の反映 | 比較的すぐ反映 | 合成・生成・書き込みが必要 |
| 実機との接続 | GPIOをプログラム側で指定する | Verilog信号とFPGAピンの割り当てを確認する |
| 動作のイメージ | プログラムが行ごとに順番に実行される | 回路全体がクロックに従って動く |
RP2040側は、普通のマイコン開発に近い感覚です。
FPGA側は、コードを書いて終わりではなく、合成して回路構成として書き込む必要があります。
さらに、Verilog上の信号が実機のどのピンにつながっているかを確認する必要があります。
この手間はありますが、bitstreamを書き込んだFPGAは「あなただけのカスタムメイドIC」になりました。
次回
前回と今回で気づいた方もいらっしゃると思いますが、RP2040側のLEDとFPGA側のLEDの点滅タイミングはバラバラです。
これは、同じShrike-Lite基板に載っていても、別々のICがそれぞれ独立して点滅タイミングを制御していることが原因です。
次回は、FPGA側LEDをRP2040側LEDのON/OFF状態に連動させるために、RP2040からFPGAに点灯/消灯のステータス信号を送ってみます。
具体的にはShrike-Liteの回路図を見て、RP2040とFPGAの間につながっているGPIO線を確認し、そのうえで、RP2040側のON/OFF状態を1本のGPIO線でFPGA側へ渡し、FPGA側LEDの制御に使ってみます。
SPIサンプルに進む前に、まずは1bitの信号を二つのIC間で受け渡す仕組みを試してみましょう。お楽しみに。
















