ZYBO (Zynq) メモ

記事にするまでもないことのメモ。自分用。

# Vivado
## 割り込み
Zynqの場合、AXI Interrupt Controllerは使わないで、割り込み信号線をPSに直接接続する。割り込みが複数ある場合は、Concat経由で接続する。

BSPで使用するライブラリは、xscugic。xscugic_example.cとかを参考にする。

https://forums.xilinx.com/t5/Embedded-Development-Tools/Connecting-AXI-Interrupt-Controller-to-Processing-System-on-Zynq/td-p/276778
https://forums.xilinx.com/t5/Embedded-Processor-System-Design/How-to-use-more-than-one-IRQ-F2P-interrupt/td-p/514661

## VDMA
Start Address(転送元アドレス)を切り替える時には、レジスタ設定後、再スタートが必要。見た目上は、ちゃんと次のフレームの頭から切り替わっているように見える。(現フレームの途中で停止したり、切り替わってはいないように見える)

# Vivado HLS
## ループの終了条件はできるだけ固定値を使う
例えば、画像処理で1ライン分の処理をするとき。WIDTH_MAX = 1280で、実際のwidthがレジスタで設定されるような時でも、
`for (int x = 0; x < width; x++)`ではなく、`for (int x = 0; x < WIDTH_MAX; x++)`の方が、結果として速いし使用リソースも少ない。コード例は↓

## ポインタやキャスト処理に注意
8ビット型のポインタ(`uint8_t*`)を使用してしまうと、キャスト時にアラインチェックのために、除算処理(urem, srem)が入ってしまう。これが非常に重い。`uint32_t*`を使うようにする。明示的にキャストしていなくても、バースト転送用に`memcpy`などがあったら同じこと。

指定アドレス(`vram`)の指定座標(`x, y`)の指定サイズ(`width, height`)に、色(`r, g, b`)を塗りつぶす関数の例。

### 8ビット型のポインタを使用したとき
```cpp:8ビット型のポインタを使用
#define CANVAS_WIDTH 1280
void drawCanvas(
		uint8_t *vram,
		uint16_t x,
		uint16_t y,
		uint16_t width,
		uint16_t height,
		uint8_t r,
		uint8_t g,
		uint8_t b
)
{
	uint8_t linebuf[CANVAS_WIDTH * 3];
	for (int i = 0; i < width * 3; ) {
		linebuf[i++] = r;
		linebuf[i++] = g;
		linebuf[i++] = b;
	}

	for (int i = 0; i < height; i++) {
		void* lineStartAddress = (void*)(vram + (y + i) * CANVAS_WIDTH * 3 + x * 3);
		memcpy(lineStartAddress, linebuf, width * 3);
	}
	return ;
}
```

パフォーマンスプロファイル結果が下記。除算(`srem, urem`)が入ってしまい、非常に遅い。
サイズが1280 x 720の時、実測値で約30msec。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/214268/34f931b3-2123-9b31-def0-21ee64582323.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214268/9fc5ded1-0d0a-725f-08ae-5be61440ce89.png)


### 32ビット型のポインタを使用したとき
```cpp:32ビット型のポインタを使用
void drawCanvas(
		uint32_t *vram,
		uint16_t x,
		uint16_t y,
		uint16_t width,
		uint16_t height,
		uint8_t r,
		uint8_t g,
		uint8_t b
)
{
	uint32_t linebuf[CANVAS_WIDTH * 3 / 4];
//	for (int i = 0; i < width * 3 / 4; ) {
	for (int i = 0; i < CANVAS_WIDTH * 3 / 4; ) {
		linebuf[i++] = r << 24 | b << 16 | g << 8 | r;
		linebuf[i++] = g << 24 | r << 16 | b << 8 | g;
		linebuf[i++] = b << 24 | g << 16 | r << 8 | b;
	}

	uint32_t* lineStartAddress = vram + 3 * (y * CANVAS_WIDTH + x) / 4;
	for (int i = 0; i < height; i++) {
		memcpy(lineStartAddress, linebuf, width * 3);
		lineStartAddress = lineStartAddress + 3 * CANVAS_WIDTH / 4;
	}
	return ;
}
```

