Casting in Classes

Hello OVM,

I came from this bueatiful world of specman, where casting was as easy as “.as_a” .

Now working in SV I realised that all my classes are designed with concept of inheritance. Once I use inheritance, downcasting and upcasting becomes the norm. But in SV I need to always use this $cast function which is such a coding pain. It requires 3 lines of code. One to declare a cast variable, second to write the $cast line and third comes the access.

I am pasting a example code to highlite my worry.


class a;
int a ;
virtual task test_a();
$display(“TEST A CALLED”);
endtask
endclass

class b extends a;
int b ;
virtual task test_a();
$display(“TEST B_A CALLED”);
endtask
endclass

program top;

initial begin
a a_obj;
a a_array[2];
b b_obj;

b b_cast; //step 1

b_obj = new();
a_obj = b_obj; //downcasting
b_obj.b = 4;
$display(“%d”,b_obj.b);
a_array[0] = b_obj;

$cast(b_cast,a_array[0]); //step 2
b_cast.b = 5;// step 3

//b’(a_array[0]).b = 5; //DOES SV give me this power. in just one step

$display(“%d”,b_cast.b);
end
endprogram

To achieve casting , I need to write the 3 lines in blue labelled step 1,2,3.
Can I acheive this in just one line.

I am going mad , with all the casting variables and real variables.

Can someone help me reduce lines of code with an inovative idea.

Regards,
Nisreen

If i understand you simply want to change the value of b defined in derived class which is pushed in the array of base classes. Am i right?

Hi Nisreen,

As the code you show above, you can directly assign b_obj to b_cast.
b b_casst = b_obj;

Nisreen,

One of the coding guidelines for OOP state that you should not access class properties directly and instead access them by a virtual method. Class properties should be protected unless you have a very good reason (usually laziness) for making them public.

When declaring a_array, I presume you will be dynamically setting each element to a class a or class b handle. What’s supposed to happen if you try to access int b, but have a handle to class a? $cast should be used as a function to check that the class handle is a valid reference to class b.

By directly referencing int b, your program block is now dependent on the existence of class b. It’s difficult to suggest a better alternative without know more what you are trying to accomplish.

Dave

Take the classic ethernet example. We have a base_ethernet_frame class which may have src ,dest and the frame type feilds, from it we derive maybe a PPPoE class, maybe a SNAP Class and so on. Now in another class called ethernet_frame, I instantiate a queue of base_ethernet_frame class . Now depending on some ingress algorithm, in run time I decide how to fill up this queue of base class. q[0] may point to pppoe frame, q[1] may point to snap frame etc. Dont think too much why in network protocol would this happen. Its just a hypothetical case.

Now I have another egress algorithm where in I read this queue of base classes. Now with the type feild in the base class, i predict that q[0] is a pppoe frame. but when I do a q[0].PPPoEfiled, I get vlog errors. Hence I got to downcast q[0] to pppoe frame type and then access the feilds.

This is very easy in specman with as_a directive. Is it factory that i must use to acheive as_a equivalence in SV. or is it the same upcast and downcast jugglery

Nisreenj,

Your recent post indicates that you do not know which index of queue will carry which type of object. In this case you will have to use cast to know the type of object. Then only you could access the properties. I am not sure how would you achieve this in one step in beautiful world of specman.
But in sv world - i have quoted a simple example code to show what you could do.

And in the example you will see that when you are poping out of the queue, you need to know which type this index is carrying. Else you will need to use the $cast to check.

I have also shown the case where you know the type of object at a index.

module tb();
class base1;
  int a;
  function new();
   a=5;
  endfunction
endclass
 
class base_child1 extends base1;
  int b;
  function new();
    super.new();
  endfunction
endclass
class base_child2 extends base1;
  int c;
  function new();
    super.new();
  endfunction
endclass
 
class env;
  base1 bq[$:4];
  base_child1 bc1;
  base_child2 bc2;
  
  function new();
   bc1 = new();
   bc2 = new();
  endfunction
  
function construct();
  bc1.b = 10;
  bc2.a = 20;
  bc2.c = 30;
  bq.push_back(bc1);
  bq.push_back(bc2);
