VOOZH about

URL: https://qiita.com/tethys_seesaa/items/3f41d4780bdbf4a6f6b3

⇱ [SystemVerilog]interfaceを入れてuvm_driverに似た何かを作る。 #HDL - Qiita


👁 Image
1

Go to list of users who liked

0

Share on X(Twitter)

Share on Facebook

Add to Hatena Bookmark

More than 5 years have passed since last update.

@tethys_seesaa

[SystemVerilog]interfaceを入れてuvm_driverに似た何かを作る。

1
Posted at

はじめに

前回の終わりに、モデルをUVMドライバ化と書きましたが、interfaceも含めて一気に書くと、 わかりにくくなると思いました。
ここではワンクッション置いて、interfaceを入れ、さらにuvmクラスを入れようと思います。

コード

共通コード

前回と同じです。

interface

全部の信号をinterfaceにまとめ、検証対象(DUT)のメソッドを入れてみました。

apbif.sv
interface apb_t();

 logic rst_n, clk;
 logic sel;
 logic enable;
 logic write;
 addr_t addr;
 data_t wdata;
 data_t rdata;

 function logic get_sel();
 return sel;
 endfunction

 function logic get_en();
 return enable;
 endfunction

 function logic get_rw();
 return write;
 endfunction

 function data_t get_data();
 return wdata;
 endfunction

 function data_t put_data(data_t data_i);
 rdata = data_i;
 return rdata;
 endfunction

 modport master (
 input rst_n, clk,
 output enable, sel, write, addr, wdata,
 input rdata
 );

 modport slave (
 import get_en, get_rw, get_sel, get_data, put_data,
 input rst_n, clk,
 input enable, sel, write, addr, wdata,
 output rdata
 );

endinterface

DUT

interfaceに応じて、信号名を変えています。

apb_slave.sv
module apb_slave #(
 parameter OFFSET = 32'h0000_0000
 )(
 apb_t.slave apb_slv
 );

 localparam MW = 256;

 addr_t addr_d;
 data_t [(MW -1) : 0 ] mem ;

 always_ff @(negedge apb_slv.rst_n, posedge apb_slv.clk)
 if(! apb_slv.rst_n)
 addr_d <= '0;
 else
 addr_d <= apb_slv.addr;

 always_ff @(negedge apb_slv.rst_n, posedge apb_slv.clk)
 if(! apb_slv.rst_n)
 mem <= '0;
 else
 if(apb_slv.get_en() && apb_slv.get_sel() && apb_slv.get_rw() && (addr_d>=OFFSET) && (addr_d< OFFSET + MW) )
 mem[addr_d - OFFSET ] <= apb_slv.get_data();

 always_comb begin
 if(apb_slv.get_en() && apb_slv.get_sel() && (! apb_slv.get_rw()) && (addr_d>=OFFSET) && (addr_d < OFFSET + MW) )
 apb_slv.put_data( mem [addr_d - OFFSET] );
 else
 apb_slv.rdata = '0;
 end

endmodule

モデル

こちらもinterfaceに合わせます。

apb_master_model.sv
module abp_master(
 apb_t.master apb_mst
 );

 task init();
 apb_mst.addr = '0;
 apb_mst.sel = '0;
 apb_mst.enable = '0;
 apb_mst.write = '0;
 apb_mst.wdata = 'z;
 endtask

 task do_write(int addr_i, data_t wdata_i);
 @(posedge apb_mst.clk);

 apb_mst.addr <= #1 addr_i;
 apb_mst.sel <= #1 1'b1;
 apb_mst.enable <= #1 1'b0;
 apb_mst.write <= #1 1'b1;
 apb_mst.wdata <= #1 wdata_i;

 @(posedge apb_mst.clk)
 apb_mst.enable <= #1 1'b1;

 @(posedge apb_mst.clk)

 apb_mst.addr <= #1 '0;
 apb_mst.sel <= #1 '0;
 apb_mst.enable <= #1 '0;
 apb_mst.write <= #1 '0;
 apb_mst.wdata <= #1 'z;
 endtask

 task do_read(int addr_i, output data_t rdata_o);

 @(posedge apb_mst.clk)
 apb_mst.addr <= #1 addr_i;
 apb_mst.sel <= #1 1'b1;
 apb_mst.enable <= #1 1'b0;
 apb_mst.write <= #1 1'b0;

 @(posedge apb_mst.clk)
 apb_mst.enable <= #1 1'b1;

 @(posedge apb_mst.clk)
 rdata_o = apb_mst.rdata;

 apb_mst.addr <= #1 '0;
 apb_mst.sel <= #1 '0;
 apb_mst.enable <= #1 '0;
 apb_mst.write <= #1 '0;
 endtask

 initial
 init();

endmodule

テスト

intterfaceを入れたので、モジュール内で宣言する信号が無くなりました。後は変わりません。UVMのクラス部分は後述します。

