AXI VDMAでHDMI出力

概要

AXI VDMAの使い方についてのメモです。
ArtyZ7でHDMI出力をしてみたいと思います。
回路構成はArty-Z7のHDMI OUTのサンプルデザインを参考にデザインを一から作り直して調べていこうと思います。
この記事は、調べながら順に更新をしていこうと思います。

必要なもの

・ArtyZ7 (HDMI出力があるボード)
・Vivado 2017.3
DigilentのIPコア

続きを読む

MicroBlazeで実行時間を測定する方法

概要

MicroBlazeで命令の実行時間を測定する方法について考えました。
MicroBlazeではxtime_l.hが存在せず、sys/time.hのgettimeofday()が使えなかったため、
外部にAXI_Timer IPを入れることで実行時間を測定することにしました。

Vivadoプロジェクトの作成

全体図

ブロックデザインにMicroBlazeとAXI Timer、AXI UARTを入れました。
図にはAXI Interrupt Controllerが入っていますが、実際には必要ありませんでした。

AXI Timerの設定

AXI Timerの設定は32bitでTimer1のみを使う設定にしました。
クロックは100MHzを入れているので、42.9秒までは測れそうです。

SDKプロジェクトの作成

ソースコードは次のようにしました。
AXI Timerをフリーランニングした状態で、実行前と実行後にタイマの値を取得して実行時間を測定しています。

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xtmrctr.h"

XTmrCtr TimerCounterInst;

int init(){
	int Status;

	Status = XTmrCtr_Initialize(&TimerCounterInst, XPAR_AXI_TIMER_0_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		printf("Timer Initialize Error\n\r");
        return XST_FAILURE;
	}

	XTmrCtr_SetOptions(&TimerCounterInst, 0, XTC_AUTO_RELOAD_OPTION);
	XTmrCtr_Start(&TimerCounterInst, 0);
}

int main()
{
    u32 tStart, tEnd;
    init_platform();

    init();

    tStart = XTmrCtr_GetValue(&TimerCounterInst, 0);
    print("Hello World\n\r");
    tEnd = XTmrCtr_GetValue(&TimerCounterInst, 0);

    printf("End - Start = %u\n\r", (unsigned int)(tEnd - tStart));
    printf("Time = %.2f us\n\r", 1.0 * (unsigned int)(tEnd - tStart) / 100000000 * 1000000);

    cleanup_platform();
    return 0;
}

実行結果

printfをした場合の結果

Hello World
End - Start = 1904
Time = 19.04 us

printfを外して、XTmrCtr_GetValueにかかる時間

End - Start = 201
Time = 2.01 us

ZYNQのPSで実行時間を測定する方法

概要

ZYNQである処理の実行時間を測定したいときにどうすればよいのかについて紹介します。
一般的なCのコードではtime.hを使ったりしますが、ZYNQではこの方法は非推奨なようです。
今回はXilinxが用意したxtime_l.hというファイルを用います。

Vivadoプロジェクトの作成

今回はZYNQのPS部分のみを使用します。

SDKプロジェクトの作成

HelloWorldのテンプレートでプロジェクトを作成し、HelloWorld.cを次のように書き換えました。

#include <stdio.h>
#include "platform.h"
#include "xparameters.h"
#include "xtime_l.h"


int main()
{
    XTime tStart, tEnd;
    init_platform();

    XTime_GetTime(&tStart);
    printf("Hello World\n\r");
    XTime_GetTime(&tEnd);

    printf("Output took %llu clock cycles.\n\r", 2 * (tEnd - tStart));
    printf("Output took %.2lf us.\n\r", 1.0 * (tEnd - tStart) / (COUNTS_PER_SECOND / 100000000));

    cleanup_platform();
    return 0;
}

実行結果

printfをした場合

Hello World
Output took 10388 clock cycles.
Output took 1731.33 us.

printfを外してGetTimeにかかる時間を測定

Output took 50 clock cycles.
Output took 8.33 us.

AXI GPIOについて

概要

AXI GPIOの使い方についてのメモ書きです。

AXI GPIOはFPGAのピンの入出力としてももちろん使えますが、
FPGA内部のIPの入出力をコントロールすることもできて非常に便利なIPです。

Vivadoプロジェクトの作成

