5日目までは、とりあえず小さな構成のモデル基本構造と、それらを構成するコードを説明してきました。
今回は、UVMのテストベクタ・テストシナリオ構築に関するちょっと細かい説明と、サンプルの追加を通してそれを理解することをしたいと思っていましたが、その話をする下準備をここでは行います。スレー ブモデルを追加して、2つのモデル間でハンドシェイクして動くようにします。
現状、なんちゃってマスター動作するモデルしか作っていませんので、マスター動作に応答するスレーブモデルを作りましょう。以下の作業をします。
説明抜けなどありそうなのと、わかりやすいように、各コードを貼り付けておきます。太字が「変更箇所」です。
sample_seq_item.sv
スレーブ追加分をカバーするため、修正します。
class sample_seq_item extends uvm_sequence_item; logic [7:0] addr, wdata, rdata; bit write; bit [3:0] wait_cycle; `uvm_object_utils(sample_seq_item) function new (string name="sample_seq_item_inst"); super.new(name); endfunctionendclasssample_master_agent.sv
class sample_master_agent extends uvm_agent; `uvm_component_utils(sample_master_agent) sample_master_driver driver; sample_master_monitor monitor; sample_master_sequencer sequencer; function new (string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); driver = sample_master_driver::type_id::create("driver", this); monitor = sample_master_monitor::type_id::create("monitor", this); sequencer = sample_master_sequencer::type_id::create("sequencer", this); endfunction function void connect_phase(uvm_phase phase); if(get_is_active() == UVM_ACTIVE)begin driver.seq_item_port.connect(sequencer.seq_item_export); end endfunction task run_phase(uvm_phase phase); uvm_report_info("AGENT", "Hi"); endtaskendclasssample_master_driver.sv
class sample_master_driver extends uvm_driver #(sample_seq_item); virtual sample_if vif; `uvm_component_utils(sample_master_driver) function new (string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); bit status; super.build_phase(phase); status = uvm_config_db#(virtual sample_if)::get(this, "", "vif", vif); if(status==1'b0) uvm_report_fatal("NOVIF", {"virtual interface must be set for: ",get_full_name(),".vif"}); endfunction task run_phase(uvm_phase phase); uvm_report_info("DRIVER", "Hi"); vif.valid <= 1'b0; @(posedge vif.rstz); // wait reset negate forever begin seq_item_port.get_next_item(req); // wait seq_item from sequence (via sequencer) @(posedge vif.clk); // sync clk vif.valid <= 1'b1; vif.write <= req.write; vif.addr <= req.addr; if(req.write==1'b1) vif.wdata <= req.wdata; @(posedge vif.ready); if(req.write==1'b0) req.rdata = vif.rdata; //reqにリードデータを入れておいて @(posedge vif.clk); vif.valid <= 1'b0; seq_item_port.item_done(req); // item_doneでreqを返すと、sequenceでリードデータを取得できる end endtaskendclasssample_master_sequencer.sv
class sample_master_sequencer extends uvm_sequencer #(sample_seq_item); `uvm_component_utils(sample_master_sequencer) function new (string name, uvm_component parent); super.new(name, parent); endfunction task run_phase(uvm_phase phase); uvm_report_info("SEQR", "Hi"); super.run_phase(phase); endtaskendclasssample_master_monitor.sv
class sample_master_monitor extends uvm_monitor; virtual sample_if vif; `uvm_component_utils(sample_master_monitor) function new (string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); bit status; super.build_phase(phase); status = uvm_config_db#(virtual sample_if)::get(this, "", "vif", vif); if(status==1'b0) uvm_report_fatal("NOVIF", {"virtual interface must be set for: ",get_full_name(),".vif"}); endfunction task run_phase(uvm_phase phase); string s_trans_kind; uvm_report_info("MONITOR", "Hi"); forever begin @(posedge vif.valid); wait(vif.ready===1'b1); if(vif.write===1'b1) uvm_report_info("MON", $sformatf("write addr=%02xh wdata=%02xh", vif.addr, vif.wdata)); else if(vif.write===1'b0) uvm_report_info("MON", $sformatf("read addr=%02xh rdata=%02xh", vif.addr, vif.rdata)); else uvm_report_info("MON", $sformatf("signal write is unknown...")); end endtaskendclassところで、上記monitorですが、vif.validの立ち上がりを待った後、wait文でvif.readyが1になるのを待っています。さて、何故だと思いますか?
sample_master_seq_lib.sv
class名をmasterに揃えて、それに付随する箇所を変更します。また、write_seqのクラスは、暫定的にリードを追加し、書いて読む動作にしています。
virtual class sample_master_base_seq extends uvm_sequence #(sample_seq_item); function new(string name="sample_master_base_seq"); super.new(name); do_not_randomize = 1; endfunction virtual task pre_body(); if (starting_phase!=null) begin `uvm_info(get_type_name(), $sformatf("%s pre_body() raising %s objection", get_sequence_path(), starting_phase.get_name()), UVM_MEDIUM); starting_phase.raise_objection(this); end endtask // Drop the objection in the post_body so the objection is removed when // the root sequence is complete. virtual task post_body(); if (starting_phase!=null) begin `uvm_info(get_type_name(), $sformatf("%s post_body() dropping %s objection", get_sequence_path(), starting_phase.get_name()), UVM_MEDIUM); starting_phase.drop_objection(this); end endtaskendclass//------------------------------------------------------------------------class write_seq extends sample_master_base_seq; `uvm_object_utils(write_seq) function new (string name="write_seq"); super.new(name); endfunction virtual task body(); `uvm_create(req) req.write <= 1'b1; req.addr <= 8'h10; req.wdata <= 8'h55; `uvm_send(req) `uvm_create(req) req.write <= 1'b0; req.addr <= 8'h10; `uvm_send(req) uvm_report_info("SEQ", $sformatf("read data is %02xh", req.rdata)); #1000; endtaskendclassslaveモデルの方を記述していきます。
sample_slave_agent.sv
master側のときと同じように修正していきます。ただし今回は、monitor をコメントアウトします。理由は、今回のサンプルにおいてmaster のmonitorとslave のmonitor が同じところをモニ ターするため。
sample_slave_sequencer.sv
class名と、uvm_component_utilsの引数名を直します。
sample_slave_driver.sv
このファイルは、スレーブ側動作をさせるので、動きを定義していきます。せっかくマスターとスレーブを用意したので、まずはバスインターフェースの信号「ready」を追加します。ま た、8bit定義のdataを、wdataとrdataの2つに分けます。
→ 修正ファイル sample_if.sv
動きですが、以下のような仕様にしてみたいと思います。
「validが1になったら、一定サイクル後にreadyを返す。ready=1のときのアドレス、ライト・リード信号、データを有効とする」
この動きを実現するためのslave_driverのコードは以下のようにしました。ポイントは、sequenceでslave_driverの挙動を変えられるようにすること。
class sample_slave_driver extends uvm_driver #(sample_seq_item); virtual sample_if vif; logic [7:0] memory[bit [7:0]]; //aa(連想配列):入れる値は4値(logic)、indexは4値を扱えないので2値で `uvm_component_utils(sample_slave_driver) function new (string name, uvm_component parent); super.new(name, parent); endfunction function void build_phase(uvm_phase phase); bit status; super.build_phase(phase); status = uvm_config_db#(virtual sample_if)::get(this, "", "vif", vif); if(status==1'b0) uvm_report_fatal("NOVIF", {"virtual interface must be set for: ",get_full_name(),".vif"}); endfunction task run_phase(uvm_phase phase); logic [7:0] rdata; uvm_report_info("DRIVER", "Hi"); vif.valid <= 1'b0; @(posedge vif.rstz); // wait reset negate forever begin seq_item_port.get_next_item(req); // wait seq_item from sequence (via sequencer) @(posedge vif.valid); repeat(req.wait_cycle) @(posedge vif.clk); //sequenceでwaitを制御できるようにする vif.ready <= 1'b1; if(vif.write === 1'b1)begin mem_write(vif.addr, vif.wdata); end else if(vif.write === 1'b0)begin vif.rdata <= mem_read(vif.addr); end @(posedge vif.clk) vif.ready <= 1'b0; seq_item_port.item_done(req); end endtask function void mem_write(bit [7:0] addr, logic [7:0] data); //uvm_report_info("SLAVE", $sformatf("write access accept. addr=%02xh, data=%02xh", addr,data)); memory[addr] = data; endfunction function logic [7:0] mem_read(bit [7:0] addr); //uvm_report_info("SLAVE", $sformatf("read access accept. addr=%02xh", addr)); if(memory.exists(addr))begin //uvm_report_info("SLAVE", $sformatf("read data is =%02xh", memory[addr])); return memory[addr]; end else return 8'hXX; endfunctionendclasssample_slave_seq_lib.sv
とりあえず、簡単な動作シーケンスを定義しておきます。
virtual class sample_slave_base_seq extends uvm_sequence #(sample_seq_item); function new(string name="sample_slave_base_seq"); super.new(name); do_not_randomize = 1; endfunctionendclass//------------------------------------------------------------------------class normal_response_seq extends sample_slave_base_seq; `uvm_object_utils(normal_response_seq) function new (string name="normal_response_seq"); super.new(name); endfunction virtual task body(); forever begin `uvm_create(req) req.wait_cycle <= 1'b1; `uvm_send(req) end endtaskendclassmasterのagent名が変わったことを反映し、slave_agentを追加します。
class sample_env extends uvm_env; `uvm_component_utils(sample_env) sample_master_agent master; sample_slave_agent slave; function new (string name, uvm_component parent); super.new(name,parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); master = sample_master_agent::type_id::create("master", this); slave = sample_slave_agent::type_id::create("slave", this); endfunction task run_phase(uvm_phase phase); uvm_report_info("ENV", "Hello ENV"); endtaskendclassslave系のファイルを追加します。
`include "sample_seq_item.sv"`include "sample_master_seq_lib.sv"`include "sample_slave_seq_lib.sv"`include "sample_master_driver.sv"`include "sample_master_monitor.sv"`include "sample_master_sequencer.sv"`include "sample_master_agent.sv"`include "sample_slave_driver.sv"`include "sample_slave_sequencer.sv"`include "sample_slave_agent.sv"`include "sample_env.sv"slaveモデル側に、使用するsequenceをセットしましょう。そして、agentのインスタンス名を変えたので、黄色で塗られたところに注意しましょう。
class sample_test extends uvm_test; `uvm_component_utils(sample_test) sample_env env; function new (string name="sample_test", uvm_component parent=null); super.new(name,parent); endfunction function void build_phase(uvm_phase phase); super.build_phase(phase); uvm_config_db#(uvm_object_wrapper)::set(this, "env.master.sequencer.run_phase", "default_sequence", write_seq::type_id::get()); uvm_config_db#(uvm_object_wrapper)::set(this, "env.slave.sequencer.run_phase", "default_sequence", normal_response_seq::type_id::get()); env = sample_env::type_id::create("env", this); endfunction task run_phase(uvm_phase phase); uvm_report_info("TEST", "Hello World"); uvm_top.print_topology(); endtaskendclassvlog -sv model/sample_if.sv
vlog -sv tb_top.sv +incdir+./+model/+../uvm-1.1d/src
vsim -c tb_top +UVM_TESTNAME=sample_test -do "run -all; quit"
# run -all# ----------------------------------------------------------------# UVM-1.1d# (C) 2007-2013 Mentor Graphics Corporation# (C) 2007-2013 Cadence Design Systems, Inc.# (C) 2006-2013 Synopsys, Inc.# (C) 2011-2013 Cypress Semiconductor Corp.# ----------------------------------------------------------------# UVM_INFO ../uvm-1.1d/src/base/uvm_root.svh(370) @ 0: reporter [NO_DPI_TSTNAME] UVM_NO_DPI defined--getting UVM_TESTNAME directly, without DPI# UVM_INFO @ 0: reporter [RNTST] Running test sample_test...# UVM_INFO @ 0: uvm_test_top [TEST] Hello World# UVM_INFO @ 0: reporter [UVMTOP] UVM testbench topology:# ---------------------------------------------------------------# Name Type Size Value# ---------------------------------------------------------------# uvm_test_top sample_test - @448# env sample_env - @461# master sample_master_agent - @469# driver sample_master_driver - @486# rsp_port uvm_analysis_port - @503# seq_item_port uvm_seq_item_pull_port - @494# monitor sample_master_monitor - @512# sequencer sample_master_sequencer - @520# rsp_export uvm_analysis_export - @528# seq_item_export uvm_seq_item_pull_imp - @634# arbitration_queue array 0 -# lock_queue array 0 -# num_last_reqs integral 32 'd1# num_last_rsps integral 32 'd1# slave sample_slave_agent - @477# driver sample_slave_driver - @646# rsp_port uvm_analysis_port - @663# seq_item_port uvm_seq_item_pull_port - @654# sequencer sample_slave_sequencer - @672# rsp_export uvm_analysis_export - @680# seq_item_export uvm_seq_item_pull_imp - @786# arbitration_queue array 0 -# lock_queue array 0 -# num_last_reqs integral 32 'd1# num_last_rsps integral 32 'd1# ---------------------------------------------------------------## UVM_INFO @ 0: uvm_test_top.env [ENV] Hello ENV# UVM_INFO @ 0: uvm_test_top.env.slave [AGENT] Hi# UVM_INFO @ 0: uvm_test_top.env.slave.sequencer [SEQR] Hi# UVM_INFO @ 0: uvm_test_top.env.slave.driver [DRIVER] Hi# UVM_INFO @ 0: uvm_test_top.env.master [AGENT] Hi# UVM_INFO @ 0: uvm_test_top.env.master.sequencer [SEQR] Hi# UVM_INFO @ 0: uvm_test_top.env.master.monitor [MONITOR] Hi# UVM_INFO @ 0: uvm_test_top.env.master.driver [DRIVER] Hi# UVM_INFO model//sample_master_seq_lib.sv(13) @ 0: uvm_test_top.env.master.sequencer@@write_seq [write_seq] write_seq pre_body() raising run objection# UVM_INFO @ 300: uvm_test_top.env.master.monitor [MON] write addr=10h wdata=55h# UVM_INFO @ 600: uvm_test_top.env.master.monitor [MON] read addr=10h rdata=55h# UVM_INFO @ 700: uvm_test_top.env.master.sequencer@@write_seq [SEQ] read data is 55h# UVM_INFO model//sample_master_seq_lib.sv(25) @ 1700: uvm_test_top.env.master.sequencer@@write_seq [write_seq] write_seq post_body() dropping run objection