oopverilogsystem-verilogfpgamodelsim

SystemVerilog inheritance, aggregated classes and parent function call


I have a problem with super.func() call in SV.

I have three main classes:
class_C extends class_B;
class_B extends class_A;
class_A;

And I have three configuration (aggregate) classes:
inner_C extends inner_B;
inner_B extends inner_A;
inner_A;

When I call function chain from clacc_C that prints self pointer and pointer to the corresponding inner class I receive normal self pointer and null for inner_B and inner_A.

Why am I getting such behavior from SV? How to workaround this problem?

simulator - Questa 2021.1 This is a code sample for problem illustration:

package test_pkg;
    // base config
    class inner_A;
        int val_0_min;
        int val_0_max;

        function new();
        endfunction : new
    endclass : inner_A
    
    // base class
    class class_A;
        inner_A in_A_h;
        
        rand int val_0;
        
        constraint constr_0 {
            val_0 inside {[in_A_h.val_0_min : in_A_h.val_0_max]};
        }
        
        function void print();
            $display("val_0 = %0d", val_0);
            // next string give an error: Fatal: (SIGSEGV) Bad handle or reference.
            //$display("val_0_min = %0d, val_0_max = %0d", 
            //  in_A_h.val_0_min, in_A_h.val_0_max);
            $display("pointer_0 this %m = ", this);
            $display("pointer_0 in_A_h %m = ", in_A_h);
        endfunction : print
        
        function new();
        endfunction : new
    endclass : class_A

    // first inheritance
    class inner_B extends inner_A;
        int val_1_min;
        int val_1_max;

        function new();
            super.new();
        endfunction : new   
    endclass : inner_B

    class class_B extends class_A;
        inner_B in_B_h;
        
        rand int val_1;
        
        constraint constr_1 {
            val_1 inside {[in_B_h.val_1_min : in_B_h.val_1_max]};
        }
        
        function void print();
            super.print();
            $display("val_1 = %0d", val_1);
            // next string give an error: Fatal: (SIGSEGV) Bad handle or reference.
            //$display("val_1_min = %0d, val_1_max = %0d, val_0_min = %0d, val_0_max = %0d", 
            //  in_B_h.val_1_min, in_B_h.val_1_max, in_B_h.val_0_min, in_B_h.val_0_max);
            $display("pointer_1 this %m = ", this);
            $display("pointer_1 in_B_h %m = ", in_B_h);
        endfunction : print
        
        function new();
            super.new();
        endfunction : new
    endclass : class_B
    
    // second inheritance
    class inner_C extends inner_B;
        int val_2_min;
        int val_2_max;

        function new();
            super.new();
        endfunction : new   
    endclass : inner_C

    class class_C extends class_B;
        inner_C in_C_h;
        
        rand int val_2;
        
        constraint constr_2 {
            val_2 inside {[in_C_h.val_2_min : in_C_h.val_2_max]};
        }
        
        function void print();
            super.print();
            $display("val_2 = %0d", val_2);
            $display("val_2_min = %0d, val_2_max = %0d, val_1_min = %0d, val_1_max = %0d, val_0_min = %0d, val_0_max = %0d", 
                in_C_h.val_2_min, in_C_h.val_2_max, in_C_h.val_1_min, in_C_h.val_1_max, in_C_h.val_0_min, in_C_h.val_0_max);
            $display("pointer_2 this %m = ", this);
            $display("pointer_2 in_C_h %m = ", in_C_h);
        endfunction : print
        
        function new();
            super.new();
        endfunction : new
    endclass : class_C  
endpackage : test_pkg

module DUT
    import test_pkg::*;
();
    inner_C    in_C_h;
    class_C    C_h;
    
    class_B    B_h;
    class_A    A_h;
    
    initial begin
        in_C_h = new();
        in_C_h.val_2_min = 20;
        in_C_h.val_2_max = 30;
        in_C_h.val_1_min = 10;
        in_C_h.val_1_max = 20;
        in_C_h.val_0_min = 1;
        in_C_h.val_0_max = 10;

        B_h = new();
        A_h = new();
        C_h = new();
        C_h.in_C_h = in_C_h;
        
        if (!C_h.randomize()) begin
            $error("randomization failed"); // $fatal();
        end
        
        // print all data by the print() chain
        $display("\n\tprint from C");
        C_h.print();
        
        // cast to parent and print only parent content
        $display("\n\tprint from B");
        if (!$cast(B_h, C_h)) begin
            $fatal();
        end
        B_h.print();

        $display("\n\tprint from A");
        if (!$cast(A_h, B_h)) begin
            $fatal();
        end
        A_h.print();
    end
endmodule : DUT

