04. classのcopy

何年ぶりかの更新になります(たぶんこの内容のエントリーは書いてないはず)。

↓ copyやdeep_copyのメソッド名は不確かですのでご注意ください

UVMを使っていると、classのcopyやdeep_copy(階層含めたcopy)をサポートしていますが、UVMを使っていない場合には、以下のようなコードを書いているとリファレンス(参照)になるのはみなさんご存知のとおりかと思います。UVMを使っていても、copyやdeep_copyメソッドを使わなければリファレンスになります。

リファレンスになるのを防ぐには、classを「new」して生成してから値を詰め込んで使います。ぼくの過去の失敗例では、スコアボードなどで配列を使用するとき、一度生成したclassオブジェクトを「再利 用」して値を詰め込み配列に入れた結果、期待値チェックの際に片方のデータが「全部同じ」ということがありました。classは代入時に「リファレンスになる」ことを知っていれば、ハマることなく直せるわけですが、知らなかったら痛い目に会うこともありますので、知らなかった方はここで覚えておいてください。

■リファレンスになる例

module test;
class packet; byte header; byte payload;
function new(header, payload); this.header = header; this.payload = payload; endfunction
function void disp; $display("packet is %02Xh, payload is %02Xh", this.header, this.payload); endfunction endclass
initial begin packet pkt1; packet pkt2; $display(""); pkt1 = new(8'h01, 8'h55); pkt2 = pkt1; pkt1.header = 8'h02; pkt1.payload = 8'hAA; pkt1.disp; pkt2.disp; $display(""); endendmodule

上記moduleをコンパイルして実行すると、pkt2にはpkt1のリファレンスが入っているため、pkt1のメンバー書き換えはpkt2にも反映され、pkt1.dispとpkt2.dispは同じ結果を出します。

■実行結果

# packet is 02h, payload is aah# packet is 02h, payload is aah

そこで、これもみなさんご存知のネタだと思うのですが、class内部に自分を持っておいて、自分でcopyメソッドを書いておいて、代入時にcopyメソッドを呼べば解決します。以下は、変更後の全記述をベタ貼 り付けしています。変更部分に色を塗っています。

module test;
class packet; byte header; byte payload; packet myself;
function new(header, payload); this.header = header; this.payload = payload; endfunction
function void disp; $display("packet is %02Xh, payload is %02Xh", this.header, this.payload); endfunction
function packet copy; packet pkt; pkt = new(this.header, this.payload); return pkt; endfunction endclass
initial begin packet pkt1; packet pkt2; $display(""); pkt1 = new(8'h01, 8'h55); pkt2 = pkt1.copy; pkt1.header = 8'h02; pkt1.payload = 8'hAA; pkt1.disp; pkt2.disp; $display(""); endendmodule

■実行結果

# packet is 02h, payload is aah# packet is 01h, payload is 01h

ポイントは、「class内に自分自身をメンバとして定義しておき、copyメソッド定義時に、他のメンバ変数を適用した新しいclassを「生成」したものを return で返すことです。

deep_copyをする場合にも、基本的な考え方は同じでいいと思います。ただし、「汎用的なclassやメソッドを定義して(UVMのように)、それを継承したclassを使えば容易にcopyやdeep_copyができる」コー ドをぼくはすぐに書けないので(答えはUVMクラスライブラリ内にあります)、ご自身で考えてコーディングしてください。