Vengineerの妄想(準備期間)

人生は短いけど、長いです。人生を楽しみましょう!

Xilinx Vitis の中を調べる(その11)

はじめに

Xilinx Vitis の中を調べるのその11。

テストプログラム(シーケンス)

UVMではテストプログラムをシーケンスと呼んでいます。この例では、multi_apuint_test_lib クラスの中で、uvm_config_db にて、multi_apuint_subsys_test_sequence_lib をシーケンスとして設定しています。

            uvm_config_db#(uvm_object_wrapper)::set(
                    this,
                    "top_env.multi_apuint_virtual_sqr.run_phase",
                    "default_sequence",
                    multi_apuint_subsys_test_sequence_lib::type_id::get()
                );

multi_apuint_subsys_test_sequence_lib は、svtb/multi_apuint_subsys_test_sequence_lib.sv です。下記のようなコードになります。task_body タスクがシーケンスの本体でこれが実行されます。

//==============================================================
//Vitis HLS - High-Level Synthesis from C, C++ and OpenCL v2022.1 (64-bit)
//Tool Version Limit: 2022.04
//Copyright 1986-2022 Xilinx, Inc. All Rights Reserved.
//==============================================================
`ifndef MULTI_APUINT_SUBSYS_TEST_SEQUENCE_LIB__SV
    `define MULTI_APUINT_SUBSYS_TEST_SEQUENCE_LIB__SV

    `define AUTOTB_TVIN_multi_in0_multi_in0  "../tv/cdatafile/c.multi_apuint.autotvin_multi_in0.dat"
    `define AUTOTB_TVIN_multi_in1_multi_in1  "../tv/cdatafile/c.multi_apuint.autotvin_multi_in1.dat"

    `include "uvm_macros.svh"

    class multi_apuint_subsys_test_sequence_lib extends uvm_sequence;

        function new (string name = "multi_apuint_subsys_test_sequence_lib");
            super.new(name);
            `uvm_info(this.get_full_name(), "new is called", UVM_LOW)
        endfunction

        `uvm_object_utils(multi_apuint_subsys_test_sequence_lib)
        `uvm_declare_p_sequencer(multi_apuint_virtual_sequencer)

        virtual task body();
            uvm_phase starting_phase;
            virtual interface misc_interface misc_if;
            multi_apuint_reference_model refm;

            string file_queue_multi_in0 [$];
            integer bitwidth_queue_multi_in0 [$];

            svr_pkg::svr_master_sequence#(8) svr_port_multi_in0_seq;
            svr_pkg::svr_random_sequence#(8) svr_port_random_port_multi_in0_seq;

            string file_queue_multi_in1 [$];
            integer bitwidth_queue_multi_in1 [$];

            svr_pkg::svr_master_sequence#(8) svr_port_multi_in1_seq;
            svr_pkg::svr_random_sequence#(8) svr_port_random_port_multi_in1_seq;

            svr_pkg::svr_slave_sequence #(16) svr_port_multi_out_seq;


            if (!uvm_config_db#(multi_apuint_reference_model)::get(p_sequencer,"", "refm", refm))
                `uvm_fatal(this.get_full_name(), "No reference model")
            `uvm_info(this.get_full_name(), "get reference model by uvm_config_db", UVM_LOW)

            `uvm_info(this.get_full_name(), "body is called", UVM_LOW)
            starting_phase = this.get_starting_phase();
            if (starting_phase != null) begin
                `uvm_info(this.get_full_name(), "starting_phase not null", UVM_LOW)
                starting_phase.raise_objection(this);
            end
            else
                `uvm_info(this.get_full_name(), "starting_phase null" , UVM_LOW)

            misc_if = refm.misc_if;


            //phase_done.set_drain_time(this, 0ns);
            wait(refm.misc_if.reset === 0);
            repeat(100)         @(posedge refm.misc_if.clock);
            ->refm.misc_if.initialed_evt;
            refm.misc_if.initialed=1;
            @(posedge refm.misc_if.clock);
            refm.misc_if.initialed=0;
            @(posedge refm.misc_if.clock);

ここまでが準備です。これ以降が各インターフェースに対して何かをしています。2つの fork が入り子になっていますが、2番目の fork の最初の begin で各インターフェースの設定を行っています。

            fork
                begin
                    fork
                        begin
                            string keystr_delay;
                            file_queue_multi_in0.push_back(`AUTOTB_TVIN_multi_in0_multi_in0);
                            bitwidth_queue_multi_in0.push_back(8);

                            `uvm_create_on(svr_port_multi_in0_seq, p_sequencer.svr_port_multi_in0_sqr);
                            svr_port_multi_in0_seq.misc_if = refm.misc_if;
                            svr_port_multi_in0_seq.ap_done  = refm.ap_done_for_nexttrans ;
                            svr_port_multi_in0_seq.ap_ready = refm.ap_ready_for_nexttrans;
                            svr_port_multi_in0_seq.finish   = refm.finish;
                            svr_port_multi_in0_seq.file_rd.config_file(file_queue_multi_in0, bitwidth_queue_multi_in0);
                            if( refm.multi_apuint_cfg.port_multi_in0_cfg.prt_type == AP_VLD ) wait(refm.misc_if.tb2dut_a
p_start === 1'b1);
                            svr_port_multi_in0_seq.isusr_delay = svr_pkg::NO_DELAY;
                            `uvm_send(svr_port_multi_in0_seq);
                        end
                        begin
                            string keystr_delay;
                            file_queue_multi_in1.push_back(`AUTOTB_TVIN_multi_in1_multi_in1);
                            bitwidth_queue_multi_in1.push_back(8);

                            `uvm_create_on(svr_port_multi_in1_seq, p_sequencer.svr_port_multi_in1_sqr);
                            svr_port_multi_in1_seq.misc_if = refm.misc_if;
                            svr_port_multi_in1_seq.ap_done  = refm.ap_done_for_nexttrans ;
                            svr_port_multi_in1_seq.ap_ready = refm.ap_ready_for_nexttrans;
                            svr_port_multi_in1_seq.finish   = refm.finish;
                            svr_port_multi_in1_seq.file_rd.config_file(file_queue_multi_in1, bitwidth_queue_multi_in1);
                            if( refm.multi_apuint_cfg.port_multi_in1_cfg.prt_type == AP_VLD ) wait(refm.misc_if.tb2dut_a
p_start === 1'b1);
                            svr_port_multi_in1_seq.isusr_delay = svr_pkg::NO_DELAY;
                            `uvm_send(svr_port_multi_in1_seq);
                        end
                        begin
                            string keystr_delay;
                            `uvm_create_on(svr_port_multi_out_seq, p_sequencer.svr_port_multi_out_sqr);
                            svr_port_multi_out_seq.misc_if = refm.misc_if;
                            svr_port_multi_out_seq.ap_done  = refm.ap_done_for_nexttrans ;
                            svr_port_multi_out_seq.ap_ready = refm.ap_ready_for_nexttrans;
                            svr_port_multi_out_seq.finish   = refm.finish;
                            svr_port_multi_out_seq.isusr_delay = svr_pkg::NO_DELAY;
                            `uvm_send(svr_port_multi_out_seq);
                        end
                        begin
                            wait(svr_port_multi_in0_seq&&svr_port_multi_in1_seq);
                            forever begin
                                wait(svr_port_multi_in0_seq.one_sect_read&&svr_port_multi_in1_seq.one_sect_read);
                                svr_port_multi_in0_seq.one_sect_read = 0;
                                svr_port_multi_in1_seq.one_sect_read = 0;
                                -> refm.allsvr_input_done;
                            end
                        end
                        begin
                            int delay;
                            for(int j=0; j<10; j++) begin
                                #0; refm.misc_if.tb2dut_ap_start = 1;
                                @(refm.dut2tb_ap_ready);
                                #0; refm.misc_if.tb2dut_ap_start = 0;
                                void'(std::randomize(delay) with {
                                delay dist {0:=8, 1:=1, 2:=1, [0:2]:/2};
                                delay inside {[0:2]};
                                });
                                repeat(delay) @(posedge refm.misc_if.clock);
                            end
                        end
                        begin
                            int delay;
                            for(int j=0; j<10; j=j+refm.ap_done_cnt) begin
                                @refm.dut2tb_ap_done;
                                #0; refm.misc_if.tb2dut_ap_continue = 0;
                            end
                        end
                    join
                end

また、下記は、2番目の fork の 2番目の begin 部分です。この部分で、リファレンスモデルの部分のチェックを行っています。

                begin
                    for(int j=0; j<10; j=j+refm.ap_done_cnt) @refm.ap_done_for_nexttrans;
                    `uvm_info(this.get_full_name(), "autotb finished", UVM_LOW)
                    -> refm.finish;
                    refm.misc_if.finished = 1;
                    @(posedge refm.misc_if.clock);
                    refm.misc_if.finished = 0;
                    @(posedge refm.misc_if.clock);
                    -> refm.misc_if.finished_evt;
                end

1番目の fork の block の何れかが終了すると、下位の join_any 以降が実行されます。ちょっと待ってから、入力・出力ポートへのシーケンスを終了し、fork を disable しています。

            join_any
            repeat(5) @(posedge refm.misc_if.clock); //5 cycles delay for finish stuff. 5 is haphazard value

            p_sequencer.svr_port_multi_in0_sqr.stop_sequences();
            p_sequencer.svr_port_multi_in1_sqr.stop_sequences();
            p_sequencer.svr_port_multi_out_sqr.stop_sequences();
            disable fork;

            starting_phase.drop_objection(this);

        endtask
    endclass

`endif

おわりに

次回は、テストベンチで使っている UVM 関連のコードをみています。