Solution

  • The answer to the problem is simple. The problem is that the child class creates the parent class it doesn't pass the reference to the inner class.

    In other words:

    C_h  >>  B_h   >> A_h
    |        |        |
    in_C     in_B     in_A
    |        |
    in_B     in_A
    |
    in_A
    

    Then we pass a reference to in_C inside C_h we pass it only for the topmost child class, not for the parent classes! The child class has its own inner classes chain and the parent class has its own and these chains is different chains, not the same.

    To do what I want I need to pass reference to the parent class from the child class. It can't be done with direct link pass. To do this I need to write a function that will get an inner object, cast it and pass to the child class config function and so on.

    Rewritten code:

        // base config
        class inner_A;
            int val_0_min;
            int val_0_max;
    
            function new();
            endfunction : new
            
            function void print();
                $display("pointer self %m = ", this);
                $display("val_0_min = %0d, val_0_max = %0d", val_0_min, val_0_max);
            endfunction : print
        endclass : inner_A
        
        // base class
        class class_A;
            protected inner_A in_A_h;
            
            rand int val_0;
            
            constraint constr_0 {
                val_0 inside {[in_A_h.val_0_min : in_A_h.val_0_max]};
            }
            
            function void print();
                $display("val_0 = %0d", val_0);
                $display("val_0_min = %0d, val_0_max = %0d", 
                    in_A_h.val_0_min, in_A_h.val_0_max);
                $display("pointer_0 this %m = ", this);
                $display("pointer_0 in_A_h %m = ", in_A_h);
            endfunction : print
            
            function void set_config(ref inner_A conf_in);
                in_A_h = conf_in;
            endfunction : set_config
            
            function new();
            endfunction : new
        endclass : class_A
    
        // first inheritance
        class inner_B extends inner_A;
            int val_1_min;
            int val_1_max;
    
            function new();
                super.new();
            endfunction : new
            
            function void print();
                super.print();
                $display("pointer self %m = ", this);
                $display("val_1_min = %0d, val_1_max = %0d", val_1_min, val_1_max);
            endfunction : print
        endclass : inner_B
    
        class class_B extends class_A;
            protected inner_B in_B_h;
            
            rand int val_1;
            
            constraint constr_1 {
                val_1 inside {[in_B_h.val_1_min : in_B_h.val_1_max]};
            }
            
            function void print();
                super.print();
                $display("val_1 = %0d", val_1);
                $display("val_1_min = %0d, val_1_max = %0d, val_0_min = %0d, val_0_max = %0d", 
                    in_B_h.val_1_min, in_B_h.val_1_max, in_B_h.val_0_min, in_B_h.val_0_max);
                $display("pointer_1 this %m = ", this);
                $display("pointer_1 in_B_h %m = ", in_B_h);
            endfunction : print
    
            function void set_config(ref inner_B conf_in);
                inner_A in_A_h;
                in_B_h = conf_in;
                if (!$cast(in_A_h, this.in_B_h)) begin
                    $fatal();
                end
                super.set_config(in_A_h);
            endfunction : set_config
            
            function new();
                super.new();
            endfunction : new
        endclass : class_B
        
        // second inheritance
        class inner_C extends inner_B;
            int val_2_min;
            int val_2_max;
    
            function new();
                super.new();
            endfunction : new
            
            function void print();
                super.print();
                $display("pointer self %m = ", this);
                $display("val_2_min = %0d, val_2_max = %0d", val_2_min, val_2_max);
            endfunction : print
        endclass : inner_C
    
        class class_C extends class_B;
            protected inner_C in_C_h;
            
            rand int val_2;
            
            constraint constr_2 {
                val_2 inside {[in_C_h.val_2_min : in_C_h.val_2_max]};
            }
            
            function void print();
                super.print();
                $display("val_2 = %0d", val_2);
                $display("val_2_min = %0d, val_2_max = %0d, val_1_min = %0d, val_1_max = %0d, val_0_min = %0d, val_0_max = %0d", 
                    in_C_h.val_2_min, in_C_h.val_2_max, in_C_h.val_1_min, in_C_h.val_1_max, in_C_h.val_0_min, in_C_h.val_0_max);
                $display("pointer_2 this %m = ", this);
                $display("pointer_2 in_C_h %m = ", in_C_h);
            endfunction : print
            
            function void set_config(ref inner_C conf_in);
                inner_B in_B_h;
                in_C_h = conf_in;
                if (!$cast(in_B_h, this.in_C_h)) begin
                    $fatal();
                end
                super.set_config(in_B_h);
            endfunction : set_config
            
            function new();
                super.new();
            endfunction : new
        endclass : class_C  
    endpackage : test_pkg
    
    module DUT
        import test_pkg::*;
    ();
        inner_C    in_C_h;
        class_C    C_h;
        
        class_B    B_h;
        class_A    A_h;
        
        initial begin
            in_C_h = new();
            in_C_h.val_2_min = 20;
            in_C_h.val_2_max = 30;
            in_C_h.val_1_min = 10;
            in_C_h.val_1_max = 20;
            in_C_h.val_0_min = 1;
            in_C_h.val_0_max = 10;
    
            C_h = new();
            C_h.set_config(in_C_h); // C_h.in_C_h = in_C_h;
            
            if (!C_h.randomize()) begin
                $error("randomization failed");
            end
            
            // print all data by the print() chain
            $display("\n\tprint from C");
            C_h.print();
            
            $display("\n\tprint from inner_C");
            in_C_h.print();
            
            // cast to parent and print only parent content
            $display("\n\tprint from B");
            if (!$cast(B_h, C_h)) begin
                $fatal();
            end
            B_h.print();
            
            $display("\n\tprint from A");
            if (!$cast(A_h, B_h)) begin
                $fatal();
            end
            A_h.print();
        end
    endmodule : DUT