tb.sv
module tb();

 apb_t apb_bus();

 apb_slave #(.OFFSET(32'h0001_0000)) dut (.apb_slv(apb_bus), .*);

 abp_master tu(.apb_mst(apb_bus), .*);

 task clk_gen();
 apb_bus.clk = 0;
 forever
 #5 apb_bus.clk = ~ apb_bus.clk;
 endtask

 class base_drv extends uvm_driver;
 `uvm_component_utils(base_drv)

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

 function void build_phase(uvm_phase phase);
 super.build_phase(phase);
 endfunction

 virtual task reset_phase(uvm_phase phase);

 phase.raise_objection(this);

 `uvm_info("base_drv", "Reset Start", UVM_MEDIUM)

 apb_bus.rst_n = '0;
 repeat(5) @(posedge apb_bus.clk);
 apb_bus.rst_n = '1;
 repeat(5) @(posedge apb_bus.clk);

 `uvm_info("base_drv", "Reset End", UVM_MEDIUM)

 phase.drop_objection(this);

 endtask

 virtual task main_phase(uvm_phase phase);
 phase.raise_objection(this);
 `uvm_info("test", "Start", UVM_MEDIUM);
 `uvm_fatal(get_type_name(), "No test");
 phase.drop_objection(this);
 endtask

 endclass

 class test_drv extends base_drv;
 `uvm_component_utils(test_drv)

 int base_addr = 'h1_0000;

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

 function void build_phase(uvm_phase phase);
 super.build_phase(phase);
 endfunction

 virtual task main_phase(uvm_phase phase);
 data_t wdata;
 data_t mem_t [int];

 phase.raise_objection(this);

 `uvm_info("test_drv", "Start", UVM_MEDIUM);

 for (int i = 0; i < 'h100; i += 4) begin
 apb_bus.addr = base_addr + i;
 wdata = $urandom_range(32'hFFFF, 0);

 tu.do_write(apb_bus.addr, wdata);
 mem_t[apb_bus.addr] = wdata;

 `uvm_info("test_drv",
 $sformatf("Write addr == 0x%8h, wdata == 0x%8h", apb_bus.addr, wdata),
 UVM_MEDIUM);
 end

 for (int i = 0; i < 'h100; i += 4) begin
 apb_bus.addr = base_addr + i;
 tu.do_read(apb_bus.addr, apb_bus.rdata);

 if(apb_bus.rdata != mem_t[apb_bus.addr]) begin
 `uvm_error("Conpare Error",
 $sformatf("Read addr == 0x%8h, exdata == 0x%8h, rdata == 0x%8h", apb_bus.addr, mem_t[apb_bus.addr], apb_bus.rdata));
 end
 else begin
 `uvm_info("Compare Success",
 $sformatf("Read addr == 0x%8h, exdata == 0x%8h, rdata == 0x%8h", apb_bus.addr, mem_t[apb_bus.addr], apb_bus.rdata),
 UVM_MEDIUM);
 end

 end

 phase.drop_objection(this);
 endtask

 endclass

 class base_test extends uvm_test;
 `uvm_component_utils(base_test)

 test_drv drv;

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

 function void build_phase(uvm_phase phase);
 super.build_phase(phase);
 drv = test_drv::type_id::create("drv", this);
 endfunction

 endclass

 initial
 fork
 clk_gen();
 run_test();
 join_none

endmodule

テストについて

いろんな資料を見ると、uvm_driverというクラスがDUTとのピンレベルでのやり取りを行うようです。しかし、前回まで見てきたように、これまでDUTを叩いていたのはuvm_testクラスです。UVMのソースコードを拝見すると、uvm_testクラスとuvm_driverクラスは「uvm_component」というクラスを継承しています。

https://verificationacademy.com/sessions/uvm-connecting-components/rte/japanese-pdf
より引用。

👁 uvm_class_layer.png

ということで、uvm_testでもピンレベルのやり取りが可能です。 この辺の緩さがなんとも微妙ですね。 よって、このコードでは、継承するクラスをuvm_testクラスからuvm_driverクラスに変更しただけです(名前を変えただけ)。

その代わり、uvm_driverに変更したからuvm_testクラスを持ってきて継承し、テストを記述しなくてはなりません。その辺が、「class base_test extends uvm_test...」辺りの記述になります。

このクラス内では、先のtest_drvクラスを宣言し、インスタンスします。
uvm_componentクラスを継承してきたクラスは、new関数でインスタンスするのではなく、createと呼ばれるメソッド(おなじない)でインスタンスします。書式はあんな感じです。

おわりに

ということで、uvm_driverクラスを使って、uvm_driverに似た何かを作ってみました。APBマスターモデルをクラス化しないと、uvm_driverとは 個人的に言えるとは思えません。

というわけで、次回はいつかAPBマスターをクラス化し、UVMドライバとしてDUTとinterfaceを介してつないでみたいと思います。

1

Go to list of users who liked

0
0

Go to list of comments

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1

Go to list of users who liked

0