FC2カウンター FPGAの部屋 2017年04月
fc2ブログ

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

FPGAの部屋

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

Vivado 2017.1 の新機能6(AXI Verification IPの使い方の覚書2)

AXI Verification IPの使い方の覚書2です。大体AXI Verification IPの使い方が分かってきた。
今回は、シミュレーションの下から2番目の sim_adv_mst_active__pt_passive__slv_mem をやってみます。
AXI Verification IP v1.0 LogiCORE IP Product Guide Vivado Design Suite PG267 April 5, 2017”の 46 ページの”Figure 6-2: Advanced Simulation Sets”のうちの sim_adv_mst_active_pt_passive_slv_mem を引用する。
Vivado_2017-1_49_170429.png
図1 sim_adv_mst_active__pt_passive__slv_mem

上の図のように、前回からはSlave VIP にメモリが入ったモデルを使用しているところが違っている。
このモデルで、Behavioral Simulation を行った。波形が表示された後で、run all を実行して、$finish で停止する所まで実行した。
Slave VIP のシミュレーション波形を図2 に示す。
Vivado_2017-1_50_170429.png
図2 Slave VIP のシミュレーション波形1

さて、ここで wready の波形を変更してみよう。
wready はスレーブが出力する。よって、axi_vip_0_mem_stimulus.sv の user_gen_wready タスクを変更してみよう。
axi_vip_0_mem_stimulus.sv の user_gen_wready タスク部分を引用する。
Vivado_2017-1_51_170429.png

wready_gen.set_ready_policy(XIL_AXI_READY_GEN_OSC);
があるが、これで、wready の出力のポリシーを設定しているようだ。今は、XIL_AXI_READY_GEN_OSC で wready_gen.set_low_time(1); と wready_gen.set_high_time(2); の設定値を交互に出力している。図 2 を見ると、wready の High の期間が 2 クロック、Low の期間が 1 クロックになっているのが分かる。
これを

wready_gen.set_low_time(1);
wready_gen.set_high_time(1);

に変更してみよう。
変更後にBehavioral Simulation を行った。波形を図3 に示す。
Vivado_2017-1_52_170429.png
図3 Slave VIP のシミュレーション波形2

図2 と図3 を比べてみると、図3 の s_axi_wready のHigh の幅が短くなっているのが分かる。図3 では、wready のHigh の幅とLow の幅が等しくなっている。

次に、

wready_gen.set_ready_policy(XIL_AXI_READY_GEN_RANDOM);

に変更してみよう。
変更後にBehavioral Simulation を行った。波形を図4 に示す。
Vivado_2017-1_53_170429.png
図4 Slave VIP のシミュレーション波形3

wready の生成がランダムになった。

次は、Write トランザクションを変更してみる。
1番目のWrite トランザクションはランダムなので、そのままとする。2番目のWrite トランザクションは値を設定しているので、それを変更してみよう。当初の2番目のWrite トランザクションを示す。
Vivado_2017-1_54_170429.png

mtestADDR(アドレス)は0番地なので 0x100 番地に、mtestBurstLength(バースト長)は0 (シングル転送)なので、15 (16バースト)に変更する。
そして、Write するデータは mtestWData にセットすれば良いらしい。32ビット幅なので、32768 バイトと宣言されているmtestWData の32ビット区切りでデータを入れていけばよいようだ。

mtestWData[31:0] = 32'h12345678;
mtestWData[63:32] = 32'h9abcdef0;を

入れてみよう。
Vivado_2017-1_55_170429.png

変更後にBehavioral Simulation を行った。全体波形を図5 に示す。
Vivado_2017-1_56_170429.png
図 5 Slave VIP のシミュレーション波形4(全体波形)


2番目のアドレス転送を拡大してみよう。
Vivado_2017-1_57_170429.png
図 6 Slave VIP のシミュレーション波形4(アドレス転送の拡大)

100番地で 16 バースト、32ビット幅のWrite トランザクションとなっている。

2番目のデータ転送を拡大してみよう。
Vivado_2017-1_58_170429.png
図 7 Slave VIP のシミュレーション波形5(データ転送の拡大)

最初のWrite データが 0x12345678 で2番目のWrite データが 0x9abcdef0であること分かる。 (2018/02/09:修正、@ryos36 さんありがとうございました)

なお、AXI VIP API の資料は、”AR# 68234 AXI Verification IP - AXI VIP のリリース ノートおよび既知の問題”のXILINX_AXI_VIP_2017_1.zip を参照するとよいと思う。
  1. 2017年04月29日 06:22 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

Vivado 2017.1 の新機能5(AXI Verification IPの使い方の覚書)

Vivado 2017.1 の新機能4(AXI Verification IPのサンプル・デザイン2)”の続き。

AXI Verification IP v1.0 LogiCORE IP Product Guide Vivado Design Suite PG267 April 5, 2017”の 48 ページ、”Useful Coding Guidelines and Examples”を読みながら覚書を書いておく。

テストベンチに必要なもの
・SystemVerilog のテストベンチ

・ axi_vip_v1_0_1_pkg と <component_name>_pkg の import
  <component_name>_pkgはAXI Verification IPを生成するときに作られて、パラメータ化されたエージェントへのtypedef クラスが
含まれている。
import 文は、AXI Verification IPをIPI でインスタンスする際に指示されている。AXI Verification IPをダブルクリックして開くと書いてある。
Vivado_2017-1_46_170427.png

それをテストベンチで import すればよい。axi_vip_0_mst_stimulus.sv の一部を引用する。
Vivado_2017-1_45_170427.png

・エージェントを宣言する。AXI Verification IPの各モードに関するエージェントの名前規則の表を引用する。
Vivado_2017-1_47_170427.png

・エージェントを new でインスタンスする。その際に、<階層パス>.IF を指定する必要がある。
<例> agent = new("master vip agent",DUT.ex_design.axi_vip_mst.inst.IF);
Vivado_2017-1_48_170427.png

・エージェントをスタートさせる。
 AXI Master VIP → agent.start_master(); (エージェント名は new したもの)

 AXI Slave VIP → agent.start_slave(); (エージェント名は new したもの)
  <component_name>_slv_t が使われている場合
READ_WRITE_MODEというのがあって、READ_ONLY やWRITE_ONLY に指定できるようだ。READ_WRITE(UGに書いていないので推定だが)の場合は、Read , Write 共に応答をテストベンチに書いておく必要がある。ONLY の場合は必要なRead またはWrite についての応答をテストベンチに書くておく。

  <component_name>_slv_mem_tが使われている場合(メモリ内蔵)
Read も Write もエージェントで処理されるそうだ。サンプル・デザインを見てみよう。

 AXI pass-through VIP をスレーブ・モードで使うとき
<hierarchy_path>.set_slave_mode(); (スレーブ・モードにセットして)
passthrough_agent.start_slave();  (スレーブとしてエージェントをスタートさせる)
スレーブと同じ注意点がある。

 AXI pass-through VIP をマスタ・モードで使うとき
<hierarchy_path>.set_master_mode(); (マスタ・モードにセットして)
passthrough_agent.start_master(); (マスタとしてエージェントをスタートさせる)

 AXI pass-through VIP をパススルー・モードで使うとき
デフォルトではパススルー・モード。パススルー・モードで使うときは設定が要らないようだ。今回のテストベンチに記述が無い。
マスタ・モードやスレーブ・モードからパススルー・モードにする場合は、下の設定を行う。
<hierarchy_path>.set_passthrough_mode();.
AXI pass-through VIP をパススルー・モードにする場合に、マスタ・モードからだったら stop_master を呼ぶ必要がある。スレーブ・モードからだったら stop_slave を呼ぶ必要がある。

レディ信号の生成
rready 信号の生成は、create_readyメソッドでやるらしい?
rready_gen = mst_agent.rd_driver.create_ready("rready");

後はPG267 を読んでください。

  1. 2017年04月27日 05:08 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

AXI4マスタ版ラプラシアン・フィルタ最速の条件

この記事はFPGAマガジンNo.17 に載るはずだったけど、ページが足りなくて載せることができなかったコラムです。

今回、「高位合成ツールVivado HLS 特設記事」第2章で説明した第5段階のAXI4マスタ版のラプラシアン・フィルタですが、その最速の条件を探すためにCソースコードを少しずつ変更して最速の条件を確かめてみました。

AXI4マスタ・ライトのみにif文を使用した場合
まずは、出力の画像フォーマットを出力画像の回り1ビットが0のフォーマットに戻してみましょう。つまり、「高位合成ツールVivado HLS 特設記事」第1章、図23の左の出力画像フォーマットにしてみようと思います。特設記事第2章のリスト1のlap変数をlap_fb[]にライトする部分を少し変更します。特設記事第2章のリスト1では、lap_fp[]にライトするときにifなどの条件文は使用していませんでしたが、今回はif文を使用します。具体的には、変数yが0のとき(つまり最初の行)は、lapの値は0に設定しているので、lap_fb[]の最後の行に書き込むことにします。変数yが1以降のとき(2番目以降の行)では、lap_fb[]の最初の行から順に書き込みます。この部分を抜き出してリスト1 に示します。

             if (y == 0)
                lap_fb[(VERTICAL_PIXEL_WIDTH-1)*HORIZONTAL_PIXEL_WIDTH+x] = lap;
            else
                lap_fb[(y-1)*HORIZONTAL_PIXEL_WIDTH+x] = lap;

リスト 1:  lap_fb[]のライト部分の変更点


Cコードの合成のLatencyは480019クロックでした。なお、今回の画像は800ピクセルx 600行の画像を使用しています。つまり、1ピクセル1クロックとして全てのピクセルを処理すると480000クロック必要です。それと比較するとCコード合成時のLatencyは効率が良いと言えると思います。次にC/RTL協調シミュレーションを行うと、Latencyは1120051クロックでした。Cコードの合成のときのLatencyからすると2倍以上にレイテンシが増えています。C/RTL協調シミュレーション波形を見ると、リードは16バーストなのですが、ライトはシングル転送になっていました。このためC/RTL協調シミュレーション時のレイテンシが増えてしまったようです。詳しくは、「AXI4-Stream向きのコードで書いたラプラシアンフィルタをVivado HLSでテスト2」をご覧ください。

