【UVM】Register階層を作成する~AdapterでAgentと接続する~【#2】

Systemverilog

前回のおさらい

Register階層を作成しました。

UVMモデルを介してDUTのレジスタにアクセスするためにAdapterを作成します。

サンプルコード

sample_sequence_item.svh

Register階層と接続するUVMモデル(Agent)のSequence_itemです。

class regbus_sequence_item extends uvm_sequence_item;
    `uvm_object_utils(regbus_sequence_item)

    rand bit write_en;
    rand byte addr;
    rand byte data;

    function new(string name="regbus_sequence_item");
        super.new(name);
    endfunction
endclass
sample_reg_adapter.svh

uvm_reg_adapterを継承してsample_reg_adapterを作成します。

uvm_reg_adapterはpureメソッドのreg2busとbus2regを持っています。

この2つでRegister階層とUVMモデルで値を受け渡ししています。

class sample_reg_adapter extends uvm_reg_adapter;
    `uvm_object_utils(sample_reg_adapter)

    function new(string name="sample_reg_adapter");
        super.new(name);
    endfunction

    // Register階層からモデルに値を渡す。
    virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
        regbus_sequence_item trans;
        trans = regbus_sequence_item::type_id::create("trans");
        trans.write_en = (rw.kind == UVM_READ) ? 0 : 1;
        trans.addr = rw.addr;
        trans.data = rw.data;
        return trans;
    endfunction

    // モデルからRegister階層に値を渡す。
    virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
        regbus_sequence_item trans;
        $cast(trans, bus_item);
        rw.kind = trans.write_en ? UVM_READ : UVM_WRITE;
        rw.addr = trans.addr;
        rw.data = trans.data;
    endfunction
endclass
sample_env.svh

AdapterとUVMモデルを接続します。

class sample_env extends uvm_env;
    `uvm_component_utils(sample_env)
    sample_reg_block regmodel;
    sample_reg_adapter adapter;  // Adapterを宣言
    regbus_agent agent;

    function new(string name="sample_env", uvm_component parent);
        super.new(name, parent);
    endfunction

    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        adapter = sample_reg_adapter::type_id::create("adapter");
        agent = regbus_agent::type_id::create("agent", this);
        regmodel = sample_reg_block::type_id::create("regmodel");
        regmodel.build();
        regmodel.lock_model();

        uvm_config_db#(sample_reg_block)::set(uvm_root::get(), "*", "regmodel", regmodel);
    endfunction

    virtual function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        regmodel.default_map.set_sequencer(agent.sequencer, adapter);  // SequencerとAdapterを接続。
        regmodel.default_map.set_base_addr('h0);
    endfunction
endclass
sample_vseq.svh

uvm_regはbuilt-inメソッドでwriteを持っています。

<uvm_reg>.write(status, <値>)で、uvm_regで定義したレジスタに<値>をAdapter→UVMモデルという流れで書き込みます。

class sample_vseq extends uvm_sequence;
    `uvm_object_utils(sample_vseq)
    `uvm_declare_p_sequencer(sample_vsequencer)

    function new(string name="sample_vseq");
        super.new(name);
        set_automatic_phase_objection(1);
    endfunction

    virtual task body();
        uvm_status_e status;
        p_sequencer.regmodel.STATUS.write(status, 'h1);
        p_sequencer.regmodel.STATUS.write(status, 'h2);
    endtask
endclass
その他のファイル
`include "regbus_pkg.svh"
module top;
    `include "uvm_macros.svh"
    import uvm_pkg::*;

    import regbus_pkg::*;

    `include "sample_reg_block.svh"
    `include "sample_reg_adapter.svh"
    `include "sample_env.svh"
    `include "sample_vsequencer.svh"
    `include "sample_vseq.svh"
    `include "sample_test.svh"

    initial begin
        run_test("sample_test");
    end

    bit clk = 0;
    always #1 clk = ~clk;
    bit rst_n = 0;
    initial #10 rst_n = 1;

    logic write_en;
    logic [7:0] addr;
    logic [7:0] data;

    regbus i_regbus (
        .clk(clk),
        .rst_n(rst_n),
        .write_en(write_en),
        .addr(addr),
        .data(data)
    );
endmodule
interface regbus_if (
    logic clk,
    logic rst_n
);
    logic write_en;
    logic [7:0] addr;
    logic [7:0] data;
endinterface

module regbus #(
)(
    input clk,
    input rst_n,
    output logic write_en,
    output logic [7:0] addr,
    output logic [7:0] data
);
    `include "uvm_macros.svh"
    import uvm_pkg::*;

    regbus_if vif(
        .clk(clk),
        .rst_n(rst_n)
    );

    assign data = vif.data;
    assign addr = vif.addr;
    assign write_en = vif.write_en;

    initial begin
        uvm_config_db #(virtual regbus_if)::set(null, "*regbus*", "vif", vif);
    end
