FC2カウンター FPGAの部屋 2016年11月

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

2台の Zybot での隊列走行4(追従走行用アプリケーションの作成1)

2台の Zybot での隊列走行3(追従用Zybotの戦略)”の続き。

前回は、追従用Zybot の戦略を考えたが、今回はそれに基づいて追従走行用アプリケーション・ソフトウェアを作成し、認識テストを行った。

前車の認識パターンは、水色の四角の中の赤い四角の大きさやその位置で追従走行のための標準的な位置のセンター、右、もっと右、左、もっと左、の5パターンと、小さいサイズ(つまり遠い位置)のセンター、右、もっと右、左、もっと左、の5パターンの合計10パターンある。それぞれに対してモーターの制御を行っている。

追従走行用のアプリケーションの表示テストは前車に水色の四角の中の赤い四角の書いてあるマーカーをつけて、後車のカメラで撮影してその赤四角の大きさと位置を判定している。
platoon_20_161129.jpg

platoon_21_161129.jpg

水色の四角の中の赤い四角が小さいサイズ(つまり遠い位置)で、画像のセンターにある場合を示す。この画像は、RGBをHSV変換して、そのままRGBとして表示しているので、色がおかしくなっている。赤色が水色になっているので注意。
platoon_22_161129.jpg

これを、追従走行用アプリケーションで認識した結果を示す。
platoon_30_161129.png
良い具合に認識している。

ノーマルサイズのセンターの場合。
platoon_23_161129.jpg

platoon_31_161129.png
1つミスっているが、これはカメラからの画像がシングルバッファに転送されて、追従走行用アプリケーションで見ているので、画像が乱れている部分だと思われる。

ノーマルサイズの右の場合。
platoon_24_161129.jpg

platoon_32_161129.png

ノーマルサイズのもっと右の場合。
platoon_25_161129.jpg

platoon_33_161129.png

ノーマルサイズの左の場合。
platoon_26_161129.jpg

platoon_34_161129.png

ノーマルサイズのもっと左の場合。
platoon_27_161129.jpg

platoon_35_161129.png

たまにおかしくなることがあるものの追従走行用の前車の位置の認識は大体できているようだ。後は、モーターを動かして追従走行を確認してみよう。
  1. 2016年11月30日 04:58 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

SDSoC 2016.2 でラプラシアンフィルタをテスト2

SDSoC 2016.2 でラプラシアンフィルタをテスト1”の続き。

前回は、SDSoC 2016.2 を使用して、すべてソフトウェアで実装したラプラシアンフィルタをコンパイルし、ZYBO 実機でテストした。今回は、ラプラシアンフィルタをハードウェアにして性能を確かめてみよう。なお、SDSoC 2015.2 の時の記事は”SDSoC 2015.2 でハードウェアとソフトウェアのラプラシアンフィルタの性能を比較した4(ハードウェア化)

project.sdsoc を開いて、Hardware Functions で Add Hardware Function の+記号をクリックする。
SDSoC_2016_2_18_161128.png

Select function for hardware acceleration ダイアログが開く。

lap_filter_axim(int *, int *, int, int) をクリックして、ハードウェア化する。
SDSoC_2016_2_19_161128.png

Hardware Functions に lap_filter_axim が入った。
SDSoC_2016_2_20_161128.png

トンカチの形のBuild ボタンをクリックしてビルドしたところ、エラーが発生した。
SDSoC_2016_2_21_161128.png

SDSoC 2015.2 でハードウェアとソフトウェアのラプラシアンフィルタの性能を比較した4(ハードウェア化)”と同様のエラーだった。
エラー内容を示す。

ERROR: [SDSoC 0-0] Function "lap_filter_axim" argument "cam_fb" is mapped to RAM interface, but it's size is bigger than 16384. Please specify #pragma SDS data zero_copy(cam_fb) or #pragma SDS data access_pattern(cam_fb:SEQUENTIAL)
ERROR: [SDSoC 0-0] Failed to generate interface tcl script: C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/vhls/lap_filter_axim.tcl
ERROR: [SDSoC 0-0] Exiting sdscc : Error when calling 'pragma_gen -func lap_filter_axim -tcl C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/vhls/lap_filter_axim.tcl C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/vhls/laplacian_filter2_pp.c -- -c -D __SDSVHLS__ -IC:/Users/Masaaki/workspace/lap_filter2/src -Wall -O0 -g -fmessage-length=0 -MMD -MP -D __SDSCC__ -I C:/HDL/Xilinx/SDSoC/2016.2/aarch32-linux/include -IC:/Users/Masaaki/workspace/lap_filter2/src -target arm-linux-gnueabihf -mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=hard -O0 -g -w -I C:/HDL/Xilinx/SDSoC/2016.2/aarch32-linux/include -I C:/HDL/Xilinx/SDSoC/2016.2/Vivado_HLS/2016.2/include -IC:/HDL/Xilinx/SDSoC/2016.2/SDK/2016.2/gnu/aarch32/nt/gcc-arm-linux-gnueabi/lib/gcc/arm-linux-gnueabihf/4.9.2/include -IC:/HDL/Xilinx/SDSoC/2016.2/SDK/2016.2/gnu/aarch32/nt/gcc-arm-linux-gnueabi/lib/gcc/arm-linux-gnueabihf/4.9.2/include-fixed -IC:/HDL/Xilinx/SDSoC/2016.2/SDK/2016.2/gnu/aarch32/nt/gcc-arm-linux-gnueabi/arm-linux-gnueabihf/include -IC:/HDL/Xilinx/SDSoC/2016.2/SDK/2016.2/gnu/aarch32/nt/gcc-arm-linux-gnueabi/arm-linux-gnueabihf/libc/usr/include'
sdscc log file saved as C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/reports/sds_laplacian_filter2.log
ERROR: [SDSoC 0-0] Build failed

make: *** [src/laplacian_filter2.o] エラー 1


次に、 #pragma SDS data zero_copy(cam_fb) か #pragma SDS data access_pattern(cam_fb:SEQUENTIAL) をつけろと言われているので、付けることにした。使い方は、”SDSoC 環境ユーザー ガイド UG1027 (v2016.2) 2016 年 7 月 13 日”の22ページ、”コピーおよび共有メモリ セマンティクス”に書いてある。

lap_filter_axim() の前に次に示すようにpragma を付けた。

#pragma SDS data zero_copy(cam_fb[0:ALL_PIXEL_VALUE])
#pragma SDS data zero_copy(lap_fb[0:ALL_PIXEL_VALUE])
int lap_filter_axim(int cam_fb[ALL_PIXEL_VALUE], int lap_fb[ALL_PIXEL_VALUE], int width, int height)
{


SDSoC_2016_2_22_161128.png

セーブしてから、ビルドしたが、エラーだった。
SDSoC_2016_2_23_161128.png

エラー内容を示す。BRAMが足りないと言われている。

ERROR: [Place 30-640] Place Check : This design requires more RAMB36/FIFO cells than are available in the target device. This design requires 353 of such cell types but only 60 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-640] Place Check : This design requires more RAMB18 and RAMB36/FIFO cells than are available in the target device. This design requires 708 of such cell types but only 120 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-640] Place Check : This design requires more RAMB36E1 cells than are available in the target device. This design requires 353 of such cell types but only 60 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-99] Placer failed with error: 'Implementation Feasibility check failed, Please see the previously displayed individual error or warning messages for more details.'
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Common 17-69] Command failed: Placer could not place all instances
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [SDSoC 0-0] Exiting system_linker: Error when calling 'C:/HDL/Xilinx/SDSoC/2016.2/Vivado/2016.2/bin/vivado -mode batch -source "C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/top.impl.tcl"'
ERROR: [SDSoC 0-0] Exiting sds++ : Error when calling 'system_linker -cf-input C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/.llvm/apsys_0.xml -cf-output-dir _sds/p0 -ip-db C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/.cdb/xd_ip_db.xml -ip-repo C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/iprepo/repo -sds-pf C:/HDL/Xilinx/SDSoC/2016.2/platforms/zybo:linux -bitstream -bit-name lap_filter2.elf.bit -boot-files -mdev-no-swgen -mdev-no-xsd -sdsoc -sd-output-dir _sds/p0/sd_card -bit-binary -elf C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/swstubs/lap_filter2.elf'
sds++ log file saved as C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/reports/sds.log
ERROR: [SDSoC 0-0] Build failed

make: *** [lap_filter2.elf] エラー 1


#pragma SDS data zero_copy の代わりに、#pragma SDS data access_pattern(cam_fb:SEQUENTIAL) を付けてみた。

#pragma SDS data access_pattern(cam_fb:SEQUENTIAL)
#pragma SDS data access_pattern(lap_fb:SEQUENTIAL)
int lap_filter_axim(int cam_fb[ALL_PIXEL_VALUE], int lap_fb[ALL_PIXEL_VALUE], int width, int height)
{


セーブして、ビルドしてみたところ、5時間くらいかかってエラーになった。
SDSoC_2016_2_24_161130.png

エラー内容を示す。今度は、LUTなどが足りないとのことだった。

ERROR: [Place 30-640] Place Check : This design requires more F7 Muxes cells than are available in the target device. This design requires 204055 of such cell types but only 8800 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-640] Place Check : This design requires more F8 Muxes cells than are available in the target device. This design requires 11953 of such cell types but only 4400 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-640] Place Check : This design requires more Slice LUTs cells than are available in the target device. This design requires 573217 of such cell types but only 17600 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device. Please set tcl parameter "drc.disableLUTOverUtilError" to 1 to change this error to warning.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-640] Place Check : This design requires more LUT as Logic cells than are available in the target device. This design requires 212504 of such cell types but only 17600 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device. Please set tcl parameter "drc.disableLUTOverUtilError" to 1 to change this error to warning.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-640] Place Check : This design requires more LUT as Memory cells than are available in the target device. This design requires 360713 of such cell types but only 6000 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device. Please set tcl parameter "drc.disableLUTOverUtilError" to 1 to change this error to warning.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-640] Place Check : This design requires more LUT as Distributed RAM cells than are available in the target device. This design requires 360452 of such cell types but only 6000 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device. Please set tcl parameter "drc.disableLUTOverUtilError" to 1 to change this error to warning.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-640] Place Check : This design requires more LUT4 cells than are available in the target device. This design requires 106690 of such cell types but only 35200 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-640] Place Check : This design requires more LUT6 cells than are available in the target device. This design requires 144752 of such cell types but only 17600 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-640] Place Check : This design requires more MUXF7 cells than are available in the target device. This design requires 204055 of such cell types but only 8800 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-640] Place Check : This design requires more MUXF8 cells than are available in the target device. This design requires 11953 of such cell types but only 4400 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-640] Place Check : This design requires more RAMD64E cells than are available in the target device. This design requires 360048 of such cell types but only 6000 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Place 30-99] Placer failed with error: 'Implementation Feasibility check failed, Please see the previously displayed individual error or warning messages for more details.'
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [Common 17-69] Command failed: Placer could not place all instances
INFO: [SDSoC 0-0] See C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/vivado.log for the context of the Vivado message above.
ERROR: [SDSoC 0-0] Exiting system_linker: Error when calling 'C:/HDL/Xilinx/SDSoC/2016.2/Vivado/2016.2/bin/vivado -mode batch -source "C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/p0/ipi/top.impl.tcl"'
ERROR: [SDSoC 0-0] Exiting sds++ : Error when calling 'system_linker -cf-input C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/.llvm/apsys_0.xml -cf-output-dir _sds/p0 -ip-db C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/.cdb/xd_ip_db.xml -ip-repo C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/iprepo/repo -sds-pf C:/HDL/Xilinx/SDSoC/2016.2/platforms/zybo:linux -bitstream -bit-name lap_filter2.elf.bit -boot-files -mdev-no-swgen -mdev-no-xsd -sdsoc -sd-output-dir _sds/p0/sd_card -bit-binary -elf C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/swstubs/lap_filter2.elf'
sds++ log file saved as C:/Users/Masaaki/workspace/lap_filter2/SDDebug/_sds/reports/sds.log
ERROR: [SDSoC 0-0] Build failed

make: *** [lap_filter2.elf] エラー 1

  1. 2016年11月29日 04:08 |
  2. SDSoC
  3. | トラックバック:0
  4. | コメント:0

映画『ファンタスティック・ビーストと魔法使いの旅』を見てきました

今日は奥さんと映画『ファンタスティック・ビーストと魔法使いの旅』を見てきました。
久しぶりの魔法使い映画、ハリーポッターファンとしては嬉しい限りです。また魔法を見ることができます。久しぶりの魔法の映画は面白かったです。しかし、今年の1番は「君の名は」、2番は「シン・ゴジラ」ですね。。。
  1. 2016年11月27日 21:40 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

SDSoC 2016.2 でラプラシアンフィルタをテスト1

前回、SDSoC を試したのはSDSoC 2015.2 の時だったが、今回、SDSoC 2016.2 でもう一度、確かめてみよう。
前回、SDSoC を確かめてみたときの記録は、FPGAの部屋のWebサイト SDSoC を参照ください。

前回やってみたラプラシアンフィルタをそのまま SDSoC 2016.2 でやってみようと思う。
ラプラシアンフィルタのソースコードは、「SDSoC 2015.2 でハードウェアとソフトウェアのラプラシアンフィルタの性能を比較した1(ソースの公開)」を参照ください。

SDSoC 2016.2 を立ちあげてCreate SDSoC Project をクリックした。
SDSoC_2016_2_1_161125.png

New Project ダイアログで、Project name に lap_filter2 と入力して、Target の Platform を zybo にして、OSはデフォルトで Linux が選択されていた。Next > ボタンをクリックした。
SDSoC_2016_2_2_161125.png

Templates で Empty Application を選択して、Finish ボタンをクリックした。
SDSoC_2016_2_3_161125.png

プロジェクトが出来上がった。
SDSoC_2016_2_4_161125.png

Project Explorer の lap_filter_2 -> src に bmp_header.h, lap_fiter_tb.c, laplacian_filter2.c をドラッグ&ドロップした。
SDSoC_2016_2_5_161125.png

File Operation ダイアログが表示された。
ラジオボタンが Copy files に設定されていることを確認して、OK ボタンをクリックした。
SDSoC_2016_2_6_161125.png

lap_filter_2 -> src に bmp_header.h, lap_fiter_tb.c, laplacian_filter2.c が入った。
SDSoC_2016_2_7_161125.png

temp.bmp も lap_filter_2 -> src に追加した。temp.bmp を示す。これは、夏の私の机の風景だ。
SDSoC_2016_2_8_161125.jpg

lap_filter_2 -> src に temp.bmp が追加された。
SDSoC_2016_2_9_161125.png

さて、ビルドしてみよう。Project Explorer のBuild Configuration -> Set Active -> SDRelease を選択した。
SDSoC_2016_2_10_161125.png

Build ボタンをクリックした。
SDSoC_2016_2_11_161125.png

Build が終了した。
SDSoC_2016_2_12_161125.png

ここまでは、SDSoC 2015.2 との違いは無いみたいだ。

次に、lap_filter2\SDRelease\sd_card の内容を SDカードにコピーした。
SDSoC_2016_2_15_161128.png

SDカードを取り外して、ZYBOに挿入し、電源ONでLinux が起動した。
./lap_filter2.elf を起動すると、ソフトウェアでのラプラシアンフィルタの処理時間と、ハードウェアでのラプラシアンフィルタの処理時間が表示された。
ls を実行すると、test_lap.bmp が生成されているのが分かる。
SDSoC_2016_2_16_161128.png

ラプラシアンフィルタ処理後の temp_lap.bmp を示す。
SDSoC_2016_2_17_161128.jpg
  1. 2016年11月27日 07:07 |
  2. SDSoC
  3. | トラックバック:0
  4. | コメント:0

Python の勉強会をやっています

やはり Python を知らなきゃダメでしょう。ということで、職場でPython の勉強会をやっています。
とりあえずは、Python を知らないので、毎週火曜日の午前中にに3人でPython の勉強会を開催しています。
勉強会の方法は、Python-izm のサイトの項目を実演しながら、そう書いたらどうなるの?これを実現するには?という感じで楽しくやっています。

今2回目を終わったところですが、print文までやりました。
配列、連想配列がタプル、リスト、ディクショナリ、セットと種類が豊富なのと、日付の計算が簡単なのが驚きでした。
関数やif の範囲がインデントで決まるのも新鮮ですね。
もっと勉強して、「ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装」を買ったので、これをやってみたいと思います。
  1. 2016年11月26日 06:12 |
  2. パソコン関連
  3. | トラックバック:0
  4. | コメント:0

2台の Zybot での隊列走行3(追従用Zybotの戦略)

2台の Zybot での隊列走行2(先頭のZybotのアプリケーション)”の続き。

前回、隊列走行の前走車のアプリケーションはできた。今回は追従用Zybot の戦略を考える。

追従用のZybot から撮影した写真を示す。最初は32 cm の距離から撮影した。
bmp_file0_t_161125.jpg

bmp_file1_t_161125.jpg

bmp_file2_t_161125.jpg

次に倍の 64 cm から撮影した。
bmp_file5_t_161125.jpg

追従する間隔は 32 cm にしようと思う。

その戦略は今のところこんな感じ。

追従走行ロボットカー動作概要

前のロボットカーのマーカーを追いかける。

前車との標準的な距離は 32㎝ とする。

マーカーは水色の四角の中に赤の四角があるものとする。水色の色相(H)は180、赤の色相は0である。

前車との距離が 32cm でも 64cmでも、マーカーをサーチする際に800ピクセル×600行のうちの上から360行の付近を横にサーチすれば水色の四角と赤の四角を横切るので、その辺りで判定する。

   1.左側から15ピクセル程度水色判定が続いたら、マーカーだと判定する。
   2.赤になった時の幅を確認する。もし赤判定が100ピクセル以下だったら、遠すぎるか、マーカー検出できていないので検出プログラムを停止する。
   3.赤判定が外れて5ピクセル以内に水色判定にならないときはエラー。(CMOSカメラのベイヤ配列のためか?赤、水色の境界では、2ピクセルほど色が赤でも水色でもない状態にある)

前車との標準的な距離 32cm のときの赤い四角の幅は 340 ピクセル、64 cm の時は 144 ピクセル

前車との間隔は 赤判定が340~280 ピクセル程度に収まるように車速を調整する。

赤い四角が画像の真ん中に来るようにする。
   赤い四角が左に寄っている場合 - 右車輪の回転数を上げるか、左車輪の回転数を下げる。
   赤い四角が右に寄っている場合 - 左車輪の回転数を上げるか、右車輪の回転数を下げる。


これをアプリケーション・ソフトウェアとして実装してみよう。
  1. 2016年11月25日 04:48 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

2台の Zybot での隊列走行2(先頭のZybotのアプリケーション)

2台の Zybot での隊列走行1(構想編)”の続き。

Vivadoを使用してZYBO_0_163_6フォルダのプロジェクトにRGB2HSV IPを追加6(UbuntuでRGB2HSVを試す)”でRGB2HSV変換 IP をZYBO のPL に入れたプラットフォームを使用してUbuntu 14.04 を起動してRGB2HSV変換を試すことができた。それで、隊列走行をやってみたい。

