FC2カウンター FPGAの部屋 Watchdog timer を Vitis HLS 2020.1 で実装する3(デバック修正)
fc2ブログ

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

FPGAの部屋

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

Watchdog timer を Vitis HLS 2020.1 で実装する3(デバック修正)

Watchdog timer を Vitis HLS 2020.1 で実装する2(Vivado 2020.1)”の続き。

GPS と 3軸加速度センサーを使ったシステム5(Vivado 2020.1 のプロジェクトを作成中)”(現在は、3軸加速度センサーが 9 個付くようになっている)に Watchdog Timer を実装することにした。そこで、Vitis HLS 2020.1 で Watchdog Timer を作成することにしたということで、前回は、Vivado 2020.1 を使用して、 WDTimer.v のシミュレーションを行って、Vitis HLS で Export RTL を行って、IP 化した。今回は、実機で確認したところ、3軸加速度センサーの表示が出てこなかった。たぶん、リセットが解除されてから、初期化設定があるはずだが、そこに時間がかかって、3軸加速度センサーの操作用アプリケーション・ソフトウェアまで 5 ms 以内に到達していないのではないか?と思う。そこで、WDTimer.cpp に enable 引数を追加して、 enable が 1 になってから WDTimer がスタートするように変更する。

WDTimer.cpp に enable 引数を追加した。

/// WDTimer.cpp
// 2020/12/23 by marsee
//

#include <ap_int.h>

int WDTimer(ap_uint<1> *rst_in, volatile ap_uint<1> *rst_out, ap_uint<1> *enable){
#pragma HLS INTERFACE ap_none register port=rst_out
#pragma HLS INTERFACE ap_ctrl_none port=return

    ap_uint<20> i;

    *rst_out = 0;
    for(i=0; i<1048575; i++){
        if(*enable == ap_uint<1>(1)){
            i = i;
        }else{
            --i;
            continue;
        }

        if(*rst_in == ap_uint<1>(1))
            i = 0;
        else
            i = i;

        if(i>524287){
            *rst_out = 1;
            --i;
        }else{
            *rst_out = 0;
        }
    }

    return(0);
}


WDTimer_11_201226.png

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

WDTimer.v を示す。

// ==============================================================
// RTL generated by Vitis HLS - High-Level Synthesis from C, C++ and OpenCL
// Version: 2020.1
// Copyright (C) 1986-2020 Xilinx, Inc. All Rights Reserved.
// 
// ===========================================================

