轉載
在UVM中經常可以看到$cast的身影,這是SV的build-in task之一,當然它還有function的版本,這里不討論。
說到這,不得不提到“類型轉換”這個術語,SV和很多其他語言一樣,都支持特定類型間的相互轉換。SV類型轉換分兩種方法,一種叫靜態類型轉換,另一種稱之為動態類型轉換。靜態類型轉換的語法類似為:
- int a = 2;
- real b;
- b = real'(a);
這種類型`()的靜態類型轉換,一般不檢查轉換是否合法,因此具有一定的危險性。
但$cast的task卻不是這樣,它會進行類型檢查。如果轉換失敗,會產生運行時錯誤。
$cast可以對不同的內建類型進行轉換,用的更多的是不同層次之間類的轉換。在這種父類與子類之間的轉換里, 父類站的高,子類在底下,從父類向子類的轉換,稱為向下類型轉換,而子類向父類的轉換,稱為向上類型轉換。
向上類型轉換是安全的,反之向下轉換是不安全的。
原因在於子類既然繼承了父類,就擁有父類的一切屬性,除此之外,子類還有自己獨特的個性,這些是父類沒有的。當進行向上類型轉換時,相當於父類的句柄指向子類對象,這樣的話句柄仍然能對子類對象與父類相同的屬性進行訪問。但是反過來,如果向下類型轉換也那么自由,當試圖把子類的句柄指向父類的對象會發生什么呢?父類本來在內存里就划好了一小塊地盤,但是因為子類含有比父類更豐富的屬性,它很有可能會訪問父類並不包含的資源,越界了。父類就好像北京的行政區域,子類非要去訪問到河北的地界,河北就不同意了,手機還得算個漫游。這就會造成嚴重的內存溢出,所以向下類型是需要有嚴格的類型檢查的,阻止非法轉換。
向下,子類很可能有父類沒有的成員。向上,只需去除沒有的成員。
eg:
很多人看到上面的話不止一次了,但是卻不知道什么情況下向下類型轉換才會成功。來看一個例子,父親有兩個孩子,孩子0有輛車,而孩子1有架飛機
- class father;
- string m_name;
- function new (string name);
- m_name = name;
- endfunction : new
- function void print ();
- $display("Hello %s", m_name);
- endfunction : print
- endclass : father
- class child0 extends father;
- string car = "car";
- function new (string name);
- super.new(name);
- endfunction : new
- endclass : child0
- class child1 extends father;
- string plane = "plane";
- function new (string name);
- super.new(name);
- endfunction : new
- endclass : child1
現在來小試牛刀:
- module top;
- father f;
- child0 c0;
- child1 c1;
- child1 c2;
- initial begin
- f = new("father");
- f.print();
- c0 = new("child0");
- f = c0;
- f.print();
- c1 = new("child1");
- f = c1;
- f.print();
- end
- endmodule : top
這里的child賦值給father的語句全部都是向上類型轉換,是安全的,因此直接用=號就能進行轉換,最后打印出來的結果是:
- father
- child0
- child1
假如說反過來,我們這樣:
- c1 = f;
或者使用了$cast但是指向對象類型和想轉換的類型有區別:
- $cast(c0, f);
都是會失敗的。只有當前父類指針指向的對象和待轉換對象的類型一致時,cast才會成功。我們把上面的代碼改一改:
initial begin
- f = new("father");
- f.print();
- c0 = new("child0");
- f = c0;
- f.print();
- c1 = new("child1");
- f = c1;
- f.print();
- bsp; c1.plane = "J10";
- $cast(c2, f);
- c2.print();
- $display("has %s", c2.plane);
- end
這時候轉換成功,因為c1和c2的類型是相同的。這里玩了個小動作,把c1的plane換成了殲十,打印出來的結果:
- Hello father
- Hello child0
- Hello child1
- Hello child1
- has J10
千萬小心這樣的句柄傳遞,會造成兩個句柄同時指向一個對象,當使用其中一個句柄對對象的內容進行修改,另一個句柄再次訪問的時候就會發現值已經被改變了。同時也可以看出cast的復制效果只是個shadow copy, 這就是為什么UVM的copy函數在$cast之后還要再對對象里的屬性進行逐個取出來賦值的原因,這樣的deep copy才能從根本上阻斷源和目的的聯系。
那么這樣的類型轉換有什么好處呢?通常的類型轉換是一種格式的需求,但是繼承類之間的相互轉換,卻有更多的便宜。將獨特的子類轉換為父類的類型,更注重通用性,共性越多,重用性越好。就好像你從宿舍去教室,他從宿舍去食堂,你們到大門這截路大家都能走。但是終究還是要分道揚鑣,這時候要處理具體的內容就需要將父類句柄類型轉換為子類類型才能訪問子類特有的資源。
指針帶有成員位寬信息,賦值就是轉換為新的成員位寬信息結構。