[UVM源代码研究] 谈谈uvm中的浅拷贝(shallow copy)与深拷贝(deep copy)

[UVM源代码研究] 谈谈uvm中的浅拷贝(shallow copy)与深拷贝(deep copy)

在开启这个话题前,我们有必要对copy相关的几个基本概念做个简单的介绍。

浅拷贝(shallow)深拷贝(deep copy)
这是面对对象语言都会涉及的一个基本概念,不仅限于sv和uvm

浅拷贝:拷贝就是拷贝指向对象的指针,意思就是说:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间,浅拷贝只是一种简单的拷贝,让几个对象共用一个内存,然而当内存销毁的时候,指向这个内存空间的所有指针需要重新定义,不然会造成野指针错误。

深拷贝:深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响,即指针指向的空间内容也会被复制一份。比较典型的就是Value(值)对象,如预定义类型Int32,Double,以及结构(struct),枚举(Enum)等会自动执行深拷贝,而类类型的对象深拷贝和浅拷贝则不同。

UVM中的的copy和clone

我们先来看下uvm_object中关于copy和clone的相关的定义

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们的出如下结论:

  1. copy是个非virtual的没有返回值的函数,所以不能被override,但是copy中调用do_copy函数是个没有返回值的virtual函数,所以我们可以通过override do_copy函数来实现对copy函数的间接override。

  2. 调用copy前对象一定要事先创建好目标对象,因为调用copy只会对目标对象内部定义的对象使用源对象内部的对象进行深拷贝赋值,而不会对目标对象本身分配空间。

  3. clone是个virtual返回值是uvm_object的函数,可以被override,但是返回值是uvm_object类型决定了clone只会需要 c a s t 给对应的实际类型,而不能直接赋值。 c l o n e 返回一个指向源对象类型的 u v m o b j e c t 类型的句柄,所以要保证源对象与目标对象类型一致(通过 cast给对应的实际类型,而不能直接赋值。clone返回一个指向源对象类型的uvm_object类型的句柄,所以要保证源对象与目标对象类型一致(通过 cast给对应的实际类型,而不能直接赋值。clone返回一个指向源对象类型的uvmobject类型的句柄,所以要保证源对象与目标对象类型一致(通过cast来check)才能完成clone行为成功,所以clone的目标对象不需要实现分配好空间(即使事先分配了,该目标对象的句柄也会指向clone新分配的空间,进而原创建的空间会被垃圾回收机制回收),clone=create+copy

copy的函数实现里除了do_copy之外第1035行的__m_uvm_field_automation(rhs, UVM_COPY, “”)完成了我们在field_automation里的相关配置的实现,如果我们没有override do_copy这个函数,那么所有的copy行为都靠这个__m_uvm_field_automation函数来完成,下面我们看看这个函数的定义
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

所以只有我们用了uvm_field_automation注册才会重写这个函数,否则用的就是uvm_object里定义的空函数,即如果没有用field_automation注册字段并且没有重写do_copy函数,那么该字段就不会在copy的时候被copy。

在uvm_object_defines.svh的171行完成了将copy传入参数cast成局部变量local_data__,该变量的类型是通过uvm_object_untils_begin(T)传入的参数类型,即调用copy的对象类型。该local_data__在后续具体的uvm_field_automation的宏内根据传入的标志位不同进行对应的操作,以uvm_field_object为例
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里的what__就是__m_uvm_field_automation()函数传入的第二个参数,ARG对应着复制对象内的相应的需要操作的子对象,FLAG为我们在field_automation传入的标志位结果,关于标志位的相关定义如下
在这里插入图片描述

回到UVM源代码中,我们在field_automation时是可以指定包含对象的拷贝类型的(202-204行),但我们惊人的发现这几行已经被注释掉了!!!那么原先这个功能如何实现的呢?我们搜索下发现同一个文件中这个parameter定义在了下面的枚举类型中,可以看出来这个枚举类型里的对应枚举值的大小与上面注释掉的parameter里的值是一样的,并且这是一个全局可见的枚举类型的定义,因而我们依然可以像使用UVM_PRINT等parameter一样在field_automation里面使用uvm_recursion_policy_enum里的几个枚举变量,UVM_DEFAULT_POLICY就是对uvm_recursion_policy使用default配置(0按位或上任何数等于其本身)。
在这里插入图片描述

该枚举类型中定义了四种枚举值,从上面的描述我们可以看出来UVM_DEEP和UVM_SHALLOW分别对应我们前面讲过的深拷贝和浅拷贝,那么UVM_DEFAULT_POLICY(默认配置)和UVM_REFERENCE分别对应什么样的行为呢?UVM_REFERENCE又有什么样的使用场景呢?

我们继续分析`uvm_field_object中关于UVM_COPY的具体操作
在这里插入图片描述

714行表明调用copy的源对象不能为空。

715行的FLAG&UVM_NOCOPY就是看FLAG的bit 1上是否为1,即看FLAG有没有设置UVM_NOCOPY,有设置则直接结束这段代码(else什么都没干),没有继续往下走。

716行就涉及到了UVM_REFERENCE的具体用法了,FLAG&UVM_REFERENCE查看是否设置UVM_REFERENCE标志位,local_data__.ARG == null查看ARG对象是否未被分配空间,也就是说只要设置了UVM_REFERENCE标志位或者ARG对象没有被分配空间,就将目标对象的ARG对象句柄指向源对象的ARG句柄(且这个句柄未指向任何空间)。是不是很奇怪,一个句柄未被分配空间却赋值给了另一个句柄?之所以有这么个应用,主要是因为copy在使用的时候一个限制,关于这一点我们看下源代码中关于UVM_REFERENCE的描述就明白了
在这里插入图片描述

也就是说当uvm_object/uvm_component中中包含了uvm_component类型的对象时,如果使用了`uvm_filed_object宏注册该对象,由于uvm中copy默认做的都是深拷贝(uvm_object_defines.svh的720行),那么该uvm_component对象在copy的时候就会被深拷贝一份新的内容,这个在uvm中是不允许的,因为我们将uvm树形结构时讲到了每个树型结构上的uvm_component都有唯一的parent,而我们在做copy时是无法指定uvm_component的parent参数的,因而uvm_component类型是不支持深拷贝的,所以在深拷贝和浅拷贝之外我们又增加了一个参数UVM_REFERENCE专门针对uvm_component类型的对象的注册,注册这种对象类型我们必须申明UVM_REFERENCE保证在进行copy和clone的时候强制对其中的uvm_component类型做了个浅拷贝。当然UVM_REFERENCE也不一定非要用在uvm_component对象上,任何对象都可以添加UVM_REFERENCE标志位强制使用浅拷贝。我们看下下面这个例子(apb_env中例化的bus_monitor和bus_collector分别赋值给了master里的monitor和collector,apb_env中例化的cfg赋值给了master里的cfg)
在这里插入图片描述
在这里插入图片描述

我们在agent下面将monitor等相关字段进行了注册,其中cfg是又上一层的env传递过来的(直接赋值传递或者config_db的set/get,指向同一块空间,并不是用的copy,所以不存在深浅拷贝的讨论),我们执行print_topology时打印结果如下
在这里插入图片描述
在这里插入图片描述

uvm_top在执行print_topology时会打印树型结构上的uvm_component信息,额外需要添加打印的内容可以通过field_automation或者重写do_copy函数进行添加。

从打印信息我们可以看到红色框对应同一个apb_monitor内存空间,但是由于加了UVM_REFERENCE所以master中的mon只打印了一行,即只打印了句柄信息,collector同理,而cfg在env和master里都打印完全了。现在我们对master里的field_automation进行如下修改
在这里插入图片描述

print_topology部分信息如下
在这里插入图片描述

我们得出如下结论:

  1. uvm_component类型的对象用uvm_field_object()进行注册时如果不加UVM_REFERENCE不会报错,但是在print打印时会打印完整的内部信息,但是如果加了UVM_REFERENCE只会打印一行句柄信息,并不会将其print的其他信息打印出来。但是在执行copy的时候如果内部的uvm_component对象用uvm_field_object()进行注册时如果不加UVM_REFERENCE就会出错,如果从来不会进行copy/clone操作则没有影响。

  2. 非uvm_component类型的对象用`uvm_field_object()进行注册时加不加UVM_REFERENCE我们通过之前对copy函数的分析可以知道会影响到进行copy时做的是浅拷贝(有UVM_REFERENCE标志位)还是深拷贝,并且print的时候浅拷贝的对象只会打印句柄/指针信息,并不会将其print打印的其他信息打印出来。

通过上面的分析我们发现UVM_REFERENCE似乎把UVM_SHALLOW的事都干了,那么UVM_SHALLOW都干嘛了呢?

grep下我们发现UVM_SHALLOW只有在联合数组相关的field_automation相关的定义里才有提到,并且还都是定义在UVM_COMPARE里,所以我们就可以把这个应用忽略不计了,基本不会使用UVM_SHALLOW(因为UVM源代码都不太支持)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

关于UVM中copy都是进行的深拷贝,UVM_REFERENCE标志位可以实现浅拷贝我们再举下面一个例子
在这里插入图片描述

uart_ctrl_config中定义两个对象,在field_automation注册时,一个用UVM_REFERENCE修饰,一个不用,我们执行如下的copy操作
在这里插入图片描述

打印结果如下
在这里插入图片描述

与我们预期的一致,UVM_REFERENCE(apb_cfg)不仅是的print的时候只打印句柄信息,还使得copy的时候做了浅拷贝(红色框中对应同一地址空间),而默认情况下 (uart_cfg)使用的是深拷贝(黄色框中分配不同地址空间)。

我们再用UVM_SHALLOW试试
在这里插入图片描述

打印结果如下
在这里插入图片描述

可以看到即使添加了标志位UVM_SHALLOW实际做的仍然是深拷贝,所以不要被源代码的表象所欺骗了,要想实现浅拷贝要么用UVM_REFERENCE要么重写do_copy函数。

总结

  1. UVM中field_automation实现的默认copy/clone都是深拷贝

  2. uvm_object里的copy函数可以通过do_copy进行override,而clone可以直接override

  3. clone = create + copy

  4. uvm_component类型的对象使用uvm_field_object注册时要加UVM_REFERENCE标志位,否则进行copy时会出错,因为uvm_component由于需要指定parent导致其无法进行clone(深拷贝)

  5. 使用浅拷贝时用UVM_REFERENCE,不要使用UVM_SHALLOW

  • 26
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值