FC2カウンター FPGAの部屋 2015年05月

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

FPGAの部屋

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

Parallella Technical Conference in Tokyo に行ってきました

昨日になりますが、Parallella Technical Conference in Tokyo に行ってきました。

First Parallella Technical Conference (PTC) to be Held in Tokyo
Parallella Technical Conference in Tokyo のご案内
”Parallella Technical Conference 開催について

プログラムは、”First Parallella Technical Conference (PTC) to be Held in Tokyo”に載っていますが、タイトルが違っていたり、順番が違っていたりしていますね。
後で、スライドが公開されるそうなので、それを見て下さい。

会場のDMM.make の作りこみ感凄いですね。ディズニーランドのビッグサンダーマウンテンに乗るまでにいろいろとオブジェがあるじゃないですか?そんな感じでした。しかし、ディズニーランドの作り物感漂う雰囲気とは違って、本物ですね。床は本物の無垢の木ですし、カウンターの装飾は革が貼ってあるのかな?とにかく凄いですね。オシャレです。。。

同時抄訳者が付いていましたが、英語がわからないので雰囲気を楽しんでいました。流れについては、Vengineerさんのツィッターのまとめ”Parallella Technical Conference in Tokyo”をどうぞ。

プロジェクターが言うことを聞かないので手こずっていましたね。Andreas Olofsson さんのお話長かったです。
途中、危機があったけど、投資してもらったので、息を吹き返したそうです。良かったです。これが無かったら私のところにParallella送られてこなかったですね。。。
そうだ、皆ケースを作ろうよという話があったけど、私のParallellaをケース付きで持って行って見せれば良かった。残念。

筑波大学の山際先生の英語はわかりやすかった。日本人の英語はわかりやすいけど、英語ネイティブの人が何言っているかよくわからない。。。

Patrick Cazeles さんが”High end audio playback with the Parallella”というタイトルで、日本語で発表していました。とっても日本語を頑張っていたのが印象的。見習いたい。内容はZynqを使って、音楽プレーヤを作ったというもので、Epiphanyはつかってなかったです。

MIPI インターフェースの話がありましたが、XilinxのXAPP894 (v1.0) 2014 年 8 月 25 日、D‐PHY ソリューションにもMIPIのインターフェース方法が載っています。これで、Raspberry Pi カメラもバッチリなのかな?やってみたこと無いけど?

懇親会は1000円で料理がついて、飲み放題なのかな?1杯しか飲まなかったけど。。。いろいろな方とお話ができて楽しかったです。イケメンT氏も来ていて、お話ができました。ワインでだいぶ顔が赤くなっていましたね。TK大の2人ともお話ができて良かったです。楽しくお話していたところ、M8.5の地震が来ました。12階はかなり揺れました。エレベーターが止まってしまったので、階段で下まで降りました。
TXののりばに行ってみると、止まっていたが、そろそろ動くというので乗っていると4分遅れで発車しました。良かったです。
山手線、京浜東北線は止まっていて、帰る方は大変だったようです。お疲れ様でした。。。

Parallella Technical Conference in Tokyo の写真を貼っておきます。
Parallella_Tokyo_150607.jpg
  1. 2015年05月31日 20:06 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で log() を高位合成

Vivado HLS で log() を高位合成してみた。

使用するC++ソースコード、handshake_test.cpp はこれだ。

//
// handshake_test.cpp
// 2015/05/24
// by marsee
//

#include <stdio.h>
#include <string.h>
#include <cmath>

void handshake_test(float cntin, float *cntout){
#pragma HLS PIPELINE
#pragma HLS INTERFACE ap_hs register port=cntin
#pragma HLS INTERFACE ap_hs register port=cntout
#pragma HLS INTERFACE ap_ctrl_none port=return
    *cntout = log(cntin);
}


最初に Vivado HLS 2014.4 で 10 ns のクロック周期(100 MHz)で高位合成を行った。その結果を示す。
C2HDL_64_150531.png
レイテンシは 34 クロックで cos() ほどレイテンシが大きくない。

C2HDL_65_150531.png
BRAM_18Kは 0 個、DSP48E 61 個、FFは 2366 個、LUTは1925 個が必要だ。DSP48Eの使用率が高い。

次に、クロック周期を 50 ns にしてみた。つまりクロック周波数は 20 MHz だ。高位合成の結果を下に示す。
C2HDL_66_150531.png
レイテンシは 8 クロックに減少した。

C2HDL_67_150531.png
リソース使用量はFF が約半分になった。LUTも少し減っているが、DSP48E の使用量は変化がない。


次に、Vivado HLS 2015.1 で同じC++ソースコードを高位合成した。
クロック周期 10 ns に設定した時に高位合成結果を下に示す。
C2HDL_68_150531.png
レイテンシは 34 クロックで、Vivado HLS 2014.4 と同じだった。

C2HDL_69_150531.png
BRAM_18Kは 0 個、DSP48E 61 個、FFは 2366 個、LUTは1925 個が必要だ。DSP48Eの使用率が高い。この結果はVivado HLS 2014.4 と同じだ。

次に、クロック周期を 50 ns、つまり動作周波数を 20 MHz に設定した。高位合成結果を下に示す。
C2HDL_70_150531.png
Vivado HLS 2014.4 と同じで、レイテンシは 8 クロックに減少した。

C2HDL_71_150531.png
リソース使用量はFF が約半分になった。LUTも少し減っているが、DSP48E の使用量は変化がない。これもVivado HLS 2014.4 と同じだ。

log() に関しては、Vivado HLS 2014.4 と Vivado HLS 2015.1 の高位合成結果は同じになるようだ。log() のリソース使用量は cos() に比べて大幅に少なかった。
Vivado HLS 2014.4 と Vivado HLS 2015.1 の高位合成結果のVerilog ファイルを WinMerge で比較しても、ヘッダ部分のみ違っているが、他のVerilog HDL コード本体は同一だった。
  1. 2015年05月31日 05:21 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で cos() を高位合成

Vivado HLS で cos() を高位合成してみた。

使用するC++ソースコード、handshake_test.cpp はこれだ。

//
// handshake_test.cpp
// 2015/05/24
// by marsee
//

#include <stdio.h>
#include <string.h>
#include <cmath>

void handshake_test(float cntin, float *cntout){
#pragma HLS PIPELINE
#pragma HLS INTERFACE ap_hs register port=cntin
#pragma HLS INTERFACE ap_hs register port=cntout
#pragma HLS INTERFACE ap_ctrl_none port=return
    *cntout = cos(cntin);
}


最初に Vivado HLS 2014.4 で 10 ns のクロック周期(100 MHz)で高位合成を行った。その結果を示す。
C2HDL_56_150530.png
なんとレイテンシは 102 クロックだった。

C2HDL_57_150530.png
BRAM_18Kは 8 個、DSP48E 49 個、FFは 28333 個、LUTは63597 個が必要だ。LUTに至っては使用率が 355 % で、つまり、ZYBOでは cos() は実装できない。Zynq7030 でやっとか?

次に、クロック周期を 50 ns にしてみた。つまりクロック周波数は 20 MHz だ。高位合成の結果を下に示す。
C2HDL_58_150530.png
レイテンシは16 クロックになった。

C2HDL_59_150530.png
リソースはFFが大幅に減って、LUTも減っていたが、まだ ZYBO には入らない。


次に、Vivado HLS 2015.1 で同じC++ソースコードを高位合成した。
クロック周期 10 ns に設定した時に高位合成結果を下に示す。
C2HDL_60_150530.png
レイテンシは 100 クロックだった。Vivado HLS 2014.4 から 2 クロック減っている。

C2HDL_61_150530.png
BRAM_18Kは 8 個、DSP48E 49 個、FFは 28174 個、LUTは62161 個が必要だ。LUTに至っては使用率が 353 % で、つまり、ZYBOでは cos() は実装できない。それでも Vivado HLS 2014.4 と比べると、LUTが 1436 個少ない。他は同じだ。

次に、クロック周期を 50 ns、つまり動作周波数を 20 MHz に設定した。高位合成結果を下に示す。
C2HDL_62_150530.png
Vivado HLS 2014.4 同様の 16 クロックだった。

C2HDL_63_150530.png
やはり、リソースはFFが大幅に減って、LUTも減っていたが、まだ ZYBO には入らない。
  1. 2015年05月30日 04:16 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム2(Vivado 2015.1でプロジェクトを作製)

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム1(構想編)”の続き。

前回はだいぶ前になるが、必要なIPが全て揃ったので、Vivado 2015.1で V_ZYBO_CAMDS_151 を作製して、AXI4-Stream版ラプラシアンフィルタ IP を使用したカメラ表示システムを作っていくことにした。

最初に、Vivado 2015.1で空の V_ZYBO_CAMDS_151 プロジェクトを作製した。これはZYBO 用のプロジェクトだ。
V_ZYBO_CAMD_151_1_150528.png

V_ZYBO_CAMDS_151 フォルダに cam_inf_d111_aixs_151 フォルダを作製して、”カメラ-AXI4-Stream出力IPの作製5(IP化)”でIP化した marsee_user_mt9d111_inf_axis_1.0.zip の中身をコピーした。
V_ZYBO_CAMD_151_2_150528.png

V_ZYBO_CAMDS_151 フォルダに axis_switcher フォルダを作製して、”AXI4-Stream Switcher IP の製作3(C/RTLコシミュレーションとIP化)”でIP化した xilinx_com_hls_axis_switcher_1_0.zip の中身をコピーした。
V_ZYBO_CAMD_151_3_150528.png

V_ZYBO_CAMDS_151 フォルダに lap_filter_axis_144 フォルダを作製して、”Vivado HLS 2014.4 でAXI4-Stream版ラプラシアンフィルタIP を作製する5(IP化)”でIP化した xilinx_com_hls_lap_filter_axis_1_0.zip の中身をコピーした。
V_ZYBO_CAMD_151_10_150528.png

左のFlow Navigator ウインドウでProject Manager のIP Catalog をクリックして、右上にIP Catalog を表示した。

IP Catalog ウインドウで右クリックし、右クリックメニューからIP Settings... を選択した。
V_ZYBO_CAMD_151_4_150528.png

Project Setting ダイアログのIP -> Repository Manager が表示された。

IP Repositories ウインドウで + ボタンをクリックしてリポジトリを追加する。
V_ZYBO_CAMD_151_5_150528.png

Z:\V_ZYBO_CAMDS_151\axis_switcher を選択して、Select ボタンをクリックした。
V_ZYBO_CAMD_151_6_150528.png

IP Repositories ウインドウに axis_switcher が追加され、IP in Selected Repository に Axis_switcher が登録された。
V_ZYBO_CAMD_151_7_150528.png

同様に、IP in Selected Repository に mt9d111_inf_axis_v1_0 を追加した。
V_ZYBO_CAMD_151_8_150528.png

同様に、IP in Selected Repository に Lap_filter_axis を追加した。
V_ZYBO_CAMD_151_11_150528.png

追加した3つのIPがIP Catalog に表示された。
V_ZYBO_CAMD_151_9_150528.png
  1. 2015年05月29日 04:58 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

カメラ-AXI4-Stream出力IPの作製5(IP化)

カメラ-AXI4-Stream出力IPの作製4(シミュレーション用HDLソースの公開)”の続き。

カメラ-AXI4-Stream出力IPのプロジェクトをIP化していなかったので、IP化を行った。
Vivado 2015.1 でIP化したことがないので、丁寧に説明していく。

カメラ-AXI4-Stream出力IPのプロジェクトを示す。
Cam_inf_AXIS_8_150528.png

Tools メニューから Create and Package IP... を選択する。

Create and Package New IP ダイアログが表示された。Next > ボタンをクリックした。
Cam_inf_AXIS_9_150528.png

Create Peripheral, Package IP or Package a Block Design では、Package a block が増えていると思う。
デフォルトのまま Next > ボタンをクリックした。
Cam_inf_AXIS_10_150528.png

Package Your Current Project が表示された。Next > ボタンをクリックした。
Cam_inf_AXIS_11_150528.png

New IP Creation が表示された。Finish ボタンをクリックした。
Cam_inf_AXIS_12_150528.png

Package IP ウインドウが表示された。
Cam_inf_AXIS_13_150528.png

Package IP ウインドウの Identification の Vender を marsee に、Vendor display name も marsee に、Company url は http://marsee101.blog19.fc2.com/ に変更した。
Cam_inf_AXIS_14_150528.png

Compatibility 画面。
Cam_inf_AXIS_15_150528.png

File Groups 画面。
Cam_inf_AXIS_16_150528.png

Customization Parameters 画面。
Cam_inf_AXIS_17_150528.png

Ports and Interfaces 画面。
Cam_inf_AXIS_18_150528.png

Addressing and Memory 画面。
Cam_inf_AXIS_19_150528.png

Customization GUI 画面。
Cam_inf_AXIS_20_150528.png

Review and Package 画面で edit packaging settings をクリックした。
Cam_inf_AXIS_21_150528.png

Project Settings ダイアログが表示された。Automatic Behavior -> After Packaging -> Create archive of IP のチェックボックスをチェックした。これをチェックすると、ZIP圧縮されたIPファイルが生成される
Cam_inf_AXIS_22_150528.png

Review and Package 画面に戻り、Package IP ボタンをクリックした。
Cam_inf_AXIS_23_150528.png

IPが成功したというダイアログが出た。
Cam_inf_AXIS_24_150528.png

IP Package 後のプロジェクトを下に示す。
Cam_inf_AXIS_25_150528.png

cam_inf_d111_axis_151 フォルダの下に、ZIP圧縮されたIPファイルの marsee_user_mt9d111_inf_axis_1.0.zip が生成されていた。
Cam_inf_AXIS_26_150528.png
  1. 2015年05月28日 04:40 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

FPGAの部屋のまとめサイトの更新(2015年5月27日)

FPGAの部屋のまとめサイトを更新しました。

追加したカテゴリはありませんでした。Vivado HLS の記事がとっても多かったです。
  1. 2015年05月27日 04:19 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:0

藤沢邸バラ園に行ってきました

5月23日の土曜日ですが、つくば市の藤沢邸バラ園に行ってきました。

元つくば市長さんのお宅をバラ園として無料で開放しているそうです。初めて行ってみましたがとっても綺麗です。大見クリニックの近くです。

たくさんバラのアーチがあったり、休憩所があったり、滑り台があったりで、とても個人の家の庭とは思えません。
rose_1_150525.jpg

rose_2_150525.jpg

rose_3_150525.jpg

いろいろな種類の薔薇がこれでもかというほど咲いていて、とっても綺麗です。
rose_4_150525.jpg

rose_5_150525.jpg

rose_6_150525.jpg

庭が2つに分かれています。ハーブや紫色のネギ坊主のようなのもありました。
rose_7_150525.jpg

rose_8_150525.jpg

rose_9_150525.jpg

rose_10_150525.jpg

彫像も何体か置いてありましたよ。本当に個人宅とは思えませんね。。。
rose_11_150525.jpg

バラの花と香りを堪能して帰って来ました。
奥さんはピンクと黄色のバラを2本買ってきました。奥で販売もしています。1本1000円でした。育ち加減でいろいろな値段のバラが売っています。1000円が一番安かったです。
  1. 2015年05月25日 03:35 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSの高位合成結果をHDLとして利用する6(sqrt() その2)

Vivado HLSの高位合成結果をHDLとして利用する5(sqrt())”の続き。

前回は平方根処理の Latency が 39 クロックも掛かっていた。これは倍精度小数点演算を行っていたからのようだ。最初に倍精度小数点にunsigned int から変換した後に、倍精度で平方根の演算をしているようだった。
今回は、倍精度小数点演算から単精度浮動小数点演算にしてみようと思う。
なお、Vivado HLS 2014.4 を使用する。

今回、単精度浮動小数点演算になったC++ソースコードを下に示す。

//
// handshake_test.cpp
// 2015/05/17
// by marsee
//

#include <stdio.h>
#include <string.h>
#include <ap_int.h>
#include <cmath>

void handshake_test(ap_uint<8> cntin, ap_uint<8> *cntout){
#pragma HLS PIPELINE
#pragma HLS INTERFACE ap_hs register port=cntin
#pragma HLS INTERFACE ap_hs register port=cntout
#pragma HLS INTERFACE ap_ctrl_none port=return

    float fsqrt_data;

    fsqrt_data = sqrt((float)cntin);
    *cntout = (ap_int<8>)fsqrt_data;
}


これで高位合成を行った結果を下に示す。
C2HDL_47_150524.png

