はじめに
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 関連のコードをみています。