Logo

Object copy in system verilog

09 Sep 2023
5 mins

In this article, we will see how we can copy objects in System Verilog. This could look easy at first glance, as we might think that copying object is same as copying any variable where we assign value of variable to another variable to copy it. But this is not true in case of objects. Let us dig deep down into the concept of object copying and how it is different.

Introduction

Object handles

We have seen to store an object of certain class we need to have object handles. Object handles are nothing but a pointer to the memory address where the object is stored. As object handles are pointer to an address, if we simply assign one object handle to another, another object handle will point to the same address as first handle and thus all the data will be same.

Therefore, simply assigning an object handle to another does not copy the object. Any change in the data from any of the object handle will be visible in another object handle.

Need for Object Copying

In test bench, we create a lot of component and objects. The stimulus to the design is created in terms of transaction in test bench. Transactions are nothing but an object of a transaction class. There are multiple scenarios, were we want a create a copy of transactions to process it, like the transaction captured in monitor needs to be processed in scoreboard.

Thus, copying objects become crucial because if objects are not copied properly original object can get affected leading to various issues.

Types of object copy

A class can have variable as well as object of another class inside its definition. Also, the instanced class can have an object of another class inside its definition. Thus, there are several types of object copy depending upon how much level we want to copy.

Simple copy

This is the most basic type which we have already discussed earlier. In this type of copy we do not actually copy the values of the variables in a new memory location, but we simply point to the original memory address.

Shallow copy

In this type of copy, we only copy the variables present in the object. If there are object handles present in class definition, then it is not copied. The object handle will keep pointing the old memory address.

This is used when we just want the variables to be copied and we do not want to manipulate the data of object present inside class. Shallow copy can be done using new keyword. If we pass the object handle with new keyword it will copy the object.

Ex - obj2 = new obj1;

We must know that copying an object consumes memory as new memory is allocated and then data of objects are copied to the new memory address. Thus, we should avoid unnecessary object copy to save memory as in large test bench this can be a considerable issue.

Deep copy

In this type of copy, we copy all the variables and object handle present inside the object. It can be considered as a recursive copying where all the objects inside class are also copied.

This is used when we want everything inside an object to be copied. New memory is allocated for all the variables and objects and thus can consume more.

Implementation

In system Verilog we do not have an in-built function to copy an object. Thus, we need to make some function in a class which can enable copying the data to another object. Let us see this with an example.

Example

In this example, we have 2 classes pckt and transaction. transaction class has an object of pckt class inside it. Both the classes have a copy function defined to enable deep copy. To shorten the example, three modules are created in same code to demonstrate three type of object copy.

class pckt;
    bit [3:0] f_id;
    function new(bit [4:0] f_id =0);
        this.f_id = f_id;
    endfunction //new()
    function pckt copy;
        pckt cp = new();
        cp.f_id = this.f_id;
        return cp;
    endfunction
endclass //pckt

class transaction;
    int addr;
    bit [7:0] data;
    pckt sub;
    function new(int addr=0, bit [7:0] data=0, bit [3:0] f_id = 0);
        this.addr = addr;
        this.data = data;
        this.sub = new(f_id);
    endfunction //new()

    function transaction copy;
        transaction cp = new();
        cp.addr = this.addr;
        cp.data = this.data;
        cp.sub = this.sub.copy;
        return cp;
    endfunction

    function void disp();
        $display("ADDR: %d, DATA: %h, F_ID: %b", this.addr, this.data, this.sub.f_id);
    endfunction
endclass //transaction

module simple_copy ();
    transaction obj1;
    transaction obj2;
    initial begin
        obj1 = new(23, 8'h23, 4'h5);
        obj2 = new(453, 8'hff, 4'ha);

        $display("\nBoth the pointers are pointing to different object");
        obj1.disp();
        obj2.disp();

        $display("\nObj2 is a simple copy of obj1");
        obj2 = obj1;
        obj1.disp();
        obj2.disp();

        $display("\nObj2 values are changed");
        obj2.addr = 43;
        obj2.data = 8'h00;
        obj2.sub.f_id = 4'hf;
        obj1.disp();
        obj2.disp();

        $display("\nObj1 values are changed");
        obj1.addr = 1000;
        obj1.data = 8'h0f;
        obj1.disp();
        obj2.disp();
    end
