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

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

FPGAの部屋

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

Synchronizer IP をVivado 2016.2 で作った

PmodHB5 のセンサー・フィードバック処理IP を作ってみた2”で作ったセンサー・フィードバック端子の入力はベースクロックに同期しているわけではないので、シンクロナイザーが必要だ。
FF を数個並べてシンクロナイザーを作ることにした。これはVivado HLS で作ろうとしてみたのだが、やはり作ることは難しい。
そこで、久しぶりにVerilog HDL を使って、シンクロナイザーを作ることにした。

ZYBO 用のVivado 2016.2 のSynchronizer プロジェクトを作成した。
Synchronizer_1_160630.png

Synchronizer.v を示す。 for 文を使用している。

// Synchronizer.v
// 2016/06/29 by marsee
//

`default_nettype none

module Synchronizer #(
    parameter   integer NUMBER_OF_STAGES    = 2,
    parameter   integer BIT_WIDTHS          = 1
)(
    input   wire                        clk,
    input   wire    [BIT_WIDTHS-1:0]    inp,
    output  wire    [BIT_WIDTHS-1:0]    outp
);

    integer i;
    reg [BIT_WIDTHS-1:0] ff[NUMBER_OF_STAGES-1:0];

    always @(posedge clk) begin : proc_gen_ff
        for (i=0; i<=NUMBER_OF_STAGES-1; i=i+1) begin
            if (i==0) begin
                ff[i] <= inp;
            end else begin
                ff[i] <= ff[i-1];
            end
        end
    end
    assign outp = ff[NUMBER_OF_STAGES-1];
endmodule

`default_nettype wire


NUMBER_OF_STAGES にFF の段数を与えて、BIT_WIDTHS にビット幅を与えるとその条件のシンクロナイザーを生成してくれる。

次にテストベンチの Synchronizer_tb.v を貼っておく。

// Synchronizer_tb.v
// 2016/06/29 by marsee
//

