開発環境
FPGA
zynq
xilinx
zybo

ZYBO (Zynq) 初心者ガイド (3) PSのGPIOでLチカ

この記事の内容を4分で見る ( https://www.youtube.com/watch?v=VJCL2Z81DLQ )

環境

  • 開発用PC: Windows 10 64-bit
    • Vivado 2017.4 WebPACKライセンス
    • Xilinx SDK 2017.4
  • ターゲットボード: ZYBO (Z7-20)

Windows環境は1回目を参照。

PSのGPIOでLチカ

前回、PSのみを搭載したハードウェアを作成して、PSのUARTとCPUを使ってHello World出力をしました。今回は、PSのGPIOを使ってLEDをチカチカさせます。これは通常のマイコンとほぼ同じなので、ソフト屋さんにもとっつきやすいです。

プロジェクトの作成

前回作成したプロジェクトをそのまま使います。これは、PSだけを搭載したハードウェア(hdf)から作成したSDKプロジェクトです。

事前知識

MIOについて

ZynqはPSとPLから構成されます。PSにはCPUのほかにUART、I2C、GPIO等のペリフェラルがハードマクロIPとして搭載されています。これら専用のピン(IO)が用意されています。それがMIO(Multiplexed IO)です。ピン毎にどの機能に割り当てるかを選ぶことが出来るので「Multiplexed」となっています。STM32などのマイコンと同じです。

このMIOはPS専用です。一方、MIOの数が足りなくなった場合には、PLにつながっている普通のIOをPSのペリフェラル用IOとして使うことが出来ます。これをEMIO(Extended MIO)というらしいです。

PSのGPIOについて

PS上のペリフェラルは通常のマイコンのペリフェラルと同じように制御できます。GPIOも通常のマイコン通り、入出力の方向を設定して、出力する、という流れです。設定するレジスタのアドレスも決まっています。

機能仕様やレジスタ詳細は、Zynq-7000のテクニカルリファレンスマニュアルに載っています。

01.jpg

ZYBOボード上での接続

ZYBO(Z7-20)ボードでは、LED4がMIO7に接続されています。このLEDを光らせることにします。

念のため、VivadoでPSのコンフィグを確認します。MIO7はどの機能ペリにも割り当てられてなく、GPIO MIOとなっていました。また、MIO7はBank0に属していることが分かります。
02.jpg

レジスタ直叩きでPSのGPIO制御

まずは、レジスタ直叩きでGPIO制御してみます。Zynq-7000のテクニカルリファレンスマニュアルを見ると、関連するレジスタは以下の通りでした。

  • GPIOPSのベースアドレス: 0xE000A000
  • DATA_0 (Output Data (GPIO Bank0, MIO))のオフセット: 0x00000040
  • DIRM_0 (Direction mode (GPIO Bank0, MIO))のオフセット: 0x00000204
  • OEN_0 (Output enable (GPIO Bank0, MIO))のオフセット: 0x00000208

コード

Vivado SDK上で、以下のようなコードを実装します。(前回のHello Worldの続きなので、ファイル名もそのままですが気にしない)
MIO7を出力設定して、その後1秒間隔でHigh/Lowを出力させることでLEDを点滅させます。

helloworld.c
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "sleep.h"

#define REG(address) *(volatile unsigned int*)(address)

#define GPIOPS_BASE   (0xE000A000)
#define GPIOPS_DATA_0 (GPIOPS_BASE + 0x0040)
#define GPIOPS_DIRM_0 (GPIOPS_BASE + 0x0204)
#define GPIOPS_OEN_0  (GPIOPS_BASE + 0x0208)

int main()
{
    init_platform();

    print("Hello World\n\r");

    /* Set MIO7 as output */
    REG(GPIOPS_DIRM_0) |= 1 << 7;
    REG(GPIOPS_OEN_0)  |= 1 << 7;
    while(1) {
        /* Set MIO7 as High */
        REG(GPIOPS_DATA_0) |= 1 << 7;
        sleep(1);
        /* Set MIO7 as Low */
        REG(GPIOPS_DATA_0) &= ~(1 << 7);
        sleep(1);
    }

    cleanup_platform();
    return 0;
}

Xilinx SDK Driver APIでPSのGPIO制御

レジスタ直叩きでの制御は、中身を理解するためには良いですが、実際にはやりたくありません。実はドライバAPI(関数)が用意されています。Xilinx SDK上でアプリケーションプロジェクトを作ったときに、BSPプロジェクトも一緒に作られたと思います。そのBSPプロジェクト内に、Vivadoで有効にしたペリフェラル制御用の関数が用意されています。さらに、使い方もドキュメントに書かれています。

03.jpg

コード

ドライバAPIを使用した場合のコードは、以下のようになります。基本的に、Xilinx提供の関数はXから始まります。そして、PS用の場合には最後にPsとつくようです。

helloworld.c
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "sleep.h"
#include "xgpiops.h"

int main()
{
    init_platform();

    print("Hello World\n\r");

    XGpioPs instXGpioPs;
    XGpioPs_Config *configXGpioPs;

    configXGpioPs = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
    XGpioPs_CfgInitialize(&instXGpioPs, configXGpioPs,configXGpioPs->BaseAddr);
    /* Set MIO7 as output */
    XGpioPs_SetDirectionPin(&instXGpioPs, 7, 1);
    XGpioPs_SetOutputEnablePin(&instXGpioPs, 7, 1);
    while(1) {
        /* Set MIO7 as High */
        XGpioPs_WritePin(&instXGpioPs, 7, 1);
        sleep(1);
        /* Set MIO7 as Low */
        XGpioPs_WritePin(&instXGpioPs, 7, 0);
        sleep(1);
    }

    cleanup_platform();
    return 0;
}

おわりに

今回はGPIOを使ってのLED制御でしたが、PSの他のペリフェラル(UARTやI2C等)も同じ感じで制御できると思います。

参考資料

https://www.xilinx.com/support/documentation/sw_manuals_j/xilinx2013_4/ug898-vivado-embedded-design.pdf
https://japan.xilinx.com/support/documentation/sw_manuals_j/xilinx2015_2/ug1165-zynq-embedded-design-tutorial.pdf
https://japan.xilinx.com/support/documentation/user_guides/j_ug585-Zynq-7000-TRM.pdf