- 1回目: 開発環境の準備
- 2回目: Hello Worldプロジェクト
- 3回目: PSのGPIOでLチカ
- 4回目: PLのAXI GPIOでPSからLチカ <--- 今回の内容
- 5回目: PLだけでLチカ
- 6回目: 自作IPでLチカ
- 7回目: ブートイメージを作る
- 8回目: Linux起動する
- 9回目: Linuxカーネルを少しカスタマイズする
- 10回目: LinuxのRootFSをカスタマイズする / PythonでHello World
- 11回目: LinuxユーザアプリケーションでLチカ
- 12回目: LinuxカーネルモジュールでLチカ
- 13回目: LAN(Ethernet 0)を使う
- 14回目: Linuxユーザアプリをデバッグする / RootFSに取り込む
- 15回目: Linux起動時にアプリケーションを自動実行させる
- 16回目: Linuxから自作IPをUIOで制御する
- 17回目: Linuxで自作IPのデバイスドライバを作る
- 18回目: IoT化してスマホからLチカ
この記事の内容を4分で見る ( https://www.youtube.com/watch?v=WF7jMu78uwc )
環境
- 開発用PC: Windows 10 64-bit
- Vivado 2017.4 WebPACKライセンス
- Xilinx SDK 2017.4
- ターゲットボード: ZYBO (Z7-20)
Windows環境は1回目を参照。
PLのAXI GPIOでPSからLチカ
ややこしいタイトルです。前回は、PSのGPIOをPSのCPUから制御しました。今回は、PLのGPIOをPSのCPUから制御して、LEDをチカチカさせます。PLでGPIOを使うには、AXI GPIOというIPを使います。
まずVivadoでAXI GPIOをPL上に搭載して、それをPSから制御するようなソフトをSDKで実装します。
対象のLED
前回説明したように、MIOはPSからのみアクセスできます。今回はPLのGPIOなので、MIO以外のIOにつながっているLEDを使います。LD0~LD3(M14、M15、G14、D18)を使います。これは、Z7-20の場合です。他のボードだと異なるかもしれません。
ハードウェアの作成 (Vivado)
プロジェクトの作成
前回、または前々回に作成したプロジェクトをそのまま使います。ZYBO(Z7-20)ボード用のプロジェクトで、PSだけを搭載したブロックデザインが既にあるものとします。
AXI GPIOの配置
Block DesignのDiagramビューを開きます。現状、PSのみが配置されていると思います。
これに、AXI GPIOを追加します。AXI GPIOはXilinxによって用意されているIPで、AXIをインターフェースに持つGPIOです。AXI GPIOを追加して、それをLED用のIOに接続するようにします。そして、PSとAXIで接続させます。具体的には、以下のようにします。ほぼ自動で行われます。
- Diagramビュー上で右クリック->Add IP->AXI GPIOを選択
- Run Connection Automationをクリック
- 下記の設定画面で、axi_gpio_0にチェックする
- GPIOのSelect Board Part Interfaceに
leds_4bits
を選ぶ
すると、以下のように自動的に接続してくれます。また、必要なIPを自動的に追加してくれます。AXI InterconnectはAXIバスを調停してくれる人です。今回はマスター(PS)とスレーブ(AXI GPIO)が1つしかありませんが、接続されるIPが増えると重要になります。Processor System Resetは、名前通りリセット管理をしてくれるのだと思います。
また、GPIOはleds_4bits
に接続されています。leds_4bits
は外に出ているピン(Exeternal Interface)になります。プロジェクト作成時にボードを指定しているので、わざわざピン番号を指定しないでも、leds_4bits
を指定することで適切なピンに割り当てることが出来ます。
設定を確認してみる (見るだけ)
この状態で、AXI GPIOをダブルクリックすることで、詳細を確認したり設定変更が出来ます。以下のようにGPIO(省略されているが、GPIO1ということ)は、leds_4bits
に接続されています。また、今回は使用していませんが、GPIO2があることから一つのAXI GPIOで2チャネル持てることが分かります。また、図は張りませんが、隣のIP Configurationタブを見ると、GPIO Widthも自動的に4になっています。
Address Editorタブで、各IPのレジスタのアドレスを編集出来ます。ここでは確認するだけにします。これを見ると、今回作ったAXI GPIO(axi_gpio_0)のアドレスは0x41200000のようです。
ビットストリームとhdfを作る
2回目でやったように、論理合成、配置配線をして、ビットストリームとhdfを作ります。その後、hdfをエクスポートします。
- Sourcesタブでdesign_1を右クリック、Generate Output Products、その後、Create HDL Wrapper
- Flow Navigator上で、Generate Bitstream
- メニューバー -> File -> Export -> Export Hardware -> include bitstreamにチェックをつけてOK
結果を確認してみる (見るだけ)
ここまでやったら、次はSDKでソフトウェアの実装をするのですが、その前に作られたハードウェアの詳細をちょっと見てみようと思います。
Flow Navigator -> RTL ANALYSIS -> Open Elaborated Design をクリックします。すると、以下のように、Packageビューが表示されます。今回は、Block Designの所でピンアサインをしましたが、この画面から割り当てることもできます。
他にも、Schematicでブロック図を見れたり、Deviceタブでフロアマップを確認することが出来ます。
ソフトウェアの実装 (SDK)
前回からの続きでやっている場合は、Vivado上でhdfをエクスポート完了した時点で、SDK上で「更新しますか?」みたいなことを聞かれるので、OKをしてください。新しくプロジェクトを始めた場合は、VivadoからLaunch SDKしてください。これで、作成したハードウェアをベースにしたSDKプロジェクトが用意できました。
前回のPSのGPIOと同様に、レジスタ直叩きバージョンと、ドライバAPIを使うバージョンをやってみようと思います。
レジスタ直叩きでAXI GPIOを制御する
今回使用しているGPIOは、AXI GPIOというXilinxから提供されているIPです。そのため、仕様などはXilinxが提供しているドキュメントにあります。AXI GPIO v2.0 LogiCORE IP Product Guideを確認することで、レジスタオフセットアドレスや、設定すべき値が分かります。ベースアドレスは、Vivadoで自由に決めることが出来ますが、今回はVivadoが自動で割り当てたものを使用します。このベースアドレスは先ほど確認しました。
関係するレジスタを以下に挙げます。今回配置したAXI GPIOでは、Channel 1をLEDに接続しています。Channel2は未使用です。
- AXI GPIOのベースアドレス: 0x41200000 (Vivadoで確認。変更可能)
- GPIO_DATA (Channel 1 AXI GPIO Data Register)のオフセット: 0x0000
- GPIO_TRI (Channel 1 AXI GPIO 3-state Control Register.)のオフセット: 0x0004
コード
GPIO_TRIに0を設定することで、出力モードにします。GPIO_DATAに0x0F/0x00を出力することで、4つのLEDを全部On/Offさせます。ちなみに、ベースアドレスは値直打ちではなくて、xparameters.hで定義されているXPAR_AXI_GPIO_0_BASEADDRを使用すべきです。
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "sleep.h"
#define REG(address) *(volatile unsigned int*)(address)
#define GPIO_BASE (0x41200000) /* XPAR_AXI_GPIO_0_BASEADDR */
#define GPIO_DATA (GPIO_BASE + 0x0000)
#define GPIO_TRI (GPIO_BASE + 0x0004)
int main()
{
init_platform();
print("Hello World\n\r");
/* Set all of 4 pins(LEDs) as output */
REG(GPIO_TRI) = 0x00;
while(1) {
/* Set all of 4 pins(LEDs) as High */
REG(GPIO_DATA) = 0x0F;
sleep(1);
/* Set all of 4 pins(LEDs) as Low */
REG(GPIO_DATA) = 0x00;
sleep(1);
}
cleanup_platform();
return 0;
}
今回は、PLの実装もあるので、メニューバー -> Xilinx -> Program FPGAでビットストリームを書き込んでから、ソフトウェアをRunします。 すると、4つのLEDが1秒間隔でチカチカするはずです。
Xilinx SDK Driver APIでAXI GPIOを制御する
前回と同様に、ドライバAPI(関数)でAXI GPIOを制御してみます。今回のプロジェクトではAXI GPIOを使っています(前回はGPIO PSでした)。そのため、SDKでBSPプロジェクトを作った時に、gpioというライブラリも用意されています(前回はgpiops)。
コード
使い方はほとんどgpiopsと同じですが、微妙に異なります。とはいえ所詮GPIOなので、雰囲気で実装できると思います。LEDはAXI GPIOのChannel 1に接続しているので、XGpio_SetDataDirectionとXGpio_DiscreteWriteの第2引数(Channel)には1を設定しています。
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "sleep.h"
#include "xgpio.h"
int main()
{
init_platform();
print("Hello World\n\r");
XGpio instXGpio;
XGpio_Initialize(&instXGpio, XPAR_AXI_GPIO_0_DEVICE_ID);
/* Set all of 4 pins(LEDs) as output */
XGpio_SetDataDirection(&instXGpio, 1, 0);
while(1) {
/* Set all of 4 pins(LEDs) as High */
XGpio_DiscreteWrite(&instXGpio, 1, 0x0F);
sleep(1);
/* Set all of 4 pins(LEDs) as Low */
XGpio_DiscreteWrite(&instXGpio, 1, 0x00);
sleep(1);
}
cleanup_platform();
return 0;
}
参考資料