まずは、自律走行する先頭Zybot のアプリケーションを作ることにした。
最初は右に曲がった状態にして追従するように書いたが、まっすぐにしたほうが良かったかな?まあ、それはすぐに直せる。
アプリケーション名は platoon_car0.c だ。
起動するとまずは直進する。こうしないと右旋回したときに少ない値のPWM値では動かないからだ。直進で動かしてから1秒後に右旋回する。その後はキー入力で直進、右旋回、左旋回、停止を選べる。

// platoon_car0.cpp
// Autonomously running car
// 2016/11/15 by marsee
//

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

#include "xpwm.h"
#include "xmotor_monitor.h"

#define DIR_LEFT_NORMAL        1
#define DIR_LEFT_REVERSE    0
#define DIR_RIGHT_NORMAL    0
#define DIR_RIGHT_REVERSE    1

#define PIXEL_NUM_OF_BYTES    4
#define SVGA_HORIZONTAL_PIXELS  800
#define SVGA_VERTICAL_LINES     600
#define SVGA_ALL_DISP_ADDRESS   (SVGA_HORIZONTAL_PIXELS * SVGA_VERTICAL_LINES * PIXEL_NUM_OF_BYTES)

// Motor
//
void motor_settings(XPwm *motorLp, XPwm *motorRp){
    XPwm_DisableAutoRestart(motorLp);
    while(!XPwm_IsIdle(motorLp)) ;
    XPwm_Start(motorLp);
    XPwm_EnableAutoRestart(motorLp);
    
     XPwm_DisableAutoRestart(motorRp);
    while(!XPwm_IsIdle(motorRp)) ;
    XPwm_Start(motorRp);
    XPwm_EnableAutoRestart(motorRp);
}

void Stopped_Zybot(XPwm *motorLp, XPwm *motorRp){
    XPwm_Set_sw_late_V(motorLp, 0);
    XPwm_Set_sw_late_V(motorRp, 0);
}

void motor_initialize(XPwm &motorL, XPwm &motorR, XMotor_monitor &mmL, XMotor_monitor &mmR){
    XPwm *motorLp, *motorRp;
    XMotor_monitor *mmLp, *mmRp;
    
    motorLp = &motorL;
    motorRp = &motorR;
    mmLp = &mmL;
    mmRp = &mmR;
    
    // Initialization of motor
    if (XPwm_Initialize(motorLp, "pwm_0") != XST_SUCCESS){
        fprintf(stderr,"pwm_0 (Left) open error\n");
        exit(-1);
    }
    if (XPwm_Initialize(motorRp, "pwm_1") != XST_SUCCESS){
        fprintf(stderr,"pwm_1 (Right) open error\n");
        exit(-1);
    }
    
    
    // Initialization of motor monitor
    if (XMotor_monitor_Initialize(mmLp, "motor_monitor_0") != XST_SUCCESS){
        fprintf(stderr,"motor_monitor_0 (Left) open error\n");
        exit(-1);
    }
    if (XMotor_monitor_Initialize(mmRp, "motor_monitor_1") != XST_SUCCESS){
        fprintf(stderr,"motor_monitor_1 (Right) open error\n");
        exit(-1);
    }

    // The Motors is rotated in the forward direction.
    XPwm_Set_sw_late_V(motorLp, 0);
    XPwm_Set_dir_V(motorLp, 1);

    XPwm_Set_sw_late_V(motorRp, 0);
     XPwm_Set_dir_V(motorRp, 0);

    motor_settings(motorLp, motorRp);
}

int main(){
    XPwm motorL, motorR;
    XMotor_monitor mmL, mmR;
    int c;

    // Motor Initialize
    motor_initialize(motorL, motorR, mmL, mmR);

    XPwm_Set_sw_late_V(&motorL, 25);
    XPwm_Set_sw_late_V(&motorR, 25);
    
    sleep(1);
    
    // right turn
    XPwm_Set_sw_late_V(&motorL, 30);
    XPwm_Set_sw_late_V(&motorR, 10);
    
    // Key input
    // r - right turn, l - left turn, g - Go straight, s - stop, q - quit
    printf("r - right turn, l - left turn, g - Go straight, s - stop, q - quit\n");
    c = getc(stdin);
    while(c != 'q'){
        switch ((char)c) {
            case 'r' : // right turn
                XPwm_Set_sw_late_V(&motorL, 30);
                XPwm_Set_sw_late_V(&motorR, 10);
                break;
            case 'l' : // left turn
                XPwm_Set_sw_late_V(&motorL, 10);
                XPwm_Set_sw_late_V(&motorR, 30);
                break;
            case 'g' : // Go straight
                XPwm_Set_sw_late_V(&motorL, 20);
                XPwm_Set_sw_late_V(&motorR, 20);
                break;
            case 's' : // stop
                XPwm_Set_sw_late_V(&motorL, 0);
                XPwm_Set_sw_late_V(&motorR, 0);
                break;
        }
        c = getc(stdin);
    }

    XPwm_Set_sw_late_V(&motorL, 0);
    XPwm_Set_sw_late_V(&motorR, 0);

    return(0);
}


Zybot でテストしてみたが、十分に良い感じで走行できている。あまり早いと、どこかにぶつかってまずいので、これでよいかな?でも2台相手するのは大変なので、一定時間走ったら停止する機能を付けたほうが良いかな?
これで、先頭のZybot のアプリケーションはできたので、自動追従する 2 番目のZybot の追従方法を考えよう。
  1. 2016年11月24日 04:07 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS によるAXI4 マスタ・インターフェースのバースト転送2

Vivado HLS によるAXI4 マスタ・インターフェースのバースト転送”の続き。

前回は、バース ト ビヘイ ビアーを指定したにもかかわらず、バースト転送しなかった。今回はバースト転送するようにCソースコードを書き換えてみた。

前回のC/RTL協調シミュレーションの波形をよく見ると、AXI4 Master Read の最初のLoop1, Loop2 の部分はバースト転送している。(ARLENが 0f で 16 バースト転送)
Vivado_HLS_AXI4M_burst_9_161121.png

AXI4 Master Write の最後の Loop7, Loop8 もバースト転送している。(AWLEN が 0f で 16 バースト転送)
Vivado_HLS_AXI4M_burst_10_161121.png

やはり、for 文の中に入っていない単独の for 文で、ループがなく単純なAXI4 Master アクセスだけだったらバースト転送できるのではと思った。

そこで、AXI4 Master Read と RGBから輝度信号への変換 (conv_rgg2y()) を rdma_and_rgb2y() 関数にすることにした。
AXI4 Master Write は wdam_buf() という関数を作って、それを使うことにした。

新しい laplacian_filter3.c を示す。

// laplacian_filter3_2.c
// 2016/11/22 by marsee
//

#define HORIZONTAL_PIXEL_WIDTH    64
#define VERTICAL_PIXEL_WIDTH    48

#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);
void rdma_and_rgb2y(int *buf, volatile int *mem, int tripcount);
void wdma_buf(int *buf, volatile int *mem, int tripcount);

int lap_filter_axim(volatile int *cam_fb, volatile int *lap_fb)
{
#pragma HLS INTERFACE s_axilite port=return

#pragma HLS INTERFACE m_axi depth=3072 port=cam_fb offset=slave bundle=cam_fb num_read_outstanding=4 max_read_burst_length=16
#pragma HLS INTERFACE m_axi depth=3072 port=lap_fb offset=slave bundle=lap_fb num_write_outstanding=4 max_write_burst_length=16

    int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
#pragma HLS array_partition variable=line_buf block factor=3 dim=1
#pragma HLS resource variable=line_buf core=RAM_2P
    int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    int x, y;
    int lap_fil_val;
    int fl, sl, tl;

    // 最初に2ラインの画素を読み込む
    Loop1: for (y=0; y<2; y++){ // 2ライン分
        rdma_and_rgb2y(&line_buf[y][0], &cam_fb[(y*HORIZONTAL_PIXEL_WIDTH)], HORIZONTAL_PIXEL_WIDTH);
    }

    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    Loop2: for (y=2; y<VERTICAL_PIXEL_WIDTH; y++){
        rdma_and_rgb2y(&line_buf[y%3][0], &cam_fb[(y*HORIZONTAL_PIXEL_WIDTH)], HORIZONTAL_PIXEL_WIDTH);

        lap_buf[0] = 0// ラインの最初に0を代入
        lap_buf[HORIZONTAL_PIXEL_WIDTH-1] = 0// ラインの最後に0を代入

        Loop3: for (x=2; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE II=1
            fl = (y-2)%3;    // 最初のライン, y=2 012, y=3 120, y=4 201, y=5 012
            sl = (y-1)%3;    // 2番めのライン
            tl = y%3;        // 3番目のライン
            lap_fil_val = laplacian_fil(line_buf[fl][x-2], line_buf[fl][x-1], line_buf[fl][x], line_buf[sl][x-2], line_buf[sl][x-1], line_buf[sl][x], line_buf[tl][x-2], line_buf[tl][x-1], line_buf[tl][x]);
            lap_buf[x-1] =  (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val ;
        }
        wdma_buf(&lap_buf[0], &lap_fb[((y-1)*HORIZONTAL_PIXEL_WIDTH)], HORIZONTAL_PIXEL_WIDTH);
     }
     Loop4: for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++) // 最初の行に 0 を入れる
#pragma HLS PIPELINE II=1
         lap_fb[x] = 0;

     Loop5: for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++) // 最後の行に 0 を入れる
#pragma HLS PIPELINE II=1
         lap_fb[HORIZONTAL_PIXEL_WIDTH*(VERTICAL_PIXEL_WIDTH-1)+x] = 0;

     return(0);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    int y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}

void rdma_and_rgb2y(int *buf, volatile int *mem, int tripcount){
    int i;

    Loop20: for (i=0; i<tripcount; i++){
#pragma HLS PIPELINE II=1
        *buf = *mem++;
        *buf++ = conv_rgb2y(*buf);
    }
}

void wdma_buf(int *buf, volatile int *mem, int tripcount){
    int i;

    Loop20: for (i=0; i<tripcount; i++)
#pragma HLS PIPELINE II=1
        *mem++ = *buf++;
}


Vivado_HLS_AXI4M_burst_11_161122.png

Cコードの合成を行った。結果を示す。
Vivado_HLS_AXI4M_burst_12_161122.png

C/RTL協調シミュレーションを行った。
Vivado_HLS_AXI4M_burst_13_161122.png

C/RTL協調シミュレーションの波形を示す。
まずは、AXI4 Master Read から、ARLEN を見ると 0f で 16 バースト転送している。
Vivado_HLS_AXI4M_burst_14_161122.png

次に、AXI4 Master Write を見てみた。こちらも AWLEN を見ると 0f で 16 バースト転送している。
Vivado_HLS_AXI4M_burst_15_161122.png

一旦、バースト ビヘービアーを外して

#pragma HLS INTERFACE m_axi depth=3072 port=cam_fb offset=slave bundle=cam_fb
#pragma HLS INTERFACE m_axi depth=3072 port=lap_fb offset=slave bundle=lap_fb

Cコードの合成、C/RTL協調シミュレーションを行って、C/RTL協調シミュレーションの波形見たところ、やはり 16 バースト転送だった。
バースト ビヘービアーはその名の通りバースト転送のパラメータを設定するだけで、バースト転送になるかどうか?は別物なんだと思う。つまり、Cソースコードや指示子の与え方でバースト転送になるかどうか?が決まって、バースト転送になった後の設定値はバースト ビヘービアーが決めるのだと思う。

次に、

#pragma HLS INTERFACE m_axi depth=3072 port=cam_fb offset=slave bundle=cam_fb num_read_outstanding=4 max_read_burst_length=32
#pragma HLS INTERFACE m_axi depth=3072 port=lap_fb offset=slave bundle=lap_fb num_write_outstanding=4 max_write_burst_length=32

というようにバースト ビヘービアーで 32 バースト転送にしてみた。
これで、Cコードの合成、C/RTL協調シミュレーションを行ったが、結果の数値は16 バースト転送の時と同じだった。
C/RTL協調シミュレーションの波形を見たところ、ARLEN と AWLEN が 1f で 32 バースト転送になっているのが分かった。
Vivado_HLS_AXI4M_burst_16_161122.png

Vivado_HLS_AXI4M_burst_17_161122.png
  1. 2016年11月22日 04:47 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS によるAXI4 マスタ・インターフェースのバースト転送

少なくとも、Vivado HLS 2015.4 時代からは、Vivado HLS によるAXI4 マスタ・インターフェースのバースト転送が memcpy() 関数を使うだけでなく、ある条件の for 文で AXI4 マスタポートを使用したときにも適用されるようになった。

Vivado HLS 2016.3 では、バース ト ビヘイ ビアーを指定できるようになっている。(Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2016.3) 2016 年 10 月 5 日の112 ページ)
latency、max_read_burst_length、num_read_outstanding、max_write_burst_length、num_write_outstanding が指定できるそうだ。
Vivado HLS 2016.3 のディレクトリ・エディタでもなぜか latency は指定できないがそれ以外のバース ト ビヘイ ビアーは指定することができる。

いつも例に出しているラプラシアンフィルタの実装を作った。if 文を無くし、可能な限りパイプラインできるように作った。
C ソースコードを示す。

// laplacian_filter3.c
// 2016/11/21 by marsee
//

#define HORIZONTAL_PIXEL_WIDTH    64
#define VERTICAL_PIXEL_WIDTH    48

#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2);
int conv_rgb2y(int rgb);

int lap_filter_axim(volatile int *cam_fb, volatile int *lap_fb)
{
#pragma HLS INTERFACE s_axilite port=return

#pragma HLS INTERFACE m_axi depth=3072 port=cam_fb offset=slave bundle=cam_fb
#pragma HLS INTERFACE m_axi depth=3072 port=lap_fb offset=slave bundle=lap_fb

    int line_buf[3][HORIZONTAL_PIXEL_WIDTH];
#pragma HLS array_partition variable=line_buf block factor=3 dim=1
#pragma HLS resource variable=line_buf core=RAM_2P
    int lap_buf[HORIZONTAL_PIXEL_WIDTH];
    int x, y;
    int lap_fil_val;
    int fl, sl, tl;

    // 最初に2ラインの画素を読み込む
    Loop1: for (y=0; y<2; y++){ // 2ライン分
        Loop2: for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){ // ライン
#pragma HLS PIPELINE II=1
            line_buf[y][x] = cam_fb[(y*HORIZONTAL_PIXEL_WIDTH)+x];
            line_buf[y][x] = conv_rgb2y(line_buf[y][x]);
        }
    }

    // RGB値をY(輝度成分)のみに変換し、ラプラシアンフィルタを掛けた。
    Loop3: for (y=2; y<VERTICAL_PIXEL_WIDTH; y++){
        Loop4: for (x=0; x<2; x++){
#pragma HLS PIPELINE II=1
                line_buf[y%3][x] = cam_fb[(y*HORIZONTAL_PIXEL_WIDTH)+x];
                line_buf[y%3][x] = conv_rgb2y(line_buf[y%3][x]);
        }
        lap_buf[0] = 0// ラインの最初に0を代入
        lap_buf[HORIZONTAL_PIXEL_WIDTH-1] = 0// ラインの最後に0を代入

        Loop5: for (x=2; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE II=1
            // 1つのピクセルを読み込みながらラプラシアン・フィルタを実行する
            line_buf[y%3][x] = cam_fb[(y*HORIZONTAL_PIXEL_WIDTH)+x];
            // y%3 は、使用済みのラインがに読み込む、y=2 の時 line[0], y=3の時 line[1], y=4の時 line[2]
            line_buf[y%3][x] = conv_rgb2y(line_buf[y%3][x]);

            fl = (y-2)%3;    // 最初のライン, y=2 012, y=3 120, y=4 201, y=5 012
            sl = (y-1)%3;    // 2番めのライン
            tl = y%3;        // 3番目のライン
            lap_fil_val = laplacian_fil(line_buf[fl][x-2], line_buf[fl][x-1], line_buf[fl][x], line_buf[sl][x-2], line_buf[sl][x-1], line_buf[sl][x], line_buf[tl][x-2], line_buf[tl][x-1], line_buf[tl][x]);
            lap_buf[x-1] =  (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val ;
        }
        Loop6: for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++)
#pragma HLS PIPELINE II=1
            lap_fb[((y-1)*HORIZONTAL_PIXEL_WIDTH)+x] = lap_buf[x]; // ラプラシアンフィルタ・データの書き込み
     }
     Loop7: for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++) // 最初の行に 0 を入れる
#pragma HLS PIPELINE II=1
         lap_fb[x] = 0;

     Loop8: for (x=0; x<HORIZONTAL_PIXEL_WIDTH; x++) // 最後の行に 0 を入れる
#pragma HLS PIPELINE II=1
         lap_fb[HORIZONTAL_PIXEL_WIDTH*(VERTICAL_PIXEL_WIDTH-1)+x] = 0;

     return(0);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8// 256で割る

    return(y);
}

// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1  8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int laplacian_fil(int x0y0, int x1y0, int x2y0, int x0y1, int x1y1, int x2y1, int x0y2, int x1y2, int x2y2)
{
    int y;

    y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
    if (y<0)
        y = 0;
    else if (y>255)
        y = 255;
    return(y);
}


Vivado HLS 2016.3 のプロジェクトを示す。
Vivado_HLS_AXI4M_burst_1_161121.png

C コードの合成結果を示す。
Vivado_HLS_AXI4M_burst_2_161121.png

C/RTL協調シミュレーション結果を示す。
Vivado_HLS_AXI4M_burst_3_161121.png

C/RTL協調シミュレーションの波形を示す。
lap_fb から示す。AWLENが 00 になっている。これはシングル転送だ。
Vivado_HLS_AXI4M_burst_4_161121.png

cam_fb を示す。やはり、ARLENが 00 になっている。これもシングル転送だ。
Vivado_HLS_AXI4M_burst_5_161121.png

次に、cam_fb には read のバースト ビヘイ ビアーを指定し、lap_fb には write のバースト ビヘイビアーを指定した。
指定には、ディレクテブ・エディタを使用した。
Vivado_HLS_AXI4M_burst_6_161121.png

Vivado_HLS_AXI4M_burst_7_161121.png

#pragma HLS INTERFACE m_axi depth=3072 port=cam_fb offset=slave bundle=cam_fb num_read_outstanding=4 max_read_burst_length=16
#pragma HLS INTERFACE m_axi depth=3072 port=lap_fb offset=slave bundle=lap_fb num_write_outstanding=4 max_write_burst_length=16


これでもう一度、Cコードの合成をしてみたが、以前と変化はなかった。
Vivado_HLS_AXI4M_burst_8_161121.png

C/RTL協調シミュレーションをしてみたが、以前と変化はない。

C/RTL協調シミュレーションの波形を見てみたが、やはり、AWLENとARLENは 00 のままだった。

バースト ビヘイビアーは効いていないのだろうか?
  1. 2016年11月21日 04:51 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

つくばマラソンの10Kmに出場しました

今日は、つくばマラソンの10Kmに出場しました。
奥さんはフルマラソンに出場です。

例によって、いつも教室に行っている阿見アスリートクラブのブースにお邪魔してお世話になりました。
フルマラソンは9時から2ブロックずつ5分ごとのウェーブスタートです。奥さんの出るEブロックは、9時15分のスタートでした。
スタート地点はかなりの人でしたね。こんなにたくさんの人がフルマラソンを走るんですね。
tukuba_10Km_1_161121.jpg