Latency は 倍精度の39 クロックから 20 クロックになった。

ちなみに単精度浮動小数点演算だと、リソース使用量も少ない。LUTの使用量は倍精度の42%程度だった。
C2HDL_48_150524.png

Analysis 画面を下に示す。uitofp と fsqrt が使われているのが分かる。
C2HDL_49_150524.png

ちなみに、このC++ ソースコードでは、単精度浮動小数点演算にならなかった。

*cntout = (ap_uint<8>)sqrt((float)cntin);

これでは倍精度浮動小数点演算だった。
C2HDL_46_150524.png


単精度浮動小数点の平方根をやってみた。
C++ ソースコードを下に示す。

//
// handshake_test.cpp
// 2015/05/24
// by marsee
//

#include <stdio.h>
#include <string.h>
#include <cmath>

void handshake_test(float cntin, float *cntout){
#pragma HLS PIPELINE
#pragma HLS INTERFACE ap_hs register port=cntin
#pragma HLS INTERFACE ap_hs register port=cntout
#pragma HLS INTERFACE ap_ctrl_none port=return
    *cntout = sqrt(cntin);
}


テストベンチを下に示す。

//
// handshake_test_tb.cpp
// 2015/05/24
// by marsee
//

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

void handshake_test(float cntin, float *cntout);

int main(){
    using namespace std;

    float result;
    float *cntout;

    cntout = &result;
    for(int i=0; i<10; i++){
        handshake_test((float)i, cntout);
        cout << "i = " << i << "  " << "result = " << result << endl;
    }

    return 0;
}


Cシミュレーション結果を下に示す。
C2HDL_50_150524.png

高位合成結果を下に示す。
C2HDL_53_150524.png

Latency は 13 クロックだった。

LUT使用量も unsigned int へのキャスト + 倍精度浮動小数点の平方根演算の 18% だった。
C2HDL_52_150524.png

C/RTLコシミュレーションを行った。
C2HDL_54_150524.png

Vivado 2015.1 を立ちあげて、例によってTCLコマンドとして、以下のコマンドを入力した。

cd C:/Users/Masaaki/Documents/Vivado_HLS/study/handshake_test/solution1/sim/verilog
current_fileset
open_wave_database handshake_test.wdb
open_wave_config handshake_test.wcfg

C/RTL コシミュレーション波形が表示された。
C2HDL_55_150524.png

Latency は 13 クロックだった。
  1. 2015年05月24日 05:29 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSの高位合成結果をHDLとして利用する5(sqrt())

Vivado HLSの高位合成結果をHDLとして利用する4(register その2、ap_hs)”の続き。

今まで、Vivado HLS 2014.4 の高位合成結果をHDLとして利用する方法を見てきた。やっていることは入力データを +1 して出力データとして出力する単純な計算だったので、一度、sqrt() をやってみることにした。

下に新しい handshake_test.cpp を示す。

//
// handshake_test.cpp
// 2015/05/17
// by marsee
//

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

void handshake_test(ap_uint<8> cntin, ap_uint<8> *cntout){
#pragma HLS PIPELINE
#pragma HLS INTERFACE ap_hs register port=cntin
#pragma HLS INTERFACE ap_hs register port=cntout
#pragma HLS INTERFACE ap_ctrl_none port=return
    *cntout = sqrt(cntin);
}


新しい handshake_test_tb.cpp を示す。

//
// handshake_test_tb.cpp
// 2015/05/18
// by marsee
//

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

void handshake_test(ap_uint<8> cntin, ap_uint<8> *cntout);

int main(){
    using namespace std;

    ap_uint<8> result;
    ap_uint<8> *cntout;

    cntout = &result;
    for(int i=0; i<10; i++){
        handshake_test((ap_uint<8>)i, cntout);
        cout << "i = " << i << "  " << "result = " << result << endl;
    }

    return 0;
}


Run C Simulation ボタンをクリックして、Cシミュレーションを行った。
C2HDL_38_150522.png

きちんと値が出ている。

C Synthesis ボタンをクリックして、高位合成を行った。
C2HDL_40_150522.png

Target の 10 ns に対して、Estimated は 8.62 ns で、Latency は 39 クロックで、Interval は 1 クロックとなった。Latency が大幅に増えている。

使用リソースを見た。
C2HDL_43_150522.png

FFが 2429 で、LUT が 3376 だった。+1 の演算と比べて二桁以上多い。

Analysis 画面を見た。
C2HDL_41_150522.png

C2HDL_42_150522.png

dsqrt はIPを使用している。
(2015/02/24 :追記) dsqrt はユーザーズガイドによると倍精度浮動小数点の平方根だそうです。倍精度になっていたので、クロック数が掛かるようです。
uitodp は、unsigned int から 倍精度浮動小数点型へのキャスト用だと思われます。

高位合成結果の handshake_test.v を見た。前回と同じポートだった。
C2HDL_44_150522.png

Run C/RTL Cosimulation ボタンをクリックして、C/RTL コシミュレーションを行った。
C2HDL_39_150522.png

Vivado 2015.1 を立ちあげて、例によってTCLコマンドを入力して、C/RTL コシミュレーション波形を表示させた。
C2HDL_45_150522.png

データを入力してから sqrt() 演算の結果が出るまでに 390 ns かかっている。クロック周期は10 ns なので、つまり、レイテンシとして 39 クロックということになる。

レイテンシは大きいし、コアも大きいが手軽に sqrt() を書けて、高位合成できるのは良いと思う。
但し、このくらいだったらROMテーブルで sqrt() の値を持っていたほうが良いと思うけれど。。。
  1. 2015年05月23日 05:24 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSの高位合成結果をHDLとして利用する4(register その2、ap_hs)

Vivado HLSの高位合成結果をHDLとして利用する3(register その1)”の続き。

前回は、INTERFACE ディレクティブに register オプションを付けることで、入出力ポートにFFを付けた。これは、IPとして必要な処理を行ったということだ。レイテンシは大きくなるが、動作周波数は上がる可能性がある。

register オプションについては、ツィッターで tu1978 さんと Vengineer さんに教えて頂きました。ありがとうございました。


入出力ポートにFFを付けるというか、組み合わせ回路で無くなると、入力データを入れてから何クロック後に出力データが出てくるかが問題となる。ap_none では、入力データを入れてクロックから正しい出力データが出てくるクロックをカウントして、出力データを受ける必要がある。つまり、自分でクロックをカウントする必要がある。
そこで、制御信号が出ているINTERFACE として、ap_vld と ap_hs がある。ap_vld はvalid 信号のみがあるINTERFACE で、ap_hs は valid 信号と ack 信号があるINTERFACE となっている。

さて、それでは、前回の続きということで、ブロックレベルのINTERFACE を ap_ctrl_none に戻して、PIPELINE ディレクティブを追加してやってみよう。入出力ポートのINTERFACE は ap_none のままだ。
C2HDL_27_150520.png

C Synthesis ボタンをクリックして、高位合成を行った。
C2HDL_35_150521.png

Target の 10 ns に対して、Estimated は 1.72 ns で、前回の時と変わらない。Latency は 2 クロックで、Interval は 1 クロックとなった。

Run C/RTL Cosimulation ボタンをクリックして、C/RTL コシミュレーションを行った。
C2HDL_28_150520.png

Vivado 2015.1 を立ちあげて、例によってTCLコマンドとして、以下のコマンドを入力した。

cd C:/Users/Masaaki/Documents/Vivado_HLS/study/handshake_test/solution1/sim/verilog
current_fileset
open_wave_database handshake_test.wdb
open_wave_config handshake_test.wcfg

C/RTL コシミュレーション波形が表示された。
C2HDL_29_150521.png

上の図の cntin_V と cntout_V 信号を見ると、cntin_V 入力FF にデータがラッチされてから、2 クロック後に cntout_V に出力されている。

次に、PIPELINE ディレクティブはそのままに、 ap_none を valid 信号と ack 信号があるINTERFACE の ap_hs に変更した。
C2HDL_30_150521.png

C Synthesis ボタンをクリックして、高位合成を行った。
C2HDL_31_150521.png

Target の 10 ns に対して、Estimated は 1.72 ns で、Latency は 2 クロックで、Interval は 1 クロックとなった。これは、前回の時と変わらない値だ。

高位合成結果の handshake_test.v を見ると、ポートとして、

cntin_V_ap_vld
cntin_V_ap_ack
cntout_V_ap_vld
cntout_V_ap_ack

が追加されているのが分かる。
C2HDL_32_150521.png

Run C/RTL Cosimulation ボタンをクリックして、C/RTL コシミュレーションを行った。
C2HDL_33_150521.png

Vivado 2015.1 を立ちあげて、例によってTCLコマンドを入力して、C/RTL コシミュレーション波形を表示させた。
C2HDL_34_150521.png

入力も出力も vld と ack が両方共 1 になった時が、データが有効な時だ。入力されてから 2 クロック後に出力されているのが分かる。

これだけでは、vld と ack の関係を確かめることができないので、Vivado 2015.1 でプロジェクトを作製して、高位合成された handshake_test.v をプロジェクトに入れて、テストベンチを作製してシミュレーションしてみた。cntin_V_ap_vld と cntout_V_ap_ack は M系列を使用してランダム値を入力した。
シミュレーションした結果を下に示す。
C2HDL_36_150522.png

上の波形の一部を拡大する。
C2HDL_37_150522.png

vld, ack とデータの関係が良くわかると思う。

最後に、テストベンチ handshake_test_tb.v を貼っておく。

// 
// handshake_test_tb.v
// 2015/05/18
// by marsee
//

`default_nettype none

`timescale 100ps / 1ps

module handshake_test_tb;

    function [7:0] mseqf8_0(input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[3] ^ din[2] ^ din[1];
            mseqf8_0 = {din[6:0], xor_result};
        end
    endfunction

    function [7:0] mseqf8_1(input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[5] ^ din[4] ^ din[1];
            mseqf8_1 = {din[6:0], xor_result};
        end
    endfunction

    wire ap_clk;
    wire ap_rst;
    reg [7:0] cntin_V;
    wire cntin_V_ap_vld;
    wire cntin_V_ap_ack;
    wire [7:0] cntout_V;
    wire cntout_V_ap_vld;
    wire cntout_V_ap_ack;
    reg [7:0] random8_0;
    reg [7:0] random8_1;

    handshake_test uut (
        .ap_clk(ap_clk),
        .ap_rst(ap_rst),
        .cntin_V(cntin_V),
        .cntin_V_ap_vld(cntin_V_ap_vld),
        .cntin_V_ap_ack(cntin_V_ap_ack),
        .cntout_V(cntout_V),
        .cntout_V_ap_vld(cntout_V_ap_vld),
        .cntout_V_ap_ack(cntout_V_ap_ack)
    );

    always @(posedge ap_clk) begin
        if (ap_rst) begin
            random8_0 <= 8'd1;
        end else begin
            random8_0 <= mseqf8_0(random8_0);
        end
    end
    always @(posedge ap_clk) begin
        if (ap_rst) begin
            random8_1 <= 8'd1;
        end else begin
            random8_1 <= mseqf8_1(random8_1);
        end
    end

    assign  cntin_V_ap_vld = random8_0[0];
    assign  cntout_V_ap_ack = random8_1[0];

    always @(posedge ap_clk) begin
        if (ap_rst) begin
            cntin_V <= 8'd0;
        end else if (cntin_V_ap_vld & cntin_V_ap_ack) begin
            cntin_V <= cntin_V + 8'd1;
        end
    end

    // ap_clk
    clk_gen #(
        .CLK_PERIOD(100),    // 10.0nsec, 100MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) ap_clk_i (
        .clk_out(ap_clk)
    );

    // ap_rst
    reset_gen #(
        .RESET_STATE(1'b1),
        .RESET_TIME(1000)    // 100nsec
    ) RESETi (
        .reset_out(ap_rst)
    );
    
endmodule

module clk_gen #(
    parameter         CLK_PERIOD = 100,
    parameter real    CLK_DUTY_CYCLE = 0.5,
    parameter        CLK_OFFSET = 0,
    parameter        START_STATE    = 1'b0 )
(
    output    reg        clk_out
);
    begin
        initial begin
            #CLK_OFFSET;
            forever
            begin
                clk_out = START_STATE;
                #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk_out = ~START_STATE;
                #(CLK_PERIOD*CLK_DUTY_CYCLE);
            end
        end
    end
endmodule

module reset_gen #(
    parameter    RESET_STATE = 1'b1,
    parameter    RESET_TIME = 100 )
(
    output    reg        reset_out
);
    begin
        initial begin
            reset_out = RESET_STATE;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
        end
    end
endmodule

`default_nettype wire

  1. 2015年05月22日 04:06 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSの高位合成結果をHDLとして利用する3(register その1)

Vivado HLSの高位合成結果をHDLとして利用する2(ap_ctrl_none)”の続き。

前回は、組み合わせ回路をHDLソースコードとして使えるように、ap_ctrl_none を使用してブロックレベルの制御信号 ( ap_start, ap_idle, ap_ready, ap_done ) を削除した。
今回は、INTERFACE ディレクティブに register オプションを入れて、I/O の入力をFFで受けて、出力をFFで出力する。

register オプションについては、ツィッターで tu1978 さんと Vengineer さんに教えて頂きました。ありがとうございました。


使用するのは、Vivado HLS 2014.4 だ。
最初に、”#pragma HLS INTERFACE ap_hs port=cntin”ディレクティブを右クリックし、右クリックメニューから Modify Directive を選択する。
C2HDL_14_150520.png

Vivado HLS Directive Editor が表示される。
register (optional) をチェックする。
C2HDL_15_150520.png

”#pragma HLS INTERFACE ap_hs port=cntout”ディレクティブを右クリックし、右クリックメニューから Modify Directive を選択する。

Vivado HLS Directive Editor が表示される。
register (optional) をチェックする。
C2HDL_16_150520.png

2 つのディレクティブに register が追加された。

#pragma HLS INTERFACE ap_none register port=cntin
#pragma HLS INTERFACE ap_none register port=cntout

C2HDL_17_150520.png

C Synthesis ボタンをクリックして、高位合成を行った。
C2HDL_18_150520.png

Target の 10 ns に対して、Estimated は 1.72 ns で、前回の組み合わせ回路の時と変わらない。Latency は 2 クロックで、Interval は 3 クロックだった。

高位合成された handshake_test.v を見ると ap_clk, ap_rst が追加されている。
cntin_V は FF 入力、cntout_V は FF 出力だった。
C2HDL_19_150520.png

Analysis 画面、FF入力、演算、FF出力になっている。
C2HDL_20_150520.png

Run C/RTL Cosimulation ボタンをクリックして、C/RTL コシミュレーションを行う。

Co-Simulation Dialog が表示された。
Dump Trace を all に変更して、OKボタンをクリックした。
C2HDL_21_150520.png

C/RTL コシミュレーションはエラーだった。エラー内容を下に示す。

@E [SIM-345] Cosim only supports the following 'ap_ctrl_none' designs: (1) combinational designs; (2) pipelined design with task inteveral of 1; (3) designs with array streaming or hls_stream or AXI4 stream ports.
@E [SIM-4] *** C/RTL co-simulation finished: FAIL ***

while executing
"cosim_design -trace_level all"
(file "C:/Users/Masaaki/Documents/Vivado_HLS/study/handshake_test/solution1/cosim.tcl" line 8)

C2HDL_22_150520.png

ブロックレベルのインターフェースを ap_ctrl_hs に戻せば、C/RTL コシミュレーションができるようだ。

ブロックレベルのインターフェースを ap_ctrl_none から ap_ctrl_hs に変更した。
C2HDL_24_150520.png

C Synthesis ボタンをクリックして、高位合成を行ってから、Run C/RTL Cosimulation ボタンをクリックして、C/RTL コシミュレーションを行った。今度は問題なく終了した。
C2HDL_25_150520.png

Vivado 2015.1 を立ちあげて、例によってTCLコマンドとして、以下のコマンドを入力した。

cd C:/Users/Masaaki/Documents/Vivado_HLS/study/handshake_test/solution1/sim/verilog
current_fileset
open_wave_database handshake_test.wdb
open_wave_config handshake_test.wcfg

、C/RTL コシミュレーション波形が表示された。
C2HDL_26_150520.png

ap_start がアサートされてから 3 クロック目に ap_done がアサートされて、cntout_V 出力に正しい出力が出力されたのが分かる。順次、3クロックごとに ap_done がアサートされて、cntout_V 出力に出力されていく。

最後に C++ のテストベンチの handshake_test_tb.cpp を貼っておく。

//
// handshake_test_tb.cpp
// 2015/05/18
// by marsee
//

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

void handshake_test(ap_int<8> cntin, ap_int<8> *cntout);

int main(){
    using namespace std;

    ap_int<8> result;
    ap_int<8> *cntout;

    cntout = &result;
    for(int i=0; i<10; i++){
        handshake_test((ap_int<8>)i, cntout);
        cout << "i = " << i << "  " << "result = " << result << endl;
    }

    return 0;
}

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

Vivado HLSの高位合成結果をHDLとして利用する2(ap_ctrl_none)

Vivado HLSの高位合成結果をHDLとして利用する1(準備編)”の続き。

今回は、ap_ctrl_none を利用して、よりHDLソースコードとして使えるようにしていく。使用するVivado HLS のバージョンは 2014.4 だ。
ブロックレベルのインターフェースを ap_ctrl_none にすると、ap_start, ap_idle, ap_ready, ap_done 信号が無くなって、よりHDLソースコードとして利用しやすくなる。

さて、handshake_test を ap_ctrl_none に設定しよう。
右のウインドウのDirective タブをクリックして、handshake_test を右 クリックし、Insert Directive を選択する。
C2HDL_7_150519.png

Vivado HLS Directive Editor が表示される。
Directive をINTERFACE に、Destination を Source File に(これは、Cソースコードにディレクティブを入れるためだ)、Options の mode で ap_ctrl_none を選択する。
C2HDL_8_150519.png

Cソースコードに

#pragma HLS INTERFACE ap_ctrl_none port=return

が挿入された。
C2HDL_9_150519.png

次に、cntout 出力のインターフェースをデフォルトの ap_vld から ap_none に変更しよう。これは、組み合わせ回路で、すぐに出力が出るので、vld のポートが要らないためだ。

右のウインドウの Directive タブで、cntout を右クリックして、Insert Directive を選択する。

Vivado HLS Directive Editor が表示される。
Directive をINTERFACE に、Destination を Source File に(これは、Cソースコードにディレクティブを入れるためだ)、Options の mode で ap_none を選択する。
C2HDL_10_150519.png

同様に、デフォルトではあるが、あえて明示するために、cntin も ap_none に指定した。
C2HDL_11_150519.png

Cソースコードは下の図の様になった。
C2HDL_12_150519.png

現在の handshake_test.cpp を下に示す。

//
// handshake_test.cpp
// 2015/05/17
// by marsee
//

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

void handshake_test(ap_int<8> cntin, ap_int<8> *cntout){
#pragma HLS INTERFACE ap_none port=cntin
#pragma HLS INTERFACE ap_none port=cntout
#pragma HLS INTERFACE ap_ctrl_none port=return
    *cntout = cntin+1;
}


これで、高位合成を行った。
C2HDL_13_150519.png

高位合成結果の Verilog HDLコードを見ると、、ap_start, ap_idle, ap_ready, ap_done 信号が無くなっているのが分かる。

// ==============================================================
// RTL generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
// Version: 2014.4
// Copyright (C) 2014 Xilinx Inc. All rights reserved.
// 
// ===========================================================

`timescale 1 ns / 1 ps 