endfunction
endclass
 
  
  initial begin
    int check;
    // ctype = 1 - c2, 2 - c3
    int ctype; 
    base_child1 c2 = new();
    base_child2 c3 = new();
    env e = new();
    e.construct();
    // assuming you do not know the type of class at this index
    if($cast(c2,e.bq[0]))
      ctype = 1;
    else
      ctype = 2;
    if(ctype == 1) begin 
      c2 = e.bq.pop_front();
      c2.b = 100;
      $display("b = %0d", c2.b);
   end
    else 
      c3 = e.bq.pop_front();
   // How do you achieve the above scenario in specman world ??
 
 
    // assuming you know the type of class at this index
    c3 = e.bq.pop_front();
    $display("a = %0d c= %0d", c3.a,c3.c);
 
  end
endmodule

Nisreenj,

Yes, using the create_object or create_component method of the factory does not require a $cast.

Dave

Hi Nisreen,

In reading through this it sounds like you need to do the type check whether you are in e or sv, so your steps 2 and 3 are happening in either case (right?).

If I understand what you are saying, you want to have code like:

if (something_tells_me_q0_is_pppoe) begin
    pppoe_type'(q[0]).PPPoEFiled();
  end

And instead you have to do:

begin
    pppoe_type is_pppoe;
    if($cast(is_pppoe, q[0])) begin
      is_pppoe.PPPoEFiled();
    end
  end

So your issue is with the temporary variable that you had to create and use (I only have the begin/end block keep the code together).

If this is your issue, there isn’t anything else you can do in SV about it. Generally, as Dave said earlier, the more correct OO methodology would be to have some virtual method to have the derived class implement.

For instance, in your ethernet example you may have a virtual method called post_frame (or whatever) that gets called on completed frames. Then, your code would just be:

q[0].post_frame();

And the fact that the particular frame calls PPPoEfiled() at this point doesn’t really matter.

Anyway, the only way I can think of to avoid the temporary variable is to use virtual methods… and, I realize that does not work in all cases.

john

As Dave suggested, the use of factory is good but that works for polymorphic virtual task/functions only and there is nothing that factory can do for field access of derived classes. Sad for me.

John you understood my problem very correctly. But again your approach points to vitual methods/task .

Agreed. I will make a class that has all the feilds of ever perceived ethernet protocols or for that matter any protocol. Then I run into issues like packing. Because if u have superset classes with all possible feilds and minimal use of inheritance in terms of feild definitions, then u have very little option but to define a do_pack of ur own. similarly do_print and the list of specialisation goes on.

Secondly, u end up having so many contraints to nullify feilds of a class that do not hold relevance when the class type is differrent. Meaning, PPPoE feilds will be set to -1 in a constraint when the frame type is SNAP or something like that.

Now my basic woe is that if specman can give a as_a() function to dynamic cast a class, what is the basic OOP methodology constraint that is stopping OVM experts to develop a similar as_a function for SV. Dave/John :: Please make an effort to clarify this doubt for me. It will help me to go on with SV. I am so nostalgic of specman and my mind works to make classes as compact and heavily dependent on inheritance.

Regards,
Nisreen

Agreed. I will make a class that has all the feilds of ever perceived ethernet protocols or for that matter any protocol. Then I run into issues like packing. Because if u have superset classes with all possible feilds and minimal use of inheritance in terms of feild definitions, then u have very little option but to define a do_pack of ur own. similarly do_print and the list of specialisation goes on.
Secondly, u end up having so many contraints to nullify feilds of a class that do not hold relevance when the class type is differrent. Meaning, PPPoE feilds will be set to -1 in a constraint when the frame type is SNAP or something like that.

You should not have to be doing this. I think you are trying top learn SV and OVM at the same time, which is a mistake. (even if unavoidable due to schedules) Maybe you should contact your vendor for more direct help than can be provided in this forum.

Dave

Nisreen,

The as_a() function is e is exactly the same as $cast for doing dynamic casts. The only difference (as far as I can tell) is that, with as_a(), you are able to chain the call with the implicitly created variable so that you don’t have to create a temporary variable.

You certainly should not be putting everything in the base class. The correct thing to do is to use inheritance, use polymorphism where it makes sense and use dynamic casting where that makes sense.

Where you need to use dynamic casting, there is no way in SV to avoid the extra temporary variables (it just isn’t part of the current syntax). I haven’t personally found this to be a major issue, but I can understand the irritation.

As Dave suggests, contacting your vendor directly may be the best plan for you in this case.

john