奥さんは「私をサブ4につれてって」Tシャツを着て走りました。サブ4とは、フルマラソンを4時間以内に走りきることです。それには、42.195Kmを5分40秒/Km程度のスピードで走りきる必要があります。

私は9時55分に10Kmのスタートでした。大体、5分45秒/Kmのペースで走る予定でしたが、大体達成できました。少し苦しかったですが。。。
結果です。
記録 58分41秒
順位 938位
ネットタイム 57分21秒

ネットタイムが自分のスタートラインをスタートしてからゴールまでの時間です。記録はスタートの号令がなった時点からゴールまでの時間です。スタートに行くまでが長いのでそうなります。ネットタイムを見ると目標のペースが達成できていることが分かります。
Gramin connect のデータを公開します。ちょっと恥ずかしいですが、今の実力です。

奥さんのフルマラソンのタイムは4時間8分でした。残念ながら、サブ4 には届きませんでした。
  1. 2016年11月20日 20:46 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

Vivadoを使用してZYBO_0_163_6フォルダのプロジェクトにRGB2HSV IPを追加6(UbuntuでRGB2HSVを試す)

Vivadoを使用してZYBO_0_163_6フォルダのプロジェクトにRGB2HSV IPを追加5(Ubuntuでの動作)”の続き。

前回は、BOOT.bin や devicetree.dtb を作成して、SDカードに書き込み、ZYBOに挿入して電源ONした。Ubuntu が起動して、それ上でカメラの動作を確認できた。今回は、RGB2HSV IP を使うアプリケーション・ソフトウェアを作成して、RGB2HSV変換を行った。

まずは、
./udmabuf_insmod
./cam_disp_plat

コマンドでカメラ画像を表示させた。
platoon_16_161118.png

まずは、./rgb2hsv_rev_plat コマンドで、RGB2HSV した画像をRGB に戻した。これは通常のカメラ画像と同じだった。

次に、./rgb2hsv_ho_plat コマンドで、S と V を 255 にしたときのRGB の値を出力するようにした。
platoon_17_161118.png

最初に原画像を示す。
platoon_18_161119.jpg

rgb2hsv_ho_plat を実行したときの画像を示す。
platoon_19_161119.jpg

rgb2hsv_rec_plat.c を貼っておく。rgb2hsv_ho_plat.c はrgb2hsv_rec_plat.c の while(1) ループで強制的に max = 255; min = 0; にしている。

//
// rgb2hsv_rev_plat.c
// Created on: 2016/11/15
//      Author: marsee
//
// Refered to http://japan.xilinx.com/support/documentation/sw_manuals_j/xilinx2014_4/ug902-vivado-high-level-synthesis.pdf
//

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>

#define CMA_START_ADDRESS 0x17800000
#define VIDEO_BUFFER_START_ADDRESS  0x18000000    // Limit 0x18800000, 800*600*4 = 2MBytes * 2

#define HORIZONTAL_PIXELS    800
#define ALL_CHAR_OF_1LINE   (HORIZONTAL_PIXELS/8)
#define VERTICAL_LINES      600
#define ALL_CHAR_OF_ROW     (VERTICAL_LINES/8)
#define ALL_DISP_ADDRESS    (HORIZONTAL_PIXELS*VERTICAL_LINES*4)
#define ALL_DISP_CHARACTOR  (HORIZONTAL_PIXELS*VERTICAL_LINES)

int main(){
    int fd1, fd2, fd3, fd4, fd6, fd7, fd9, fd10;
    volatile unsigned *axis_switch_0, *axis_switch_1;
    volatile unsigned *rgb2hsv_0;
    volatile unsigned *dmaw4gabor_0;
    volatile unsigned *bmdc_axi_lites0, *bmdc_axi_lites1;
    volatile unsigned *frame_buffer_bmdc;
    unsigned char  attr[1024];
    unsigned long  phys_addr;
    int i, j;
    int max, min;
    int h, s, v;
    int r, g, b;
    volatile int *fb_hsv, *fb_rgb;


    // axis_switch_0 (UIO2)
    fd2 = open("/dev/uio2", O_RDWR); // axis_switch_0 interface AXI4 Lite Slave
    if (fd2 < 1){
        fprintf(stderr, "/dev/uio2 (axis_switch_0) open error\n");
        exit(-1);
    }
    axis_switch_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
    if (!axis_switch_0){
        fprintf(stderr, "axis_switch_0 mmap error\n");
        exit(-1);
    }
    
    // axis_switch_1 (UIO3)
    fd3 = open("/dev/uio3", O_RDWR); // axis_switch_1 interface AXI4 Lite Slave
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 (axis_switch_1) open error\n");
        exit(-1);
    }
    axis_switch_1 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (!axis_switch_1){
        fprintf(stderr, "axis_switch_1 mmap error\n");
        exit(-1);
    }
    
    // rgb2hsv_0 (UIO15)
    fd4 = open("/dev/uio15", O_RDWR); // rgb2hsv_0 interface AXI4 Lite Slave
    if (fd4 < 1){
        fprintf(stderr, "/dev/uio14 (rgb2hsv_0) open error\n");
        exit(-1);
    }
    rgb2hsv_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd4, 0);
    if (!rgb2hsv_0){
        fprintf(stderr, "lap_filter_axis_0 mmap error\n");
        exit(-1);
    }
    
    // dmaw4gabor_0 (UIO1)
    fd1 = open("/dev/uio1", O_RDWR); // dmaw4gabor_0 interface AXI4 Lite Slave
    if (fd1 < 1){
        fprintf(stderr, "/dev/uio1 (dmaw4gabor_0) open error\n");
        exit(-1);
    }
    dmaw4gabor_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, 0);
    if (!dmaw4gabor_0){
        fprintf(stderr, "dmaw4gabor_0 mmap error\n");
        exit(-1);
    }
    
    // Bitmap Display Controller 0 AXI4 Lite Slave (UIO6)
    fd6 = open("/dev/uio6", O_RDWR); // bitmap_display_controller 0 axi4 lite
    if (fd6 < 1){
        fprintf(stderr, "/dev/uio6 (bitmap_disp_cntrler_axi_master_0) open error\n");
        exit(-1);
    }
    bmdc_axi_lites0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd6, 0);
    if (!bmdc_axi_lites0){
        fprintf(stderr, "bmdc_axi_lites0 mmap error\n");
        exit(-1);
    }
    
    // Bitmap Display Controller 1 AXI4 Lite Slave (UIO7)
    fd7 = open("/dev/uio7", O_RDWR); // bitmap_display_controller axi4 lite
    if (fd7 < 1){
        fprintf(stderr, "/dev/uio7 (bitmap_disp_cntrler_axi_master_0) open error\n");
        exit(-1);
    }
    bmdc_axi_lites1 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd7, 0);
    if (!bmdc_axi_lites1){
        fprintf(stderr, "bmdc_axi_lites1 mmap error\n");
        exit(-1);
    }

    // udmabuf0
    fd9 = open("/dev/udmabuf0", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fd9 == -1){
        fprintf(stderr, "/dev/udmabuf0 open error\n");
        exit(-1);
    }
    frame_buffer_bmdc = (volatile unsigned *)mmap(NULL, 5760000, PROT_READ|PROT_WRITE, MAP_SHARED, fd9, 0);
    if (!frame_buffer_bmdc){
        fprintf(stderr, "frame_buffer_bmdc mmap error\n");
        exit(-1);
    }

    // phys_addr of udmabuf0
    fd10 = open("/sys/devices/virtual/udmabuf/udmabuf0/phys_addr", O_RDONLY);
    if (fd10 == -1){
        fprintf(stderr, "/sys/devices/virtual/udmabuf/udmabuf0/phys_addr open error\n");
        exit(-1);
    }
    read(fd10, attr, 1024);
    sscanf(attr, "%lx", &phys_addr);  
    close(fd10);
    printf("phys_addr = %x\n", (int)phys_addr);
    
    // axis_switch_1, 1to2 ,Select M01_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_1[16] = 0x80000000// 0x40 = 0x80000000; disable, through
    axis_switch_1[17] = 0x80000000// 0x44 = 0x80000000; disable, laplacian filter
    axis_switch_1[18] = 0x80000000// 0x48 = 0x80000000; disable, gabor filter
    axis_switch_1[19] = 0// 0x48 = 0; RGB2HSV
    axis_switch_1[0] = 0x2// 0x0 = 2; Commit registers
    
    // RGB2HSV AXIS Start
    rgb2hsv_0[0] = 0x01// Start bit set
    rgb2hsv_0[0] = 0x80// Auto Restart bit set
    
    // axis_switch_0, 2to1, Select S01_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_0[16] = 0x3// 0x40 = 0x2; RGB2HSV
    axis_switch_0[0] = 0x2// 0x0 = 2; Commit registers
    
    // bitmap display controller settings
    bmdc_axi_lites0[0] = (int)phys_addr+ALL_DISP_ADDRESS; // Bitmap Display Controller 0 start
    bmdc_axi_lites1[0] = (int)phys_addr+ALL_DISP_ADDRESS; // Bitmap Display Controller 1 start

    munmap((void *)axis_switch_0, 0x10000);
    munmap((void *)axis_switch_1, 0x10000);
    munmap((void *)rgb2hsv_0, 0x10000);
    munmap((void *)dmaw4gabor_0, 0x10000);
    munmap((void *)bmdc_axi_lites0, 0x10000);
    munmap((void *)bmdc_axi_lites1, 0x10000);
    
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    close(fd6);
    close(fd7);
 
    fb_hsv = (volatile int *)frame_buffer_bmdc;
    fb_rgb = (volatile int *)((int)frame_buffer_bmdc+(int)ALL_DISP_ADDRESS); 

    while(1){
        for(j=0; j<VERTICAL_LINES; j++){
            for(i=0; i<HORIZONTAL_PIXELS; i++){
                h = (fb_hsv[HORIZONTAL_PIXELS*j+i]/65536) & 0x1ff;
                s = (fb_hsv[HORIZONTAL_PIXELS*j+i]/256) & 0xff;
                v = fb_hsv[HORIZONTAL_PIXELS*j+i] & 0xff;

                max = v;
                min = max - (int)(((float)s/255.0)*(float)max + 0.5);

                if(h>=0 && h<60){
                    r = max;
                    g = (int)(((float)h/60.0)*(float)(max-min)+0.5) + min;
                    b = min;
                }else if(h>=60 && h<120){
                    r = (int)(((120.0-(float)h)/60.0)*((float)(max-min))+0.5) + min;
                    g = max;
                    b = min;
                }else if(h>=120 && h<180){
                    r = min;
                    g = max;
                    b = (int)((((float)h-120.0)/60.0)*((float)(max-min))+0.5) + min;
                }else if(h>=180 && h<240){
                    r = min;
                    g = (int)(((240.0-(float)h)/60.0)*((float)(max-min))+0.5) + min;
                    b = max;
                }else if(h>=240 && h<300){
                    r = (int)((((float)h-240.0)/60.0)*((float)(max-min))+0.5) + min;
                    g = min;
                    b = max;
                }else{// if(h<=300 && h<=360){
                    r = max;
                    g = min;
                    b = (int)(((360.0-(float)h)/60.0)*((float)(max-min))+0.5) + min;
                }
                fb_rgb[HORIZONTAL_PIXELS*j+i] = (r&0xff)*65536 | (g&0xff)*256 | b&0xff;
            }
        }
        sleep(1);
    }
   
    munmap((void *)frame_buffer_bmdc, 576000);
    close(fd9);
    
    return(0);
}

  1. 2016年11月19日 04:22 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

MT9D111をコードを伸ばしてステレオ・カメラにする6(I2CリピータIO付きカメラ・インターフェース基板が届いた)

MT9D111をコードを伸ばしてステレオ・カメラにする3(I2CリピータIO付きカメラ・インターフェース基板)”が昨日届きました。

MT9D111_inf2_3_161117.jpg

実装して試してみるのが楽しみです。
なお、幅を 50 mm から 45 mm に変更したので、ZYBO のPMOD にカメラを 2 つ刺すことができるようになりました。よって、延長ケーブルを使用しないでもステレオカメラにすることもできます。

2016/11/18 追記:
今日、部品を実装して試してみたところ、ZYBOの電源が入りません。カメラを除いてカメラ・インターフェース基板だけで、ZYBOの電源を入れたらI2Cリピーターから煙が出てきました。慌てて、電源をOFFして、基板のパターンを見たら、電源とGNDのパターンが入れ替わっていました。つまり電源とGNDが逆でした。orz
作り直しです。。。
カメラが無事だったのが不幸中の幸いです。
今日、カメラ・インターフェース基板の修正版をFusionPCBに注文しました。今度は黄色にしました。DHLで頼んだので、10日くらいで出来上がると思います。
  1. 2016年11月17日 04:00 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

Vivadoを使用してZYBO_0_163_6フォルダのプロジェクトにRGB2HSV IPを追加5(Ubuntuでの動作)

Vivadoを使用してZYBO_0_163_6フォルダのプロジェクトにRGB2HSV IPを追加4(RGB2HSV)”の続き。

前回はSDK でHSV からRGB に変換するソフトウェアや、H はそのままで、S と V を最高値としたときにRGB に変換するソフトウェアを作成した。今回はUbuntu 上でRGB2HSV 変換IP を入れたBOOT.bin やdevicetree.dtb で動作を確かめた。

まずは、”Vivadoを使用してZYBO_0_163_6フォルダのプロジェクトにRGB2HSV IPを追加4(RGB2HSV)”のZYBO_0_163_6 フォルダのVivado 2016.3 プロジェクトでやってみたのだが、”Vivado 2016.2 のプロジェクトをVivado 2016.3 にアップグレードすると動作しなかった2(解決編)”で、いったんAXI4-Lite のAXI Interconnect を消去して、もう一度Add IP しているので、アドレスがバラバラになってしまっている。よって、devicetree.dtb のアドレスはバラバラになってしまっているので、書き直しが必要だ。当然、Ubuntu 上のアプリケーション・ソフトウェアも全面書き直しが必要なので、面倒だ。よって、Vivado 2016.2 の ZYBO_0_162_6 フォルダのプロジェクトを使うことにした。

ZYBO_0_162_6 フォルダのプロジェクトを示す。
platoon_9_161115.png

SDK でBoot Image を作成した。
platoon_10_161115.png

BOOT.bin が作成された。
platoon_11_161115.png

パソコンのVirtualBox 上のzynq-zybo.dts に rgb2hsv の項を追加した。
platoon_12_161115.png

sudo apt-get install device-tree-compiler で dtc をインストールした。
dtc -I dts -O dtb -o devicetree.dtb zynq-zybo.dts コマンドで devicetree.dtb を生成した。
platoon_13_161115.png

出来上がったBOOT.bin と devicetree.dtb をSDカードにコピー&ペーストして、ZYBO に入れ、電源ON でブートした。
/sys/devices/amb.0 に cd すると 43cc0000.rgb2hsv_0 が見えた。
platoon_14_161115.png

./udmabuf_insmod
./cam_disp_plat

コマンドでカメラ画像が表示された。成功した。
platoon_15_161115.png
  1. 2016年11月16日 04:11 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

FPGAの部屋のまとめサイトの更新(2016年11月15日)

FPGAの部屋のまとめサイトを更新しました。
PYNQ白線検出のカテゴリを追加しました。今日までのその他の記事のリンクと概要を追加しました。
  1. 2016年11月15日 05:00 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:0

RAMの初期化ファイルのあるAXI4 Slave BFM(Verilog HDL版)を使用した論理合成後の機能シミュレーション

RAMの初期化ファイルのあるAXI4 Slave BFM(Verilog HDL版)”で論理合成後の機能シミュレーションをやってみた。

IOピンが足りないので、ZYBOから xcku035-sfva784-1-c にFPGAのパッケージを変更した。以前、論理合成後の機能シミュレーションがダメだったときは、ピン数がオーバーフローしていたのだった。それが論理合成後の機能シミュレーションが動作しない原因だったようだ。
AXI4_Slave_BFM_10_161114.png

Flow Navigator の Simulation → Run Simulation → Run Post-Synthesis Functional Simulation を実行した。
更に run 100us で 100 us 追加でシミュレーションを行った。
AXI4_Slave_BFM_11_161114.png

波形ウインドウを貼っておく。
AXI4_Slave_BFM_12_161114.png

AXI4_Slave_BFM_13_161114.png

問題なくシミュレーションができている。
論理合成後の機能シミュレーションをするときは、ピン数が足りないと論理が消されてしまう。考えてみれば当然かも?また1つ賢くなったということで。。。orz
ikzwm さん、ありがとうございました。
  1. 2016年11月14日 05:08 |
  2. AXI4バス
  3. | トラックバック:0
  4. | コメント:0

坂東市いわい将門ハーフマラソン(2016)

11月13日(日)に坂東市いわい将門ハーフマラソンに出てきました。
今回は奥さんは応援で来てくれました。
雨で逃げ帰った去年と比べて、とっても良い天気でした。
ショップのテントも出ていて、いろいろなランニングウェアやランニングシューズなどを売っていました。
iwai_masakado_5km_1_161117.jpg

いろいろと買い込んでしまいました。特に、帰りが安かったです。帰りを狙うのが良いです。千円均一で奥さんと合わせて6点も買っちゃいました。

グランドの近くの芝生にはテントも並んでいます。着替えもできますね。
iwai_masakado_5km_2_161117.jpg

グランドです。走っている人がいますね。
iwai_masakado_5km_3_161117.jpg

今年は奥さんが応援で来てくれたので、スタートの写真も取れました。これは速い人たち。
iwai_masakado_5km_4_161117.jpg

私の走っている姿も写真にとってくれました。
iwai_masakado_5km_5_161117.jpg

完走証です。
5km 男子50歳以上の部でグロスで 27 分 08 秒、距離別順位 150 位、男女別順位 121 位、種目別順位 39 位でした。
iwai_masakado_5km_6_161117.jpg

暑かったです。そして、疲れました。。。
  1. 2016年11月13日 20:03 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

FPGA GO のXiltera ディストリビューションをビルドした

FPGAマガジンNo.12の「第4章 共通カスタムLinuxディストリビューションの開発」をやってみた1”でXiltera のビルドに失敗したので、ひでみさんのFPGA GO の 24 ページの Xiltera ディストリビューションをビルドしてみることにした。

ただし、、ひでみさんのFPGA GO では、Ubuntu 16.04 を使用していたが、私が使用しているのはUbuntu 14.04 だ。

最初の 28 ページの下準備は”FPGAマガジンNo.12の「第4章 共通カスタムLinuxディストリビューションの開発」をやってみた1”でやったのでパスした。

repo もすでにインストールされている。Ubuntu 14.04 では apt-get では repo のパッケージがなかった。

git clone git://github.com/aquaxis/build-xiltera.git
cd build-xitrea
を行った。
XA_Linux_build_8_161108.png

./bulid-xiltera.sh を行った。
XA_Linux_build_9_161109.png

エラーが出て進まない。やはりUbuntu 14.04 ではダメなのかな?
  1. 2016年11月12日 04:28 |
  2. Linux
  3. | トラックバック:0
  4. | コメント:0

RAMの初期化ファイルのあるAXI4 Slave BFM(Verilog HDL版)