endmodule
package regbus_pkg;
    `include "uvm_macros.svh"
    import uvm_pkg::*;

    `include "regbus_sequence_item.svh"
    `include "regbus_sequencer.svh"
    `include "regbus_driver.svh"
    `include "regbus_agent.svh"
endpackage
class regbus_sequencer extends uvm_sequencer #(regbus_sequence_item);
    `uvm_component_utils(regbus_sequencer)

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
endclass
class regbus_driver extends uvm_driver #(regbus_sequence_item);
    `uvm_component_utils(regbus_driver)

    virtual regbus_if vif;

    function new(string name, uvm_component parent);
        super.new(name, parent);
        if (!uvm_config_db #(virtual regbus_if)::get(this, "regbus", "vif", vif))
            `uvm_fatal(this.get_name(), "vif is invalid")
    endfunction

    task run_phase(uvm_phase phase);
        forever begin
            seq_item_port.get_next_item(req);
            @(posedge vif.clk iff vif.rst_n);
            vif.write_en = req.write_en;
            vif.data = req.data;
            vif.addr = req.addr;
            @(posedge vif.clk);
            seq_item_port.item_done(rsp);
        end
    endtask
endclass
class regbus_agent extends uvm_agent;
    `uvm_component_utils(regbus_agent)

    regbus_driver driver;
    regbus_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 = regbus_driver::type_id::create("driver", this);
        sequencer = regbus_sequencer::type_id::create("sequencer", this);
    endfunction

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        driver.seq_item_port.connect(sequencer.seq_item_export);
    endfunction
endclass
class STATUS_reg extends uvm_reg; // レジスタクラス。
    `uvm_object_utils(STATUS_reg)

    function new(string name="STATUS_reg");
        super.new(name, 8, UVM_NO_COVERAGE);
    endfunction

    rand uvm_reg_field status;

    function void build();
        status = uvm_reg_field::type_id::create("status");
        status.configure(
            .parent(this),  // とりあえずthisでOK。
            .size(8),  // レジスタのビット幅
            .lsb_pos(0),  // LSBの開始位置
            .access("RW"),  // アクセスタイプ
            .volatile(0),  // わからん。
            .reset(0),  // 初期値
            .has_reset(1),  // リセット値を持つかどうか。
            .is_rand(1),  // 乱数対応
            .individually_accessible(0)  // わからん。
        );
    endfunction
endclass

class sample_reg_block extends uvm_reg_block;  // レジスタクラスをまとめる。
    rand STATUS_reg STATUS;  // 作成したレジスタクラスを羅列する。

    `uvm_object_utils(sample_reg_block)
    function new(string name="sample_reg_block");
        super.new(name, build_coverage(UVM_NO_COVERAGE));
    endfunction

    function void build();
        STATUS = STATUS_reg::type_id::create("STATUS");
        STATUS.configure(this);
        STATUS.build();

        default_map = create_map("", 0, 1, UVM_LITTLE_ENDIAN);
        default_map.add_reg(STATUS, 'h1); // レジスタクラスを追加する。
    endfunction
endclass
class sample_vsequencer extends uvm_sequencer;
    `uvm_component_utils(sample_vsequencer)

    regbus_sequencer seqr;
    sample_reg_block regmodel;

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
endclass
class sample_vseq extends uvm_sequence;
    `uvm_object_utils(sample_vseq)
    `uvm_declare_p_sequencer(sample_vsequencer)

    function new(string name="sample_vseq");
        super.new(name);
        set_automatic_phase_objection(1);
    endfunction

    virtual task body();
        uvm_status_e status;
        p_sequencer.regmodel.STATUS.write(status, 'h1);
        p_sequencer.regmodel.STATUS.write(status, 'h2);
    endtask
endclass

まとめ

Adapterを作成してUVMモデルを介してレジスタアクセスしました。

コメント

タイトルとURLをコピーしました