endmodule

module shallow_copy ();
    transaction obj1;
    transaction obj2;
    initial begin
        obj1 = new(23, 8'h23, 4'h5);
        obj2 = new(453, 8'hff, 4'ha);

        $display("\nBoth the pointers are pointing to different object");
        obj1.disp();
        obj2.disp();

        $display("\nObj2 is a shallow copy of obj1");
        obj2 = new obj1;
        obj1.disp();
        obj2.disp();

        $display("\nObj2 values are changed");
        obj2.addr = 50;
        obj2.data = 8'h0f;
        obj2.sub.f_id = 4'hf;
        obj1.disp();
        obj2.disp();
    end
endmodule

module deep_copy ();
    transaction obj1;
    transaction obj2;
    initial begin
        obj1 = new(23, 8'h23, 4'h5);
        obj2 = new(453, 8'hff, 4'ha);

        $display("\nBoth the pointers are pointing to different object");
        obj1.disp();
        obj2.disp();

        $display("\nObj2 is a deep copy of obj1");
        obj2 = obj1.copy;
        obj1.disp();
        obj2.disp();

        $display("\nObj2 values are changed");
        obj2.addr = 50;
        obj2.data = 8'h0f;
        obj2.sub.f_id = 4'hf;
        obj1.disp();
        obj2.disp();
    end
endmodule
Output

For simple copy

# Both the pointers are pointing to different object
# ADDR:          23, DATA: 23, F_ID: 0101
# ADDR:         453, DATA: ff, F_ID: 1010
# 
# Obj2 is a simple copy of obj1
# ADDR:          23, DATA: 23, F_ID: 0101
# ADDR:          23, DATA: 23, F_ID: 0101
# 
# Obj2 values are changed
# ADDR:          43, DATA: 00, F_ID: 1111
# ADDR:          43, DATA: 00, F_ID: 1111
# 
# Obj1 values are changed
# ADDR:        1000, DATA: 0f, F_ID: 1111
# ADDR:        1000, DATA: 0f, F_ID: 1111

In this output, we can see that any data change in any of the object is reflected in both the objects and thus we can say that the object is not actually copied inside memory.

For Shallow Copy

# Both the pointers are pointing to different object
# ADDR:          23, DATA: 23, F_ID: 0101
# ADDR:         453, DATA: ff, F_ID: 1010
# 
# Obj2 is a shallow copy of obj1
# ADDR:          23, DATA: 23, F_ID: 0101
# ADDR:          23, DATA: 23, F_ID: 0101
# 
# Obj2 values are changed
# ADDR:          23, DATA: 23, F_ID: 1111
# ADDR:          50, DATA: 0f, F_ID: 1111

In this output, we can see that the immediate variables of the object are copied and thus any change in these variables are not reflected in original object. But the object inside obj1 is not getting copied and change in the data is reflected in original object.

For Deep copy

# Both the pointers are pointing to different object
# ADDR:          23, DATA: 23, F_ID: 0101
# ADDR:         453, DATA: ff, F_ID: 1010
# 
# Obj2 is a deep copy of obj1
# ADDR:          23, DATA: 23, F_ID: 0101
# ADDR:          23, DATA: 23, F_ID: 0101
# 
# Obj2 values are changed
# ADDR:          23, DATA: 23, F_ID: 0101
# ADDR:          50, DATA: 0f, F_ID: 1111
Try this code in EDA Playground

Conclusion

In this article, we covered the basics of copying an object in system Verilog. We also investigated how copying an object is different from copying a normal variable. Though not an overly complex topic, but it is used frequently in test bench design and implementation.