記事にするまでもないことのメモ。自分用。
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ビット型のポインタを使用したとき
#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。
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ピクセルアライメントである必要がある」という制約がつく。
16ビット型のポインタを使用したとき
32ビットと同様に、除算(srem, urem
)はなくなったが、速度は実測で約15msec。なんでだろう??? たぶん、AXIバースト転送アラインとか調べてるのかな??
ap_uint<>型をシフトするときに注意
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の説明
Nasne (PC TV Plus)をしながらC synthesisすると死ぬ
長時間かかるPIPELINEを有効にしているとき、2/2の確率で死んだ。
高位合成結果、TimingのEstimatedがTargetをオーバーしてしまった
メニューバー -> Solution -> Solution Settings -> Synthesis、Uncertiantyの値を3とかにする。
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以降
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++
を設定
- Settings -> ARM v7 gcc linker -> Commandに
設定は念のため、適当にC++プロジェクトを作ってコピペした方がいい。
XSDKに限らず、一般のEclipseプロジェクトでも同じ方法でいけるはず。
PetaLinux
Xilinx製IPのデバイスドライバ
Xilinx SDKのベアメタルプロジェクトで作られるBSPに入っているような、デバイスドライバライブラリを使いたい (例えば、XGpio_CfgInitializeやXGpio_DiscreteWrite)。
GPIOやDMAなどのメジャーどころは、用意されている。http://www.wiki.xilinx.com/Linux+Drivers
それ以外は、自分で書くかmmapでレジスタ直書きしかない。
XGpio_CfgInitializeにmmapしたアドレス設定するだけで、簡単に移植出来たりしないのかな?
SDSoC
Xilinx SDSoC 2017.4はZYBO未サポート
2018年1月23日現在の状況です。
ZYBOしか持っていない人は、SDSoC 2017.4をインストールしても、使用できないのでご注意ください
SDSoCの2017.2以前だと、以下のようにZYBOがサポートされている旨が記載されています。
すべての ZCU102 プラットフォーム (C/C++ アプリケーションのみ)、ZC702、ZC706、Zybo、
ZedBoard、MicroZed プラットフォームをサポート。
2017.4だと、以下のように、現状は一般ユーザは使用できないようです。
Zybo および MicroZed プラットフォームはボードベンダーからのみ使用可能。
ちょうど先日、Digilentのフォーラムで対応状況に関して質問が上がっていました。しばらくは動向を見守るしかなさそうです。
SDS pragmaを追加したのに反映されない
ハードウェア化する関数宣言(ヘッダ)にSDS pragmaを追加しても反映されないことがあります。
その場合は、Application Project Settings画面のHardware Functionsからその関数を一度削除して、再度登録することで反映されます。
マクロ関数からのハードウェア化関数呼び出しはできないっぽい
CPU側コードからハードウェア化関数を呼ぶときに、Cのマクロ経由だと、ビルドは通り実行は出来るが、遅かった。Hardware Functionsに追加しないときと同じくらいの速度だったから、ハードウェア化されていないっぽい。
#define CALL(func) func();
int main(int argc, char **argv)
{
// myIP();
CALL(myIP);
}