(* CORE_GENERATION_INFO="handshake_test,hls_ip_2014_4,{HLS_INPUT_TYPE=cxx,HLS_INPUT_FLOAT=0,HLS_INPUT_FIXED=1,HLS_INPUT_PART=xc7z010clg400-1,HLS_INPUT_CLOCK=10.000000,HLS_INPUT_ARCH=others,HLS_SYN_CLOCK=1.720000,HLS_SYN_LAT=0,HLS_SYN_TPT=none,HLS_SYN_MEM=0,HLS_SYN_DSP=0,HLS_SYN_FF=0,HLS_SYN_LUT=8}" *)

module handshake_test (
        cntin_V,
        cntout_V
);

parameter    ap_true = 1'b1;
parameter    ap_const_lv8_1 = 8'b1;
parameter    ap_const_logic_1 = 1'b1;
parameter    ap_const_logic_0 = 1'b0;

input  [7:0] cntin_V;
output  [7:0] cntout_V;




assign cntout_V = (cntin_V + ap_const_lv8_1);


endmodule //handshake_test


  1. 2015年05月20日 04:45 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLSの高位合成結果をHDLとして利用する1(準備編)

Vivado HLS はAXI4バスのプロトコルを C や C++ から、とっても便利に使用することができる。コードをろくに書かなくてもAXI4バスの機能を使うことができるのをこのブログでも書いてきた。それじゃ、CやC++ で書いて高位合成したHDLを普通のHDLとして使うことができるかを検証していきたいと思う。

まだ、”AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム”を作っている途中ではあるが、一時中断して、こっちをやってみようと思っている。高位合成で簡単に大部分のHDLを作って楽をしたいと思っている。

”Vivado Design Suite ユーザーガイド 高位合成 UG902 (v2014.3) 2014 年 10 月 1 日”を参考にさせて頂く。

まずは、インターフェース合成時プロトコルとして、129ページの”図 1‐49 : デー タ型と インターフェイス合成のサポート”を引用させて頂く。
C2HDL_1_150519.png
D : 各型のデフォルトのインターフェースモード
I : 入力引数(Readのみ)
O : 出力引数(Write のみ)
I/O : 入出力引数(Read Write 両方)

このようにいろいろなプロトコルがサポートされている。例えば、ap_vld はvalid つまり有効信号が付いたプロトコルである。Return の通常値はap_ctrl_hs なので、通常は、ap_start, ap_idle, ap_ready, ap_done 信号が付加される。

今回の目的は、C++ で書いてHDLとして使うことを目的としている。HDLでデータのやりとりをするハンドシェーク信号を ap_??? を使って実装するようにしていけば、通常のHDLとして使えるのではないかと思っている。つまり、IPそのものをVivado HLSで生成するのでも良いのだが、IPの一部品としてのHDLコードをVivado HLSで作っていけるのではないか?ということでブログで結果を書いていきたいと思っている。

まずは、簡単な例を作って、Vivado HLSで高位合成してみよう。いろいろなプロトコルのディレクティブを付加すると、どのようにインターフェースが変わるかを見ていこう。

下に例を示す。入力した8ビットのデータ ( cntin ) を +1 して、8ビットのデータとして出力 ( cntout ) する一番簡単な C ソースコードを下に示す。

//
// handshake_test.cpp
// 2015/05/17
// by marsee
//

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

void handshake_test(ap_int<8> cntin, ap_int<8> *cntout){
    *cntout = cntin+1;
}


ZYBOのZYNQを指定してVivado HLS 2014.4 でプロジェクトを作製した。もういろいろとテストしてあるが気にしないでください。
C2HDL_2_150519.png

下に高位合成をした結果を示す。
C2HDL_3_150519.png

10 ns 周期のクロックに対して、Estimated は 1.72 ns 、Latency は 0 クロック、Interval は 1 クロックで、つまりは組み合わせ回路になっているということだ。

Analysis 画面を示す。
C2HDL_6_150519.png

生成されたHDLファイルを下に示す。ap_clk が無く、組み合わせ回路になっていることが分かる。 ( handshake_test.v )
入力の cntin は ap_none で、出力の cntout は ap_vld だった。これはデフォルト値だ。

// ==============================================================
// RTL generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
// Version: 2014.4
// Copyright (C) 2014 Xilinx Inc. All rights reserved.
// 
// ===========================================================

`timescale 1 ns / 1 ps 

(* CORE_GENERATION_INFO="handshake_test,hls_ip_2014_4,{HLS_INPUT_TYPE=cxx,HLS_INPUT_FLOAT=0,HLS_INPUT_FIXED=1,HLS_INPUT_PART=xc7z010clg400-1,HLS_INPUT_CLOCK=10.000000,HLS_INPUT_ARCH=others,HLS_SYN_CLOCK=1.720000,HLS_SYN_LAT=0,HLS_SYN_TPT=none,HLS_SYN_MEM=0,HLS_SYN_DSP=0,HLS_SYN_FF=0,HLS_SYN_LUT=8}" *)

module handshake_test (
        ap_start,
        ap_done,
        ap_idle,
        ap_ready,
        cntin_V,
        cntout_V,
        cntout_V_ap_vld
);

parameter    ap_const_logic_1 = 1'b1;
parameter    ap_const_logic_0 = 1'b0;
parameter    ap_const_lv8_1 = 8'b1;
parameter    ap_true = 1'b1;

input   ap_start;
output   ap_done;
output   ap_idle;
output   ap_ready;
input  [7:0] cntin_V;
output  [7:0] cntout_V;
output   cntout_V_ap_vld;

reg cntout_V_ap_vld;




/// cntout_V_ap_vld assign process. ///
always @ (ap_start)
begin
    if (~(ap_start == ap_const_logic_0)) begin
        cntout_V_ap_vld = ap_const_logic_1;
    end else begin
        cntout_V_ap_vld = ap_const_logic_0;
    end
end
assign ap_done = ap_start;
assign ap_idle = ap_const_logic_1;
assign ap_ready = ap_start;
assign cntout_V = (cntin_V + ap_const_lv8_1);


endmodule //handshake_test


クロック信号が無いのは、クロック周期の制約を満たせているからだ。ここで、クロック周期を今の、Estimated よりも小さい 1.5 ns にしてみよう。

高位合成結果を下に示す。
C2HDL_4_150519.png

10 ns 周期のクロックに対して、Estimated は 0.85 ns 、Latency は 1 クロック、Interval は 2 クロックで、FF が入った順序回路になっていることが分かる。
生成されたVerilog ソースコードも handshake_test.v と handshake_test_add_8ns_8ns_8_2.v の2つ生成されている。
Analysis 画面を示す。
C2HDL_5_150519.png

+ 演算に2クロックかけているようだ。

handshake_test.v を見ると ap_clk ポートが追加されていた。長いので、ポート宣言部分のみ貼っておく。

module handshake_test (
        ap_clk,
        ap_rst,
        ap_start,
        ap_done,
        ap_idle,
        ap_ready,
        cntin_V,
        cntout_V,
        cntout_V_ap_vld
);

  1. 2015年05月19日 04:29 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Lenovo G560 のキーボード交換をしました

家の奥さんが Lenovo G560 というノートパソコンを使っているのですが、キーボードが壊れちゃいました。具体的に言うとTABキーのキートップが外れちゃって、はまりません。
この機種はキーボードが壊れやすいらしく、アマゾンでも複数売っています
これが今付いているキーボードですが高いのでLenovo G560 G565A G560L Z560 G570 用日本語キーボード 25011296 G565-JP MP-10F30J0-686 を購入しました。

Lenovo G560 のキーボード交換について参考にした記事は”Lenovo G560のキーボード交換”です。参考にした動画は”LENOVO G560 キーボード外し”です。記事は全般に渡って参照して作業しました。動画は、記事では分からなかったキーボードの外し方を参照しました。ネジを外して、精密ドライバーのマイナスをキーボードの上側の爪の部分に差し込むと簡単にキーボードが外れました。

最初の行程で、Lenovo G560 を裏返しにして、ピンク矢印のネジを外しました。
Lenovo_G560_1_150517.jpg

裏蓋のネジを取って裏蓋を外しました。裏蓋の真ん中の2本のネジは外れましたが、裏蓋の他のネジは取れません。
Lenovo_G560_2_150517.jpg

FANの脇のネジを外しました。
Lenovo_G560_3_150517.jpg

Lenovo G560 をひっくり返して、精密ドライバーのマイナスをキーボードの上側の爪の部分に差し込むと簡単にキーボードが外れました。
Lenovo_G560_4_150517.jpg

キーボードのフレキシブル基板が入っているコネクタは参照動画のタイプではなく、参照記事のタイプでした。茶色い部分を精密ドライバーのマイナスで上にずらすとキーボードのフレキシブル基板が外れました。
Lenovo_G560_5_150517.jpg

キーボードが外れた状態です。
Lenovo_G560_6_150517.jpg

購入したキーボードのフレキシブル基板をコネクタに入れて、茶色の部分を慎重におろしました。その際に、キーボードのフレキシブル基板が斜めになっていないかを確認します。

キーボードをはめ込んでいくとキーボードが装着されます。
ネジや裏蓋を戻すと完成です。
Lenovo_G560_7_150517.jpg

キーボードの配列は元から付いていたものと違いますが、こっちのキー配列の方が打ちやすいです。私が触ったキーはすべて問題なく使えました。交換できてとっても良かったです。
  1. 2015年05月17日 04:53 |
  2. パソコン関連
  3. | トラックバック:0
  4. | コメント:3

AXI4-Stream Switcher IP の製作3(C/RTLコシミュレーションとIP化)

AXI4-Stream Switcher IP の製作2(Cシミュレーションと高位合成)”の続き。

前回は、Cシミュレーションと高位合成を行った。今回は、C/RTLコシミュレーションをして、その波形をVivado 2015.1で観察し、IP化を行う。

Run C/RTL Cosimulation ボタンをクリックしてC/RTLコシミュレーションを行う。

Co-simulation Dialog でDump Trace を all に変更した。これは後で、Vivado 2015.1を立ちあげて、波形を観察するためだ。
AXI4_Stream_Switcher_8_150516.png

C/RTLコシミュレーションが終了した。
AXI4_Stream_Switcher_9_150516.png

ここで、高位合成では表示されていなかった Latency と Interval が表示された。同じ値なので、完全にパイプラインされていると思う。

シミュレーション波形を表示するために、Vivado 2015.1 を立ちあげて、tclコンソールに以下のコマンドを入力した。

cd C:/Users/Masaaki/Documents/Vivado_HLS/ZYBO/axis_switcher_2014_4/solution1/sim/verilog
current_fileset
open_wave_database axis_switcher.wdb
open_wave_config axis_switcher.wcfg


波形が表示された。
AXI4_Stream_Switcher_14_150516.png

波形を拡大した。
AXI4_Stream_Switcher_10_150516.png

最初のマーカーから2番めのマーカーまでは、ins0_TDATA のM系列が outs_TDATA に出力されている。
3番目のマーカーから4番目のマーカーまでは、ins1_TDATA の矩形波が outs_TDATA に出力されている。
よって、Switcher としての機能は問題無いと思う。outs の出力を見ても、1クロックごとにデータが出力されている。完全にパイプラインされているようだ。
AXI4_Stream_Switcher_11_150516.png

次の波形を説明する前に、ドライバの xaixs_switcher_hw.h のレジスタのアドレス参照部分を下に示す。

// BUS_AXI4LS
// 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 select_r
//        bit 31~0 - select_r[31:0] (Read/Write)
// 0x1c : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)


0x18 番地が select の値を書き込むレジスタとなっている。それを踏まえて下の波形を見ていこう。
AXI4_Stream_Switcher_12_150516.png
最初の点線のカーソルの所でAXI4 Lite Slave Write アクセスで 0x18 番地に 0 を書いている。つまり、select_r に 0 を入れている。次のAXI4 Lite Slave Write アクセスで、0x00 番地に 1 を書いている。つまり、Control signals レジスタの ap_start ビットに 1 を書いている。
その後、青いカーソルのところで、ins0_TUSER が 1 にアサートされて、M系列の AXI4 Stream がスタートしている。

下の波形では、M系列の AXI4 Stream の終了が青いカーソルで示されている。1450 ns 辺りで AXI4 Lite Slave Read で 0x6 が Read できている。これは、ap_done と ap_idle が 1 ということであり、M系列の AXI4 Stream が終了したことを示している。
AXI4_Stream_Switcher_13_150516.png
次の矩形波の AXI4 Stream を始めるために、点線のカーソルの所でAXI4 Lite Slave Write アクセスで 0x18 番地に 1 を書いている。つまり、select_r に 1 を入れている。次のAXI4 Lite Slave Write アクセスで、0x00 番地に 1 を書いている。つまり、Control signals レジスタの ap_start ビットに 1 を書いている。
次の青いカーソルのところで、ins0_TUSER が 1 にアサートされて、矩形波の AXI4 Stream がスタートしている。

最後に Export RTL ボタンをクリックして IP化を行った。
AXI4_Stream_Switcher_15_150517.png
  1. 2015年05月17日 03:43 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream Switcher IP の製作2(Cシミュレーションと高位合成)

AXI4-Stream Switcher IP の製作1(CPP ソースコードの公開)”の続き。

前回、貼ったC++のソースコードを使用して、今回は、Cシミュレーションと高位合成を行う。

Run C Simulation ボタンをクリックして、Cシミュレーションを行った。
最初の outs はM系列が表示されて、2番めの outs は矩形波が表示された。
AXI4_Stream_Switcher_2_150516.png
M系列

AXI4_Stream_Switcher_3_150516.png
矩形波

Project Settings の Synthesis の Top Function を axis_switcher に設定した。
AXI4_Stream_Switcher_4_150516.png

これで、C Synthesis ボタンをクリックして高位合成を行った。
AXI4_Stream_Switcher_5_150516.png

Timing はTarget 10.00 ns に対して、Estimated が 6.55 ns だった。

右端のAnalysis ボタンをクリックして、高位合成結果の解析を行った。
AXI4_Stream_Switcher_6_150516.png

AXI4_Stream_Switcher_7_150516.png
  1. 2015年05月16日 04:27 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

今日でFPGAの部屋のブログは10週年を迎えました

今日、2015年5月15日で、FPGAの部屋のブログは10週年を迎えました。わ~い。。。パチパチパチ。。。(拍手です)

10年間、皆様に見て頂いて、また、いろいろとアドバイスを頂いて、ありがとうございました。できうる限り、ブログを続けていきたいと思っていますので、よろしくお願いします。自分でも良く10年続いたなと思います。
今まで書いた記事は、この記事を含めて 3093 記事です。1ヶ月平均では 25.775 記事を書いたことになります。

ブログを始めたのは、2005年5月15日でした。初めの記事は、”Chipscopeについて”でした。

初めはYahooブログで始めたのですが、タイミングレポートがYahooブログでは書けないためにFC2ブログに移って来ました。

ブログを始めた時は小さかった子どもたちも大きくなりました。これからも日記も書きながら、FPGAの話題を主にブログを書いていきたいと思います。

最後に、FPGAの部屋のブログの9年間のアクセス数の推移表を下に貼っておきます。残念ながら、最初の1年間のアクセス数統計はありません。少なかったと思います。
Access_to_2015_1_150515.png
  1. 2015年05月15日 05:00 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream Switcher IP の製作1(CPP ソースコードの公開)

AXI4-Stream Switcher IP の製作を行う。
AXI4-Stream Switcher IP は、”AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム1(構想編)”のブロック図に示してあるが、ラプラシアンフィルタを通した画像データのAXI4-Steram とカメラの画像データのAXI4-Steram を切り替える。

AXI4-Stream Switcher IP は、Vivado HLS 2014.4 で作製する。

まずは、ZYBO用 (xc7z010clg400-1)のVivado HLS 2014.4 の axis_switcher_2014_4 プロジェクトを作製した。

Source に、axis_switcher.cpp と axis_switcher.h を Add Source した。

Test Bench に axis_switcher_tb.cpp を Add Source した。

下に Vivado HLS 2014.4 のプロジェクトを示す。下の図はIP化まで終わった状態だ。
今回は、テストベンチを作るのに手間取ってしまった。
AXI4_Stream_Switcher_1_150515.png

下に、axis_switcher.cpp を示す。

//
// axis_switcher.cpp
// 2015/05/14
// by marsee
//

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

#include "axis_switcher.h"

int axis_switcher(hls::stream<ap_axis<32,1,1,1> >& ins0, hls::stream<ap_axis<32,1,1,1> >& ins1, 
    hls::stream<ap_axis<32,1,1,1> >& outs, int select) {
#pragma HLS INTERFACE axis port=ins0
#pragma HLS INTERFACE axis port=ins1
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE s_axilite port=select bundle=BUS_AXI4LS
#pragma HLS INTERFACE s_axilite port=return bundle=BUS_AXI4LS
#pragma HLS INTERFACE ap_none port=select

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

    do {    // user が 1になった時にフレームがスタートする
        if (select == 0){
            ins0 >> pix;
        } else{
            ins1 >> 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
            if (!(x==0 && y==0)){    // 最初の入力はすでに入力されている
                if (select == 0){
                    ins0 >> pix;
                } else{
                    ins1 >> pix;
                }
            }

            outs << pix;    // AXI4-Stream へ出力
        }
    }

    return 1;
}


次に、axis_switcher.h を示す。現在はHDL Simulation 用の小さい値にしている。

//
// axis_switcher.h
// 2015/05/14
// by marsee
//

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

#define HORIZONTAL_PIXEL_WIDTH 20
#define VERTICAL_PIXEL_WIDTH 5

#define ALL_PIXEL_VALUE (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)


最後に、テストベンチの axis_switcher_tb.cpp を下に示す。

//
// axis_switcher_tb.cpp
// 2015/05/14
// by marsee
//

#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 "axis_switcher.h"

int axis_switcher(hls::stream<ap_axis<32,1,1,1> >& ins0, hls::stream<ap_axis<32,1,1,1> >& ins1, 
    hls::stream<ap_axis<32,1,1,1> >& outs, int select);

#define CLOCK_PERIOD 10

int main() {
    using namespace std;

    hls::stream<ap_axis<32,1,1,1> > ins0;
    hls::stream<ap_axis<32,1,1,1> > ins1;
    hls::stream<ap_axis<32,1,1,1> > outs;

    ap_axis<32,1,1,1> pix0, pix1;
    ap_axis<32,1,1,1> vals;
 
    int m_seq = 1// M系列の値
    int i;
    int xor_shift;
    int rect_wave;
    int select;

    for(int i=0; i<5; i++){ // dummy data
        pix0.user = 0;
        pix0.data = i;
        pix1.user = 0;
        pix1.data = i;
        ins0 << pix0;
        ins1 << pix1;
    }

    // ins0 には M系列、ins1 には、矩形波を入力する
    for(int j=0; j < VERTICAL_PIXEL_WIDTH; j++){
        for(i=0; i < HORIZONTAL_PIXEL_WIDTH; i++){
            xor_shift = (m_seq>>30 & 1) ^ ((m_seq>>2)& 1) ^ ((m_seq>>1) & 1) ^ (m_seq & 1); // (31, 3, 2, 1) 31ビットのM系列
            m_seq = ((m_seq<<1) | xor_shift) & 0x7FFFFFFF;
            if (((i+j)%CLOCK_PERIOD) < (CLOCK_PERIOD/2))
                rect_wave = 0;
            else
                rect_wave = 1000;

            pix0.data = (ap_int<32>)m_seq;
            pix1.data = (ap_int<32>)rect_wave;

            select = 0// M 系列            

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

            if (i == HORIZONTAL_PIXEL_WIDTH-1) { // 行の最後でTLASTをアサートする
                pix0.last = 1;
                pix1.last = 1;
            } else {
                pix0.last = 0;
                pix1.last = 0;
            }

            ins0 << pix0;
            ins1 << pix1;
        }
    }

    axis_switcher(ins0, ins1, outs, select);

    cout << endl;
    cout << "outs" << endl;
    for(int j=0; j < VERTICAL_PIXEL_WIDTH; j++){
        for(i=0; i < HORIZONTAL_PIXEL_WIDTH; i++){
            outs >> vals;
            ap_int<32> val = vals.data;
            cout << hex << val << endl;
            if (vals.last)
                cout << "AXI-Stream is end" << endl;
        }
    }

    // ins0 には M系列、ins1 には、矩形波を入力する
    for(int j=0; j < VERTICAL_PIXEL_WIDTH; j++){
        for(i=0; i < HORIZONTAL_PIXEL_WIDTH; i++){
            xor_shift = (m_seq>>30 & 1) ^ ((m_seq>>2)& 1) ^ ((m_seq>>1) & 1) ^ (m_seq & 1); // (31, 3, 2, 1) 31ビットのM系列
            m_seq = ((m_seq<<1) | xor_shift) & 0x7FFFFFFF;
            if (((i+j)%CLOCK_PERIOD) < (CLOCK_PERIOD/2))
                rect_wave = 0;
            else
                rect_wave = 1000;

            pix0.data = (ap_int<32>)m_seq;
            pix1.data = (ap_int<32>)rect_wave;

            select = 1// 矩形波            

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

            if (i == HORIZONTAL_PIXEL_WIDTH-1) { // 行の最後でTLASTをアサートする
                pix0.last = 1;
                pix1.last = 1;
            } else {
                pix0.last = 0;
                pix1.last = 0;
            }

            ins0 << pix0;
            ins1 << pix1;
        }
    }

    axis_switcher(ins0, ins1, outs, select);

    cout << endl;
    cout << "outs" << endl;
    for(int j=0; j < VERTICAL_PIXEL_WIDTH; j++){
        for(i=0; i < HORIZONTAL_PIXEL_WIDTH; i++){
            outs >> vals;
            ap_int<32> val = vals.data;
            cout << hex << val << endl;
            if (vals.last)
                cout << "AXI-Stream is end" << endl;
        }
    }

    return 0;   
}

  1. 2015年05月15日 04:36 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

カメラ-AXI4-Stream出力IPの作製4(シミュレーション用HDLソースの公開)

”カメラ-AXI4-Stream出力IPの作製3(論理合成用HDLソースの公開)”の続き。

今回は、カメラ-AXI4-Stream出力IPのシミュレーション用 Verilog HDL のソースコードを貼っておく。

まずは、mt9d111_inf_axis_tb.v から貼っておく。

`default_nettype none