AXI4 Slave Bus Functional Model のVerilog HDL版3(RAMの初期化ファイルを追加)”に”AXI4 Slave Bus Functional Model のVerilog HDL版4”の変更を加えた。具体的には、Xilinx社のIP はAxREADY がいつもは 1 になっていることから、AxREADYがいつも 1 になるように変更した。これがデフォルトのモードで、AWREADY_IS_USUALLY_HIGH、ARREADY_IS_USUALLY_HIGH を 0 にすることで、従来のAxREADY がいつもは 0 のモードにすることができる。

プロジェクトを示す。VHDL版のプロジェクトからVHDL版のAXI4 Slave BFMを外して、Verilog HDL版のAXI4 Slave BFMを入れた。
AXI4_Slave_BFM_5_161111.png

RAM初期化ファイルの init_ram_data.data をシミュレーション用のファイルとして、プロジェクトに入れてある。これでシミュレーションはOKだと思ったが、論理シミュレーションをすると初期値を入力されているはずのram_array がやはりすべて X だった。
AXI4_Slave_BFM_6_161111.png

論理シミュレーション波形を示す。 101 us シミュレーションを行った。
AXI4_Slave_BFM_7_161111.png

AXI4_Slave_BFM_8_161111.png

ピンクの最初のRead 部分を拡大してみた。
AXI4_Slave_BFM_9_161111.png

きちんと初期化したデータが出ていた。Objects ウインドウで U になっていても実際は初期化されているようだ。ここには初期化データは表示されないので、注意が必要だ。

axi_slave_BFM_initf.v を貼っておく。
(追加)このHDLコードは無保証です。このコードを使用したことによる損害の保証はいたしません。ホビー向けとします。お仕事で使われるなど、信頼性が必要な用途には、Xilinx社で販売している売り物のBFMをご使用下さい。

/* AXI Master用 Slave Bus Function Mode (BFM)
   axi_slave_BFM_intf.v

   2012/02/25 : S_AXI_AWBURST=1 (INCR) にのみ対応、AWSIZE, ARSIZE = 000 (1byte), 001 (2bytes), 010 (4bytes) のみ対応。
   2012/07/04 : READ_ONLY_TRANSACTION を追加。Read機能のみでも+1したデータを出力することが出来るように変更した。
   2014/01/05 : ポート名をM_AXI?からS_AXI?に修正、Verilogに移植(By Koba)
*/
// 2014/07/18 : Write Respose Channel に sync_fifo を使用した by marsee
// 2014/07/19 : RAM を初期化する初期化ファイルを追加(init_ram_data.txt) by marsee
// 2014/07/20 : RAM 初期化ファイル名を parameter に追加 by marsee
// 2014/08/31 : READ_RANDOM_WAIT=1 の時に、S_AXI_RREADY が S_AXI_RVALID に依存するバグをフィック。 by marsee
//              WRITE_RANDOM_WAIT=1 の時に、S_AXI_WVALID が S_AXI_WREADY に依存するバグをフィック。 by marsee
//              LOAD_RAM_INIT_FILE パラメータを追加 by marsee
//
// 2016/07/03 : AWREADY_IS_USUALLY_HIGH と ARREADY_IS_USUALLY_HIGH の2つのパラメータを追加 by marsee
//
// ライセンスは二条項BSDライセンス (2-clause BSD license)とします。
//


module axi_slave_bfm #(
    parameter integer C_S_AXI_ID_WIDTH       = 1,
    parameter integer C_S_AXI_ADDR_WIDTH     = 32,
    parameter integer C_S_AXI_DATA_WIDTH     = 32,
    parameter integer C_S_AXI_AWUSER_WIDTH   = 1,
    parameter integer C_S_AXI_ARUSER_WIDTH   = 1,
    parameter integer C_S_AXI_WUSER_WIDTH    = 1,
    parameter integer C_S_AXI_RUSER_WIDTH    = 1,
    parameter integer C_S_AXI_BUSER_WIDTH    = 1,

    parameter integer C_S_AXI_TARGET         = 0,
    parameter integer C_OFFSET_WIDTH         = 10, // 割り当てるRAMのアドレスのビット幅
    parameter integer C_S_AXI_BURST_LEN      = 256,

    parameter integer WRITE_RANDOM_WAIT      = 1, // Write Transactionデータ転送時にランダムなWaitを発生させる=1、Waitしない=0
    parameter integer READ_RANDOM_WAIT       = 0, // Read Transactionデータ転送時にランダムなWaitを発生させる=1、Waitしない=0
    parameter integer READ_DATA_IS_INCREMENT = 0, // Read TransactionでRAMのデータを読み出す=0、0はじまりの+1データを使う=1
    parameter integer RANDOM_BVALID_WAIT     = 0,  // Write Transaction後、BVALIDをランダムにWaitする=1、ランダムにWaitしない=0
    parameter [80*8:1] RAM_INIT_FILE         = "init_ram_data.data",
    parameter integer LOAD_RAM_INIT_FILE     = 0, // RAM_INIT_FILE をLoadする - 1, Load しない - 0
    parameter integer AWREADY_IS_USUALLY_HIGH = 1, // AWRAEDY は通常はLow=0, High=1
    parameter integer ARREADY_IS_USUALLY_HIGH = 1 // AWRAEDY は通常はLow=0, High=1    
)
(
    // System Signals
    input ACLK,
    input ARESETN,

    // Slave Interface Write Address Ports
    input   [C_S_AXI_ID_WIDTH-1 : 0]    S_AXI_AWID,
    input   [C_S_AXI_ADDR_WIDTH-1 : 0]  S_AXI_AWADDR,
    input   [8-1 : 0]                   S_AXI_AWLEN,
    input   [3-1 : 0]                   S_AXI_AWSIZE,
    input   [2-1 : 0]                   S_AXI_AWBURST,
    // input S_AXI_AWLOCK [2-1 : 0],
    input   [1 : 0]                     S_AXI_AWLOCK,
    input   [4-1 : 0]                   S_AXI_AWCACHE,
    input   [3-1 : 0]                   S_AXI_AWPROT,
    input   [4-1 : 0]                   S_AXI_AWQOS,
    input   [C_S_AXI_AWUSER_WIDTH-1 :0] S_AXI_AWUSER,
    input                               S_AXI_AWVALID,
    output                              S_AXI_AWREADY,

    // Slave Interface Write Data Ports
    input   [C_S_AXI_DATA_WIDTH-1 : 0]  S_AXI_WDATA,
    input   [C_S_AXI_DATA_WIDTH/8-1 : 0]S_AXI_WSTRB,
    input                               S_AXI_WLAST,
    input   [C_S_AXI_WUSER_WIDTH-1 : 0] S_AXI_WUSER,
    input                               S_AXI_WVALID,
    output                              S_AXI_WREADY,

    // Slave Interface Write Response Ports
    output  [C_S_AXI_ID_WIDTH-1 : 0]    S_AXI_BID,
    output  [2-1 : 0]                   S_AXI_BRESP,
    output  [C_S_AXI_BUSER_WIDTH-1 : 0] S_AXI_BUSER,
    output                              S_AXI_BVALID,
    input                               S_AXI_BREADY,

    // Slave Interface Read Address Ports
    input   [C_S_AXI_ID_WIDTH-1 : 0]    S_AXI_ARID,
    input   [C_S_AXI_ADDR_WIDTH-1 : 0]  S_AXI_ARADDR,
    input   [8-1 : 0]                   S_AXI_ARLEN,
    input   [3-1 : 0]                   S_AXI_ARSIZE,
    input   [2-1 : 0]                   S_AXI_ARBURST,
    input   [2-1 : 0]                   S_AXI_ARLOCK,
    input   [4-1 : 0]                   S_AXI_ARCACHE,
    input   [3-1 : 0]                   S_AXI_ARPROT,
    input   [4-1 : 0]                   S_AXI_ARQOS,
    input   [C_S_AXI_ARUSER_WIDTH-1 : 0]S_AXI_ARUSER,
    input                               S_AXI_ARVALID,
    output                              S_AXI_ARREADY,

    // Slave Interface Read Data Ports
    output  reg [C_S_AXI_ID_WIDTH-1: 0] S_AXI_RID,
    output  [C_S_AXI_DATA_WIDTH-1 : 0]  S_AXI_RDATA,
    output  reg [2-1 : 0]               S_AXI_RRESP,
    output                              S_AXI_RLAST,
    output  [C_S_AXI_RUSER_WIDTH-1 : 0] S_AXI_RUSER,
    output                              S_AXI_RVALID,
    input                               S_AXI_RREADY
);

localparam AXBURST_FIXED = 2'b00;
localparam AXBURST_INCR = 2'b01;
localparam AXBURST_WRAP = 2'b10;
localparam RESP_OKAY = 2'b00;
localparam RESP_EXOKAY = 2'b01;
localparam RESP_SLVERR = 2'b10;
localparam RESP_DECERR = 2'b11;
localparam DATA_BUS_BYTES = (C_S_AXI_DATA_WIDTH / 8);
//localparam ADD_INC_OFFSET = log2(DATA_BUS_BYTES);
localparam ADD_INC_OFFSET = (DATA_BUS_BYTES==1) ? 0:
                            (DATA_BUS_BYTES==2) ? 1:
                            (DATA_BUS_BYTES==4) ? 2:
                            (DATA_BUS_BYTES==8) ? 3:
                            (DATA_BUS_BYTES==16) ? 4:
                            (DATA_BUS_BYTES==32) ? 5:
                            (DATA_BUS_BYTES==64) ? 6:
                            (DATA_BUS_BYTES==128) ? 7: 32'hxxxxxxxx;

// fifo depth for address
localparam AD_FIFO_DEPTH         = 16;

// wad_fifo field
localparam WAD_FIFO_WIDTH        = C_S_AXI_ADDR_WIDTH+5+C_S_AXI_ID_WIDTH-1+1;
localparam WAD_FIFO_AWID_HIGH    = C_S_AXI_ADDR_WIDTH+5+C_S_AXI_ID_WIDTH-1;
localparam WAD_FIFO_AWID_LOW     = C_S_AXI_ADDR_WIDTH+5;
localparam WAD_FIFO_AWBURST_HIGH = C_S_AXI_ADDR_WIDTH+4;
localparam WAD_FIFO_AWBURST_LOW  = C_S_AXI_ADDR_WIDTH+3;
localparam WAD_FIFO_AWSIZE_HIGH  = C_S_AXI_ADDR_WIDTH+2;
localparam WAD_FIFO_AWSIZE_LOW   = C_S_AXI_ADDR_WIDTH;
localparam WAD_FIFO_ADDR_HIGH    = C_S_AXI_ADDR_WIDTH-1;
localparam WAD_FIFO_ADDR_LOW     = 0;

// wres_fifo field
localparam WRES_FIFO_WIDTH          = 2+C_S_AXI_ID_WIDTH-1+1;
localparam WRES_FIFO_AWID_HIGH      = 2+C_S_AXI_ID_WIDTH-1;
localparam WRES_FIFO_AWID_LOW       = 2;
localparam WRES_FIFO_AWBURST_HIGH   = 1;
localparam WRES_FIFO_AWBURST_LOW    = 0;

// rad_fifo field
localparam RAD_FIFO_WIDTH        = C_S_AXI_ADDR_WIDTH+13+C_S_AXI_ID_WIDTH-1+1;
localparam RAD_FIFO_ARID_HIGH    = C_S_AXI_ADDR_WIDTH+13+C_S_AXI_ID_WIDTH-1;
localparam RAD_FIFO_ARID_LOW     = C_S_AXI_ADDR_WIDTH+13;
localparam RAD_FIFO_ARBURST_HIGH = C_S_AXI_ADDR_WIDTH+12;
localparam RAD_FIFO_ARBURST_LOW  = C_S_AXI_ADDR_WIDTH+11;
localparam RAD_FIFO_ARSIZE_HIGH  = C_S_AXI_ADDR_WIDTH+10;
localparam RAD_FIFO_ARSIZE_LOW   = C_S_AXI_ADDR_WIDTH+8;
localparam RAD_FIFO_ARLEN_HIGH   = C_S_AXI_ADDR_WIDTH+7;
localparam RAD_FIFO_ARLEN_LOW    = C_S_AXI_ADDR_WIDTH;
localparam RAD_FIFO_ADDR_HIGH    = C_S_AXI_ADDR_WIDTH-1;
localparam RAD_FIFO_ADDR_LOW     = 0;

// RAMの生成
localparam SLAVE_ADDR_NUMBER = 2 ** (C_OFFSET_WIDTH - ADD_INC_OFFSET);
reg [(C_S_AXI_DATA_WIDTH - 1):0] ram_array [0:(SLAVE_ADDR_NUMBER - 1)];

// for write transaction
// write_address_state
localparam IDLE_WRAD  = 1'd0;
localparam AWR_ACCEPT = 1'd1;
reg wradr_cs;

// write_data_state
localparam IDLE_WRDT = 1'd0;
localparam WR_BURST  = 1'd1;
reg wrdat_cs;

// write_response_state
localparam IDLE_WRES     = 2'd0;
localparam WAIT_BVALID   = 2'd1;
localparam BVALID_ASSERT = 2'd2;
reg [1:0] wrres_cs;

integer addr_inc_step_wr = 1;
reg awready;
reg [(C_OFFSET_WIDTH - 1):0]   wr_addr;
reg [(C_S_AXI_ID_WIDTH - 1):0] wr_bid;
reg [1:0] wr_bresp;
reg wr_bvalid;
reg [15:0] m_seq16_wr;
reg wready;

// wready_state
localparam IDLE_WREADY     = 2'd0;
localparam ASSERT_WREADY   = 2'd1;
localparam DEASSERT_WREADY = 2'd2;
reg [1:0] cs_wready;

wire cdc_we;
wire wad_fifo_full;
wire wad_fifo_empty;
wire wad_fifo_almost_full;
wire wad_fifo_almost_empty;
wire wad_fifo_rd_en;
wire wad_fifo_wr_en;
wire [WAD_FIFO_WIDTH-1:0] wad_fifo_din;
wire [WAD_FIFO_WIDTH-1:0] wad_fifo_dout;
reg  [15:0] m_seq16_wr_res;
reg  [4:0]  wr_resp_cnt;

// wres_fifo
wire wres_fifo_wr_en;
wire wres_fifo_full;
wire wres_fifo_empty;
wire wres_fifo_almost_full;
wire wres_fifo_almost_empty;
wire wres_fifo_rd_en;
wire [WRES_FIFO_WIDTH-1:0] wres_fifo_din;
wire [WRES_FIFO_WIDTH-1:0] wres_fifo_dout;

// for read transaction
// read_address_state
localparam IDLE_RDA   = 1'd0;
localparam ARR_ACCEPT = 1'd1;
reg rdadr_cs;

// read_data_state
localparam IDLE_RDD = 1'd0;
localparam RD_BURST = 1'd1;
reg rddat_cs;

// read_last_state
localparam IDLE_RLAST   = 1'd0;
localparam RLAST_ASSERT = 1'd1;
reg rdlast;

integer addr_inc_step_rd = 1;
reg arready;
reg [(C_OFFSET_WIDTH - 1):0] rd_addr;
reg [7:0] rd_axi_count;
reg rvalid;
reg rlast;
reg [15:0] m_seq16_rd;

// rvalid_state
localparam IDLE_RVALID     = 2'd0;
localparam ASSERT_RVALID   = 2'd1;
localparam DEASSERT_RVALID = 2'd2;
reg [1:0] cs_rvalid;

reg [(C_S_AXI_DATA_WIDTH - 1):0] read_data_count;
reg reset_1d;
reg reset_2d;
wire reset;
wire rad_fifo_full;
wire rad_fifo_empty;
wire rad_fifo_almost_full;
wire rad_fifo_almost_empty;
wire rad_fifo_rd_en;
wire rad_fifo_wr_en;
wire [RAD_FIFO_WIDTH-1:0] rad_fifo_din;
wire [RAD_FIFO_WIDTH-1:0] rad_fifo_dout;

// ram_array を初期化
initial begin
    if (LOAD_RAM_INIT_FILE==1) begin
        $readmemh(RAM_INIT_FILE, ram_array,  0, (SLAVE_ADDR_NUMBER - 1));
    end
end

// ARESETN をACLK で同期化
always @ ( posedge ACLK ) begin
    reset_1d <= ~ARESETN;
    reset_2d <= reset_1d;
end

assign reset = reset_2d;


