UVM Connect基础教程
--- Integrating SystemC into UVM
前言:尽管 UVM 为构建参考模型提供了高级抽象,但 SystemC 在用于设计和验证的高级模型方面具有传统。 为了允许在 UVM 测试平台中重用使用 SystemC 编写的参考模型,UVMC 在 SystemC 和 SystemVerilog UVM 模型和组件之间提供 TLM1 和 TLM2 连接(参见图 1)。
Figure 1 - UVMC: SystemC and SystemVerilog sides
对于 TLM 通信,验证组件必须同意它们正在交换的数据。 使用事务参数化这些组件使它们能够建立 UVMC 连接 [1]。
一、一个例子
为了说明 UVM Connect 的使用,将使用图 2 的测试平台作为示例。 与简单的加法器测试台不同,这个测试台没有监视器,也没有驱动程序,一旦检查的是模块refmod_low(用SystemC编写)的操作,在refmod(用SystemC编写的另一个模块)的操作中。
两个 refmod 的操作都非常简单:它们只是获取来自 uvm_tlm_analysis_fifo 的 from_refmod 和 from_refmod_low 的事务(带有消息的字符串),并将这些事务分别放入 uvm_tlm_fifo 的 to_refmod 和 to_refmod_low 中。
序列读取图 3 的树的文件,并且定序器将事务发送到 path_tr,其中使用 analysis_port 将其传输到 uvm_tlm_analysis_fifo 的 from_refmod 和 from_refmod_low。
Figure 3 - Tree containing transactions for the sequence
测试平台的代码及其 refmods 可以在下面看到。
- The class packet_in that defines the input transaction
class packet_in extends uvm_sequence_item;
string message;
`uvm_object_utils_begin(packet_in)
`uvm_field_string(message, UVM_DEFAULT|UVM_HEX)
`uvm_object_utils_end
function new(string name="packet_in");
super.new(name);
endfunction: new
endclass: packet_in
- The class packet_out that defines the output transaction
class packet_out extends uvm_sequence_item;
string message;
`uvm_object_utils_begin(packet_out)
`uvm_field_string(message, UVM_DEFAULT|UVM_HEX)
`uvm_object_utils_end
function new(string name="packet_in");
super.new(name);
endfunction: new
endclass: packet_out
- The sequence_in that generates the transactions withe the DPI function read_message()
import "DPI-C" context function string read_message(string msg);
class sequence_in extends uvm_sequence #(packet_in);
`uvm_object_utils(sequence_in)
function new(string name="sequence_in");
super.new(name);
endfunction: new
int data_file, scan_file;
string filename;
function void open_file();
data_file = $fopen("myfile.txt", "r");
if(data_file == 0)begin
$display("file could not be open!!!");
end
endfunction: open_file
task body();
packet_in tr;
open_file();
while(1)begin
scan_file = $fscanf(data_file, "%s", filename);
tr = packet_in::type_id::create("tr");
start_item(tr);
tr.message = read_message(filename);
finish_item(tr);
if($feof(data_file))
break;
end
$finish();
endtask: body
endclass: sequence_in
- The DPI function called in the sequence_in
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
extern "C" const char* read_message(const char* message){
string msg;
ifstream myfile(message);
if(myfile.is_open()){
getline(myfile, msg);
return msg.c_str();
}
else
cout << "unable to open file";
}
- The sequencer that sends the transaction to the class path_tr
class sequencer extends uvm_sequencer #(packet_in);
`uvm_component_utils(sequencer)
function new (string name = "sequencer", uvm_component parent = null);
super.new(name, parent);
endfunction
endclass: sequencer
- The class path_tr that trasmits the transaction to the uvm_tlm_analysis_fifo's from_refmod and from_refmod_low using an analysis_port
class path_tr extends uvm_driver #(packet_in);
packet_in tr;
`uvm_component_utils(path_tr)
uvm_analysis_port #(packet_in) item_collected_port;
function new(string name, uvm_component parent);
super.new(name, parent);
item_collected_port = new ("item_collected_port", this);
endfunction
virtual task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(tr);
begin_tr(tr, "path_tr");
item_collected_port.write(tr);
end_tr(tr);
seq_item_port.item_done();
end
endtask
endclass: path_tr
- The refmod (written in SystemC)
#include "systemc.h"
#include "tlm.h"
#include <string>
using namespace std;
using namespace tlm;
struct tr {
string message;
};
#include "uvmc.h"
using namespace uvmc;
UVMC_UTILS_1(tr, message)
SC_MODULE(refmod) {
sc_port<tlm_get_peek_if<tr> > in;
sc_port<tlm_put_if<tr> > out;
void p() {
tr tr;
while(1){
tr = in->get();
cout <<"refmod: " <<tr.message <<"\n";
out->put(tr);
}
}
SC_CTOR(refmod): in("in"), out("out") { SC_THREAD(p); }
};
- The refmod_low (written in SystemC)
SC_MODULE(refmod_low){
sc_port<tlm_get_peek_if<tr> > in;
sc_port<tlm_put_if<tr> > out;
void p() {
tr tr;
while(1){
tr = in->get();
cout <<"refmod_low: " <<tr.message <<"\n";
out->put(tr);
}
}
SC_CTOR(refmod_low): in("in"), out("out") { SC_THREAD(p); }
};
- The sc_main (file top.cpp) for making connections on the SystemC side
#include "refmod.cpp"
#include "refmod_low.cpp"
int sc_main(int argc, char* argv[]) {
refmod refmod_i("refmod_i");
refmod_low refmod_low_i("refmod_low_i");
uvmc_connect(refmod_i.in, "refmod_i.in");
uvmc_connect(refmod_low_i.in, "refmod_low_i.in");
uvmc_connect(refmod_i.out, "refmod_i.out");
uvmc_connect(refmod_low_i.out, "refmod_low_i.out");
sc_start();
return(0);
}
- The class comparator that expects transactions from the outputs of the refmod and refmod_low in order to compare them
class comparator #(type T = packet_in) extends uvm_scoreboard;
typedef comparator #(T) this_type;
`uvm_component_utils(this_type)
uvm_tlm_fifo #(T) from_refmod;
uvm_tlm_fifo#(T) from_refmod_low;
T tr1, tr2;
int match, mismatch;
function new(string name, uvm_component parent);
super.new(name, parent);
from_refmod = new("from_refmod", null, 1);
from_refmod_low = new("from_refmod_low", null, 1);
tr1 = new("tr1");
tr2 = new("tr2");
endfunction
function void connect_phase(uvm_phase phase);
uvmc_tlm1 #(T)::connect(from_refmod.put_export, "refmod_i.out");
uvmc_tlm1#(T)::connect(from_refmod_low.put_export, "refmod_low_i.out");
endfunction: connect_phase
task run_phase(uvm_phase phase);
forever begin
from_refmod.get(tr1);
from_refmod_low.get(tr2);
compare();
end
endtask: run_phase
virtual function void compare();
if(tr1.message == tr2.message) begin
$display("Comparator MATCH");
match++;
end
else begin
$display("Comparator MISMATCH");
mismatch++;
end
endfunction: compare
endclass: comparator
- The environment class
`include "comparator.sv"
class env extends uvm_env;
sequencer sqr;
path_tr path;
comparator #(packet_out) comp;
uvm_tlm_analysis_fifo #(packet_in) to_refmod;
uvm_tlm_analysis_fifo #(packet_in) to_refmod_low;
`uvm_component_utils(env)
function new(string name, uvm_component parent = null);
super.new(name, parent);
to_refmod = new("to_refmod", this);
to_refmod_low = new("to_refmod_low", this);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
sqr = sequencer::type_id::create("sqr", this);
path = path_tr::type_id::create("path", this);
comp = comparator #(packet_out)::type_id::create("comp", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
path.seq_item_port.connect(sqr.seq_item_export);
path.item_collected_port.connect(to_refmod.analysis_export);
uvmc_tlm1 #(packet_in)::connect(to_refmod.get_export, "refmod_i.in");
path.item_collected_port.connect(to_refmod_low.analysis_export);
uvmc_tlm1 #(packet_in)::connect(to_refmod_low.get_export, "refmod_low_i.in");
endfunction
virtual function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
endfunction
endclass
- The test class
class test extends uvm_test;
env env_h;
sequence_in seq;
`uvm_component_utils(test)
function new(string name, uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
env_h = env::type_id::create("env_h", this);
seq = sequence_in::type_id::create("seq", this);
endfunction
task run_phase(uvm_phase phase);
seq.start(env_h.sqr);
endtask: run_phase
endclass: test
- The top module(top.sv)
import uvm_pkg::*;
import uvmc_pkg::*;
`include "uvm_macros.svh"
`include "packet_in.sv"
`include "packet_out.sv"
`include "sequence_in.sv"
`include "sequencer.sv"
`include "path_tr.sv"
`include "env.sv"
`include "test.sv"
//Top
module top;
initial begin
`ifdef INCA
$recordvars();
`endif
`ifdef VCS
$vcdpluson;
`endif
`ifdef QUESTA
$wlfdumpvars();
set_config_int("*", "recording_detail", 1);
`endif
run_test("test");
end
endmodule
- 在顶部模块中,应导入 uvm 连接包(import uvmc_pkg::*;)。 图 4 显示了 refmod 和 refmod_low(SystemC 端)与比较器(SystemVerilog 端)之间的连接。
- SystemC端的连接可以在sc_main的以下几行中看到:
uvmc_connect(refmod_i.out, "refmod_i.out");
uvmc_connect(refmod_low_i.out, "refmod_low_i.out");
- SystemVerilog 端的连接可以在比较器的以下行中看到:
function void connect_phase(uvm_phase phase);
uvmc_tlm1 #(T)::connect(from_refmod.put_export, "refmod_i.out");
uvmc_tlm1#(T)::connect(from_refmod_low.put_export, "refmod_low_i.out");
endfunction: connect_phase