`timescale 100ps / 1ps

// mt9d111_inf_axis_tb.v
// 2015/05/11
//

module mt9d111_inf_axis_tb;
    parameter integer C_S_AXI_LITE_ADDR_WIDTH = 9; // Address width of the AXI Lite Interface
    parameter integer C_S_AXI_LITE_DATA_WIDTH = 32; // Data width of the AXI Lite Interface

    parameter integer C_M_AXIS_DATA_WIDTH = 32; // AXI4 Stream Master Interface
    
    parameter DELAY = 1;
    
    // Inputs
    wire ACLK;
    wire ARESETN;
    wire pclk_from_pll;
    wire pclk;
    wire href;
    wire vsync;
    wire [7:0] cam_data;
    wire m_axis_tready;

    // Outputs
    wire xck;
    wire standby;
    wire pfifo_overflow;
    wire pfifo_underflow;
    wire [C_M_AXIS_DATA_WIDTH-1:0] m_axis_tdata;
    wire [(C_M_AXIS_DATA_WIDTH/8)-1:0] m_axis_tstrb;
    wire m_axis_tvalid;
    wire m_axis_tlast;
    wire m_axis_tuser;

    // AXI Lite Write Address Channel
    reg                                        s_axi_lite_awvalid = 1'b0;
    wire                                    s_axi_lite_awready;
    reg        [C_S_AXI_LITE_ADDR_WIDTH-1: 0]    s_axi_lite_awaddr = 0;
    reg        [3-1:0]                            s_axi_lite_awport = 1'b0;

    // AXI Lite Write Data Channel
    reg                                        s_axi_lite_wvalid =1'b0;
    wire                                    s_axi_lite_wready;
    reg        [C_S_AXI_LITE_DATA_WIDTH-1: 0]    s_axi_lite_wdata = 0;
    
    // AXI Lite Write Response Channel
    wire    [1:0]                            s_axi_lite_bresp;
    wire                                    s_axi_lite_bvalid;
    reg                                        s_axi_lite_bready = 1'b0;

    // AXI Lite Read Address Channel
    reg                                        s_axi_lite_arvalid = 1'b0;
    wire                                    s_axi_lite_arready;
    reg        [C_S_AXI_LITE_ADDR_WIDTH-1: 0]    s_axi_lite_araddr = 1'b0;
    reg        [3-1:0]                            s_axi_lite_arport = 0;
    
    // AXI Lite Read Data Channel
    wire                                    s_axi_lite_rvalid;
    reg                                        s_axi_lite_rready = 1'b0;
    wire    [C_S_AXI_LITE_DATA_WIDTH-1: 0]    s_axi_lite_rdata;
    wire    [1:0]                            s_axi_lite_rresp;

    integer i;

    // Instantiate the Unit Under Test (UUT)
    mt9d111_inf_axis # (
        .C_M_AXIS_DATA_WIDTH(C_M_AXIS_DATA_WIDTH)
    ) uut (
        .s_axi_lite_aclk(ACLK),
        .m_axis_aclk(ACLK), 
        .axi_resetn(ARESETN), 
        
        .s_axi_lite_awvalid(s_axi_lite_awvalid),
        .s_axi_lite_awready(s_axi_lite_awready),
        .s_axi_lite_awaddr(s_axi_lite_awaddr),
        //.s_axi_lite_awport(s_axi_lite_awport),
        .s_axi_lite_wvalid(s_axi_lite_wvalid),
        .s_axi_lite_wready(s_axi_lite_wready),
        .s_axi_lite_wdata(s_axi_lite_wdata),
        .s_axi_lite_bresp(s_axi_lite_bresp),
        .s_axi_lite_bvalid(s_axi_lite_bvalid),
        .s_axi_lite_bready(s_axi_lite_bready),
        .s_axi_lite_arvalid(s_axi_lite_arvalid),
        .s_axi_lite_arready(s_axi_lite_arready),
        .s_axi_lite_araddr(s_axi_lite_araddr),
        //.s_axi_lite_arport(s_axi_lite_arport),
        .s_axi_lite_rvalid(s_axi_lite_rvalid),
        .s_axi_lite_rready(s_axi_lite_rready),
        .s_axi_lite_rdata(s_axi_lite_rdata),
        .s_axi_lite_rresp(s_axi_lite_rresp),

        .m_axis_tdata(m_axis_tdata),
        .m_axis_tstrb(m_axis_tstrb),
        .m_axis_tvalid(m_axis_tvalid),
        .m_axis_tready(m_axis_tready),
        .m_axis_tlast(m_axis_tlast),
        .m_axis_tuser(m_axis_tuser),
        
        .pclk_from_pll(pclk_from_pll), 
        .pclk(pclk), 
        .xck(xck), 
        .href(href), 
        .vsync(vsync), 
        .cam_data(cam_data), 
        .standby(standby), 
        .pfifo_overflow(pfifo_overflow), 
        .pfifo_underflow(pfifo_underflow)
    );
    assign m_axis_tready = 1'b1;

    // ACLK のインスタンス
    clk_gen #(
        .CLK_PERIOD(100),    // 10nsec, 100MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) ACLKi (
        .clk_out(ACLK)
    );

    // pclk_from_pll のインスタンス
    clk_gen #(
        .CLK_PERIOD(278),    // 27.8nsec, 約36MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) pclk_from_pll_i (
        .clk_out(pclk_from_pll)
    );

    // reset_gen のインスタンス
    reset_gen #(
        .RESET_STATE(1'b0),
        .RESET_TIME(1000)    // 100nsec
    ) RESETi (
        .reset_out(ARESETN)
    );

    // MT9D111 モデル
    mt9d111_model #(
        // .HORIZONTAL_PIXELS(800),
        // .VERTICAL_LINES(600),
        // .HBLANK_REG(174),
        // .VBLANK_REG(16),
        .HORIZONTAL_PIXELS(50),
        .VERTICAL_LINES(10),
        .HBLANK_REG(10),
        .VBLANK_REG(5),
        .PCLK_DELAY(1)
    ) mt9d111_model_i (
        .xck(xck),
        .pclk(pclk),
        .href(href),
        .vsync(vsync),
        .d(cam_data),
        .scl(1'b1),
        .sda(),
        .standby(standby)
    );

    initial begin
        // Initialize Inputs
        s_axi_lite_awaddr = 0;
        s_axi_lite_awport = 0;
        s_axi_lite_wvalid = 0;
        s_axi_lite_wdata = 0;
        s_axi_lite_wvalid = 0;
        s_axi_lite_bready = 0;
        s_axi_lite_araddr = 0;
        s_axi_lite_arport = 0;
        s_axi_lite_arvalid = 0;
        s_axi_lite_rready = 0;

        // Wait Reset rising edge
        @(posedge ARESETN);
        
        for (i=0; i<10; i=i+1) begin
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
        end
        
        // Add stimulus here
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        AXI_MASTER_WADC1(32'h0000_0000, 32'h1200_0000);
        @(posedge ACLK);    // 次のクロックへ
        #DELAY;
        AXI_MASTER_RADC1(32'h0000_0000);
        #DELAY;
        
        // @(posedge ACLK);    // 次のクロックへ        
        // #DELAY;
        // AXI_MASTER_WADC2(32'h0000_0004, 32'h0000_0001);    // one_shot mode
        // @(posedge ACLK);    // 次のクロックへ        
        // #DELAY;
        // AXI_MASTER_RADC2(32'h0000_0004);
        
        // @(posedge ACLK);    // 次のクロックへ        
  //       #DELAY;
  //       AXI_MASTER_WADC2(32'h0000_0004, 32'h0000_0003); // one_shot trigger
  //       @(posedge ACLK);    // 次のクロックへ        
  //       #DELAY;
  //       AXI_MASTER_RADC2(32'h0000_0004);
        
        // @(posedge ACLK);    // 次のクロックへ        
  //       #DELAY;
  //       AXI_MASTER_WADC2(32'h0000_0004, 32'h0000_0003); // one_shot trigger
  //       @(posedge ACLK);    // 次のクロックへ        
  //       #DELAY;
  //       AXI_MASTER_RADC2(32'h0000_0004);
    end
            
    // Write Transcation 1
    task AXI_MASTER_WADC1;
        input    [C_S_AXI_LITE_ADDR_WIDTH-1:0]    awaddr;
        input    [C_S_AXI_LITE_DATA_WIDTH-1:0]    wdata;
        begin
            s_axi_lite_awaddr    = awaddr;
            s_axi_lite_awvalid    = 1'b1;
            
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
            
            s_axi_lite_awvalid = 1'b0;
            s_axi_lite_wdata = wdata;
            s_axi_lite_wvalid = 1'b1;
            @(posedge ACLK);    // 次のクロックへ, s_axi_lite_wready は常に 1
            
            #DELAY;
            s_axi_lite_wvalid = 1'b0;
            s_axi_lite_bready = 1'b1;
            
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
                
            s_axi_lite_bready = 1'b0;
        end
    endtask

    // Write Transcation 2
    task AXI_MASTER_WADC2;
        input    [C_S_AXI_LITE_ADDR_WIDTH-1:0]    awaddr;
        input    [C_S_AXI_LITE_DATA_WIDTH-1:0]    wdata;
        begin
            s_axi_lite_awaddr    = awaddr;
            s_axi_lite_awvalid    = 1'b1;
            
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
            
            s_axi_lite_awvalid = 1'b0;
            s_axi_lite_wdata = wdata;
            s_axi_lite_wvalid = 1'b1;
            @(posedge ACLK);    // 次のクロックへ, s_axi_lite_wready は常に 1
            
            #DELAY;
            s_axi_lite_wvalid = 1'b0;        
            @(posedge ACLK);    // 次のクロックへ
            
            #DELAY;        
            s_axi_lite_bready = 1'b1;
            
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
                
            s_axi_lite_bready = 1'b0;
        end
    endtask
    
    // Read Transcation 1    
    task AXI_MASTER_RADC1;
        input    [31:0]    araddr;
        begin
            s_axi_lite_araddr    = araddr;
            s_axi_lite_arvalid     = 1'b1;
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
            
            s_axi_lite_araddr    = 0;
            s_axi_lite_arvalid     = 1'b0;
            s_axi_lite_rready = 1'b1;

            @(posedge ACLK);    // 次のクロックへ
            #DELAY;

            s_axi_lite_rready = 1'b0;
        end
    endtask
    
    // Read Transcation 2   
    task AXI_MASTER_RADC2;
        input    [31:0]    araddr;
        begin
            s_axi_lite_araddr    = araddr;
            s_axi_lite_arvalid     = 1'b1;
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;
            
            s_axi_lite_araddr    = 0;
            s_axi_lite_arvalid     = 1'b0;
            @(posedge ACLK);    // 次のクロックへ
            #DELAY;

            s_axi_lite_rready = 1'b1;

            @(posedge ACLK);    // 次のクロックへ
            #DELAY;

            s_axi_lite_rready = 1'b0;
        end
    endtask
    
endmodule

module clk_gen #(
    parameter         CLK_PERIOD = 100,
    parameter real    CLK_DUTY_CYCLE = 0.5,
    parameter        CLK_OFFSET = 0,
    parameter        START_STATE    = 1'b0 )
(
    output    reg        clk_out
);
    begin
        initial begin
            #CLK_OFFSET;
            forever
            begin
                clk_out = START_STATE;
                #(CLK_PERIOD-(CLK_PERIOD*CLK_DUTY_CYCLE)) clk_out = ~START_STATE;
                #(CLK_PERIOD*CLK_DUTY_CYCLE);
            end
        end
    end
endmodule

module reset_gen #(
    parameter    RESET_STATE = 1'b1,
    parameter    RESET_TIME = 100 )
(
    output    reg        reset_out
);
    begin
        initial begin
            reset_out = RESET_STATE;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
        end
    end
endmodule

`default_nettype wire