Block DesignなどでAXI GPIOを追加するだけなので、省略します。

SDKプロジェクトの作成

LEDを点灯するサンプルコードを下記に示します。

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xgpio.h"

// Gpio構造体
XGpio Gpio_led;

int main()
{
    int Status;

    init_platform();

    // AXI GPIOの初期化
    Status = XGpio_Initialize(&Gpio_led, XPAR_AXI_GPIO_0_DEVICE_ID);
    if (Status != XST_SUCCESS){
    	xil_printf("XPAR_AXI_GPIO_0_DEVICE_ID initialize failed\n\r");
    	return XST_FAILURE;
    }

    // AXI GPIOの入出力設定
    XGpio_SetDataDirection(&Gpio_led, 1, 0); // all output

    // AXI GPIOの出力値設定
    XGpio_DiscreteWrite(&Gpio_led, 1, 0x1); // led high

    cleanup_platform();
    return 0;
}

AXI GPIOの初期化

int XGpio_Initialize(XGpio *InstancePtr, u16 DeviceId);

でAXI GPIOが初期化されます。

DeviceIdはxparameters.hを見て、初期化したいAXI GPIOのDeviceIdを設定してください。

IPの初期化に成功するとXST_SUCCESSが返ってきます。
それ以外の場合はデバイスIDが間違っていたり、AXI GPIOがなかったりする可能性があります。
今回は初期化に失敗したらXST_FAILUREを返してプログラムを修了するようにしています。

AXI GPIOの入出力設定

void XGpio_SetDataDirection(XGpio *InstancePtr, unsigned Channel,
			    u32 DirectionMask);

で使用するピンの入出力方向を設定します。

Channelには「1」か「2」の値が入ります。
AXI GPIOはチャネルが2つまで設定できます。
どちらのチャネルを設定するのか、それぞれ決めなければいけません。
今回はチャネル1のみ使用するため1を設定しました。

DirectionMaskで入出力方向を決めます。
各bitは
0:出力
1:入力
となります。

AXI GPIOの出力値設定

void XGpio_DiscreteWrite(XGpio *InstancePtr, unsigned Channel, u32 Mask);

でピンの出力値が設定できます。
Maskで出力値のHigh/Lowを切り替えます。
各bitは
0:Low
1:High
となります。

AXI GPIOの入力値読み出し

u32 XGpio_DiscreteRead(XGpio *InstancePtr, unsigned Channel);

でピンの入力値が読み出せます。
予め各ピンは入力と設定されている必要があります。

AXI GPIOのビットセット/クリア

void XGpio_DiscreteSet(XGpio *InstancePtr, unsigned Channel, u32 Mask);
void XGpio_DiscreteClear(XGpio *InstancePtr, unsigned Channel, u32 Mask);

でビット単位でセット/クリアができます。

割り込み設定

AXI GPIOのIP設定画面から割り込みを有効にすることができます。

void XGpio_InterruptGlobalEnable(XGpio *InstancePtr);
void XGpio_InterruptGlobalDisable(XGpio *InstancePtr);
void XGpio_InterruptEnable(XGpio *InstancePtr, u32 Mask);
void XGpio_InterruptDisable(XGpio *InstancePtr, u32 Mask);
void XGpio_InterruptClear(XGpio *InstancePtr, u32 Mask);
u32 XGpio_InterruptGetEnabled(XGpio *InstancePtr);
u32 XGpio_InterruptGetStatus(XGpio *InstancePtr);

という関数があります。

これらについてはまた機会があれば書こうかと思います。

Cmod A7のSPIFlashにSDKのプロジェクトを書き込む

概要

Digilent社のCmodA7というFPGAボードにMicroBlazeを実装したBitstreamとそのSDKプロジェクトをSPI Flashに書き込む方法についてです。

この方法はDigilent社の公式HPに、そのやり方が書いてあるのですが、かなり省略された説明でわかりにくかったため、
分かりにくかったところを追記しようと思います。

基本的にはDigilent社のページを見ながらやって見てください。
How To Store Your SDK Project in SPI Flash [Reference.Digilentinc]

vivadoプロジェクトの作成

公式HPの説明ですと、Vivadoプロジェクトについて
「Before you start; this guide assumes that you already have a Microblaze system built complete with Quad SPI, External Memory, and Uart cores, and that you have the appropriate QSpi mode jumper setting. This tutorial takes place in SDK.」
としか書かれていません。

