使用SystemVerilog搭建环境的时候,经常会遇到需要对一个对象进行复制,以防止对象的方法修改原始对象的值。对于初学者经常会使用new函数来实现class的内建的复制功能,但是,当要复制的对象中含有其他对象(object)时,使用内建的复制功能将会得到不期望的结果。本文将示例说明shallow copy和deep copy的区别。
1 浅复制(shallow copy)
在SystemVerilog中,当一个class声明时会指定一个句柄handle(可以理解为其他语言中的指针),该句柄handle指向一块存储空间,该存储空间后续只能存放该class数据类型的数据。在没有对其进行任何操作之前,该handle相当于没有指向任何实际的数据对象,只有在使用了new函数之后,才会为其开辟具体的存储对应class类型数据的存储空间。
【示例】
示例中构造函数new()被调用后,就会创建一个transaction的实例,这时就会为其开辟一块存储transaction数据类型的空间,声明的句柄tr就指向该空间的起始地址。
那么如果又声明了一个transaction的句柄tr1,将旧句柄赋值给该变量或者使用new将旧句柄拷贝给新的句柄,即tr1 =tr(或tr1 = new tr;),此时会将tr中的所有变量复制给tr1,但是tr中的new函数都不会被调用。
【示例】
【仿真结果】
示例中,tr1中的所有属性变量被复制到了tr2中,这种操作方式相当于对tr1进行了简单盲目的复制操作,其执行过程如下图所示:
但是其使用还是存在一定的问题,如果属性成员中存在具有类属性的成员时,复制后操作的结果可能跟期望不一致,下例所示。
【示例】
【仿真结果】
示例中,tr1被复制给tr2,此时第一次打印和第二次打印输出的结果是一样的。再通过tr2修改了被复制的pkt中的属性payload为’hBA,但是在第三次和第四次分别打印输出的结果却不一样,除了tr1的变量属性成员不一样外,pkt.payload一样,都是修改后的’hBA。这是因为虽然tr1被复制给了tr2,此时tr1中的句柄pkt也被复制给了tr2,而句柄本身可以理解为指针,那么相当于tr2和tr1使用了相同的指针句柄,此时如果对tr1或者tr2中的句柄指向的内容进行了修改,相当于对于tr1和tr2中pkt句柄共同指向的空间进行了修改,所以此时虽然仅仅修改了tr2,但是tr1中pkt指向的空间的内容也被其同时修改了。而且可以通过实际仿真结果可以看出,tr中new函数中的“pkt = new()”在复制时并没有被执行,因为如果该new函数被执行的话,那么tr1被复制后,tr2中的pkt和tr1中的pkt是会不一样的,即tr1和tr2中的pkt会指向不同的空间。其执行过程如下图示意所示:
因此可以得到下面的结果:
浅复制会将所有的变量包括integers,strings,intance handle(指针)等等被复制过去。
对一个对象进行复制,复制过程不会调用构造函数,所以也不会使用构造函数对变量的初始化。
可见,浅复制是将原始对象中的所有属性拷贝到新对象中去,将句柄属性复制到新对象中去,不把“句柄指向的对象”复制进去,所以原始对象和新对象引用同一对象,新对象中的句柄指向的内容发生变化会导致原始对象中的对应内容也发生变化。因此,为了实现复制后的两个对象互相不影响,就不能使用class默认内建复制操作(浅复制),此时就需要采用深复制。
2 深复制(deep copy)
为了避免产生浅复制后的藕断丝连,我们需要对原有的class进行适当的修改完善,实现对象的深复制。深复制相较浅复制是在引用方面不同,深复制就是创建一个新的和原始句柄指向的内容相同的字段,是两个一样大的数据段,所以两者的句柄指向的空间是不同的,但内容是相通的,之后的新对象中句柄指向的内容发生改变,不会引起原始对象中句柄指向内容也发生改变。
【示例】
【仿真结果】
示例中,tr1通过transaction的copy函数被复制给tr2,此时第一次打印和第二次打印输出的结果是一样的。再通过tr2修改了被复制的pkt中的属性payload为’hBA,在第三次和第四次分别打印输出的结果不一样,除了tr1的变量属性成员不一样外,pkt.payload也不一样,tr2此中的pkt指向的payload值为’hBA,而tr1中的pkt只想的payload的值为’hAB,即此时tr1和tr2中的pkt指向的payload值已经是不一样的了。这是因为虽然tr1使用transaction的copy函数被复制给了tr2时调用了packet的copy函数,即此时将tr1中的pkt指向的对象复制了一份给tr2,此时tr1和tr2中的pkt指向的对象不再是同一个对象了。因此tr1和tr2对pkt进行修改复制就不会相互影响了。其执行过程如下图示意所示:
因此,在SystemVerilog中,如果要为所使用的类使用deep copy时,就需要为所有的相关class定义好copy函数,为了避免一些因为coding导致的错误,在UVM中已经集成了copy和clone操作,用户在使用时已经完全不需要在单独额外的编写copy函数,尽管调用即可。
下图个人技术微信公众号,欢迎朋友们关注沟通!
本文纯属学习之用,欢迎指正文中不足,
封面图片若有侵权,请及时沟通!