次に、カメラの動作モデルの mt9d111_model.v を貼っておく。

// mt9d111_model.v 
// mt9d111 の動作モデル
// RGB565 を出力

`default_nettype none
`timescale 1ns / 1ps

module mt9d111_model # (
    parameter    integer HORIZONTAL_PIXELS    = 800,
    parameter    integer    VERTICAL_LINES        = 600,
    parameter    integer    HBLANK_REG            = 174,     // pixels
    parameter    integer    VBLANK_REG            = 16,    // rows
    parameter    integer    PCLK_DELAY            = 1
)(
    input    wire    xck,
    output    reg        pclk = 1'b1,
    output    reg        href = 1'b0,
    output    reg        vsync = 1'b1,
    output    reg        [7:0]    d = 8'd0,
    input    wire    scl,
    inout    wire    sda,
    input    wire    standby
);

    parameter    [2:0]    FRAME_START_BLANKING =    3'b000,
                        ACTIVE_DATA_TIME =        3'b001,
                        HORIZONTAL_BLANKING =    3'b011,
                        FRAME_END_BLANKING =    3'b010,
                        VERTICAL_BLANKING =        3'b110;
                        
    reg        [2:0]    mt9d111_cs = VERTICAL_BLANKING;
    reg        [2:0]    fseb_count = 3'd5;
    reg        [15:0]    adt_count = (HORIZONTAL_PIXELS * 2) - 1;
    reg        [15:0]    hb_count = HBLANK_REG - 1;
    reg        [15:0]    fvt_count = VERTICAL_LINES - 1;
    reg        [31:0]    vb_count = VBLANK_REG * (HORIZONTAL_PIXELS + HBLANK_REG) - 1;
    reg        href_node = 1'b0;
    reg        vsync_node = 1'b0;
    reg        dout_is_even = 1'b0;

    // R, G, B 毎に違った生成多項式のM系列を用意した
    function [7:0] mseqf8_R (input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[3] ^ din[2] ^ din[1];
            mseqf8_R = {din[6:0], xor_result};
        end
    endfunction
    
    function [7:0] mseqf8_G (input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[4] ^ din[2] ^ din[0];
            mseqf8_G = {din[6:0], xor_result};
        end
    endfunction

    function [7:0] mseqf8_B (input [7:0] din);
        reg xor_result;
        begin
            xor_result = din[7] ^ din[5] ^ din[2] ^ din[1];
            mseqf8_B = {din[6:0], xor_result};
        end
    endfunction

    reg        [7:0]    mseq8r = 8'd1;
    reg        [7:0]    mseq8g = 8'd1;
    reg        [7:0]    mseq8b = 8'd1;
    
    // pclk の出力
    always @*
        pclk <= #PCLK_DELAY    xck;
        
    // MT9D111 のステート
    always @(posedge pclk) begin
        case (mt9d111_cs)
            FRAME_START_BLANKING : begin
                if (fseb_count==0) begin
                    mt9d111_cs <= ACTIVE_DATA_TIME;
                    href_node <= 1'b1;
                end
            end
            ACTIVE_DATA_TIME : begin
                if (adt_count==0) begin
                    if (fvt_count==0)    // frame end
                        mt9d111_cs <= FRAME_END_BLANKING;
                    else
                        mt9d111_cs <= HORIZONTAL_BLANKING;
                    href_node <= 1'b0;
                end
            end
            HORIZONTAL_BLANKING : begin
                if (hb_count==0) begin
                    mt9d111_cs <= ACTIVE_DATA_TIME;
                    href_node <= 1'b1;
                end
            end
            FRAME_END_BLANKING : begin
                if (fseb_count==0) begin
                    mt9d111_cs <= VERTICAL_BLANKING;
                    vsync_node <= 1'b0;
                end
            end
            VERTICAL_BLANKING : begin
                if (vb_count==0) begin
                    mt9d111_cs <= FRAME_START_BLANKING;
                    vsync_node <= 1'b1;
                end
            end
        endcase
    end
                
    // vsync, href 出力、レーシングを防ぐためにpclk よりも出力を遅らせる
    always @* begin
        vsync <= #1    vsync_node;
        href <= #1    href_node;
    end
    
    // Frame Start/End Blanking Counter (6 pixel clocks)
    always @(posedge pclk) begin
        if (mt9d111_cs==FRAME_START_BLANKING || mt9d111_cs==FRAME_END_BLANKING) begin
            if (fseb_count > 0)
                fseb_count <= fseb_count - 3'd1;
        end else
            fseb_count <= 3'd5;
    end
    
    // Active Data Time Counter
    always @(posedge pclk) begin
        if (mt9d111_cs==ACTIVE_DATA_TIME) begin
            if (adt_count > 0)
                adt_count <= adt_count - 16'd1;
        end else
            adt_count <= (HORIZONTAL_PIXELS * 2) - 1;
    end
    
    // Horizontal Blanking Counter
    always @(posedge pclk) begin
        if (mt9d111_cs==HORIZONTAL_BLANKING) begin
            if (hb_count > 0)
                hb_count <= hb_count - 16'd1;
        end else
            hb_count <= HBLANK_REG - 1;
    end
    
    // Frame Valid Time Counter
    always @(posedge pclk) begin
        if (mt9d111_cs==ACTIVE_DATA_TIME && adt_count==0)
            fvt_count <= fvt_count - 16'd1;
        else if (mt9d111_cs==FRAME_END_BLANKING)
            fvt_count <= VERTICAL_LINES - 1;
    end
    
    // Vertical Blanking Counter
    always @(posedge pclk) begin
        if (mt9d111_cs==VERTICAL_BLANKING) begin
            if (vb_count > 0)
                vb_count <= vb_count - 32'd1;
        end else
            vb_count <= VBLANK_REG * (HORIZONTAL_PIXELS + HBLANK_REG) - 1;
    end
    
    // Red のM系列符号生成
    always @(posedge pclk) begin
        // if (mt9d111_cs==ACTIVE_DATA_TIME)
            mseq8r <= mseqf8_R(mseq8r);
    end
    
    // Green のM系列符号生成
    always @(posedge pclk) begin
        // if (mt9d111_cs==ACTIVE_DATA_TIME)
            mseq8g <= mseqf8_G(mseq8g);
    end
    
    // Blue のM系列符号生成
    always @(posedge pclk) begin
        // if (mt9d111_cs==ACTIVE_DATA_TIME)
            mseq8b <= mseqf8_B(mseq8b);
    end
    
    // d 出力のODD とEVEN を示す
    always @(posedge pclk) begin
        if (mt9d111_cs==ACTIVE_DATA_TIME)
            dout_is_even <= ~dout_is_even;
        else
            dout_is_even <= 1'b0;
    end
    
    // d 出力、レーシングを防ぐためにpclk よりも出力を遅らせる
    always @(posedge pclk) begin
        if (mt9d111_cs==ACTIVE_DATA_TIME) begin
            if (dout_is_even)
                d <= #1 {mseq8g[4:2], mseq8b[7:3]};
            else
                d <= #1 {mseq8r[7:3], mseq8g[7:5]};
        end
    end

endmodule

`default_nettype wire

  1. 2015年05月13日 04:15 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

カメラ-AXI4-Stream出力IPの作製3(論理合成用HDLソースの公開)

カメラ-AXI4-Stream出力IPの作製2(論理シミュレーション)”の続き。

今回は、前回の論理シミュレーションで問題なかった論理合成用のHDLソースコードを貼っておく。

まずは、トップの mt9d111_inf_axims.vhd から貼っておく。

-------------------------------------------------------------------------------
--
-- AXI Stream
--
-- VHDL-Standard:   VHDL'93
----------------------------------------------------------------------------
--
-- Structure:
--   mt9d111_inf_axims
--
----------------------------------------------------------------------------
--
-- 2014/11/07 : AXI4 Lite Slave を追加
-- 2014/11/08 : one_shot_reg を実装。
-- オフセット0番地: フレーム・バッファの先頭アドレス(fb_start_address)
-- オフセット4番地: 0 ビット目が 0 の時動画、0 ビット目に 1 の時に、ワンショットで取得した1フレームのカメラ画像を表示(one_shot_reg)
--            1 ビット目に 1 を Write した時に、ワンショットで1フレームの画像をフレーム・バッファに保存
-- 2015/05/09 : AXI4 Master から AXI4 Master Stream にインターフェースを変更、generic map, port map は ar37425\axi_stream_v1_00_a\hdl\vhdl の axi_stream.vhd から引用した
--                 AXI4 Stream なので、fb_start_address は使用しない。そこに書き込まれたというスタート信号だけを使用する


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

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

entity mt9d111_inf_axis is
  generic(
    -- AXI4 Lite Slave Interface
    C_S_AXI_LITE_ADDR_WIDTH         : integer       := 9; -- Address width of the AXI Lite Interface
    C_S_AXI_LITE_DATA_WIDTH         : integer       := 32; -- Data width of the AXI Lite Interface

    -- AXI4 Stream Master Interface
    C_M_AXIS_DATA_WIDTH : integer range 32 to 256 := 32  -- Master AXI Stream Data Width  
  );
  port (
    -- Global Ports
    s_axi_lite_aclk                : in   std_logic;
    m_axis_aclk                    : in std_logic;
    axi_resetn                     : in std_logic;

    -- Master Stream Ports
    -- m_axis_aresetn : out std_logic;
    m_axis_tdata   : out std_logic_vector(C_M_AXIS_DATA_WIDTH-1 downto 0);
    m_axis_tstrb   : out std_logic_vector((C_M_AXIS_DATA_WIDTH/8)-1 downto 0);
    m_axis_tvalid  : out std_logic;
    m_axis_tready  : in  std_logic;
    m_axis_tlast   : out std_logic;
    m_axis_tuser   : out std_logic_vector(0 downto 0);

    -- AXI Lite Slave Ports
    s_axi_lite_awvalid          : in  std_logic;
    s_axi_lite_awready          : out std_logic;
    s_axi_lite_awaddr           : in  std_logic_vector(C_S_AXI_LITE_ADDR_WIDTH-1 downto 0);

    -- AXI Lite Write Data Channel --
    s_axi_lite_wvalid           : in  std_logic;
    s_axi_lite_wready           : out std_logic;
    s_axi_lite_wdata            : in  std_logic_vector(C_S_AXI_LITE_DATA_WIDTH-1 downto 0);

    -- AXI Lite Write Response Channel --
    s_axi_lite_bresp            : out std_logic_vector(1 downto 0);
    s_axi_lite_bvalid           : out std_logic;
    s_axi_lite_bready           : in  std_logic;

    -- AXI Lite Read Address Channel --
    s_axi_lite_arvalid          : in  std_logic;
    s_axi_lite_arready          : out std_logic;
    s_axi_lite_araddr           : in  std_logic_vector(C_S_AXI_LITE_ADDR_WIDTH-1 downto 0);
    
    -- AXI Lite Read Data Channel --
    s_axi_lite_rvalid           : out std_logic;
    s_axi_lite_rready           : in  std_logic;
    s_axi_lite_rdata            : out std_logic_vector(C_S_AXI_LITE_DATA_WIDTH-1 downto 0);
    s_axi_lite_rresp            : out std_logic_vector(1 downto 0);
    
    -- MT9D111 Camera Interface
    pclk_from_pll    : in    std_logic;    -- PLLからMT9D111のxck に出力するクロック
    pclk            : in     std_logic;    -- MT9D111からのピクセルクロック入力
    xck                : out    std_logic;    -- MT9D111へのピクセルクロック出力
    href            : in     std_logic;
    vsync            : in    std_logic;
    cam_data        : in    std_logic_vector(7 downto 0);
    standby            : out    std_logic;    -- STANDBY出力(ディスエーブル、0固定)
    pfifo_overflow    : out    std_logic;    -- pfifo overflow
    pfifo_underflow    : out    std_logic    -- pfifo underflow
);

end mt9d111_inf_axis;

-------------------------------------------------------------------------------
-- Architecture
-------------------------------------------------------------------------------
architecture implementation of mt9d111_inf_axis is

constant    ONE_SHOT_PULSE_LENGTH : integer := 20;    -- 1ショットパルスの長さのAXI Lite Slave ACLKのクロック数をセット

signal reset_1d, reset_2d, reset : std_logic;
signal preset_1d, preset_2d, preset : std_logic;
signal pfifo_empty        : std_logic;
signal pfifo_almost_empty    : std_logic;
signal pfifo_rd_data_count    : std_logic_vector(9 downto 0);
signal pfifo_rd_dcount_dec : unsigned(9 downto 0);
signal pfifo_rd_en : std_logic;
signal ACLK : std_logic := '0';
signal one_shot_state : std_logic;
signal one_shot_trigger : std_logic;
signal init_done : std_logic;
signal pfifo_dout : std_logic_vector(33 downto 0);

component mt9d111_cam_cont 
    port(
        aclk            : in std_logic;
        areset            : in std_logic;
        pclk            : in std_logic;
        preset            : in std_logic;
        pclk_from_pll    : in std_logic;
        xclk            : out std_logic;
        line_valid        : in std_logic;
        frame_valid        : in std_logic;
        cam_data        : in std_logic_vector(7 downto 0);
        standby            : out std_logic;
        pfifo_rd_en        : in std_logic;
        pfifo_dout        : out std_logic_vector(33 downto 0); -- frame data start signal + data[31:0]
        pfifo_empty        : out std_logic;
        pfifo_almost_empty        : out std_logic;
        pfifo_rd_data_count    : out std_logic_vector(9 downto 0);
        pfifo_overflow        : out std_logic;
        pfifo_underflow        : out std_logic;
        one_shot_state            : in std_logic;    -- 1フレーム分取り込んだカメラ画像を保持する
        one_shot_trigger        : in std_logic        -- 1フレーム分のカメラ画像取り込みのトリガー、1クロックパルス
    ); 
end component;

component mt9d111_axi_lite_slave generic (
        C_S_AXI_LITE_ADDR_WIDTH         : integer range 9 to 9        := 9; -- Address width of the AXI Lite Interface
        C_S_AXI_LITE_DATA_WIDTH         : integer range 32 to 32        := 32; -- Data width of the AXI Lite Interface
        
        C_DISPLAY_START_ADDRESS            : std_logic_vector(31 downto 0) := x"1A000000";
        ONE_SHOT_PULSE_LENGTH            : integer                     := 20    -- 1ショットパルスの長さのAXI Lite Slave ACLKのクロック数をセット
    );
    port(
        -- Clock and Reset
        s_axi_lite_aclk                : in   std_logic;
        axi_resetn                    : in   std_logic;
        
        -------------------------------
        -- AXI4 Lite Slave Interface --
        -------------------------------
        -- AXI Lite Write Address Channel --
        s_axi_lite_awvalid          : in  std_logic;
        s_axi_lite_awready          : out std_logic;
        s_axi_lite_awaddr           : in  std_logic_vector(C_S_AXI_LITE_ADDR_WIDTH-1 downto 0);
        
        -- AXI Lite Write Data Channel --
        s_axi_lite_wvalid           : in  std_logic;
        s_axi_lite_wready           : out std_logic;
        s_axi_lite_wdata            : in  std_logic_vector(C_S_AXI_LITE_DATA_WIDTH-1 downto 0);

        -- AXI Lite Write Response Channel  --
        s_axi_lite_bresp            : out std_logic_vector(1 downto 0);
        s_axi_lite_bvalid           : out std_logic;
        s_axi_lite_bready           : in  std_logic;

        -- AXI Lite Read Address Channel --
        s_axi_lite_arvalid          : in  std_logic;
        s_axi_lite_arready          : out std_logic;
        s_axi_lite_araddr           : in  std_logic_vector(C_S_AXI_LITE_ADDR_WIDTH-1 downto 0);
    
        -- AXI Lite Read Data Channel
        s_axi_lite_rvalid           : out std_logic;
        s_axi_lite_rready           : in  std_logic;
        s_axi_lite_rdata            : out std_logic_vector(C_S_AXI_LITE_DATA_WIDTH-1 downto 0);
        s_axi_lite_rresp            : out std_logic_vector(1 downto 0);
        
        fb_start_address            : out std_logic_vector(31 downto 0);    -- Frame Buffer のスタートアドレス
        init_done                   : out std_logic;  -- fb_start_address に書き込まれた
        one_shot_state                : out std_logic;    -- 1フレーム分取り込んだカメラ画像を保持する
        one_shot_trigger            : out std_logic        -- 1フレーム分のカメラ画像取り込みのトリガー、1クロックパルス
    );
end component;

begin
    ACLK <= m_axis_aclk;
    
    -- mt9d111_axi_lite_slave のインスタンス
    mt9d111_axi_lite_slave_i : mt9d111_axi_lite_slave generic map(
        C_S_AXI_LITE_ADDR_WIDTH    => C_S_AXI_LITE_ADDR_WIDTH,
        C_S_AXI_LITE_DATA_WIDTH    => C_S_AXI_LITE_DATA_WIDTH,
        
        C_DISPLAY_START_ADDRESS    => (others => '0'),
        ONE_SHOT_PULSE_LENGTH    => ONE_SHOT_PULSE_LENGTH
    ) port map (
        s_axi_lite_aclk        => s_axi_lite_aclk,
        axi_resetn            => axi_resetn,
        s_axi_lite_awvalid    => s_axi_lite_awvalid,
        s_axi_lite_awready    => s_axi_lite_awready,
        s_axi_lite_awaddr    => s_axi_lite_awaddr,
        s_axi_lite_wvalid    => s_axi_lite_wvalid,
        s_axi_lite_wready    => s_axi_lite_wready,
        s_axi_lite_wdata    => s_axi_lite_wdata,
        s_axi_lite_bresp    => s_axi_lite_bresp,
        s_axi_lite_bvalid    => s_axi_lite_bvalid,
        s_axi_lite_bready    => s_axi_lite_bready,
        s_axi_lite_arvalid    => s_axi_lite_arvalid,
        s_axi_lite_arready    => s_axi_lite_arready,
        s_axi_lite_araddr    => s_axi_lite_araddr,
        s_axi_lite_rvalid    => s_axi_lite_rvalid,
        s_axi_lite_rready    => s_axi_lite_rready,
        s_axi_lite_rdata    => s_axi_lite_rdata,
        s_axi_lite_rresp    => s_axi_lite_rresp,
        fb_start_address    => open,
        init_done            => init_done,
        one_shot_state        => one_shot_state,
        one_shot_trigger    => one_shot_trigger
    );
        
    -- axi_resetn をACLK で同期化
    process (ACLK) begin
        if ACLK'event and ACLK='1' then 
            reset_1d <= not axi_resetn or not init_done;
            reset_2d <= reset_1d;
        end if;
    end process;
    reset <= reset_2d;
    
    -- axi_resetn をpclk で同期化
    process(pclk) begin
        if pclk'event and pclk='1' then
            preset_1d <= not axi_resetn or not init_done;
            preset_2d <= preset_1d;
        end if;
    end process;
    preset <= preset_2d;
        
    pfifo_rd_en <= not pfifo_empty and m_axis_tready;
    mt9d111_cam_cont_i : mt9d111_cam_cont port map(
        aclk                => ACLK,
        areset                => reset,
        pclk                => pclk,
        preset                => preset,
        pclk_from_pll        => pclk_from_pll,
        xclk                => xck,
        line_valid            => href,
        frame_valid            => vsync,
        cam_data            => cam_data,
        standby                => standby,
        pfifo_rd_en            => pfifo_rd_en,
        pfifo_dout            => pfifo_dout, -- frame start data signal + tlast + data[31:0]
        pfifo_empty            => pfifo_empty,
        pfifo_almost_empty    => pfifo_almost_empty,
        pfifo_rd_data_count    => pfifo_rd_data_count,
        pfifo_overflow        => pfifo_overflow,
        pfifo_underflow        => pfifo_underflow,
        one_shot_state        => one_shot_state,
        one_shot_trigger    => one_shot_trigger
    );
    m_axis_tdata <= pfifo_dout(31 downto 0);
    m_axis_tuser(0) <= pfifo_dout(32);
    m_axis_tlast <= pfifo_dout(33);
    m_axis_tvalid <= not pfifo_empty;
    pfifo_rd_en <= not pfifo_empty and m_axis_tready;
    m_axis_tstrb <= (others => '1');

end implementation;


次に、mt9d111_axi_lite_slave.v を貼っておく。

// mt9d111_axi_lite_slave.v 
// mt9d111_inf_axi_master のAXI Lite Slave モジュール。Frame Buffer のスタートアドレス・レジスタを持つ。
//
// 2014/11/08 : one_shot_reg を実装。
// オフセット0番地: フレーム・バッファの先頭アドレス(fb_start_address)
// オフセット4番地: 0 ビット目が 0 の時動画、0 ビット目に 1 の時に、ワンショットで取得した1フレームのカメラ画像を表示(one_shot_reg)
//            1 ビット目に 1 を Write した時に、ワンショットで1フレームの画像をフレーム・バッファに保存
// 2014/11/11 : init_done を追加
//

`default_nettype none

module mt9d111_axi_lite_slave # (
    parameter integer C_S_AXI_LITE_ADDR_WIDTH = 9, // Address width of the AXI Lite Interface
    parameter integer C_S_AXI_LITE_DATA_WIDTH = 32, // Data width of the AXI Lite Interface
    
    parameter [31:0] C_DISPLAY_START_ADDRESS = 32'h1A00_0000,
    parameter integer ONE_SHOT_PULSE_LENGTH = 20   // 1ショットパルスの長さのAXI Lite Slave ACLKのクロック数をセット
)(
    input    wire                                    s_axi_lite_aclk,
    input    wire                                    axi_resetn,
    
    // AXI Lite Write Address Channel
    input    wire                                    s_axi_lite_awvalid,
    output    wire                                    s_axi_lite_awready,
    input    wire    [C_S_AXI_LITE_ADDR_WIDTH-1: 0]    s_axi_lite_awaddr,

    // AXI Lite Write Data Channel
    input    wire                                    s_axi_lite_wvalid,
    output    wire                                    s_axi_lite_wready,
    input    wire    [C_S_AXI_LITE_DATA_WIDTH-1: 0]    s_axi_lite_wdata,
    
    // AXI Lite Write Response Channel
    output    wire    [1:0]                            s_axi_lite_bresp,
    output    wire                                    s_axi_lite_bvalid,
    input    wire                                    s_axi_lite_bready,

    // AXI Lite Read Address Channel
    input    wire                                    s_axi_lite_arvalid,
    output    wire                                    s_axi_lite_arready,
    input    wire    [C_S_AXI_LITE_ADDR_WIDTH-1: 0]    s_axi_lite_araddr,
    
    // AXI Lite Read Data Channel
    output    wire                                    s_axi_lite_rvalid,
    input    wire                                    s_axi_lite_rready,
    output    reg        [C_S_AXI_LITE_DATA_WIDTH-1: 0]    s_axi_lite_rdata,
    output    wire    [1:0]                            s_axi_lite_rresp,
    
    output    wire    [31:0]                            fb_start_address,    // Frame Buffer のスタートアドレス
    output    reg                                        init_done,            // fb_start_address に書き込まれた
    output    wire                                    one_shot_state,        // 1フレーム分取り込んだカメラ画像を保持する
    output    reg                                        one_shot_trigger    // 1フレーム分のカメラ画像取り込みのトリガー、1クロックパルス
);

    // RESP の値の定義
    parameter    RESP_OKAY =        2'b00;
    parameter    RESP_EXOKAY =    2'b01;
    parameter    RESP_SLVERR =     2'b10;
    parameter    RESP_DECERR =    2'b11;
    
    parameter    [1:0]    IDLE_WR =            2'b00,    // for wrt_cs
                        DATA_WRITE_HOLD =    2'b01,
                        BREADY_ASSERT =        2'b11;
                
    parameter    IDLE_RD    =        1'b0,            //  for rdt_cs
                AR_DATA_WAIT =    1'b1;

    reg        [1:0]    wrt_cs = IDLE_WR;
    
    reg        [31:0]    fb_start_addr_reg = C_DISPLAY_START_ADDRESS;
    
    reg        rdt_cs = IDLE_RD;
    
    reg        reset_1d = 1'b0;
    reg        reset = 1'b0;
    reg        awready = 1'b1;
    reg        bvalid = 1'b0;
    reg        arready = 1'b1;
    reg        rvalid = 1'b0;
    wire        aclk;
    reg        [31:0]    one_shot_reg;

    parameter    [1:0]    IDLE_TSM =            2'b00,    // for one_shot_tsm
                        WAIT_ONE_SHOT =        2'b01,
                        ONE_SHOT_TRIG =        2'b11;
    reg        [1:0]    one_shot_tsm;
    integer    one_shot_counter;

    assign aclk = s_axi_lite_aclk;
    // Synchronization of axi_resetn
    always @(posedge aclk) begin
        reset_1d <= ~axi_resetn;
        reset <= reset_1d;
    end
    
    // AXI4 Lite Slave Write Transaction State Machine
    always @(posedge aclk) begin
        if (reset) begin
            wrt_cs <= IDLE_WR;
            awready <= 1'b1;
            bvalid <= 1'b0;
        end else begin
            case (wrt_cs)
                IDLE_WR :
                    if (s_axi_lite_awvalid & ~s_axi_lite_wvalid) begin    // Write Transaction Start
                        wrt_cs <= DATA_WRITE_HOLD;
                        awready <= 1'b0;
                    end else if (s_axi_lite_awvalid & s_axi_lite_wvalid) begin    // Write Transaction Start with data
                        wrt_cs <= BREADY_ASSERT;
                        awready <= 1'b0;
                        bvalid <= 1'b1;
                    end
                DATA_WRITE_HOLD :
                    if (s_axi_lite_wvalid) begin    // Write data just valid
                        wrt_cs <= BREADY_ASSERT;
                        bvalid <= 1'b1;
                    end
                BREADY_ASSERT :
                    if (s_axi_lite_bready) begin    // The write transaction was terminated.
                        wrt_cs <= IDLE_WR;
                        bvalid <= 1'b0;
                        awready <= 1'b1;
                    end
            endcase
        end
    end
    assign s_axi_lite_awready = awready;
    assign s_axi_lite_bvalid = bvalid;
    assign s_axi_lite_wready = 1'b1;
    assign s_axi_lite_bresp = RESP_OKAY;
    
    // AXI4 Lite Slave Read Transaction State Machine
    always @(posedge aclk) begin
        if (reset) begin
            rdt_cs <= IDLE_RD;
            arready <= 1'b1;
            rvalid <= 1'b0;
        end else begin
            case (rdt_cs)
                IDLE_RD :
                    if (s_axi_lite_arvalid) begin
                        rdt_cs <= AR_DATA_WAIT;
                        arready <= 1'b0;
                        rvalid <= 1'b1;
                    end
                AR_DATA_WAIT :
                    if (s_axi_lite_rready) begin
                        rdt_cs <= IDLE_RD;
                        arready <= 1'b1;
                        rvalid <= 1'b0;
                    end
            endcase
        end
    end
    assign s_axi_lite_arready = arready;
    assign s_axi_lite_rvalid = rvalid;
    assign s_axi_lite_rresp = RESP_OKAY;
                    
    // fb_start_addr_reg
    always @(posedge aclk) begin
        if (reset) begin
            init_done <= 1'b0;
            fb_start_addr_reg <= C_DISPLAY_START_ADDRESS;
        end else begin
            if (s_axi_lite_wvalid==1'b1 && s_axi_lite_awaddr[2]==1'b0) begin
                init_done <= 1'b1;
                fb_start_addr_reg <= s_axi_lite_wdata;
            end
        end
    end
    assign fb_start_address = fb_start_addr_reg;

    // one_shot_reg
    always @(posedge aclk) begin
        if (reset)
            one_shot_reg <= 32'd0;    // default is continuous display mode
        else
            if (s_axi_lite_wvalid==1'b1 && s_axi_lite_awaddr[2]==1'b1)
                one_shot_reg <= s_axi_lite_wdata;
    end
    assign one_shot_state = one_shot_reg[0];

    // one_shot_tsm(State Machine for one_shot_trgger)
    always @(posedge aclk) begin
        if (reset) begin
            one_shot_tsm <= IDLE_TSM;
            one_shot_trigger <= 1'b0;
        end else begin
            case (one_shot_tsm)
                IDLE_TSM :
                    if (s_axi_lite_awvalid & awready & s_axi_lite_awaddr[2]) begin // one_shot_reg address
                        if (s_axi_lite_wvalid) begin // s_axi_wready is always 1
                            if (s_axi_lite_wdata[1]) begin // one_shot was triggered
                                one_shot_tsm <= ONE_SHOT_TRIG;
                                one_shot_trigger <= 1'b1;
                            end else begin // is not trigger
                                one_shot_tsm <= IDLE_TSM;
                                one_shot_trigger <= 1'b0;
                            end
                        end else begin // s_axi_lite_wvalid is not asserted
                            one_shot_tsm <= WAIT_ONE_SHOT;
                            one_shot_trigger <= 1'b0;
                        end
                    end
                WAIT_ONE_SHOT :
                    if (s_axi_lite_wvalid) begin // s_axi_wready is always 1
                        if (s_axi_lite_wdata[1]) begin // one_shot was triggered
                            one_shot_tsm <= ONE_SHOT_TRIG;
                            one_shot_trigger <= 1'b1;
                        end else begin // is not trigger
                            one_shot_tsm <= IDLE_TSM;
                            one_shot_trigger <= 1'b0;
                        end
                    end
                ONE_SHOT_TRIG : begin
                    if (one_shot_counter == 0) begin
                        one_shot_tsm <= IDLE_TSM;
                        one_shot_trigger <= 1'b0;
                    end
                end
            endcase
        end
    end

    // one shot pulse length counter
    always @(posedge aclk) begin
        if (reset) begin
            one_shot_counter <= ONE_SHOT_PULSE_LENGTH;
        end else if (one_shot_tsm == ONE_SHOT_TRIG) begin
            one_shot_counter <= one_shot_counter - 1;
        end else begin
            one_shot_counter <= ONE_SHOT_PULSE_LENGTH;
        end
    end
    // s_axi_lite_rdata
    always @(posedge aclk) begin
        if (reset) begin
            s_axi_lite_rdata <= 0;
        end else if (s_axi_lite_arvalid) begin
            case (s_axi_lite_araddr[2])
                1'b0 :     s_axi_lite_rdata <= fb_start_addr_reg;
                1'b1 :    s_axi_lite_rdata <= one_shot_reg;
            endcase
        end
    end
endmodule
    
`default_nettype wire        


最後に、mt9d111_cam_cont.v を貼っておく。

// MT9D111カメラコントローラ
// mt9d111_cam_cont.v
// 2012/12/26
//
// 2014/11/07 : fb_start_address を追加。レジスタに設定された値をスタートアドレスとして参照。
// 2014/11/08 : one_shot_state, one_shot_trigger の入力ポートを追加
// 2015/05/09 : UPSIDE_DOWN を削除、pifo_dout を 64ビット長から 32 ビット長へ変更、paddr を削除
// 2015/05/11 : last_pixel_4_line を出力するために、データを1つ遅らせることで、line_valid の falling edge を検出し、last_pixel_4_line とする。fb_start_addresを削除

`default_nettype none

module mt9d111_cam_cont (
    input    wire    aclk,
    input    wire    areset,
    input    wire    pclk,
    input    wire    preset,
    input    wire    pclk_from_pll,
    output    wire    xclk,
    input    wire    line_valid,
    input    wire    frame_valid,
    input    wire    [7:0]    cam_data,
    output    wire    standby,
    input    wire    pfifo_rd_en,
    output    wire    [33:0]    pfifo_dout,
    output    wire    pfifo_empty,
    output    wire    pfifo_almost_empty,
    output    wire    [9:0]    pfifo_rd_data_count,
    output    wire    pfifo_overflow,
    output    wire    pfifo_underflow,
    input    wire    one_shot_state,        // 1フレーム分取り込んだカメラ画像を保持する
    input    wire    one_shot_trigger    // 1フレーム分のカメラ画像取り込みのトリガー、1クロックパルス
);
    `include "./disp_timing_parameters.vh"

    // Frame Buffer End Address
    reg        line_valid_1d, line_valid_2d;
    reg        frame_valid_1d, frame_valid_2d;
    reg        [7:0]    cam_data_1d;
    reg        line_valid_1d_odd;
    reg        line_v_1d_odd_1d, line_v_1d_odd_2d;
    reg        [31:0]    rgb565, rgb565_1d;
    wire    pfifo_full, pfifo_almost_full;
    parameter    [1:0]    IDLE_ADDR_RST =    2'b00,
                        ADDR_RST =        2'b01,
                        ADDR_RST_HOLD =    2'b11;

    parameter    [2:0]    IDLE_OS =                3'b000,
                        WAIT_FRAME_VALID_END =    3'b001,
                        HOLD_PICTURE =            3'b011,
                        WAIT_FRAME_VALID_LOW =    3'b010,
                        WAIT_FRAME_VALID_HIGH =    3'b110;
    reg        [2:0]    one_shot_sm;
    reg        frame_valid_1d_oh;
    reg        ost_1d, ost_2d, ost_3d, ost_4d;
    reg        one_shot_tig_pclk;
    reg        first_pixel;
    reg        last_pixel_4_line;
    reg        one_shot_state_1d, one_shot_state_2d;

    assign standby = 1'b0;

    // MT9D111 へのクロックを出力 (xclk)
    ODDR #(
        .DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
        .INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
        .SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
    ) ODDR_inst (
        .Q(xclk), // 1-bit DDR output
        .C(pclk_from_pll), // 1-bit clock input
        .CE(1'b1), // 1-bit clock enable input
        .D1(1'b1), // 1-bit data input (positive edge)
        .D2(1'b0), // 1-bit data input (negative edge)
        .R(1'b0), // 1-bit reset
        .S(1'b0) // 1-bit set
    );

    // 入力信号を一旦ラッチする
    always @(posedge pclk) begin
        if (preset) begin
            line_valid_1d <=    1'b0;
            line_valid_2d <=    1'b0;
            frame_valid_1d <=    1'b0;
            frame_valid_2d <=    1'b0;
            cam_data_1d <=        8'd0;
        end else begin
            line_valid_1d <=    line_valid;
            line_valid_2d <=     line_valid_1d;
            frame_valid_1d <=    frame_valid;
            frame_valid_2d <=     frame_valid_1d;
            cam_data_1d <=        cam_data;
        end
    end

    // one_shot_state を pclk で同期化する
    always @(posedge pclk) begin
        if (preset) begin
            one_shot_state_1d <= 1'b0;
            one_shot_state_2d <= 1'b0;
        end else begin
            one_shot_state_1d <= one_shot_state;
            one_shot_state_2d <= one_shot_state_1d;
        end
    end

    // one_shot_trigger はAXIバスのaclkで生成されているので、pclkで動作するステートマシンでは、本当にone shotでは取れない
    // よって、one shotと言ってもある程度の幅を用意してある。pclk の幅のone shot pulse を作る必要がある
    always @(posedge pclk) begin // one_shot_trigger を pclk で同期化
        if (preset) begin
            ost_1d <= 1'b0;
            ost_2d <= 1'b0;
            ost_3d <= 1'b0;
            ost_4d <= 1'b0;
        end else begin
            ost_1d <= one_shot_trigger;
            ost_2d <= ost_1d;
            ost_3d <= ost_2d;
            ost_4d <= ost_3d;
        end
    end

    // pclk 幅の one shot pulse を生成
    always @(posedge pclk) begin
        if (preset) begin
            one_shot_tig_pclk <= 1'b0;
        end else if (ost_3d==1'b1 && ost_4d==1'b0) begin // rising edge
            one_shot_tig_pclk <= 1'b1;
        end else begin
            one_shot_tig_pclk <= 1'b0;
        end
    end
    
    // one shot state machine
    // frame_valid_1d_oh を生成する
    always @(posedge pclk) begin
        if (preset) begin
            one_shot_sm <= IDLE_OS;
            frame_valid_1d_oh <= frame_valid_1d;
        end else begin
            case (one_shot_sm)
                IDLE_OS : begin
                    frame_valid_1d_oh <= frame_valid_1d;
                    if (one_shot_state_2d) begin
                        one_shot_sm <= WAIT_FRAME_VALID_END;
                    end
                end
                WAIT_FRAME_VALID_END : begin
                    frame_valid_1d_oh <= frame_valid_1d;
                    if (!frame_valid_1d) begin
                        one_shot_sm <= HOLD_PICTURE;
                    end
                end
                HOLD_PICTURE : begin
                    frame_valid_1d_oh <= 1'b0;
                    if (one_shot_tig_pclk) begin
                        one_shot_sm <= WAIT_FRAME_VALID_LOW;
                    end else if (~one_shot_state_2d & ~frame_valid_1d) begin
                        one_shot_sm <= IDLE_OS;
                    end
                end
                WAIT_FRAME_VALID_LOW : begin
                    frame_valid_1d_oh <= 1'b0;
                    if (!frame_valid_1d) begin
                        one_shot_sm <= WAIT_FRAME_VALID_HIGH;
                    end
                end
                WAIT_FRAME_VALID_HIGH : begin
                    frame_valid_1d_oh <= frame_valid_1d;
                    if (frame_valid_1d) begin
                        one_shot_sm <= WAIT_FRAME_VALID_END;
                    end
                end
            endcase
        end
    end

    // line_valid_1d が偶数か奇数かをカウント
    always @(posedge pclk) begin
        if (preset)
            line_valid_1d_odd <= 1'b0;
        else begin
            if (!frame_valid_1d_oh)
                line_valid_1d_odd <= 1'b0;
            else if (line_valid_1d)
                line_valid_1d_odd <= ~line_valid_1d_odd;
            else
                line_valid_1d_odd <= 1'b0;
        end
    end

    // rgb565でラッチしているので、line_valid_1d_odd を1クロック遅延する
    always @(posedge pclk) begin
        if (preset) begin
            line_v_1d_odd_1d <= 1'b0;
            line_v_1d_odd_2d <= 1'b0;
        end else begin
            line_v_1d_odd_1d <= line_valid_1d_odd;
            line_v_1d_odd_2d <= line_v_1d_odd_1d;
        end
    end

    // RGB565 のデータを保存する。正常と上下反転ではバイト配列が異なる
    always @(posedge pclk) begin
        if (preset)
            rgb565 <= 32'd0;
        else begin
            case (line_valid_1d_odd)
                1'b0 : // 1番目
                    rgb565[31:13] <= {8'd0, cam_data_1d[7:3], 3'b000, cam_data_1d[2:0]};    // cam_data_1d = R7 R6 R5 R4 R3 G7 G6 G5
                1'b1 : // 2番目
                    rgb565[12:0] <= {cam_data_1d[7:5], 2'b00, cam_data_1d[4:0], 3'b000};    // cam_data_1d = G4 G3 G2 B7 B6 B5 B4 B3
            endcase
        end
    end

    // rgb565 を 1 クロック遅延する
    always @(posedge pclk) begin
        if (preset) begin
            rgb565_1d <= 32'd0;
        end begin
            rgb565_1d <= rgb565;    
        end
    end

    // line_valid の falling edge の検出
    always @(posedge pclk) begin
        if (preset) begin
            last_pixel_4_line <= 1'b0;
        end else if (line_valid_1d==1'b0 && line_valid_2d==1'b1) begin // line_valid_1d の falling edge
            last_pixel_4_line <= 1'b1;
        end else if (line_v_1d_odd_2d) begin
            last_pixel_4_line <= 1'b0;
        end
    end

    // frame_valid が 1 になってから初めてのピクセルを示す
    always @(posedge pclk) begin
        if (preset) begin
            first_pixel <= 1'b0;
        end else if (frame_valid_1d==1'b1 && frame_valid_2d==1'b0) begin // frame_valid_1d rising edge
            first_pixel <= 1'b1;
        end else if (line_v_1d_odd_2d) begin // first pixel
            first_pixel <= 1'b0;
        end
    end

    // pixel FIFO をインスタンスする
    pixel_fifo pfifo (
        .rst(areset), // input rst
        .wr_clk(pclk), // input wr_clk
        .rd_clk(aclk), // input rd_clk
        .din({last_pixel_4_line, first_pixel, rgb565_1d}), // input [33 : 0] din
        .wr_en(line_v_1d_odd_2d), // input wr_en
        .rd_en(pfifo_rd_en), // input rd_en
        .dout(pfifo_dout), // output [33 : 0] dout
        .full(pfifo_full), // output full
        .almost_full(pfifo_almost_full), // output almost_full
        .overflow(pfifo_overflow), // output overflow
        .empty(pfifo_empty), // output empty
        .almost_empty(pfifo_almost_empty), // output almost_empty
        .underflow(pfifo_underflow), // output underflow
        .rd_data_count(pfifo_rd_data_count) // output [9 : 0] rd_data_count
    );
endmodule

`default_nettype wire

  1. 2015年05月13日 03:48 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

カメラ-AXI4-Stream出力IPの作製2(論理シミュレーション)

カメラ-AXI4-Stream出力IPの作製1(プロジェクト作製とpixel_fifo)”の続き。

前回は論理合成用とシミュレーション用のソースを作り、Vivado 2015.1 のプロジェクトを作製した。今回は、論理シミュレーションを行う。

Vivado 2015.1 で Flow navigator の Simulation -> Run Simulation -> Run Behavioral Simulation を選択して論理シミュレーションを行った。
下の図は 100 us シミュレーションを行ったところだ。
Cam_inf_AXIS_7_150513.png

なお、mt9d111_model.v は見やすくするため画像1フレームを大幅に小さくしている。
m_axis_tdata に画像データが出力される画像フレーム(vsync) の最初で、m_axis_tuser がアサートされているのがわかると思う。
href の切れ目でも m_axis_tlast がアサートされているのもわかると思う。これでとりあえずは問題ないだろう?

なお、カメラ・インターフェースのAXI4-Stream 版は以前に作ったことがあるのだった。XPS で作ってあって s2mm_fsync を使っていた。”カメラ・インターフェース用AXI4-Stream IPの作製4(HDLソース1)”にソースがある。これがあるのをすっかり忘れてしまって、今回は、AXI4-Master 版から書き直してしまった。
  1. 2015年05月13日 03:34 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

カメラ-AXI4-Stream出力IPの作製1(プロジェクト作製とpixel_fifo)

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム1(構想編)”のブロック図のカメラ-AXI4-Stream出力IPを作製する。

Vivado 2015.1 でZYBOに搭載されているZynq の xc7z010clg400-1 のプロジェクトを作製した。

プロジェクトフォルダ直下の hdl フォルダの下に VHDL フォルダと Verilog フォルダを作って、VHDLフォルダには、mt9d111_inf_axims.vhd 、Verilog フォルダには、mt9d111_axi_lite_slave.v、mt9d111_cam_cont.v を置いて、cam_inf_d11_axis_151 プロジェクトに Add Source した。

同様に、プロジェクトフォルダ直下の simulation フォルダに mt9d111_inf_axis_tb.v、mt9d111_model.v を置いて、cam_inf_d11_axis_151 プロジェクトに Add Source した。
Cam_inf_AXIS_1_150511.png

pixel_fifo を生成した。

Basic タブ。
Fifo Implementation を Independent Clocks Block RAM に設定した。
Cam_inf_AXIS_2_150511.png

Native Ports タブ。
Write Width を 34 、Write Depth を 512 に設定した。
Cam_inf_AXIS_3_150511.png

Status Flags タブ。
Almost Full Flag と Almost Empty Flag、Overflow、Underflow Flag にチェックを入れた。
Cam_inf_AXIS_4_150511.png

Data Counts タブ。
Read Data Counts (Synchronized with Read Clk) にチェックを入れた。
Cam_inf_AXIS_5_150511.png

Summary タブ。
Cam_inf_AXIS_6_150511.png

なお、データ幅が 34 なのは、TLAST、TUSER、ピクセル・データ 32 ビットがパックされているからだ。
  1. 2015年05月11日 21:54 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

さくらんぼ狩りとさくらんぼジャム2

さくらんぼ狩りとさくらんぼジャム1”の続きです。

今日もさくらんぼを収穫しました。
こんな風に実っています。
Cherry_4_150510.jpg

木の間から覗く青空が綺麗でした。
Cherry_5_150510.jpg

枝を高枝切りバサミで切り取って、ボールの上でキッチンバサミでヘタごと切り取っていきます。
Cherry_6_150510.jpg

ボール2つがいっぱいになりました。昨日の2倍弱取れました。
Cherry_7_150510.jpg

まだまだ、さくらんぼはありますが、今日はこれで終わりにしました。来週も取れれば良いですが、来週までに鳥に全て食べられちゃうと思います。。。
  1. 2015年05月10日 19:57 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム1(構想編)

FPGAの部屋のブログで書いてきた Vivado HLS で作製したAXI4-Stream版ラプラシアンフィルタ IP を使用したカメラ表示システムを作製しようと思う。

システムを構築するボードはいつも使っている Zynq 7010 を搭載した ZYBO とする。
ブロック図を作製したので、下に示す。

AXIS_cam_disp_1_150510.png

CMOSカメラから撮影された画像が”カメラーAXI4-Stream出力”モジュールに入って、AXI4-Stream に変換されて出てくる。このモジュールは、既存の”カメラ・インターフェース”モジュールを変更する。AXI4-Master出力だったので、AXI4-Stream に変更中だ。ついでにと言うか AXI4-Master出力のデータバス幅は 64 ビット長だったので、32 ビット幅に変更する。

”AXI4-Stream入力ラプラシアンフィルタ”は今まで作ってきた Vivado HLS 2014.4 の 100 MHz 動作バージョンを使用する。

それで、ラプラシアンフィルタ出力画像だけでなく、カメラの生画像を見たいので、”AXI4-Stream Switcher”を Vivado HLS 2014.4 で新たに作製する。”AXI4-Stream Switcher”はカメラの画像のAXI4-Stream とラプラシアンフィルタを通した画像の AXI4-Stream を切り替える。

AXI4-Stream から AXI4-Master で DDR3 SDRAM へ画像をDMAする部分は、Xilinx 社の IP の”AXI VDMA”を使おうと思っているが、私が使って動いたことが無いため、Vivado HLS で自作することも考えている。

Vivado HLS は2014.4 を使用したいと思うが、Vivado は最新の 2015.1 を使いたいと思う。この組み合わせでうまくいくかどうかも検証したい。

(2015/11/15:追記)
ブロック図が間違っていたので、下に示す。
AXI4-Stream は1対1で接続する必要があり、分岐はできないため1つを2つに分けることもできない。AXI4 Stream Switcher を置いて、1本のAXI4 Stream を2本のAXI4 Stream に分けた。
AXIS_cam_disp_1_151115.png
  1. 2015年05月10日 04:45 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

さくらんぼ狩りとさくらんぼジャム1

最近、FPGAの話題じゃないのが多いですが、今回はさくらんぼ狩りとそのさくらんぼを使ったジャムのブログです。

さくらんぼ狩りの様子は写真を取るのを忘れてしまったので、明日、第2弾としてブログにアップしますが、今日は収穫したさくらんぼを使ったさくらんぼジャムのお話です。

家のさくらんぼの木にさくらんぼがたくさん実をつけたので、さくらんぼを収穫してジャムを作ります。
1粒1粒、種とヘタを取って身と皮だけを取り分けました。結構これが大変なんです。さくらんぼの汁も飛んで、ベタベタになりますよ。
Cherry_1_150509.jpg

さくらんぼの身と皮の重量の約6割の白砂糖と本当はレモンを入れるんですが、無かったので、マクドナルドでレモンティーの時にもらった、1人分のレモンパックを入れました。
これで煮詰めます。アク取りをしながら、煮詰めていくと出来上がりです。
Cherry_2_150509.jpg

大体、4つの瓶に収まりました。味見をしたのですが、かなり緩いけれど、程よい酸味と桜餅の香りのおいしいジャムになりました。明日が楽しみです。
Cherry_3_150509.jpg

朝、ヨーグルトに入れて食べましたが、とっても美味しいです。毎日、食べていますよ。
  1. 2015年05月09日 22:08 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

1x4 材で本棚を作ります2

1x4 材で本棚を作ります1”の続きです。

今日、本棚が完成しました。
前回の段階で、ほとんど、完成していたので午前中に完成させることができました。
bookrack_12_150509.jpg

奥さんが走りに行っていたので、帰って来てから午後5時40分頃設置しました。
bookrack_13_150509.jpg
設置した時、壁に向かって左が上がり気味でしたが、本を入れたら大丈夫でしょう?家も狂っているかも知れませんし、本棚が狂っているかもしれません。本当は現地で削って調整しますが、今回は省略ということで。。。
  1. 2015年05月09日 21:51 |
  2. 木工
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 vs Vivado HLS 2015.1(AXI4-Stream版ラプラシアンフィルタ IPの比較)

AXI4-Stream版ラプラシアンフィルタ IPで、Vivado HLS 2014.4 と Vivado HLS 2015.1 の高位合成結果を比較してみた。

C++ ソースコードは、両方とも同じもので、”Vivado 2014.4 でAXI4-Stream版ラプラシアンフィルタIP を作製する1(C++ ソースコードの公開)”で示したものだ。但し、lap_filter_axis.h は実際の 800 x 600 ピクセルに変更した。lap_filter_axis.h を下に示す。

// lap_filter_axis.h
// 2015/05/01

#define HORIZONTAL_PIXEL_WIDTH 800
#define VERTICAL_PIXEL_WIDTH 600

// #define HORIZONTAL_PIXEL_WIDTH 50
// #define VERTICAL_PIXEL_WIDTH 10

#define ALL_PIXEL_VALUE (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)


クロック周期 10 ns
クロック周期 10 ns で Vivado HLS 2014.4 で高位合成を行った結果を示す。
lap_filter_AXIS_52_150507.png
Timing の Estimated は 7.58 ns だった。FF は 382 個、LUT は 626 個使用している。

クロック周期 10 ns で Vivado HLS 2015.1 で高位合成を行った結果を示す。
lap_filter_AXIS_53_150507.png
Timing の Estimated は 9.40 ns だった。FF は 387 個、LUT は 556 個使用している。


クロック周期 9 ns
クロック周期 9 ns で Vivado HLS 2014.4 で高位合成を行った結果を示す。
lap_filter_AXIS_54_150507.png
Timing の Estimated は 7.58 ns だった。FF は 382 個、LUT は 626 個使用している。つまり、クロック周期が 10 ns の時と同じ回路だと思う。

クロック周期 9 ns で Vivado HLS 2015.1 で高位合成を行った結果を示す。
lap_filter_AXIS_55_150507.png
Timing の Estimated は 9.40 ns で、クロック周期 9 ns に対して遅くなりエラーになっている。FF は 387 個、LUT は 556 個使用している。これも、クロック周期が 10 ns の時と同じ回路だと思う。


クロック周期 8 ns
クロック周期 8 ns で Vivado HLS 2014.4 で高位合成を行った結果を示す。
lap_filter_AXIS_56_150507.png
Timing の Estimated は 6.91 ns だった。FF は 435 個、LUT は 627 個使用している。今回は回路が変更されているようだ。

クロック周期 8 ns で Vivado HLS 2015.1 で高位合成を行った結果を示す。
lap_filter_AXIS_57_150507.png
Timing の Estimated は 9.40 ns で、クロック周期 8 ns に対して遅くなりエラーになっている。FF は 436 個、LUT は 556 個使用している。FF が増えているので回路は変更されているようだ。

クロック周期 7 ns
クロック周期 7 ns で Vivado HLS 2014.4 で高位合成を行った結果を示す。
lap_filter_AXIS_58_150507.png
Timing の Estimated は 6.88 ns だった。FF は 455 個、LUT は 627 個使用している。今回も回路が変更されているようだ。

クロック周期 7 ns で Vivado HLS 2015.1 で高位合成を行った結果を示す。
lap_filter_AXIS_59_150507.png
Timing の Estimated は 8.77 ns で、クロック周期 8 ns に対して遅くなりエラーになっている。FF は 455 個、LUT は 564 個使用している。FF が増えているので回路は変更されているようだ。

クロック周期 6 ns
クロック周期 6 ns で Vivado HLS 2014.4 で高位合成を行った結果を示す。
lap_filter_AXIS_60_150507.png
Timing の Estimated は 6.88 ns で、クロック周期 6 ns に対して遅くなりエラーになっている。FF は 473 個、LUT は 637 個使用している。

クロック周期 6 ns で Vivado HLS 2015.1 で高位合成を行った結果を示す。
lap_filter_AXIS_61_150507.png
Timing の Estimated は 6.88 ns で、クロック周期 6 ns に対して遅くなりエラーになっている。FF は 473 個、LUT は 566 個使用している。

クロック周期 2.5 ns
クロック周期 2.5 ns で Vivado HLS 2014.4 で高位合成を行った結果を示す。限界を見極めるためだ。
lap_filter_AXIS_62_150507.png
Timing の Estimated は 6.88 ns で、クロック周期 2.5 ns に対して遅くなりエラーになっている。FF は 1367 個、LUT は 693 個使用している。これだけFFを使用してもタイミングを満たさないので、ここが限界のようだ。

クロック周期 2.5 ns で Vivado HLS 2015.1 で高位合成を行った結果を示す。
lap_filter_AXIS_63_150507.png
Timing の Estimated は 6.88 ns で、クロック周期 2.5 ns に対して遅くなりエラーになっている。FF は 1335 個、LUT は 622 個使用している。これだけFFを使用してもタイミングを満たさないので、こちらもここが限界のようだ。

FF や LUT の数に差があるけれども、どちらのバージョンを使いたいか?は上記の結果を見れば、明らかだと思う。
  1. 2015年05月08日 05:45 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

1x4 材で本棚を作ります1

昨日から 1x4 材で本棚を作っています。

昨日は、グランステージに 1x4 材を買い出しに行きました。作る予定の本棚は 2440x638x180mm です。
1x4 材は 12F があったので、4本を 2440mm と 1270mm 程度に切ってもらいました。
棚板と連結板用に 6F の 1x4 材を6本買ってきました。合計金額は3300円程度でした。
家に帰ってから、棚板と連結板を切り出しました。棚板はぴったり 600mm、連結板は 170mm 程度です。スライド鋸で切ると、0.2mm 程度の誤差で切れると思います。見本と触って段差がないようにセットしてから切ります。
切り終えて今日は終わりです。ちなみに、これが家の作業場のスライド鋸と手押しかんな盤です。
bookrack_1_150506.jpg


今日は、まずは棚板の位置を縦の板に鉛筆でマーキングします。棚数は10なので、11箇所マーキングです。上3つの棚はコミックが入るように200mm の間隔にします。6つの棚は少し大きめの230mm の間隔です。後は残りの一番下の棚は250mm でした。

2つの1x4材の縦の板を2つ並べて、4箇所を連結板で連結します。その際には木工用クランプを使って締め上げます。その際に4mmのシナベニヤを間隔を確保するために間に挟みました。長さ32mmのコーススレッドを使いました。
製材した木はどうしても曲がっているので修正が必要です。買ってくるときになるべく真っ直ぐな物を選別してきますが、どうしても曲がっています。これで曲がりは修正出来ました。
bookrack_2_150506.jpg

bookrack_3_150506.jpg

間に挟む4mmのシナベニヤは、板を付けて上から挟んでも抜けないように自分で加工してあります。
bookrack_4_150506.jpg

これを2つ作りました。
bookrack_5_150506.jpg

縦の板を木工用クランプで作業台に固定して棚板をコーススレッドで固定していきます。下の棚板を固定したら、やはり、4mm のシナベニヤを挟んで、上の棚板を固定します。ここは、強度が必要だし長さ65mmの半ねじコーススレッドを使いました。半ねじを使うと連結対象との間に隙間があっても板が引き込まれて、よく締まります。
bookrack_6_150506.jpg

bookrack_7_150506.jpg

棚板を取り付けるときには、皿取錐で下穴を開けてから、コーススレッドをねじ込んでいきます。
皿取錐は、本来は埋木をしてコーススレッドを見えなくするものですが、浅く皿ネジの形に削ることによって、木の面を触ってもコーススレッドの頭が手に触らないようにします。皿取錐&埋木錐セットで購入しました。
bookrack_8_150506.jpg

だいぶできてきました。
bookrack_9_150506.jpg

なお棚板をつけている時は、直角を確認しながらやっています。縦の板の長さが正確なのと、棚板も0.2mmくらいの精度で切ってあるので、マーキングに合わせるとぴったり直角でした。
bookrack_10_150506.jpg

結構、重くなってしまったので、息子を呼んできて本棚を回転しました。下に作業台が無いと作業がやりにくいからです。
bookrack_11_150506.jpg

今日はこの辺りで終了。次回に完成させます。
  1. 2015年05月06日 20:14 |
  2. 木工
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 でAXI4-Stream版ラプラシアンフィルタIP を作製する5(IP化)

Vivado HLS 2014.4 でAXI4-Stream版ラプラシアンフィルタIP を作製する4(RTLシミュレーション2)”の続き。

2015/06/25:修正AXI4-Stream版ラプラシアンフィルタのC++ ソースコード lap_filter_axis.cpp にバグがあったので、ブログを書き換えました)

