struct animal {
char name[20];
int birthday;/*example: 20030910*/
char category[20];/*example: bird, non_bird*/
int food_weight;
int is_healthy;
};
void print_animal(struct animal * zoo_member){
printf("My name is %s\n", zoo_member->name);
printf("My birthday is %d\n", zoo_member->birthday);
printf("I am a %s\n", zoo_member->category);
printf("I could eat %d gram food one day\n", zoo_member->food_weight);
printf("My healthy status is %d\n", zoo_member->is_healthy);
}
void main()
{
struct animal members[20];
strcpy(members[0].name, "parrot");
members[0].birthday = 20091021;
strcpy(members[0].category, "bird");
members[0].food_weight = 20;
members[0].is_healthy = 1;
print_animal(&members[0]);
}
class animal;
string name;
int birthday; /*example: 20030910*/
string category; /*example: bird, non_bird*/
int food_weight;
int is_healthy;
function void print();
$display("My name is %s", name);
$display("My birthday is %d", birthday);
$display("I am a %s", category);
$display("I could eat %d gram food one day", food_weight);
$display("My healthy status is %d", is_healthy);
endfunction
endclass
initial begin
animal members[20];
members[0] = new();
members[0].name = "parrot";
members[0].birthday = 20091021;
members[0].category = "bird";
members[0].food_weight = 20;
members[0].is_healthy = 1;
members[0].print();
end
在上节的例子中,animal中所有的成员变量对于外部来说都是可见的,所以在initial语句中可以直接使用直接引用的方式对其进行赋值,为了避免这种情况,面向对象的开发者们设计了私有变量(SystemVerilog中为local,其他编程语言各不相同,如private)当一个变量被设置为local类型后,那么这个变量就会具有两大特点:
例:
class animal;
string name;
local int birthday; /*example: 20030910*/
local string category; /*example: bird, non_bird*/
local int food_weight;
local int is_healthy;
endclass
function void init(string iname, int ibirthday, string icategory, int ifood _weight, int iis_healthy);
name = iname;
birthday = ibirthday;
category = icategory;
food_weight = ifood_weight;
is_healthy = iis_healthy;
endfunction
面向对象编程的第二大特征就是继承。在一个动物园中,有两种动物,一种是能飞行的鸟类,一种是不能飞行的爬行动物。 假设动物园中有100只鸟类、200只爬行动物。在建立动物园的管理系统时,需要实例化100个animal变量,这100个变量的category 都要设置为bird,同时需要实例化200个animal变量,这200个变量的category都要设置为non_bird。100次或者200次做同样一件事情 是比较容易出错的。
一个动物园中所有的动物都可以抽像成上节所示的animal类,在animal类的基础上,派生(继承)出bird类和non_bird类:
class 子类 extends 父类;
endclass
//local 不可访问内部成员
class animal;
string name;
local int birthday; /*example: 20030910*/
local string category; /*example: bird, non_bird*/
local int food_weight;
local int is_healthy;
endclass
class bird extends animal;
function new();
super.new();
category = "bird";
endfunction
endclass
class non_bird extends animal;
function new();
super.new();
category = "non_bird";
endfunction
endclass
如果一个变量是local类型的,那么它是不能被外部直接访问的。如果父类中某成员变量是local类型,那么子类不可以访问父类的local变量和函数。父类中的成员变量想让子类访问,同时不想被外部访问,那么可以将这些变量声明为protected类型,与local类似,protected关键字同样可以应用于函数/任务中.
//protected子类可访问,外部不可访问
class animal;
string name;
protected int birthday;/*example: 20030910*/
protected string category;/*example: bird, non_bird*/
protected int food_weight;
protected int is_healthy;
endclass
class animal;
string name;
protected int birthday;/*example: 20030910*/
protected string category;/*example: bird, non_bird*/
protected int food_weight;
protected int is_healthy;
function void print_hometown();
$display("my hometown is on the earth!");
endfunction
endclass
//子类
class bird extends animal;
function void print_hometown();
$display("my hometown is in sky!");
endfunction
endclass
class non_bird extends animal;
function void print_hometown();
$display("my hometown is on the land!");
endfunction
endclass
/*
关键字automatic用于声明自动变量(automatic variables)。
自动变量是在声明时创建并在每次进作用域时分配内存,在离开作用域时自动释放内存。
这意味着自动变量的生命周期仅限于其所在的作用域。
*/
function automatic void print_animal(animal p_animal);
p_animal.print();
p_animal.print_hometown();
endfunction
接下来调用,print_animal的参数是一个animal类型的指针,如果实例化了一个bird,并且将其传递给print_animal函数,这样做是完全允许 的,因为bird是从animal派生的,所以bird本质上是个animal:
initial begin
bird members[20];
members[0] = new();
members[0].init("parrot", 20091021, "bird", 20, 1); initialize
print_animal(members[0]); //把类当参数传递
End
/*
结果: my hometown is on the earth!
即调用的不是子类brid, 而是父类animal的函数
*/
function automatic void print_animal2(animal p_animal);
bird p_bird;
non_bird p_nbird;
p_animal.print();
if($cast(p_bird, p_animal))
p_bird.print_hometown();
else if($cast(p_nbird, p_animal))
p_nbird.print_hometown();
endfunction
class animal;
virtual function void print_hometown2();
$display("my hometown is on the earth!");
endfunction
endclass
class bird extends animal;
virtual function void print_hometown2();
$display("my hometown is in sky!");
endfunction
endclass
class non_bird extends animal;
virtual function void print_hometown2();
$display("my hometown is on the land!");
endfunction
endclass
function automatic void print_animal3(animal p_animal);
p_animal.print();
p_animal.print_hometown2();
endfunction
initial begin
non_bird members[20];
members[0] = new();
members[0].init("tiger", 20091101, "non_bird", 2000, 1);
print_animal(members[0]);
end
在SystemVerilog(以下简称SV)中,并没有像 C++ 中使用 virtual
关键字来声明虚函数。在 SystemVerilog 中,多态性可以通过继承和重载的方式来实现。
class Animal;
task makeSound();
$display("Animal makes a sound.");
endtask
endclass
class Cat extends Animal;
task makeSound();
$display("Cat meows.");
endtask
endclass
class Dog extends Animal;
task makeSound();
$display("Dog barks.");
endtask
endclass
module testbench;
initial begin
Animal animal1;
Animal animal2;
animal1 = new Cat();
animal2 = new Dog();
animal1.makeSound(); // 输出:Cat meows.
animal2.makeSound(); // 输出:Dog barks.
end
endmodule
class animal;
bit [10:0] kind;
rand bit[5:0] data;
rand int addr;
endclass
initial begin
animal aml;
aml = new();
assert(aml.randomize());
end
class animal;
rand bit[5:0] data;
constraint data_cons{
data > 10;
data < 30;
}
endclass
initial begin
animal aml;
aml = new();
assert(aml.randomize() with {data > 10; data < 30;});
end
1、前身是OVM,
2、要学习如何使用sequence机制、factory机制、callback机制、寄存器模型(
典型的验证平台
module dut(clk,
rst_n,
rxd,
rx_dv,
txd,
tx_en);
input clk;
input rst_n;
input[7:0] rxd;
input rx_dv;
output [7:0] txd;
output tx_en;
reg[7:0] txd;
reg tx_en;
always @(posedge clk) begin
if(!rst_n) begin
txd <= 8'b0;
tx_en <= 1'b0;
end
else begin
txd <= rxd;
tx_en <= rx_dv;
end
end
endmodule
//my_driver.sv
class my_driver extends uvm_driver;
function new(string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass
task my_driver::main_phase(uvm_phase phase);
top_tb.rxd <= 8'b0;
top_tb.rx_dv <= 1'b0;
while(!top_tb.rst_n)
@(posedge top_tb.clk);
for(int i = 0; i < 256; i++)begin
@(posedge top_tb.clk);
top_tb.rxd <= $urandom_range(0, 255);
top_tb.rx_dv <= 1'b1;
`uvm_info("my_driver", "data is drived", UVM_LOW)
//打印信息
end
@(posedge top_tb.clk);
top_tb.rx_dv <= 1'b0;
endtask
`uvm_info("my_driver", "data is drived", UVM_LOW)
第一个参数是字符串,用于把打印的信息归类;
UVM_INFO my_driver.sv(20)
@48500000
:drv[my_driver]data is drived
//定义类
classs A;
…
endclass
//实例化
//实例化指的是通过new创造出A的一个实例
A a_inst;
a_inst = new();
//top_tb.sv
`timescale 1ns/1ps
`include "uvm_macros.svh" //是UVM中的一个文件,里面包含了众多的宏定义
import uvm_pkg::*; Import the entire uvm_pkg into the validation platform
`include "my_driver.sv" Introducing a written driver class (derived from UVM_driver)
module top_tb;
reg clk;
reg rst_n;
reg[7:0] rxd;
reg rx_dv;
wire[7:0] txd;
wire tx_en;
dut my_dut (
.clk(clk),
.rst_n(rst_n),
.rxd(rxd),
.rx_dv(rx_dv),
.txd(txd),
.tx_en(tx_en)
);
initial begin
my_driver drv;
drv = new("drv", null); //传入name 和 parent
drv.main_phase(null);
$finish();
end
initial begin
clk = 0;
forever begin
#100 clk = ~clk;
end
end
initial begin
rst_n = 1'b0;
#1000;
rst_n = 1'b1;
end
endmodule
//my_driver.sv
class my_driver extends uvm_driver;
`uvm_component_utils(my_driver)
function new(string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
`uvm_info("my_driver", "new is called", UVM_LOW);
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass
task my_driver::main_phase(uvm_phase phase);
`uvm_info("my_driver", "main_phase is called", UVM_LOW);
top_tb.rxd <= 8'b0;
top_tb.rx_dv <= 1'b0;
while(!top_tb.rst_n)
@(posedge top_tb.clk);
for(int i = 0; i < 256; i++)begin
@(posedge top_tb.clk);
top_tb.rxd <= $urandom_range(0, 255);
top_tb.rx_dv <= 1'b1;
`uvm_info("my_driver", "data is drived", UVM_LOW);
end
@(posedge top_tb.clk);
top_tb.rx_dv <= 1'b0;
endtask
//top_tb.sv
`timescale 1ns/1ps
`include "uvm_macros.svh" //是UVM中的一个文件,里面包含了众多的宏定义
import uvm_pkg::*; Import the entire uvm_pkg into the validation platform
`
`include "my_driver.sv" Introducing a written driver class (derived from UVM_driver)
module top_tb;
reg clk;
reg rst_n;
reg[7:0] rxd;
reg rx_dv;
wire[7:0] txd;
wire tx_en;
dut my_dut (
.clk(clk),
.rst_n(rst_n),
.rxd(rxd),
.rx_dv(rx_dv),
.txd(txd),
.tx_en(tx_en)
);
initial begin
run_test("my_driver");
end
//输出
//new is called
//main_phased is called
initial begin
clk = 0;
forever begin
#100 clk = ~clk;
end
end
initial begin
rst_n = 1'b0;
#1000;
rst_n = 1'b1;
end
endmodule
//my_driver.sv
task my_driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("my_driver", "main_phase is called", UVM_LOW);
top_tb.rxd <= 8'b0;
top_tb.rx_dv <= 1'b0;
while(!top_tb.rst_n)
@(posedge top_tb.clk);
for(int i = 0; i < 256; i++)begin
@(posedge top_tb.clk);
top_tb.rxd <= $urandom_range(0, 255);
top_tb.rx_dv <= 1'b1;
`uvm_info("my_driver", "data is drived", UVM_LOW);
end
@(posedge top_tb.clk);
top_tb.rx_dv <= 1'b0;
phase.drop_objection(this);
endtask
//data is drived”按照预期输出了256次。
`define TOP top_tb
task my_driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("my_driver", "main_phase is called", UVM_LOW);
`TOP.rxd <= 8'b0;
`TOP.rx_dv <= 1'b0;
while(!`TOP.rst_n)
@(posedge `TOP.clk);
for(int i = 0; i < 256; i++)begin
@(posedge `TOP.clk);
`TOP.rxd <= $urandom_range(0, 255);
`TOP.rx_dv <= 1'b1;
`uvm_info("my_driver", "data is drived", UVM_LOW);
end
@(posedge `TOP.clk);
`TOP.rx_dv <= 1'b0;
phase.drop_objection(this);
endtask
interface my_if(
input clk,
input rst_n
);
logic [7:0] data;
logic valid;
endinterface
//top_tb.sv
my_if input_if(clk, rst_n);
my_if output_if(clk, rst_n);
dut my_dut(
.clk(clk),
.rst_n(rst_n),
.rxd(input_if.data),
.rx_dv(input_if.valid),
.txd(output_if.data),
.tx_en(output_if.valid)
);
如何在driver中使用interface ?
class my_driver extends uvm_driver;
my_if drv_if; //可以在module中这样
//因为my_driver是一个类,在类中不能使用上述方式声明一个interface
endclass
因为my_driver是一个类,在类中不能使用上述方式声明一个interface,要用virtual interface
//my_driver.sv
class my_driver extends uvm_driver;
virtual my_if vif;
//my_driver.sv
class my_driver extends uvm_driver;
virtual my_if vif;
//用interface来避免绝对路径
`uvm_component_utils(my_driver)
//加入factory机制
function new(string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
`uvm_info("my_driver", "new is called", UVM_LOW);
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass
task my_driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
//加入objection机制 通过objection机制来控制验证平台的关闭
`uvm_info("my_driver", "main_phase is called", UVM_LOW);
vif.data <= 8'b0;
vif.valid <= 1'b0;
while(!vif.rst_n)
@(posedge vif.clk);
for(int i = 0; i < 256; i++)begin
@(posedge vif.clk);
vif.data <= $urandom_range(0, 255);
vif.valid <= 1'b1;
`uvm_info("my_driver", "data is drived", UVM_LOW);
end
@(posedge vif.clk);
vif.valid <= 1'b0;
phase.drop_objection(this);
//加入objection机制
endtask
剩下的最后一个问题就是,如何把top_tb中的input_if和my_driver中的vif对应,最简单的方法莫过于直接赋值,但是top_tb.my_driver.xxx是不可以的。这个问题的终极原因在于UVM通过run_test语句实例化了一个脱离了top_tb层次结构的实例,建立了一个新的层次结构。
//config_db机制
//top_tb.sv
initial begin
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input_if);
end
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("my_driver", "build_phase is called", UVM_LOW);
if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
endfunction
//tob_tb.sv
initial begin
uvm_config_db#(int)::set(null, "uvm_test_top", "var", 100);
end
//my_driver.sv
class my_driver extends uvm_driver;
int var;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("my_driver", "build_phase is called", UVM_LOW);
if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
if(!uvm_config_db#(int)::get(this, "", "var", var))
`uvm_fatal("my_driver", "var must be set!!!")
endfunction
//tob_tb.sv
initial begin
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input_if);
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif2", output_if);
end
//my_driver.sv
virtual my_if vif;
virtual my_if vif2;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("my_driver", "build_phase is called", UVM_LOW);
if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
if(!uvm_config_db#(virtual my_if)::get(this, "", "vif2", vif2))
`uvm_fatal("my_driver", "virtual interface must be set for vif2!!!")
endfunction
//my_transaction.sv
//所有的transaction都要从uvm_sequence_item派生
class my_transaction extends uvm_sequence_item;
rand bit[47:0] dmac; //48bit的以太网目的地址
rand bit[47:0] smac; //smac是48bit的以太网源地址
rand bit[15:0] ether_type; //ether_type是以太网类型
rand byte pload[]; //pload是其携带数据的大小
rand bit[31:0] crc; //CRC是前面所有数据的校验值
constraint pload_cons{ //大小被限制在46~1500byte
pload.size >= 46;
pload.size <= 1500;
}
function bit[31:0] calc_crc();
return 32'h0;
endfunction
function void post_randomize(); //post_randomize是SystemVerilog中提供的一个函数
crc = calc_crc;
endfunction
`uvm_object_utils(my_transaction)
function new(string name = "my_transaction");
super.new(name);
endfunction
endclass
//my_driver.sv
class my_driver extends uvm_driver;
virtual my_if vif;
//用interface来避免绝对路径
`uvm_component_utils(my_driver)
//加入factory机制
function new(string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
`uvm_info("my_driver", "new is called", UVM_LOW);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("my_driver", "build_phase is called", UVM_LOW);
if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass
task my_driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
//加入objection机制 通过objection机制来控制验证平台的关闭
//加入transaction的驱动
my_transaction tr;
for(int i = 0; i < 2; i++) begin
tr = new("tr");
//先使用randomize将tr随机化
assert(tr.randomize() with {pload.size == 200;});
//通过drive_one_pkt任务将tr的内容驱动到DUT的端口上
drive_one_pkt(tr);
end
`uvm_info("my_driver", "main_phase is called", UVM_LOW);
vif.data <= 8'b0;
vif.valid <= 1'b0;
while(!vif.rst_n)
@(posedge vif.clk);
for(int i = 0; i < 256; i++)begin
@(posedge vif.clk);
vif.data <= $urandom_range(0, 255);
vif.valid <= 1'b1;
`uvm_info("my_driver", "data is drived", UVM_LOW);
end
@(posedge vif.clk);
vif.valid <= 1'b0;
phase.drop_objection(this);
//加入objection机制
endtask
task my_driver::drive_one_pkt(my_transaction tr);
bit [47:0] tmp_data;
bit [7:0] data_q[$];
//push dmac to data_q
tmp_data = tr.dmac;
for(int i = 0; i < 6; i++) begin
data_q.push_back(tmp_data[7:0]);
tmp_data = (tmp_data >> 8);
end
//push smac to data_q
//push ether_type to data_q
//push payload to data_q
//push crc to data_q
tmp_data = tr.crc;
for(int i = 0; i < 4; i++) begin
data_q.push_back(tmp_data[7:0]);
tmp_data = (tmp_data >> 8);
end
`uvm_info("my_driver", "begin to drive one pkt", UVM_LOW);
repeat(3) @(posedge vif.clk);
while(data_q.size() > 0) begin
@(posedge vif.clk);
vif.valid <= 1'b1;
vif.data <= data_q.pop_front();
end
@(posedge vif.clk);
vif.valid <= 1'b0;
`uvm_info("my_driver", "end drive one pkt", UVM_LOW);
endtask
class my_env extends uvm_env;
my_driver drv;
//由于my_driver在uvm_env中实例化,
//所以my_driver的父结点(parent)就是my_env。
function new(string name = "my_env", uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = my_driver::type_id::create("drv", this);
endfunction
`uvm_component_utils(my_env)
//uvm_component_utils宏来实现factory的注册
endclass
`ifndef MY_MONITOR__SV
`define MY_MONITOR__SV
class my_monitor extends uvm_monitor;
virtual my_if vif;
`uvm_component_utils(my_monitor)
//uvm_component_utils宏来实现factory的注册
function new(string name = "my_monitor", uvm_component parent = null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("my_monitor", "virtual interface must be set for vif!!!")
endfunction
extern task main_phase(uvm_phase phase);
extern task collect_one_pkt(my_transaction tr);
endclass
task my_monitor::main_phase(uvm_phase phase);
my_transaction tr;
while(1) begin
tr = new("tr");
collect_one_pkt(tr);
end
endtask
task my_monitor::collect_one_pkt(my_transaction tr);
bit[7:0] data_q[$];
int psize;
while(1) begin
@(posedge vif.clk);
if(vif.valid) break;
end
`uvm_info("my_monitor", "begin to collect one pkt", UVM_LOW);
while(vif.valid) begin
data_q.push_back(vif.data);
@(posedge vif.clk);
end
//pop dmac
for(int i = 0; i < 6; i++) begin
tr.dmac = {tr.dmac[39:0], data_q.pop_front()};
end
//pop smac
for(int i = 0; i < 6; i++) begin
tr.smac = {tr.smac[39:0], data_q.pop_front()};
end
//pop ether_type
for(int i = 0; i < 2; i++) begin
tr.ether_type = {tr.ether_type[7:0], data_q.pop_front()};
end
psize = data_q.size() - 4;
tr.pload = new[psize];
//pop payload
for(int i = 0; i < psize; i++) begin
tr.pload[i] = data_q.pop_front();
end
//pop crc
for(int i = 0; i < 4; i++) begin
tr.crc = {tr.crc[23:0], data_q.pop_front()};
end
`uvm_info("my_monitor", "end collect one pkt, print it:", UVM_LOW);
tr.my_print();
endtask
`endif
class my_env extends uvm_env;
my_driver drv;
//由于my_driver在uvm_env中实例化,
//所以my_driver的父结点(parent)就是my_env。
my_monitor i_mon;
my_monitor o_mon;
function new(string name = "my_env", uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = my_driver::type_id::create("drv", this);
i_mon = my_monitor::type_id::create("i_mon", this);
o_mon = my_monitor::type_id::create("o_mon", this);
endfunction
`uvm_component_utils(my_env)
//uvm_component_utils宏来实现factory的注册
endclass
在env中实例化monitor后,要在top_tb中使用config_db将input_if和output_if传递给两个monitor:
//my_agent.sv
class my_agent extends uvm_agent ;
my_driver drv;
my_monitor mon;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void connect_phase(uvm_phase phase);
`uvm_component_utils(my_agent)
endclass
function void my_agent::build_phase(uvm_phase phase);
super.build_phase(phase);
//is_active是uvm_agent的一个成员变量
if (is_active == UVM_ACTIVE) begin
drv = my_driver::type_id::create("drv", this);
end
mon = my_monitor::type_id::create("mon", this);
endfunction
function void my_agent::connect_phase(uvm_phase phase);
super.connect_phase(phase);
endfunction
class my_env extends uvm_env;
my_agent i_agt;
my_agent o_agt;
function new(string name = "my_env", uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
i_agt = my_agent::type_id::create("i_agt", this);
o_agt = my_agent::type_id::create("o_agt", this);
i_agt.is_active = UVM_ACTIVE;
o_agt.is_active = UVM_PASSIVE;
endfunction
`uvm_component_utils(my_env)
endclass
//my_model
class my_model extends uvm_component;
uvm_blocking_get_port #(my_transaction) port;
uvm_analysis_port #(my_transaction) ap;
extern function new(string name, uvm_component parent);
extern function void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);
`uvm_component_utils(my_model)
endclass
function my_model::new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void my_model::build_phase(uvm_phase phase);
super.build_phase(phase);
port = new("port", this);
ap = new("ap", this);
endfunction
//单纯地复制一份从i_agt得到的tr,并传递给后级的scoreboard中
task my_model::main_phase(uvm_phase phase);
my_transaction tr;
my_transaction new_tr;
super.main_phase(phase);
while(1) begin
port.get(tr);
new_tr = new("new_tr");
new_tr.my_copy(tr); //my_copy是一个在my_transaction中定义的函数
`uvm_info("my_model", "get one transaction, copy and print it:", UVM_LOW)
new_tr.my_print();
ap.write(new_tr);
end
endtask
//my_monitor.sv
uvm_analysis_port #(my_transaction) ap;
//uvm_analysis_port是一个参数化的类,其参数就是这个analysis_port需要传递的数据的类型,在本节中是my_transaction
virtual function void build_phase(uvm_phase phase);
ap = new("ap", this);
endfunction
task my_monitor::main_phase(uvm_phase phase);
my_transaction tr;
while(1) begin
tr = new("tr");
collect_one_pkt(tr);
ap.write(tr);
end
endtask
function void my_env::connect_phase(uvm_phase phase);
super.connect_phase(phase);
i_agt.ap.connect(agt_mdl_fifo.analysis_export);
mdl.port.connect(agt_mdl_fifo.blocking_get_export);
endfunction
class my_scoreboard extends uvm_scoreboard;
my_transaction expect_queue[$];
//通过exp_port获取来源于reference model较的数据
uvm_blocking_get_port #(my_transaction) exp_port;
//通过act_port获取来源于o_agt的monitor的数据
uvm_blocking_get_port #(my_transaction) act_port;
`uvm_component_utils(my_scoreboard)
extern function new(string name, uvm_component parent = null);
extern virtual function void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);
endclass
function my_scoreboard::new(string name, uvm_component parent = null);
super.new(name, parent);
endfunction
function void my_scoreboard::build_phase(uvm_phase phase);
super.build_phase(phase);
exp_port = new("exp_port", this);
act_port = new("act_port", this);
endfunction
task my_scoreboard::main_phase(uvm_phase phase);
my_transaction get_expect, get_actual, tmp_tran;
bit result;
super.main_phase(phase);
fork
while (1) begin
exp_port.get(get_expect);
expect_queue.push_back(get_expect);
end
while (1) begin
act_port.get(get_actual);
if(expect_queue.size() > 0) begin
tmp_tran = expect_queue.pop_front();
result = get_actual.my_compare(tmp_tran);
if(result) begin
`uvm_info("my_scoreboard", "Compare SUCCESSFULLY", UVM_LOW);
end
else begin
`uvm_error("my_scoreboard", "Compare FAILED");
$display("the expect pkt is");
tmp_tran.my_print();
$display("the actual pkt is");
get_actual.my_print();
end
end
else begin
`uvm_error("my_scoreboard", "Received from DUT, while Expect Que ue is empty");
$display("the unexpected pkt is");
get_actual.my_print();
end
end
join
endtask
function bit my_compare(my_transaction tr);
bit result;
if(tr == null)
`uvm_fatal("my_transaction", "tr is null!!!!")
result = ((dmac == tr.dmac) &&
(smac == tr.smac) &&
(ether_type == tr.ether_type) &&
(crc == tr.crc));
if(pload.size() != tr.pload.size())
result = 0;
else
for(int i = 0; i < pload.size(); i++) begin
if(pload[i] != tr.pload[i])
result = 0;
end
return result;
endfunction
my_scoreboard要比较的数据一是来源于reference model,二是来源于o_agt的monitor。前者通过exp_port获取,而后者通过act_port获取。在main_phase中通过fork建立起了两个进程,一个进程处理exp_port的数据,当收到数据后,把数据放入expect_queue中;
另外一个进程处理act_port的数据,这是DUT的输出数据,当收集到这些数据后,从expect_queue中弹出之前从exp_port收到的数据,并调用my_transaction的my_compare函数。
class my_transaction extends uvm_sequence_item;
rand bit[47:0] dmac; //48bit的以太网目的地址
rand bit[47:0] smac; //smac是48bit的以太网源地址
rand bit[15:0] ether_type; //ether_type是以太网类型
rand byte pload[]; //pload是其携带数据的大小
rand bit[31:0] crc; //CRC是前面所有数据的校验值
constraint pload_cons{ //大小被限制在46~1500byte
pload.size >= 46;
pload.size <= 1500;
}
function bit[31:0] calc_crc();
return 32'h0;
endfunction
function void post_randomize(); //post_randomize是SystemVerilog中提供的一个函数
crc = calc_crc;
endfunction
`uvm_object_utils_begin(my_transaction)
`uvm_field_int(dmac, UVM_ALL_ON)
`uvm_field_int(smac, UVM_ALL_ON)
`uvm_field_int(ether_type, UVM_ALL_ON)
`uvm_field_array_int(pload, UVM_ALL_ON)
`uvm_field_int(crc, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "my_transaction");
super.new(name);
endfunction
function void my_print();
$display("dmac = %0h", dmac);
$display("smac = %0h", smac);
$display("ether_type = %0h", ether_type);
for(int i = 0; i < pload.size; i++) begin
$display("pload[%0d] = %0h", i, pload[i]);
end
$display("crc = %0h", crc);
endfunction
endclass
//引入field_automation机制后的简化
task my_driver::drive_one_pkt(my_transaction tr);
byte unsigned data_q[];
int data_size;
data_size = tr.pack_bytes(data_q) / 8;
`uvm_info("my_driver", "begin to drive one pkt", UVM_LOW);
repeat(3) @(posedge vif.clk);
for ( int i = 0; i < data_size; i++ ) begin
@(posedge vif.clk);
vif.valid <= 1'b1;
vif.data <= data_q[i];
end
@(posedge vif.clk);
vif.valid <= 1'b0;
`uvm_info("my_driver", "end drive one pkt", UVM_LOW);
endtask