// AXI4バス Write Address State Machine
always @ ( posedge ACLK ) begin
    if ( reset ) begin
        wradr_cs <= IDLE_WRAD;
        awready  <= 1'b0;
    end
    else
        case (wradr_cs)
            IDLE_WRAD:  if ((S_AXI_AWVALID == 1'b1) && (wad_fifo_full == 1'b0) && (wres_fifo_full == 1'b0))    // S_AXI_AWVALIDが1にアサートされた
                        begin
                            wradr_cs <= AWR_ACCEPT;
                            awready <= 1'b1;
                        end
            AWR_ACCEPT: begin
                            wradr_cs <= IDLE_WRAD;
                            awready <= 1'b0;
                        end
        endcase
end

assign S_AXI_AWREADY = (AWREADY_IS_USUALLY_HIGH==1) ? ~wad_fifo_full : awready;


// {S_AXI_AWID, S_AXI_AWBURST, S_AXI_AWSIZE, S_AXI_AWADDR}を保存しておく同期FIFO
assign wad_fifo_din = {S_AXI_AWID, S_AXI_AWBURST, S_AXI_AWSIZE, S_AXI_AWADDR};
assign wad_fifo_wr_en = (AWREADY_IS_USUALLY_HIGH==1) ? (S_AXI_AWVALID & ~wad_fifo_full) : awready;

sync_fifo  #(
    .C_MEMORY_SIZE  (AD_FIFO_DEPTH),
    .DATA_BUS_WIDTH (WAD_FIFO_WIDTH)
  ) wad_fifo (
    .clk            (ACLK),
    .rst            (reset),
    .wr_en          (wad_fifo_wr_en),
    .din            (wad_fifo_din),
    .full           (wad_fifo_full),
    .almost_full    (wad_fifo_almost_full),
    .rd_en          (wad_fifo_rd_en),
    .dout           (wad_fifo_dout),
    .empty          (wad_fifo_empty),
    .almost_empty   (wad_fifo_almost_empty)
);

assign wad_fifo_rd_en = (wready & S_AXI_WVALID & S_AXI_WLAST);


// AXI4バス Write Data State Machine
always @( posedge ACLK ) begin
    if ( reset )
        wrdat_cs <= IDLE_WRDT;
    else
        case (wrdat_cs)
            IDLE_WRDT:  if ( wad_fifo_empty == 1'b0 )   // AXI Writeアドレス転送の残りが1個以上ある
                            wrdat_cs <= WR_BURST;
            WR_BURST :  if ( S_AXI_WLAST & S_AXI_WVALID & wready )  // Write Transaction終了
                            wrdat_cs <= IDLE_WRDT;
        endcase
end

// M系列による16ビット乱数生成関数
function [15:0] M_SEQ16_BFM_F;
input [15:0] mseq16in;
reg   xor_result;
begin
    xor_result = mseq16in[15] ^ mseq16in[12] ^ mseq16in[10] ^ mseq16in[8] ^
                 mseq16in[7]  ^ mseq16in[6]  ^ mseq16in[3]  ^ mseq16in[2];
    M_SEQ16_BFM_F = {mseq16in[14:0], xor_result};
end
endfunction


// m_seq_wr、16ビットのM系列を計算する
always @( posedge ACLK ) begin
    if ( reset )
        m_seq16_wr <= 16'b1;
    else begin
        if ( WRITE_RANDOM_WAIT ) begin // Write Transaction時にランダムなWaitを挿入する
            if ( wrdat_cs == WR_BURST )
                m_seq16_wr <= M_SEQ16_BFM_F(m_seq16_wr);
        end
        else    // Wait無し
            m_seq16_wr <= 16'b0;
    end
end


// wready の処理、M系列を計算して128以上だったらWaitする。
always @( posedge ACLK ) begin
    if ( reset ) begin
        cs_wready <= IDLE_WREADY;
        wready    <= 1'b0;
    end
    else
        case (cs_wready)
            IDLE_WREADY:    if ( (wrdat_cs == IDLE_WRDT) && (wad_fifo_empty == 1'b0) ) begin
                                if ( (m_seq16_wr[7] == 1'b0) && (wres_fifo_full==1'b0) ) begin
                                    cs_wready <= ASSERT_WREADY;
                                    wready    <= 1'b1;
                                end
                                else begin
                                    cs_wready <= DEASSERT_WREADY;
                                    wready    <= 1'b0;
                                end
                            end
            ASSERT_WREADY:  if ( (wrdat_cs == WR_BURST) && S_AXI_WLAST && S_AXI_WVALID ) begin
                                cs_wready <= IDLE_WREADY;
                                wready <= 1'b0;
                            end
                            else if ( (wrdat_cs == WR_BURST) && S_AXI_WVALID ) begin
                                if ((m_seq16_wr[7] == 1'b1) || (wres_fifo_full==1'b1)) begin
                                    cs_wready <= DEASSERT_WREADY;
                                    wready <= 1'b0;
                                end
                            end
            DEASSERT_WREADY:if ( (m_seq16_wr[7] == 1'b0) && (wres_fifo_full==1'b0) ) begin
                                cs_wready <= ASSERT_WREADY;
                                wready <= 1'b1;
                            end
        endcase
end

assign S_AXI_WREADY = wready;
assign cdc_we = ( (wrdat_cs == WR_BURST) && wready && S_AXI_WVALID );


// addr_inc_step_wrの処理
always @ ( posedge ACLK ) begin
    if ( reset )
        addr_inc_step_wr <= 1;
    else begin
        if ( (wrdat_cs == IDLE_WRDT) & (wad_fifo_empty == 1'b0) )
            case (wad_fifo_dout[WAD_FIFO_AWSIZE_HIGH:WAD_FIFO_AWSIZE_LOW])
                3'b000 : addr_inc_step_wr <=   1;   //    8ビット転送
                3'b001 : addr_inc_step_wr <=   2;   //   16ビット転送
                3'b010 : addr_inc_step_wr <=   4;   //   32ビット転送
                3'b011 : addr_inc_step_wr <=   8;   //   64ビット転送
                3'b100 : addr_inc_step_wr <=  16;   //  128ビット転送
                3'b101 : addr_inc_step_wr <=  32;   //  256ビット転送
                3'b110 : addr_inc_step_wr <=  64;   //  512ビット転送
                default: addr_inc_step_wr <= 128;   // 1024ビット転送
            endcase
    end
end

// wr_addr の処理
always @ (posedge ACLK ) begin
    if ( reset )
        wr_addr <= 'b0;
    else begin
        if ( (wrdat_cs == IDLE_WRDT) && (wad_fifo_empty == 1'b0) )
            wr_addr <= wad_fifo_dout[(C_OFFSET_WIDTH - 1):0];
        else if ( (wrdat_cs == WR_BURST) && S_AXI_WVALID && wready )    // アドレスを進める
            wr_addr <= (wr_addr + addr_inc_step_wr);
    end
end

// Wirte Response FIFO (wres_fifo)
sync_fifo #(
    .C_MEMORY_SIZE(AD_FIFO_DEPTH),
    .DATA_BUS_WIDTH(WRES_FIFO_WIDTH)
) wres_fifo (
    .clk(ACLK),
    .rst(reset),
    .wr_en(wres_fifo_wr_en),
    .din(wres_fifo_din),
    .full(wres_fifo_full),
    .almost_full(wres_fifo_almost_full),
    .rd_en(wres_fifo_rd_en),
    .dout(wres_fifo_dout),
    .empty(wres_fifo_empty),
    .almost_empty(wres_fifo_almost_empty)
);
assign wres_fifo_wr_en = (S_AXI_WLAST & S_AXI_WVALID & wready) ? 1'b1 : 1'b0;   // Write Transaction 終了
assign wres_fifo_din = {wad_fifo_dout[WAD_FIFO_AWID_HIGH:WAD_FIFO_AWID_LOW], wad_fifo_dout[WAD_FIFO_AWBURST_HIGH:WAD_FIFO_AWBURST_LOW]};
assign wres_fifo_rd_en = (wr_bvalid & S_AXI_BREADY) ? 1'b1 : 1'b0;

// S_AXI_BID の処理
assign S_AXI_BID = wres_fifo_dout[WRES_FIFO_AWID_HIGH:WRES_FIFO_AWID_LOW];

// S_AXI_BRESP の処理
// S_AXI_AWBURSTがINCRの時はOKAYを返す。それ以外はSLVERRを返す。
assign S_AXI_BRESP = (wres_fifo_dout[WRES_FIFO_AWBURST_HIGH:WRES_FIFO_AWBURST_LOW]==AXBURST_INCR) ? RESP_OKAY : RESP_SLVERR;

// wr_bvalid の処理
// wr_bvalid のアサートは、Write Data Channelの完了より必ず1クロックは遅延する
always @ ( posedge ACLK ) begin
    if ( reset ) begin
        wrres_cs <= IDLE_WRES;
        wr_bvalid <= 1'b0;
    end
    else
        case (wrres_cs)
            IDLE_WRES:  if ( wres_fifo_empty == 1'b0 ) begin    // Write Transaction 終了
                            if ( (m_seq16_wr_res == 0) || (RANDOM_BVALID_WAIT == 0) ) begin
                                wrres_cs <= BVALID_ASSERT;
                                wr_bvalid <= 1'b1;
                            end
                            else
                                wrres_cs <= WAIT_BVALID;
                        end
            WAIT_BVALID:if ( wr_resp_cnt == 0 ) begin
                            wrres_cs <= BVALID_ASSERT;
                            wr_bvalid <= 1'b1;
                        end
            BVALID_ASSERT: if ( S_AXI_BREADY ) begin
                            wrres_cs <= IDLE_WRES;
                            wr_bvalid <= 1'b0;
                          end
        endcase
end

assign S_AXI_BVALID = wr_bvalid;
assign S_AXI_BUSER  = 'b0;

// wr_resp_cnt
always @ ( posedge ACLK ) begin
    if ( reset )
        wr_resp_cnt <= 'b0;
    else begin
        if ( (wrres_cs == IDLE_WRES) && (wres_fifo_empty==1'b0) )
            wr_resp_cnt <= m_seq16_wr_res[4:0];
        else if ( wr_resp_cnt!=0 )
            wr_resp_cnt <= wr_resp_cnt - 1;
    end
end

// m_seq_wr_res、16ビットのM系列を計算する
always @ ( posedge ACLK ) begin
    if ( reset )
        m_seq16_wr_res <= 16'b1;
    else
        m_seq16_wr_res <= M_SEQ16_BFM_F(m_seq16_wr_res);
end


// AXI4バス Read Address Transaction State Machine
always @ ( posedge ACLK ) begin
    if ( reset ) begin
        rdadr_cs <= IDLE_RDA;
        arready <= 1'b0;
    end
    else
        case (rdadr_cs)
            IDLE_RDA:   if ( (S_AXI_ARVALID == 1'b1) && (rad_fifo_full == 1'b0) ) begin // Read Transaction要求
                            rdadr_cs <= ARR_ACCEPT;
                            arready  <= 1'b1;
                        end
            ARR_ACCEPT: begin   // S_AXI_ARREADYをアサート
                            rdadr_cs <= IDLE_RDA;
                            arready  <= 1'b0;
                        end
        endcase
end

assign S_AXI_ARREADY = (ARREADY_IS_USUALLY_HIGH==1) ? ~rad_fifo_full : arready;


// S_AXI_ARID & S_AXI_ARBURST & S_AXI_ARSIZE & S_AXI_ARLEN & S_AXI_ARADDR を保存しておく同期FIFO
assign rad_fifo_din ={S_AXI_ARID, S_AXI_ARBURST, S_AXI_ARSIZE, S_AXI_ARLEN, S_AXI_ARADDR};
assign rad_fifo_wr_en = (ARREADY_IS_USUALLY_HIGH==1) ? (S_AXI_ARVALID & ~rad_fifo_full) : arready;

sync_fifo #(
    .C_MEMORY_SIZE  (AD_FIFO_DEPTH),
    .DATA_BUS_WIDTH (RAD_FIFO_WIDTH)
  ) rad_fifo
 (
    .clk            (ACLK),
    .rst            (reset),
    .wr_en          (rad_fifo_wr_en),
    .din            (rad_fifo_din),
    .full           (rad_fifo_full),
    .almost_full    (rad_fifo_almost_full),
    .rd_en          (rad_fifo_rd_en),
    .dout           (rad_fifo_dout),
    .empty          (rad_fifo_empty),
    .almost_empty   (rad_fifo_almost_empty)
);

assign rad_fifo_rd_en = (rvalid & S_AXI_RREADY & rlast);


// AXI4バス Read Data Transaction State Machine
always @( posedge ACLK ) begin
    if ( reset )
        rddat_cs <= IDLE_RDD;
    else
        case (rddat_cs)
            IDLE_RDD:   if ( rad_fifo_empty == 1'b0 )   // AXI Read アドレス転送の残りが1個以上ある
                            rddat_cs <= RD_BURST;
            RD_BURST:   if ( (rd_axi_count == 0) && rvalid && S_AXI_RREADY )  // Read Transaction終了
                            rddat_cs <= IDLE_RDD;
        endcase
end

// m_seq_rd、16ビットのM系列を計算する
always @ ( posedge ACLK ) begin
    if ( reset )
        m_seq16_rd <= 16'hffff;
    else begin
        if ( READ_RANDOM_WAIT) begin
            if ( rddat_cs == RD_BURST )
                m_seq16_rd <= M_SEQ16_BFM_F(m_seq16_rd);
        end else
            m_seq16_rd <= 16'b0;
    end
end


// rvalidの処理、M系列を計算して128以上だったらWaitする
always @( posedge ACLK ) begin
    if ( reset ) begin
        cs_rvalid <= IDLE_RVALID;
        rvalid    <= 1'b0;
    end
    else
        case (cs_rvalid)
            IDLE_RVALID:    if ( (rddat_cs == IDLE_RDD) && (rad_fifo_empty == 1'b0) ) begin // 次はrd_burst
                                if ( m_seq16_rd[7] == 1'b0 ) begin
                                    cs_rvalid <= ASSERT_RVALID;
                                    rvalid    <= 1'b1;
                                end
                                else begin
                                    cs_rvalid <= DEASSERT_RVALID;
                                    rvalid <= 1'b0;
                                end
                            end
            ASSERT_RVALID:  if ( (rddat_cs == RD_BURST) && rlast && S_AXI_RREADY ) begin    // 終了
                                cs_rvalid <= IDLE_RVALID;
                                rvalid    <= 1'b0;
                            end
                            else if ( (rddat_cs == RD_BURST) & S_AXI_RREADY ) begin // 1つのトランザクション終了
                                if ( m_seq16_rd[7] ) begin
                                    cs_rvalid <= DEASSERT_RVALID;
                                    rvalid    <= 1'b0;
                                end
                            end
            DEASSERT_RVALID:if ( m_seq16_rd[7] == 1'b0 ) begin
                                cs_rvalid <= ASSERT_RVALID;
                                rvalid    <= 1'b1;
                            end
        endcase
end

assign S_AXI_RVALID = rvalid;

// addr_inc_step_rdの処理
always @( posedge ACLK ) begin
    if ( reset )
        addr_inc_step_rd <= 1;
    else begin
        if ( (rddat_cs == IDLE_RDD) && (rad_fifo_empty == 1'b0) )
            case (rad_fifo_dout[RAD_FIFO_ARSIZE_HIGH:RAD_FIFO_ARSIZE_LOW])
                3'b000: addr_inc_step_rd <=   1;    //    8ビット転送
                3'b001: addr_inc_step_rd <=   2;    //   16ビット転送
                3'b010: addr_inc_step_rd <=   4;    //   32ビット転送
                3'b011: addr_inc_step_rd <=   8;    //   64ビット転送
                3'b100: addr_inc_step_rd <=  16;    //  128ビット転送
                3'b101: addr_inc_step_rd <=  32;    //  256ビット転送
                3'b110: addr_inc_step_rd <=  64;    //  512ビット転送
                default:addr_inc_step_rd <= 128;    // 1024ビット転送
            endcase
        end
end


// rd_addr の処理
always @ ( posedge ACLK ) begin
    if ( reset )
        rd_addr <= 'b0;
    else begin
        if ( (rddat_cs == IDLE_RDD) && (rad_fifo_empty == 1'b0) )
            rd_addr <= rad_fifo_dout[(C_OFFSET_WIDTH - 1):0];
        else if ( (rddat_cs == RD_BURST) && S_AXI_RREADY && rvalid )
            rd_addr <= (rd_addr + addr_inc_step_rd);
    end
end


// rd_axi_countの処理(AXIバス側のデータカウント)
always @ ( posedge ACLK ) begin
    if ( reset )
        rd_axi_count <= 'b0;
    else begin
        if ( (rddat_cs == IDLE_RDD) && (rad_fifo_empty == 1'b0) ) // rd_axi_countのロード
            rd_axi_count <= rad_fifo_dout[RAD_FIFO_ARLEN_HIGH:RAD_FIFO_ARLEN_LOW];
        else if ( (rddat_cs == RD_BURST) && rvalid && S_AXI_RREADY )    // Read Transactionが1つ終了
            rd_axi_count <= rd_axi_count - 1;
    end
end


// rdlast State Machine
always @ ( posedge ACLK ) begin
    if ( reset ) begin
        rdlast <= IDLE_RLAST;
        rlast  <= 1'b0;
    end
    else
        case (rdlast)
            IDLE_RLAST: if ( (rd_axi_count == 1) && rvalid && S_AXI_RREADY ) begin  // バーストする場合
                            rdlast <= RLAST_ASSERT;
                            rlast  <= 1'b1;
                        end
                        else if ( (rddat_cs == IDLE_RDD) && (rad_fifo_empty == 1'b0) &&
                                  (rad_fifo_dout[RAD_FIFO_ARLEN_HIGH:RAD_FIFO_ARLEN_LOW] == 0) ) begin // 転送数が1の場合
                            rdlast <= RLAST_ASSERT;
                            rlast  <= 1'b1;
                        end
            RLAST_ASSERT:if ( rvalid && S_AXI_RREADY ) begin    // Read Transaction終了(rd_axi_count=0は決定)
                            rdlast <= IDLE_RLAST;
                            rlast  <= 1'b0;
                         end
        endcase
end

assign S_AXI_RLAST = rlast;


// S_AXI_RID, S_AXI_RUSER の処理
always @ ( posedge ACLK ) begin
    if ( reset )
        S_AXI_RID <= 'b0;
    else begin
        if ( (rddat_cs == IDLE_RDD) && (rad_fifo_empty == 1'b0) )
            S_AXI_RID <= rad_fifo_dout[RAD_FIFO_ARID_HIGH:RAD_FIFO_ARID_LOW];
    end
end

assign S_AXI_RUSER = 'b0;


// S_AXI_RRESP は、S_AXI_ARBURST がINCR の場合はOKAYを返す。それ以外はSLVERRを返す。
always @( posedge ACLK ) begin
    if ( reset )
        S_AXI_RRESP <= 'b0;
    else begin
        if ( (rddat_cs == IDLE_RDD) && (rad_fifo_empty == 1'b0) ) begin
            if ((rad_fifo_dout[RAD_FIFO_ARBURST_HIGH:RAD_FIFO_ARBURST_LOW] == AXBURST_INCR))
                S_AXI_RRESP <= RESP_OKAY;
            else
                S_AXI_RRESP <= RESP_SLVERR;
        end
    end
end

// RAM
integer i;

always @( posedge ACLK ) begin
    if ( cdc_we ) begin :Block_Name_2
        for (i=0; i<(C_S_AXI_DATA_WIDTH / 8); i=i+1) begin
            if ( S_AXI_WSTRB[i] )
                ram_array[wr_addr[(C_OFFSET_WIDTH - 1):ADD_INC_OFFSET]][(i * 8) +: 8]
                    <= S_AXI_WDATA[(i * 8) +: 8];
        end
    end
end

// Read Transaciton の時に +1 されたReadデータを使用する(Read 毎に+1)
always @( posedge ACLK ) begin
    if ( reset )
        read_data_count <= 'b0;
    else begin
        if ( (rddat_cs == RD_BURST) && rvalid && S_AXI_RREADY )
            read_data_count <= read_data_count + 1;
    end
end

assign S_AXI_RDATA = (READ_DATA_IS_INCREMENT == 0) ?
                      ram_array[rd_addr[(C_OFFSET_WIDTH - 1):ADD_INC_OFFSET]] : read_data_count;

endmodule


init_ram_data.data は、”RAMの初期化ファイルのあるAXI4 Slave BFM(VHDL版)”をご覧ください。

sync_fifo.v を貼っておく。

// Synchronous FIFO for Simulation
//
// 2013/11/08 by marsee
//
//  ライセンスは二条項BSDライセンス (2-clause BSD license)とします。
//

`default_nettype none

module sync_fifo #(
    parameter integer C_MEMORY_SIZE =        512,    // Word (not byte), 2のn乗
    parameter integer DATA_BUS_WIDTH =        32        // RAM Data Width
)
(
    input    wire    clk,
    input    wire    rst,
    input    wire    wr_en,
    input    wire    [DATA_BUS_WIDTH-1:0]    din,
    output    wire    full,
    output    wire    almost_full,
    input    wire    rd_en,
    output    wire     [DATA_BUS_WIDTH-1:0]    dout,
    output    wire    empty,
    output    wire    almost_empty
);

    // Beyond Circuts, Constant Function in Verilog 2001を参照しました
    // http://www.beyond-circuits.com/wordpress/2008/11/constant-functions/
    function integer log2;
        input integer addr;
        begin
            addr = addr - 1;
            for (log2=0; addr>0; log2=log2+1)
                addr = addr >> 1;
        end
    endfunction

    reg        [DATA_BUS_WIDTH-1:0]    mem    [0:C_MEMORY_SIZE-1];
    reg        [log2(C_MEMORY_SIZE)-1:0]   mem_waddr = 0;
    reg        [log2(C_MEMORY_SIZE)-1:0]   mem_raddr = 0;
    reg        [log2(C_MEMORY_SIZE)-1:0]    rp = 0;
    reg        [log2(C_MEMORY_SIZE)-1:0]    wp = 0;

    wire    [log2(C_MEMORY_SIZE)-1:0]   plus_1 = 1;
    wire    [log2(C_MEMORY_SIZE)-1:0]   plus_2 = 2;

    wire    almost_full_node;
    wire    almost_empty_node;

    integer i;
    // initialize RAM Data
    initial begin
        for (i=0; i<C_MEMORY_SIZE; i=i+1)
            mem[i] = 0;
    end

    // Write
    always @(posedge clk) begin
        if (rst) begin
            mem_waddr <= 0;
            wp <= 0;
        end else begin
            if (wr_en) begin
                mem_waddr <= mem_waddr + 1;
                wp <= wp + 1;
            end
        end
    end

    always @(posedge clk) begin
         if (wr_en) begin
            mem[mem_waddr] <= din;
        end
    end

    assign full = (wp+plus_1 == rp) ? 1'b1 : 1'b0;
    assign almost_full_node = (wp+plus_2 == rp) ? 1'b1 : 1'b0;
    assign almost_full = full | almost_full_node;

    // Read
    always @(posedge clk) begin
        if (rst) begin
            mem_raddr <= 0;
            rp <= 0;
        end else begin
            if (rd_en) begin
                mem_raddr <= mem_raddr + 1;
                rp <= rp + 1;
            end
        end
    end

    assign dout = mem[mem_raddr];

    assign empty = (wp == rp) ? 1'b1 : 1'b0;
    assign almost_empty_node = (wp == rp+plus_1) ? 1'b1 : 1'b0;
    assign almost_empty = empty | almost_empty_node;
endmodule

`default_nettype wire

  1. 2016年11月11日 04:10 |
  2. AXI4バス
  3. | トラックバック:0
  4. | コメント:0

RAMの初期化ファイルのあるAXI4 Slave BFM(VHDL版)

AXI4 Slave Bus Functional Model のVHDL版3(RAMの初期化ファイルを追加)”に”AXI4 Slave Bus Functional Model のVHDL版4”の変更を加えた。具体的には、Xilinx社のIP はAxREADY がいつもは 1 になっていることから、AxREADYがいつも 1 になるように変更した。これがデフォルトのモードで、AWREADY_IS_USUALLY_HIGH、ARREADY_IS_USUALLY_HIGH を 0 にすることで、従来のAxREADY がいつもは 0 のモードにすることができる。

プロジェクトを示す。
AXI4_Slave_BFM_1_161109.png

RAM初期化ファイルの init_ram_data.data をシミュレーション用のファイルとして、プロジェクトに入れてある。これでシミュレーションはOKだと思ったが、論理シミュレーションをすると初期値を入力されているはずのram_array がすべて U だった。
AXI4_Slave_BFM_2_161109.png

論理シミュレーション波形を示す。 101 us シミュレーションを行った。
AXI4_Slave_BFM_3_161109.png

こうしてみると、画像フィルタなので、当然だが、Write よりも Read が速い。そこで、ピンクの最初のRead 部分を拡大してみた。
AXI4_Slave_BFM_4_161109.png

きちんと初期化したデータが出ていた。Objects ウインドウで U になっていても実際は初期化されているようだ。ここには初期化データは表示されないので、注意が必要だ。

axi_slave_BFM_initf.vhd を貼っておく。
(追加)このHDLコードは無保証です。このコードを使用したことによる損害の保証はいたしません。ホビー向けとします。お仕事で使われるなど、信頼性が必要な用途には、Xilinx社で販売している売り物のBFMをご使用下さい。

-----------------------------------------------------------------------------
--
-- AXI Master用 Slave Bus Function Mode (BFM)   by marsee
-- axi_slave_BFM_initf.vhd
-- 
-----------------------------------------------------------------------------
-- 2012/02/25 : S_AXI_AWBURST=1 (INCR) にのみ対応、AWSIZE, ARSIZE = 000 (1byte), 001 (2bytes), 010 (4bytes) のみ対応。
-- 2012/07/04 : READ_ONLY_TRANSACTION を追加。Read機能のみでも+1したデータを出力することが出来るように変更した。
-- sync_fifo を使用したオーバーラップ対応版
-- 2014/07/04 : M_AXIをスレーブに対応した名前のS_AXIに変更
-- 2014/07/16 : Write Respose Channel に sync_fifo を使用した
-- 2014/07/19 : RAM を初期化する初期化ファイルを追加(init_ram_data.txt)
-- 2014/07/20 : RAM 初期化ファイル名を generic に追加
-- 2014/08/31 : READ_RANDOM_WAIT=1 の時に、S_AXI_RREADY が S_AXI_RVALID に依存するバグをフィック。
--              WRITE_RANDOM_WAIT=1 の時に、S_AXI_WVALID が S_AXI_WREADY に依存するバグをフィック。
--              LOAD_RAM_INIT_FILE パラメータを追加
--
-- 2016/07/03 : AWREADY_IS_USUALLY_HIGH と ARREADY_IS_USUALLY_HIGH の2つのパラメータを追加 by marsee
--
-- ライセンスは二条項BSDライセンス (2-clause BSD license)とします。
--


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_misc.all;

package m_seq_bfm_pack is
    function M_SEQ16_BFM_F(mseq16in : std_logic_vector
        )return std_logic_vector;
end package m_seq_bfm_pack;
package body m_seq_bfm_pack is
    function M_SEQ16_BFM_F(mseq16in : std_logic_vector
        )return std_logic_vector is
            variable mseq16 : std_logic_vector(15 downto 0);
            variable xor_result : std_logic;
    begin
        xor_result := mseq16in(15) xor mseq16in(12) xor mseq16in(10) xor mseq16in(8) xor mseq16in(7) xor mseq16in(6) xor mseq16in(3) xor mseq16in(2);
        mseq16 := mseq16in(14 downto 0) & xor_result;
        return mseq16;
    end M_SEQ16_BFM_F;
end m_seq_bfm_pack;


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_misc.all;
use IEEE.math_real.all;
use STD.textio.all;
use IEEE.std_logic_textio.all;

library work;
use work.m_seq_bfm_pack.all;

--library unisim;
--use unisim.vcomponents.all;

entity axi_slave_bfm is
  generic (
    C_S_AXI_ID_WIDTH             : integer := 1;
    C_S_AXI_ADDR_WIDTH           : integer := 32;
    C_S_AXI_DATA_WIDTH           : integer := 32;
    C_S_AXI_AWUSER_WIDTH    : integer := 1;
    C_S_AXI_ARUSER_WIDTH    : integer := 1;
    C_S_AXI_WUSER_WIDTH     : integer := 1;
    C_S_AXI_RUSER_WIDTH     : integer := 1;
    C_S_AXI_BUSER_WIDTH      : integer := 1;
    
    C_S_AXI_TARGET            : integer := 0;
    C_OFFSET_WIDTH            : integer := 10; -- 割り当てるRAMのアドレスのビット幅
    C_S_AXI_BURST_LEN        : integer := 256;
    
    WRITE_RANDOM_WAIT        : integer := 1; -- Write Transaction のデータ転送の時にランダムなWaitを発生させる=1, Waitしない=0
    READ_RANDOM_WAIT        : integer := 0; -- Read Transaction のデータ転送の時にランダムなWaitを発生させる=1, Waitしない=0
    READ_DATA_IS_INCREMENT    : integer := 0; -- ReadトランザクションでRAMの内容をReadする = 0(RAMにWriteしたものをReadする)、Readデータを+1する = 1(データは+1したデータをReadデータとして使用する
    RANDOM_BVALID_WAIT        : integer := 0;    -- Write Data Transaction が終了した後で、BVALID をランダムにWaitする = 1、BVALID をランダムにWaitしない = 0, 31 ~ 0 クロックのWait
    RAM_INIT_FILE            : string := "init_ram_data.data"; -- RAM の初期化ファイル名
    LOAD_RAM_INIT_FILE        : integer := 0; -- RAM_INIT_FILE をLoadする - 1, Load しない - 0
    AWREADY_IS_USUALLY_HIGH    : integer := 1; -- AWRAEDY は通常はLow=0, High=1
    ARREADY_IS_USUALLY_HIGH : integer := 1  -- AWRAEDY は通常はLow=0, High=1    
    );
  port(
    -- System Signals
    ACLK    : in std_logic;
    ARESETN : in std_logic;

    -- Master Interface Write Address Ports
    S_AXI_AWID     : in  std_logic_vector(C_S_AXI_ID_WIDTH-1 downto 0);
    S_AXI_AWADDR   : in  std_logic_vector(C_S_AXI_ADDR_WIDTH-1 downto 0);
    S_AXI_AWLEN    : in  std_logic_vector(8-1 downto 0);
    S_AXI_AWSIZE   : in  std_logic_vector(3-1 downto 0);
    S_AXI_AWBURST  : in  std_logic_vector(2-1 downto 0);
    -- S_AXI_AWLOCK   : in  std_logic_vector(2-1 downto 0);
    S_AXI_AWLOCK   : in  std_logic_vector(1 downto 0);
    S_AXI_AWCACHE  : in  std_logic_vector(4-1 downto 0);
    S_AXI_AWPROT   : in  std_logic_vector(3-1 downto 0);
    S_AXI_AWQOS    : in  std_logic_vector(4-1 downto 0);
    S_AXI_AWUSER   : in  std_logic_vector(C_S_AXI_AWUSER_WIDTH-1 downto 0);
    S_AXI_AWVALID  : in  std_logic;
    S_AXI_AWREADY  : out std_logic;

    -- Master Interface Write Data Ports
    S_AXI_WDATA  : in  std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0);
    S_AXI_WSTRB  : in  std_logic_vector(C_S_AXI_DATA_WIDTH/8-1 downto 0);
    S_AXI_WLAST  : in  std_logic;
    S_AXI_WUSER  : in  std_logic_vector(C_S_AXI_WUSER_WIDTH-1 downto 0);
    S_AXI_WVALID : in  std_logic;
    S_AXI_WREADY : out std_logic;

    -- Master Interface Write Response Ports
    S_AXI_BID    : out std_logic_vector(C_S_AXI_ID_WIDTH-1 downto 0);
    S_AXI_BRESP  : out std_logic_vector(2-1 downto 0);
    S_AXI_BUSER  : out std_logic_vector(C_S_AXI_BUSER_WIDTH-1 downto 0);
    S_AXI_BVALID : out std_logic;
    S_AXI_BREADY : in  std_logic;

    -- Master Interface Read Address Ports
    S_AXI_ARID     : in  std_logic_vector(C_S_AXI_ID_WIDTH-1 downto 0);
    S_AXI_ARADDR   : in  std_logic_vector(C_S_AXI_ADDR_WIDTH-1 downto 0);
    S_AXI_ARLEN    : in  std_logic_vector(8-1 downto 0);
    S_AXI_ARSIZE   : in  std_logic_vector(3-1 downto 0);
    S_AXI_ARBURST  : in  std_logic_vector(2-1 downto 0);
    S_AXI_ARLOCK   : in  std_logic_vector(2-1 downto 0);
    S_AXI_ARCACHE  : in  std_logic_vector(4-1 downto 0);
    S_AXI_ARPROT   : in  std_logic_vector(3-1 downto 0);
    S_AXI_ARQOS    : in  std_logic_vector(4-1 downto 0);
    S_AXI_ARUSER   : in  std_logic_vector(C_S_AXI_ARUSER_WIDTH-1 downto 0);
    S_AXI_ARVALID  : in  std_logic;
    S_AXI_ARREADY  : out std_logic;

    -- Master Interface Read Data Ports
    S_AXI_RID    : out std_logic_vector(C_S_AXI_ID_WIDTH-1 downto 0);
    S_AXI_RDATA  : out std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0);
    S_AXI_RRESP  : out std_logic_vector(2-1 downto 0);
    S_AXI_RLAST  : out std_logic;
    S_AXI_RUSER  : out std_logic_vector(C_S_AXI_RUSER_WIDTH-1 downto 0);
    S_AXI_RVALID : out std_logic;
    S_AXI_RREADY : in  std_logic
    );

end axi_slave_bfm;

architecture implementation of axi_slave_bfm is

constant    AxBURST_FIXED    : std_logic_vector := "00";
constant    AxBURST_INCR    : std_logic_vector := "01";
constant    AxBURST_WRAP    : std_logic_vector := "10";

constant    RESP_OKAY        : std_logic_vector := "00";
constant    RESP_EXOKAY        : std_logic_vector := "01";
constant    RESP_SLVERR        : std_logic_vector := "10";
constant    RESP_DECERR        : std_logic_vector := "11";

constant    DATA_BUS_BYTES     : natural := C_S_AXI_DATA_WIDTH/8; -- データバスのビット幅
constant    ADD_INC_OFFSET    : natural := natural(log(real(DATA_BUS_BYTES), 2.0));

-- fifo depth for address
constant    AD_FIFO_DEPTH            : natural := 16;

-- wad_fifo field
constant    WAD_FIFO_WIDTH            : natural := C_S_AXI_ADDR_WIDTH+5+C_S_AXI_ID_WIDTH-1+1;
constant    WAD_FIFO_AWID_HIGH        : natural := C_S_AXI_ADDR_WIDTH+5+C_S_AXI_ID_WIDTH-1;
constant    WAD_FIFO_AWID_LOW        : natural := C_S_AXI_ADDR_WIDTH+5;
constant    WAD_FIFO_AWBURST_HIGH    : natural := C_S_AXI_ADDR_WIDTH+4;
constant    WAD_FIFO_AWBURST_LOW    : natural := C_S_AXI_ADDR_WIDTH+3;
constant    WAD_FIFO_AWSIZE_HIGH    : natural := C_S_AXI_ADDR_WIDTH+2;
constant    WAD_FIFO_AWSIZE_LOW        : natural := C_S_AXI_ADDR_WIDTH;
constant    WAD_FIFO_ADDR_HIGH        : natural := C_S_AXI_ADDR_WIDTH-1;
constant    WAD_FIFO_ADDR_LOW        : natural := 0;

-- wres_fifo field
constant    WRES_FIFO_WIDTH            : natural := 2+C_S_AXI_ID_WIDTH-1+1;
constant    WRES_FIFO_AWID_HIGH        : natural := 2+C_S_AXI_ID_WIDTH-1;
constant    WRES_FIFO_AWID_LOW        : natural := 2;
constant    WRES_FIFO_AWBURST_HIGH    : natural := 1;
constant    WRES_FIFO_AWBURST_LOW    : natural := 0;

-- rad_fifo field
constant    RAD_FIFO_WIDTH            : natural := C_S_AXI_ADDR_WIDTH+13+C_S_AXI_ID_WIDTH-1+1;
constant    RAD_FIFO_ARID_HIGH        : natural := C_S_AXI_ADDR_WIDTH+13+C_S_AXI_ID_WIDTH-1;
constant    RAD_FIFO_ARID_LOW        : natural := C_S_AXI_ADDR_WIDTH+13;
constant    RAD_FIFO_ARBURST_HIGH    : natural := C_S_AXI_ADDR_WIDTH+12;
constant    RAD_FIFO_ARBURST_LOW    : natural := C_S_AXI_ADDR_WIDTH+11;
constant    RAD_FIFO_ARSIZE_HIGH    : natural := C_S_AXI_ADDR_WIDTH+10;
constant    RAD_FIFO_ARSIZE_LOW        : natural := C_S_AXI_ADDR_WIDTH+8;
constant    RAD_FIFO_ARLEN_HIGH        : natural := C_S_AXI_ADDR_WIDTH+7;
constant    RAD_FIFO_ARLEN_LOW        : natural := C_S_AXI_ADDR_WIDTH;
constant    RAD_FIFO_ADDR_HIGH        : natural := C_S_AXI_ADDR_WIDTH-1;
constant    RAD_FIFO_ADDR_LOW        : natural := 0;

-- RAMの生成
constant    SLAVE_ADDR_NUMBER    : integer := 2**(C_OFFSET_WIDTH - ADD_INC_OFFSET);
type ram_array_def is array (0 to SLAVE_ADDR_NUMBER-1) of std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0);

impure function InitRamFromFile (RamFileName : in string) return ram_array_def is
    FILE RamFile : text is in RamFileName;
    variable RamFileLine : line;
    variable RAM : ram_array_def;
begin
    for I in ram_array_def'range loop
        if (LOAD_RAM_INIT_FILE=1) then
            readline (RamFile, RamFileLine);
            hread (RamFileLine, RAM(I));
        end if;
    end loop;
    return RAM;
end function;
signal ram_array : ram_array_def := InitRamFromFile(RAM_INIT_FILE);

-- for write transaction
type write_address_state is (idle_wrad, awr_accept);
type write_data_state is (idle_wrdt, wr_burst);
type write_response_state is (idle_wres, wait_bvalid, bvalid_assert);
signal wradr_cs : write_address_state;
signal wrdat_cs : write_data_state;
signal wrres_cs : write_response_state;
signal addr_inc_step_wr : integer := 1;
signal awready         : std_logic;
signal wr_addr         : std_logic_vector(C_OFFSET_WIDTH-1 downto 0);
signal wr_bvalid     : std_logic;
signal m_seq16_wr    : std_logic_vector(15 downto 0);
signal wready        : std_logic;
type wready_state is (idle_wready, assert_wready, deassert_wready);
signal cs_wready : wready_state;
signal cdc_we : std_logic;
signal wad_fifo_full, wad_fifo_empty : std_logic;
signal wad_fifo_almost_full, wad_fifo_almost_empty : std_logic;
signal wad_fifo_rd_en : std_logic;
signal wad_fifo_wr_en : std_logic;
signal wad_fifo_din : std_logic_vector(WAD_FIFO_WIDTH-1 downto 0);
signal wad_fifo_dout : std_logic_vector(WAD_FIFO_WIDTH-1 downto 0);
signal m_seq16_wr_res    : std_logic_vector(15 downto 0);
signal wr_resp_cnt : std_logic_vector(4 downto 0);
signal wres_fifo_wr_en : std_logic;
signal wres_fifo_full, wres_fifo_empty : std_logic;
signal wres_fifo_almost_full, wres_fifo_almost_empty : std_logic;
signal wres_fifo_rd_en : std_logic;
signal wres_fifo_din : std_logic_vector(WRES_FIFO_WIDTH-1 downto 0);
signal wres_fifo_dout : std_logic_vector(WRES_FIFO_WIDTH-1 downto 0);

-- for read transaction
type read_address_state is (idle_rda, arr_accept);
type read_data_state is (idle_rdd, rd_burst);
type read_last_state is (idle_rlast, rlast_assert);
signal rdadr_cs : read_address_state;
signal rddat_cs : read_data_state;
signal rdlast : read_last_state;
signal addr_inc_step_rd : integer := 1;
signal arready         : std_logic;
signal rd_addr         : std_logic_vector(C_OFFSET_WIDTH-1 downto 0);
signal rd_axi_count    : std_logic_vector(7 downto 0);
signal rvalid        : std_logic;
signal rlast        : std_logic;
signal m_seq16_rd    : std_logic_vector(15 downto 0);
type rvalid_state is (idle_rvalid, assert_rvalid, deassert_rvalid);
signal cs_rvalid : rvalid_state;
signal read_data_count : std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0);

signal reset_1d, reset_2d, reset : std_logic := '1';
signal rad_fifo_full, rad_fifo_empty : std_logic;
signal rad_fifo_almost_full, rad_fifo_almost_empty : std_logic;
signal rad_fifo_rd_en : std_logic;
signal rad_fifo_wr_en : std_logic;
signal rad_fifo_din : std_logic_vector(RAD_FIFO_WIDTH-1 downto 0);
signal rad_fifo_dout : std_logic_vector(RAD_FIFO_WIDTH-1 downto 0);

component sync_fifo generic (
    constant    C_MEMORY_SIZE     : integer := 512;    -- Word (not byte), 2のn乗
    constant    DATA_BUS_WIDTH    : integer := 32        -- RAM Data Width
);
 port (
    clk                : in    std_logic;
    rst                : in     std_logic;
    wr_en            : in     std_logic;
    din                : in     std_logic_vector(DATA_BUS_WIDTH-1 downto 0);
    full            : out     std_logic;
    almost_full     : out     std_logic;
    rd_en            : in     std_logic;
    dout            : out    std_logic_vector(DATA_BUS_WIDTH-1 downto 0);
    empty            : out    std_logic;
    almost_empty    : out    std_logic
);
end component;

begin
    -- ARESETN をACLK で同期化
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            reset_1d <= not ARESETN;
            reset_2d <= reset_1d;
        end if;
    end process;
    reset <= reset_2d;
    
    -- AXI4バス Write Address State Machine
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                wradr_cs <= idle_wrad;
                awready <= '0';
            else
                case (wradr_cs) is
                    when idle_wrad =>
                        if S_AXI_AWVALID='1' and wad_fifo_full='0' and wres_fifo_full='0' then -- S_AXI_AWVALID が1にアサートされた
                            wradr_cs <= awr_accept;
                            awready <= '1';
                        end if;
                    when awr_accept => -- S_AXI_AWREADY をアサート
                        wradr_cs <= idle_wrad;
                        awready <= '0';
                end case;
            end if;
        end if;
    end process;
    S_AXI_AWREADY <= not wad_fifo_full when AWREADY_IS_USUALLY_HIGH=1 else awready;
    
    -- S_AXI_AWID & S_AXI_AWBURST & S_AXI_AWSIZE & S_AXI_AWADDR を保存しておく同期FIFO
    wad_fifo_din <= (S_AXI_AWID & S_AXI_AWBURST & S_AXI_AWSIZE & S_AXI_AWADDR);
    wad_fifo_wr_en <= (S_AXI_AWVALID and (not wad_fifo_full)) when AWREADY_IS_USUALLY_HIGH=1 else awready;

    wad_fifo : sync_fifo generic map(
        C_MEMORY_SIZE => AD_FIFO_DEPTH,
        DATA_BUS_WIDTH => WAD_FIFO_WIDTH
    ) port map (
        clk =>            ACLK,
        rst =>            reset,
        wr_en =>         wad_fifo_wr_en,
        din =>            wad_fifo_din,
        full =>            wad_fifo_full,
        almost_full =>    wad_fifo_almost_full,
        rd_en =>        wad_fifo_rd_en,
        dout =>            wad_fifo_dout,
        empty =>         wad_fifo_empty,
        almost_empty =>    wad_fifo_almost_empty
    );
    wad_fifo_rd_en <= '1' when wready='1' and S_AXI_WVALID='1' and S_AXI_WLAST='1' else '0';

    -- AXI4バス Write Data State Machine
    process (ACLK) begin
        if ACLK'event and ACLK='1' then
            if reset='1' then
                wrdat_cs <= idle_wrdt;
            else
                case( wrdat_cs ) is
                    when idle_wrdt =>
                        if wad_fifo_empty='0' then -- AXI Write アドレス転送の残りが1個以上ある
                            wrdat_cs <= wr_burst;
                        end if;
                    when wr_burst => -- Writeデータの転送
                        if S_AXI_WLAST='1' and S_AXI_WVALID='1' and wready='1' then -- Write Transaction 終了
                            wrdat_cs <= idle_wrdt;
                        end if;
                    when others =>
                
                end case ;
            end if;
        end if;
    end process;

    -- m_seq_wr、16ビットのM系列を計算する
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                m_seq16_wr <= (0 => '1', others => '0');
            else
                if WRITE_RANDOM_WAIT=1 then -- Write Transaction 時にランダムなWaitを挿入する
                    if wrdat_cs=wr_burst then
                        m_seq16_wr <= M_SEQ16_BFM_F(m_seq16_wr);
                    end if;
                else -- Wait無し
                    m_seq16_wr <= (others => '0');
                end if;
            end if;
        end if;
    end process;
                
    -- wready の処理、M系列を計算して128以上だったらWaitする。
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                cs_wready <= idle_wready;
                wready <= '0';
            else
                case (cs_wready) is
                    when idle_wready =>
                        if wrdat_cs=idle_wrdt and wad_fifo_empty='0' then -- 次はwr_burst
                            if m_seq16_wr(7)='0' and wres_fifo_full='0' then -- wready='1'
                                cs_wready <= assert_wready;
                                wready <= '1';
                            else -- m_seq16_wr(7)='1' then -- wready='0'
                                cs_wready <= deassert_wready;
                                wready <= '0';
                            end if;
                        end if;
                    when assert_wready => -- 一度wreadyがアサートされたら、1つのトランザクションが終了するまでwready='1'
                        if wrdat_cs=wr_burst and S_AXI_WLAST='1' and S_AXI_WVALID='1' then -- 終了
                            cs_wready <= idle_wready;
                            wready <= '0';
                        elsif wrdat_cs=wr_burst and S_AXI_WVALID='1' then -- 1つのトランザクション終了。
                            if m_seq16_wr(7)='1' or wres_fifo_full='1' then
                                cs_wready <= deassert_wready;
                                wready <= '0';
                            end if;
                        end if;
                    when deassert_wready =>
                        if m_seq16_wr(7)='0' and wres_fifo_full='0' then -- wready='1'
                            cs_wready <= assert_wready;
                            wready <= '1';
                        end if;
                end case;
            end if;
        end if;
    end process;
    
    S_AXI_WREADY <= wready;
    cdc_we <= '1' when wrdat_cs=wr_burst and wready='1' and S_AXI_WVALID='1' else '0';
    
    -- addr_inc_step_wr の処理
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                addr_inc_step_wr <= 1;
            else
                if wrdat_cs=idle_wrdt and wad_fifo_empty='0' then
                    case (wad_fifo_dout(WAD_FIFO_AWSIZE_HIGH downto WAD_FIFO_AWSIZE_LOW)) is
                        when "000" => -- 8ビット転送
                            addr_inc_step_wr <= 1;
                        when "001" => -- 16ビット転送
                            addr_inc_step_wr <= 2;
                        when "010" => -- 32ビット転送
                            addr_inc_step_wr <= 4;
                        when "011" => -- 64ビット転送
                            addr_inc_step_wr <= 8;
                        when "100" => -- 128ビット転送
                            addr_inc_step_wr <= 16;
                        when "101" => -- 256ビット転送
                            addr_inc_step_wr <= 32;
                        when "110" => -- 512ビット転送
                            addr_inc_step_wr <= 64;
                        when others => --"111" => -- 1024ビット転送
                            addr_inc_step_wr <= 128;
                    end case;
                end if;
            end if;
        end if;
    end process;
    
    -- wr_addr の処理
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                wr_addr <= (others => '0');
            else
                if wrdat_cs=idle_wrdt and wad_fifo_empty='0' then
                    wr_addr <= wad_fifo_dout(C_OFFSET_WIDTH-1 downto 0);
                elsif wrdat_cs=wr_burst and S_AXI_WVALID='1' and wready='1' then -- アドレスを進める
                    wr_addr <= std_logic_vector(unsigned(wr_addr) + addr_inc_step_wr);
                end if;
            end if;
        end if;
    end process;
    
    -- Wirte Response FIFO (wres_fifo)
    wres_fifo : sync_fifo generic map(
        C_MEMORY_SIZE => AD_FIFO_DEPTH,
        DATA_BUS_WIDTH => WRES_FIFO_WIDTH
    ) port map (
        clk =>            ACLK,
        rst =>            reset,
        wr_en =>         wres_fifo_wr_en,
        din =>            wres_fifo_din,
        full =>            wres_fifo_full,
        almost_full =>    wres_fifo_almost_full,
        rd_en =>        wres_fifo_rd_en,
        dout =>            wres_fifo_dout,
        empty =>         wres_fifo_empty,
        almost_empty =>    wres_fifo_almost_empty
    );
    wres_fifo_wr_en <= '1' when S_AXI_WLAST='1' and S_AXI_WVALID='1' and wready='1' else '0'; -- Write Transaction 終了
    wres_fifo_din <= (wad_fifo_dout(WAD_FIFO_AWID_HIGH downto WAD_FIFO_AWID_LOW) & wad_fifo_dout(WAD_FIFO_AWBURST_HIGH downto WAD_FIFO_AWBURST_LOW));
    wres_fifo_rd_en <= '1' when wr_bvalid='1' and S_AXI_BREADY='1' and wres_fifo_empty='0' else '0';

    -- S_AXI_BID の処理
    S_AXI_BID <= wres_fifo_dout(WRES_FIFO_AWID_HIGH downto WRES_FIFO_AWID_LOW);
    
    -- S_AXI_BRESP の処理
    -- S_AXI_AWBURSTがINCRの時はOKAYを返す。それ以外はSLVERRを返す。
    S_AXI_BRESP <= RESP_OKAY when wres_fifo_dout(WRES_FIFO_AWBURST_HIGH downto WRES_FIFO_AWBURST_LOW)=AxBURST_INCR else RESP_SLVERR;
    
    -- wr_bvalid の処理
    -- wr_bvalid のアサートは、Write Data Channelの完了より必ず1クロックは遅延する
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                wrres_cs <= idle_wres;
                wr_bvalid <= '0';
            else
                case( wrres_cs ) is
                    when idle_wres =>
                        if wres_fifo_empty='0' then -- Write Transaction 終了
                            if unsigned(m_seq16_wr_res) = 0 or RANDOM_BVALID_WAIT=0 then
                                wrres_cs <= bvalid_assert;
                                wr_bvalid <= '1';
                            else
                                wrres_cs <= wait_bvalid;
                            end if;
                        end if;
                    when wait_bvalid =>
                        if unsigned(wr_resp_cnt) = 0 then
                            wrres_cs <= bvalid_assert;
                            wr_bvalid <= '1';
                        end if;
                    when bvalid_assert =>
                        if (S_AXI_BREADY='1') then
                            wrres_cs <= idle_wres;
                            wr_bvalid <= '0';
                        end if;
                    when others =>
                
                end case ;
            end if;
        end if;
    end process;
    S_AXI_BVALID <= wr_bvalid;
    S_AXI_BUSER <= (others => '0');

    -- wr_resp_cnt
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                wr_resp_cnt <= (others => '0');
            else
                if wrres_cs=idle_wres and wres_fifo_empty='0' then
                    wr_resp_cnt <= m_seq16_wr_res(4 downto 0);
                elsif unsigned(wr_resp_cnt) /= 0 then
                    wr_resp_cnt <= std_logic_vector(unsigned(wr_resp_cnt) - 1);
                end if;
            end if;
        end if;
    end process;

    -- m_seq_wr_res、16ビットのM系列を計算する
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                m_seq16_wr_res <= (0 => '1', others => '0');
            else
                m_seq16_wr_res <= M_SEQ16_BFM_F(m_seq16_wr_res);
            end if;
        end if;
    end process;
    
    
    -- AXI4バス Read Address Transaction State Machine
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                rdadr_cs <= idle_rda;
                arready <= '0';
            else
                case (rdadr_cs) is
                    when idle_rda =>
                        if S_AXI_ARVALID='1' and rad_fifo_full='0' then -- Read Transaction 要求
                            rdadr_cs <= arr_accept;
                            arready <= '1';
                        end if;
                    when arr_accept => -- S_AXI_ARREADY をアサート
                        rdadr_cs <= idle_rda;
                        arready <= '0';
                end case;
            end if;
        end if;
    end process;
    S_AXI_ARREADY <= not rad_fifo_full when ARREADY_IS_USUALLY_HIGH=1 else arready;

    -- S_AXI_ARID & S_AXI_ARBURST & S_AXI_ARSIZE & S_AXI_ARLEN & S_AXI_ARADDR を保存しておく同期FIFO
    rad_fifo_din <= (S_AXI_ARID & S_AXI_ARBURST & S_AXI_ARSIZE & S_AXI_ARLEN & S_AXI_ARADDR);
    rad_fifo_wr_en <= (S_AXI_ARVALID and (not rad_fifo_full)) when ARREADY_IS_USUALLY_HIGH=1 else arready;

    rad_fifo : sync_fifo generic map (
        C_MEMORY_SIZE =>    AD_FIFO_DEPTH,
        DATA_BUS_WIDTH =>    RAD_FIFO_WIDTH
    ) port map (
        clk =>            ACLK,
        rst =>            reset,
        wr_en =>        rad_fifo_wr_en,
        din =>             rad_fifo_din,
        full =>            rad_fifo_full,
        almost_full =>    rad_fifo_almost_full,
        rd_en =>        rad_fifo_rd_en,
        dout =>            rad_fifo_dout,
        empty =>        rad_fifo_empty,
        almost_empty =>    rad_fifo_almost_empty
    );
    rad_fifo_rd_en <= '1' when rvalid='1' and S_AXI_RREADY='1' and rlast='1' else '0';

    -- AXI4バス Read Data Transaction State Machine
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                rddat_cs <= idle_rdd;
            else
                case (rddat_cs) is
                    when idle_rdd =>
                        if rad_fifo_empty='0' then -- AXI Read アドレス転送の残りが1個以上ある
                            rddat_cs <= rd_burst;
                        end if;
                    when rd_burst =>
                        if unsigned(rd_axi_count)=0 and rvalid='1' and S_AXI_RREADY='1' then -- Read Transaction 終了
                            rddat_cs <= idle_rdd;
                        end if;
                end case;
            end if;
        end if;
    end process;

    -- m_seq_rd、16ビットのM系列を計算する
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                m_seq16_rd <= (others => '1'); -- Writeとシードを変更する
            else
                if READ_RANDOM_WAIT=1 then -- Read Transaciton のデータ転送でランダムなWaitを挿入する場合
                    if rddat_cs=rd_burst then
                        m_seq16_rd <= M_SEQ16_BFM_F(m_seq16_rd);
                    end if;
                else -- Wati無し
                    m_seq16_rd <= (others => '0');
                end if;
            end if;
        end if;
    end process;
                
    -- rvalid の処理、M系列を計算して128以上だったらWaitする。
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                cs_rvalid <= idle_rvalid;
                rvalid <= '0';
            else
                case (cs_rvalid) is
                    when idle_rvalid =>
                        if rddat_cs=idle_rdd and rad_fifo_empty='0' then -- 次はrd_burst
                            if m_seq16_rd(7)='0' then -- rvalid='1'
                                cs_rvalid <= assert_rvalid;
                                rvalid <= '1';
                            else -- m_seq16_rd(7)='1' then -- rvalid='0'
                                cs_rvalid <= deassert_rvalid;
                                rvalid <= '0';
                            end if;
                        end if;
                    when assert_rvalid => -- 一度rvalidがアサートされたら、1つのトランザクションが終了するまでrvalid='1'
                        if rddat_cs=rd_burst and rlast='1' and S_AXI_RREADY='1' then -- 終了
                            cs_rvalid <= idle_rvalid;
                            rvalid <= '0';
                        elsif rddat_cs=rd_burst and S_AXI_RREADY='1' then -- 1つのトランザクション終了。
                            if m_seq16_rd(7)='1' then
                                cs_rvalid <= deassert_rvalid;
                                rvalid <= '0';
                            end if;
                        end if;
                    when deassert_rvalid =>
                        if m_seq16_rd(7)='0' then -- rvalid='1'
                            cs_rvalid <= assert_rvalid;
                            rvalid <= '1';
                        end if;
                end case;
            end if;
        end if;
    end process;
    
    S_AXI_RVALID <= rvalid;
    
    -- addr_inc_step_rd の処理
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                addr_inc_step_rd <= 1;
            else
                if rddat_cs=idle_rdd and rad_fifo_empty='0' then
                    case (rad_fifo_dout(RAD_FIFO_ARSIZE_HIGH downto RAD_FIFO_ARSIZE_LOW)) is
                        when "000" => -- 8ビット転送
                            addr_inc_step_rd <= 1;
                        when "001" => -- 16ビット転送
                            addr_inc_step_rd <= 2;
                        when "010" => -- 32ビット転送
                            addr_inc_step_rd <= 4;
                        when "011" => -- 64ビット転送
                            addr_inc_step_rd <= 8;
                        when "100" => -- 128ビット転送
                            addr_inc_step_rd <= 16;
                        when "101" => -- 256ビット転送
                            addr_inc_step_rd <= 32;
                        when "110" => -- 512ビット転送
                            addr_inc_step_rd <= 64;
                        when others => -- "111" => -- 1024ビット転送
                            addr_inc_step_rd <= 128;
                    end case;
                end if;
            end if;
        end if;
    end process;
    
    -- rd_addr の処理
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                rd_addr <= (others => '0');
            else
                if rddat_cs=idle_rdd and rad_fifo_empty='0' then
                    rd_addr <= rad_fifo_dout(C_OFFSET_WIDTH-1 downto 0);
                elsif rddat_cs=rd_burst and S_AXI_RREADY='1' and rvalid='1' then
                    rd_addr <= std_logic_vector(unsigned(rd_addr) + addr_inc_step_rd);
                end if;
            end if;
        end if;
    end process;
    
    -- rd_axi_count の処理(AXIバス側のデータカウント)
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                rd_axi_count <= (others => '0');
            else
                if rddat_cs=idle_rdd and rad_fifo_empty='0' then -- rd_axi_count のロード
                    rd_axi_count <= rad_fifo_dout(RAD_FIFO_ARLEN_HIGH downto RAD_FIFO_ARLEN_LOW);
                elsif rddat_cs=rd_burst and rvalid='1' and S_AXI_RREADY='1' then -- Read Transaction が1つ終了
                    rd_axi_count <= std_logic_vector(unsigned(rd_axi_count) - 1);
                end if;
            end if;
        end if;
    end process;
    
    -- rdlast State Machine
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                rdlast <= idle_rlast;
                rlast <= '0';
            else
                case (rdlast) is
                    when idle_rlast =>
                        if unsigned(rd_axi_count)=1 and rvalid='1' and S_AXI_RREADY='1' then -- バーストする場合
                            rdlast <= rlast_assert;
                            rlast <= '1';
                        elsif rddat_cs=idle_rdd and rad_fifo_empty='0' and unsigned(rad_fifo_dout(RAD_FIFO_ARLEN_HIGH downto RAD_FIFO_ARLEN_LOW))=0 then -- 転送数が1の場合
                            rdlast <= rlast_assert;
                            rlast <= '1';
                        end if;
                    when rlast_assert => 
                        if rvalid='1' and S_AXI_RREADY='1' then -- Read Transaction 終了(rd_axi_count=0は決定)
                            rdlast <= idle_rlast;
                            rlast <= '0';
                        end if;
                end case;
            end if;
        end if;
    end process;
    S_AXI_RLAST <= rlast;
    
    -- S_AXI_RID, S_AXI_RUSER の処理
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                S_AXI_RID <= (others => '0');
            else
                if rddat_cs=idle_rdd and rad_fifo_empty='0' then
                    S_AXI_RID <= rad_fifo_dout(RAD_FIFO_ARID_HIGH downto RAD_FIFO_ARID_LOW);
                end if;
            end if;
        end if;
    end process;
    S_AXI_RUSER <= (others => '0');
    
    -- S_AXI_RRESP は、S_AXI_ARBURST がINCR の場合はOKAYを返す。それ以外はSLVERRを返す。
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                S_AXI_RRESP <= (others => '0');
            else
                if rddat_cs=idle_rdd and rad_fifo_empty='0' then
                    if rad_fifo_dout(RAD_FIFO_ARBURST_HIGH downto RAD_FIFO_ARBURST_LOW)=AxBURST_INCR then
                        S_AXI_RRESP <= RESP_OKAY;
                    else
                        S_AXI_RRESP <= RESP_SLVERR;
                    end if;
                end if;
            end if;
        end if;
    end process;
    
    -- RAM
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if cdc_we='1' then
                for i in 0 to C_S_AXI_DATA_WIDTH/8-1 loop
                    if S_AXI_WSTRB(i)='1' then -- Byte Enable
                        ram_array(TO_INTEGER(unsigned(wr_addr(C_OFFSET_WIDTH-1 downto ADD_INC_OFFSET))))(i*8+7 downto i*8) <= S_AXI_WDATA(i*8+7 downto i*8);
                    end if;
                end loop;
            end if;
        end if;
    end process;

    -- Read Transaciton の時に +1 されたReadデータを使用する(Read 毎に+1)
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            if reset='1' then
                read_data_count <= (others => '0');
            else
                if rddat_cs=rd_burst and rvalid='1' and S_AXI_RREADY='1' then -- Read Transaction が1つ終了
                    read_data_count <= std_logic_vector(unsigned(read_data_count) + 1);
                end if;
            end if;
        end if;
    end process;
    
    S_AXI_RDATA <= ram_array(TO_INTEGER(unsigned(rd_addr(C_OFFSET_WIDTH-1 downto ADD_INC_OFFSET)))) when READ_DATA_IS_INCREMENT=0 else read_data_count;
    
end implementation;


init_ram_data.data を貼っておく。

00000000
00000001
00000002
00000003
00000004
00000005
00000006
00000007
00000008
00000009
0000000A
0000000B
0000000C
0000000D
0000000E
0000000F
00000010
00000011
00000012
00000013
00000014
00000015
00000016
00000017
00000018
00000019
0000001A
0000001B
0000001C
0000001D
0000001E
0000001F
00000020
00000021
00000022
00000023
00000024
00000025
00000026
00000027
00000028
00000029
0000002A
0000002B
0000002C
0000002D
0000002E
0000002F
00000030
00000031
00000032
00000033
00000034
00000035
00000036
00000037
00000038
00000039
0000003A
0000003B
0000003C
0000003D
0000003E
0000003F
00000040
00000041
00000042
00000043
00000044
00000045
00000046
00000047
00000048
00000049
0000004A
0000004B
0000004C
0000004D
0000004E
0000004F
00000050
00000051
00000052
00000053
00000054
00000055
00000056
00000057
00000058
00000059
0000005A
0000005B
0000005C
0000005D
0000005E
0000005F
00000060
00000061
00000062
00000063
00000064
00000065
00000066
00000067
00000068
00000069
0000006A
0000006B
0000006C
0000006D
0000006E
0000006F
00000070
00000071
00000072
00000073
00000074
00000075
00000076
00000077
00000078
00000079
0000007A
0000007B
0000007C
0000007D
0000007E
0000007F
00000080
00000081
00000082
00000083
00000084
00000085
00000086
00000087
00000088
00000089
0000008A
0000008B
0000008C
0000008D
0000008E
0000008F
00000090
00000091
00000092
00000093
00000094
00000095
00000096
00000097
00000098
00000099
0000009A
0000009B
0000009C
0000009D
0000009E
0000009F
000000A0
000000A1
000000A2
000000A3
000000A4
000000A5
000000A6
000000A7
000000A8
000000A9
000000AA
000000AB
000000AC
000000AD
000000AE
000000AF
000000B0
000000B1
000000B2
000000B3
000000B4
000000B5
000000B6
000000B7
000000B8
000000B9
000000BA
000000BB
000000BC
000000BD
000000BE
000000BF
000000C0
000000C1
000000C2
000000C3
000000C4
000000C5
000000C6
000000C7
000000C8
000000C9
000000CA
000000CB
000000CC
000000CD
000000CE
000000CF
000000D0
000000D1
000000D2
000000D3
000000D4
000000D5
000000D6
000000D7
000000D8
000000D9
000000DA
000000DB
000000DC
000000DD
000000DE
000000DF
000000E0
000000E1
000000E2
000000E3
000000E4
000000E5
000000E6
000000E7
000000E8
000000E9
000000EA
000000EB
000000EC
000000ED
000000EE
000000EF
000000F0
000000F1
000000F2
000000F3
000000F4
000000F5
000000F6
000000F7
000000F8
000000F9
000000FA
000000FB
000000FC
000000FD
000000FE
000000FF


sync_fifo.vhd を貼っておく。

-- Synchronous FIFO for Simulation
--
-- 2014/07/03 by marsee
--
-- ライセンスは二条項BSDライセンス (2-clause BSD license)とします。
--

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_misc.all;
use IEEE.math_real.all;

entity sync_fifo is 
    generic (
        C_MEMORY_SIZE    : integer := 512;    -- Word (not byte), 2のn乗
        DATA_BUS_WIDTH    : integer := 32        -- RAM Data Width
    );
    port (
        clk                : in    std_logic;
        rst                : in    std_logic;
        wr_en            : in    std_logic;
        din                : in    std_logic_vector(DATA_BUS_WIDTH-1 downto 0);
        full            : out    std_logic;
        almost_full        : out    std_logic;
        rd_en            : in    std_logic;
        dout            : out    std_logic_vector(DATA_BUS_WIDTH-1 downto 0);
        empty            : out     std_logic;
        almost_empty    : out    std_logic
    );
end sync_fifo;

architecture RTL of sync_fifo is

constant C_MEMORY_LENGTH : natural := natural(ceil(log(real(C_MEMORY_SIZE), 2.0))); -- C_MEMORY_SIZEの2進数の桁数

type mem_type is array (0 to C_MEMORY_SIZE-1) of std_logic_vector(DATA_BUS_WIDTH-1 downto 0);
signal mem             : mem_type := (OTHERS => (OTHERS => '0'));
signal mem_waddr    : std_logic_vector(C_MEMORY_LENGTH-1 downto 0) := (others => '0');
signal mem_raddr    : std_logic_vector(C_MEMORY_LENGTH-1 downto 0) := (others => '0');
signal rp            : std_logic_vector(C_MEMORY_LENGTH-1 downto 0) := (others => '0');
signal wp            : std_logic_vector(C_MEMORY_LENGTH-1 downto 0) := (others => '0');

signal almost_full_node        : std_logic;
signal almost_empty_node    : std_logic;
signal full_node                : std_logic;
signal empty_node                : std_logic;
begin
    -- Write
    process (clk) begin
        if clk'event and clk='1' then
            if rst='1' then
                mem_waddr <= (others => '0');
                wp <= (others => '0');
            else
                if wr_en='1' then
                    mem_waddr <= std_logic_vector(unsigned(mem_waddr) + 1);
                    wp <= std_logic_vector(unsigned(wp) + 1);
                end if;
            end if;
        end if;
    end process;

    process (clk) begin
        if clk'event and clk='1' then
            if wr_en='1' then
                mem(TO_INTEGER(unsigned(mem_waddr))) <= din;
            end if;
        end if;
    end process;

    full_node <= '1' when std_logic_vector(unsigned(wp)+1)=rp else '0';
    full <= full_node;
    almost_full_node <= '1' when std_logic_vector(unsigned(wp)+2)=rp else '0';
    almost_full <= full_node or almost_full_node;

    -- Read
    process (clk) begin
        if clk'event and clk='1' then
            if rst='1' then
                mem_raddr <= (others => '0');
                rp <= (others => '0');
            else
                if rd_en='1' then
                    mem_raddr <= std_logic_vector(unsigned(mem_raddr) + 1);
                    rp <= std_logic_vector(unsigned(rp) + 1);
                end if;
            end if;
        end if;
    end process;

    dout <= mem(TO_INTEGER(unsigned(mem_raddr)));

    empty_node <= '1' when wp=rp else '0';
    empty <= empty_node;
    almost_empty_node <= '1' when wp=std_logic_vector(unsigned(rp)+1) else '0';
    almost_empty <= empty_node or almost_empty_node;
end RTL;

  1. 2016年11月09日 06:59 |
  2. AXI4バス
  3. | トラックバック:0
  4. | コメント:0

ikzmw さんの axi_slave_bfm test をやってみた2

ikzmw さんの axi_slave_bfm test をやってみた1”の続き。

Vivado 2016.3 のaxi_slave_bfm_test_1 プロジェクトが表示されたところで前回のブログが終了したので、今回はそこから始めよう。このプロジェクトはVHDL のAXI4 Slave BFM 用だ。
axi_slave_bfm_test_21_161107.png

Flow Navigator の Simulation → Run Simulation → Run Behavioral Simulation を実行した。
axi_slave_bfm_test_22_161108.png

Tcl Console を開くと、Error、Mismatch、Warning は 0 だった。
axi_slave_bfm_test_23_161108.png

シミュレーションの波形を示す。
axi_slave_bfm_test_24_161108.png

axi_slave_bfm_test_25_161108.png

次に、create_axi_slave_bfm_v_post_synth_test_1.tcl を試してみた。

前回同様に、Tools メニュー -> Run Tcl Script... を選択する。

create_axi_slave_bfm_v_post_synth_test_1.tcl を選択してOK ボタンをクリックした。

tcl スクリプトが実行された。

Open project アイコンをクリックして、axi_slave_bfm_test\sim\vivado\axi_slave_bfm_test\axi_slave_bfm_v_post_synth_test_1.xpr を開いた。

Vivado 2016.3 のaxi_slave_bfm_v_post_synth_test_1プロジェクトが表示された。
axi_slave_bfm_test_26_161108.png

Flow Navigator のSynthesis → Run Synthesis をクリックして論理合成を行った。

Flow Navigator の Simulation → Run Simulation → Run Post-Synthesis Functional Simulation を実行した。
axi_slave_bfm_test_27_161108.png

Tcl Console を開くと、Error、Mismatch、Warning は 0 だった。
axi_slave_bfm_test_28_161108.png

シミュレーションの波形を示す。
axi_slave_bfm_test_29_161108.png

axi_slave_bfm_test_30_161108.png

論理合成後のシミュレーションも問題ないようだ。ikwzm さんありがとうございました。
  1. 2016年11月08日 04:35 |
  2. AXI4バス
  3. | トラックバック:0
  4. | コメント:0

ikzmw さんの axi_slave_bfm test をやってみた1

ツィッターで ikwzm さんが私の(Verilog 版はcobac さんがVHDL版を直してくれたのだが)AXI4 Slave BFM をシミュレーションして動いたと報告してくれた。それで、 ikwzm さんのikwzm/axi_slave_bfm_test をやってみた。

なお、AXI4 Slave BFM は
AXI4 Slave Bus Functional Model のVerilog HDL版4
AXI4 Slave Bus Functional Model のVHDL版4

git コマンドはWindows 10 のUbuntu を試してみることにした。

コマンドプロンプトを起動して、bash を起動した。

/mnt/c/Users/Masaaki/Documents/Vivado/Sim ディレクトリにcd した。
git コマンドを sudo apt-get install git でインストールした。
git clone git://github.com/ikwzm/axi_slave_bfm_test.git axi_slave_bfm_test
cd axi_slave_bfm_test
git submodule init
git submodule update

axi_slave_bfm_test_1_161106.png

これで axi_slave_bfm ディレクトリが作成された。
axi_slave_bfm_test_2_161106.png

ここで、Vivaod 2016.3 を起動した。

Tools メニュー -> Run Tcl Script... を選択する。
axi_slave_bfm_test_3_161106.png

C:\Users\Masaaki\Documents\Vivado\Sim\axi_slave_bfm_test\sim\vivado\axi_slave_bfm_test のcreate_axi_slave_bfm_test_1.tcl を選択してOK ボタンをクリックした。
axi_slave_bfm_test_4_161106.png

tcl スクリプトが実行された。

Open project アイコンをクリックして、axi_slave_bfm_test\sim\vivado\axi_slave_bfm_test\axi_slave_bfm_test_1.xpr を開いた。

Vivado 2016.3 のaxi_slave_bfm_test_1 プロジェクトが表示された。
axi_slave_bfm_test_21_161107.png
  1. 2016年11月07日 05:09 |
  2. AXI4バス
  3. | トラックバック:0
  4. | コメント:0

JACORN2016に参加しました

金曜日、土曜日とJACORN2016 に参加して、発表もさせていただきました。

車でJACORN2016 の行われる会津若松まで初めて行ったのですが、朝6時ころ出たら、午前10時30分頃着きそうなので、鶴ヶ城を見学してきました。なかなか立派な城で、屋上からの眺めも良かったです。

金曜日の夕方に「Vivado HLS(高位合成)を使ってみませんか?」という題で、Vivado HLS のことをお話しさせていただきました。
JACORN2016_1_161106.jpg

JACORN2016_2_161106.jpg

参加者の皆様と楽しい時間を過ごさせていただきました。また夜遅くまでFPGA の話題で盛り上がっていて、とても居心地が良かったです。

土曜日はJACORN2016 の後で会津大学の奥山先生に会津大学を案内していただきました。とっても綺麗で素敵な大学です。
ドローンも見せていただいて、飛ばさせて頂いたのですが、失敗してプロペラを破損させてしまいました。申し訳ありませんでした。
その後で、奥山先生に連れて行ってもらった美味しいお蕎麦のお店で皆さんとお蕎麦を頂きました。本当にお世話になりました。ありがとうございました。

また、機会があったら行ってみたいです。皆様ありがとうございました。
  1. 2016年11月06日 04:05 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:4

FPGAマガジンNo.12の「第4章 共通カスタムLinuxディストリビューションの開発」をやってみた1

FPGAマガジンNo.12 ARMコアFPGA×Linux初体験」の「第4章 共通カスタムLinuxディストリビューションの開発」をやってみることにした。
きっかけは、「Intel SoC FPGA デベロッパー・フォーラム」でDE0-Nano-SoC をもらってしまったので、何かやらねばということで、沿いう言えばひでみさんの記事のZynq とSoC FPGA 両対応でブートできるSDカードをやってみたかったんだっけ?ということで、試しにやってみることにした。
なお、売り物の記事の内容を実行してみているので、具体的な手順ははっきりと示さない。やってみたいと思ったら、FPGAマガジンNo.12 を購入してやってほしい。

環境はWindows 10 でVirtualBox 5.1.8 を動かして、そこにUbuntu 14.04 LTS を動作させている。それ上でビルドしている。

最初に49 ページ左カラム真ん中のYocto Project のビルドで必要なツール類をインストールした。
XA_Linux_build_1_161103.png

49ページ左下のrepo コマンドをダウンロードしたところだ。
XA_Linux_build_2_161103.png

図5 の repo sync が終了した。
XA_Linux_build_3_161103.png

図5 の cd poky; ls が終了したところだ。
XA_Linux_build_4_161103.png

49 ページ真ん中の ls conf が終了した。
XA_Linux_build_5_161103.png

Linux ディストリビューションのレシピ名を表示した。
XA_Linux_build_6_161103.png

図6 のcd tmp/deploy/sdk できなかった。tmp ディレクトリの下は cache しかなかった。
XA_Linux_build_7_161103.png

困ったな。どうしよう?
善後策を考える。
  1. 2016年11月03日 07:58 |
  2. Linux
  3. | トラックバック:0
  4. | コメント:0

Intel SoC FPGA デベロッパー・フォーラム

昨日、Intel SoC FPGA デベロッパー・フォーラムに行って来ました。

竹村さんのOpenCLのお話が分かりやすくて、とても良かったです。
鳥海さんと初めてお会いできて、お話をすることができました。
私にはあまり関係はないのですが、セキュア・ブートの仕組みとかを聞けたのが、面白かったです。

ハードウェア・トラックとソフトウェア・トラックに分かれいていて、ソフトウェア・トラックに参加したのですが、やはり、SoC FPGAはソフトウェアもハードウェアもないので、両方の面白そうなセミナに参加したいですね。

抽選でTerasic のDE0-Nano-SoC ボードが当たっちゃいました。
DE0-Nano-SoC_161102.jpg

凄い、くじ運で、一生分の運を使ってしまったのでなければ良いのですが。。。
DE0-Nano-SoC は1台持っているので、どうしようかな?と思っています。

ひでみさんのFPGAマガジンNo.12 のZynq, SoC FPGA 両対応のLinux をビルドしてみましょうか?
  1. 2016年11月02日 04:39 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:0

MT9D111をコードを伸ばしてステレオ・カメラにする5(AXI IICの出力を遅延する)

MT9D111をコードを伸ばしてステレオ・カメラにする4(AXI IICの設定)”の続き。

前回はAXI IIC IP のSDA Inertial delay 設定を入れて、出力が遅延すると思ったのだが、それは勘違いだった。今回は、Synchronizerを入れてAXI IIC の出力を遅延してみた。

Synchronizer はFF を入れていくことによって異なるクロックへ同期するIP で、Number of Stages に入れた数のFF を通してくれる。下の図は250 つまり、 2.5 us の遅延を入れてあるが、最初は 100 つまり、1 us にしてみた。
stereo_cam_13_161101.png

ブロックデザインを示す。Synchronizer はAXI IIC の sda_t に入っている。最初は sda_o に入れたのだが、全く関係なかった。オープンドレイン信号なので、sda_o は 0 固定で、0 にするときだけ、sda_t をイネーブルするのだろう。
最初にcamera_interface 内を示す。
stereo_cam_14_161101.png

camera_interface のインスタンス状況を示す。
stereo_cam_15_161101.png

ZYBO_0_wrapper.v のmt9d111_iic_sda_iobuf を以下の通りに書き換えた。

   IOBUF mt9d111_iic_sda_iobuf
        (.I(mt9d111_iic_sda_o),
         .IO(mt9d111_iic_sda_io),
         .O(mt9d111_iic_sda_i),
         .T(outp));


これで、論理合成、インプリメント、ビットストリームの生成を行った。
stereo_cam_16_161101.png

ハードウェアをエクスポートして、SDKを立ち上げ、FPGAにビットストリームをダウンロードして、cam_disp_lgh.elf を実行した。

Synchronizer のNumber of Stages = 100 (1us)の時は、以前よりも確率は上がったが、やはりI2C が通らないときがあった。
MT9D111カメラ・インターフェース基板をZYBO のPMOD に直付けの時の波形を示す。約 1 us SDAがSCL に対して遅延している。(MT9D111 カメラ基板のプルアップ抵抗は5 KΩ)
stereo_cam_17_161101.jpg

Synchronizer のNumber of Stages = 250 (2.5 us)にしてやってみた。こちらも、1 us の時と同様だった。少し、2.5 us の時のほうがI2C が通る確率が少し上がったかな?いずれにせよ。確実にI2C が通るわけではない。(MT9D111 カメラ基板のプルアップ抵抗は5 KΩ)
MT9D111カメラ・インターフェース基板を延長したときの波形を示す。ノイズはあるが、約 2.5 us 約 1 us SDAがSCL に対して遅延している。
stereo_cam_18_161101.jpg

やはり、I2C リピーターIC が搭載できる新しいMT9D111 カメラ・インターフェース基板を待つことにしよう。
  1. 2016年11月01日 04:57 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:1