システムタスク

$sformat, $sformatf, $psprintf

文字列を生成する、似たようなシステムタスクです。$sformatは、引数の中にstring変数をおいて、そこに結果を入れます。$psprintf, $sformatf は、返り値としてstringを返します。同じ挙動のようです。

string s;int i; i=10;$sformat(s, "This is a pen. %0d", i);s = $psprintf("This is a pen. %0d",i);/// 上記2行の結果は同じ

$fopen, $fdisplay, $fclose

参考にしたページはこちら。ファイルを開いてログなどを書きだすのに使います。

integer filehandle;filehandle = $fopen("filename", attribute);/// attribute : "w" or "r" or "a" or ...$fdisplay(filehandle, "xxx %d yyy...", ...); :$fclose(filehandle);

$fgetc, $fgets, $fscanf

参考にしたページはこちら。$fopenで開いたファイルを読み出すシステムタスクです。$fgetcは、1文字を取得するタスク、$fgetsは各行の行単位で文字列を取得するタスクのようです。

使用例:($fgetc, $fscanf)

---- test.sv ---`define EOF 32'hFFFF_FFFFmodule test; initial begin integer fp; byte scan_data; fp = $fopen("readmemb","r"); ///readmembファイルは下記$readmembと同じ if(fp!=0)begin ///ファイルが開けたら while($fgetc(fp) != `EOF)begin ///ファイルの最後でなければ void'($fscanf(fp,"%08b",scan_data)); ///返り値を無視するcast #10 $display("[%0d] readmemb : %b", $stime, scan_data); end end else begin $display("Error : readmemb can not open..."); end endendmodule

ただ、このコードを実行すると、ちょっと意図しない動作をします。

# [10] readmemb : 00000001# [20] readmemb : 00000010# [30] readmemb : 00000100# [40] readmemb : 00001000# [50] readmemb : 00010000# [60] readmemb : 00100000# [70] readmemb : 01000000# [80] readmemb : 10000000# [90] readmemb : 10000000 ← この行が余計

なぜ9行目が表示されるのかはわかりませんが、実用レベルで最後の行が2回処理されるのは困るので、ファイルの終了を示す記号を記述しておき、それを検出したらbreakで抜ける方法で回避すればいいかな?と思っています。(解決したら上記コードも修正しておきます…)

$readmemb, $readmemh

指定したファイルから、bin($readmemb), hex($readmemh)で記述された数値を読み込みます。読み込んだデータを格納する配列は、Dynamic Arrayに格納ができます。このときは、newしなくていいようです。(ModelSim上の動作確認ではOKでした)

$readmemb("ファイル名", array);

$readmemh("ファイル名", array);

※arrayはDynamic Arrayでもよさそう。この場合はDynamic Arrayをnewしなくても、システムタスクで取り込んだ分の配列サイズになるもよう。

例:8bitのbin記述ファイルから、配列に数値を格納

--- file : readmemb ---0000000100000010000001000000100000010000001000000100000010000000--- file : readmemh ---0102040810204080--- file : test.sv ---module test; initial begin byte memb []; byte memh []; $readmemb("readmemb", memb); $readmemh("readmemh", memh); foreach (memb[i]) $display("readmemb : %08b", memb[i]); foreach (memh[i]) $display("readmemh : %02h", memh[i]); endendmodule--- 実行結果 ---# readmemb : 00000001# readmemb : 00000010# readmemb : 00000100# readmemb : 00001000# readmemb : 00010000# readmemb : 00100000# readmemb : 01000000# readmemb : 10000000# readmemh : 01# readmemh : 02# readmemh : 04# readmemh : 08# readmemh : 10# readmemh : 20# readmemh : 40# readmemh : 80

$timeformat

参考にしたページはこちら。$timeformatを使うと、時間表示するときの単位を指定できるようになります。例えば、timescaleが1nsだったときと1psだったとき、$display文はどちらのtimescaleが使われているかわからないので、ログにnsと出せばいいのかpsと出せばいいのかわかりません。この問題は、$timeformatを使用した上で、時間表示を%tで行えば解決します。

$timeformat(arg0, arg1, arg2, arg3)

  • arg0 : units_number
  • arg1 : precision_number
  • arg2 : suffix_string
  • arg3 : minimum_field_width

例えば、時間表示を1ns単位にしたいときは、

$timeformat(-9, 3, "ns" ,9);

とします。arg3は表示に使用する文字数になりますので、上記設定はarg0でnsにあわせて、arg1で少数3桁(psまで)、arg2でns表記にすることになります。arg2でnsと2文字消費するため、数値表示は最低7文字で表現されることになります。

上記設定でtimescale=1ps/1psとして、以下のコード

#10ns $display("[%t]",$stime);#10ns $display("[%t]",$stime);#10ps $display("[%t]",$stime);

を実行すると、

# [ 10.000ns]# [ 20.000ns]# [ 20.010ns]

となります。

$printtimescale

指定インスタンス、もしくはカレントmoduleのtimescaleを表示させることができます。getできるわけではないので、timescaleを取得して処理をモゴモゴすることはできないと思いますが、「あれ?この moduleのtimescaleは1ns/1psではないのか?」などのように、おかしいな、というときに該当moduleに暫定組み込み、もしくはifdefで組み込んでおくといいかもしれません。

例:topの下にtest1とtest2のインスタンスがある

module top; test1 test1(); test2 test2(); initial begin $printtimescale(test1); $printtimescale(test2); $printtimescale; endendmodule
`timescale 100ns/1psmodule test1;endmodule
`timescale 10ns/1nsmodule test2;endmodule

実行すると、

# run -all# Time scale of (top.test1) is 100ns / 1ps# Time scale of (top.test2) is 10ns / 1ns# Time scale of (top) is 1ps / 1ps

このように表示されます。