この部分について実際に作ってみましょう。

プロジェクトの作成

プロジェクト名:任意
ボードファイル:CmodA7-XXT (使う予定のボードに合わせてボードファイルを定義してください。)
でプロジェクトを作成してください。

BlockDesignの作成

BlockDesignの名前は適当につけてください。

まず、MicroBlazeを追加して、Run Block Automationを実行します。
このときの設定は、

Preset None
Local Memory 32kB
Local Memory ECC None
Cache Configuration None
Debug Module Debug Only
Peripheral AXI Port Enabled
Interrupt Controller Checked
Clock Connection New Clocking Wizard

としました。
メモリのサイズは適当です。

私は最初、Interrupt Controllerにチェックマークを入れず、自分でAXI Interrupt Controllerを入れていたのですが、なぜかこれでは後にSDKでエラーになってしまい、
かなり苦労しました。

この後は、画像のようにBoardタブより
・System Clock
・Cell Ram
・QSPI Flash
・Reset
・USB UART
を追加して、Run Connection Automationを実行しました。

次に、AXI UARTとAXI QUAD SPIからInterrupt ControllerにInterrupt信号を接続しましたが、これが必要かはわかりません。。。

ここまで終わったら、Generate Bitstreamを実行します。

公式HPに書いてあるBitstreamの圧縮は任意で行ってください。

SDKプロジェクトの作成

ここは公式HPどおりです。
Cmod-A7では、4MBのn25q032a13ef440fというSPI Flashを使っているため、
FLASH_IMAGE_BASEADDRは0x00300000
Program Flash MemoryのFlash Typeはn25q32-3.3v-spi-x1_x2_x4
に設定するというところに気をつける必要があります。

このような設定にしたところ、SPI FlashからSDKのプロジェクトを読み出すことができるようになりました。

さいごに

結局、Interrput Controllerを今回の方法で設定したらうまくいくようになったんですが、理由はよくわかりません。

EZ-BLEでI2C to BLEを試す

今回はCypressのEZ-BLEモジュールでI2C→BLE変換を試してみたいと思います。

EZ-BLEモジュール用プロジェクトの作成

I2C→BLE変換プロジェクトはCypress公式のPSoC-4-BLEのサンプルプロジェクトとして公開されています。
PSoC-4-BLE/100_Projects_in_100_Days/Day035_I2C_BLE_Bridge at master · cypresssemiconductorco/PSoC-4-BLE · GitHub
今回はこれをCYBLE-022001-00用にビルドして用いました。

I2C_BLE_Bridgeサンプルロジェクトを開き、プロジェクトのデバイスを変更します。
プロジェクトを右クリック→Device Selector
からCYBLE-022001-00を選択しました。

次にこちらを参考に、コンポーネントをアップデートしたり、Bootloadableを追加したりします。
必要に応じてピン配置を変える必要もあります。
EZ-BLE PRoC を使ってみる - たるたるソース

ビルドがうまく行ったら書き込みEZ-BLEモジュールの準備は完了です。

PCとの接続

PCとの接続方法はI2C_BLE_BridgeプロジェクトのドキュメントP5~7に書いてあるため省略します。
PSoC-4-BLE/I2C-BLE Peripheral.pdf at master · cypresssemiconductorco/PSoC-4-BLE · GitHub

ドキュメントのFigure 8 Enabling notificationsの画面が表示されればそれ以降はやらなくてOKです。
この画面に受信したデータが表示されます。

EZ-BLEにI2C信号を入力する

今回はI2Cのテスト信号としてESP32を用いました。
I2Cの信号が出ればなんでもOKです。

I2C_BLE_BridgeプロジェクトのI2Cモジュールのプロパティを見ると、slave addressは0x08になっています。
なので、0x08を送ってから送りたいデータを送ればOKです。

ESP32で使用したソースコードはこちらです。

#include <Wire.h>

void setup() {
  int SDA = 25;
  int SCL = 26;

  Wire.begin(SDA, SCL);
}

byte val = 0;

void loop() {
  Wire.beginTransmission(0x08);
  Wire.write(val);
  Wire.endTransmission(true);

  val++;
  delay(20);
}