パフォーマンスプロファイル結果が下記。除算(`srem, urem`)が無くなり、高速化。
サイズが1280 x 720の時、実測値で約8msec。

注意点としては、指定する座標やサイズに、「4ピクセルアライメントである必要がある」という制約がつく。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/214268/95e9aca8-fb39-13d1-fde2-a91650a956a9.png)


### 16ビット型のポインタを使用したとき
32ビットと同様に、除算(`srem, urem`)はなくなったが、速度は実測で約15msec。なんでだろう??? たぶん、AXIバースト転送アラインとか調べてるのかな??

## ap_uint<>型をシフトするときに注意
```c
ap_uint<8> r0 = 0xFF, g0 = 0xFF, b0 = 0xFF;
ap_uint<8> r1 = 0xFF;
ap_uint<32> pixel = = r1 << 24 | b0 << 16 | g0 << 8 | r0;
```

としたとき、r0以外は全部0になる。uint8_tを使うと0xFFになる。

## Directiveの説明
https://japan.xilinx.com/html_docs/xilinx2017_2/sdsoc_doc/topics/pragmas/concept-Intro_to_HLS_pragmas.html

## Nasne (PC TV Plus)をしながらC synthesisすると死ぬ
長時間かかるPIPELINEを有効にしているとき、2/2の確率で死んだ。

## 高位合成結果、TimingのEstimatedがTargetをオーバーしてしまった
メニューバー -> Solution -> Solution Settings -> Synthesis、Uncertiantyの値を3とかにする。

http://marsee101.blog19.fc2.com/blog-entry-4001.html?sp

# Xilinx SDK
## Git管理したワークスペース
一度Gitで管理したワークスペースを、再度ローカルに持ってきたとき、色々と設定ファイルが消えている可能性がある。場合によっては、下記が必要。

- ワークスペース内の各プロジェクトは手動でインポートする
- ビルドの前に、BSPをRe-generateする


## msysやmingwのバイナリへのパス追加は消す
msysやmingwのbinフォルダにパスが通っていると、XSDKが内部で使用するコマンド(C:\Xilinx\SDK\2017.4\gnuwin\bin)実行時に問題が起きる。特にmake。

例えば、OpenAMPアプリケーション作成時に、XSDKで内部的にmake(cmake?)コマンドを走らせようとするが、msys側のbinにパスが通っていると、msys側のmakeが使用されるっぽい。エラーは発生せず、フリーズするだけ。環境変数からmsysへのパスを削除したら治った。

## FreeRTOS
### キュー(メッセージ)送信後のコンテキストスイッチ
タスクAからタスクBに`xQueueSend()`でメッセージを送る。タスクBは`xQueueReceive()`で待っている。
この時、仮にタスクBの優先度の方が高いとしても、自動でタスクは切り替わらない。適当に`vTaskDelay(1);`などでコンテキストスイッチさせる必要がある。(もっといい関数はあると思うが。)

## メモリダンプと保存
メモリダンプは、Debugパースペクティブの右下。

バイナリとして保存するのは、メニューバー -> Xilinx -> Dump/Restore Memory。
Processorは、今デバッグ中の停止している方のコアを選ぶ。Name=Xilinx Hardware Server /APU/ARM Cortex-A9 MPCore #0

## Linuxユーザーアプリケーション
### 実際にPetaLinuxで作成したLinux System Rootを使用する
- PetaLinux側で、`zip -r plnx_arm.zip build/tmp/sysroots/plnx_arm`でSystem Rootを圧縮(シンボリックリンクは実体化する)
- XSDKのあるWindows側でplnx_arm.zipを解凍
- XSDKで新規アプリケーションプロジェクト(OS Platform=linux)作成時に、Linux System Rootにチェックを付けて、`C:\vivado\project_openamp\project_openamp.sdk\sysroots\plnx_arm`を指定
- Project Explorer -> プロジェクト -> 右クリック -> Properties -> ARM v7 Linux gcc linker -> Miscellaneuous -> Linker Flagsに以下を追加
    - --sysroot=C:\vivado\project_openamp\project_openamp.sdk\sysroots\plnx_arm
- 追加ライブラリがある場合は、ARM v7 Linux gcc linker -> Libraries -> Libraries (-l)に、追加。