`timescale 100ps / 1ps

module Synchronizer_tb;
    parameter   integer NUMBER_OF_STAGES    = 2;
    parameter   integer BIT_WIDTHS          = 1;

    reg     [BIT_WIDTHS-1:0]    inp;
    wire    [BIT_WIDTHS-1:0]    outp;
    reg     clk = 1'b0;

    Synchronizer # (
        .NUMBER_OF_STAGES(NUMBER_OF_STAGES),
        .BIT_WIDTHS(BIT_WIDTHS)
    ) uut (
        .clk(clk),
        .inp(inp),
        .outp(outp)
    );

    initial begin
        inp = 0;

        #1000
        inp = -1;

        #7000
        inp = 0;
    end

    always #(500)
        clk = ~clk;

endmodule


このシミュレーション結果を示す。
Synchronizer_2_160630.png

次に、

parameter   integer NUMBER_OF_STAGES    = 3;
parameter   integer BIT_WIDTHS          = 8;

の場合のシミュレーション結果を示す。
Synchronizer_3_160630.png

Elaborated Desgin の Schematic を示す。
Synchronizer_4_160630.png

Tools メニューから Create and Package IP... を選択してIP 化を行った。
Synchronizer_5_160630.png

Synchronizer_6_160630.png

Synchronizer.srcs フォルダの下に component.xml ができた。Synchronizer.srcs フォルダをコピーしてIP として使えるようだ。
Synchronizer_7_160630.png
  1. 2016年06月30日 04:55 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

KiCad-4.0.2 で既存のコンポーネントを修正して、新しいコンポーネント・ライブラリを作る方法

今日は、KiCad の回路図のシンボルにHD74LVC541A がないので、74LS541 をコピーして新しいコンポーネント・ライブラリを作成した。

最初にKiCad の初期画面からコンポーネント ライブラリ エディタを選択する。
MT9D111_inf_19_160627.png

最初に”作業ライブラリの選択”ボタンをクリックする。
MT9D111_inf_20_160627.png

ライブラリの選択ダイアログで、74xx を選択する。
MT9D111_inf_21_160627.png

コンポーネント ライブラリ エディタで、”現在のライブラリから、エディタへコンポーネントを読み込む”ボタンをクリックする。
MT9D111_inf_22_160627.png

74LS541 を選択する。
MT9D111_inf_23_160627.png

74LS541 を右クリックして、フィールドを編集を選択する。
MT9D111_inf_24_160627.png

コンポーネント名を HD74LVC541A に変更する。
MT9D111_inf_25_160627.png

コンポーネント名を HD74LVC541A に変更された。
”新しいライブラリへ現在のコンポーネントを保存”ボタンをクリックする。
MT9D111_inf_26_160627.png

ファイル名を 74xx_Ono.lib という名前でコンポーネント ライブラリを保存する。
MT9D111_inf_27_160627.png

回路図エディタを設定する。
設定メニューからコンポーネント ライブラリを選択する。
MT9D111_inf_28_160627.png

コンポーネント ライブラリのダイアログで、追加ボタンをクリックする。
MT9D111_inf_29_160627.png

先ほど作った 74xx_Ono.lib を指定する。
MT9D111_inf_30_160627.png

74xx_Ono.lib が追加された。
MT9D111_inf_31_160627.png

回路図エディタで”コンポーネントの配置”ボタンを選択すると、HD74LVC541A が見えた。
MT9D111_inf_32_160627.png
  1. 2016年06月29日 04:40 |
  2. CADツール
  3. | トラックバック:0
  4. | コメント:0

KiCad-4.0.2 のフットプリント作成方法

最近KiCadの勉強会をしている。最後の課題で自分で好きなものを作ってくるという課題を自分で出した。私は、Zybot に付けるステレオカメラ用に延長コードで延長できるようにバッファのついてMT9D111 拡張ボードを作ることにした。その際に、フットプリントを作成したのだが、その作成方法を覚書として書いておこう。

フットプリントの作り方は、トランジスタ技術の「KiCadの回路記号&フットプリントを作る方法」を参考にしている。

作成するフットプリントは、秋月電子の「8回路3ステートバッファHD74LVC541A(10個入)」だ。これは、TSSOP パッケージだ。
フットプリントは、HD74LVC541Aのデータシートを見ながら作成した。

最初にKiCad の初期画面からフットプリント エディタを選択する。
MT9D111_inf_1_160627.png

”新規フットプリント”ボタンをクリックし、TSSOP_20_Pin と名前を付けた。
MT9D111_inf_2_160627.png

TSSOP_20_Pin の新規フットプリントが生成された。
”新規ライブラリを生成して現在のフットプリントを保存”ボタンをクリックした。
MT9D111_inf_3_160627.png

”フットプリント ライブラリのフォルダを指定”ダイアログが表示された。パスのベースとライブラリ フォルダを入力した。
MT9D111_inf_4_160627.png

設定メニューからフットプリント ライブラリ ウィザードを選択した。
MT9D111_inf_5_160627.png

”フットプリントライブラリの追加ウィザード”ダイアログが表示された。”このコンピュータにあるファイル”のラジオボタンが選択されていた。そのまま”Next >”ボタンをクリックした。
MT9D111_inf_6_160627.png

追加するライブラリフォルダを選択した。SMD_Packages_Ono.pretty
Next >ボタンをクリックした。
MT9D111_inf_7_160627.png

SMD_Packages_Ono ライブラリが選択された。Next >ボタンをクリックした。
MT9D111_inf_8_160627.png

新しいライブラリの追加方法を選択する。グローバル ライブラリとして設定するか?現在のプロジェクトのみとするか?
今回は現在のプロジェクトのみとした。
MT9D111_inf_9_160627.png

次に、”アクティブなライブラリを選択してください”ボタンをクリックして、SMD_Packages_Ono ライブラリを選択した。
MT9D111_inf_10_160627.png

アクティブなライブラリがSMD_Packages_Ono ライブラリになった。
”新しいフットプリントにフットプリント ウィザードを使用”ボタンをクリックした。
MT9D111_inf_11_160627.png

フットプリント ウィザードが立ち上がった。フットプリント作成ツールのダイアログで、SOIC を選択した。
MT9D111_inf_12_160627.png

SOIC ウィザードの起動画面。パラメータの定数を指定していくことで、フットプリントを作成するようだ。
MT9D111_inf_13_160627.png

HD74LVC541Aのパッケージの情報を示す。、HD74LVC541Aのデータシートから引用する。
MT9D111_inf_14_160627.png

値を指定することでフットプリントを作成する。なおこの値を決定するについては、”パナソニックの参考ランド寸法”を参考にさせていただいた。
ピン数は20ピンで、pad pitch は 0.65 mm 、Pad width は 0.3 mm とした。これは、0.3 mm 以上パッドのギャップがないとハンダがブリッジしやすいとのことだ。
パッド長は内側に 0.3 mm + IC のピン長 0.5 mm + 誤差 0.1 mm + 手付けパッド長 0.8 mm = 1.7 mm とした。
row spacing は、チップの横幅 + パッド長だった。
row spacing は、チップの横幅 4.4 mm + チップのピンの長さ 1 mm - パッドに接触するピン長 0.5 mm + 誤差 0.1 mm - パッドのパッドに接触するピン長から内側の部分 0.3 mm + パッド長 1.7 mm = 6.3 mm を設定した。
MT9D111_inf_15_160627.png

Body は outline x margin は 0.7 mm、outline y margin は 0.6 mm に設定した。
MT9D111_inf_16_160627.png

フットプリントの名称を”TSSOPIC-20”に変更した。
”アクティブなライブラリへフットプリントを保存”ボタンをクリックして、フットプリントを保存する。
MT9D111_inf_17_160627.png

”TSSOPIC-20”という名前で保存した。
MT9D111_inf_18_160627.png

いつの間にか、KiCad のフットプリント エディタのウィザードでフットプリントを生成できるようになっていた。。。
  1. 2016年06月28日 05:51 |
  2. CADツール
  3. | トラックバック:0
  4. | コメント:0

舌下免疫療法(スギ花粉症)

舌下免疫療法(スギ花粉症)を今日からやり始めた。

舌下免疫療法とは、スギ花粉を薄めたの?を毎日、舌下に入れて、スギ花粉に体を慣らす療法だ。

参考、トリーさんの免疫療法ナビ

この療法は、研修を受けた医師がいる医療機関でしか受けることができない。舌下免疫療法を受けられる医療機関は上の参考のURLにある。
始めの日は、シダトレン スギ花粉 200JAU/mLのボトルをもらって、1日1回、1プッシュを舌下に投与した。これは医療機関で行って、30分間そこにいなくてはいけない。重篤なアレルギー症状、アナフィラキシーショックにならないかを見るためだ。
大丈夫だったら、次の日は自宅で、1日に1プッシュ、舌下に投与する。
3日目からは2プッシュ、5日目に3プッシュ、6日目は4プッシュ、7日目は5プッシュというようにだんだんと増やしていく。

8日目からは、2,000JAU/mL のボトルとなる。つまり濃度は10倍だ。それをまた同様に増やしていく。なお、このときは医療機関を受診する必要がある。

15日目からは、2,000JAU/mL のパックを1本舌下に投与する。これを3年から5年程度続けるそうだ。

途中で具合が悪くなったら、当然中止するようだ。薬価は割と安いようだ。今日のボトルは保険使わなくても400円くらいらしい。(保険は適用されます)
これで、スギ花粉症が良くなるとよいな。あと、ヒノキの方がひどいので、ヒノキもやりたい。
  1. 2016年06月25日 19:01 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

PmodHB5 のセンサー・フィードバック処理IP を作ってみた2

PmodHB5 のセンサー・フィードバック処理IP を作ってみた”の続き。

(2016/07/02 修正: ”PmodHB5 のセンサー・フィードバック処理IP を作ってみた3”で大幅に修正しました。そちらをご覧ください)

PmodHB5 のセンサー・フィードバック処理IP motor_monitor.cpp などを修正した。
まずは、overflow 引数と return 値の両方で overflow を知らせているのは無駄なので、return 値で overflow を知らせることにして、overflow 引数は削除した。
motor_monitor.cpp を示す。

// motor_monitor.cpp
// 2016/06/15 by marsee
// 2016/06/25 : overflow を削除、return 1 のときがoverflow
//

#include <ap_int.h>
#include <hls_stream.h>

#include "motor_monitor.h"

int motor_monitor(hls::stream<ap_uint_1 >& sa, hls::stream<ap_uint_1 >& sb,
        ap_uint<32> & sa_count, ap_uint_1 & sb_level){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE s_axilite port=sb_level
#pragma HLS INTERFACE s_axilite port=sa_count
#pragma HLS INTERFACE ap_hs port=sa
#pragma HLS INTERFACE ap_hs port=sb

    ap_uint_1 sad;
    ap_uint_1 sbd;

    for (sa_count=0; sa_count!=0xffffffff; sa_count++){ // sa=1の間はループ
#pragma HLS PIPELINE II=1
        sa >> sad;
        sb >> sbd;
        if (!sad)
            break;
    }
    if (sa_count == 0xffffffff) // overflow
        return 1;

    for (sa_count=1; sa_count!=0xffffffff; sa_count++){ // sa=0の間はループ
#pragma HLS PIPELINE II=1
        sa >> sad;
        sb >> sbd;
        if (sad)
            break;
    }
    if (sa_count == 0xffffffff) // overflow
        return 1;

    sb_level = sbd; // saが立ち上がった時のsbの値

    for (sa_count++; sa_count!=0xffffffff; sa_count++){ // sa=1の間はループ
#pragma HLS PIPELINE II=1
        sa >> sad;
        sb >> sbd;
        if (!sad)
            break;
    }
    if (sa_count == 0xffffffff) // overflow
        return 1;

    return 0;
}


motor_monitor_tb.cpp を示す。

// motor_monitor_tb.cpp
// 2016/06/15 by marsee
// 2016/06/25 : overflow を削除、return 1 のときがoverflow
//

#include <ap_int.h>
#include <hls_stream.h>

#include "motor_monitor.h"

#define DATASIZE 20

int motor_monitor(hls::stream<ap_uint_1 >& sa, hls::stream<ap_uint_1 >& sb,
        ap_uint<32> & sa_count, ap_uint_1 & sb_level);

int main(){
    using namespace std;

    ap_uint_1 saa[DATASIZE] = {1,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,1,1};
    ap_uint_1 sba[DATASIZE] = {0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1};

    hls::stream<ap_uint_1> sa;
    hls::stream<ap_uint_1> sb;

    ap_uint<32> sa_count;
    ap_uint_1 sb_level;

    for (int i=0; i<DATASIZE; i++){
        sa << saa[i];
        sb << sba[i];
    }
    int ret_val = motor_monitor(sa, sb, sa_count, sb_level);

    printf("sa_count = %d, sb_level = %d, return value = %d\n",
            (unsigned int)sa_count, (unsigned int)sb_level, ret_val);

    return 0;
}


なお、motor_monitor.h には修正がない。

これでC シミュレーションを行った。
motor_monitor_1_160625.png

C コードの合成を行った。
motor_monitor_2_160625.png

リソースが少なくなった。

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

Latency は 2 クロック分少なくなっている。

C/RTLコシミュレーションの波形を示す。
motor_monitor_4_160625.png

Wait が合計3クロックから 1 + 1 = 2 クロックに減少した。
  1. 2016年06月25日 05:22 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で PWM モジュールIP を作ってみた2

Vivado HLS で PWM モジュールIP を作ってみた”の続き。

PmodHB5インターフェース回路 (PmodHB5_inf) の作成”でPWMモジュールのバグがわかったので、修正を行う。

修正したC ソースコードを示す。なお、引数の渡し方をポインタ渡しから参照渡しに変更した。

// PWMmodule.cpp
// 2016/06/08 by marsee
// PmodHB5のPWM用モジュール
// 100MHzのクロックで2KHz(500us)のPWM出力(最初に500分周する)

#include <ap_int.h>

#define PWM_MAX_VALUE    100
#define PWM_WAIT_CLOCK    500

void pwm(ap_uint<7> sw_late, ap_uint<1> dir,
        volatile ap_uint<1> & pwm_out, ap_uint<1>  & dir_out){
#pragma HLS INTERFACE s_axilite port=dir
#pragma HLS INTERFACE ap_none register port=dir_out
#pragma HLS INTERFACE ap_none register port=pwm_out
#pragma HLS INTERFACE s_axilite port=sw_late
#pragma HLS INTERFACE s_axilite port=return

    dir_out = dir;
    for (int i=1; i<=PWM_MAX_VALUE; i++){
        for (int n=0; n<PWM_WAIT_CLOCK; n++){
#pragma HLS PIPELINE II=1 rewind
            if (sw_late == 0){
                pwm_out = 0;
            } else if(i>=1 && sw_late >= i){
                pwm_out = 1;
            } else if (sw_late < i){
                pwm_out = 0;
            }
        }
    }
}


テストベンチも参照渡しに変更した。

// PWMmodule_tb.cpp
// 2016/06/08 by marsee
// PWMmodule用テストベンチ
//

#include <ap_int.h>

void pwm(ap_uint<7> sw_late, ap_uint<1> dir,
        volatile ap_uint<1> & pwm_out, ap_uint<1>  & dir_out);

int main(){
    ap_uint<1> pwm_out;
    ap_uint<1> dir_out;

    pwm(00, pwm_out, dir_out);
    pwm(501, pwm_out, dir_out);
    pwm(1000, pwm_out, dir_out);

    return 0;
}


これで、C コードの合成を行った。結果を示す。
PWMmodule_6_160624.png

FFが 8 個増えていた。

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

Latency も Interval も増えている。

C/RTLコシミュレーションの波形を示す。
PWMmodule_8_160624.png

PWMmodule_9_160624.png

PWM 1回の幅は500.39 us だった。前回よりも 70 ns 増えていたが、このくらいは問題ないと思う。
  1. 2016年06月24日 05:20 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

Zybot の車輪が回転した

Zybot の車輪がVivado HLS で作ったPWMモジュールで回転した。

ZYBOのVivado 2016.2を使用したブロック・デザインのベースはZYBO_0_2 を使用して、そこに左車輪と右車輪用のPWMモジュールを追加した。
ブロック・デザインを示す。
Zybot_14_160223.png

論理合成、インプリメントを行って、SDK を立ち上げ、アプリケーションを作成して、出来上がったので、早速モーターを回してみることにした。
Zybot_15_160223.png

アプリケーションの pwm_test.c を示す。

/* * PWM_test.c * *  Created on: 2016/06/20 *      Author: ono */

#include <stdio.h>
#include <stdlib.h>
#include "xil_io.h"
#include "xparameters.h"
#include "sleep.h"
#include "xpwm.h"

int main(){
    XPwm XPwmL, XPwmR;
    XPwm_Config *XPwmLPtr, *XPwmRPtr;

    XPwmLPtr = XPwm_LookupConfig(0);
    if(!XPwmLPtr){
        fprintf(stderr, "Left XPwm configuration failed.\n");
        return(-1);
    }
    int XPwmL_status = XPwm_CfgInitialize(&XPwmL, XPwmLPtr);
    if (XPwmL_status != XST_SUCCESS){
        fprintf(stderr, "Could not Initialize Left XPwm\n");
        return(-1);
    }

    XPwmRPtr = XPwm_LookupConfig(1);
    if(!XPwmRPtr){
        fprintf(stderr, "Right XPwm configuration failed.\n");
        return(-1);
    }
    int XPwmR_status = XPwm_CfgInitialize(&XPwmR, XPwmRPtr);
    if (XPwmR_status != XST_SUCCESS){
        fprintf(stderr, "Could not Initialize Right XPwm\n");
        return(-1);
    }

    XPwm_Set_sw_late_V(&XPwmL, 0); // 25で車輪が回り始める
    XPwm_Set_sw_late_V(&XPwmR, 0); // 25で車輪が回り始める

    XPwm_Set_dir_V(&XPwmL, 1); // 1 - 正転、0 - 逆転
    XPwm_Set_dir_V(&XPwmR, 0); // 0 - 正転、1 - 逆転

    XPwm_DisableAutoRestart(&XPwmL);
    while(!XPwm_IsIdle(&XPwmL)) ;
    XPwm_Start(&XPwmL);
    XPwm_EnableAutoRestart(&XPwmL);

    XPwm_DisableAutoRestart(&XPwmR);
    while(!XPwm_IsIdle(&XPwmR)) ;
    XPwm_Start(&XPwmR);
    XPwm_EnableAutoRestart(&XPwmR);

    return 0;
}


Zybot を箱の上に置いて、車輪が回転しても動かないようにした。センサー・フィードバック・ピンの SA と SB にオシロスコープのプローブを当てて、波形を観測できるようにした。その写真を示す。
Zybot_9_160623.jpg

このZybot では、右左のモーターが180度向きを変えて実装してあるので、前進するときはPmodHB5 の回転方向は、左モーターはDIR = 1, 右モーターはDIR = 0 にする必要がある。後進するときは、逆になる。

まずは、PWM 値が 25 % のときに回り始めた。80 % までやってみたが、車輪が勢い良く回っている。

次に右モーターだけであるが、各PWM 値でどの様な波形が出てくるか?を観察した。

PWM 25 % 、DIR = 0 の時 (XPwm_Set_sw_late_V(&XPwmR, 25); XPwm_Set_dir_V(&XPwmR, 0); )
Zybot_10_160623.jpg

CH1 が SA で CH2 が SB だ。周波数は 338 Hz で、周期は 29.6 ms だった。
SA の立ち上がり時の SB は0 になっている。

PWM 25 % 、DIR = 1 の時 (XPwm_Set_sw_late_V(&XPwmR, 25); XPwm_Set_dir_V(&XPwmR, 1); )
Zybot_11_160623.jpg

SA の立ち上がり時の SB は1 になっている。

PWM 50 % 、DIR = 1 の時 (XPwm_Set_sw_late_V(&XPwmR, 50); XPwm_Set_dir_V(&XPwmR, 1); )
Zybot_12_160623.jpg

周波数は 609 Hz で、周期は 16.4 ms だった。

PWM 80 % 、DIR = 1 の時 (XPwm_Set_sw_late_V(&XPwmR, 80); XPwm_Set_dir_V(&XPwmR, 1); )
Zybot_13_160623.jpg

周波数は 733 Hz で、周期は13.6 ms だった。
  1. 2016年06月23日 11:52 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

PmodHB5インターフェース回路 (PmodHB5_inf) の作成

PmodHB5: H-bridge Driver with Feedback Inputs のインターフェース回路を作成することにした。
今まで作ったPWM モジュールの”Vivado HLS で PWM モジュールIP を作ってみた”と”PmodHB5 のセンサー・フィードバック処理IP を作ってみた”を合わせたIP を作ろうということだ。

具体的には、pwm() と motor_monitor() を順にコールするのだが、関数を並列実行できるDATAFLOW 指示子を使って並列実行させようということだ。具体的なメインのC++ コードだけを示す。

int PmodHB5_inf(ap_uint<7> sw_late, ap_uint<1> dir, volatile ap_uint<1> & pwm_out, ap_uint<1> & dir_out,
        hls::stream<ap_uint_1 >& sa, hls::stream<ap_uint_1 >& sb,
        ap_uint<32> & sa_count, ap_uint_1 & sb_level, ap_uint_1 & overflow){
#pragma HLS DATAFLOW
#pragma HLS INTERFACE s_axilite port=dir
#pragma HLS INTERFACE ap_none register port=dir_out
#pragma HLS INTERFACE ap_none register port=pwm_out
#pragma HLS INTERFACE s_axilite port=sw_late
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE s_axilite port=overflow
#pragma HLS INTERFACE s_axilite port=sb_level
#pragma HLS INTERFACE s_axilite port=sa_count
#pragma HLS INTERFACE ap_hs port=sb
#pragma HLS INTERFACE ap_hs port=sa

    pwm(sw_late, dir, pwm_out, dir_out);

    motor_monitor(sa, sb, sa_count, sb_level, overflow);

    return 0;
}


ヘッダファイル PmodHB5_inf.h を示す。

// PmodHB5_inf.h
// 2016/06/20 by marsee
//

#ifndef __MOTOR_MONITOR___
#define __MOTOR_MONITOR___

#include <ap_int.h>

typedef ap_uint<1> ap_uint_1;

#define PWM_MAX_VALUE    100
#define PWM_WAIT_CLOCK    500
#define PWM_ALL_CLOCKS    ((PWM_MAX_VALUE)*(PWM_WAIT_CLOCK))
#endif


テストベンチファイル PmodHB5_inf_tb.cpp を示す。

// PmodHB5_inf_tb.cpp
// 2016/06/20 by marsee
//
// PWMとモーターのフィードバック・ピン処理用IP のテストベンチ
//

#include <ap_int.h>
#include <hls_stream.h>

#include "PmodHB5_inf.h"

#define SASB_PULSE_WIDTH 500

int PmodHB5_inf(ap_uint<7> sw_late, ap_uint<1> dir, volatile ap_uint<1> & pwm_out, ap_uint<1> & dir_out,
        hls::stream<ap_uint_1 >& sa, hls::stream<ap_uint_1 >& sb,
        ap_uint<32> & sa_count, ap_uint_1 & sb_level, ap_uint_1 & overflow);

int main(){
    using namespace std;

    hls::stream<ap_uint_1> sa;
    hls::stream<ap_uint_1> sb;

    ap_uint<32> sa_count;
    ap_uint_1 sb_level;
    ap_uint_1 overflow;

    ap_uint_1 pwm_out;
    ap_uint_1 dir_out;

    for (int i=0; i<PWM_ALL_CLOCKS; i++){
        if (i%SASB_PULSE_WIDTH <= SASB_PULSE_WIDTH/4)
            sa << (ap_uint_1)1;
        else if (i%SASB_PULSE_WIDTH > SASB_PULSE_WIDTH/4 && i%SASB_PULSE_WIDTH <= SASB_PULSE_WIDTH/4*3)
            sa << (ap_uint_1)0;
        else
            sa << (ap_uint_1)1;

        if (i%SASB_PULSE_WIDTH < SASB_PULSE_WIDTH/2)
            sb << (ap_uint_1)0;
        else
            sb << (ap_uint_1)1;
    }
    PmodHB5_inf(00, pwm_out, dir_out,
            sa, sb, sa_count, sb_level, overflow);
    PmodHB5_inf(501, pwm_out, dir_out,
            sa, sb, sa_count, sb_level, overflow);
    PmodHB5_inf(1000, pwm_out, dir_out,
            sa, sb, sa_count, sb_level, overflow);

    printf("*sa_count = %d, *sb_level = %d, overflow = %d\n",
               (unsigned int)sa_count, (unsigned int)sb_level, (unsigned int)overflow);

    return 0;
}


これでC コードを合成して、C/RTLコシミュレーションを行った。C/RTLコシミュレーション波形を表示した。
PmodHB5_inf_1_160622.png

pwm_out_V を見ると 1 になるはずのところが X になっている。これはバグだ。

sa_V_V と sb_V_V のセンサー・フィードバック・ピンは、ほんの少し波形が出て後は 0 の定常状態になっている。
最初の部分を拡大してみよう。
PmodHB5_inf_2_160623.png 

sa_V_V が 1 -> 0 -> 1 と変化しているので、PWM に比べて短い時間で終了している。つまり、motor_monitor() は短い時間で終了したが、pwm() が終了していないので、付き合わされているということのようだ。これは考えてみれば当たり前かもしれない。

次に pwm() のバグを考えてみよう。pwm() のCソースコードを示す。

void pwm(ap_uint<7> sw_late, ap_uint<1> dir,
        volatile ap_uint<1> & pwm_out, ap_uint<1> & dir_out){

    dir_out = dir;
    for (int i=1; i<=PWM_MAX_VALUE; i++){
        for (int n=0; n<PWM_WAIT_CLOCK; n++){
#pragma HLS PIPELINE II=1 rewind
            if (sw_late == 0){
                pwm_out = 0;
            } else if(i==1){
                pwm_out = 1;
            } else if (sw_late < i){
                pwm_out = 0;
            }
        }
    }
}


これを見ると、「 } else if(i==1){ 」の部分は明らかにおかしい。 i が 1 のときのみ pwm_out = 1; になる。つまり他の pwm_out = 1; になるはずの部分の記述がないことになる。これは”Vivado HLS で PWM モジュールIP を作ってみた”と同じコード(厳密に言うと、dir_out 出力を追加してある)なのだが、前は良く正しく動いたと思う。明らかにバグだ。つまり、関数にすることでバグが顕在化したのだろう?
pwm() の新しいソースコードを示す。

void pwm(ap_uint<7> sw_late, ap_uint<1> dir,
        volatile ap_uint<1> & pwm_out, ap_uint<1> & dir_out){

    dir_out = dir;
    for (int i=1; i<=PWM_MAX_VALUE; i++){
        for (int n=0; n<PWM_WAIT_CLOCK; n++){
#pragma HLS PIPELINE II=1 rewind
            if (sw_late == 0){
                pwm_out = 0;
            } else if(i>=1 && sw_late >= i){
                pwm_out = 1;
            } else if (sw_late < i){
                pwm_out = 0;
            }
        }
    }
}

これで、C コードの合成、C/RTLコシミュレーションを行った。C/RTLコシミュレーション波形を示す。
PmodHB5_inf_3_160623.png

これで pwm_out_V も正常になった。

これで良いように思うが、 pwm_out_V の処理時間よりもセンサー・フィードバック処理用IPのsa, sb の入力の処理時間が長くなるとどうなるだろうか?
PWM の時間も伸びてしまうと思う。つまり、PmodHB5 用のIP として 2 つの関数を一緒にVivado HLS でIP 化するのは問題があると思う。
  1. 2016年06月22日 05:54 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2016.2 で volatile を使用する場合

Vivado HLS で AXI4 Master IP を作る際に volatile を使ってあったので、使ってきたがVivado HLS でどのようなときに volatile を使用するのかを調べたことはなかった。今回、hiyuh さんのおかげで、volatile を使う意味が分かった。

Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2016.1) 2016 年 4 月 6 日”の304ページからの”揮発性デー タの理解”によると、

volatile キーワー ド を使用する と 、 C コンパイラ (および Vivado HLS) でポインター アクセスに関する仮定がされないので、 データが揮発性であ り 変化する可能性があ る と解釈 されます。

ということです。
つまり、ap_start から ap_done の間に関数の入力や出力が変化する場合は、volatile を付けるということのようです。(純粋な C の場合にはそんなことはないのですが、Vivado HLSで合成するとハードウェアになるので、あり得ます。というか、よく使います)
Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2016.1) 2016 年 4 月 6 日”の305ページの一部を引用します。
Vivado_HLS_volatile_1_160619.png

つまり、AXI4 Master のポートはap_start してから読みまくり、書きまくりになるので、volatile を付けるということのようです。
通常のポートでは、ap_start してからap_done までの間に、連続的に入力ポートから値をRead して、処理して出力ポートから出力という場合には、”Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2016.1) 2016 年 4 月 6 日”の303ページの真ん中辺りに HLS ストリームライブラリを使ってくださいと書いてあった。

それでは、volatile を使うと、特に入力ポートにINTERFACE 指示子の register オプションを付ければ、単純なFF を付けてくれればよいような気がする。しかし、実際に合成してみた結果はそうなっていないのだが。。。
さて、それでは実際にやってみよう。

Vivado HLS で volatile_test プロジェクトを作成した。
volatile_test.cpp を新規作成した。
Vivado_HLS_volatile_2_160619.png

volatile_test.cpp を示す。

#include <ap_int.h>

void volatile_test(volatile ap_uint<16> multi_in0, volatile ap_uint<16> multi_in1,
        volatile ap_uint<16> *multi_out){
#pragma HLS INTERFACE ap_none register port=multi_out
#pragma HLS INTERFACE ap_none register port=multi_in1
#pragma HLS INTERFACE ap_none register port=multi_in0
    for(int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        ap_uint<16> temp = (ap_uint<16>)multi_in0 * (ap_uint<16>)multi_in1;
        *multi_out = temp;
    }
}


C コードを合成した結果を示す。
Vivado_HLS_volatile_3_160619.png

合成された volatile_test.v の multi_in0_V を見ると、条件がかなり付いてレジスタに代入されている。
Vivado_HLS_volatile_4_160619.png

これで、本当に1クロックごとに入力ポートからレジスタに代入されているかどうか?をVivado HLS では確認できないので、Vivado 2016.2 でシミュレーションをしてみた。vivado_test プロジェクトを作成して、テストベンチを作りシミュレーションを行う。
Vivado_HLS_volatile_5_160619.png

テストベンチの volatile_test_tb.v を示す。

`default_nettype none
`timescale 100 ps / 1ps

module volatile_test_tb;
    wire   ap_clk;
    wire   ap_rst;
    wire   ap_start;
    wire   ap_done;
    wire   ap_idle;
    wire   ap_ready;
    reg   [15:0] multi_in0_V;
    reg   [15:0] multi_in1_V;
    wire  [15:0] multi_out_V;

    volatile_test volatile_test_i (
        .ap_clk(ap_clk),
        .ap_rst(ap_rst),
        .ap_start(ap_start),
        .ap_done(ap_done),
        .ap_idle(ap_idle),
        .ap_ready(ap_ready),
        .multi_in0_V(multi_in0_V),
        .multi_in1_V(multi_in1_V),
        .multi_out_V(multi_out_V)
    );

    assign ap_start = 1'b1;
    
    always @(posedge ap_clk) begin
        if (ap_rst) begin
            multi_in0_V <= 0;
            multi_in1_V <= 1;
        end else begin
            multi_in0_V <= multi_in0_V + 1; 
            multi_in1_V <= multi_in1_V + 1;
        end
    end

    // ap_clk
    clk_gen #(
        .CLK_PERIOD(100),    // 10.0nsec, 100MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) sys_clock_i (
        .clk_out(ap_clk)
    );
    
    // ap_rst
    reset_gen #(
        .RESET_STATE(1'b1),
        .RESET_TIME(1000)    // 100nsec
    ) RESET2i (
        .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


シミュレーションを行うと、入力ポートは毎クロック、ラッチされているが、出力ポートはap_start から3クロック後のみのようだった。
Vivado_HLS_volatile_6_160619.png

さて、volatile_test.cpp のPIPELINE指示子を削除するとどうだろうか?そうすると、ステートマシンは1状態ではなくなり、複数の状態を取るので、入力ポートがクロックごとにラッチしているか?どうか?がわかる。
volatile_test.cpp を示す。

#include <ap_int.h>

void volatile_test(volatile ap_uint<16> multi_in0, volatile ap_uint<16> multi_in1,
        volatile ap_uint<16> *multi_out){
#pragma HLS INTERFACE ap_none register port=multi_out
#pragma HLS INTERFACE ap_none register port=multi_in1
#pragma HLS INTERFACE ap_none register port=multi_in0
    for(int i=0; i<10; i++){
//#pragma HLS PIPELINE II=1
        ap_uint<16> temp = (ap_uint<16>)multi_in0 * (ap_uint<16>)multi_in1;
        *multi_out = temp;
    }
}


これでC コードの合成を行って、Vivado でシミュレーションを行った。
Vivado_HLS_volatile_7_160619.png

入力ポートは毎クロックごとにラッチされている。PIPELINE指示子を外しても大丈夫のようだ。出力ポートは以前と同様だ。

さて次は、出力ポートのINTERFACE指示子のregister オプションを外してみよう。

#include <ap_int.h>

void volatile_test(volatile ap_uint<16> multi_in0, volatile ap_uint<16> multi_in1,
        volatile ap_uint<16> *multi_out){
#pragma HLS INTERFACE ap_none port=multi_out
#pragma HLS INTERFACE ap_none register port=multi_in1
#pragma HLS INTERFACE ap_none register port=multi_in0
    for(int i=0; i<10; i++){
//#pragma HLS PIPELINE II=1
        ap_uint<16> temp = (ap_uint<16>)multi_in0 * (ap_uint<16>)multi_in1;
        *multi_out = temp;
    }
}


これでC コードの合成を行って、Vivado でシミュレーションを行った。
Vivado_HLS_volatile_8_160619.png

出力ポートのレジスタは無くなったが、出力ポートは依然と同様に、出力ポートはap_start から3クロック後のみだった。ダメじゃん~。。。どうすれば良いのだろうか???
PWMではうまく行っているんだけど、違いは何だろう?あ。。。入出力とも volatile だからか?

(追加)入力もポインタにしてみた。

#include <ap_int.h>

void volatile_test(volatile ap_uint<16> *multi_in0, volatile ap_uint<16> *multi_in1,
        volatile ap_uint<16> *multi_out){
#pragma HLS INTERFACE ap_none port=multi_out
#pragma HLS INTERFACE ap_none register port=multi_in1
#pragma HLS INTERFACE ap_none register port=multi_in0
    for(int i=0; i<10; i++){
//#pragma HLS PIPELINE II=1
        *multi_out = (ap_uint<16>)*multi_in0 * (ap_uint<16>)*multi_in1;
    }
}


これでC コードの合成を行って、Vivado でシミュレーションを行った。
Vivado_HLS_volatile_9_160619.png

良かった。出力ポートに2クロックごとに掛け算の結果が出力されている様に見える(3クロックに1回のようだ)。入力をポインタ(参照渡し)にする必要があるようだ。

次に、出力ポートのレジスタ・オプションを入れてみた。

#include <ap_int.h>

void volatile_test(volatile ap_uint<16> *multi_in0, volatile ap_uint<16> *multi_in1,
        volatile ap_uint<16> *multi_out){
#pragma HLS INTERFACE ap_none register port=multi_out
#pragma HLS INTERFACE ap_none register port=multi_in1
#pragma HLS INTERFACE ap_none register port=multi_in0
    for(int i=0; i<10; i++){
//#pragma HLS PIPELINE II=1
        *multi_out = (ap_uint<16>)*multi_in0 * (ap_uint<16>)*multi_in1;
    }
}


これでC コードの合成を行って、Vivado でシミュレーションを行った。
Vivado_HLS_volatile_10_160619.png

3クロックに1回、出力ポートに結果が出力されている。

最後にPIPELINE指示子を入れてみた。

#include <ap_int.h>

void volatile_test(volatile ap_uint<16> *multi_in0, volatile ap_uint<16> *multi_in1,
        volatile ap_uint<16> *multi_out){
#pragma HLS INTERFACE ap_none register port=multi_out
#pragma HLS INTERFACE ap_none register port=multi_in1
#pragma HLS INTERFACE ap_none register port=multi_in0
    for(int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        *multi_out = (ap_uint<16>)*multi_in0 * (ap_uint<16>)*multi_in1;
    }
}


レイシテンシはあるが、1クロックごとに出力ポートに結果が出力されている。
Vivado_HLS_volatile_11_160619.png

結局、出力ポートに結果を順次結果を出すためには、入力ポートもポインタ渡しにする必要があるようだ。

おまけに volatile を外すとどうなるか?

#include <ap_int.h>

void volatile_test(ap_uint<16> *multi_in0, ap_uint<16> *multi_in1,
       ap_uint<16> *multi_out){
#pragma HLS INTERFACE ap_none register port=multi_out
#pragma HLS INTERFACE ap_none register port=multi_in1
#pragma HLS INTERFACE ap_none register port=multi_in0
    for(int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        *multi_out = (ap_uint<16>)*multi_in0 * (ap_uint<16>)*multi_in1;
    }
}


これでC コードの合成を行って、Vivado でシミュレーションを行った。
Vivado_HLS_volatile_12_160619.png

volatile ありと同じ波形だった。結局、今のところは volatile 要らないかも?
  1. 2016年06月19日 06:44 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

PmodHB5 のセンサー・フィードバック処理IP を作ってみた

PmodHB5 のPWM 部分は”Vivado HLS で PWM モジュールIP を作ってみた”で完成したが、センサー・フィードバック・ピンの処理は作っていなかったので、作成する。

2016/06/25 : Cコードを修正しました。詳しくは、”PmodHB5 のセンサー・フィードバック処理IP を作ってみた2”を参照ください)

PmodHB5™ Reference Manual によると、モーターのセンサー・フィードバック・ピンの SA, SB があるので、それを処理するIP を作成することにする。Project 10: Frequency Measurement Using Input Captureに PmodHB5のPWM、SA、SBの波形が13ページに載っているが、これを見るとモーターの回転速度が速くなると、SA や SB のパルスの周波数が高くなり、回転方向が変わると SA と SB の相対位置が変化するようだ。

このPmodHB5 のセンサー・フィードバック処理IP をVivado HLS で作る際には、どのような方法でC ソースコードを書くか迷った。通常の ap_uint<1> SA, SB で書いたのでは、C ソースコードは書けるのだが、テストベンチは書くことができなかった。それは、なぜか?というと、通常のC では、関数をコールするときには、引数はステーブルになっていて、引数によって導かれる答えを返す。ところが、SA や SB は関数コールされてからもハードウェアなので、値が変動する。それに応じて処理を行う必要があるため、通常のC コードではテストベンチを書くことができない。
配列渡しにしても、インターフェースはRAM インターフェースになってしまって、うまく行かなかった。
そこで、HLSストリームライブラリを使うことにした。HLSストリームライブラリを使用すると、Valid, Ack 信号が生成されてしまう。Valid 信号は 1 固定とし、Ack 信号は無視することにしようと思う。

それではソースを貼っておく。
まずは、motor_monitor.h から。

// motor_monitor.h
// 2016/06/16 by marsee
//

#ifndef __MOTOR_MONITOR___
#define __MOTOR_MONITOR___

#include <ap_int.h>

typedef ap_uint<1> ap_uint_1;
#endif


次に、ソースの motor_monitor.cpp を貼っておく。

// motor_monitor.cpp
// 2016/06/15 by marsee
//

#include <ap_int.h>
#include <hls_stream.h>

#include "motor_monitor.h"

int motor_monitor(hls::stream<ap_uint_1 >& sa, hls::stream<ap_uint_1 >& sb,
        ap_uint<32> & sa_count, ap_uint_1 & sb_level, ap_uint_1 & overflow){
#pragma HLS INTERFACE s_axilite port=overflow
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE s_axilite port=sb_level
#pragma HLS INTERFACE s_axilite port=sa_count
#pragma HLS INTERFACE ap_hs port=sb
#pragma HLS INTERFACE ap_hs port=sa

    ap_uint_1 sad;
    ap_uint_1 sbd;

    overflow = 0;
    for (sa_count=0; sa_count!=0xffffffff; sa_count++){ // sa=1の間はループ
#pragma HLS PIPELINE II=1
        sa >> sad;
        sb >> sbd;
        if (sa_count == 0xfffffffe) // overflow
            overflow = 1;
        if (!sad)
            break;
    }

    for (sa_count=1; sa_count!=0xffffffff; sa_count++){ // sa=0の間はループ
#pragma HLS PIPELINE II=1
        sa >> sad;
        sb >> sbd;
        if (sa_count == 0xfffffffe) // overflow
            overflow = 1;
        if (sad)
            break;
    }

    sb_level = sbd; // saが立ち上がった時のsbの値

    for (sa_count++; sa_count!=0xffffffff; sa_count++){ // sa=1の間はループ
#pragma HLS PIPELINE II=1
        sa >> sad;
        sb >> sbd;
        if (sa_count == 0xfffffffe) // overflow
            overflow = 1;
        if (!sad)
            break;
    }

    return 0;
}


次に、テストベンチの motor_monitor_tb.cpp を貼っておく。

// motor_monitor_tb.cpp
// 2016/06/15 by marsee
//

#include <ap_int.h>
#include <hls_stream.h>

#include "motor_monitor.h"

#define DATASIZE 20

int motor_monitor(hls::stream<ap_uint_1 >& sa, hls::stream<ap_uint_1 >& sb,
        ap_uint<32> & sa_count, ap_uint_1 & sb_level, ap_uint_1 & overflow);

int main(){
    using namespace std;

    ap_uint_1 saa[DATASIZE] = {1,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,1,1};
    ap_uint_1 sba[DATASIZE] = {0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1};

    hls::stream<ap_uint_1> sa;
    hls::stream<ap_uint_1> sb;

    ap_uint<32> sa_count;
    ap_uint_1 sb_level;
    ap_uint_1 overflow;

    for (int i=0; i<DATASIZE; i++){
        sa << saa[i];
        sb << sba[i];
    }
    motor_monitor(sa, sb, sa_count, sb_level, overflow);

    printf("*sa_count = %d, *sb_level = %d, overflow = %d\n",
            (unsigned int)sa_count, (unsigned int)sb_level, (unsigned int)overflow);

    return 0;
}


これでC シミュレーションを行った。
reg_write_read_22_160618.png

C コードの合成を行った。
reg_write_read_23_160618.png

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

C/RTLコシミュレーション波形を確認したl。
reg_write_read_25_160618.png

1 + 2 = 3 クロック分Wait が入っているが、後で、sa_count の値に +3 すればよいだろう。
  1. 2016年06月18日 08:16 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

レジスタ設定用AXI4 Master IPを使用してVivado HLSで作ったPWM モジュールIPをシミュレーションした2

レジスタ設定用AXI4 Master IPを使用してVivado HLSで作ったPWM モジュールIPをシミュレーションした”の続き。

前回は、PWMmodule の動作を確かめるために、レジスタ設定用AXI4 Master IPを使用して、PWM モジュールIPをシミュレーションしてみた。前回、auto-restart が動作するのを確認できたが、今回は、本命のauto-restart 動作中にPWM の幅の変更ができるのか?を確かめてみた。

前回も書いたが、reg_write_read2 IP のROM の設定を変更するためにはIP をアップデートする必要がある。
reg_write_read2 IP のROM の設定を下に示す様に、0x44A00000 番地に 0x80 を書く auto-restart の設定後に、PWM の幅の設定を0xA (10)に変更した。(0x44A00010 番地に 0xA を書いた)

0    0            0x44A00010    0x32
0    0            0x44A00000    0x1
0    5            0x44A00000    0x80
0    0x20        0x44A00010    0xA
0    0xFFFFFFFF    0            0


ROM 設定を上に示すように書き換えて、IP をアップデートし、シミュレーションを行った。結果を示す。
reg_write_read_19_160615.png

PWM の幅が 50 % から 10 % に変更されているのがわかった。これで完璧だと思う。

4 つのレジスタ設定用AXI4 のWrite トランザクションを示す。
reg_write_read_20_160616.png

最後の PWM の幅の設定を0xA (10)に変更した。(0x44A00010 番地に 0xA を書いた) レジスタ設定用AXI4 のWrite トランザクションを示す。
reg_write_read_21_160616.png
  1. 2016年06月16日 04:15 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

レジスタ設定用AXI4 Master IPを使用してVivado HLSで作ったPWM モジュールIPをシミュレーションした

レジスタ設定用AXI4 Master IPをVivado HLS で作ってみた2”で作ったレジスタ設定用AXI4 Master IPを使用して、”Vivado HLS で PWM モジュールIP を作ってみた”で作ったPWM モジュールIPをシミュレーションしてみた。

まずは、Vivado 2016.2 のプロジェクトのPWMmodule_test を作成して、PWMmodule_test ブロックデザインを作成した。
reg_write_read_11_160615.png

レジスタ設定用AXI4 Master IP の reg_write_read は、Vivado HLS のIP そのものを使用すると、VHDLのIP となってしまって、ビット表現でしか書けなかった。

signal addr0_tmp : std_logic_vector(awidth-1 downto 0); 
type mem_array is array (0 to mem_size-1) of std_logic_vector (dwidth-1 downto 0); 
signal mem : mem_array := (
    0 => "00000000000000000000000000000000", 
    1 => "00000000000000000000000000010100", 
    2 => "00000000000000000000000000001010", 
    3 => "10001111111111111111111111111111", 
    4 => "01001111111111111111111111110000", 
    5 => "11111111111111111111111111111111", 
    6 to 255=> "00000000000000000000000000000000" );


これを修正するのはとってもつらいので、レジスタ設定用AXI4 Master IP のVerilog HDL コードを抜き出して、IP を作成した (reg_write_read2)。
reg_write_read_12_160615.png

PWMmodule_test ブロックデザインの拡大図を示す。
reg_write_read_13_160615.png

テストベンチの PWMmodule_test_tb.v を作成して、シミュレーションを行った。
reg_write_read_14_160615.png

reg_write_read のトランザクションは次の通り。

0    0            0x44A00010    0x32
0    0            0x44A00000    0x1
0    5            0x44A00000    0x80
0    0xFFFFFFFF    0            0


フォーマットはこれ。

// reg_addr_data フォーマット、
// 第1フィールド(reg_ad[x][0]):Write - 0, Read - 1
// 第2フィールド(reg_ad[x][1]):Delay、単位はクロック数
// 第3フィールド(reg_ad[x][2]):アドレス、16進数
// 第4フィールド(reg_ad[x][3]):データ、16進数


600 us シミュレーションを行ったところ、PWM の幅はぴったり 500 us、1 の幅も 250 us だった。
reg_write_read_15_160615.png

reg_write_read のトランザクションを1つずつ示す。最初は、0x44A00010 番地への 0x32 (50) のWrite だ。これはPWMの 1 の幅だ。
reg_write_read_16_160615.png

次に、0x44A00000 番地への0x1 のWrite で、これは ap_start を 1 にしている。
reg_write_read_17_160615.png

最後は、0x44A00000 番地への0x80 のWrite で、これは auto_restart を 1 にしている。
reg_write_read_18_160615.png

最後に、テストベンチの PWMmodule_test_tb.v を示す。

`default_nettype none
`timescale 100 ps / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2016/06/13 15:14:23
// Design Name: 
// Module Name: PWMmodule_test_tb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module PWMmodule_test_tb;

    wire [31:0]dummy_out;
    wire pwm_out_V;
    wire reset_rtl_n;
    wire sys_clock;
    
    PWMmodule_test_wrapper PWMmodule_test_wrapper_i (
        .dummy_out(dummy_out),
        .pwm_out_V(pwm_out_V),
        .reset_rtl_n(reset_rtl_n),
        .sys_clock(sys_clock)
    );

    // sys_clock
    clk_gen #(
        .CLK_PERIOD(100),    // 10.0nsec, 100MHz
        .CLK_DUTY_CYCLE(0.5),
        .CLK_OFFSET(0),
        .START_STATE(1'b0)
    ) sys_clock_i (
        .clk_out(sys_clock)
    );
    
    // reset_rtl_n
    reset_gen #(
        .RESET_STATE(1'b0),
        .RESET_TIME(1000)    // 100nsec
    ) RESET2i (
        .reset_out(reset_rtl_n)
    );
    
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


reg_write_read のトランザクションを変更するにはIP をアップデートする必要があるのが、面倒だが、それ以外は快適に使えている。
  1. 2016年06月15日 06:22 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

レジスタ設定用AXI4 Master IPをVivado HLS で作ってみた2

レジスタ設定用AXI4 Master IPをVivado HLS で作ってみた”の続き。

前回は32 ビット長の予定のディレイ値やアドレス、データが 32 ビット長いらないということで、必要なビット幅に丸められてしまった。
今回は、ビット幅を丸められないで、32 ビット長を確保できるような reg_ad[][] の初期値を探ってみよう。

reg_ad[][] の初期値をいろいろと変えてみてやってみたが、下に示す値にするとディレイ値やアドレス、データのビット幅が 32 ビットになることが分かった。
reg_write_read.h の一部を示す。

const unsigned int reg_ad[AD_ARRAY_LIMIT][4]={
        {000x1001},
        {1200x1000},
        {0100x1042},
        {00x8fffffff, 0xAffffff0, 0x8fffffff},
        {10x4ffffff0, 0xCfffffff, 0x5fffffff},
        {00xffffffff, 00},


これで C コードの合成を行ったところ、ディレイ値やアドレス、データのビット幅が 32 ビット長になった。下にディレイ値のVerilog ファイルを示す。
reg_write_read_8_160614.png

合成のレポートを示す。やはり、使用リソース量は増えた。
reg_write_read_9_160614.png

IP 化を行った。
reg_write_read_10_160614.png
  1. 2016年06月14日 04:47 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

レジスタ設定用AXI4 Master IPをVivado HLS で作ってみた

PWM モジュールIP をシミュレーションするために、レジスタ設定用AXI4 Master IPをVivado HLS で作ってみた。

PWM モジュールIP は単体ではVivado HLS のC/RTLコシミュレーションで、PWM がきちんと動作するのを確認した。しかし、AXI4-Lite Slave IP として実際に使用するためには、AXI4 Lite Slave スタートさせた後、オートリスタートにする必要がある。さらに、オートリスタートしている間にPWM の比率を変更できるかどうか?も気になる点だ。よって、HDL レベルでシミュレーションを行いたいのだが、依然作製してある reg_set_axi_lite_master IP では、ある一定時間Wait することができないし、Read もできないので、Vivado HLS で作ってみることにした。

Vivado HLS 2016.2 を使って、reg_write_read プロジェクトを作成した。
reg_write_read_1_160613.png

まずは、レジスタ設定のWrite/Read を指定する配列 reg_ad[AD_ARRAY_LIMIT][4] を reg_write_read.h に書いた。フォーマットを次に示す。

// reg_addr_data フォーマット、
// 第1フィールド(reg_ad[x][0]):Write - 0, Read - 1
// 第2フィールド(reg_ad[x][1]):Delay、単位はクロック数
// 第3フィールド(reg_ad[x][2]):アドレス、16進数
// 第4フィールド(reg_ad[x][3]):データ、16進数
//
// 終了は第2フィールド:Delayに 0xffffffff が書いてあったとき


例えば、{0, 10, 0x104, 2} は、Write、ディレイが10 クロック、0x104 番地に 2 をWrite する。
最初に reg_write_read.h の一部を示す。

// reg_write_read.h
// 2016/06/10 by marsee
//
// reg_addr_data フォーマット、
// 第1フィールド(reg_ad[x][0]):Write - 0, Read - 1
// 第2フィールド(reg_ad[x][1]):Delay、単位はクロック数
// 第3フィールド(reg_ad[x][2]):アドレス、16進数
// 第4フィールド(reg_ad[x][3]):データ、16進数
//
// 終了は第2フィールド:Delayに 0xffffffff が書いてあったとき

#define AD_ARRAY_LIMIT 256
#define REG_WRITE 0
#define REG_READ 1

#define R_W_FIELD 0
#define DELAY_FIELD 1
#define ADDRESS_FIELD 2
#define DATA_FIELD 3

const unsigned int reg_ad[AD_ARRAY_LIMIT][4]={
        {000x1001},
        {1200x1000},
        {0100x1042},
        {0, 0xffffffff, 0, 0},
        {1000},
        {0000},
        {1000},
        {0000},
        {1000},
        {0000},
        {1000},
        {0000},


次に、reg_write_read.cpp を示す。

// reg_write_read.cpp
// 2016/06/11 by marsee
//
// レジスタにWrite or Read する
//

#include "reg_write_read.h"

int reg_write_read(volatile int *axi4m, volatile int *dummy_out){
#pragma HLS INTERFACE ap_none port=dummy_out
#pragma HLS INTERFACE ap_ctrl_hs port=return
#pragma HLS INTERFACE m_axi depth=1024 port=axi4m offset=off

    int ret;

    for(int i=0; i<255; i++){
        if(reg_ad[i][DELAY_FIELD] == 0xffffffff) // end
            break;
        for(unsigned int n=0; n<reg_ad[i][DELAY_FIELD]; n++){ // Delay count
#pragma HLS PIPELINE II=1
            *dummy_out = n;
        }
        int index = reg_ad[i][ADDRESS_FIELD]/sizeof(unsigned int);
        if(reg_ad[i][R_W_FIELD] == REG_WRITE){
            axi4m[index] = reg_ad[i][DATA_FIELD];
        } else { // read
            *dummy_out = axi4m[index];
        }
    }
}


20行弱のコードで書けてしまった。

次にテストベンチのreg_write_read_tb.cpp を示す。

// reg_write_read_tb.cpp
// 2016/06/12 by marsee
//

int reg_write_read(volatile int *axi4m, volatile int *dummy_out);

int main(){
    int axi4m[512];
    int dm_out;

    reg_write_read(axi4m, &dm_out);

    return 0;
}


これで C コードの合成を行った。結果を示す。
reg_write_read_2_160613.png 

Verilog コードを見ると、フィールドごとにROM になっていた。第 3 フィールドのアドレスを見ると、3 桁の16進数になっていた。
reg_write_read_6_160613.png 

アドレスのROM を読み込んでいるVerilog コードを見ると、DWIDTH が 9 ビットになっていた。
reg_write_read_7_160613.png 

これは後で、最大桁のアドレスや、ディレイの値を入れる必要があるようだ。最適なビット数に丸められてしまう。C/RTL コシミュレーションの関係でこのアドレスになったが、後で修正する。

次に、C/RTLコシミュレーションを行った。
reg_write_read_3_160613.png

シミュレーション波形はできたので、見てみよう。
reg_write_read_4_160613.png

reg_write_read_5_160613.png

AXI4 Master の Write と Read がうまくできているようだ。Wait も働いているようだ。

  1. 2016年06月13日 05:52 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

「64-ロクヨン-後編」を見てきました

今日は娘の高校の文化祭に行った後で、「64-ロクヨン-後編」を見てきました。
前編はとっても良かったのですが、後編はちょっと肩透かしというか?前編の方が良かったと思いました。
それに原作にもない、あの展開は、誘拐そのものなんじゃないでしょうか?
  1. 2016年06月11日 20:08 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

Altera SDK for OpenCL勉強会に行ってきた

昨日、東工大で開催された「Altera SDK for OpenCL勉強会」に行ってきた。

東工大の前に秋葉原に行って、「ヴイストン ロボットセンター東京秋葉原店」に行って、いろいろロボットの部品を見てきました。

東工大に行って、南3号館に行った。セキュリティがついていて入れるのかな?と思ったのだが、Tさんが来て、入れますよ。。。と教えてくれた。一応入れるのを確認した後で、時間が早いので東工大内をお散歩していたが、なかなか、お散歩に良かった。事実、お散歩している一般の方と思える人もたくさんいた。

OpenCL はヘテロジニアスなコンピューティング環境をサポートするフレームワークなので、理想的にはGPU もFPGA アクセラレータも同じコードで実行できるのだろうが、Altera の方言はあるようだ。Altera SDK for OpenCLはXilinx でいうとSDAccel だと思う。SDSoC とはOpenCL 対応という違いはあるが、同じようなものだと思っている。SDSoC のプラットフォームがAltera SDK for OpenCL では、BSPと呼ばれているようだ。

Altera SDK for OpenCLは、レポートが良いですね。グラフィカルに構造を表示してくれるようだし、どこがボトルネックになっているか?リソースがどこが足りないかを表示してくれるようだ。これは私が Vivado HLS で表示してほしいと思っている事項である。これはとっても良いと思う。

Altera SDK for OpenCL は、FPGAの動作周波数を指定することができないそうだ。動作周波数はBSP でFPGAファミリごとに目標とする動作周波数が決まっているそうだ。それはほとんど変えられない、変えることを考えていないということだった。その性能に向かって高位合成するようだ。Vivado HLS だと、動作周波数(周期)を自分で自由に決定できて、それによって、パイプライン段数、回路を変更する。つまり、高速動作が必要な指定だと、よりFFやLUT を使って高速動作を確保し、低速動作の指定だとそれらのリソースを減らして、エリアを確保するが、それが指定できなく、ある一定の基準でしか高位合成できないということになる。また、Quartus Primeでコンパイルしてみるまで、動作周波数がわからず、予測値も示されないようだ。この辺りは改善を望みたいと思っているが、そういうツールじゃないんだろうか?

DE0 nano SoC でもAltera SDK for OpenCL が動くようなので、FPGAマガジンの次号で、60日間のAltera SDK for OpenCL の無料ライセンスがもらえるようだったら、試してみたいと思う。

Venginner さん、東工大の中原先生、参加した皆さん、Altera の方々、お世話になりました。ありがとうございました。
  1. 2016年06月11日 06:49 |
  2. その他のFPGAの話題
  3. | トラックバック:0
  4. | コメント:0

Vivado 2016.2 が出ました

Vivado 2016.2 が出たので、インストールしました。
Vivado_2016_2_160610.png

リリースノートによると、デバイスサポートが増えたくらいのようです。

Vivado HLS 2016.2 もリリースノートの新機能に記述がなかったので、変更点は無いようです。
Vivado_HLS_2016_2_160610.png

たぶん、多少、バグフィックスはされていると思うので、2016.2 を使うことにしようと思います。
  1. 2016年06月10日 04:15 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で PWM モジュールIP を作ってみた

Zybot のモーターコントロール用Hブリッジ回路のPmodの Digilent PmodHB5 を制御するPWM モジュールIP をVivado HLS 2016.1 で作ってみた。

2016/06/24 : 更新: PWMモジュールのC コードにバグがあったので、”Vivado HLS で PWM モジュールIP を作ってみた2”で修正しました)

PmodHB5 のマニュアルによると、2KHz の周期(500 us)でPWMをすれば良さそうだ。Vivado HLS で作るとすると、100MHz のクロックを使用して、0 ~ 100 の出力を変化させることにしよう。まずは 100回ループしてスイッチング・レート の値まで PWM出力に 1 を出力するのだが、それだけだと速すぎてしまうので、Waiting Loop として 500 カウントする。それすれば、合計 50000 カウントするので、500 us になる予定だ。

PWMmodule というVivado HLS 2016.1 のプロジェクトを作成した。
PWMmodule_1_160609.png

次に、ソースファイルのPWMmodule.cpp を示す。なるべくPWM の精度を高めたかったので、PIPELINEディレクティブの rewind オプションを追加した。なお、このPWM モジュールはLinux から使おうと思っているので、AXI4-Lite のSlave デバイスにした。
sw_late 入力には、スイッチグ・レートを入力する。0 ~ 100 までだ。PWM出力のパーセンテージを設定する。これは、AXI4-Lite のレジスタにマップする。
pwm_out はPWM出力で、これはポートである必要があるので、インターフェースを ap_none とした。

// PWMmodule.cpp
// 2016/06/08 by marsee
// PmodHB5のPWM用モジュール
// 100MHzのクロックで2KHz(500us)のPWM出力(最初に500分周する)

#include <ap_int.h>

void pwm(ap_uint<7> sw_late, volatile ap_uint<1> *pwm_out){
#pragma HLS INTERFACE ap_none register port=pwm_out
#pragma HLS INTERFACE s_axilite port=sw_late
#pragma HLS INTERFACE s_axilite port=return

    for (int i=1; i<=100; i++){
        for (int n=0; n<500; n++){
#pragma HLS PIPELINE II=1 rewind
            if (sw_late == 0){
                *pwm_out = 0;
            } else if(i==1){
                *pwm_out = 1;
            } else if (sw_late < i){
                *pwm_out = 0;
            }
        }
    }
}


テストベンチのPWMmodule_tb.cpp を示す。

// PWMmodule_tb.cpp
// 2016/06/08 by marsee
// PWMmodule用テストベンチ
//

#include <ap_int.h>

void pwm(ap_uint<7> sw_late, volatile ap_uint<1> *pwm_out);

int main(){
    ap_uint<1> pwmo, *pwm_out;

    pwm_out = &pwmo;

    pwm(0, pwm_out);
    pwm(50, pwm_out);
    pwm(100, pwm_out);

    return 0;
}


これで、C シミュレーションを行っても意味がないので、C コードの合成を行った。結果を示す。
PWMmodule_2_160609.png

Estimated は 5.24 ns なので、100MHz クロックで使用するには、全く問題ない。
Interval はPIPELINEディレクティブのrewind オプションのために 50000 クロックぴったりになっている。

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

C/RTL コシミュレーションの波形を観察した。
PWMmodule_4_160609.png

1つのPWM 出力が 500.32 us 掛かっているので、500 / 500.32 ≒ 0.999 よって、0.1 % 程度の誤差だったので、問題ないだろう?と思う。それにオートリスタートの場合は 500 us ぴったりになることが期待できる。

最後にIP 化を行った。
PWMmodule_5_160609.png

PWM モジュールもViado HLS でAXI4-Lite バスのSlave として、数時間で書くことができた。50000 クロックにするのにいろいろと試行錯誤をしたので、少し時間がかかったが、HDL で書いていたら、AXI4-Lite のSlave を書いたりするので、2,3日はかかると思う。
Vivado HLS を使用して C で実装したので、とっても速くできたと思う。
  1. 2016年06月09日 04:46 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

Zybot の作製2(組み立て1)

Zybot 組み立てマニュアルを参照して、Zybot を途中まで組み立ててみた。なお、これは、Zybot の仮組でとりあえずモーターを動かして、車を動かすのを目的とする。後で、独自のベースをレーザー加工機で作成する予定だ。

まずは、Base Plate Expansion Kit を2枚使用して、ねじでつないだ。
Rounded Plate Explansion Kit をねじでつないで、Double Motor Mount を取り付けた。
Zybot_1_160607.jpg

だが、Zybot 組み立てマニュアルのBuliding the Base を見るとDuble Motor Mount は Base Plate の横幅いっぱいなのだが、実物はベース・プレートよりもダブル・モーター・マウントの方が短い。これではタイヤを付けたときに干渉してしまう。
Zybot_2_160607.jpg
Zybot_3_160607.jpg

ダブル・モーター・マウントは使えないようなので、Single Motor Mount (Pair) を使用した。こっちならば、融通が利く。けれど、モーターの平行が取れているわけではないので、注意する必要があるだろう?
写真はDC Motor/Gearbox (1:53 Gear Ratio): Custom 6V Motor Designed for Digilent Robot Kits を 2 つ取り付けたところだ。
Zybot_4_160607.jpg

次に、Drag Button を取り付けたところだ。
Zybot_5_160607.jpg

Zybot_6_160607.jpg

Zybot_7_160607.jpg

PmodHB5: H-bridge Driver with Feedback Inputs Pmod Clip: Mechanical Mount for Pmod boards で取り付けたところで、今回は終了。
Zybot_8_160607.jpg
  1. 2016年06月07日 05:08 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

Zybot の作製1(構想編)

Zybot を作製しようとしている。Zybot はZYBO を搭載した車といっても本物の車のサイズではなくミニカーの部類だ。

オリジナルのZybot はZYBO が1台でカメラも1台なのだが、作ろうと思っているのは、ステレオカメラを搭載したZybot になる予定だ。My Zybot はレーザー加工機でベースを作る予定なのだが、その前に1度オリジナルを作ってみようと思っている。

すでにDigilent 社から部品は届ているので、組み立てるだけなのだが、モータードライバにPmodHB5: H-bridge Driver with Feedback Inputs を使っているので、使い方を覚える必要がある。

PmodHB5 はHブリッジ回路で、モーターを制御しているようだ。”モータのOn/Off制御法”の【Hブリッジ制御回路】によると、

単一の電源でモータに加える電圧の向きを変えられる回路として考案されたのが、「Hブリッジ回路」

だそうだ。

PmodHB5™ Reference Manual”を見ると、DIR で回す方向を決めて、ENをPWM してモーター・パワーをコントロールすれば良さそうだ。センサー・フィードバック・ピンはモーターからのセンサー情報がFPGA側へ出力されるようだ。PmodHB5 の回路図を見ると、センサー・フィードバック・ピンには+3.3Vからプルアップされていて、そこからシュミットトリガ・インバータでFPGA 側に接続されている。これで、回転数を観測できるのだろう?
モーターは”DC Motor/Gearbox (1:53 Gear Ratio): Custom 6V Motor Designed for Digilent Robot Kits”の予定で、そのMagnetic Encoder の仕様書がこれだ。ロータリー・エンコーダーがついてるようなものだと思う。
  1. 2016年06月06日 05:20 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

Z-turn Board のDTS の覚書

Z-turn Board のDTS が見つからなかったのだが、検索していると”Xilinx Zynq port not available #903”を見つけた。

それによるとZ-turn Board のDTS は、”https://github.com/andreamerello/linux-zynq-stable”の”arch/arm/boot/dts/zynq-zturn-myir.dts”だそうだ。

zynq-zturn-myir.dts を見て、だいぶエントリが少ないと思ったが、zynq-7000.dtsi がインクルードされていた。
そこからリンクされていた”skeleton.dtsi”。

  1. 2016年06月05日 05:09 |
  2. Z-turn Board
  3. | トラックバック:0
  4. | コメント:0

Z-turn Board のPS設定をサンプル・プロジェクトからコピーする

Z-turn Board のVivado プロジェクトを作ろうと思ったが、PS(Processing System つまり、ARM SoC の設定です) の設定はどうするんだろうと思っていた。ZYBO では、ボード・ファイルをVivado のボード・ファイルのフォルダにコピーしたが、Z-turn Board では、そのようなファイルは見当たらない。そこで、サンプル・プロジェクトのPS設定をエクスポートできて、新しく作るVivado プロジェクトのPS の設定として読み込めないかな?と探ってみた。

Z-turn Board についてくるDVD の 05-ProgrammableLogic_Source\7Z020 に mys-xc7z020-trd.rar があって、これを解凍すると mys-xc7z020-trd フォルダができる。
Vivado_PS_settings_1_160603.png

mys-xc7z020-trd.xpr をダブルクリックして、Vivado を起動した。Vivado 2016.1 が起動するので、IP アップグレードを行って、ブロックデザインを表示したところだ。ZYNQ7 Processing System はたくさん接続されているので、いい具合だ。
processing_system7_0 をダブルクリックして、設定画面を出すことにする。
Vivado_PS_settings_2_160603.png

PS の設定画面が開いたら、Presets -> Save Configuration... を選択した。
Vivado_PS_settings_3_160603.png

Save Current Configuration... ダイアログが開く。
Preset Name と File name に mys-xc7x020_trd と入力して、OKボタンをクリックした。
Vivado_PS_settings_4_160603.png

そうすると、mys-xc7z020-trd フォルダに、mys-xc7x020_trd.tcl ファイルができた。
Vivado_PS_settings_5_160603.png

mys-xc7x020_trd.tcl ファイルを開いてみるとこんな感じだった。
Vivado_PS_settings_6_160603.png

さて、このTCL ファイルを読み込んでみたいということで、test_proj プロジェクトを作成した。
ブロックデザインを作成して、ZYNQ7 Processing System をAdd IP した。
Vivado_PS_settings_7_160603.png

processing_system7_0 をダブルクリックして、設定画面を出した。
Presets -> Apply Configuration... を選択した。
Vivado_PS_settings_8_160603.png

mys-xc7x020_trd.tcl ファイルを指定した。
Vivado_PS_settings_9_160603.png

mys-xc7x020_trd.tcl ファイルの設定がPS設定としてロードされている。
Vivado_PS_settings_10_160603.png

PS 設定画面を終了すると、processing_system7_0 が設定されている。成功だ。
Vivado_PS_settings_11_160603.png

自分でデフォルトのPS 設定として、良かれと思う様に設定した。
Vivado_PS_settings_12_160603.png

そして、PS 設定画面から Presets -> Save Configuration... でファイルとしてセーブした。
ファイル名は、def_z_turn_board_20_ps_settings.tcl で、Preset Name は、default_z-turn_board_20_PS_settings とした。
Vivado_PS_settings_13_160603.png

def_z_turn_board_20_ps_settings.tcl を開いてみた。
Vivado_PS_settings_14_160603.png

これで、def_z_turn_board_20_ps_settings.tcl を使用して、Z-turn Board でPS を使ったVivado プロジェクトを作ることができる様になった。
  1. 2016年06月04日 04:45 |
  2. Vivado
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS を使用した車の白線検出11(ラプラシアンフィルタ2)

Vivado HLS を使用した車の白線検出10(ラプラシアンフィルタ1)”の続き。

前回、性能の出なかったハードウェア版Canny フィルタの代わりにラプラシアンフィルタを使用したところ白線検出の成績が良かった。今回は、自分で撮ってきたいろいろな道路の写真でラプラシアンフィルタを使用した白線検出を行うことができるか?検証してみた。

今回の写真はどれも白線検出が難しいと思う。それに道路に白線がない写真もあるので、どうなるだろうか?

最初は左側の白線は見えるのだが、右側の白線は木陰がある写真を白線検出してみる。
現画像を示す。
Lane-Detecting_98_160603.jpg

白線検出結果を示す。
Lane-Detecting_91_160602.jpg
木陰が映っている右側の白線が検出できていない。

次の画像を示す。たまに白線が点線の部分がある。退避場所があるところだ。
Lane-Detecting_99_160603.jpg

白線検出結果を示す。
Lane-Detecting_92_160602.jpg
点線の部分が白線検出できない。ラプラシアンフィルタを使用したハードウェアバージョンの方が、右の白線を検出できているので、優秀のようだ。

次の画像は、割と白線が出ている画像だ。
Lane-Detecting_100_160603.jpg

白線検出結果を示す。
Lane-Detecting_93_160602.jpg
これも、ラプラシアンフィルタを使用したハードウェアバージョンの方が、左の白線を検出できているので、優秀のようだ。

次の画像は、白線のない道路だ。
Lane-Detecting_101_160603.jpg

白線検出結果を示す。
Lane-Detecting_94_160603.jpg
白線の検出はできていない。白線がないので当たり前だと言える。

次も白線がない道路。
Lane-Detecting_102_160603.jpg

白線検出結果を示す。
Lane-Detecting_95_160603.jpg
やはり、白線が検出できていない。

これも白線がない道路だが、曲がっている。
Lane-Detecting_103_160603.jpg

白線検出結果を示す。
Lane-Detecting_96_160603.jpg
やはり、白線のない道路で白線検出は無理のようだ。

最後の写真も最初同様に左側の白線は見えるのだが、右側の白線は木陰がある写真。
Lane-Detecting_104_160603.jpg

白線検出結果を示す。
Lane-Detecting_97_160603.jpg
やはり、木陰が映っている右側の白線が検出できていない。

木陰が白線に掛かっていると白線検出はできていない。
白線のない道路では白線検出はできない。
Canny フィルタ+HoughLineを用いたOpenCVよりもラプラシアンフィルタ+OpenCVのHoughLineの方が白線検出できているようだ。
  1. 2016年06月03日 20:54 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Z-turn Board の電源を入れてみた

Z-turn Board の付属してきたSDカードにBOOT.bin, devicetree.dtb, uImage が入っていたので、Z-turn Board に入れて電源を入れてみた。
Z-turn Board は電源スイッチがなく、DCプラグを入れると電源ONしてしまう。しかも、USB_UART からの電源供給もDCプラグの電源に直接接続されてしまう。ただし、USB_UART からの電源には回路図を見ると逆流防止のショットキー・バリアー・ダイオードが入っている。よって、USB_UART のケーブルをつないでしまうと電源が入ってしまうので、DCプラグを入れてからUSB_UART を接続する必要がある。

Z-turn Board の写真を示す。LANも接続したので、DHCPでIPアドレスが割り振られている。
Z_turn_Board_7_160601.jpg

HDMI を表示してみると、Ubuntu のディスクトップが表示されていた。
Z_turn_Board_8_160601.jpg

Tera Term を立ち上げてシリアルで、115200bps, 8 bits, 1stop bit, none に設定するとプロンプトが出た。
ls と / に行って ls を行った。
Z_turn_Board_5_160601.png

dfcat /etc/lsb-release をやってみた。Ubuntu 12.04 だった。
Z_turn_Board_6_160601.png
  1. 2016年06月02日 05:16 |
  2. Z-turn Board
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS を使用した車の白線検出10(ラプラシアンフィルタ1)

ZYBO 上のOpenCV で白線検出3(Canny フィルタのみの場合)”でZYBO のLinux 上でソフトウェアによる白線検出のめどはついた。
しかし、これは、ソフトウェアのみによる白線検出で、ハードウェアにオフロードにしていないのは寂しい。”Vivado HLS を使用した車の白線検出9(Canny フィルタ3)”で road_1.jpg については白線検出を行うことができたが、road_2.jpg についてはどうスレッショルドを変更しても白線検出を行うことができなかった。これは、ハードウェアによる白線検出の精度が低いという感じがした。
そこで、もう一度、Canny フィルタのアルゴリズムを”Canny Edge Detection -アルゴリズム-”で勉強した。”Canny Edge Detection -アルゴリズム-”によると、

Canny Edge Detectionのアルゴリズムは次の手順。
STEP1:ガウシアンフィルタで平滑化
STEP2:エッジ強度と勾配方向(4方向に量子化)を計算
STEP3:細線化処理(Non-maximum Suppression)
STEP4:ヒステリシス閾処理(Hysteresis Threshold)


エッジ強度と細線化処理を行っているが、これをラプラシアンフィルタに置き換えたらどうか?と考えてみた。ラプラシアンフィルタ処理後のエッジはすでに細線化されているような感じだし、行けるかもしれない?ということで実際にやってみることにした。
ソースファイルの lap_filter_cpp の一部を示す。なお、これは、”ka367/Lane-Detecting-Using-Hough-Transform”のtop.cpp を元に大幅に修正を加えている。なお、 hls::Filter2D()のコードは”hls::Filter2D and cv::Filter2D gives different results”を参考にさせて頂いた。

void lap_filter(AXI_STREAM& input, AXI_STREAM& output, int rows, int cols, int threshold_low, int threshold_high) {
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS DATAFLOW
#pragma HLS INTERFACE s_axilite port=threshold_high
#pragma HLS INTERFACE s_axilite port=threshold_low
#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows
#pragma HLS INTERFACE axis port=output
#pragma HLS INTERFACE axis port=input

#pragma HLS INTERFACE ap_stable port=rows
#pragma HLS INTERFACE ap_stable port=cols

    RGB_IMAGE src(rows, cols);
    RGBSINGLE_IMAGE src_bw(rows, cols);
    RGBSINGLE_IMAGE src_blur(rows, cols);
    RGBSINGLE_IMAGE lap_fil(rows, cols);
    RGB_IMAGE lap_fil_color(rows, cols);
    RGB_IMAGE lap_edges(rows, cols);
  
//#pragma HLS dataflow
    // AXI to RGB_IMAGE stream
    hls::AXIvideo2Mat( input, src );

  // Grayscaling
    hls::CvtColor<HLS_RGB2GRAY>( src, src_bw );

    // Gaussian Blur Noise Reduction
    hls::GaussianBlur<5,5>( src_bw, src_blur );
    //hls::GaussianBlur<3,3>( src_bw, src_blur);

    // Calculate laplacian filter
    const int lap_weight[3][3] = {{-1, -1, -1},{-18, -1},{-1, -1, -1}};
    hls::Window<33int> kernel;
    hls::Point_<int> anchor;

    for (int i=0; i<3; i++){
        for (int j=0; j<3; j++){
            kernel.val[i][j] = lap_weight[i][j];
        }
    }
    anchor.x = -1;
    anchor.y = -1;
    hls::Filter2D(src_blur, lap_fil, kernel, anchor);

    // gray to color
    hls::CvtColor<HLS_GRAY2BGR>( lap_fil, lap_fil_color );

    // Perform hysteresis thresholding for edge tracing
    hysteresis( lap_fil_color, lap_edges, threshold_low, threshold_high );

    hls::Mat2AXIvideo(lap_edges, output );
}


次にテストベンチのLane-Detecting_tb.cpp を示す。

// Lane-Detecting_tb.cpp
// 2016/05/22 by marsee
// OpenCV 2 の Mat を使用したバージョン
// Vivado HLSでは Canny フィルタのみハードウェア化した。OpenCV で処理したCannyフィルタとHough変換を行って比較する。

#include <iostream>
#include "hls_opencv.h"
#include "lap_filter.h"

using namespace cv;

void lap_filter(AXI_STREAM& input, AXI_STREAM& output, int rows, int cols, int threshold_low, int threshold_high);
void opencv_lap_filter(Mat& src, Mat& dst, int threshold_low, int threshold_high, int sobel_size);

int main (int argc, char** argv) {
    //const int threshold_low = 50;
    //const int threshold_high = 200;
    const int threshold_low = 40;
    const int threshold_high = 50;
    const int sobel_size = 3;

    // OpenCV で 画像を読み込む
    Mat src = imread(INPUT_IMAGE);
    AXI_STREAM src_axi, dst_axi;

    // Mat フォーマットから AXI4 Stream へ変換
    cvMat2AXIvideo(src, src_axi);

    // image_filter() 関数をコール
    lap_filter(src_axi, dst_axi, src.rows, src.cols, threshold_low, threshold_high);

    // AXI4 Stream から Mat フォーマットへ変換
    // dst は宣言時にサイズとカラー・フォーマットを定義する必要がある
    Mat dst(src.rows, src.cols, CV_8UC3);
    AXIvideo2cvMat(dst_axi, dst);
    imshow("Vivado HLS lap_filter", dst);

    // opencv_image_filter() をコール
    Mat dst_cv(src.rows, src.cols, CV_8UC3);
    opencv_lap_filter(src, dst_cv, 200300, sobel_size);
    imshow("OpenCV Canny filter", dst_cv);

    // Vivado HLS の Canny filter の Hough変換
    // detect lines
    vector<Vec2f> lines;
    Mat gray_dst;
    cvtColor(dst, gray_dst, CV_BGR2GRAY);
    HoughLines(gray_dst, lines, 1, CV_PI/18012000);

    // draw lines
    for( size_t i = 0; i < lines.size(); i++ )
    {
        float rho = lines[i][0], theta = lines[i][1];
        Point pt1, pt2;
        double a = cos(theta), b = sin(theta);
        double x0 = a*rho, y0 = b*rho;
        pt1.x = cvRound(x0 + 1000*(-b));
        pt1.y = cvRound(y0 + 1000*(a));
        pt2.x = cvRound(x0 - 1000*(-b));
        pt2.y = cvRound(y0 - 1000*(a));
        line( dst, pt1, pt2, Scalar(0,0,255), 3, CV_AA);
    }
    imwrite(OUTPUT_IMAGE, dst);

    // OpenCV の Canny filter の Hough変換
    // detect lines
    Mat cdst_cv;
    cvtColor(dst_cv, cdst_cv, CV_GRAY2BGR);

    HoughLines(dst_cv, lines, 1, CV_PI/18015000);

    // draw lines
    for( size_t i = 0; i < lines.size(); i++ )
    {
        float rho = lines[i][0], theta = lines[i][1];
        Point pt1, pt2;
        double a = cos(theta), b = sin(theta);
        double x0 = a*rho, y0 = b*rho;
        pt1.x = cvRound(x0 + 1000*(-b));
        pt1.y = cvRound(y0 + 1000*(a));
        pt2.x = cvRound(x0 - 1000*(-b));
        pt2.y = cvRound(y0 - 1000*(a));
        line( cdst_cv, pt1, pt2, Scalar(0,0,255), 3, CV_AA);
    }
    imwrite(OUTPUT_IMAGE_GOLDEN, cdst_cv);

    printf("Test with 0 errors.\n");

    imshow("Detect lines of Vivado HLS lap_filter", dst);
    imshow("Detect lines of OpenCV", cdst_cv);

    waitKey();

    return 0;
}

void opencv_lap_filter(Mat& src, Mat& dst, int threshold_low, int threshold_high, int sobel_size){
    Canny(src, dst, (double)threshold_low, (double)threshold_high, sobel_size);
}


Vivado HLS 2016.1 のプロジェクトの Lane-Detecting_cf_2 を示す。
Lane-Detecting_90_160601.png

これで、test_1080p.bmp と road_1.jpg, road_2.jpg, road_3.jpg, road_4.jpg の白線検出を行った。

最初は、test_1080.bmp から白線検出結果を示す。
Lane-Detecting_85_160531.jpg

次に、road_1.jpg の白線検出結果を示す。
Lane-Detecting_86_160531.jpg

road_2.jpg の白線検出結果を示す。
Lane-Detecting_87_160531.jpg

road_3.jpg の白線検出結果を示す。
Lane-Detecting_88_160531.jpg

road_4.jpg の白線検出結果を示す。
Lane-Detecting_89_160601.jpg

思いがけなく良好な結果と言えるのではないだろうか?HLSビデオライブラリを使用したハードウェアの方は、余計な線を検出しているが、それは家の屋根の端のエッジを検出しているようだ。これは、画像の下半分をHoughLine変換することで、取り除くことができると思う。

  1. 2016年06月01日 04:23 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0