FPGAオンボードメモリのデバッグ　～テストパターン生成/表示画面のキャプチャ～

# 1. はじめに
今年もやってきましたIntel Advent Calendar
12月13日投稿予定なので、13日に時間取って書けばいいやと高を括っていましたが、ふと当日の朝公開されることに気付き、土日に慌てて書いています（汗）

本稿では、[**FPGA**](https://jp.mathworks.com/discovery/fpga.html)**ボードに搭載されているDDRメモリをMATLABの機能を使ってデバッグ**する方法についてご紹介します。

ここでは例としてモニターディスプレイのパターンジェネレータの表示データを読み書きする内容となっていますが、この機能を利用すると、PC上で作成したデータをFPGAから出力したり、逆に何からの処理を行った結果をDDRメモリから吸い上げてデバッグするのに利用できます。



# 2. 使用ボード Intel DE10-Nano
本稿で使用したボードは**Intel DE10-Nano**です。特にボードに依存したことは行なっていないので、他のボードでも同様に動作すると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/e20cc0da-6d0a-5081-97db-8cc64b87b971.png)

このボードはセンサー類が搭載されたドーターボードとセットになったIntel FPGA Cloud Connectivity Kitのマザーボードとなっています。
Intel FPGA Cloud Connectivity Kitはこのあたりの記事が参考になると思います。

https://www.intel.co.jp/content/www/jp/ja/products/docs/programmable/cloud-connectivity-solution-brief.html

https://qiita.com/milkyellow/items/386fad612eb332766c21

このボード、仕事絡みでお借りしているものを、ずっと借りっぱなしになっておりまして、すいませんTさん・・・この場を借りてお詫び申し上げますm(_ _)m

# 3. AXI Managerの設定
オンボードメモリへのアクセスはMATLAB/[HDL Verifier](https://jp.mathworks.com/products/hdl-verifier.html)の[AXI Manager](https://jp.mathworks.com/help/hdlverifier/axi-manager.html)という機能を使用して行います。

AXI Managerは、FPGAに組み込む[IPコア](https://jp.mathworks.com/discovery/ip-core-generation.html)が提供されており、これを組み込んでFPGAを実装すると、**PCからJTAGやEthernetでレジスタやメモリにアクセス**できるようになるという機能です。

レジスタアクセス機能を使ってFPGAボードを制御する記事は、以前こちらに投稿しました。

https://qiita.com/SacredTubes/items/8ac3ba4eee3d3e4ebbc2

今回はオンボードのDDRメモリにアクセスしてRead/Writeを行なっています。

まずMATLAB上で、AXI Manager IPファイルの保存場所を確認します。

```matlab:
>> setupAXIManagerForQuartus
The Quartus project was not specified. You can manually add C:\ProgramData\MATLAB\SupportPackages\R2022b\toolbox\hdlverifier\
supportpackages\fpgadebug_intel\+hdlverifier\+fpga\+quartus to the IP 
search path setting of your Qsys file.
```

Quartus > Platform Designer(Qsys)を起動し、上記パスを読み込むと、AXI ManagerのIPが見つかります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/768ee0b9-c4f8-f37b-1c3d-0505594ddcf8.png)


このIPを追加して接続すると使えるようになります。

# 4. 既存のシステムにAXI Managerを追加
DE10-NanoのDemoファイルに含まれていた、DDR3_VIPを流用しました。

https://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=167&No=1046&PartNo=4#contents

このDemoデザインでは、
Test Pattern Generator > FrameBuffer/DDR Memory > Clock Video Out
という構成で、生成したテストパターンを一旦DDRメモリでバッファリングしてから、ビデオ出力しています。

このDemoデザインのPlatform Designerを起動し、AXI ManagerのIPを追加して、DDRメモリやTest Pattern Generator IPとFrame Buffer IPのコントロールレジスタと接続します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/d661a87d-7ab6-d238-d52d-73c1d7f31e83.png)

コントロールレジスタにも接続したのは、パターン生成のOn/Offを制御するためです。

アドレスマップを設定します。
Test Pattern GeneratorとFrame Bufferのコントロールレジスタは、それぞれ0x4000_0000, 0x5000_0000、DDRメモリは0x0000_0000に設定しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/19f19189-9c70-ec7b-9d8f-4c023a0a710d.png)