前回は、動作周波数を 100 MHz から 150 MHz にすることで、性能が 1.49 倍に向上した。今回は、シミュレーション用に少ないピクセルの行列でやっていたのを 800 x 600 ピクセルに戻して、高位合成、IP 化を行う。

まずは、lap_filter_axis.h を以下の様に書き換えた。

// lap_filter_axis.h
// 2015/05/01

#define HORIZONTAL_PIXEL_WIDTH 800
#define VERTICAL_PIXEL_WIDTH 600

// #define HORIZONTAL_PIXEL_WIDTH 50
// #define VERTICAL_PIXEL_WIDTH 10

#define ALL_PIXEL_VALUE (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)


これで 800 x 600 ピクセルになったので、クロック周期を 10 ns に戻して高位合成を行った。
lap_filter_AXIS_50_150504.png

Utilization Estimates を示す。BRAM_18K を 2 個使用している。FF も LUT も当然ながら使用量が増えている。
lap_filter_AXIS_51_150504.png

Export RTL ボタンをクリックして IP 化を行った。
  1. 2015年05月05日 04:36 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2014.4 でAXI4-Stream版ラプラシアンフィルタIP を作製する4(RTLシミュレーション2)

Vivado HLS 2014.4 でAXI4-Stream版ラプラシアンフィルタIP を作製する3(RTLシミュレーション)”の続き。