実際に通信してみる

受信したデータはCySmartの画面から見れます。青いところが受信したデータです。
f:id:taltalp:20170926232257p:plain
通信中のI2C信号も見てみました。 ちゃんとACKが返ってきています。
f:id:taltalp:20170926232335p:plain

EZ-BLE PRoC を使ってみる

Cypress EZ-BLE PRoC というとても小さなモジュールを手に入れたので、
早速これでLチカしてみたいと思います。

用意したもの

チップ単体は1cm×1cmと非常に小さく、これだけでは扱いにくいので、
評価ボードを使って見ようと思います。

今回用意したのはこれだけです。
S6SAE101A00SA1002 Solar-Powered IoT Device Kit
※このキットの場合、技適の通ったBLEモジュールを載せ替える必要があります。

ちなみに、このモジュールを用いたものとして他に、
コイン型センサーモジュールなんかもあります。
CYALKIT-E03 Solar-Powered BLE Sensor 5 Pack

小さな太陽電池でも動く小型で省電力なモジュールなようで、いろんな用途に使えそうです。

PSoC Creator のインストール

S6SAE101A00SA1002 Solar-Powered IoT Device Kitの下の方の関連ファイルから
S6SAE101A00SA1002 Complete Setup (Kit Design Files, PSoC Creator 3.2 SP1, CySmart, PSoC Programmer, Documentation, Examples)
というファイルをダウンロードしてインストールしました。

これでPSoC開発環境のPSoC CreatorとEX-BLEの書き込みソフトやサンプルプロジェクトなどがインストールされました。

サンプルプロジェクトを開く

先ほどインストールしたディレクトリを見てみると、
C:\Program Files\Cypress\Solar-Powered IoT Device Kit\Firmware\other
にサンプルプロジェクトの
LED_ONOFFSimple_BLEがあります。
LED_ONOFFのディレクトリをマイドキュメントなど適当な場所にコピーして、
LED_ONOFF.cydsn\LED_ONOFF.cyprj
を開きます。

LED_ONOFFプロジェクトのTopDesign.cyschを開くとこんな画面が出てきました。

コンポーネントのバージョンが古いと怒られているので、
Project→Update Components
コンポーネントのバージョンを最新版にしました。
BootloadableとLED_Greenピンがあります。

どうやらブートローダーでプログラムを読み出すみたいです。
(詳しくはhttp://www.cypress.com/file/187321/download:Title=S6SAE101A00SA1002 User Guide.pdfに書いてあります。)

同様に、
UART_BootloaderをSet As Active Projectでアクティブにし、Update Componentsします。

こちらは、BootloaderとUARTコンポーネントが含まれています。
おそらくUART経由でプログラムを読み出すということでしょうか。

プロジェクトのビルド

まずは、UART_Bootloaderのプロジェクトを右クリックして、Buildします。
うまくいくとたくさんのファイルが生成されます。

次は、LED_ONOFFプロジェクトのBootloadableコンポーネントをダブルクリックして設定を見てみます。
DependenciesタブでBootloaderのhexとelfのパスが指定されていますが、微妙にパスが違っていることがあるので、
もともと書いてあったパスを参考にこのhexとelfを探して指定します。

そして、LED_ONOFFプロジェクトをビルドします。
すると、こちらも大量のファイルが生成されます。

ここで、LED_ONOFFのプロジェクトディレクトリで、「.cyacd」ファイルを検索すると、
「LED_ONOFF.cyacd」というファイルが見つかると思います。
これが書き込むファイルです。

書き込み

書き込みについては詳しくはhttp://www.cypress.com/file/187321/download:Title=S6SAE101A00SA1002 User Guide.pdf
に書いてあります。

簡単に説明すると、
C:\Program Files\Cypress\Solar-Powered IoT Device Kit\1.0\PMIC Software\Bootloader_Host_GUI_exe\UARTBootloaderHost.exe
を実行すると、書き込み画面がでます。

評価ボードをUSB接続して、COMポートを指定、
ボーレートを115200に指定
さっきのLED_ONOFF.cyacdを指定
評価ボードのSW2を一回押してからBootloadボタンを押すと書き込みが始まる。

と言った感じです。

動作確認

評価ボードのLEDがチカチカすれば成功です。