UVMについて学習
概要
参考記事にある実践UVM入門:検証のためのSystemVerilogクラスライブラリーをもとに学習した内容を本記事に記載.
実行環境概要
- OS:Ubuntu 24.04
- Verilator:5.053 devel rev v5.052-141-gc8b8c79ee
参考記事にある実践UVM入門:検証のためのSystemVerilogクラスライブラリーからサンプルソースコードをダウンロードできる.
UVMとは何か
UVMとは,検証分野で推奨されている技術,ルール,慣習,規律等をコードとして具体化したSystemVerilogのクラスライブラリー.
UVMは検証技術の再利用性を促進して生産性向上をさせる目的を持っている.
検証技術のトレンド
近年の検証技術では階層的テストベンチ記述手法が採用されている.
階層には以下表のような種類がある.
| 階層 | 機能例 | 検証目的および機能 |
|---|---|---|
| テストレイヤー | テストベンチ | テストベンチのトップレベルのレイヤー.テストを生成して下位の層に送る. |
| シナリオレイヤー | ジェネレーター | シナリオに沿ってトランザクションのシーケンスを生成する.ジェネレーターは,UVMのシーケンサーに相当. |
| ファンクショナルレイヤー | エージェント スコアボード チェッカー |
高位レベルのコマンドを処理するレイヤー.例えば,DMA read/write等のコマンドを受け取ると,個々のbus read/write等のコマンドに分割してコマンドレイヤーに送る. |
| コマンドレイヤー | ドライバー モニター コレクター |
トランザクションレベルのコマンドをシグナルレベルに変換してシグナルレイヤーに送る.ドライバーはDUTをドライブする目的を持つ.コレクターはDUTからのレスポンスをサンプリングし,トランザクションに変換する.モニターはコレクターからトランザクションを受け取り,チェック,カバレッジ等の計算を行う. |
| シグナルレイヤー | DUT | DUTからのレスポンスは上位層に送られる.アサーション等の結果も上位層に戻される. |
以下図ではそれぞれの階層における役割およびトランザクションの流れを検証環境として表現している.
環境構築
Verilatorのインストール
cd ~ # ホームディレクトリへ移動
sudo apt-get -y upgrade
sudo apt-get -y updata
sudo apt-get install \
git help2man perl python3 make \
autoconf g++ flex bison ccache \
libfl2 libfl-dev zlib1g \
zlib1g-dev liblz4-dev z3 # Verilatorに必要なパッケージをインストール
git clone https://github.com/verilator/verilator
cd verilator
unset VERILATOR_ROOT
autoconf
make -j `nproc`
sudo make install
hash -r
UVMのインストール
git clone https://github.com/chipsalliance/uvm-verilator.git
export UVM_HOME=$PWD/uvm-verilator # .bashrcに記載するなどして常に参照できるようにした方が良い
Verilator用UVMはここからダウンロードする必要がある.
GTKWaveのインストール
sudo apt-get install -y gtkwave
GTKWaveでVCD波形ファイルを出力し,動作確認をする.
サンプルの実行
サンプル環境概要
ディレクトリ構成は以下.
UVM
└── UVM_sample1
├── rundir
└── src
├── UVM_sample1.sv
├── agent.sv
├── collector.sv
├── driver.sv
├── sequencer.sv
├── monitor.sv
├── sample_data.sv
├── sample_if.sv
├── sample_pkg.sv
└── dut.sv
ソースコード
UVM_sample1.sv
`include "uvm_pkg.sv"
`include "uvm_macros.svh"
`include "sample_if.sv"
`include "sample_pkg.sv"
module tb_top;
import uvm_pkg::*;
import sample_pkg::*;
bit clk;
agent parent;
sample_if sif(clk);
dut DUT(.a(sif.a),.b(sif.b),.co(sif.co),.sum(sif.sum));
string wave_file;
initial begin
if( !$value$plusargs("wave=%s",wave_file) ) begin
wave_file = "dump.vcd";
end
$dumpfile(wave_file);
$dumpvars();
parent = agent::type_id::create("agent0",null);
uvm_config_db #(virtual sample_if)::set(null,"*agent0*","vif",sif);
run_test();
end
initial forever #10 clk = ~clk;
endmodule
agent.sv
class agent extends uvm_component;
driver d;
sequencer s;
collector c;
monitor m;
`uvm_component_utils(agent)
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
extern function void build_phase(uvm_phase phase);
extern function void connect_phase(uvm_phase phase);
endclass
function void agent::build_phase(uvm_phase phase);
super.build_phase(phase);
d = driver::type_id::create("driver",this);
s = sequencer::type_id::create("sequencer",this);
c = collector::type_id::create("collector",this);
m = monitor::type_id::create("monitor",this);
endfunction
function void agent::connect_phase(uvm_phase phase);
super.connect_phase(phase);
d.get_port.connect(s.get_export);
c.analysis_port.connect(m.analysis_export);
endfunction
collector.sv
class collector extends uvm_component;
virtual sample_if vif;
uvm_analysis_port #(sample_data_t) analysis_port;
`uvm_component_utils(collector)
function new(string name,uvm_component parent);
super.new(name,parent);
analysis_port = new("analysis_port",this);
endfunction
extern function void connect_phase(uvm_phase phase);
extern task run_phase(uvm_phase phase);
extern task get_response();
endclass
function void collector::connect_phase(uvm_phase phase);
super.connect_phase(phase);
if( !uvm_config_db #(virtual sample_if)::get(this,get_full_name(),"vif",vif) )
`uvm_error("NO-VIF",{"VIF error for ",get_full_name(),".vif"})
endfunction
task collector::run_phase(uvm_phase phase);
get_response();
endtask
task collector::get_response();
sample_data_t data;
data = sample_data_t::type_id::create("collected_data");
forever begin
@(vif.a,vif.b); // wait until inputs change
#0; // wait until outputs become stable
data.a = vif.a;
data.b = vif.b;
data.co = vif.co;
data.sum = vif.sum;
analysis_port.write(data); // send transaction to monitor
end
endtask
driver.sv
class collector extends uvm_component;
virtual sample_if vif;
uvm_analysis_port #(sample_data_t) analysis_port;
`uvm_component_utils(collector)
function new(string name,uvm_component parent);
super.new(name,parent);
analysis_port = new("analysis_port",this);
endfunction
extern function void connect_phase(uvm_phase phase);
extern task run_phase(uvm_phase phase);
extern task get_response();
endclass
function void collector::connect_phase(uvm_phase phase);
super.connect_phase(phase);
if( !uvm_config_db #(virtual sample_if)::get(this,get_full_name(),"vif",vif) )
`uvm_error("NO-VIF",{"VIF error for ",get_full_name(),".vif"})
endfunction
task collector::run_phase(uvm_phase phase);
get_response();
endtask
task collector::get_response();
sample_data_t data;
data = sample_data_t::type_id::create("collected_data");
forever begin
@(vif.a,vif.b); // wait until inputs change
#0; // wait until outputs become stable
data.a = vif.a;
data.b = vif.b;
data.co = vif.co;
data.sum = vif.sum;
analysis_port.write(data); // send transaction to monitor
end
endtask
sequencer.sv
class sequencer extends uvm_component;
uvm_blocking_get_imp #(sample_data_t,sequencer) get_export;
`uvm_component_utils(sequencer)
function new(string name,uvm_component parent);
super.new(name,parent);
get_export = new("get_export",this);
endfunction
extern task get(output sample_data_t data);
endclass
task sequencer::get(output sample_data_t data);
data = sample_data_t::type_id::create("sample_data");
assert( data.randomize() );
endtask
monitor.sv
class monitor extends uvm_component;
uvm_analysis_imp #(sample_data_t,monitor) analysis_export;
`uvm_component_utils(monitor)
function new(string name,uvm_component parent);
super.new(name,parent);
analysis_export = new("analysis_export",this);
endfunction
extern function void write(sample_data_t data);
endclass
function void monitor::write(sample_data_t data);
`uvm_info( "MONITOR",$sformatf("a=%0d b=%0d {co,sum}=%0d", data.a, data.b, {data.co,data.sum}), UVM_LOW );
endfunction
sample_data.sv
class sample_data_t extends uvm_sequence_item;
rand bit [1:0] a, b;
bit [1:0] sum;
bit co;
`uvm_object_utils_begin(sample_data_t)
`uvm_field_int(a,UVM_DEFAULT)
`uvm_field_int(b,UVM_DEFAULT)
`uvm_field_int(sum,UVM_DEFAULT|UVM_NOCOMPARE|UVM_NOPACK)
`uvm_field_int(co,UVM_DEFAULT|UVM_NOCOMPARE|UVM_NOPACK)
`uvm_object_utils_end
function new(string name="sample_data_t");
super.new(name);
endfunction
endclass
sample_if.sv
interface sample_if(input bit clk);
bit [1:0] a, b, sum;
bit co;
clocking cb @(posedge clk); endclocking
endinterface
sample_pkg.sv
`ifndef PKG_H
`define PKG_H
package sample_pkg;
import uvm_pkg::*;
`include "sample_data.sv"
`include "sequencer.sv"
`include "driver.sv"
`include "monitor.sv"
`include "collector.sv"
`include "agent.sv"
endpackage
`endif
dut.sv
module dut(input [1:0] a,b,output co,[1:0] sum);
assign {co,sum} = a+b;
endmodule
コンパイルから実行
コンパイル
cd UVM/UVM_sample1/rundir
export UVM_HOME=${HOME}/uvm-verilator
verilator --binary -j 0 --vpi \
--timescale 1ns/1ps \
--trace-vcd --trace-structs \
--top-module tb_top \
-Wno-WIDTH \
-CFLAGS -O0 \
+incdir+$UVM_HOME/src \
+incdir+../src \
$UVM_HOME/src/uvm_pkg.sv \
$UVM_HOME/src/dpi/uvm_dpi.cc \
UVM_sample1.sv
実行
コンパイルしたrundirディレクトリで以下コマンドを実行する.
./obj_dir/Vtb_top +UVM_VERBOSITY=UVM_LOW +UVM_NO_RELNOTES
参考のため実行ログおよびGTKWaveによる波形を表示する.
UVM_INFO @ 0: reporter [RNTST] Running test ...
UVM_INFO ../src/monitor.sv(16) @ 10000: agent0.monitor [MONITOR] a=3 b=1 {co,sum}=4
UVM_INFO ../src/monitor.sv(16) @ 30000: agent0.monitor [MONITOR] a=2 b=2 {co,sum}=4
UVM_INFO ../src/monitor.sv(16) @ 50000: agent0.monitor [MONITOR] a=0 b=2 {co,sum}=2
UVM_INFO ../src/monitor.sv(16) @ 70000: agent0.monitor [MONITOR] a=3 b=1 {co,sum}=4
UVM_INFO ../src/monitor.sv(16) @ 90000: agent0.monitor [MONITOR] a=3 b=0 {co,sum}=3
UVM_INFO ../src/monitor.sv(16) @ 110000: agent0.monitor [MONITOR] a=1 b=2 {co,sum}=3
UVM_INFO ../src/monitor.sv(16) @ 130000: agent0.monitor [MONITOR] a=2 b=2 {co,sum}=4
UVM_INFO ../src/monitor.sv(16) @ 150000: agent0.monitor [MONITOR] a=3 b=2 {co,sum}=5
UVM_INFO ../src/monitor.sv(16) @ 170000: agent0.monitor [MONITOR] a=0 b=2 {co,sum}=2
UVM_INFO ../src/monitor.sv(16) @ 190000: agent0.monitor [MONITOR] a=0 b=0 {co,sum}=0
UVM_INFO ../src/monitor.sv(16) @ 210000: agent0.monitor [MONITOR] a=2 b=3 {co,sum}=5
UVM_INFO ../src/monitor.sv(16) @ 230000: agent0.monitor [MONITOR] a=2 b=0 {co,sum}=2
UVM_INFO ../src/monitor.sv(16) @ 250000: agent0.monitor [MONITOR] a=2 b=2 {co,sum}=4
UVM_INFO ../src/monitor.sv(16) @ 270000: agent0.monitor [MONITOR] a=0 b=3 {co,sum}=3
UVM_INFO ../src/monitor.sv(16) @ 290000: agent0.monitor [MONITOR] a=3 b=1 {co,sum}=4
UVM_INFO /home/uvm_exeuter/uvm-verilator/src/base/uvm_report_server.svh(1009) @ 290000: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
** Report counts by severity
UVM_INFO : 16
UVM_WARNING : 0
UVM_ERROR : 0
UVM_FATAL : 0
** Report counts by id
[MONITOR] 15
[RNTST] 1
- /home/uvm_exeuter/uvm-verilator/src/base/uvm_test_runner.svh:297: Verilog $finish
- S i m u l a t i o n R e p o r t: Verilator 5.053 devel
- Verilator: $finish at 290ns; walltime 0.045 s; speed 6.473 us/s
- Verilator: cpu 0.022 s on 1 threads; allocated 18 MB
