分享

SystemVerilog中的$cast()向下类型转换

 晓理晓章 2016-09-27

    在UVM中经常可以看到$cast的身影,这是SV的build-in task之一,当然它还有function的版本,这里不讨论。说到这,不得不提到“类型转换”这个术语,SV和很多其他语言一样,都支持特定类型间的相互转换。SV类型转换分两种方法,一种叫静态类型转换,另一种称之为动态类型转换。静态类型转换的语法类似为:

  1. int a = 2;  
  2. real b;  
  3. b = real'(a);  

    这种静态类型转换一般是不会检查转换是否合法的,因此具有一定的危险性。但是$cast的task却不是这样,它在运行时将进行类型检查,如果转换失败,会产生运行时错误。

    $cast可以对不同的内建类型进行转换,用的更多的是不同层次之间类的转换。在这种父类于子类之间的转换里, 父类站的高,子类在底下,从父类向子类的转换,称为向下类型转换,而子类向父类的转换称为向上类型转换。向上类型转换是安全的,而反之则是不安全的。原因在于子类既然继承了父类,就拥有父类的一切属性,除此之外,子类还有自己独特的个性,这些是父类没有的。当进行向上类型转换时,相当于父类的句柄指向子类对象,这样的话句柄仍然能对子类对象与父类相同的属性进行访问。但是反过来,如果向下类型转换也那么自由,当试图把子类的句柄指向父类的对象会发生什么呢?父类本来在内存里就划好了一小块地盘,但是因为子类含有比父类更丰富的属性,它很有可能会访问父类并不包含的资源,越界了。父类就好像北京的行政区域,子类非要去访问到河北的地界,河北就不同意了,手机还得算个漫游。这就会造成严重的内存溢出,所以向下类型是需要有严格的类型检查的,阻止非法转换。

    很多人看到上面的话不止一次了,但是却不知道什么情况下向下类型转换才会成功。来看一个例子,父亲有两个孩子,孩子0有辆车,而孩子1有架飞机

  1. class father;  
  2.   string m_name;  
  3.   
  4.   function new (string name);  
  5.     m_name = name;  
  6.   endfunction : new  
  7.   
  8.   function void print ();  
  9.     $display("Hello %s", m_name);  
  10.   endfunction : print  
  11. endclass : father  
  12.   
  13. class child0 extends father;  
  14.   string car = "car";  
  15.   
  16.   function new (string name);  
  17.     super.new(name);  
  18.   endfunction : new  
  19. endclass : child0  
  20.   
  21. class child1 extends father;  
  22.   string plane = "plane";  
  23.   
  24.   function new (string name);  
  25.     super.new(name);  
  26.   endfunction : new  
  27. endclass : child1  


    现在来小试牛刀:

  1. module top;  
  2.   father f;  
  3.   child0 c0;  
  4.   child1 c1;  
  5.   child1 c2;  
  6.   
  7.   initial begin  
  8.     f = new("father");  
  9.     f.print();  
  10.     c0 = new("child0");  
  11.     f = c0;  
  12.     f.print();  
  13.     c1 = new("child1");  
  14.     f = c1;  
  15.     f.print();  
  16.   end    
  17. endmodule : top  

    这里的child赋值给father的语句全部都是向上类型转换,是安全的,因此直接用=号就能进行转换,最后打印出来的结果是:

  1. father  
  2. child0  
  3. child1  

    假如说反过来,我们这样:

  1. c1 = f;  

    或者使用了$cast但是指向对象类型和想转换的类型有区别:

  1. $cast(c0, f);  

    都是会失败的。只有当前父类指针指向的对象和待转换对象的类型一致时,cast才会成功。我们把上面的代码改一改:

  1. initial begin  
  2.   f = new("father");  
  3.   f.print();  
  4.   c0 = new("child0");  
  5.   f = c0;  
  6.   f.print();  
  7.   c1 = new("child1");  
  8.   f = c1;  
  9.   f.print();  
  10.   
  11. bsp;   c1.plane = "J10";  
  12.   
  13.   $cast(c2, f);  
  14.   c2.print();  
  15.   $display("has %s", c2.plane);  
  16. end    

    这时候转换成功,因为c1和c2的类型是相同的。这里玩了个小动作,把c1的plane换成了歼十,打印出来的结果:

  1. Hello father  
  2. Hello child0  
  3. Hello child1  
  4. Hello child1  
  5. has J10  

    千万小心这样的句柄传递,会造成两个句柄同时指向一个对象,当使用其中一个句柄对对象的内容进行修改,另一个句柄再次访问的时候就会发现值已经被改变了。同时也可以看出cast的复制效果只是个shadow copy, 这就是为什么UVM的copy函数在$cast之后还要再对对象里的属性进行逐个取出来赋值的原因,这样的deep copy才能从根本上阻断源和目的的联系。

    那么这样的类型转换有什么好处呢?通常的类型转换是一种格式的需求,但是继承类之间的相互转换,却有更多的便宜。将独特的子类转换为父类的类型,更注重通用性,共性越多,重用性越好。就好像你从宿舍去教室,他从宿舍去食堂,你们到大门这截路大家都能走。但是终究还是要分道扬镳,这时候要处理具体的内容就需要将父类句柄类型转换为子类类型才能访问子类特有的资源。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多