Helioボード: プッシュボタンの内部バス幅を2→3ビットに拡張する
はじめに
Helioボードのリファレンスデザインには、FPGA側のプッシュボタンが押されたらコンソールにそれを通知するサンプルが同梱されています。しかし、実は、Helioボードに実装されている3つのプッシュボタンのうち1つが、このサンプルでは反応してくれません。今回は、その原因を究明し、解決してみます。
今回のハイライト: Quartus IIのRTL ViewerでFPAG内部の結線状況を確認している様子です。これから赤線の一部のバス幅を2ビットから3ビットに拡張します。
プッシュボタンの割り込み検出サンプル
HelioボードのLinuxを起動して、rootユーザーでログインすると、ホームディレクトリにREADMEというファイルがあります。 このREADMEにリファレンスデザインで提供されている各種サンプルの動かし方が説明されています。
root@socfpga:~# cat README Table of Contents ================= 1. Soft IP Driver Example 2. Application Examples 1. Soft IP Driver Example ========================= GPIO driver for soft PIO in the FPGA domain serves as a reference for writing a simple driver in Linux. The source code of this driver is located in ...
今回は試すサンプルは、「Application to register interrupt and write simple interrupt service routine」のセクションに書かれているものです。
READMEに従い、GPIOを確認してみると、以下が認識されています。
root@socfpga:~# ls -l /sys/class/gpio/ --w------- 1 root root 4096 Jan 17 22:10 export lrwxrwxrwx 1 root root 0 Jan 17 22:10 gpiochip149 -> ../../devices/virtual/gpio/gpiochip149 lrwxrwxrwx 1 root root 0 Jan 17 22:10 gpiochip152 -> ../../devices/virtual/gpio/gpiochip152 lrwxrwxrwx 1 root root 0 Jan 17 22:10 gpiochip156 -> ../../devices/virtual/gpio/gpiochip156 lrwxrwxrwx 1 root root 0 Jan 17 22:10 gpiochip160 -> ../../devices/virtual/gpio/gpiochip160 lrwxrwxrwx 1 root root 0 Jan 17 22:10 gpiochip192 -> ../../devices/virtual/gpio/gpiochip192 lrwxrwxrwx 1 root root 0 Jan 17 22:10 gpiochip224 -> ../../devices/virtual/gpio/gpiochip224 --w------- 1 root root 4096 Jan 17 22:10 unexport
試しにgpiochip149のlabelを確認してみます。「gpio@0x1000100C0」は、button_pioモジュールのBaseアドレス(Qsysで確認できます)に一致しています。つまり、このgpiochip149がFPGA側のプッシュボタンに対応しています。
root@socfpga:~/altera# cat /sys/class/gpio/gpiochip149/label /sopc@0/bridge@0xc0000000/gpio@0x1000100C0
READMEの説明に従い、gpio_interruptモジュールをカーネルにロードします。先ほどGPIOの149番がプッシュボタンに対応しているとわかったので、gpio_numberには149を指定します。
root@socfpga:~# modprobe gpio_interrupt gpio_number=149
プッシュボタン(SW-11)を押してみます。すると、ターミナルに「Interrupt happened at gpio:149」と表示されました。
同様に、2つ目のプッシュボタン(SW-12)でも試してみます。READMEの説明にあるように、gpio_interruptをアンロードしてから、再度モジュールをロードします。ここではgpio_numberに149+1=150を指定します。これも動作しました。
root@socfpga:~# rmmod gpio_interrupt root@socfpga:~# modprobe gpio_interrupt gpio_number=150
この調子で、3つ目のプッシュボタン(SW-13)を試してみると、残念ながら、何も応答がありません。
原因の究明
Qsysでbutton_pioの設定を確認してみます。すると、パラレルI/OのWidthが2ビットになっていることに気づきます。プッシュボタンは3つあるのに、Widthが2ビットということは、3つあるプッシュボタンのどれかの1つの信号がHPS(ARMプロセッサ)に到達していないということになります。
ピン割付を確認してみます。信号線fpga_button_pioは3ビットのバスになっています。また、念のため、HelioボードのReference Manualの「4.7.1 User-Defined push button」を確認してみても、正しく接続されていることがわかります。ということは、ピン割付には問題はなく、すべてのプッシュボタンの信号はFPGAに接続されているということになります。
先ほどピン割付で表示されていた信号線fpga_button_pioからHPS(ARMプロセッサ)までの結線を確認していきます。Quartus IIのTools -> Netlist Viewers -> RTL Viewerを選択します。
結線を良く見ると、fpga_button_pio[2..0]がdebounce_instのdata_in[1..0]に接続されています。fpga_button_pioが3ビットなのに対し、data_inは2ビットのバスです。つまり、ここでプッシュボタンの信号がロストしていたわけです。
ちなみに、このdebounce_instモジュールは、スイッチをON/OFF時に発生するチャタリング対策のためのものです。(チャタリング対策は、ソフトウェア側でもできますが、リファレンスデザインではFPGA側でやっているということになります。)
また、以降に結線されている、data_out[1..0]とbutton_pio_external_connection[1..0]のいずれも2ビットのバスです。これらも3つのプッシュボタンの信号を伝達するには3ビットのバスが必要です。
button_pio_external_connectionはbutton_pioのパラレルI/Oポートですので、以上でプッシュボタンからbutton_pioまでの結線状況の確認は完了です。
まとめると、button_pioとdebouceフィルタの入出力のバス幅が2ビットになっているため、3つのプッシュボタンを動作させるには、これらのバス幅を3ビットに拡張が必要ということになります。
解決
button_pioのパラレルI/OのWidthを2ビットから3ビットに拡張します。
HDLを生成します。(Qsysのメニュー Generate -> Generate HDL)
button_pio_external_coneection_exportのバス幅が3ビットになったことを確認します。
helio_ghrd_top.vを開き、関連する箇所のバス幅を変更します。変更点は2点です。
1つ目は、debounceフィルター後の信号線fpga_debounced_buttonsです。バス幅を3ビットに拡張します。
// wire [1:0] fpga_debounced_buttons; // 変更前 wire [2:0] fpga_debounced_buttons; // 変更後
2つ目は、debounceフィルターです。debounceモジュール内部のパラメータWIDTHを3ビットに拡張します。
// Debounce logic to clean out glitches within 1ms debounce debounce_inst ( .clk (fpga_clk_50), .reset_n (hps_fpga_reset_n), .data_in (fpga_button_pio), .data_out (fpga_debounced_buttons) ); // defparam debounce_inst.WIDTH = 2; // 変更前 defparam debounce_inst.WIDTH = 3; // 変更後
コンパイルします。
コンパイルが終わったら、RTL Viewerを開きます。バス幅が3ビットに拡張されているのを確認できます。
もうほぼ終わりです。後は、FPGAに書き込み、Linuxを起動後、以下のコマンドで3つ目のプッシュボタン(SW-13)に対応するGPIOを指定すると、無事3つ目のプッシュボタンも反応するようになります。
root@socfpga:~# modprobe gpio_interrupt gpio_number=151
サンプルのソースコードとモジュールのデプロイ先
問題は解決しました。しかし、gpio_interruptのソースコードのありかとmodprobeした時にどこからロードされているのか気になったので調べました。
まず、ソースコードですが、前回Linuxカーネルを自前でビルドするのに使ったビルドマシン(Ubuntu)にありました。具体的には、~/yocto/meta-altera/recipes-gsrd/pio-interrupt-altera/ です。Makefileを見ると、yoctoでgpio_interruptをビルドする時に、カーネルモジュールとしてデプロイされるように記述されています。
次に、gpio_interruptモジュールのデプロイ先ですが、gpio_interruptをmodprobeした時のシステムコールをstraceで解析してみると、gpio_interruptの実体が見つかりました。modprobe gpio_interruptすると、裏ではこのgpio_interrupt.koがロードされている、ということになります。
root@socfpga:~# ls -l /lib/modules/3.9.0/extra/ -rw-r--r-- 1 root root 4472 Jan 17 16:04 gpio_interrupt.ko
おわりに
原因を特定して解決できた時は、ハードウェアでもソフトウェアと同じで、とても楽しいです。 今回はじめてGPIOに入門したので、今後、もう少し踏み込んでみたいです。