训练一:最简单的class(热身)
题目
定义一个类 Counter,要求:
-
有一个成员变量
int count。 -
构造函数
new()把count初始化为0。 -
有一个方法
increment(),每次调用让count加1。 -
有一个方法
show(),打印当前count值。
在 initial 里创建对象,调用 increment() 3次,然后 show(),期望输出 count=3。
你自己写(先别看下面)
systemverilog
Copy
Download
// 在这里写你的代码
参考答案
systemverilog
Copy
Download
class Counter;
int count;
function new();
count = 0;
endfunction
function void increment();
count = count + 1;
endfunction
function void show();
$display("count=%0d", count);
endfunction
endclass
module tb;
initial begin
Counter c;
c = new(); // 必须new!
c.increment();
c.increment();
c.increment();
c.show(); // 输出 count=3
end
endmodule
老师点评
-
这是最基础的封装:数据(count) + 操作(increment/show) 打包在一起。
-
UVM对应 :UVM里所有组件都是这样,比如
uvm_driver里有seq_item_port(数据)和run_phase(操作)。 -
小白坑 :
Counter c;后忘了c = new();,仿真直接报null。
训练二:带参数的构造函数
题目
定义一个类 Student,要求:
-
成员变量
string name和int age。 -
构造函数
new(string n, int a),把name和age赋值。 -
方法
show()打印name is X years old。
创建两个对象:("Tom", 20) 和 ("Jerry", 22),分别show。
参考答案
systemverilog
Copy
Download
class Student;
string name;
int age;
function new(string n, int a);
name = n;
age = a;
endfunction
function void show();
$display("%s is %0d years old", name, age);
endfunction
endclass
module tb;
initial begin
Student s1 = new("Tom", 20);
Student s2 = new("Jerry", 22);
s1.show();
s2.show();
end
endmodule
老师点评
-
构造函数可以带参数,也可以重载(不同参数列表)。
-
UVM对应 :
uvm_component的构造函数固定是new(string name, uvm_component parent),你必须写这两个参数,UVM框架自动传入。 -
记住这个格式,写UVM组件时无脑套:
systemverilog
Copy
Download
function new(string name = "my_comp", uvm_component parent = null); super.new(name, parent); endfunction
训练三:继承入门(extends + super)
题目
-
定义父类
Animal:-
成员
string name。 -
构造函数
new(string n),赋值name。 -
方法
speak(),打印Animal X makes a sound。
-
-
定义子类
Dog extends Animal:-
构造函数
new(string n),调用父类构造函数。 -
重写
speak(),打印Dog X says Woof。
-
-
定义子类
Cat extends Animal:-
构造函数
new(string n)。 -
重写
speak(),打印Cat X says Meow。
-
创建 Dog("WangCai") 和 Cat("Mimi"),分别speak。
参考答案
systemverilog
Copy
Download
class Animal;
string name;
function new(string n);
name = n;
endfunction
virtual function void speak();
$display("Animal %s makes a sound", name);
endfunction
endclass
class Dog extends Animal;
function new(string n);
super.new(n); // 调用父类构造函数
endfunction
virtual function void speak();
$display("Dog %s says Woof", name);
endfunction
endclass
class Cat extends Animal;
function new(string n);
super.new(n);
endfunction
virtual function void speak();
$display("Cat %s says Meow", name);
endfunction
endclass
module tb;
initial begin
Dog d = new("WangCai");
Cat c = new("Mimi");
d.speak(); // Dog WangCai says Woof
c.speak(); // Cat Mimi says Meow
end
endmodule
老师点评
-
三要素 :
extends(继承)、super.new()(调父类构造)、virtual(可重写)。 -
UVM对应 :这就是UVM工厂机制的本质!
uvm_driver extends uvm_component,my_driver extends uvm_driver,层层继承。 -
小白坑1 :忘了
super.new(n),父类的name不会被初始化。 -
小白坑2 :
virtual忘了加,子类的speak不会生效(后面训练五会演示)。
训练四:多态(父类句柄指向子类对象)
题目
用训练三的Animal/Dog/Cat,写下面的代码,先猜输出,再跑:
systemverilog
Copy
Download
module tb;
initial begin
Animal a;
Dog d = new("WangCai");
Cat c = new("Mimi");
a = d;
a.speak(); // 输出??
a = c;
a.speak(); // 输出??
end
endmodule
答案
text
Copy
Download
Dog WangCai says Woof
Cat Mimi says Meow
老师点评
-
这就是多态 :父类句柄
a运行时才知道指向Dog还是Cat,调用的是子类重写的方法。 -
前提 :父类方法必须是
virtual,否则调用的是父类的speak。 -
UVM对应 :工厂覆盖(factory override)就靠多态。你把
my_driver换成err_driver,UVM用父类句柄创建,实际得到err_driver的行为。 -
这就是UVM最核心的魔法! 一定要理解。
训练五:故意不加virtual,看会发生什么(反例)
题目
把训练三的Animal.speak()的virtual去掉,其他不变,跑训练四的代码,看输出。
答案
text
Copy
Download
Animal WangCai makes a sound
Animal Mimi makes a sound
老师点评
-
没加virtual,多态失效! 父类句柄调用的是父类方法。
-
这就是为什么UVM里所有方法几乎都带virtual。
-
死记:要被子类重写的方法,必须加virtual。
训练六:子类扩展父类(加新成员)
题目
-
父类
Shape:-
成员
string color。 -
构造函数
new(string c)。 -
方法
area()返回real,打印"Shape area unknown",返回0。
-
-
子类
Rectangle extends Shape:-
新增成员
real width, height。 -
构造函数
new(string c, real w, real h),先调super.new(c),再赋值width/height。 -
重写
area(),返回width * height。
-
-
子类
Circle extends Shape:-
新增成员
real radius。 -
构造函数
new(string c, real r)。 -
重写
area(),返回3.14159 * radius * radius。
-
创建 Rectangle("red", 3.0, 4.0) 和 Circle("blue", 2.0),打印各自area,期望12.0和12.566。
参考答案
systemverilog
Copy
Download
class Shape;
string color;
function new(string c);
color = c;
endfunction
virtual function real area();
$display("Shape area unknown");
return 0;
endfunction
endclass
class Rectangle extends Shape;
real width, height;
function new(string c, real w, real h);
super.new(c);
width = w;
height = h;
endfunction
virtual function real area();
return width * height;
endfunction
endclass
class Circle extends Shape;
real radius;
function new(string c, real r);
super.new(c);
radius = r;
endfunction
virtual function real area();
return 3.14159 * radius * radius;
endfunction
endclass
module tb;
initial begin
Rectangle r = new("red", 3.0, 4.0);
Circle c = new("blue", 2.0);
$display("Rectangle area = %0.2f", r.area()); // 12.00
$display("Circle area = %0.2f", c.area()); // 12.57
end
endmodule
老师点评
-
子类可以在父类基础上加新成员 (width/height/radius),这叫扩展。
-
UVM对应 :
my_transaction extends uvm_sequence_item,你会加rand字段;my_driver extends uvm_driver #(my_transaction),你会加virtual interface。 -
记住顺序:先super.new(),再初始化自己的成员。
训练七(综合大作业):模拟UVM的component树
题目
模仿UVM的层次结构,写三个类:
-
Component(基类):-
成员
string name,Component parent。 -
构造函数
new(string n, Component p)。 -
virtual function void show():打印自己的name。
-
-
Driver extends Component:-
构造函数
new(string n, Component p),调super。 -
重写
show():打印Driver: X。
-
-
Monitor extends Component:-
构造函数同上。
-
重写
show():打印Monitor: X。
-
在tb里:
systemverilog
Copy
Download
Component env = new("env", null);
Driver drv = new("drv", env);
Monitor mon = new("mon", env);
drv.show();
mon.show();
期望输出:
text
Copy
Download
Driver: drv
Monitor: mon
参考答案
systemverilog
Copy
Download
class Component;
string name;
Component parent;
function new(string n, Component p);
name = n;
parent = p;
endfunction
virtual function void show();
$display("Component: %s", name);
endfunction
endclass
class Driver extends Component;
function new(string n, Component p);
super.new(n, p);
endfunction
virtual function void show();
$display("Driver: %s", name);
endfunction
endclass
class Monitor extends Component;
function new(string n, Component p);
super.new(n, p);
endfunction
virtual function void show();
$display("Monitor: %s", name);
endfunction
endclass
module tb;
initial begin
Component env = new("env", null);
Driver drv = new("drv", env);
Monitor mon = new("mon", env);
drv.show();
mon.show();
end
endmodule
老师点评
-
这个结构就是UVM的component树 !
env是父,drv/mon是子。 -
UVM里
new(string name, uvm_component parent)的两个参数,正是来自这里。 -
一旦你理解这个,
uvm_component的构造函数、build_phase的层次调用,全都通了。
训练八(进阶):理解UVM工厂的本质
题目
不改tb代码,只改一处,让 Driver 被创建时实际得到 ErrDriver。
提示:用父类句柄 + 工厂函数 。写一个函数 Component create_driver(string n, Component p),在函数里根据某个标志返回Driver或ErrDriver。
systemverilog
Copy
Download
class ErrDriver extends Component;
function new(string n, Component p);
super.new(n, p);
endfunction
virtual function void show();
$display("ErrDriver: %s", name);
endfunction
endclass
参考答案
systemverilog
Copy
Download
class Factory;
static bit use_err = 0;
static function Component create_driver(string n, Component p);
if (use_err)
return new ErrDriver(n, p);
else
return new Driver(n, p);
endfunction
endclass
module tb;
initial begin
Component env = new("env", null);
Component drv;
Factory::use_err = 0;
drv = Factory::create_driver("drv", env);
drv.show(); // Driver: drv
Factory::use_err = 1;
drv = Factory::create_driver("drv", env);
drv.show(); // ErrDriver: drv
end
endmodule
老师点评
-
这就是UVM工厂机制的简化版 !UVM通过
type_id::create()和set_type_override实现同样的事。 -
关键点:
-
static函数/变量属于类本身,不用new就能用。 -
返回值类型是父类
Component,实际返回子类对象(多态)。 -
用标志决定创建哪个子类。
-
-
学到这里,你就懂了UVM工厂的灵魂。
训练总结与自测表
做完上面8题,用下表自测,全部打勾才算过关:
| 技能 | 会 | 不会 |
|---|---|---|
| 定义class、成员变量、方法 | ☐ | ☐ |
| 用new创建对象 | ☐ | ☐ |
| 带参数的构造函数 | ☐ | ☐ |
| extends继承 | ☐ | ☐ |
| super.new()调父类构造 | ☐ | ☐ |
| virtual方法重写 | ☐ | ☐ |
| 父类句柄指向子类对象(多态) | ☐ | ☐ |
| 子类新增成员 | ☐ | ☐ |
| 理解工厂本质 | ☐ | ☐ |