2015/06/25:修正AXI4-Stream版ラプラシアンフィルタのC++ ソースコード lap_filter_axis.cpp にバグがあったので、ブログを書き換えました)

前回は、クロック周期を 10 ns つまり 100 MHz で高位合成を行った。今回は、動作周波数の限界に挑戦してみようと思う。
横 50ピクセル、縦 10行の画像で高位合成した回路を使用している

最初に 2.5 ns のクロック周期で高位合成を行ったが、限界は 6.62 ns だった。それじゃということで、150 MHz の 6.66 ns で高位合成を行った。その結果を下に示す。
lap_filter_AXIS_43_150504.png

Utilization Estimates を示す。やはり、BRAM_18K は使用していないが、DSP48E は5 個使用している。
FFはクロック周期が 10 ns の時よりも 44 個多い。LUT は 2 個多いだけだった。
lap_filter_AXIS_44_150504.png

Analyze 画面を示す。真ん中下のResource タブをクリックしている。
クロック周期が 10 ns の時は、C8 ステートまでだったが、C12 ステートまでとなった。
lap_filter_AXIS_45_150504.png

lap_filter_AXIS_46_150504.png

Co-simulation Dialog の Dump Trace を all に変更して、Co-simulation を行った。

Vivado 2014.4 を立ち上げて、RTLシミュレーション波形を見てみた。
lap_filter_AXIS_47_150504.png

