- 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=JoQePlMliyk )
環境
- 開発用PC: Windows 10 64-bit
- Vivado 2017.4 WebPACKライセンス
- Xilinx SDK 2017.4
- ターゲットボード: ZYBO (Z7-20)
Windows環境は1回目を参照。
自作IPでLチカ
GPIOの OUT だけの機能を持ったIPを作ります。この自作IPをPS側のCPUから制御してLEDをチカチカさせます。前回の最後に、LED点灯するモジュール(blink)をIP Integratorから呼んでみました。しかし、厳密にはこれはあくまで「モジュール」であって「IP」ではないようです。IPを作るためには、ちゃんとパッケージ化する必要があります。まずはそれをやります。
今回も、最初からVivadoプロジェクトを作ります。ボードにZyboを指定した空のプロジェクトを用意済みとして、話を進めます。
自作IPを作る (Vivado)
自作IPの仕様
今回のIPはPSから制御できるようにします。PSとPLでデータをやり取りするときには、AXIバスを使います。CPUはAXI経由でIP内部のレジスタに値をR/Wします。IPはこのレジスタの値を使って動作します。今回は、レジスタ値の下位4ビットを反転して出力するようにします。
(このIP単体にとっては、この4bit出力はただの信号線です。なので、「LED」とかは付けないことにします。どこに接続するかは、このIPを使用するIP Integratorで決めます。(という考えはソフト屋さん的な考えなのかな。。。))
- 名前: myip
- PSとのインターフェース: AXI4-Lite Slave
- レジスタ: 32-bit x 4 (実際に使用するのは1個だけだが、デフォルト設定のまま使うため)
- 出力: 4-bitの信号線 (後でLEDに接続される)
自作IPのひな形を作る
空のVivadoプロジェクトを開いたら、まずはメニューバー -> Tools -> Create and Package New IPをクリックします。何をやるか聞かれるので、「Create AXI4 Peripheral」を選びます。OKを押すと、IP名などを設定する画面になります。基本はデフォルトのままで良いですが、僕は保存先だけ変更しました。デフォルトだと、1つ上のフォルダに作られてしまいます。今回作るIPはこのプロジェクトでのみ使うので、「../」を削除しました。
続いて、インターフェースを編集できますが、ここもひとまずはデフォルトのままにします。デフォルトでは、AXI4-Lite Slaveで、32-bit x 4個のレジスタが用意されます。OKを押して、次の画面もAdd IP to the repositoryのままでFinishします。(4-bitの出力は、後でコード上で追加します)
自作IPを編集する
Flow Navigator -> PROJECT MANAGER -> IP Catalogをクリックして、IP Catalogを確認すると、User Repositoryの下に「myip_v1.0」が出来ています。先ほど全てデフォルト設定で作成したので、これから編集します。右クリックして「Edit in IP Packager」を選びます。これで、このIPを編集する新しい一時的なプロジェクトを開きます。そのプロジェクトの保存先を聞かれますが、デフォルトのままOKします。
自作IP(myip)を編集するための、新しいVivadoウィンドウが開かれます。ソースコードは2つあり、myip_v1_0_S00_AXI.vが実体で、myip_v1_0.vはラッパー的な感じです。
今回は出力として、4-bitのMY_OUT
を追加します。これに、4個のうち最初のレジスタの下位4-bitを反転して割り当ててみます。レジスタ名はslv_reg0
のようです。(この記事では簡単のために、例としてこのようなシンプルなロジックにしました。実際のプロジェクトではもっと複雑なロジックになると思います。)
myip_v1_0_S00_AXI.vを開くと、既にかなりのコードが実装されています。これは、AXIインターフェースでレジスタに読み書きするコードになります。「Users to add ...」というところに、コードを追加していきます。追加は2行だけで、ポート定義と、ロジック記述だけです。
module myip_v1_0_S00_AXI #
省略
(
// Users to add ports here
output [3:0] MY_OUT,
// User ports ends
// Do not modify the ports beyond this line
省略
);
省略
// Add user logic here
assign MY_OUT = ~slv_reg0[3:0];
// User logic ends
endmodule
myip_v1_0.vにも同様に追加します。MY_OUT
を接続するコードを追加するだけです。
module myip_v1_0 #
省略
(
// Users to add ports here
output [3:0] MY_OUT,
// User ports ends
省略
);
// Instantiation of Axi Bus Interface S00_AXI
myip_v1_0_S00_AXI # (
省略
) myip_v1_0_S00_AXI_inst (
.MY_OUT(MY_OUT),
省略
);
endmodule
コードの編集が終わったら、IPを再作成します。Package IPタブで全てのPackaging Stepsが編集マークからチェックマークになるように、更新します。各Stepの所で「Merge changes...」をクリックすればOKです。最後に、Review and Packageを選び、Re-Package IPをクリックします。成功したら、IP編集用のプロジェクトは閉じられます。
自作IPを使う (Vivado)
PSと、作成したIP(myip)を搭載したハードウェアを作ります。元のVivadoプロジェクト上で、Flow Navigator -> IP INTEGRATOR -> Create Block Designで新しいブロックデザインを作ります。作成されたブロックデザインのDiagramビュー上でPSを追加して、Run Block Automationします。ここまでは、2回目、3回目の内容と同じです。同様の手順で、作成したmyipも追加します。Add IPで表示されるIP一覧にmyipが追加されています。配置後、Run Connection Automationで自動接続してもらいます。設定は全てデフォルトでOKです。
myipが配置されたら、MY_OUTの出力ポートを用意します。myipのMY_OUT端子付近で右クリックしてMake Externalを選びます。すると、MY_OUT_0ポートが作られます。
必要部品の配置が出来たので、このブロックデザインをトップモジュールとして使います。ここから先は前回と同様です。Sourcesのdesign_1の所で右クリック -> Generate Output Productsと Create HDL Wrapperします。その後、MY_OUT_0ポートをLEDの接続されているピンに割り当てます。これも前回と同様に、Flow Navigator -> RTL ANALYSIS -> Open Elaborated Designしてから、以下のようにピン設定をして、適当な名前で制約ファイルを保存します。
最後に、Generate Bitstreamでビットストリームファイルを作り、Export Hardwareして、Launch SDKします。
ノート: 後で再びIPを編集する場合
自作IPを既にブロックデザイン上で配置した後に、自作IPを編集した場合は、更新作業が必要になります。自作IPの編集を終えて、元のVivadoプロジェクトに戻ると、以下のようにメッセージが上部に表示されるので、「Report IP Status」をクリックします。
すると、画面下部にIP Statusが表示されます。変更のあったmyipがチェックされていることを確認して、「Upgrade Selected」をクリックします。その後、必要であればデザインを変更し、Generate Output Products、Create HDL Wrapper、ビットストリーム作成をすることで更新完了です。
自作IPを呼び出してLチカするソフトを書く (SDK)
起動したSDK上で、適当なプロジェクトを作ります。ここでは名前をblinkとします。
まず最初に、自動生成されたblink_bspプロジェクトを確認します。xparameters.hを見ると、#define XPAR_MYIP_0_S00_AXI_BASEADDR 0x43C00000
が定義されています。これが、自作IP内のレジスタのアドレスになります。各レジスタアドレスは、以下のようになります。
- slv_reg0: 0x43C00000
- slv_reg1: 0x43C00004
- slv_reg2: 0x43C00008
- slv_reg3: 0x43C0000C
今回は、0x43C00000にアクセスすることでLED制御できます。いつも通り、0x43C00000をポインタ化してアクセスしてもいいのですが、アクセス関数も自動生成されています。libsrc/myip_v1_0/src/myip.hを見ると、MYIP_mWriteReg()
関数が用意されています。また、オフセットもマクロ化されて用意されているのでそれを使います。これらを使い、以下のように実装します。
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "sleep.h"
#include "xparameters.h"
#include "myip.h"
int main()
{
init_platform();
print("Hello World\n\r");
while(1) {
MYIP_mWriteReg(XPAR_MYIP_0_S00_AXI_BASEADDR, MYIP_S00_AXI_SLV_REG0_OFFSET, 0x00);
sleep(1);
MYIP_mWriteReg(XPAR_MYIP_0_S00_AXI_BASEADDR, MYIP_S00_AXI_SLV_REG0_OFFSET, 0x0F);
sleep(1);
}
cleanup_platform();
return 0;
}
ビルド後、まずはXilinx -> Program FPGAでビットストリームファイルを書き込みます。確認してほしいのは、この時点でLEDが点灯することです。理由は、myipではレジスタ値の反転を出力しているため、初期値(0)の反転でHigh出力になっているためです。その後、ビルドしたプログラムをRunします。すると、1秒間隔でLEDが点滅するはずです。