VOOZH about

URL: https://qiita.com/tethys_seesaa/items/8bc05346a2d487895834

⇱ [SystemVerilog]既存のモジュールを無理矢理UVMのフローに載せてみる。 #HDL - Qiita


👁 Image
6

Go to list of users who liked

5

Share on X(Twitter)

Share on Facebook

Add to Hatena Bookmark

More than 5 years have passed since last update.

@tethys_seesaa

[SystemVerilog]既存のモジュールを無理矢理UVMのフローに載せてみる。

6
Posted at

はじめに

前回はHollow Worldを出すまで到達しました。今回は実際にDUT(検証対象)を入れていきます。
UVMの解説だと、TLMがぁシーケンサーがぁドライバがぁ…とかありますが、それはまず置いておいて、ここでは普通のSystemVerilogのモデル、DUTをUVMの検証環境に載せてみます。
ここでは、APBバス通信をサンプルにやってみます。

コード

共通コード

UVMライブラリのインポートと、APBのアドレス/データ幅を決めます。

common.sv
`include "uvm_macros.svh"
import uvm_pkg::*;

parameter MEM_DEPTH = 256;
parameter ADR_WIDTH = 32;
parameter DAT_WIDTH = 32;

typedef logic [ADR_WIDTH-1:0] addr_t;
typedef logic [DAT_WIDTH-1:0] data_t;

DUT

中にレジスタアレイを持つAPBスレーブモジュールです。

apb_slave.sv
module apb_slave #(
 parameter OFFSET = 32'h0000_0000,
 parameter MW = 32
 )(
 input logic rst_n, clk,
 input logic sel,
 input logic enable,
 input logic write,
 input addr_t addr,
 input data_t wdata,
 output data_t rdata
 );

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

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

 always_ff @(negedge rst_n, posedge clk)
 if(! rst_n)
 mem <= '0;
 else
 if(write && sel && enable && (addr_d>=OFFSET) && (addr_d< OFFSET + MW) )
 mem[addr_d - OFFSET ] <= wdata;

 always_comb begin
 if(sel && (! write) && enable && (addr_d>=OFFSET) && (addr_d < OFFSET + MW) )
 rdata = mem[addr_d - OFFSET];
 else
 rdata = '0;
 end

endmodule

モデル

リードライト機能を持つAPBマスターモデルです。

apb_master_model.sv
module abp_master(
 input logic rst_n, clk,
 output logic sel,
 output logic enable,
 output logic write,
 output addr_t addr,
 output data_t wdata,
 input data_t rdata
 );

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

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

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

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

 @(posedge clk)

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

 task do_read(int addr_i, output data_t rdata_o);

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

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

 @(posedge clk)
 rdata_o = rdata;

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

 initial
 init();

endmodule

テスト

UVMのフローに沿って記述します。DUT、モデルのインスタンスとクロック発生を実施しています。テスト内容は、ランダムデータの書き込み読み出しと値の確認を行っています。UVMのクラス部分は後述します。

tb.sv
module tb();

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

 apb_slave #(.OFFSET(32'h0001_0000), .MW(MEM_DEPTH))
 dut (.*);

 abp_master tu(.*);

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

 class test_base extends uvm_test;
 `uvm_component_utils(test_base)

 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_test", "Reset Start", UVM_MEDIUM)

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

 `uvm_info("base_test", "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 test0 extends test_base;
 `uvm_component_utils(test0)

 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 rdata, wdata;
 data_t mem_t [int];

 phase.raise_objection(this);

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

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

 tu.do_write(addr, wdata); // Write data to DUT
 mem_t[addr] = wdata; // Write data to TB

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

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

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

 end

 phase.drop_objection(this);
 endtask

 endclass

 initial
 fork
 clk_gen();
 run_test();
 join_none

endmodule

テストについて

UVMのクラスでは、uvm_testクラスから継承したtest_baseクラスと、さらにそれを継承したtest0クラスがあります。uvm_component_utilsへのクラス名の登録と、function newは前回と同様のおまじないです。

function void build_phaseもおまじないと思って下さい。

で、taskの説明の前に、UVMのフロー(フェイジング)についてちょっと説明しておきます。

UVMではテストフローが決められており、それぞれのフローに相当するクラスを継承してコーディングすれば、UVMのフローでテストが実行されます。UVMは以下のフローを持ちます。
https://verificationacademy.com/cookbook/phasing
より引用。
👁 uvm_flow.png

先に書いたfunction void build_phaseが図の最初にある「Build Phases」に相当します。
タスクの部分は、「Run Phases」に相当します。Run Phasesは図のように細々としたステップがありますが、特に記述しなければ、UVMのデフォルトのステップで実行されます。

さてコードに戻り、taskではreset_phaseとmain_phaseをチョイスして、テストシナリオを記述しています。test_baseクラスではreset_phaseにてリセットの実行、main_phaseではメッセージを出すだけとなっています。

test0クラスではmain_phaseでテスト内容を記述しています。

テストの実行

+UVM_TESTNAME=test_baseでテスト実行するとこんな感じでFATALを出します。


UVM_FATAL tb.sv(53) @ 95: uvm_test_top [test_base] No test

これは、test_baseクラスではreset_phaseでDUTのリセット後、main_phaseではテストを実行していないため、uvm_fatalがコールされ、FATALとなります。

+UVM_TESTNAME=test0で実施すると、テストが正常実行されます。test0クラスはtest_baseクラスを継承しているため、reset_phaseが親クラスで実行されたあと、main_phaseでテストが実行されるためです。

おわりに

UVMはテスト手順が決まっていて、そこを拡張してピースを当てはめると、一応それらしいUVMテストが実施できます。
次はいつになるかわかりませんが、モデルをUVMドライバ化して、DUTとInterfaceで接続していくところをやってみようかと思います。

6

Go to list of users who liked

5
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
6

Go to list of users who liked

5