やはり、スループットは 1 クロックだった。

シミュレーションの最初の部分と最後の部分を示す。
lap_filter_AXIS_48_150504.png

lap_filter_AXIS_49_150504.png

クロック周期は 6.66 ns (クロック周波数は 150 MHz)だった。レイテンシは最後の入力ストリームの破線のカーソルと最後の出力ストリームの実線のカーソル間の 46.62 ns で、クロック数は 66.600 ns / 6.66 ns = 10 クロックだ。クロック周期が 10 ns の時は 6 クロックなので、4 クロック増えている。それでも完全にパイプラインされているので、レイテンシは最初と最後にしか見えない。つまり、AXI4-Master 版の完全にパイプラインされていない場合よりもクロック周波数を増加した場合の性能向上が大きいといえる。
それでは、ラプラシアンフィルタ全体の処理時間を計算してみよう。TUSERが 1 にアサートされたのを認識した時から、出力ストリームの最後までのラプラシアンフィルタ全体の処理時間は

3613.050 ns - 283.050 ns = 3330.000 ns

となった。
クロック周期が 10 ns の場合のラプラシアンフィルタ全体の処理時間は、5020 ns だったので、何倍になっているか計算してみよう。

5020 ns / 3330 ns ≒ 1.51 倍

となった。
周波数が 1.5 倍になっているので、ラプラシアンフィルタの処理速度は、大体 1.5 倍程度になっている。
  1. 2015年05月04日 05:24 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0
»