あとはPlatform DesignerでHDLをGenerateし、QuartusでコンパイルしたらFPGA側の準備は完了です。

# 5. MATLABからアクセス

SOFファイルをダウンロードしたら、MATLABから次のコマンドでAXI Managerのセットアップを行います。

```matlab:
>> h = aximanager('Intel') 
h = 
  AXIManagerJTAG のプロパティ:

           Vendor: 'Intel'
    JTAGCableName: 'auto'
        DataWidth: 32
        AddrWidth: 32
```

次にTest Pattern GeneratorとFrame Bufferのコントロールレジスタを叩き、Enableします。

```matlab:
>> writememory(h, 0x40000000, 1)    % Enable Test Pattern Generator 
>> writememory(h, 0x50000000, 1)    % Enable Frame Buffer
```

Test Pattern GeneratorをDisableし、代わりにフレームバッファにデータを書き込みます。
```matlab:
>> writememory(h, 0x40000000, 0)    % Disable Test Pattern Generator

% メモリから100ワードのデータを読み出し
readData = readmemory(h, 0, 100);

% メモリに100ワードの0データを書き込み
>> writememory(h, 0, zeros(1, 100, 'uint32'));
```

# 6. 実行結果

はい、こちらはTest Pattern Generatorが生成したDDRメモリのデータを読み出して、MATLABで表示した結果。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/8280ca51-c8f6-01a4-a863-72b428ea793a.png)

また、逆にこちらのMATLAB画像データを↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/5ff5115b-f5fd-1a02-9e27-da82f2760734.png)

オンボードのDDRメモリに書き込んで表示した結果↓。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/dda933ba-46f9-622c-54ca-4c7edd77fa08.png)

# 7. 最後に
### DDRメモリ内の画像データの並び順
画像データがDDRメモリにどのような順序で書き込まれているか確認するのに結構時間がかかりました。
備忘録として書いておきたいと思います。

Intel Video and Image Processing SuiteのAvalon Streaming Videoなどのドキュメントと、実際にデータを見て確認しました。

https://www.intel.com/content/www/us/en/docs/programmable/683416/19-4/avalon-streaming-video.html

下図のようにB=>G=>Rの順でDDRメモリに格納されているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/4c47c9cb-59b5-5e04-2ed3-c5148b5f95a9.png)

ちなみにRGBの配列順序を間違えるとこんな表示になってしまいます。
元画像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/f0d2c169-4fb6-277b-3d53-bffd78763708.png)

表示画像
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/b370d22a-427d-3281-f924-24182d2deb2c.png)


### MATLABでの固定小数点操作
また、MATLABでRGB 8bit の画像データから 32bit のDDRメモリに格納されるデータ配列に変換する際の固定小数点操作に非常に時間がかかって困りました。当初、1枚の画像データを変換する以下のコードに71分もかかっており、とてもじゃないけど実用的に使えそうもありませんでした。

```matlab:
imageSize = [768, 1024];  % v, h

nt8 = numerictype(0, 8, 0);
nt32 = numerictype(0, 32, 0);

inputImageP = imresize(imread('peppers.png'), imageSize);
figure, imshow(inputImageP), shg

% RGB => BGR
inputImagePP = inputImageP(:,:,[3 2 1]);

% BGRBGRの順に並び替え 1024*768*3
serialImageP = reshape(permute(inputImagePP, [3, 2, 1]), [prod(imageSize)*3, 1, 1]);

% Pre Allocation
writeDataP = fi(zeros(1, prod(imageSize)*3/4), nt32);

tic  
for n = 0:length(serialImageP)/4-1
    writeDataP(n+1) = fi(bitconcat(...
        fi(serialImageP(n*4+4), nt8),...
        fi(serialImageP(n*4+3), nt8),...
        fi(serialImageP(n*4+2), nt8), ...
        fi(serialImageP(n*4+1), nt8)), nt32);
end
toc
```

これはfiaccelコマンドでプリコンパイルすることで、なんと9万倍！に超高速化して解決できました。
これに関しては別の記事[「MATLABの固定小数点演算って時間かかりすぎじゃね？」](https://qiita.com/SacredTubes/items/10d9f2aa714d2970db9b) と [「Simulinkの固定小数点演算って速くね？」](https://qiita.com/SacredTubes/items/7311625f20cef285b3ce) で詳しく書いています。

終わり