#### 追記 2018.x以降
https://forums.xilinx.com/t5/Embedded-Linux/Where-is-the-quot-linux-sysroot-path-quot/td-p/874525

```
Starting from 2018.1, sysroot folder is not generated in build/tmp. You need to build the petalinux project with 

petalinux-build --sdk  (Make sure to have doble –‘s)

petalinux-package --sysroot

 

This will generate sysroots in <plnx_proj>/images/linux/sdk/sysroots
```

## C/C++ 混合プロジェクト
### プロジェクト内でCコードとC++コードを使いたいとき
ライブラリを使うなどしていなければ、プロジェクト作成時点でlanguageにC++を選ぶのが楽。これだと、CソースでもC++ソースでもg++が使われる。extern "C"などで名前解決されていないライブラリを使うとエラーになる可能性がある。

### Cコードにはgccを使い、C++コードにはg++を使いたいとき
ライブラリ(libjpegやOpenAMP)は素のgccでビルドして、自分のコードだけC++(g++)にしたい場合。
プロジェクトはlanguage = Cで作成する。

- cppファイル追加後、右クリック -> Properties
    - Tool Chain Editor -> Select toolにARM v7 g++ compilerを選ぶ
    - Settings -> ARM v7 g++ compiler -> Miscellaneousに`-c -fmessage-length=0 -MT"$@" -mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=hard`を設定　(Cファイルの設定からコピペ)
    - include/libパスの追加は、cppファイルごとに一つ一つやる必要がある。面倒。。。
- プロジェクトを右クリック -> Properties
    - Settings -> ARM v7 gcc linker -> Commandに`arm-none-eabi-g++`を設定

設定は念のため、適当にC++プロジェクトを作ってコピペした方がいい。
XSDKに限らず、一般のEclipseプロジェクトでも同じ方法でいけるはず。

# PetaLinux
## Xilinx製IPのデバイスドライバ
Xilinx SDKのベアメタルプロジェクトで作られるBSPに入っているような、デバイスドライバライブラリを使いたい (例えば、XGpio_CfgInitializeやXGpio_DiscreteWrite)。
GPIOやDMAなどのメジャーどころは、用意されている。http://www.wiki.xilinx.com/Linux+Drivers
それ以外は、自分で書くかmmapでレジスタ直書きしかない。

https://forums.xilinx.com/t5/Embedded-Linux/How-can-I-generate-a-BSP-for-linux/td-p/404639

XGpio_CfgInitializeにmmapしたアドレス設定するだけで、簡単に移植出来たりしないのかな?

# SDSoC
## Xilinx SDSoC 2017.4はZYBO未サポート
2018年1月23日現在の状況です。


__ZYBOしか持っていない人は、SDSoC 2017.4をインストールしても、使用できないのでご注意ください__

SDSoCの2017.2以前だと、以下のようにZYBOがサポートされている旨が記載されています。

```txt:SDSoC_2017.2_リリースノート
すべての ZCU102 プラットフォーム (C/C++ アプリケーションのみ)、ZC702、ZC706、Zybo、
ZedBoard、MicroZed プラットフォームをサポート。
```

2017.4だと、以下のように、現状は一般ユーザは使用できないようです。

```txt:SDSoC_2017.4_リリースノート
Zybo および MicroZed プラットフォームはボードベンダーからのみ使用可能。
```

ちょうど先日、Digilentのフォーラムで対応状況に関して質問が上がっていました。しばらくは動向を見守るしかなさそうです。

https://forum.digilentinc.com/topic/8827-zybo-sdsoc-20174-support/

## SDS pragmaを追加したのに反映されない
ハードウェア化する関数宣言(ヘッダ)にSDS pragmaを追加しても反映されないことがあります。
その場合は、Application Project Settings画面のHardware Functionsからその関数を一度削除して、再度登録することで反映されます。

## マクロ関数からのハードウェア化関数呼び出しはできないっぽい
CPU側コードからハードウェア化関数を呼ぶときに、Cのマクロ経由だと、ビルドは通り実行は出来るが、遅かった。Hardware Functionsに追加しないときと同じくらいの速度だったから、ハードウェア化されていないっぽい。

```c
#define CALL(func) func();

int main(int argc, char **argv)
{
//	myIP();
	CALL(myIP);
}
```

# ZYBO