06日目~モデルの追加

5日目までは、とりあえず小さな構成のモデル基本構造と、それらを構成するコードを説明してきました。

今回は、UVMのテストベクタ・テストシナリオ構築に関するちょっと細かい説明と、サンプルの追加を通してそれを理解することをしたいと思っていましたが、その話をする下準備をここでは行います。スレー ブモデルを追加して、2つのモデル間でハンドシェイクして動くようにします。

スレーブモデルを追加する

ファイルのコピーと変更

現状、なんちゃってマスター動作するモデルしか作っていませんので、マスター動作に応答するスレーブモデルを作りましょう。以下の作業をします。

  • コピー
    • sample_agent.sv
    • → sample_master_agent.sv, sample_slave_agent.sv
    • sample_driver.sv
      • → sample_master_driver.sv, sample_slave_driver.sv
    • sample_monitor.sv
      • → sample_master_monitor.sv, sample_slave_monitor.sv
    • sample_sequencer.sv
      • → sample_master_sequencer.sv, sample_slave_sequencer.sv
    • sample_seq_lib.sv
      • → sample_master_seq_lib.sv, sample_slave_seq_lib.sv
  • 削除
    • sample_agent.sv, sample_driver.sv, sample_monitor.sv, sample_sequencer.sv, sample_seq_lib.sv
  • 修正1
    • まず、master系4つ、slave系4つ、計8つのファイルを修正します。
      • class sample_agent... → class sample_master_agent... という修正を各ファイルに
      • `uvm_component_utilsの引数名も変える
      • agentファイルでは、3つのインスタンス元クラス名を変える
        • sample_driver driver → sample_master_driver driver; のように
        • build_phaseの中も修正します。
    • 次に、sample_model.svhを修正します。
      • agent, driver, monitor, sequencer の各ファイル名を、masterのに変えましょう。
      • slaveのは、まだ入れないでおきましょう。
    • sample_env.sv を修正します。
      • sample_agent をインスタンスしているので、sample_master_agent に変えます。インスタンス名は master に変えます。
      • build_phase の中も修正するのを忘れずに。
    • sample_test.sv を修正します。
      • build_phaseのところで、default_sequenceを指定しているところがありますので、ここを直します。
  • 修正2
    • スレーブモデルを追加するにあたり、valid - ready のハンドシェイクを行うようにします。driver と monitor は、このハンドシェイクに対応した修正を行います。

説明抜けなどありそうなのと、わかりやすいように、各コードを貼り付けておきます。太字が「変更箇所」です。

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);
  endfunction
endclass

sample_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");
  endtask
endclass

sample_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
  endtask
endclass

sample_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);
  endtask
endclass

sample_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
  endtask
endclass

ところで、上記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
  endtask
endclass
//------------------------------------------------------------------------
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;
  endtask
endclass

スレーブモデル

slaveモデルの方を記述していきます。

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;
  endfunction
endclass

sample_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;
  endfunction
endclass
//------------------------------------------------------------------------
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
  endtask
endclass

sample_env.sv

masterの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");
  endtask
endclass

sample_model.svh

slave系のファイルを追加します。

`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"

uvm_testの修正

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();
  endtask
endclass

コンパイル

vlog -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