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,systemverilog中有些语法不太熟悉, 为什么Factory::use_err这样写?::是什么时候使用,Factory::create_driver;说明一下哈,可以举一些例子强化一下这个薄弱项吗,老师,感谢您啦,
SystemVerilog 中的 :: 作用域解析运算符详解
一、:: 是什么?
:: 叫做 作用域解析运算符(scope resolution operator) 。它的作用是:告诉编译器"我要访问的是某个类/包/接口里面的东西",而不是当前作用域里的东西。
你可以把它理解成 C++ 里的 ::,或者 Java 里的 类名.静态成员,或者 Python 里的 类名.类属性。
格式:
text
Copy
Download
类名::成员名
包名::成员名
二、为什么 Factory::use_err 要这样写?
看你的代码:
systemverilog
Copy
Download
class Factory;
static bit use_err = 0; // 静态变量
static function Component create_driver(...); // 静态方法
if (use_err) ...
endfunction
endclass
注意两个关键字 static:
-
static bit use_err------ 这是类级别的变量,不属于任何一个对象,整个类只有一份。 -
static function create_driver------ 这是类级别的方法,不需要 new 出对象就能调用。
对于 static 成员,你不需要、也不应该通过对象去访问 ,而是通过类名去访问。怎么把"类名"和"成员"连起来?就用 :::
systemverilog
Copy
Download
Factory::use_err = 0; // 直接改类里的静态变量
drv = Factory::create_driver(...); // 直接调类里的静态方法
对比一下 :如果是非静态成员,你就得先 new 一个对象,用 . 访问:
systemverilog
Copy
Download
Factory f = new();
f.use_err = 0; // 非静态才这么写
f.create_driver(...); // 非静态才这么写
所以简单记:
-
.→ 通过对象访问(实例成员) -
::→ 通过类/包名访问(静态成员、类型、参数等)
三、:: 的常见使用场景(举例强化)
场景 1:访问静态变量 / 静态方法
systemverilog
Copy
Download
class Counter;
static int count = 0;
static function void inc();
count++;
endfunction
endclass
module tb;
initial begin
Counter::inc(); // 不需要 new
Counter::inc();
$display("count = %0d", Counter::count); // 输出 2
end
endmodule
这跟你的 Factory 例子一模一样。
场景 2:访问类里的 typedef / enum / parameter
类里面定义的类型、枚举、参数 ,也要用 :: 访问:
systemverilog
Copy
Download
class Packet;
typedef enum {READ, WRITE} cmd_e; // 类内定义的类型
parameter int WIDTH = 8; // 类内参数
endclass
module tb;
initial begin
Packet::cmd_e c; // 用 :: 引用类型
c = Packet::WRITE; // 用 :: 引用枚举值
$display("WIDTH = %0d", Packet::WIDTH);
end
endmodule
这在 UVM 里非常常见,比如:
systemverilog
Copy
Download
uvm_phase::uvm_phase_type
uvm_object::type_id
场景 3:访问包(package)里的内容
:: 也用于包 。包里的类、函数、变量都可以用 包名:: 访问:
systemverilog
Copy
Download
package my_pkg;
int g_var = 100;
function int add(int a, int b);
return a + b;
endfunction
class MyClass;
int x;
endclass
endpackage
module tb;
import my_pkg::*; // 通配导入
initial begin
my_pkg::MyClass obj; // 也可以显式用 :: 引用
$display("%0d", my_pkg::g_var);
$display("%0d", my_pkg::add(3, 4));
end
endmodule
UVM 里到处都是这种:
systemverilog
Copy
Download
uvm_pkg::uvm_component
uvm_macros::uvm_info
场景 4:UVM 里最经典的 type_id::create
你以后写 UVM 一定会见到:
systemverilog
Copy
Download
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver) // 这行宏会生成 type_id 这个 typedef
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
endclass
// 在别的地方创建:
my_driver drv;
drv = my_driver::type_id::create("drv", this);
// ^^^^^^^^^^^^^^^^^^^^^^^^
// 类名::type_id(类内类型)::create(静态方法)
这里连着用了两次 :::
-
my_driver::type_id------ 访问 my_driver 类里的type_id这个 typedef -
type_id::create------ 调用 type_id 这个类里的静态方法 create
这就是 UVM factory 机制的核心写法 ,理解了 :: 你就能看懂它了。
场景 5:访问父类被覆盖的成员(super 场景的补充)
有时候子类想调用父类的静态方法:
systemverilog
Copy
Download
class Base;
static function void hello();
$display("Base hello");
endfunction
endclass
class Child extends Base;
static function void hello();
Base::hello(); // 显式调用父类的静态方法
$display("Child hello");
endfunction
endclass
四、总结对照表
| 写法 | 含义 | 什么时候用 |
|---|---|---|
obj.member |
通过对象访问 | 实例变量、实例方法 |
Class::member |
通过类名访问 | 静态变量、静态方法、typedef、enum、parameter |
pkg::member |
通过包名访问 | 包里的类、函数、变量 |
Class::type_id::create(...) |
链式访问 | UVM factory 创建对象 |
一句话记忆:
左边是对象 就用
.,左边是类型/类/包的名字 就用::。
五、回到你的代码
systemverilog
Copy
Download
Factory::use_err = 0; // use_err 是 static,用类名 + ::
drv = Factory::create_driver("drv", env); // create_driver 是 static,用类名 + ::
如果 use_err 和 create_driver 前面没有 static,那你就必须先 Factory f = new();,然后 f.use_err = 0;、f.create_driver(...)。
正因为它们是 static(属于类本身,不属于某个对象),所以 SV 要求你用 Factory:: 来访问------这就是 :: 出现的根本原因。
希望这个解释帮你把这块补上了,UVM 里 :: 会天天见到,多看几次就习惯了 👍