`timescale 1 ns / 1 ps 

(* CORE_GENERATION_INFO="WDTimer_WDTimer,hls_ip_2020_1,{HLS_INPUT_TYPE=cxx,HLS_INPUT_FLOAT=0,HLS_INPUT_FIXED=0,HLS_INPUT_PART=xc7z020-clg400-1,HLS_INPUT_CLOCK=10.000000,HLS_INPUT_ARCH=others,HLS_SYN_CLOCK=6.914250,HLS_SYN_LAT=-1,HLS_SYN_TPT=none,HLS_SYN_MEM=0,HLS_SYN_DSP=0,HLS_SYN_FF=27,HLS_SYN_LUT=210,HLS_VERSION=2020_1}" *)

module WDTimer (
        ap_clk,
        ap_rst,
        rst_in,
        rst_out,
        enable,
        ap_return
);

parameter    ap_ST_fsm_state1 = 4'd1;
parameter    ap_ST_fsm_state2 = 4'd2;
parameter    ap_ST_fsm_pp0_stage0 = 4'd4;
parameter    ap_ST_fsm_state5 = 4'd8;

input   ap_clk;
input   ap_rst;
input  [0:0] rst_in;
output  [0:0] rst_out;
input  [0:0] enable;
output  [31:0] ap_return;

reg   [0:0] rst_out_1_data_reg;
reg   [0:0] rst_out_1_data_in;
reg    rst_out_1_vld_reg;
reg    rst_out_1_vld_in;
reg    rst_out_1_ack_in;
reg   [19:0] empty_reg_77;
wire   [0:0] enable_read_read_fu_64_p2;
(* fsm_encoding = "none" *) reg   [3:0] ap_CS_fsm;
wire    ap_CS_fsm_state2;
wire   [0:0] icmp_ln882_fu_99_p2;
wire    ap_CS_fsm_pp0_stage0;
wire    ap_block_state3_pp0_stage0_iter0;
wire    ap_block_state4_pp0_stage0_iter1;
wire    ap_block_pp0_stage0_11001;
wire   [0:0] tmp_fu_120_p3;
wire   [19:0] add_ln695_fu_135_p2;
reg    ap_enable_reg_pp0_iter0;
wire    ap_block_pp0_stage0_subdone;
reg    ap_condition_pp0_exit_iter0_state3;
reg    ap_enable_reg_pp0_iter1;
reg   [19:0] ap_phi_mux_empty_11_phi_fu_91_p6;
wire   [19:0] add_ln696_fu_105_p2;
wire   [19:0] ap_phi_reg_pp0_iter0_empty_11_reg_88;
wire   [19:0] select_ln23_fu_112_p3;
wire   [19:0] add_ln696_1_fu_128_p2;
wire    ap_CS_fsm_state1;
wire    ap_block_pp0_stage0_01001;
wire    ap_block_pp0_stage0;
wire    ap_CS_fsm_state5;
reg   [3:0] ap_NS_fsm;
reg    ap_idle_pp0;
wire    ap_enable_pp0;

// power-on initialization
initial begin
#0 rst_out_1_data_reg = 1'd0;
#0 rst_out_1_vld_reg = 1'b0;
#0 ap_CS_fsm = 4'd1;
#0 ap_enable_reg_pp0_iter0 = 1'b0;
#0 ap_enable_reg_pp0_iter1 = 1'b0;
end

always @ (posedge ap_clk) begin
    if (ap_rst == 1'b1) begin
        ap_CS_fsm <= ap_ST_fsm_state1;
    end else begin
        ap_CS_fsm <= ap_NS_fsm;
    end
end

always @ (posedge ap_clk) begin
    if (ap_rst == 1'b1) begin
        ap_enable_reg_pp0_iter0 <= 1'b0;
    end else begin
        if (((1'b0 == ap_block_pp0_stage0_subdone) & (1'b1 == ap_condition_pp0_exit_iter0_state3) & (1'b1 == ap_CS_fsm_pp0_stage0))) begin
            ap_enable_reg_pp0_iter0 <= 1'b0;
        end else if ((1'b1 == ap_CS_fsm_state2)) begin
            ap_enable_reg_pp0_iter0 <= 1'b1;
        end
    end
end

always @ (posedge ap_clk) begin
    if (ap_rst == 1'b1) begin
        ap_enable_reg_pp0_iter1 <= 1'b0;
    end else begin
        if (((1'b0 == ap_block_pp0_stage0_subdone) & (1'b1 == ap_condition_pp0_exit_iter0_state3))) begin
            ap_enable_reg_pp0_iter1 <= (1'b1 ^ ap_condition_pp0_exit_iter0_state3);
        end else if ((1'b0 == ap_block_pp0_stage0_subdone)) begin
            ap_enable_reg_pp0_iter1 <= ap_enable_reg_pp0_iter0;
        end else if ((1'b1 == ap_CS_fsm_state2)) begin
            ap_enable_reg_pp0_iter1 <= 1'b0;
        end
    end
end

always @ (posedge ap_clk) begin
    if ((1'b1 == ap_CS_fsm_state2)) begin
        empty_reg_77 <= 20'd0;
    end else if (((ap_enable_reg_pp0_iter0 == 1'b1) & (1'b0 == ap_block_pp0_stage0_11001) & (icmp_ln882_fu_99_p2 == 1'd0) & (1'b1 == ap_CS_fsm_pp0_stage0))) begin
        empty_reg_77 <= add_ln695_fu_135_p2;
    end
end

always @ (posedge ap_clk) begin
    if ((((1'b1 == 1'b1) & (rst_out_1_vld_in == 1'b1) & (rst_out_1_vld_reg == 1'b1)) | ((rst_out_1_vld_in == 1'b1) & (rst_out_1_vld_reg == 1'b0)))) begin
        rst_out_1_data_reg <= rst_out_1_data_in;
    end
end

always @ (*) begin
    if ((icmp_ln882_fu_99_p2 == 1'd1)) begin
        ap_condition_pp0_exit_iter0_state3 = 1'b1;
    end else begin
        ap_condition_pp0_exit_iter0_state3 = 1'b0;
    end
end

always @ (*) begin
    if (((ap_enable_reg_pp0_iter1 == 1'b0) & (ap_enable_reg_pp0_iter0 == 1'b0))) begin
        ap_idle_pp0 = 1'b1;
    end else begin
        ap_idle_pp0 = 1'b0;
    end
end

always @ (*) begin
    if ((icmp_ln882_fu_99_p2 == 1'd0)) begin
        if (((tmp_fu_120_p3 == 1'd1) & (enable_read_read_fu_64_p2 == 1'd1))) begin
            ap_phi_mux_empty_11_phi_fu_91_p6 = add_ln696_1_fu_128_p2;
        end else if (((tmp_fu_120_p3 == 1'd0) & (enable_read_read_fu_64_p2 == 1'd1))) begin
            ap_phi_mux_empty_11_phi_fu_91_p6 = select_ln23_fu_112_p3;
        end else if ((enable_read_read_fu_64_p2 == 1'd0)) begin
            ap_phi_mux_empty_11_phi_fu_91_p6 = add_ln696_fu_105_p2;
        end else begin
            ap_phi_mux_empty_11_phi_fu_91_p6 = ap_phi_reg_pp0_iter0_empty_11_reg_88;
        end
    end else begin
        ap_phi_mux_empty_11_phi_fu_91_p6 = ap_phi_reg_pp0_iter0_empty_11_reg_88;
    end
end

always @ (*) begin
    if (((rst_out_1_vld_reg == 1'b0) | ((1'b1 == 1'b1) & (rst_out_1_vld_reg == 1'b1)))) begin
        rst_out_1_ack_in = 1'b1;
    end else begin
        rst_out_1_ack_in = 1'b0;
    end
end

always @ (*) begin
    if (((ap_enable_reg_pp0_iter0 == 1'b1) & (tmp_fu_120_p3 == 1'd1) & (1'b0 == ap_block_pp0_stage0_01001) & (icmp_ln882_fu_99_p2 == 1'd0) & (enable_read_read_fu_64_p2 == 1'd1) & (1'b1 == ap_CS_fsm_pp0_stage0))) begin
        rst_out_1_data_in = 1'd1;
    end else if (((1'b1 == ap_CS_fsm_state1) | ((ap_enable_reg_pp0_iter0 == 1'b1) & (tmp_fu_120_p3 == 1'd0) & (1'b0 == ap_block_pp0_stage0_01001) & (icmp_ln882_fu_99_p2 == 1'd0) & (enable_read_read_fu_64_p2 == 1'd1) & (1'b1 == ap_CS_fsm_pp0_stage0)))) begin
        rst_out_1_data_in = 1'd0;
    end else begin
        rst_out_1_data_in = 'bx;
    end
end

always @ (*) begin
    if (((1'b1 == ap_CS_fsm_state1) | ((ap_enable_reg_pp0_iter0 == 1'b1) & (tmp_fu_120_p3 == 1'd1) & (1'b0 == ap_block_pp0_stage0_11001) & (icmp_ln882_fu_99_p2 == 1'd0) & (enable_read_read_fu_64_p2 == 1'd1) & (1'b1 == ap_CS_fsm_pp0_stage0)) | ((ap_enable_reg_pp0_iter0 == 1'b1) & (tmp_fu_120_p3 == 1'd0) & (1'b0 == ap_block_pp0_stage0_11001) & (icmp_ln882_fu_99_p2 == 1'd0) & (enable_read_read_fu_64_p2 == 1'd1) & (1'b1 == ap_CS_fsm_pp0_stage0)))) begin
        rst_out_1_vld_in = 1'b1;
    end else begin
        rst_out_1_vld_in = 1'b0;
    end
end

always @ (*) begin
    case (ap_CS_fsm)
        ap_ST_fsm_state1 : begin
            ap_NS_fsm = ap_ST_fsm_state2;
        end
        ap_ST_fsm_state2 : begin
            ap_NS_fsm = ap_ST_fsm_pp0_stage0;
        end
        ap_ST_fsm_pp0_stage0 : begin
            if (~((ap_enable_reg_pp0_iter0 == 1'b1) & (1'b0 == ap_block_pp0_stage0_subdone) & (icmp_ln882_fu_99_p2 == 1'd1))) begin
                ap_NS_fsm = ap_ST_fsm_pp0_stage0;
            end else if (((ap_enable_reg_pp0_iter0 == 1'b1) & (1'b0 == ap_block_pp0_stage0_subdone) & (icmp_ln882_fu_99_p2 == 1'd1))) begin
                ap_NS_fsm = ap_ST_fsm_state5;
            end else begin
                ap_NS_fsm = ap_ST_fsm_pp0_stage0;
            end
        end
        ap_ST_fsm_state5 : begin
            if (((1'b1 == ap_CS_fsm_state5) & (rst_out_1_ack_in == 1'b1))) begin
                ap_NS_fsm = ap_ST_fsm_state1;
            end else begin
                ap_NS_fsm = ap_ST_fsm_state5;
            end
        end
        default : begin
            ap_NS_fsm = 'bx;
        end
    endcase
end

assign add_ln695_fu_135_p2 = (ap_phi_mux_empty_11_phi_fu_91_p6 + 20'd1);

assign add_ln696_1_fu_128_p2 = ($signed(select_ln23_fu_112_p3) + $signed(20'd1048575));

assign add_ln696_fu_105_p2 = ($signed(empty_reg_77) + $signed(20'd1048575));

assign ap_CS_fsm_pp0_stage0 = ap_CS_fsm[32'd2];

assign ap_CS_fsm_state1 = ap_CS_fsm[32'd0];

assign ap_CS_fsm_state2 = ap_CS_fsm[32'd1];

assign ap_CS_fsm_state5 = ap_CS_fsm[32'd3];

assign ap_block_pp0_stage0 = ~(1'b1 == 1'b1);

assign ap_block_pp0_stage0_01001 = ~(1'b1 == 1'b1);

assign ap_block_pp0_stage0_11001 = ~(1'b1 == 1'b1);

assign ap_block_pp0_stage0_subdone = ~(1'b1 == 1'b1);

assign ap_block_state3_pp0_stage0_iter0 = ~(1'b1 == 1'b1);

assign ap_block_state4_pp0_stage0_iter1 = ~(1'b1 == 1'b1);

assign ap_enable_pp0 = (ap_idle_pp0 ^ 1'b1);

assign ap_phi_reg_pp0_iter0_empty_11_reg_88 = 'bx;

assign ap_return = 32'd0;

assign enable_read_read_fu_64_p2 = enable;

assign icmp_ln882_fu_99_p2 = ((empty_reg_77 == 20'd1048575) ? 1'b1 : 1'b0);

assign rst_out = rst_out_1_data_reg;

assign select_ln23_fu_112_p3 = ((rst_in[0:0] === 1'b1) ? 20'd0 : empty_reg_77);

assign tmp_fu_120_p3 = select_ln23_fu_112_p3[32'd19];

endmodule //WDTimer


Vivado 2020.1 でのシミュレーション結果を示す。
WDTimer_13_201226.png

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

enable の変化部分。 enable の前はカウントが止まっている。
WDTimer_15_201226.png

rst_in が 1 になる部分。カウンタがリセットされているのが分かる。
WDTimer_16_201226.png

rst_out が 1 になる部分。 rst_in から rst_out までの時間を測っている。約 5.24 ms だった。これは、 WDTimer のタイムアウトの時間だ。
WDTimer_17_201226.png

これで OK なので、Vitis HLS 2020.1 で Export RTL を行って、IP 化した。
WDTimer_18_201226.png

最後に Vivado 2020.1 のシミュレーション時のテストベンチファイルの WDTimer_tb.v を貼っておく。

`default_nettype none
`timescale 100ps / 1ps

// WDTimer_tb.v
// 2020/12/24 by marsee
//

module WDTimer_tb;
    parameter DELAY    = 10;

    wire ap_clk;
    wire ap_rst;
    reg  rst_in;
    wire rst_out;
    wire ap_rst_0;
    wire ap_rst_1;
    reg  enable;
    
    assign ap_rst = ap_rst_0 | ap_rst_1;

    WDTimer WDTimer_i(
        .ap_clk(ap_clk),
        .ap_rst(ap_rst),
        .rst_in(rst_in),
        .rst_out(rst_out),
        .enable(enable),
        .ap_return()
    );

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

    // reset_gen のインスタンス
    reset_gen #(
        .RESET_STATE(1'b1),
        .RESET_TIME(1000)    // 100nsec
    ) RESETi (
        .reset_out(ap_rst_0),
        .init_done()
    );
    
    assign ap_rst_1 = rst_out;

    initial begin
        rst_in = 1'b0;
        enable = 1'b0;
        #1000 // wait reset
        
        #10000000 // 1 ms wait
        enable = 1'b1;

        #30000000 // 3 ms wait
        @(posedge ap_clk);    // 次のクロックへ
        #DELAY;
        rst_in = 1'b1;
        @(posedge ap_clk);    // 次のクロックへ
        #DELAY;
        rst_in = 1'b0;

        #70000000 // 7 ms wait
        
        $stop;
    end

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,
    output    reg        init_done
);
    begin
        initial begin
            reset_out = RESET_STATE;
            init_done = 1'b0;
            #RESET_TIME;
            reset_out = ~RESET_STATE;
            init_done = 1'b1;
        end
    end
endmodule

`default_nettype wire


(追記)
WDTimerが動作したことをどうやって確認しようか?と考えていた。ハードを追加しようか?

でも結局、リセットされるので、初期化の時にあるビットを1にして、その後は0にリセットするようにアプリケーション・ソフトウェアを書くだけで良いと思った。当然、AXI GPIOでそのビットは用意するのだが。。
  1. 2020年12月26日 05:04 |
  2. FPGAを使用したシステム
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/5100-49b5014b
この記事にトラックバックする(FC2ブログユーザー)