AXI4マスタのリードとライト両方にif文を使用した場合
次に、出力の画像フォーマットを出力画像の回り1ビットが0のフォーマットに戻すのは同じですが、1行余計にループを回すことにします。最初の行はlap_fb[]に書き込まないようにし、最後の行(つまり、1行多いので、リードするピクセルはもう残っていないため)cam_fb[]から読み込まないようにします。つまり、リードもライトもif文が入ることになります。リード部分の変更点をリスト2 にライト部分の変更点をリスト3 に示します。

    for (int y=0; y<=VERTICAL_PIXEL_WIDTH; y++){
        for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE
            if (y < VERTICAL_PIXEL_WIDTH)
                pix = cam_fb[y*HORIZONTAL_PIXEL_WIDTH+x];

リスト 2:  cam_fb[]のリード部分の変更点


            if (y != 0)
                lap_fb[(y-1)*HORIZONTAL_PIXEL_WIDTH+x] = lap;

リスト 3:  lap_fb[]のライト部分の変更点


この実装では、Cコードの合成時のLatencyは480821クロックでした。この実装でも、画像は800ピクセルx 600行の画像を使用しています。次にC/RTL協調シミュレーションを行ってみたところ、1121430クロックでした。この実装でも、Cコードの合成のときのLatencyからすると2倍以上にレイテンシが増えています。しかも、「AXI4マスタ・ライトのみにif文を使用した場合」よりも遅くなってしまいました。C/RTL協調シミュレーション波形を見ると、リードもライトもシングル転送でした。詳しくは、「AXI4-Stream向きのコードで書いたラプラシアンフィルタをVivado HLSでテスト3」をご覧ください。

2つの実装の結果から言えることは、メモリにリード、ライトする記述にif文があるとシングル転送になるのではないか?ということです。つまり、AXI4マスタ版で最速実装ができる幅は狭いということが言えると思います。
  1. 2017年04月26日 20:30 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

FPGAマガジンNo.17にVivado HLS の記事を書きました(ソースコードをGitHubで公開)

FPGAマガジンNo.17 の 80 ページから「高位合成ツールVivado HLS 特設記事」ということで、記事を書きました。
なお、その記事中に使用したC や C++ のソースコードをGitHubで公開しました。

第1章
【データが順番に流れるならストリームが最適】
無料のVivado HL WebPACK Edition 2016.4で高位合成にチャレンジしよう(AXI4ストリーム編)
AXI4ストリームによるラプラシアン・フィルタ処理回路の実装

でVivado HLS のAXI4-Stream編として書きました。これが本命だと思っています。このようにデータのストリーミング中に処理を行うという方法がFPGA に合っていると言いますか、これができないようだとCPU やGPU に処理速度で負けちゃうんじゃないでしょうか?というのが私の意見です。
このソースコードは、marsee101/FPGAmagagine17_Vivado_HLS_AXI4S にアップロードしてあります。

第2章
【これ以上の高速化は難しい?!】
無料のVivado HL WebPACK Edition 2016.4で高位合成にチャレンジしよう(AXI4ストリーム編)
ついに80倍高速化!AXI4マスタ版ラプラシアン・フィルタの最適化

第2章ではVivado HLS のAXI4 Master 編(FPGAマガジンNo.16)でアルゴリズムの通りに実装したラプラシアンフィルタ回路と最適化を行ったラプラシアンフィルタ回路の間の性能差は約 25 倍でしたが、約 80 倍の性能のAXI4 Master のラプラシアンフィルタ回路ができたので、ご紹介しました。もう原理的に限界の性能を出すことができました。記事のタイトルが誤解を招くかもしれませんが、これはAXI4-Stream インターフェースではなく、AXI4 インターフェースのMaster です。AXI4-Stream 版とい同じように書いたら最速になった、AXI4 インターフェースのMaster 版のラプラシアンフィルタ回路の話です。よろしくお願いします。
まあ、本当はどのように書いても同じように最速だったら良いのですが、ソースコードの意図を回路に反映するのか?しないのか?という話もあるので、一概には言えないと思います。ソースコードの意図を無視して、いつも最速にしちゃってよいのか?という話はありますよね。。。
この最速のAXI4 Master のソースコードは marsee101/FPGAmagagine16_Vivado_HLS_AXI4M/lap_filter5_axim/ にあります。
  1. 2017年04月26日 04:57 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado 2017.1 の新機能4(AXI Verification IPのサンプル・デザイン2)

Vivado 2017.1 の新機能3(AXI Verification IPのサンプル・デザイン1)”の続き。

前回は、AXI Verification IPをIPI にAdd IP してサンプル・デザインを呼び出した。今回は、サンプル・デザインをシミュレーションしてみよう。

最初に、Source ウインドウを示す。
10 個のシミュレーション用テストベンチが並んでいる。
そのうちのaxi_vip_0_exdes_adv_mst_active__pt_passive__slv_comb がアクティブになっている。
Vivado_2017-1_37_170423.png

axi_vip_0_exdes_adv_mst_active__pt_passive__slv_comb の図を”AXI Verification IP v1.0 LogiCORE IP Product Guide Vivado Design Suite PG267 April 5, 2017”の”Figure 6-2: Advanced Simulation Sets”から引用する。
Vivado_2017-1_44_170425.png
上の図を見ると、Pass-Through VIP はPassive でトランザクションを通すモードになっていると思う。

Flow Navigator のSIMULATION -> Run Simulation をクリックして、Run Behavioral Simulation を選択した。
論理シミュレーションがスタートした。
Vivado_2017-1_38_170423.png

最初に波形ウインドウに入っていたのは、clk と reset だけだったので、Divider を挟みながら、各VIP モジュールの信号を入れていった。そして、run all を行った。
axi_vip_mst の全体波形を示す。
Vivado_2017-1_39_170425.png

axi_vip_passthroug の全体波形を示す。
Vivado_2017-1_40_170425.png
Vivado_2017-1_41_170425.png

axi_vip_slv の全体波形を示す。
Vivado_2017-1_42_170425.png

axi_vip_mst のアドレス転送部分を拡大してみよう。
Vivado_2017-1_43_170425.png

まずは、Write トランザクションから、最初のWrite トランザクションのパラメータを示す。
awaddr = 0, awlen = 0d, awsize = 0, awburst = 0, awlock = 0, awcache = e, awport = 4, awregion = 0, awqos = 1
awlen のバースト長 14、awsize は1バイトずつの転送、awburst はアドレス固定、awlock はノーマル・アクセス、awcache はWrite-through Read and Write-allocate、awport は Instruction access, Secure access, Unprivileged acces だった。

次のWrite トランザクションを示す。
awaddr = 0, awlen = 0, awsize = 2, awburst = 1, awlock = 0, awcache = 0, awport = 0, awregion = 0, awqos = 0
awlen のバースト長 1、awsize は4バイトずつの転送、awburst はアドレスをインクリメント、awlock はノーマル・アクセス、awcache はDevice Non-bufferable、awport は Data access, Secure access, Unprivileged acces だった。

最初のRead トランザクションを示す。
araddr = fffffffe, arlen = 0f, arsize = 1, arburst = 2, arlock = 0, arcache = f, arport = 1, arregion = c, arqos = b
arlen のバースト長 16、arsize は2 バイトずつの転送、arburst はアドレスをキャッシュに使われるようにwrap around、arlock はノーマル・アクセス、arcache はWrite-back Read and Write-allocate、arport は Data access, Non-Secure access, Unprivileged acces だった。

次の Read トランザクションを示す。
araddr = 00000000, arlen = 0, arsize = 2, arburst = 1, arlock = 0, arcache = 0, arport = 0, arregion = 0, arqos = 0
arlen のバースト長 1、arsize は4バイトずつの転送、arburst はアドレスをインクリメント、arlock はノーマル・アクセス、arcache はDevice Non-bufferable、arport は Data access, Secure access, Unprivileged acces だった。

最初の Read トランザクションは 0xfffffffe から 16バーストなので、4 K バイト境界を越してしまう。よって、AXI のプロトコル違反なんじゃないか?と思う。ランダムで生成されているようなのだが、ランダムの数に制約は付けられないのかな?
ランダムに発生させた数に自分で制約を与えれば良いのだとは思うが。。。
  1. 2017年04月25日 06:08 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

「ニューラルネットワークと深層学習」をやってみる1(CHAPTER 1 ニューラルネットワークを用いた手書き文字認識)

とっても良い資料の「ニューラルネットワークと深層学習」をやってみよう。

そのうちの、「CHAPTER 1 CHAPTER 1 ニューラルネットワークを用いた手書き文字認識」をやってみる。
内容はWeb サイトをよく見てもらうとよくわかると思う。
更にGitHub にPython 2系のコードが置いてあって実際に試してみることができる。
試した環境はVirtualBox 5.1.20 上にインストールしたUbuntu 16.04 となっている。Python のバージョンは2.7.6 だった。

まずは、

git clone https://github.com/mnielsen/neural-networks-and-deep-learning.git

でGitHub からクローンした。
neural-networks-and-deep-learning ディレクトリが作成されて、中に data, fig, src ディレクトリがあった。
DLNN_1_170424.png

data ディレクトリには、mnist.pkl.gz たぶん手書き文字のデータが入っていた。
fig には、文字の画像やグラフが入っている。
DLNN_2_170424.png

src にはPython のソース・ファイルが入っている。
DLNN_3_170424.png

Python を動かすには src ディレクトリで動作させるが、危ないかな?ということで、DLNN_example ディレクトリを新規作成して、neural-networks-and-deep-learning ディレクトリの中身をコピー&ペーストした。
DLNN_4_170424.png

~/DNN/DLNN_example/src ディレクトリに行って、

python

とタイプしてPython をインタプリタモードで起動した。
次のコマンドを入力した

import mnist_loader
training_data, validation_data, test_data = \
mnist_loader.load_data_wrapper()
import network
net = network.Network([784, 30, 10])
net.SGD(training_data, 30, 10, 3.0, test_data=test_data)


DLNN_5_170424.png

学習が始まり、30回で終了した。
DLNN_6_170424.png
Epoch 28 で 86.27 % なので、書いてあるよりも性能が悪い。

次に、隠れニューロンを 30 から 100 に変更した。
net = network.Network([784, 100, 10])
net.SGD(training_data, 30, 10, 3.0, test_data=test_data)


DLNN_7_170424.png
DLNN_8_170424.png
Epoch 28 で 96.37 % だった。こっちは書いてあるのに近い。

学習率η=0.001 に変更した。

net = network.Network([784, 100, 10])
net.SGD(training_data, 30, 10, 0.001, test_data=test_data)


DLNN_9_170424.png
DLNN_10_170424.png
やはり学習が進まない。

学習率η=0.01 に変更した。

net = network.Network([784, 100, 10])
net.SGD(training_data, 30, 10, 0.01, test_data=test_data)


DLNN_11_170424.png
DLNN_12_170424.png
学習率η=0.001 ほどではないがやはり学習が遅い。

学習率η=100 にしてみた。

net = network.Network([784, 100, 10])
net.SGD(training_data, 30, 10, 100, test_data=test_data)


DLNN_13_170424.png
DLNN_14_170424.png
ほとんど学習できていない感じだ。

全てのコマンドと表示を貼っておく。

ono@ono-VirtualBox:~/DNN$ cd DLNN_example/
ono@ono-VirtualBox:~/DNN/DLNN_example$ ls
README.md  data  fig  requirements.txt  src
ono@ono-VirtualBox:~/DNN/DLNN_example$ cd src
ono@ono-VirtualBox:~/DNN/DLNN_example/src$ python
Python 2.7.6 (default, Oct 26 2016, 20:30:19) 
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import mnist_loader
>>> import network
>>> net = network.Network([784, 30, 10])
>>> net.SGD(training_data, 30, 10, 3.0, test_data=test_data)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'training_data' is not defined
>>> 
>>> q
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'q' is not defined
>>> exit
Use exit() or Ctrl-D (i.e. EOF) to exit
>>> 
ono@ono-VirtualBox:~/DNN/DLNN_example/src$ python
Python 2.7.6 (default, Oct 26 2016, 20:30:19) 
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import mnist_loader
>>> training_data, validation_data, test_data = \
... mnist_loader.load_data_wrapper()
>>> import network
>>> net = network.Network([784, 30, 10])
>>> net.SGD(training_data, 30, 10, 3.0, test_data=test_data)
Epoch 0: 7500 / 10000
Epoch 1: 8497 / 10000
Epoch 2: 8557 / 10000
Epoch 3: 8609 / 10000
Epoch 4: 8605 / 10000
Epoch 5: 8638 / 10000
Epoch 6: 8635 / 10000
Epoch 7: 8631 / 10000
Epoch 8: 8662 / 10000
Epoch 9: 8654 / 10000
Epoch 10: 8657 / 10000
Epoch 11: 8672 / 10000
Epoch 12: 8682 / 10000
Epoch 13: 8691 / 10000
Epoch 14: 8702 / 10000
Epoch 15: 8681 / 10000
Epoch 16: 8691 / 10000
Epoch 17: 8696 / 10000
Epoch 18: 8672 / 10000
Epoch 19: 8704 / 10000
Epoch 20: 8695 / 10000
Epoch 21: 8715 / 10000
Epoch 22: 8711 / 10000
Epoch 23: 8718 / 10000
Epoch 24: 8710 / 10000
Epoch 25: 8726 / 10000
Epoch 26: 8686 / 10000
Epoch 27: 8693 / 10000
Epoch 28: 8727 / 10000
Epoch 29: 8703 / 10000
>>> net = network.Network([784, 100, 10])
>>> net.SGD(training_data, 30, 10, 3.0, test_data=test_data)
Epoch 0: 8352 / 10000
Epoch 1: 8510 / 10000
Epoch 2: 8550 / 10000
Epoch 3: 8613 / 10000
Epoch 4: 8629 / 10000
Epoch 5: 8659 / 10000
Epoch 6: 8689 / 10000
Epoch 7: 8695 / 10000
Epoch 8: 8705 / 10000
Epoch 9: 8742 / 10000
Epoch 10: 9498 / 10000
Epoch 11: 9561 / 10000
Epoch 12: 9588 / 10000
Epoch 13: 9569 / 10000
Epoch 14: 9600 / 10000
Epoch 15: 9595 / 10000
Epoch 16: 9617 / 10000
Epoch 17: 9610 / 10000
Epoch 18: 9619 / 10000
Epoch 19: 9625 / 10000
Epoch 20: 9626 / 10000
Epoch 21: 9626 / 10000
Epoch 22: 9630 / 10000
Epoch 23: 9641 / 10000
Epoch 24: 9634 / 10000
Epoch 25: 9629 / 10000
Epoch 26: 9649 / 10000
Epoch 27: 9628 / 10000
Epoch 28: 9637 / 10000
Epoch 29: 9629 / 10000
>>> net = network.Network([784, 100, 10])
>>> net.SGD(training_data, 30, 10, 0.001, test_data=test_data)
Epoch 0: 1172 / 10000
Epoch 1: 1116 / 10000
Epoch 2: 1022 / 10000
Epoch 3: 1201 / 10000
Epoch 4: 1693 / 10000
Epoch 5: 1810 / 10000
Epoch 6: 1906 / 10000
Epoch 7: 1973 / 10000
Epoch 8: 2024 / 10000
Epoch 9: 2089 / 10000
Epoch 10: 2158 / 10000
Epoch 11: 2206 / 10000
Epoch 12: 2255 / 10000
Epoch 13: 2291 / 10000
Epoch 14: 2341 / 10000
Epoch 15: 2378 / 10000
Epoch 16: 2411 / 10000
Epoch 17: 2455 / 10000
Epoch 18: 2479 / 10000
Epoch 19: 2526 / 10000
Epoch 20: 2555 / 10000
Epoch 21: 2598 / 10000
Epoch 22: 2640 / 10000
Epoch 23: 2682 / 10000
Epoch 24: 2718 / 10000
Epoch 25: 2751 / 10000
Epoch 26: 2777 / 10000
Epoch 27: 2809 / 10000
Epoch 28: 2845 / 10000
Epoch 29: 2878 / 10000
>>> net = network.Network([784, 100, 10])
>>> net.SGD(training_data, 30, 10, 0.01, test_data=test_data)
Epoch 0: 1515 / 10000
Epoch 1: 1890 / 10000
Epoch 2: 2038 / 10000
Epoch 3: 2185 / 10000
Epoch 4: 2339 / 10000
Epoch 5: 2506 / 10000
Epoch 6: 2684 / 10000
Epoch 7: 2852 / 10000
Epoch 8: 2981 / 10000
Epoch 9: 3123 / 10000
Epoch 10: 3253 / 10000
Epoch 11: 3422 / 10000
Epoch 12: 3560 / 10000
Epoch 13: 3640 / 10000
Epoch 14: 3721 / 10000
Epoch 15: 3770 / 10000
Epoch 16: 3834 / 10000
Epoch 17: 3899 / 10000
Epoch 18: 3967 / 10000
Epoch 19: 4052 / 10000
Epoch 20: 4134 / 10000
Epoch 21: 4216 / 10000
Epoch 22: 4316 / 10000
Epoch 23: 4412 / 10000
Epoch 24: 4513 / 10000
Epoch 25: 4591 / 10000
Epoch 26: 4686 / 10000
Epoch 27: 4763 / 10000
Epoch 28: 4868 / 10000
Epoch 29: 4977 / 10000
>>> net = network.Network([784, 100, 10])
>>> net.SGD(training_data, 30, 10, 100, test_data=test_data)
Epoch 0: 894 / 10000
Epoch 1: 894 / 10000
Epoch 2: 894 / 10000
Epoch 3: 894 / 10000
Epoch 4: 894 / 10000
Epoch 5: 894 / 10000
Epoch 6: 894 / 10000
Epoch 7: 894 / 10000
Epoch 8: 894 / 10000
Epoch 9: 893 / 10000
Epoch 10: 893 / 10000
Epoch 11: 893 / 10000
Epoch 12: 893 / 10000
Epoch 13: 893 / 10000
Epoch 14: 893 / 10000
Epoch 15: 893 / 10000
Epoch 16: 893 / 10000
Epoch 17: 893 / 10000
Epoch 18: 893 / 10000
Epoch 19: 893 / 10000
Epoch 20: 892 / 10000
Epoch 21: 892 / 10000
Epoch 22: 892 / 10000
Epoch 23: 1262 / 10000
Epoch 24: 1262 / 10000
Epoch 25: 1262 / 10000
Epoch 26: 1262 / 10000
Epoch 27: 1262 / 10000
Epoch 28: 1262 / 10000
Epoch 29: 1262 / 10000
>>> 


  1. 2017年04月24日 21:32 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

Vivado 2017.1 の新機能3(AXI Verification IPのサンプル・デザイン1)

Vivado 2017.1 の新機能2(AXI Verification IPの概要)”の続き。

前回は、AXI Verification IP の概要を調べた。今回は、AXI Verification IP のサンプル・デザインをやってみよう。

なお、AXI Verification IPの記事を tethys_seesaa さんにお願いして書いて頂いた。検証のプロだけに、要点がまとまった良い記事だと思うので、ご紹介したい。”XilinxのAXI Verification IPを試す。

AXI Verification IPのサンプル・デザインには、10 個のシミュレーション・セット・テストベンチがあって、シミュレーション用のファイルが 3 個ある。 generic_tb.sv と マスタ・スティミュラス、スレーブ・スティミュラスだ。
generic_tb.sv はマスタ側をエラーチェックする機能がある。マスタ・スティミュラスは、AXI Master VIPとAXI pass-through VIP で生成されて、スレーブ・スティミュラスはAXI Slave VIPでマスタへの応答として生成されるようだ。

それでは、AXI Verification IPのサンプル・デザインを始めよう。
まずは、”Digilent社のボード・ファイルのインストール”を参考にして、Vivado 2017.1 に Digilent 社のボード・ファイルをインストールする。
Vivado 2017.1 で ZYBO 用のプロジェクトを作成する。
次に、AXI_VIP_test という名前のブロック・デザインを生成した。
Vivado_2017-1_30_170423.png

Diagram ウインドウで + ボタンをクリックして、IP を追加する。
Vivado_2017-1_31_170423.png

Search のテキスト・ボックスに”AXI Veri”と入力して、AXI Verification IPを選択する。
Vivado_2017-1_32_170423.png

axi_vip_0 がインスタンスされた。
セーブ・ボタンでセーブする。(セーブしないと、Open IP Examples Design... が出てこない)
Vivado_2017-1_33_170423.png

左のDegign ウインドウの axi_vip_0 を右クリックし、右クリックメニューからOpen IP Examples Design... を選択する。
Vivado_2017-1_34_170423.png

Open IP Example Design ダイアログが表示された。
デフォルトで、OK ボタンをクリックする。
Vivado_2017-1_35_170423.png

axi_vip_0_ex プロジェクトが生成されて、開いた。
Vivado_2017-1_36_170423.png
  1. 2017年04月24日 06:13 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:2

Vivado 2017.1 の新機能2(AXI Verification IPの概要)

Vivado 2017.1 の新機能1(概要)”の続き。

前回は、Vivado 2017.1 の新機能の概要を書いた。今回は、AXI Verification IPの概要を調査しよう。

AXI Verification IP v1.0 LogiCORE IP Product Guide Vivado Design Suite PG267 April 5, 2017”を参照させて頂くことにする。
”AR# 68234 AXI Verification IP - AXI VIP のリリース ノートおよび既知の問題”も参考にしよう。

AXI Verification IP v1.0 LogiCORE IP Product Guide Vivado Design Suite PG267 April 5, 2017”によると、AXI Verification IP には、3つのモードがある。

AXI Master VIP
AXI Slave VIP
AXI Pass-Through VIP


つまり、AXI Master になるか、AXI Slave になるか、AXI Master とAXI Slave の間に入るか?の違いのようだ。
AXI VIP はSystemVerilog で書かれているそうだ。Vivado のSystemVerilog サポートも進んできて、だいぶ良くなったのだろう?
UVMと同様の名前付けと構造を使用しているようだ。
AXI VIP は2つの部分で構成されている。

other traditional IP (modules in the static/physical world)
dynamic world


つまり、HDLで書かれた従来のIP があってダイナミック?なソフトウェアがあって、AXI VIP は、仮想インターフェース(virtual interface)でその2つの橋渡しをするようだ。

AXI Master VIP
AXI Master VIPは3つのパートからできている。

• User environment
• Master agent
• AXI master VIP


ソフトウェアの世界には、マスター・エージェントがあって、それには4つのクラス・メンバがあるそうだ。

• Master write driver
• Master read driver
• Monitor
• Virtual interface


AXI Verification IP v1.0 LogiCORE IP Product Guide Vivado Design Suite PG267 April 5, 2017”の29ページの”Figure 4-4: AXI VIP Master Test Bench”を引用する。
Vivado_2017-1_27_170423.png
この図を見ると一目瞭然だ。

ユーザー環境(User Environment)がWrite トランザクションを定義して、マスタ・エージェントのマスタ・ライト・ドライバが create_transaction を行う。
ユーザー環境はデータ入力かランダム入力?トランザクションを決めて、データを出力して、マスタ・エージェントに送る。
マスタ・エージェントでは、send でAXI VIP 経由で検証対象のAXI System に送られるようだ。
AXI Verification IP v1.0 LogiCORE IP Product Guide Vivado Design Suite PG267 April 5, 2017”の30ページの”Figure 4-5: Write Transaction Flow”を引用する。
Vivado_2017-1_28_170423.png

AXI Slave VIP
AXI slave VIP は3つのパートからできている。

• User environment
• Slave agent without a memory model
• AXI slave VIP


やはり、AXI Master VIPと同様にマスタ・エージェントの代わりにスレーブ・エージェントがあって、メモリモデル無しのスレーブ・エージェントには4つのクラス・メンバがある。

• Slave write driver
• Slave read driver
• Monitor
• Virtual interface


”Figure 4-6: AXI VIP Slave Test Bench ”は、構造的には、”Figure 4-5: Write Transaction Flow”と同じ。

Writeする場合は、ユーザー環境は、トランザクション・タイプの変数を宣言してから、スレーブWrite ドライバが get_wr_reactive をコールしてWrite トランザクションが来るまで待っている。
Write トランザクションが来たら、ユーザー環境が用意されたデータでもランダムデータでも応答を用意する。それをスレーブWrite ドライバが仮想インターフェース越しに、AXI VIP に送る。
AXI VIP は応答をAXI System に物理的な信号線で伝える。

Simple SRAM Memory Model
AXI Slave VIP はSystemVerilog の配列を使ったシンプルなメモリ・モデルがある。
Write トランザクションでは、メモリのデータをWrite して、Read トランザクションでは、メモリからデータをRead することができる。
この機能は、ランタイム・スレーブ・モードで、AXI Slave VIPとAXI Pass-through VIP に実装されているそうだ。
メモリ・モデルには、backdoor_memory_write と backdoor_memory_read があって、それぞれ Write または Read するバックドアAPI とのこと。
やはり、メモリ・モデルが無いとAXI Slave を処理するのは厳しい気がするので、これがあって良かった。
AXI Verification IP v1.0 LogiCORE IP Product Guide Vivado Design Suite PG267 April 5, 2017”の32ページのFigure 4-8: Memory Model”を引用する。
Vivado_2017-1_29_170423.png
なお、readmemh はサポートされていないそうだ。あと、reset してもメモリ内容には影響が無いそうだ。

AXI Slave Simple Memory VIP
AXI Slave Simple Memory VIP はAXI Slave VIP にメモリ・モデルが入っているもので、こっちのほうが使いやすいと思う。
私だったら、これを使用すると思う。
  1. 2017年04月23日 07:00 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

Vivado 2016.4 から Vivado 2017.1 へアップグレード

Vivado 2016.2 とVivado 2016.3 の間にはアップグレードの壁がある。
Vivado 2016.4 から Vivado 2017.1 へは簡単にアップグレードできるかどうかを確かめてみた。それにVivado 2017.1 ではルック&フィールが違っているので、確かめてみた。

Vivado 2016.4 からVivado 2017.1 にアップグレードするプロジェクトは”Vivado HLS で生成した AXI4 Master DMA IP を使用したカメラ画像表示システム”を使用する。

それでは、Vivado 2017.1 を立ち上がるところから行ってみよう。立ち上げたら、Open Project > をクリックする。
Vivado_2017-1_01_170421.png

予め cam_disp_axis_171 フォルダにプロジェクトをコピーしておいたので、そのフォルダの cam_disp_axis.xpr を選択してOKボタンをクリックした。
Vivado_2017-1_2_170421.png

Older Project Version ダイアログが表示された。Automatically upgrade to the current version のラジオボタンが選択されていることを確認して、OK ボタンをクリックする。
Vivado_2017-1_3_170421.png

Critical Warning が表示されている。Project Upgraded ダイアログも表示された。Report IP Status ボタンをクリックする。
Vivado_2017-1_4_170421.png

IP Status の Upgrade Selected ボタンをクリックする。
Vivado_2017-1_5_170421.png

Upgrade IP ダイアログが表示された。OK ボタンをクリックする。
Vivado_2017-1_6_170421.png

Upgrade IP ダイアログが表示された。IP Upgrade Completed だった。OK ボタンをクリックする。
Vivado_2017-1_7_170421.png

Genrerate Output Products ダイアログが表示された。Generate ボタンをクリックする。
Vivado_2017-1_8_170421.png

Critical Warning を示す。
board value is unset. ということだった。digilent のIP なので、とりあえず放置する。
Vivado_2017-1_9_170421.png

アップグレードが終了した。
Vivado_2017-1_10_170421.png

ブロックデザインを示す。
Vivado_2017-1_26_170422.png

Vivado 2017.1 では、ブロックデザインを表示すると、標準でIP Catalog が表示されているようだ。
Vivado_2017-1_11_170421.png

Address Editor を示す。
Vivado_2017-1_12_170421.png

Flow Navigator のGenerate Bitstream をクリックしてビットストリームの生成を行った。なお、上のアイコンにGenerate Bitstream があるので、これをクリックしても良いと思う。

Synthesis is Out-of-date ダイアログが表示された。Yes ボタンをクリックする。
Vivado_2017-1_13_170421.png

Launch Runs ダイアログが表示された。OK ボタンをクリックする。
Vivado_2017-1_14_170421.png

ビットストリームの生成が終了した。Bitstream Generation Completed ダイアログが表示された。Cancel ボタンをクリックする。
Vivado_2017-1_15_170421.png

Project Summary ボタンをクリックすると、Project Summary が表示される。
Vivado_2017-1_16_170421.png

左にVivado 2017.1 のProject Summary を示す。右にVivado 2016.4 のProject Summary を示す。Vivado 2017.1 のほうが少しリソース使用量が増えている。
Vivado_2017-1_17_170421.pngVivado_2017-1_18_170421.png

File メニューから Export -> Export Hardware... を選択した。
Vivado_2017-1_19_170421.png

Export Hardware ダイアログが表示された。Include bitstream にチェックを入れて、OK ボタンをクリックする。
Vivado_2017-1_20_170421.png

Vivado 2016.4 からのアップグレードなので、Module Already Exported ダイアログが表示された。Yes ボタンをクリックする。
Vivado_2017-1_21_170421.png

File メニューから Launch SDK を選択した。Launch SDK ダイアログが表示された。OK ボタンをクリックする。
Vivado_2017-1_22_170421.png

SDK が立ち上がり、Older Workspace Versin ダイアログが表示された。OK ボタンをクリックする。
Vivado_2017-1_23_170421.png

cam_disp_axis_wrapper_hw_platform_1 が新規作成された。
Vivado_2017-1_24_170421.png

cam_disp_axis_wrapper_hw_platform_1 プロジェクト以外のプロジェクトをすべて消去した。
cam_disp_hls プロジェクトを新規作成し、ソースコードをドラック&ドロップするとビルドが行われた。
ZYBO を接続して、Program FPGA を行い、cam_disp_hls.elf を起動すると、正常に画像が表示された。
Vivado_2017-1_25_170421.png

Vivado 2016.4 から Vivado 2017.1 へのアップグレードは問題ないようだ。
  1. 2017年04月22日 07:52 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

Vivado 2017.1 の新機能1(概要)

昨日、Vivado 2017.1 が出た。早速、インストールしてみたのだが、GUI が新しくなっていた。Windows と Linux のルック・アンド・フィールを統一してあるそうだ。
Vivado_2017-1_01_170421.png

Vivado_2017-1_02_170421.png

Vivado Design Suite ユーザー ガイド リリース ノート 、 インス トール およびライセンス UG973 (v2017.1) 2017 年 4 月 5 日”を見るとたくさんの機能が新しくなったようだ。
気になる機能を、”Vivado Design Suite ユーザー ガイド リリース ノート 、 インス トール およびライセンス UG973 (v2017.1) 2017 年 4 月 5 日”から引用する。

・Vivado IP インテグーター
 ・ AXI 設計アシ ス タ ン ス:
  ° AXI Interconnect (古い) または AXI SmartConnect (新しい) か ら選択可能。
  ° 設計アシ ス タ ン スによ る自動化を AXI4-Stream インターフェイス、 CLK、 リ セ ッ ト に拡張。


IPI での自動配線で、AXI Interconnect と AXI SmartConnect を選択可能なのか?やってみたい。
コメントをIPI 上に書けて、HDL ソースにも含められるそうだ。今までもコメントは書けたので、HDL に含められるのが新しい?

シ ミ ュレーション フ ローおよび検証 IP では、検証IP として、AXI Verification IP と Zynq-7000 VIP が導入されたそうだ。
AXI Verification IPは検証だけで、BFMの機能は無いのだろうか? Zynq-7000 VIPも興味深い。
AXI Verification IP に関しては、”サンプル デザインおよびテスト ベンチを IP インテグレーターで提供”とのことなので、すぐにやってみたい。
Vivado Design Suite ユーザー ガイド リリース ノート 、 インス トール およびライセンス UG973 (v2017.1) 2017 年 4 月 5 日”の 8 ページから引用する。

• 検証 IP
 ° AXI Verification IP を導入
  - SystemVerilog ベース、 ラ イセンス不要
  - AXI3、 AXI4、 および AXI4-Lite をサポー ト 。
  - すべてのプ ロ ト コル データ幅およびア ド レ ス幅、 転送タ イプ、 応答をサポー ト
  - AXI プロ ト コル チェ ッ カーを完全にサポー ト
  - 統合 ARM ラ イセンスのプロ ト コル アサーシ ョ ン
  - パス スルー モー ド で合成を イネーブル (ワ イヤに合成)
  - シ ミ ュレーシ ョ ン メ ッ セージを設定可能
  - サンプル デザイ ンおよびテス ト ベンチを IP イ ンテグ レーターで提供
 ° Zynq-7000 VIP を導入 (上記の AXI VIP に基づ く )
  - ラ イセン ス不要、 SystemVerilog ベース
  - PS Configuration ウ ィ ザー ド の出力フ ァ イル と し て提供
  - 既存の Zynq-7000 BFM 用 API と下位互換性あ り


もしかして、AXI Verification IP はライセンス不要と書いてないので、ライセンスが必要なのか?その場合は試してみることができないな。。。 失礼しました。ライセンス不要と書いてありました。

Vivado デバックで、IPI でのデバックでランタイムのAXIトランザクションの表示をサポートするそうだ。波形のトランザクション行を表示するということで、Vivado アナライザでトランザクション行を表示してくれるのだろうか?楽しみだ。
Vivado Design Suite ユーザー ガイド リリース ノート 、 インス トール およびライセンス UG973 (v2017.1) 2017 年 4 月 5 日”の 10 ページから引用する。

Vivado デバッグ
• IP イ ンテグレーターでのシステム デバ ッ グで ラ ン タ イ ムの AXI ト ラ ンザ ク シ ョ ンの表示をサポー ト 。
 ° 波形に ト ラ ンザ ク シ ョ ン行を表示。
 ° IP イ ンテグ レーターか ら ビ ッ ト ス ト リ ーム生成のフ ローを改善。
  - IP イ ンテグ レーターの設計アシ ス タ ン ス を向上。
  - 合成後に [Debug] ウィンドウで System ILA を表示。


皆さん。。。

System Edition および Design Edition でパーシャル リコンフィ ギュ レーシ ョン (PR) ラ イセン ス を追加コ ス ト な しで提供。

だそうです。やった~。。。これで私もパーシャル リコンフィ ギュ レーシ ョン 試せる。Xilinxさん太っ腹。。。
残念ながら、WebPACK では、ライセンス料が値下げされていはいるが、PR のライセンス料が発生するようだ。

IP としてHDMI のIPが追加されて、MIPI IP の 7 シリーズのサポートが追加されたそうです。

その他、数え切れない程の追加があるようです。でも、こんなにアップデートが多いとバグが心配ですね。気を付けて使ったほうがよさそうです。。。

Vivado HLS のアップデートですが、Vivado System Edition 製品としてアップデートが書いてあるんですが、これは前に System Edition にしかVivado HLS が入っていなかった頃の名残なんでしょうか? System Edition だけVivado HLS を更新して、他のエディションのVivado HLS を更新しないということはあり得ないと思うんですが。。。
math.h ライブラリがアップデートされて、DATAFLOWプラグマで範囲が可変のループをサポートしたそうです。
これに期待しているのですが、C/RTL協調シミュレーションで、ユーザーアシスタンス機能をイネーブルにしたそうです。どのように使いやすくなっているか?楽しみです。

  1. 2017年04月21日 05:05 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を使用してカメラの画像を表示

「”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる9(入れ替え2)」では、FASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路を使用して、カメラ画像を表示することができなかった。これは、PS から出力される fclk の出力周波数が設定されていないのが原因ではないか?というご指摘を ikwzm さんから頂いた。
そこで、「ikwzm さんの構築したPYNQ ボード用DebianでのPS出力クロックfclkの設定方法」で、デバイスツリー・オーバーレイを使用した。fclk の設定のやり方を学習したので、実際にFASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路に適用して試してみよう。

まずは、「”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる7(shellスクリプト)」で作成した devtov スクリプトを変更した。
PYNQ_Linux_ikwzm_107_170419.png

#!/bin/sh
# device tree overlay command (devtov)
# Be sure to run it as superuser
# $1 is device tree name

mkdir /config/device-tree/overlays/$1
cp $1.dtbo /config/device-tree/overlays/$1/dtbo
echo 1 > /config/device-tree/overlays/$1/status

sleep 0.1
chmod 666 /dev/uio*
ls -l /dev/uio*

if test -e /dev/udmabuf*; then
    chmod 666 /dev/udmabuf*
    ls -l /dev/udmabuf*
fi

if test -e /dev/fclk0; then
    chmod 666 /dev/fclk*
    ls -l /dev/fclk*
fi


次に、「ikwzm さんの構築したPYNQ ボード用DebianでのPS出力クロックfclkの設定方法」を参考に fclk のエントリを追加したデバイスツリー・オーバーレイの pynq_fastx_fclk.dts を作成した。
PYNQ_Linux_ikwzm_108_170419.png

/dts-v1/;
/ {
    fragment@0 {
        target-path = "/amba";
        __overlay__ {
            #address-cells = <0x1>;
            #size-cells = <0x1>;
            axi_iic_0@41600000 {
                compatible = "generic-uio";
                reg = <0x41600000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            axi_vdma_0@43000000 {
                compatible = "generic-uio";
                reg = <0x43000000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            axis_switch_0@43C10000 {
                compatible = "generic-uio";
                reg = <0x43C10000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            axis_switch_1@43C20000 {
                compatible = "generic-uio";
                reg = <0x43C20000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            bitmap_disp_cntrler_axi_master_0@43C00000 {
                compatible = "generic-uio";
                reg = <0x43C00000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            fastx_corner_det_0@43C30000 {
                compatible = "generic-uio";
                reg = <0x43C30000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            lap_filter_axis_0@43C50000 {
                compatible = "generic-uio";
                reg = <0x43C50000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            mt9d111_inf_axis_0@43C40000 {
                compatible = "generic-uio";
                reg = <0x43C40000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            unsharp_mask_axis_0@43C60000 {
                compatible = "generic-uio";
                reg = <0x43C60000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            udmabuf4 {
                compatible = "ikwzm,udmabuf-0.10.a";
                minor-number = <4>;
                size = <0x00600000>;
            };
            fclk0 {
                compatible  = "ikwzm,fclkcfg-0.10.a";
                clocks      = <1 15>;
            };
            fclk1 {
                compatible  = "ikwzm,fclkcfg-0.10.a";
                clocks      = <1 16>;
            };
            fclk2 {
                compatible  = "ikwzm,fclkcfg-0.10.a";
                clocks      = <1 17>;
            };
        };
    };
};


dtc -I dts -O dtb -o pynq_fastx_fclk.dtbo pynq_fastx_fclk.dts
で dts をコンパイルして、pynq_fastx_fclk.dtbo を生成した。
PYNQ_Linux_ikwzm_109_170419.png

スーパーユーザーになって、
./devtov pynq_fastx_fclk
で、デバイスツリー・オーバーレイをロードした。uio, udmabuf, fclk のデバイスツリー・オーバーレイがロードされた。
PYNQ_Linux_ikwzm_110_170419.png

/sys/class/fclkcfg/fclk0 ディレクトリに移動して fclk0 のステータスを見た。
100 MHz に設定されていた。 enable = 1 なので、すでに出力されている。
cd /sys/class/fclkcfg/fclk0
cat rate
cat round_rate
cat enable

PYNQ_Linux_ikwzm_111_170419.png

/sys/class/fclkcfg/fclk1 ディレクトリに移動して fclk1 のステータスを見た。
rate を 25 MHz に設定した。
cd ../fclk1
cat rate
cat round_rate
cat enable
su
echo 25000000 > round_rate
cat round_rate
echo 25000000 > rate
cat rate
exit

PYNQ_Linux_ikwzm_112_170419.png

/sys/class/fclkcfg/fclk2 ディレクトリに移動して fclk2 のステータスを見た。
rate を 72 MHz に設定したが、round_rate の結果をみると、71.428572 MHz だった。
cd ../fclk2
cat rate
cat round_rate
cat enable
su
echo 72000000 > round_rate
cat round_rate
echo 72000000 > rate
cat rate
exit

PYNQ_Linux_ikwzm_113_170419.png

~/device_tree_overlay/ ディレクトリに戻って、pynq_fastx_wrapper.bit をPYNQ ボードにコンフィギュレーションした。
cd ~/device_tree_overlay/
ls
su
./fpgamag pynq_fastx_wrapper.bit
exit


”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる9(入れ替え2)」の cam_disp.c の表示が出ないように書き換えて、コンパイルを行った。
cam_disp を起動した。
gcc -o cam_disp cam_disp.c
./cam_disp

PYNQ_Linux_ikwzm_114_170419.png

すると、カメラ画像を表示することができました。(部屋が散らかっていて、お見苦しい点をお詫びいたします)
PYNQ_Linux_ikwzm_115_170419.jpg

最後に、修正した cam_disp.c を貼っておきます。

// cam_disp.c
// 2017/04/08 by marsee
//

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

#define NUMBER_OF_WRITE_FRAMES  3 // Note: If not at least 3 or more, the image is not displayed in succession.

#define HORIZONTAL_PIXELS   800
#define VERTICAL_LINES      600
#define PIXEL_NUM_OF_BYTES  4
#define ALL_DISP_ADDRESS    (HORIZONTAL_PIXELS*VERTICAL_LINES*PIXEL_NUM_OF_BYTES)

#define FASTX_THRESHOLD     20

void cam_i2c_init(volatile unsigned *mt9d111_i2c_axi_lites) {
    mt9d111_i2c_axi_lites[64] = 0x2// reset tx fifo ,address is 0x100, i2c_control_reg
    mt9d111_i2c_axi_lites[64] = 0x1// enable i2c
}

void cam_i2x_write_sync(void) {
    // unsigned c;

    // c = *cam_i2c_rx_fifo;
    // while ((c & 0x84) != 0x80)
        // c = *cam_i2c_rx_fifo; // No Bus Busy and TX_FIFO_Empty = 1
    usleep(1000);
}

void cam_i2c_write(volatile unsigned *mt9d111_i2c_axi_lites, unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    mt9d111_i2c_axi_lites[66] = 0x100 | (device_addr & 0xfe);   // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    mt9d111_i2c_axi_lites[66] = write_addr;
    mt9d111_i2c_axi_lites[66] = (write_data >> 8)|0xff;         // first data
    mt9d111_i2c_axi_lites[66] = 0x200 | (write_data & 0xff);        // second data
    cam_i2x_write_sync();
}

int main()
{
    int fd0, fd1, fd2, fd3, fd4, fd5, fd6, fd7, fd8;
    int fd_udmabuf, fd_paddr;
    volatile unsigned int *axi_iic_0, *axi_vdma_0, *axis_switch_0, *axis_switch_1;
    volatile unsigned int *bitmap_disp_cntrler_axim_0, *fastx_corner_det_0;
    volatile unsigned int *mt9d111_inf_axis_0;
    volatile unsigned int *frame_buffer;
    unsigned char  attr[1024];
    unsigned long  phys_addr;

    // axi_iic_0 (uio0)
    fd0 = open("/dev/uio0", O_RDWR); // axi_iic_0
    if (fd0 < 1){
        fprintf(stderr, "/dev/uio0 (axi_iic_0) open errorn");
        exit(-1);
    }
    axi_iic_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd0, 0);
    if (axi_iic_0 == MAP_FAILED){
        fprintf(stderr, "axi_iic_0 mmap errorn");
        exit(-1);
    }
    
    // axi_vdma_0 (uio1)
    fd1 = open("/dev/uio1", O_RDWR); // axi_vdma_0
    if (fd1 < 1){
        fprintf(stderr, "/dev/uio1 (axi_vdma_0) open errorn");
        exit(-1);
    }
    axi_vdma_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, 0);
    if (axi_vdma_0 == MAP_FAILED){
        fprintf(stderr, "axi_vdma_0 mmap errorn");
        exit(-1);
    }

    // axis_switch_0 (uio2)
    fd2 = open("/dev/uio2", O_RDWR); // axis_switch_0
    if (fd2 < 1){
        fprintf(stderr, "/dev/uio2 (axis_switch_0) open errorn");
        exit(-1);
    }
    axis_switch_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
    if (axis_switch_0 == MAP_FAILED){
        fprintf(stderr, "axis_switch_0 mmap errorn");
        exit(-1);
    }
    
    // axis_switch_1 (uio3)
    fd3 = open("/dev/uio3", O_RDWR); // axis_switch_1
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 (axis_switch_1) open errorn");
        exit(-1);
    }
    axis_switch_1 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (axis_switch_1 == MAP_FAILED){
        fprintf(stderr, "axis_switch_1 mmap errorn");
        exit(-1);
    }
    
    // bitmap_disp_cntrler_axim_0 (uio4)
    fd4 = open("/dev/uio4", O_RDWR); // bitmap_disp_cntrler_axim_0
    if (fd4 < 1){
        fprintf(stderr, "/dev/uio4 (bitmap_disp_cntrler_axim_0) open errorn");
        exit(-1);
    }
    bitmap_disp_cntrler_axim_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd4, 0);
    if (bitmap_disp_cntrler_axim_0 == MAP_FAILED){
        fprintf(stderr, "bitmap_disp_cntrler_axim_0 mmap errorn");
        exit(-1);
    }
    
    // mt9d111_inf_axis_0 (uio7)
    fd7 = open("/dev/uio7", O_RDWR); // mt9d111_inf_axis_0
    if (fd7 < 1){
        fprintf(stderr, "/dev/uio7 (mt9d111_inf_axis_0) open errorn");
        exit(-1);
    }
    mt9d111_inf_axis_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd7, 0);
    if (mt9d111_inf_axis_0 == MAP_FAILED){
        fprintf(stderr, "mt9d111_inf_axis_0 mmap errorn");
        exit(-1);
    }
    
    // udmabuf4
    fd_udmabuf = open("/dev/udmabuf4", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fd_udmabuf == -1){
        fprintf(stderr, "/dev/udmabuf4 open errorn");
        exit(-1);
    }
    frame_buffer = (volatile unsigned int *)mmap(NULL, (ALL_DISP_ADDRESS*3), PROT_READ|PROT_WRITE, MAP_SHARED, fd_udmabuf, 0);
    if (frame_buffer == MAP_FAILED){
        fprintf(stderr, "frame_buffer mmap errorn");
        exit(-1);
    }

    // phys_addr of udmabuf4
    fd_paddr = open("/sys/devices/soc0/amba/amba:udmabuf4/udmabuf/udmabuf4/phys_addr", O_RDONLY);
    if (fd_paddr == -1){
        fprintf(stderr, "/sys/devices/soc0/amba/amba:udmabuf4/udmabuf/udmabuf4/phys_addr open errorn");
        exit(-1);
    }
    read(fd_paddr, attr, 1024);
    sscanf(attr, "%lx", &phys_addr);  
    close(fd_paddr);
    printf("phys_addr = %x\n", (unsigned int)phys_addr);
    
    // axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_1[16] = 0x0// 0x40 = 0
    axis_switch_1[17] = 0x80000000// 0x44 = 0x80000000, disable
    axis_switch_1[18] = 0x80000000// 0x48 = 0x80000000, disable
    axis_switch_1[19] = 0x80000000// 0x4C = 0x80000000, disable
    axis_switch_1[0] = 0x2// Comit registers
    
    // axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_0[16] = 0x0// 0x40 = 0;
    axis_switch_0[0] = 0x2// Comit registers

    // AXI VDMA Initialization sequence (axi_vdma_0)
    axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    axi_vdma_0[18] = NUMBER_OF_WRITE_FRAMES; // S2MM_FRMSTORE (0x48) register
    axi_vdma_0[12] = 0x00010002// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1)
    axi_vdma_0[41] = HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Horizontal Size Register(S2MM_HSIZE)0xc80 = 3200dec = 800 x 4
    axi_vdma_0[42] = HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Frame Delay and Stride Register(S2MM_FRMDLY_STRIDE)0xc80 = 3200dec = 800 x 4
    axi_vdma_0[43] = (unsigned)phys_addr; // S2MM Start Address (1 to 16) Start Address 1
    axi_vdma_0[44] = (unsigned)phys_addr; // S2MM Start Address (1 to 16) Start Address 2
    axi_vdma_0[45] = (unsigned)phys_addr; // S2MM Start Address (1 to 16) Start Address 3
    axi_vdma_0[12] = 0x00010003// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1, Run/stop = 1)
    while((axi_vdma_0[13] & 0x1) == 0x1) ; // Halt? (S2MM_VDMASR 0x34)
    axi_vdma_0[40] = VERTICAL_LINES; // S2MM Vertical Size (S2MM_VSIZE  Offset 0xA0) 0x258 = 600dec

    // CMOS Camera initialize, MT9D111
    cam_i2c_init(axi_iic_0);
    
    cam_i2c_write(axi_iic_0, 0xba, 0xf00x1);        // Changed regster map to IFP page 1
    cam_i2c_write(axi_iic_0, 0xba, 0x970x20);    // RGB Mode, RGB565

    mt9d111_inf_axis_0[1] = 0;
    mt9d111_inf_axis_0[0] = (unsigned int)phys_addr;

    bitmap_disp_cntrler_axim_0[0] = (unsigned int)phys_addr;
        
    munmap((void *)axi_iic_0, 0x10000);
    munmap((void *)axi_vdma_0, 0x10000);
    munmap((void *)axis_switch_0, 0x10000);
    munmap((void *)axis_switch_1, 0x10000);
    munmap((void *)bitmap_disp_cntrler_axim_0, 0x10000);
    munmap((void *)mt9d111_inf_axis_0, 0x10000);
    munmap((void *)frame_buffer, (ALL_DISP_ADDRESS*3));
    
    close(fd0);
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    close(fd7);
    close(fd_udmabuf);
}

  1. 2017年04月19日 04:55 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で DMA Read IP を作る3(オフセット・アドレス指定版)

Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)”の続き。

ついでに作ったので書いておこう。
Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)”は絶対アドレスを3つ指定して、DMA Read を行うが、DMA Read IP のオフセット・アドレス指定版はベース・アドレスを指定し、ベースアドレスに対してのオフセット・アドレスを3つ指定してDMA Readするアドレスを決定する。ただし、C のソースコードはm_axi 指示子の depth と offset オプションのみが違っている。
depth は本来の値に設定する。そして、offset=slave とする必要がある。offset=slave とすると、ベース・アドレスがAXI4-Lite のレジスタとして実装される。
なおターゲットとしてはZYBO ボードとする。

Vivado HLS 2016.4 の DMA_Read_offset プロジェクトを示す。
DMA_Read_offset_1_170418.png

C シミュレーションを行った。成功した。
DMA_Read_offset_2_170418.png

C コードの合成を行った。結果を示す。
DMA_Read_offset_3_170418.png
ベース・アドレスのレジスタが増えているだけに、オフセット・アドレス版のほうがリソース使用量が多い。
アドレス・マップを示す。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of in_r
//        bit 31~0 - in_r[31:0] (Read/Write)
// 0x1c : reserved
// 0x20 : Data signal of fb0_offset_addr
//        bit 31~0 - fb0_offset_addr[31:0] (Read/Write)
// 0x24 : reserved
// 0x28 : Data signal of fb1_offset_addr
//        bit 31~0 - fb1_offset_addr[31:0] (Read/Write)
// 0x2c : reserved
// 0x30 : Data signal of fb2_offset_addr
//        bit 31~0 - fb2_offset_addr[31:0] (Read/Write)
// 0x34 : reserved
// 0x38 : Data signal of mode_V
//        bit 0  - mode_V[0] (Read/Write)
//        others - reserved
// 0x3c : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


DMAのベース・アドレスのレジスタ”0x18 : Data signal of in_r”があるのが分かる。

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

C/RTL協調シミュレーション波形を示す。3つのDMA Readが見えている。
DMA_Read_offset_5_170418.png
絶対アドレス版と違って、オフセット・アドレス版は dma_result0.bmp、dma_result1.bmp、dma_result2.bmp が正常に”A”になっている。

DMA_Read_offset_6_170418.png

Export RTL を行った。
DMA_Read_offset_7_170418.png

DMA_Read.h を示す。

// DMA_Read.h
// 2016/07/14 by marsee
// 2016/09/18 : BURST_LENGTH を追加
//

#ifndef __DMA_READ_H__
#define __DMA_READ_H__

//#define HORIZONTAL_PIXEL_WIDTH    800
//#define VERTICAL_PIXEL_WIDTH    600

#define HORIZONTAL_PIXEL_WIDTH    64
#define VERTICAL_PIXEL_WIDTH    48

#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define MAX_FRAME_NUMBER    3

#define DMA_WRITE_MODE    0
#define FREE_RUN_MODE    1

#define MEMCPY_LENGTH    (HORIZONTAL_PIXEL_WIDTH*4)

#endif


DMA_Read_offset.cpp を示す。

// DMA_Read_offset.cpp
// 2017/04/17 by marsee
//
// fb0_offset_addr, fb1_offset_addr, fb2_offset_addr には3つのフレームバッファのオフセット・アドレスを入れる
// mode = 0 : DMA Write IP の active_frame を見て、その1つ前のフレームをDMA Readするモード(DMA_WRITE_MODE)
// mode = 1 : フリーラン モード(FREE_RUN_MODE)
//
// カメラのフレームレートの方が小さい場合に、フリーランモードで3つフレームバッファが別々だと画像がぶれてしまう。
// その場合は、3つのフレームバッファ・アドレスを1つのアドレスを入れて、シングル・フレームバッファとする必要がある。

// 1フレーム分のDMAを行う
//

#include <stdio.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#include "DMA_Read.h"

int DMA_Read_offset(volatile int *in, hls::stream<ap_axis<32,1,1,1> >& outs,
        unsigned int fb0_offset_addr, unsigned int fb1_offset_addr,
        unsigned int fb2_offset_addr, ap_uint<2> & active_frame,
        ap_uint<1> mode){
#pragma HLS INTERFACE s_axilite port=mode
#pragma HLS INTERFACE ap_none register port=active_frame
#pragma HLS INTERFACE s_axilite port=fb0_offset_addr
#pragma HLS INTERFACE s_axilite port=fb1_offset_addr
#pragma HLS INTERFACE s_axilite port=fb2_offset_addr
#pragma HLS INTERFACE m_axi depth=9216 port=in offset=slave
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE s_axilite port=return

    ap_axis<32,1,1,1> pix;
    int dma_index;
    static int n = 0;

    if (mode == DMA_WRITE_MODE){
        n = (int)active_frame;
    }else{
        n++;
        if (n > 2)
            n = 0;
    }

    switch (n){ // 1つ前のフレームバッファを読みだす
        case 0 :
            dma_index = fb2_offset_addr/sizeof(int);
            break;
        case 1 :
            dma_index = fb0_offset_addr/sizeof(int);
            break;
        case 2 :
            dma_index = fb1_offset_addr/sizeof(int);
            break;
        default :
            dma_index = fb0_offset_addr/sizeof(int);
            break;
    }

    for (int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE II=1
            pix.data = in[dma_index+(y*HORIZONTAL_PIXEL_WIDTH)+x];

            if (y==0 && x==0)
                pix.user = 1;
            else
                pix.user = 0;

            if (x == (HORIZONTAL_PIXEL_WIDTH-1))
                pix.last = 1;
            else
                pix.last = 0;

            outs << pix;
        }
    }

    return 0;
}


DMA_Read_offset_tb.cpp を示す。

// DMA_Read_offset_tb.cpp
// 2016/07/15 by marsee
// 2017/03/16 : 3フレーム処理から1フレーム処理に変更
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include <ap_axi_sdata.h>

#include "DMA_Read.h"
#include "bmp_header.h"

int DMA_Read_offset(volatile int *in, hls::stream<ap_axis<32,1,1,1> >& outs,
        unsigned int fb0_offset_addr, unsigned int fb1_offset_addr,
        unsigned int fb2_offset_addr, ap_uint<2> & active_frame,
        ap_uint<1> mode);

int main()
{
    using namespace std;

    hls::stream<ap_axis<32,1,1,1> > outs;
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> vals;

    BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
    BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
    FILE *fbmpr, *fbmpw;
    int *rd_bmp, *hw_lapd;
    int blue, green, red;
    ap_uint<2> active_frame = 0;
    int *frame_buffer;

    if ((fbmpr = fopen("test.bmp""rb")) == NULL){ // test.bmp をオープン
    //if ((fbmpr = fopen("road_1.bmp", "rb")) == NULL){ // test.bmp をオープン
        fprintf(stderr, "Can't open test.bmp by binary read mode\n");
        exit(1);
    }
    // bmpヘッダの読み出し
    fread(&bmpfhr.bfType, sizeof(char), 2, fbmpr);
    fread(&bmpfhr.bfSize, sizeof(long), 1, fbmpr);
    fread(&bmpfhr.bfReserved1, sizeof(short), 1, fbmpr);
    fread(&bmpfhr.bfReserved2, sizeof(short), 1, fbmpr);
    fread(&bmpfhr.bfOffBits, sizeof(long), 1, fbmpr);
    fread(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpr);

    // ピクセルを入れるメモリをアロケートする
    if ((rd_bmp =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }

    int *buf;
    if ((buf =(int *)malloc(MAX_FRAME_NUMBER * sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate buf memory\n");
        exit(1);
    }

    // rd_bmp にBMPのピクセルを代入。その際に、行を逆転する必要がある
    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            blue = fgetc(fbmpr);
            green = fgetc(fbmpr);
            red = fgetc(fbmpr);
            rd_bmp[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] = (blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16);
        }
    }
    fclose(fbmpr);

    // frame buffer をアロケートする、3倍の領域を取ってそれを3つに分ける
    if ((frame_buffer =(int *)malloc(MAX_FRAME_NUMBER * sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate frame_buffer0 ~ 2\n");
        exit(1);
    }

    // 3 つのフレームバッファにそれぞれ'A' を入力する(1つに変更)
    memcpy(frame_buffer, rd_bmp, bmpihr.biHeight * bmpihr.biWidth * sizeof(int));

    memcpy((int *)((unsigned int)frame_buffer + bmpihr.biHeight * bmpihr.biWidth * sizeof(int)),
        rd_bmp, bmpihr.biHeight * bmpihr.biWidth * sizeof(int));

    memcpy((int *)((unsigned int)frame_buffer + 2 * bmpihr.biHeight * bmpihr.biWidth * sizeof(int)),
        rd_bmp, bmpihr.biHeight * bmpihr.biWidth * sizeof(int));

    active_frame = 1// fb0_offset_addr
    DMA_Read_offset((volatile int *)frame_buffer, outs, (unsigned int)0,
        (unsigned int)(bmpihr.biWidth * bmpihr.biHeight * sizeof(int)),
        (unsigned int)(2 * (bmpihr.biWidth * bmpihr.biHeight) * sizeof(int)),
        active_frame, DMA_WRITE_MODE);

    active_frame = 2// fb1_offset_addr
    DMA_Read_offset((volatile int *)frame_buffer, outs, (unsigned int)0,
        (unsigned int)(bmpihr.biWidth * bmpihr.biHeight * sizeof(int)),
        (unsigned int)(2 * (bmpihr.biWidth * bmpihr.biHeight) * sizeof(int)),
        active_frame, DMA_WRITE_MODE);

    active_frame = 0// fb2_offset_addr
    DMA_Read_offset((volatile int *)frame_buffer, outs, (unsigned int)0,
        (unsigned int)(bmpihr.biWidth * bmpihr.biHeight * sizeof(int)),
        (unsigned int)(2 * (bmpihr.biWidth * bmpihr.biHeight) * sizeof(int)),
        active_frame, DMA_WRITE_MODE);

    // outs ストリームのデータを buf に入力する
    for (int k=0; k<3; k++){
        for(int j=0; j < bmpihr.biHeight; j++){
            for(int i=0; i < bmpihr.biWidth; i++){
                outs >> vals;
                ap_int<32> val = vals.data;
                buf[(k*bmpihr.biWidth*bmpihr.biHeight)+(j*bmpihr.biWidth)+i] = (int)val;

                if (val != frame_buffer[(k*bmpihr.biWidth*bmpihr.biHeight)+(j*bmpihr.biWidth)+i]){
                    printf("ERROR HW and SW results mismatch k = %d, i = %d, j = %d, HW = %08x, SW = %08x\n", k, i, j, (int)val, (int)frame_buffer[(k*bmpihr.biWidth*bmpihr.biHeight)+(j*bmpihr.biWidth)+i]);
                    return(1);
                }
            }
        }
    }

    // DMAされたデータをBMPフィルに書き込む
    char output_file[] = "dma_result0.bmp";
    for (int i=0; i<MAX_FRAME_NUMBER; i++){
        switch (i){
            case 0:
                strcpy(output_file,"dma_result0.bmp");
                break;
            case 1:
                strcpy(output_file,"dma_result1.bmp");
                break;
            case 2:
                strcpy(output_file,"dma_result2.bmp");
                break;
        }
        if ((fbmpw=fopen(output_file, "wb")) == NULL){
            fprintf(stderr, "Can't open %s by binary write mode\n", output_file);
            exit(1);
        }
        // BMPファイルヘッダの書き込み
        fwrite(&bmpfhr.bfType, sizeof(char), 2, fbmpw);
        fwrite(&bmpfhr.bfSize, sizeof(long), 1, fbmpw);
        fwrite(&bmpfhr.bfReserved1, sizeof(short), 1, fbmpw);
        fwrite(&bmpfhr.bfReserved2, sizeof(short), 1, fbmpw);
        fwrite(&bmpfhr.bfOffBits, sizeof(long), 1, fbmpw);
        fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpw);

        // RGB データの書き込み、逆順にする
        int offset = i * bmpihr.biWidth * bmpihr.biHeight;
        for (int y=0; y<bmpihr.biHeight; y++){
            for (int x=0; x<bmpihr.biWidth; x++){
                blue = buf[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                green = (buf[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                red = (buf[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

                fputc(blue, fbmpw);
                fputc(green, fbmpw);
                fputc(red, fbmpw);
            }
        }
        fclose(fbmpw);
    }
    free(rd_bmp);
    free(frame_buffer);
    return 0;
}

  1. 2017年04月18日 05:39 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Thinkpad 13が届きました

2017/04/17 金曜日に新しいノートパソコンのThinkpad 13 が届きました。
「PYNQ祭り」延長戦 : FPGAディープラーニング実践懇親会”でがっつり実装するために購入しました。大赤字です。。。w

CPU は i5-7200、メモリ 16GB、SSD 512GB です。メモリは 16 GB 1枚なので、もう1枚実装すると 32 GB になります。かなりハイスペックですね。CPUが多少しょぼいですが。。。

古い世代には憧れのThinkpad ということで、キーボードやトラックパッドは良い感じです。
ThinkPad_13_1_170417.jpg

早速、Vivado 2016.4 と SDSoC 2016.4 をインストールしました。SDSoCのライセンスについてはひでみさんの本のAGPF WORKS に書いてある通りでした。満足です。
ThinkPad_13_2_170417.jpg

今まで使っていたマウスコンピューターのノートパソコンはVivado などをアンインストールして、奥さん用にします。
今回購入したThinkpad 13 はスペックが良いので、第2のVivado コンパイルマシンとしても活用したいと思います。
  1. 2017年04月17日 05:30 |
  2. パソコン関連
  3. | トラックバック:0
  4. | コメント:0

かすみがうらマラソン2017に出場しました

今日は、かすみがうらマラソン2017の 5 km に出場してきました。

午前6時45分頃家を出て、かすみがうらマラソンの臨時駐車場に車を入れました。たくさんの車がありました。
kasumigaura_marathon_1_170416.jpg

臨時駐車場からかすみがうらマラソンの出発地点の近くまでは、シャトルバスが出ています。
kasumigaura_marathon_2_170416.jpg

お世話になった、阿見アスリートクラブ、JCOMテントの近くの様子です。たくさんの人とたくさんの臨時トイレがあります。トイレの量も多く、また、小便器の仮設トイレもあったので、トイレの行列が長くなかったのは良かったです。
kasumigaura_marathon_3_170416.jpg

kasumigaura_marathon_4_170416.jpg

kasumigaura_marathon_5_170416.jpg

マッサージのテントなども近くにありました。無料でマッサージをやってもらえますよ。

私は、花粉症なので、5 km に出場したのですが、マスクを3枚持って走りました。2 km ごとに交換しようと思っていました。去年はそうしたんですが、今日の気温は26度、湿度は60% 以上だったので、とっても苦しくなって、途中でマスクを取って走りました。

結果は、5 km オフィシャルタイムで、23分32秒、プライベートタイムで 28分14秒でした。順位は 184 位/614人中、50歳代では 32 位/ 114 人中でした。今日は守谷マラソンよりも 40秒程度遅かったです。。。orz
kasumigaura_marathon_6_170416.jpg
  1. 2017年04月16日 21:30 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSのAXI4マスタ機能のオプションの違いによるテストベンチからの呼び出し方法

ちょっと訳があって、Vivado HLSのAXI4マスタ機能のオプションの違いによるテストベンチからの呼び出し方法を書いておく。

AXI4マスタ・インターフェースを生成するプラグマは

#pragma HLS INTERFACE m_axi port=<ポート名>

で、オプションにオフセット・アドレスを指定する offset がある。

この offset オプションには3つの設定がある。

off : ベース・アドレス無し。アドレスは 0x00000000
direct : ベース・アドレスは32ビット幅のポートの値となる。
slave : ベース・アドレスを示すためにAXI4-Liteインターフェースに32ビット幅のレジスタが追加される。この場合は、ブロックレベルのインターフェースをAXI4-Liteにする必要がある。
(混同を避けるため、こちらの用語はベース・アドレスにした)


なお、「Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2016.4) 2016 年 11 月 30 日」の114ページの「AXI4 インターフ ェイスのアドレス オフセットの制御」を参照している。

direct オプションの場合はポートでアドレスを与えるので、決まってしまうが、off と slave の場合のテストベンチでのアドレスの与え方の考え方を書いておこう。(私独自なのかもしれないが。。。)

例にとるのは、「Vivado HLS で DMA Write IP を作る2(絶対アドレス指定編)」と「Vivado HLS で DMA Write IP を作る(オフセット・アドレス指定編)」を例にとって説明しよう。

何を問題にしているか?を最初に書いておくと、ソフトウェアでのテストベンチのアドレスの与え方とハードウェアのIPとしたときのアプリケーションソフトでのアドレスの与え方の整合性なのだ。
DMA Write IP では、画像のフレームバッファを3つ用意して、3つアドレスに順番に順番にWrite していく。それは、画像を表示したときにジャギーを出さない標準的な方法だ。これを実現するために、1つの呼び出しで3回異なるアドレスにAXI4マスタ・インターフェースを使用して3回のDMA Write を行う。
その3つのアドレスをどう指定するか?なのだが、絶対アドレス3つを指定するのと、1つの絶対アドレスを指定して、そのオフセット・アドレスを3つ指定する方法が考えられる。たとえ、最初のオフセット・アドレス0だとしても、3つ指定することになる。
この2つの指定方法の差を説明すると、最初は、3つの独立したアドレスを指定するのだが、2番目は1つの独立したアドレスとその3つのオフセット・アドレスを指定するという違いがある。
最初の「絶対アドレスを3つ指定する方法」では、指定するアドレスは3つなのだが、「1つの絶対アドレスを指定して、そのオフセット・アドレスを3つ指定する方法」では、4つのアドレスを指定する必要がある。

Vivado HLS で DMA Write IP を作る(絶対アドレス指定編)」では、最初の「絶対アドレスを3つ指定する方法」で書いたDMA Write IP となっている。
関数の定義は、

int DMA_Write(hls::stream >& ins, volatile int *out,
  unsigned int frame_buffer0, unsigned int frame_buffer1,
  unsigned int frame_buffer2, volatile ap_uint<2> & active_frame)

なのだが、指示子は以下のようになっている。

#pragma HLS INTERFACE ap_vld port=active_frame
#pragma HLS INTERFACE s_axilite port=frame_buffer0
#pragma HLS INTERFACE s_axilite port=frame_buffer1
#pragma HLS INTERFACE s_axilite port=frame_buffer2
#pragma HLS INTERFACE m_axi depth=5000000 port=out offset=off
#pragma HLS INTERFACE axis port=ins
#pragma HLS INTERFACE s_axilite port=return

out ポートのAXI4マスタ・インターフェースのoffset オプションは off としている。つまり、このベース・アドレスは、0 で、frame_buffer0、frame_buffer1、frame_buffer2 を絶対アドレスとして設定して、それらのアドレスを指定してDMAすることになる。
offset オプションを off とする実装での、C コードの合成結果のレジスタマップを見てみよう。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of frame_buffer0
//        bit 31~0 - frame_buffer0[31:0] (Read/Write)
// 0x1c : reserved
// 0x20 : Data signal of frame_buffer1
//        bit 31~0 - frame_buffer1[31:0] (Read/Write)
// 0x24 : reserved
// 0x28 : Data signal of frame_buffer2
//        bit 31~0 - frame_buffer2[31:0] (Read/Write)
// 0x2c : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


上のレジスタのアドレスマップでは、ベース・アドレスを設定するレジスタ設定が存在しない。つまり、テストベンチでDMA_Write() を呼ぶときに適当な呼び方は、ベース・アドレスを 0 とする呼び方になる。つまり、offset オプションを off としたときのベース・アドレスが 0x00000000 になるというVivado HLS の規則に沿った関数コールの方法となる。そのDMA_Write() 関数のテストベンチでの呼び出し部分を下に示す。

DMA_Write(ins, (volatile int *)0, (unsigned int)frame_buffer,
 (unsigned int)frame_buffer+(bmpihr.biWidth * bmpihr.biHeight * sizeof(unsigned int)),
 (unsigned int)frame_buffer+(2 * (bmpihr.biWidth * bmpihr.biHeight) * sizeof(unsigned int)),
 active_frame);


ここでハードウェアの場合を考えてみると、ベース・アドレスは上のレジスタ・マップに無いように設定する必要が無く、frame_buffer0、frame_buffer1、frame_buffer2 の3つの絶対アドレスだけを指定すればよいことになり、関数コールのやり方とあっている。

下の関数コールのように読んでしまうと、ベース・アドレスと、3つの絶対アドレスを指定することになり、上のアドレスマップでは設定できないことになってしまう。

DMA_Write(ins, (volatile int *)frame_buffer, (unsigned int)0,
 (unsigned int)(bmpihr.biWidth * bmpihr.biHeight * sizeof(int)),
 (unsigned int)(2 * (bmpihr.biWidth * bmpihr.biHeight) * sizeof(int)),
 active_frame);


この関数コールを使う場合は、ベース・アドレスにframe_buffer の値を使っている。そしてのベース・アドレスからのオフセット・アドレスを3つ設定している。これは、ハードウェアにすると4つのレジスタを設定することになる。AXI4マスタ・インターフェースのoffset オプションは off にしていたのでは、4つのレジスタを設定することはないので、offset オプションは slave にする必要がある。こうすれば、ベース・アドレス+オフセット・アドレスを3つ設定することができる。
Vivado HLS で DMA Write IP を作る(オフセット・アドレス指定編)」の場合のアドレスマップを示す。この実装では、AXI4マスタ・インターフェースのoffset オプションは slave になっている。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of out_r
//        bit 31~0 - out_r[31:0] (Read/Write)
// 0x1c : reserved
// 0x20 : Data signal of fb0_offset_addr
//        bit 31~0 - fb0_offset_addr[31:0] (Read/Write)
// 0x24 : reserved
// 0x28 : Data signal of fb1_offset_addr
//        bit 31~0 - fb1_offset_addr[31:0] (Read/Write)
// 0x2c : reserved
// 0x30 : Data signal of fb2_offset_addr
//        bit 31~0 - fb2_offset_addr[31:0] (Read/Write)
// 0x34 : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


0x18に outr のレジスタが設置されている。
  1. 2017年04月15日 20:28 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で DMA Write IP を作る2(絶対アドレス指定編)

Vivado HLS で DMA Write IP を作る(絶対アドレス指定編)”のテストベンチが間違っていたので、その修正です。その他のC ソースコードも修正してあります。

まずは、Source の DMA_Write.c を貼っておきます。

// DMA_Write.cpp
// 2016/07/10 by marsee
//
// frame_buffer0, frame_buffer1, frame_buffer2 には3つのフレームバッファのアドレスを入れる
// 2017/04/15 : m_axiプラグマのdepthを変更した
//

#include <stdio.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#include "DMA_Write.h"

int DMA_Write(hls::stream<ap_axis<32,1,1,1> >& ins, volatile int *out,
        unsigned int frame_buffer0, unsigned int frame_buffer1,
        unsigned int frame_buffer2, volatile ap_uint<2> & active_frame){
#pragma HLS INTERFACE ap_vld port=active_frame
#pragma HLS INTERFACE s_axilite port=frame_buffer0
#pragma HLS INTERFACE s_axilite port=frame_buffer1
#pragma HLS INTERFACE s_axilite port=frame_buffer2
#pragma HLS INTERFACE m_axi depth=9216 port=out offset=off
//#pragma HLS INTERFACE m_axi depth=30000000 port=out offset=off
#pragma HLS INTERFACE axis port=ins
#pragma HLS INTERFACE s_axilite port=return

    ap_axis<32,1,1,1> pix;
    int dma_index;

    for (int i=0; i<MAX_FRAME_NUMBER; i++){
        switch (i){
            case 0 :
                dma_index = frame_buffer0/sizeof(int);
                break;
            case 1 :
                dma_index = frame_buffer1/sizeof(int);
                break;
            case 2 :
                dma_index = frame_buffer2/sizeof(int);
                break;
        } 
        active_frame = i;

        do { // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
            ins >> pix;
        } while(pix.user == 0);

        for (int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
            for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE II=1
                if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                    ins >> pix;    // AXI4-Stream からの入力
                out[dma_index+(y*HORIZONTAL_PIXEL_WIDTH)+x] = pix.data;
            }
        }
    }
    return 0;
}


次に、DMA_Write.h を貼っておきます。

// DMA_Write.h
// 2016/07/10 by marsee
//

#ifndef __DMA_WRITE_H__
#define __DMA_WRITE_H__

//#define HORIZONTAL_PIXEL_WIDTH    800
//#define VERTICAL_PIXEL_WIDTH    600

#define HORIZONTAL_PIXEL_WIDTH    64
#define VERTICAL_PIXEL_WIDTH    48

#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#define MAX_FRAME_NUMBER    3

#endif


DMA_Write_tb.cpp を貼っておきます。

// DMA_Write_tb.cpp
// 2016/07/10 by marsee
//
// 2017/04/15 : DMA_Write()を呼び出すときの引数を変更した
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include <ap_axi_sdata.h>

#include "DMA_Write.h"
#include "bmp_header.h"

int DMA_Write(hls::stream<ap_axis<32,1,1,1> >& ins, volatile int *out,
        unsigned int frame_buffer0, unsigned int frame_buffer1,
        unsigned int frame_buffer2, volatile ap_uint<2> & active_frame);

int main()
{
    using namespace std;

    hls::stream<ap_axis<32,1,1,1> > ins;
    ap_axis<32,1,1,1> pix;

    BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
    BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
    FILE *fbmpr, *fbmpw;
    int *rd_bmp, *hw_lapd;
    int blue, green, red;
    ap_uint<2> active_frame;
    int *frame_buffer;

    if ((fbmpr = fopen("test.bmp""rb")) == NULL){ // test.bmp をオープン
        fprintf(stderr, "Can't open test.bmp by binary read mode\n");
        exit(1);
    }
    // bmpヘッダの読み出し
    fread(&bmpfhr.bfType, sizeof(char), 2, fbmpr);
    fread(&bmpfhr.bfSize, sizeof(long), 1, fbmpr);
    fread(&bmpfhr.bfReserved1, sizeof(short), 1, fbmpr);
    fread(&bmpfhr.bfReserved2, sizeof(short), 1, fbmpr);
    fread(&bmpfhr.bfOffBits, sizeof(long), 1, fbmpr);
    fread(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpr);

    // ピクセルを入れるメモリをアロケートする
    if ((rd_bmp =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }

    // rd_bmp にBMPのピクセルを代入。その際に、行を逆転する必要がある
    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            blue = fgetc(fbmpr);
            green = fgetc(fbmpr);
            red = fgetc(fbmpr);
            rd_bmp[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] = (blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16);
        }
    }
    fclose(fbmpr);

    // ins に入力データを用意する
    for(int i=0; i<5; i++){    // dummy data
           pix.user = 0;
         pix.data = i;
        ins << pix;
    }

    for(int k=0; k<MAX_FRAME_NUMBER; k++){
        for(int j=0; j < bmpihr.biHeight; j++){
            for(int i=0; i < bmpihr.biWidth; i++){
                pix.data = (ap_int<32>)rd_bmp[(j*bmpihr.biWidth)+i];

                if (j==0 && i==0)    // 最初のデータの時に TUSER を 1 にする
                    pix.user = 1;
                else
                    pix.user = 0;

                if (i == bmpihr.biWidth-1// 行の最後でTLASTをアサートする
                    pix.last = 1;
                else
                    pix.last = 0;

                ins << pix;
            }
        }
    }

    // frame buffer をアロケートする、3倍の領域を取ってそれを3つに分ける
    if ((frame_buffer =(int *)malloc(MAX_FRAME_NUMBER * sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate frame_buffer0 ~ 2\n");
        exit(1);
    }

    DMA_Write(ins, (volatile int *)0, (unsigned int)frame_buffer,
        (unsigned int)frame_buffer+(bmpihr.biWidth * bmpihr.biHeight * sizeof(unsigned int)),
        (unsigned int)frame_buffer+(2 * (bmpihr.biWidth * bmpihr.biHeight) * sizeof(unsigned int)),
        active_frame);
    
    // DMAされたデータをBMPフィルに書き込む
    char output_file[] = "dma_result0.bmp";
    for (int i=0; i<MAX_FRAME_NUMBER; i++){
        switch (i){
            case 0:
                strcpy(output_file,"dma_result0.bmp");
                break;
            case 1:
                strcpy(output_file,"dma_result1.bmp");
                break;
            case 2:
                strcpy(output_file,"dma_result2.bmp");
                break;
        }
        if ((fbmpw=fopen(output_file, "wb")) == NULL){
            fprintf(stderr, "Can't open %s by binary write mode\n", output_file);
            exit(1);
        }
        // BMPファイルヘッダの書き込み
        fwrite(&bmpfhr.bfType, sizeof(char), 2, fbmpw);
        fwrite(&bmpfhr.bfSize, sizeof(long), 1, fbmpw);
        fwrite(&bmpfhr.bfReserved1, sizeof(short), 1, fbmpw);
        fwrite(&bmpfhr.bfReserved2, sizeof(short), 1, fbmpw);
        fwrite(&bmpfhr.bfOffBits, sizeof(long), 1, fbmpw);
        fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpw);

        // RGB データの書き込み、逆順にする
        int offset = i * bmpihr.biWidth * bmpihr.biHeight;
        for (int y=0; y<bmpihr.biHeight; y++){
            for (int x=0; x<bmpihr.biWidth; x++){
                blue = frame_buffer[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                green = (frame_buffer[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                red = (frame_buffer[offset+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

                fputc(blue, fbmpw);
                fputc(green, fbmpw);
                fputc(red, fbmpw);
            }
        }
        fclose(fbmpw);
    }       
    free(rd_bmp);
    free(frame_buffer);
    return 0;
}


これで、C コードの合成を行いました。Vivado HLS のバージョンは 2016.4 です。
結果を示します。
DMA_Write_IP2_1_170415.png

DMA_Write_IP2_2_170415.png
2016.2 の結果と比べると多少、リソース使用量が減っていますね。

合成されたAXI4 Lite SlaveのVerilog HDL ファイルのDMA_Write_AXILiteS_s_axi.v のアドレスマップを下に示します。
ここでのポイントは、 DMA_Write() 関数自体のオフセット・アドレスを示すレジスタは無いことです。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of ap_return
//        bit 31~0 - ap_return[31:0] (Read)
// 0x18 : Data signal of frame_buffer0
//        bit 31~0 - frame_buffer0[31:0] (Read/Write)
// 0x1c : reserved
// 0x20 : Data signal of frame_buffer1
//        bit 31~0 - frame_buffer1[31:0] (Read/Write)
// 0x24 : reserved
// 0x28 : Data signal of frame_buffer2
//        bit 31~0 - frame_buffer2[31:0] (Read/Write)
// 0x2c : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


C/RTL協調シミュレーションを行いました。
DMA_Write_IP2_3_170415.png
エラーが発生してしまいました。

次に、DMA_Write.cpp の

#pragma HLS INTERFACE m_axi depth=9216 port=out offset=off

のdepth だけを 30000000 に変更します。
DMA_Write_IP2_4_170415.png

Cコードの合成を行います。depth の値は合成結果に影響を及ぼさないので、結果の変化はありません。
C/RTL協調シミュレーションを行いました。今度は、正常終了です。
DMA_Write_IP2_5_170415.png

C/RTL協調シミュレーションの波形を見ていきます。
まずは波形全体です。
DMA_Write_IP2_6_170415.png

DMA_Write_IP2_7_170415.png
出力のAXI4 Master Write も正常にデータが出いているのが分かります。

レジスタ設定用のAXI4-Lite Slave アクセスを見てみましょう。
DMA_Write_IP2_8_170415.png
3つのDMAのWrite の先頭アドレスはそれぞれ 0x0133ad98, 0x1133dd98, 0x01340d98 です。

3つの DMA Write の先頭アドレスを見てみましたが、見事に、0x0133ad98, 0x1133dd98, 0x01340d98 でした。
1つ目のDMA Write の先頭アドレス。
DMA_Write_IP2_9_170415.png

2つ目のDMA Write の先頭アドレス。
DMA_Write_IP2_10_170415.png

3つ目のDMA Write の先頭アドレス。
DMA_Write_IP2_11_170415.png

malloc() で確保した画像領域の最後+1 は 0x01340d98 + 64(10進数)x 48(10進数) x 4(バイト) = 0x01343d98 です。
因みに depth の 30000000 は、0x01c9c380 です。
テストベンチで DMA_Write() の out のオフセットに0 を入れています。(「Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2016.4) 2016 年 11 月 30 日」の114ページの「AXI4 インターフ ェイスのアドレス オフセットの制御」参照)
そして、何度かやってみたのですが、malloc() で取られた画像用のバッファのアドレス以上のdepth の値を与えないとエラーになってしまうようなのです。つまり、Vivado HLS のC/RTL協調シミュレーションの仕組みはアクセスする可能性のあるバッファ領域を確保しなくてはいけないようです。考えてみれば当たり前とも言えます。通常はオフセット・アドレス+オフセットですので、使う領域だけを depth に指定すれば良いのですが、malloc() された絶対アドレスを引数経由で渡してDMA するとこのような結果になります。

また、C/RTL協調シミュレーションの結果も奇妙なことになっています。
DMA_Write_IP2_12_170415.png

Cシミュレーションの結果である sim -> wrapc フォルダのdma_result0.bmp は正常に「A」ですが、
DMA_Write_IP2_13_170415.png

C/RTL協調シミュレーションの結果の sim -> wrapc_pc フォルダのdma_result0.bmp は真っ暗です。
DMA_Write_IP2_14_170415.png

このようなアドレスの与え方には、Vivado HLS が対応していないのかもしれません?
  1. 2017年04月15日 17:03 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

ikwzm さんの構築したPYNQ ボード用DebianでのPS出力クロックfclkの設定方法

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる9(入れ替え2)”を解決する試み。

昨日のFASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路が動かないと書いたが、ikwzm さんがクロックはどうなっているかと聞いてくれて、それでハッと気が付いた。Zynq のクロックはPSから供給されていて、ビットファイルは関係ない。。。
ikzwm さんから、”Linux でユーザー空間から Zynq の PLクロック信号を制御するデバイスドライバ”を教えて頂いた。いつもありがとうございます。しかし、何でも作ってありますね。。。
更に、今回のFASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路用の設定も教えてもらった

ikwzm/fclk-install によると、デバイスツリー・オーバーレイを設定する dtbocfg.rb というコマンドがPYNQ ボードのDebian に入っているそうだ。
PYNQ_Linux_ikwzm_95_170414.png

ikwzm/fclk-install の 4 つの DTS ファイルを fclk0-zynq-pynq.dts、fclk1-zynq-pynq.dts、fclk2-zynq-pynq.dts、fclk3-zynq-pynq.dts ファイルとして、~/device_tree_overlay/fclk_install ディレクトリにセーブした。
PYNQ_Linux_ikwzm_96_170414.png

4 つの DTS ファイルのうちの fclk0-zynq-pynq.dts ファイルを示す。なお、これらのファイルはikwzm/fclk-install の fclk0-zynq-zybo.dts ~ fclk3-zynq-zybo.dts のコピーだ。
fclk0-zynq-pynq.dts を示す。

/dts-v1/;
/ {
    fragment@0 {
        target-path = "/amba";
        __overlay__ {
            fclk0 {
                compatible  = "ikwzm,fclkcfg-0.10.a";
                clocks      = <1 15>;
            };
        };
    };
};


fclk1-zynq-pynq.dts は fclk0 が fclk1 に変更されている。
更に、
clocks      = <1 15>;

clocks      = <1 16>;
になっていて、+1 されている。これは、fclk2, fclk3 も同様だ。

さて、 fclk0-zynq-pynq.dts をデバイスツリー・オーバーレイとしてロードしてみよう。
スーパーユーザーになって、次のコマンドを実行した。
dtbocfg.rb --install --dts fclk0-zynq-pynq.dts fclk0
PYNQ_Linux_ikwzm_97_170414.png

シリアル接続のコンソールに fclk0 の表示が出た。
PYNQ_Linux_ikwzm_106_170414.png

/config/device-tree/overlays ディレクトリに fclk0 ディレクトリができた。
PYNQ_Linux_ikwzm_98_170414.png

fclk0 ディレクトリの中には、dtbo と status ファイルがあった。
PYNQ_Linux_ikwzm_99_170414.png

/sys/class/flckcfg ディレクトリには、fclk0 ディレクトリへのリンクがあった。
PYNQ_Linux_ikwzm_100_170414.png

/sys/class/flckcfg/fclk0 ディレクトリの内容を示す。
PYNQ_Linux_ikwzm_101_170414.png

enable, rate, round_rate ファイルをリードした。
PYNQ_Linux_ikwzm_102_170414.png

enable = 1, rate = 100000000 で 100 MHz だった。round_rate は値がおかしいというか、0 Hz の設定値だったら 251.953 KHz だよ、ということなんだろう?と思う。

試しにスーパーユーザーになって、100000000 を設定してみたところ、100000000 になった。
PYNQ_Linux_ikwzm_103_170414.png

試しに
echo 25000000 > rate
で、fclk0 を 25 MHz に設定してみた。
PYNQ_Linux_ikwzm_104_170414.png
  1. 2017年04月14日 05:12 |
  2. Linux
  3. | トラックバック:0
  4. | コメント:0

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる9(入れ替え2)

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる8(入れ替え1)”の続き。

前回は、新たなFPGA回路として、以前作ったFASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路をコンフィギュレーションし、デバイスツリー・オーバーレイを行った。今回は、その動作をチェック(動作しなかったのだが)し、その後で、PYNQのLinux を起動しながら、GPIO でLED をテストする回路をコンフィギュレーションし、デバイスツリー・オーバーレイを行って、動作を確認する。

デバイスツリー・オーバーレイ用のShell スクリプトの devtov を変更して、/dev/udmabuf* があるときは、udmabuf* も chmod 666 に変更するようにスクリプトを書き換えた。

FASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路のデバイスツリー・オーバーレイのロードによって、コンソール・ウインドウに出ている udmabuf のステータスを示す。
PYNQ_Linux_ikwzm_89_170413.png

次に、FASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路をディスプレイに表示するためのアプリケーションソフトの cam_disp.c を作成した。
PYNQ_Linux_ikwzm_90_170413.png

cam_disp.c を
gcc -o cam_disp cam_disp.c
でコンパイルした。(もうすでにしてあった)
./cam_disp を起動したが、HDMI 画面には何も映らなかった。下の図ではデバック用に一部のレジスタの内容を表示している。
cam_disp.c は間違ってはいないと私は思うのだが?どこかおかしいのかもしれない。ちなみにベアメタル・アプリケーションとして、SDKから起動するとカメラの画像を表示できる。
もっと簡単な事例でもう一度、確かめてみよう。

動作を確認することはできなかったが、PYNQ のFPGA部分のコンフィギュレーションとデバイスツリー・オーバーレイを行った。これのデバイスツリー・オーバーレイを削除して、GPIO でLED をテストする回路のビットファイルに入れ替えて、それ用のデバイスツリー・オーバーレイをロードしよう。

まずは、FASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路のデバイスツリー・オーバーレイを削除する。
/config/device-tree/overlays に pynq_fastx ディレクトリがあるので、Shell スクリプトの rmdevtov で削除する。
./rmdevtov pynq_fastx
PYNQ_Linux_ikwzm_92_170413.png

pynq_fastx ディレクトリが削除された。
PYNQ_Linux_ikwzm_92_170413.png

次に、GPIO でLED をテストする回路のビットファイルをPYNQ のFPGA 部分にダウンロードする。
./fpgamag pynq_led_test_wrapper.bit

GPIO でLED をテストする回路のデバイスツリー・オーバーレイをロードする。
./devtov uio_gpio_0

アプリケーションソフトを起動すると正常に動作し、LED を +1 しながら、表示することができた。
./pynq_led_test
PYNQ_Linux_ikwzm_94_170413.png

最後に、FASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路用の現在のアプリケーションソフトの cam_disp.c を貼っておく。

// cam_disp.c
// 2017/04/08 by marsee
//

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

#define NUMBER_OF_WRITE_FRAMES    3 // Note: If not at least 3 or more, the image is not displayed in succession.

#define HORIZONTAL_PIXELS    800
#define VERTICAL_LINES        600
#define PIXEL_NUM_OF_BYTES    4
#define ALL_DISP_ADDRESS    (HORIZONTAL_PIXELS*VERTICAL_LINES*PIXEL_NUM_OF_BYTES)

#define FASTX_THRESHOLD        20

void cam_i2c_init(volatile unsigned *mt9d111_i2c_axi_lites) {
    mt9d111_i2c_axi_lites[64] = 0x2// reset tx fifo ,address is 0x100, i2c_control_reg
    mt9d111_i2c_axi_lites[64] = 0x1// enable i2c
}

void cam_i2x_write_sync(void) {
    // unsigned c;

    // c = *cam_i2c_rx_fifo;
    // while ((c & 0x84) != 0x80)
        // c = *cam_i2c_rx_fifo; // No Bus Busy and TX_FIFO_Empty = 1
    usleep(1000);
}

void cam_i2c_write(volatile unsigned *mt9d111_i2c_axi_lites, unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    mt9d111_i2c_axi_lites[66] = 0x100 | (device_addr & 0xfe);   // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    mt9d111_i2c_axi_lites[66] = write_addr;
    mt9d111_i2c_axi_lites[66] = (write_data >> 8)|0xff;         // first data
    mt9d111_i2c_axi_lites[66] = 0x200 | (write_data & 0xff);        // second data
    cam_i2x_write_sync();
}

int main()
{
    int fd0, fd1, fd2, fd3, fd4, fd5, fd6, fd7, fd8;
    int fd_udmabuf, fd_paddr;
    volatile unsigned int *axi_iic_0, *axi_vdma_0, *axis_switch_0, *axis_switch_1;
    volatile unsigned int *bitmap_disp_cntrler_axim_0, *fastx_corner_det_0;
    volatile unsigned int *mt9d111_inf_axis_0;
    volatile unsigned int *frame_buffer;
    unsigned char  attr[1024];
    unsigned long  phys_addr;

    // axi_iic_0 (uio0)
    fd0 = open("/dev/uio0", O_RDWR); // axi_iic_0
    if (fd0 < 1){
        fprintf(stderr, "/dev/uio0 (axi_iic_0) open errorn");
        exit(-1);
    }
    axi_iic_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd0, 0);
    if (axi_iic_0 == MAP_FAILED){
        fprintf(stderr, "axi_iic_0 mmap errorn");
        exit(-1);
    }
    
    // axi_vdma_0 (uio1)
    fd1 = open("/dev/uio1", O_RDWR); // axi_vdma_0
    if (fd1 < 1){
        fprintf(stderr, "/dev/uio1 (axi_vdma_0) open errorn");
        exit(-1);
    }
    axi_vdma_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, 0);
    if (axi_vdma_0 == MAP_FAILED){
        fprintf(stderr, "axi_vdma_0 mmap errorn");
        exit(-1);
    }

    // axis_switch_0 (uio2)
    fd2 = open("/dev/uio2", O_RDWR); // axis_switch_0
    if (fd2 < 1){
        fprintf(stderr, "/dev/uio2 (axis_switch_0) open errorn");
        exit(-1);
    }
    axis_switch_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
    if (axis_switch_0 == MAP_FAILED){
        fprintf(stderr, "axis_switch_0 mmap errorn");
        exit(-1);
    }
    
    // axis_switch_1 (uio3)
    fd3 = open("/dev/uio3", O_RDWR); // axis_switch_1
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 (axis_switch_1) open errorn");
        exit(-1);
    }
    axis_switch_1 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (axis_switch_1 == MAP_FAILED){
        fprintf(stderr, "axis_switch_1 mmap errorn");
        exit(-1);
    }
    
    // bitmap_disp_cntrler_axim_0 (uio4)
    fd4 = open("/dev/uio4", O_RDWR); // bitmap_disp_cntrler_axim_0
    if (fd4 < 1){
        fprintf(stderr, "/dev/uio4 (bitmap_disp_cntrler_axim_0) open errorn");
        exit(-1);
    }
    bitmap_disp_cntrler_axim_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd4, 0);
    if (bitmap_disp_cntrler_axim_0 == MAP_FAILED){
        fprintf(stderr, "bitmap_disp_cntrler_axim_0 mmap errorn");
        exit(-1);
    }
    
    // mt9d111_inf_axis_0 (uio7)
    fd7 = open("/dev/uio7", O_RDWR); // mt9d111_inf_axis_0
    if (fd7 < 1){
        fprintf(stderr, "/dev/uio7 (mt9d111_inf_axis_0) open errorn");
        exit(-1);
    }
    mt9d111_inf_axis_0 = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd7, 0);
    if (mt9d111_inf_axis_0 == MAP_FAILED){
        fprintf(stderr, "mt9d111_inf_axis_0 mmap errorn");
        exit(-1);
    }
    
    // udmabuf4
    fd_udmabuf = open("/dev/udmabuf4", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fd_udmabuf == -1){
        fprintf(stderr, "/dev/udmabuf4 open errorn");
        exit(-1);
    }
    frame_buffer = (volatile unsigned int *)mmap(NULL, (ALL_DISP_ADDRESS*3), PROT_READ|PROT_WRITE, MAP_SHARED, fd_udmabuf, 0);
    if (frame_buffer == MAP_FAILED){
        fprintf(stderr, "frame_buffer mmap errorn");
        exit(-1);
    }

    // phys_addr of udmabuf4
    fd_paddr = open("/sys/devices/soc0/amba/amba:udmabuf4/udmabuf/udmabuf4/phys_addr", O_RDONLY);
    if (fd_paddr == -1){
        fprintf(stderr, "/sys/devices/soc0/amba/amba:udmabuf4/udmabuf/udmabuf4/phys_addr open errorn");
        exit(-1);
    }
    read(fd_paddr, attr, 1024);
    sscanf(attr, "%lx", &phys_addr);  
    close(fd_paddr);
    printf("phys_addr = %x\n", (unsigned int)phys_addr);
    
    // axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_1[16] = 0x0// 0x40 = 0
    axis_switch_1[17] = 0x80000000// 0x44 = 0x80000000, disable
    axis_switch_1[18] = 0x80000000// 0x48 = 0x80000000, disable
    axis_switch_1[19] = 0x80000000// 0x4C = 0x80000000, disable
    axis_switch_1[0] = 0x2// Comit registers
    printf("axis_swtich_1[17] = %x\n", axis_switch_1[17]);
    
    // axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    axis_switch_0[16] = 0x0// 0x40 = 0;
    axis_switch_0[0] = 0x2// Comit registers
    printf("axis_swtich_0[16] = %x\n", axis_switch_0[16]);

    // AXI VDMA Initialization sequence (axi_vdma_0)
    axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    printf("axi_vdma_0[12] = %x\n", axi_vdma_0[12]);
    axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    axi_vdma_0[18] = NUMBER_OF_WRITE_FRAMES; // S2MM_FRMSTORE (0x48) register
    axi_vdma_0[12] = 0x00010002// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1)
    axi_vdma_0[41] = HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Horizontal Size RegisterS2MM_HSIZE0xc80 = 3200dec = 800 x 4
    axi_vdma_0[42] = HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Frame Delay and Stride RegisterS2MM_FRMDLY_STRIDE0xc80 = 3200dec = 800 x 4
    axi_vdma_0[43] = (unsigned)phys_addr; // S2MM Start Address (1 to 16) Start Address 1
    axi_vdma_0[44] = (unsigned)phys_addr; // S2MM Start Address (1 to 16) Start Address 2
    axi_vdma_0[45] = (unsigned)phys_addr; // S2MM Start Address (1 to 16) Start Address 3
    axi_vdma_0[12] = 0x00010003// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1, Run/stop = 1)
    while((axi_vdma_0[13] & 0x1) == 0x1) ; // Halt? (S2MM_VDMASR 0x34)
    axi_vdma_0[40] = VERTICAL_LINES; // S2MM Vertical Size (S2MM_VSIZE  Offset 0xA0) 0x258 = 600dec

    // CMOS Camera initialize, MT9D111
    cam_i2c_init(axi_iic_0);
    
    cam_i2c_write(axi_iic_0, 0xba, 0xf00x1);        // Changed regster map to IFP page 1
    cam_i2c_write(axi_iic_0, 0xba, 0x970x20);    // RGB Mode, RGB565

    mt9d111_inf_axis_0[1] = 0;
    mt9d111_inf_axis_0[0] = (unsigned int)phys_addr;
    printf("mt9d111_inf_axis_0[0] = %x\n", mt9d111_inf_axis_0[0]);

    bitmap_disp_cntrler_axim_0[0] = (unsigned int)phys_addr;
    printf("bitmap_disp_cntrler_axim_0[0] = %x\n", bitmap_disp_cntrler_axim_0[0]);
    printf("frame_buffer[0] = %x\n", frame_buffer[0]);
    printf("frame_buffer[1] = %x\n", frame_buffer[1]);
        
    munmap((void *)axi_iic_0, 0x10000);
    munmap((void *)axi_vdma_0, 0x10000);
    munmap((void *)axis_switch_0, 0x10000);
    munmap((void *)axis_switch_1, 0x10000);
    munmap((void *)bitmap_disp_cntrler_axim_0, 0x10000);
    munmap((void *)mt9d111_inf_axis_0, 0x10000);
    munmap((void *)frame_buffer, (ALL_DISP_ADDRESS*3));
    
    close(fd0);
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    close(fd7);
    close(fd_udmabuf);
}

  1. 2017年04月13日 04:22 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

昨日、家のインターネット・アクセスが出来なくなりました

昨日、午前9時くらいに突然、家のインターネット・アクセスが出来なくなりました。家は、NTT光+Yahoo BBルーター、Yahoo BBフォンなのですが、全くアクセスできません。
自分のパソコンでコマンド プロンプトを立ち上げて、ipconfig /all をやってみましたが、DHCP リースの状況が滅茶苦茶でした。
internet_access_1_170412.png

午後5時50分頃、家に帰って来た時に確認したのですが、全くインターネットに接続できません。
Yahoo BB ルーターやNTT光の光モデムは電源を抜いて、10秒数えてもう一度電源ON もしてみましたが、改善しません。
DHCP リースがダメなので、Yahoo BB ルーターがダメだと思い、Yahoo BB のサポートに連絡しました。DHCP リースがダメだというと、すぐに代わりのルーターを木曜日に送ってくれるということになりました。

次の日、つまり今日のインターネット・アクセスはあきらめていたのですが、パソコンを付けるとインターネット・アクセスが復活しました。なんででしょうか?良かったんですが、原因が不明です。
ともかく、コマンド プロンプトを立ち上げて、ipconfig /all をやってみると、正常に DHCP リースされていました。
internet_access_2_170412.png
  1. 2017年04月12日 05:16 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

ikwzm/vivado-hls-axi-dma-read-failureを試してみた

以前、

「Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)」を使って合成後の機能シミュレーション
「Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)」を使って合成後の機能シミュレーション2
「Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)」を使って合成後の機能シミュレーション3

「Vivado HLS で DMA Read IP を作る2(絶対アドレス指定版)」をIP として入れたVivado プロジェクトを論理シミュレーションすると動作するが、 Post-Synthesis Functional Simulation すると動作しない。しかし、Project Settings のTarget language を VHDL にする(DMA Read IPはVerilog HDL で IP化しているのだが)と Post-Synthesis Functional Simulation が動作するという現象があった。それを ikwzm さんが vivado-hls-axi-dma-read-failure でテストケースを作ってくれたのだが、Linux にVivado 2016.4 の環境が無かったためやっていなかった。今回は、環境を構築したためやってみた。

まずは、私のVirtualBox 上で vivado-hls-axi-dma-read-failureに書いてある通りにGitHub からクローンした(Ubuntu 16.04、Vivado 2016.4)
git clone https://github.com/ikwzm/vivado-hls-axi-dma-read-failure.git
cd vivado-hls-axi-dma-read-failure
git submodule init
git submodule update

vivado-hls-axi-read-failure_1_170411.png

これで、vivado-hls-axi-dma-read-failure ディレクトリが生成され、中にproject_post_synth_sim_ng ディレクトリとproject_post_synth_sim_ok ディレクトリがある。この中に create_project.tcl がある。
vivado-hls-axi-read-failure_2_170411.png

Vivado を起動した。
vivado-hls-axi-read-failure_3_170411.png

Vivado のGUI が立ち上がったので、まずは、tcl console で ~/vivado-hls-axi-dma-read-failure/project_post_synth_sim_ng に移動する。
cd /home/masaaki/vivado-hls-axi-dma-read-failure/project_post_synth_sim_ng/
vivado-hls-axi-read-failure_4_170411.png

souce create_project.tcl を実行した。
vivado-hls-axi-read-failure_5_170411.png

プロジェクトが生成された。
vivado-hls-axi-read-failure_6_170411.png

Flow Navigator からSynthesis -> Run Synthesis をクリックして、論理合成を行った。
論理合成が終了した。
vivado-hls-axi-read-failure_7_170411.png

Flow Navigator の Simulation -> Run Simulation -> Run Post-Synthesis Functional Simulation を選択して、論理合成後の機能シミュレーションを行った。
Vivado Internal Exception が出てしまった。tcl console から close_sim -force でもシミュレーションが落ちなかったので、vivado のプロセスを kill した。
vivado-hls-axi-read-failure_8_170411.png

もう一度、Vivado を起動して、tcl console に
cd /home/masaaki/vivado-hls-axi-dma-read-failure/project_post_synth_sim_ok/
souce create_project.tcl

を入力した。
vivado-hls-axi-read-failure_9_170411.png

論理合成を行った後、Flow Navigator の Simulation -> Run Simulation -> Run Post-Synthesis Functional Simulation を選択して、論理合成後の機能シミュレーションを行った。
Vivado Internal Exception が出てしまった。vivado のプロセスを kill した。
vivado-hls-axi-read-failure_10_170411.png

Linux でやるとうまく行かないので、vivado-hls-axi-dma-read-failure ディレクトリをそのままWindows 10 にコピーしてPost-Synthesis Functional Simulation をやってみた。
まずは、project_post_synth_sim_ng からやってみた。
もう一度、論理合成を行ってから、 Post-Synthesis Functional Simulation を行った。
vivado-hls-axi-read-failure_11_170411.png

100065 ns でタイムアウトで停止している。
vivado-hls-axi-read-failure_19_170411.png

波形を示す。
vivado-hls-axi-read-failure_12_170411.png
vivado-hls-axi-read-failure_13_170411.png
vivado-hls-axi-read-failure_14_170411.png

DMA Read IPのレジスタを設定するAXI4-Lite Slave アクセスのみだ。

次に、project_post_synth_sim_ok をやってみよう。
もう一度、論理合成を行ってから、Flow Navigator の Simulation -> Run Simulation -> Run Post-Synthesis Functional Simulation を選択して、論理合成後の機能シミュレーションを行った。
vivado-hls-axi-read-failure_15_170411.png

1636 ns で Failure: Simulation complete. で終了している。
vivado-hls-axi-read-failure_20_170411.png

波形を示す。
vivado-hls-axi-read-failure_16_170411.png
vivado-hls-axi-read-failure_17_170411.png
vivado-hls-axi-read-failure_18_170411.png

AXI4 Master も動作しているのが分かる。

(2017/04/12 : 追記)
Post-Synthesis Functional Simulation のOK とNG の違いですが、NG は Target language が Verilog です。
vivado-hls-axi-read-failure_21_170411.png

OK は、 Target language が VHDL です。
vivado-hls-axi-read-failure_22_170411.png

その違いで、論理合成がおかしくなるか?、正常か?が決まるようです。ちなみにDMA Read IP はVerilog HDLを指定してIP 化しています。
  1. 2017年04月11日 05:24 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

技術書典2に行ってきました

昨日、技術書展2 に行ってきました。

秋葉原のアキバ・スクエアでやってました。
tech_book_1_170410.jpg

開場する11時前に行ったのですが、すごい人で、雨の中並んでいました。
tech_book_2_170410.jpg

1時間くらい並んでやっと会場に入れました。いろいろな自作の技術書がたくさんありました。やはりソフトウェアの本が多かったですね。FPGAの本もありました。Lattice のFPGAの基板も売っていて、本も売っている方もいました。

私が買ったのは、ひでみさんのFPGA本、AGPF WORKS (とっても内容があって1,000円では安かったです)、買いたくても買えなかったFPGA技術SPECIAL、「TesorFlow はじめました」とこれはオライリーの本の「Caffe をはじめよう」です。
tech_book_3_170410.jpg

雨の中並ぶのは大変でしたが、とっても楽しかったです。また技術書典3があったら行きたいですね。
そうそう、傘を畳むときに水しぶきをかけてしまった並んでいた時の隣の方、すみませんでした。
あと、オライリーの売り場で「本当にオライリーなんだ」と言ってしまって、ツィートされていました。疑って申し訳ない。でも、薄くて背表紙に何も書いてなかったので、もしかして、お礼―リーなのかな?と思っちゃったんですよ。。。
  1. 2017年04月10日 05:27 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:0

Raspberry Pi Zero にLinux をインストール

Raspberry Pi Zeroが来ました”の続き。

Raspberry Pi Zero インストール(2016/07/02)”のほぼその通りにRaspberry Pi Zero のSDカードにLinux をインストールした。

ミニHDMI ポートは、”SANWA SUPPLY HDMI変換アダプタ ミニHDMI 黒 AD-HD07M”で通常のHDMI に変化し、USB-OTG は”エレコム USBハブ 2.0対応 4ポート microUSBケーブル+変換アダプタ付 バスパワー ブラック U2HS-MB02-4BBK”を使用して、USB-A コネクタを接続できるようにした。

Raspberry Pi Zero にHDMI 経由でディスプレイとUSB-OTG 経由でキーボードとマウスを接続して、Raspberry Pi Zero のSD カードにNOOBSを書いて挿入した。
すると、GUI が立ち上がった。
Raspberry_Pi_Zero_10_170407.jpg

アプリケーションソフトを立ち上げてみた。Chromium、Scratch…
Raspberry_Pi_Zero_11_170407.jpg

BUFFALO 11n対応 11g/b 無線LAN子機 親機-子機デュアルモード対応モデル WLI-UC-GNM2”を”エレコム USBハブ 2.0対応 4ポート microUSBケーブル+変換アダプタ付 バスパワー ブラック U2HS-MB02-4BBK”に入れると、自動認識して、そのまま無線LAN親機が見えたので、そのままインターネットのに接続できた。実に簡単だった。
Raspberry_Pi_Zero_12_170407.jpg

まだ、時計をJST に設定していない。あと、日本語環境も入れる予定。
Raspberry Pi Zero は特にブラウザとか使っていると、動作は遅いが、これが 648 円とは信じられない。。。
  1. 2017年04月09日 15:06 |
  2. マイコン関連
  3. | トラックバック:0
  4. | コメント:0

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる8(入れ替え1)

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる7(shellスクリプト)”の続き。

前回は、デバイスツリー・オーバーレイとFPGA Manager を使いやすいようにShell スクリプトを作成した。今回は、2つのFPGA回路とデバイスツリーをLinux 起動しながら入れ替えてみようと思う。

まずは、新たなFPGA回路として、以前作ったFASTX コーナー検出、ラプラシアンフィルタ、アンシャープ・マスクキング・フィルタの回路をコンフィギュレーションして、動作を確認する。

まずは、デバイスツリーを作成しよう。
pynq_fastx.dts を作成した。それぞれのデバイスとudmabuf を記述した。udmabuf はSVGA 3画面分のメモリを確保することにした。
PYNQ_Linux_ikwzm_86_170408.png

/dts-v1/;
/ {
    fragment@0 {
        target-path = "/amba";
        __overlay__ {
            #address-cells = <0x1>;
            #size-cells = <0x1>;
            axi_iic_0@41600000 {
                compatible = "generic-uio";
                reg = <0x41600000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            axi_vdma_0@43000000 {
                compatible = "generic-uio";
                reg = <0x43000000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            axis_switch_0@43C10000 {
                compatible = "generic-uio";
                reg = <0x43C10000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            axis_switch_1@43C20000 {
                compatible = "generic-uio";
                reg = <0x43C20000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            bitmap_disp_cntrler_axi_master_0@43C00000 {
                compatible = "generic-uio";
                reg = <0x43C00000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            fastx_corner_det_0@43C30000 {
                compatible = "generic-uio";
                reg = <0x43C30000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            lap_filter_axis_0@43C50000 {
                compatible = "generic-uio";
                reg = <0x43C50000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            mt9d111_inf_axis_0@43C40000 {
                compatible = "generic-uio";
                reg = <0x43C40000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            unsharp_mask_axis_0@43C60000 {
                compatible = "generic-uio";
                reg = <0x43C60000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
            udmabuf4 {
                compatible = "ikwzm,udmabuf-0.10.a";
                minor-number = <4>;
                size = <0x00600000>;
            };
        };
    };
};


デバイスツリー・ソースを
dtc -I dts -O dtb -o pynq_fastx.dtbo pynq_fastx.dts
でコンパイルした。
pynq_fastx.dtbo を使用してデバイスツリー・オーバーレイを行う。そのために、前回作成したShell スクリプトを使用する。
まずは、FPGA Manager を使用して、FPGA をコンフィグレーションする。
./fpgamag pynq_fastx_wrapper.bit
次に、デバイスツリー・オーバーレイを行う。
./devtov pynq_fastx
udmabuf がロードされたかどうか?確かめてみよう。
ls -l /dev/udmabuf4
PYNQ_Linux_ikwzm_87_170408.png

/sys/devices/soc0/amba を見ると、各 uio と udmabuf4 が見える。
PYNQ_Linux_ikwzm_88_170408.png
  1. 2017年04月08日 06:57 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる7(shellスクリプト)

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる6(FPGA Manager)”の続き。

前回は、FPGA Manager を試して、ikwzm さんのおかげでビットファイルをダウンロードして、アプリケーションソフトを動作させることができた。今回は、デバイスツリー・オーバーレイとFPGA Manager を shell スクリプトにしてみようと思う。

まずは、デバイスツリー・オーバーレイから shell コマンドを作ってみた。
コマンド名は、devtov だ。第1引数はデバイスツリー名を設定する。
PYNQ_Linux_ikwzm_82_170407.png

#!/bin/sh
# device tree overlay command (devtov)
# Be sure to run it as superuser
# $1 is device tree name

mkdir /config/device-tree/overlays/$1
cp $1.dtbo /config/device-tree/overlays/$1/dtbo
echo 1 > /config/device-tree/overlays/$1/status

sleep 0.1
chmod 666 /dev/uio*
ls -l /dev/uio*

if test -e /dev/udmabuf*; then
    chmod 666 /dev/udmabuf*
    ls -l /dev/udmabuf*
fi


devtov のコマンドで、なぜ sleep 0.1 が入っているか?だが、この sleep を入れないと chmod が効かなかったからだ。つまり、/dev/uio* にマウントするのに一定の時間がかかるらしく、chmod するときには、uio が完全にできていないためだと思う。

次に、デバイスツリー・オーバーレイを削除する rmdevtov を示す。やはり、第1引数はデバイスツリー名を設定する。
PYNQ_Linux_ikwzm_83_170407.png

#!/bin/sh
# Remove device tree overlay command (rmdevtov)
# Be sure to run it as superuser
# $1 is device tree name

rmdir /config/device-tree/overlays/$1


FPGA Manager でビットファイルをFPGA にダウンロードするコマンドが fpgamag だ。第1引数はビットファイルを指定する。
PYNQ_Linux_ikwzm_84_170407.png

#!/bin/sh
# fpga manager command (fpgamag)
# Be sure to run it as superuser
# $1 is bit file name

echo 1 > /sys/class/fpgacfg/fpgacfg0/data_format
echo 1 > /sys/class/fpgacfg/fpgacfg0/load_start
cp $1 /dev/fpgacfg0


rmdevtov, devtov, fpgamag を使って、ロードしてあったデバイスツリー・オーバーレイを削除して、再度デバイスツリー・オーバーレイをロードし、FPGA をコンフィギュレーションして、./pynq_led_test を動かしたスナップショットを示す。ちゃんとLED が点灯して動作した。
PYNQ_Linux_ikwzm_85_170407.png
  1. 2017年04月07日 04:41 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる6(FPGA Manager)

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる5(デバイスツリー・オーバーレイ)”の続き。

前回は、デバイスツリー・オーバーレイを試して、成功した。今回は、FPGAをコンフィギュレーションする FPGA Manager を試してみた。

(2017/04/06:修正 デバイスツリー・オーバーレイとFPGA Manager がうまく動作した)

最初に PYNQ ボードのFPGA 部分をコンフィギュレーションする。
まずは、lsmod で、どんなモジュールが insmod されているか?を確かめた。
fpgacfg も入っている。
ls /dev/fpgacfg0 をやってみたが、すでにデバイスがあった。
PYNQ_Linux_ikwzm_70_170405.png

pynq_led_test_wrapper.bit を WinSCP で PYNQ ボードの Debian に転送した。/home/fpga/device_tree_overlay ディレクトリに転送した。
PYNQ_Linux_ikwzm_77_170405.png

FPGA をコンフィギュレーションした。
su
echo 1 > /sys/class/fpgacfg/fpgacfg0/data_format
echo 1 > /sys/class/fpgacfg/fpgacfg0/load_start
cp pynq_led_test_wrapper.bit /dev/fpgacfg0

PYNQ_Linux_ikwzm_72_170405.png
するとDONEランプが点灯した。

次に、デバイスツリー・オーバーレイを行った。
mkdir /config/device-tree/overlays/uio_gpio_0
cp uio_gpio_0.dtbo /config/device-tree/overlays/uio_gpio_0/dtbo
echo 1 > /config/device-tree/overlays/uio_gpio_0/status
ls /dev/uio0

PYNQ_Linux_ikwzm_73_170405.png

/dev/uio0 をユーザーからもリード、ライトできるように、chmod を 666 に設定した。
ls -l /dev/uio0
chmod 666 /dev/uio0
ls -l /dev/uio0

PYNQ_Linux_ikwzm_74_170406.png

pynq_led_test.c を作成した。
PYNQ_Linux_ikwzm_79_170406.png

// pynq_led_test.c
// 2017/04/06 by marsee
//

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

 int main() {
    int fd;
    volatile unsigned int *led_gpio;

    fd = open("/dev/uio0", O_RDWR);
    if (fd < 1){
        fprintf(stderr, "/dev/uio0 open error\n");
        exit(-1);
    }

    led_gpio = (volatile unsigned int *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    
    if (led_gpio == MAP_FAILED){
        fprintf(stderr, "mmap error\n");
        exit(-1);
    }
    
    unsigned int i = 0;  
    while(i<17){
        led_gpio[0] = i++;
        sleep(1);
    }
    
    munmap((void *)led_gpio, 0x10000);
    return(0);
}


pynq_led_test.c を gcc でコンパイルした。
gcc -o pynq_led_test pynq_led_test.c
PYNQ_Linux_ikwzm_80_170406.png

./pynq_led_test を起動すると、LD3~0が2進数で +1 されて表示された。
PYNQ_Linux_ikwzm_81_170406.png

デバイスツリー・オーバーレイとFPGA Manager がうまく動作した。
  1. 2017年04月06日 06:09 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる5(デバイスツリー・オーバーレイ)

”FPGA+SoC+Linux+Device Tree Overlay+FPGA Manager(PYNQ-Z1対応)”を試してみる4(環境の整備)”の続き。

前回は、ikwzm さんの配布しているDebian の環境整備を行った。今回は、 デバイスツリー・オーバーレイを確かめてみよう。

FPGA+SoC+LinuxでDevice Tree Overlayを試してみた”を参考に、デバイスツリー・オーバーレイを試してみよう。追加するデバイスツリーは、下の2つの記事で作成したAXI GPIO を使用したLED表示回路とする。
PYNQのLED表示プロジェクト1(ブロックデザインの作成)”
”PYNQのLED表示プロジェクト2(完成)

まずは、dtbocfg.ko はどこにあるのだろうか?ということで、nautilus を起動して、検索した。
その結果、/lib/modules/4.8.17-armv7-fpga/ikwzm ディレクトリにあることが分かった。
PYNQ_Linux_ikwzm_61_170401.png

ls -la /config/device-tree/overlays/
をやってみたところ、あったので、dtbocfg デバイスドライバはロードされているようだ。
PYNQ_Linux_ikwzm_62_170402.png

/config/device-tree/overlays で uio_gpio_0 ディレクトリを作成した。
sudo mkdir uio_gpio_0
ls -la uio_gpio_0/
でディレクトリを見ると、dtbo と status ファイルが生成されていた。
PYNQ_Linux_ikwzm_63_170402.png

uio_gpio_0..dts を address editor の内容とテンプレートを参考に作成した。
PYNQ_Linux_ikwzm_64_170402.png

/dts-v1/;
/ {
    fragment@0 {
        target-path = "/amba";
        __overlay__ {
            #address-cells = <0x1>;
            #size-cells = <0x1>;
            uio_gpio_0@41200000 {
                compatible = "generic-uio";
                reg = <0x41200000 0x10000>;
                #interrupts = <0x0 0x1d 0x4>;
            };
        };
    };
};


(2017/04/06:修正 デバイスツリーの領域を 0x1000 から 0x10000 に修正した)

uio_gpio_0.dts をコンパイルして uio_gpio_0.dtbo を生成した。
dtc -I dts -O dtb -o uio_gpio_0.dtbo uio_gpio_0.dts
スーパーユーザーになって、uio_gpio_0.dtbo を /config/device-tree/overlays/uio_gpio_0/dtbo にコピーした。
cp uio_gpio_0.dtbo /config/device-tree/overlays/uio_gpio_0/dtbo
PYNQ_Linux_ikwzm_65_170402.png

echo 1 > /config/device-tree/overlays/uio_gpio_0/status を行った。
/dev/uio0 が見えた。
ls -la /dev/uio*
なお、uio_gpio_0/status に 1 を書く前は /dev/uio0 は存在しない。
PYNQ_Linux_ikwzm_66_170402.png

/sys/devices/soc0/amba ディレクトリに 41200000.uio._gpio_0 ディレクトリがあった。
PYNQ_Linux_ikwzm_67_170402.png

/sys/devices/soc0/amba/41200000.uio._gpio_0/uio/uio0 ディレクトリを示す。
PYNQ_Linux_ikwzm_68_170402.png

/sys/devices/soc0/amba/41200000.uio._gpio_0/uio/uio0 ディレクトリの name ファイルの内容を示す。
uio_gpio_0 だった。
PYNQ_Linux_ikwzm_69_170402.png

デバイスツリー・オーバーレイで uio_gpio_0 をロードできることが分かった。
  1. 2017年04月04日 05:00 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

PYNQのLED表示プロジェクト2(完成)

PYNQのLED表示プロジェクト1(ブロックデザインの作成)”の続き。

前回は、ikwzm さんのFPGA Managerデバイスツリー・オーバーレイを確かめてみるために、簡単な例としてPYNQボードのLED表示プロジェクトを作ることにして、ブロックデザインを作って、論理合成を始めるところまでだった。今回は、論理合成、インプリメント、ビットストリームの生成を行って、SDKでアプリケーションソフトを作って、PYNQで確かめた。

前回は、Synthesis -> Run Synthesis をクリックして、論理合成を行ったところまでだった。
Launch Runs ダイアログが表示された。OKボタンをクリックした。
PYNQ_LED_test_24_170402.png

論理合成が終了して、Synthesis Completed ダイアログが表示された。
Open Synthesized Design のラジオボタンをクリックして、OKボタンをクリックした。
PYNQ_LED_test_25_170402.png

Lyaout から I/O Planning を選択した。
PYNQ_LED_test_26_170402.png

Package 画面が表示された。下のI/O Ports も表示された。
LD が見える。これを制約する。
PYNQ_LED_test_27_170402.png

なお、LD のピン配置を確認するために”PYNQ-Z1 Reference Manual”のFigure 12.1. PYNQ-Z1 GPIO を引用する。
PYNQ_LED_test_28_170402.png

Figure 12.1. PYNQ-Z1 GPIO の通りに LD をピン配置を制約すれば良い。
I/O Std をLVCMOS33 にして、LD[3 ] から LD[0] のPackage Pin を制約した。
PYNQ_LED_test_29_170402.png

セーブ・ボタンをクリックすると、Out of Data Design ダイアログが表示された。OKボタンをクリックする。
PYNQ_LED_test_30_170402.png

次に、Save Constraints ダイアログが表示された。
File name に pynq_led_test と入力して、OKボタンをクリックした。
PYNQ_LED_test_31_170402.png

pynq_led_test.xdc を示す。
PYNQ_LED_test_32_170402.png

Generate Bitstream をクリックして、論理合成、インプリメント、ビットストリームの生成を行った。
結果を示す。
PYNQ_LED_test_33_170402.png

ハードウェアをエクスポートして、SDK を起動した。
PYNQ_LED_test_34_170402.png

Vivado 2016.4 のSDKでデバイスツリーのソース(DTS)を生成する”を行って、AXI GPIO のデバイスツリーを生成してみた。
PYNQ_LED_test_35_170402.png

pynq_led_test アプリケーション・プロジェクトを作成し、pynq_led_test.c を作成した。
PYNQ_LED_test_36_170403.png

PYNQを電源ON し、FPGA ビットストリームをダウンロードして、アプリケーションソフトを起動したところ、0 から 16 まで、LEDに2進数で表示された。動作した。
これで、ikwzm さんのFPGA Managerデバイスツリー・オーバーレイを確かめる準備は完了した。
  1. 2017年04月03日 04:47 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0

PYNQのLED表示プロジェクト1(ブロックデザインの作成)

ikwzm さんのFPGA Managerデバイスツリー・オーバーレイを確かめてみるために、簡単な例としてPYNQボードのLED表示プロジェクトを作ることにした。

まずは、PYNQ_LED_test_164 プロジェクトを作成した。Zynq はPYNQボードの xc7z020clg400-1 を指定した。
PYNQ_LED_test_1_170402.png

Create Block Design をクリックして、ブロックデザインを生成した。名前は pynq_led_test とした。
スケルトンのDiagram で、add IP アイコンをクリックした。
PYNQ_LED_test_2_170402.png

ZYNQ Processing System を選択してダブルクリックした。
PYNQ_LED_test_3_170402.png

processing_system_7_0 が追加された。
PYNQ_LED_test_4_170402.png

processing_system_7_0 をダブルクリックし、設定画面を表示した。
PYNQボード用PSの設定方法”に従って、PSをPYNQボード用に設定した。
AXI GP0 を設定する。
processing_system_7_0 の設定画面で、Zynq Block Design をクリックしておいて、32b GP AXI Master Ports にマウスを持って行って、緑の箱の中が白くなったらクリックすると、AXI GP0 の設定画面が開く。
PYNQ_LED_test_5_170402.png

M AXI GP0 interface にチェックを入れた。
PYNQ_LED_test_6_170402.png

Clock Configuration をクリックして、FCLK_CLK0 が 100 MHz であることを確かめた。
PYNQ_LED_test_7_170402.png

OK ボタンをクリックして、PS の設定画面を終了した。PYNQボードの設定が行われている。
Run Block Automation をクリックした。
PYNQ_LED_test_8_170402.png

PS のFIXED_IO と DDR を接続する。
PYNQ_LED_test_9_170402.png

PS のFIXED_IO と DDR が接続された。
PYNQ_LED_test_10_170402.png

Add IP アイコンをクリックして、出てきたダイアログに gpio と入れると、AXI GPIO が出てくるので、それをダブルクリックしてブロックデザインに追加する。
PYNQ_LED_test_11_170402.png

AXI GPIO をブロックデザインに追加した。
PYNQ_LED_test_12_170402.png

Run Block Automation をクリックした。
下の図では、GPIO にチェックが入っているが、OUT端子のみを出したいので、チェックを外して、S_AXIのみをチェックする。
PYNQ_LED_test_13_170402.png

Processor System Reset と AXI Interconnect がインスタンスされて、配線された。
PYNQ_LED_test_14_170402.png

axi_gpio_0 の GPIO を展開して、gpio_io_o を右クリックし、右クリックメニューから Make External を選択した。
PYNQ_LED_test_15_170402.png

gpio_io_o[3:0] ポートが生成された。
PYNQ_LED_test_16_170402.png

gpio_io_o を LD に名前を変更する。
gpio_io_o[3:0]ポートをクリックした。
PYNQ_LED_test_16_170402.png

External Port Properties の Name を LD に変更した。
PYNQ_LED_test_17_170402.png

ブロックデザイン上で gpio_io_o[3:0] が LD[3:0] に変わった。
PYNQ_LED_test_18_170402.png

Validate Design アイコンをクリックして、デザインが正しいかどうか?を確認した。
PYNQ_LED_test_19_170402.png

Validation が成功した。
Address Editor を示す。
PYNQ_LED_test_20_170402.png

pynq_led_test ブロックデザインを右クリックし、右クリックメニューから Create HDLWapper... を選択した。
PYNQ_LED_test_21_170402.png

デフォルトのまま、OKボタンクリックした。
PYNQ_LED_test_22_170402.png

pynq_led_test_wrapper.v が生成された。
Synthesis -> Run Synthesis をクリックして、論理合成を行った。
PYNQ_LED_test_23_170402.png
  1. 2017年04月02日 11:04 |
  2. PYNQ
  3. | トラックバック:0
  4. | コメント:0