システムタスク
$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_FFFF
module 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
end
endmodule
ただ、このコードを実行すると、ちょっと意図しない動作をします。
# [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 ---
00000001
00000010
00000100
00001000
00010000
00100000
01000000
10000000
--- file : readmemh ---
01
02
04
08
10
20
40
80
--- 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]);
end
endmodule
--- 実行結果 ---
# 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;
end
endmodule
`timescale 100ns/1ps
module test1;
endmodule
`timescale 10ns/1ns
module 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
このように表示されます。