5

我主要有 C++ 背景。我正在追踪我正在处理的一些 SystemVerilog 代码中的一个错误,并惊讶地发现我认为是对象复制分配实际上是一个引用分配。这个简化的代码说明了我的意思:

for (int i = 0; i < max_num; ++i)
{
    var cls_obj obj1;
    obj1 = obj_array[i];

    some_function(obj1); // modifies the object passed in

    // at this point BOTH obj1 and obj_array[i] are modified.

    // some other code goes here
}

我期待的只是obj1被修改。这是因为 var 关键字吗?复制分配与引用分配在 SystemVerilog 中究竟是如何工作的?我很难从网络搜索中找到信息。

4

2 回答 2

6

SystemVerilog 中的类变量是引用或句柄。new只有在您使用关键字时才会创建实例。

因此,在您的示例中,obj1两者obj_array[i]都引用(或指向)同一个实例。

默认情况下,SystemVerilog 中的函数参数是按值传递的。但是,类句柄被视为值,因此您传递给函数的任何类都有效地通过引用传递。

在初始化类对象时,语言中有一种内置机制可以进行浅拷贝。

Packet p1;
Packet p2;
p1 = new;
p2 = new p1;

这是一个浅拷贝。对于对象,仅复制句柄!

IEEE 1800-2009 第 8.11 章中的示例对此进行了解释。

关键字与您看到的var行为没有任何关系。事实上,我什至从未见过或使用过var. 根据 LRM,这允许在声明变量时省略类型。在您的代码中,指定了类型 (cls_obj),因此我认为它的存在不会做任何事情。

于 2013-02-20T21:34:43.957 回答
0

当我学习 C++ 和 SV 时,我也注意到了这个问题。实际上,所有 sv 属性都在 LRM 中进行了详细描述(最新版本是 IEEE-1800-2017)。这个问题在第 8.2 章和第 13.5 章中有说明:

在第 13.5 章中:关于子程序

SystemVerilog 提供了两种将参数传递给任务和函数的方法:通过值和通过引用。参数也可以通过名称和位置来绑定。子程序参数也可以被赋予默认值,允许对子程序的调用不传递参数

总结:
子例程(包括函数和任务)参数默认按值传递,只有在添加 ref 关键字时才通过引用传递。

第8.2章:关于类和对象

面向对象的类扩展允许动态创建和销毁对象。类实例或对象可以通过对象句柄传递,这提供了安全指针功能。可以将对象声明为具有方向输入、输出、输入输出或参考的参数。在每种情况下,复制的参数都是对象句柄,而不是对象的内容。

总而言之:
对象是类的实例,对象的名称是它的引用或句柄,而不是它本身,这类似于 c++ 中的指针(通过 type *ptrname 声明)或引用(delcare by: type &refname)。对象,你操纵它的句柄。因此,当你将对象传递给没有 ref 关键字 althrough 的子程序时,它会通过值传递,但值(对象名称)是对象的句柄,而不是对象本身。因此,类是通过引用传递的您是否添加 ref 关键字。

此外,这就是为什么对于 SV 中的新人来说,流动时会出现一个非常常见的错误(来自SystemVerilog for Verification, Second Edition: A Guide to Learning the Testbench Language FeaturesJune 2008):

> task generate_transaction()
Transaction t;
mailbox mbx;
**t = new(); // wrong place**
repeat(10) begin
    assert(t.randomize());
    mbx.put(t);
end

错误是:只创建一个类 Transaction 的实例,因此邮箱 mbx 中的 10 个对象指向单个相同的实例。正确的方法是在需要对象时创建许多对象,换句话说,将“t = new()”放在循环块中。

> task generate_transaction()
Transaction t;
mailbox mbx;
repeat(10) begin
    **t = new(); // right place**
    assert(t.randomize());
    mbx.put(t);
end
于 2021-02-01T12:55:56.790 回答