電気回路/HDL/ModelSim の radix define の履歴(No.4)
更新ステートマシンのステートを記号で表示したい†
ModelSim の wave ウィンドウでは信号線の状態を 2進数や16進数、アナロググラフなど、 さまざまな形式で表示することができます。
しかし、Verilog で書いたステートマシンのステートを、 分かりやすく記号で表示するのは一筋縄では行きません。
こんなのを例として考えます。
LANG:verilog
module trimac_tx (
...
);
...
localparam stIdle = 0;
localparam stMem = 1;
localparam stTx = 2;
localparam UdpChecksum0 = 3;
localparam UdpChecksum1 = 4;
localparam UdpChecksum2 = 5;
localparam UdpChecksum3 = 6;
localparam UdpChecksum4 = 7;
localparam stDone = 8;
localparam stRewound = 9;
reg [3:0] state;
always @(posedge tx_clk)
if ( rst ) begin
state <= stIdle;
end else
case (state)
stIdle:
if (tx_start)
state <= stMem;
...
この state という信号は、stIdle, stMem などの値を取り得るのですが、 ModelSim 上では 0 や 1 などの数値として表示されてしまいます。
テスト時には頭の中で数値をステートに翻訳しなければなりません。
これを数値ではなく記号で表示する方法として、以下の2つを見つけました。
色分けできること、処理が軽いこと、を考えると後者がお勧めです。
表示用の信号を別に用意する†
http://cafeandverify.blogspot.com/2007/09/verilog-modeenum.html
にもあるように、テストベンチ側に表示用の信号を別に用意して、 それを wave ウィンドウに追加すれば、どんな表示も可能になります。
例えば、いくつかの状態を1つにまとめて表示するなどの応用も可能です。
LANG:verilog
string trimac_tx_state;
always @(uut.trimac.tx.state)
case (uut.trimac.tx.state)
0: trimac_tx_state = "Idle";
1: trimac_tx_state = "Mem";
2: trimac_tx_state = "Tx";
3,4,5,6,7: trimac_tx_state = "UdpChecksumX";
8: trimac_tx_state = "Done";
9: trimac_tx_state = "Rewound";
endcase;
string を使うとシミュレーション速度が気になる場合には、 SystemVerilog の enum 型に代入するだけでも良いのかもしれません。
これの応用で、ModelSim 用 PicoBlaze ディスアセンブラを作ってみました。 PicoBlaze のテスト時に、インストラクションをアセンブラで表示できます。
radix define を使う†
ModelSim の radix define コマンドを使います。
ModelSim の Transcript ウィンドウに以下を打ち込むか、 あるいは *.udo ファイルに以下を記述して読み込むと、 wave の Radix 指定で TrimacTxStates を選択できるようになります。
radix define TrimacTxStates {
4'd0 "Idle",
4'd1 "Mem",
4'd2 "Tx",
4'd3 "UdpChecksum0",
4'd4 "UdpChecksum1",
4'd5 "UdpChecksum2",
4'd6 "UdpChecksum3",
4'd7 "UdpChecksum4",
4'd8 "Done",
4'd9 "Rewound"
-default decimal
}
この方法の良いところは、記号で表示するだけでなく以下のように色も指定できるため、 文字が読めないくらいまで表示を縮小した場合にも、大まかな流れが分かります。
radix define TrimacTxStates {
0 "Idle" -color white,
1 "Mem" -color yellow,
2 "Tx" -color yellow,
3 "UdpChecksum0" -color yellow,
4 "UdpChecksum1" -color yellow,
5 "UdpChecksum2" -color yellow,
6 "UdpChecksum3" -color yellow,
7 "UdpChecksum4" -color yellow,
8 "Done" -color yellow,
9 "Rewound" -color red
-default decimal
-defaultcolor purple
}
-default と -defaultcolor は、 本来あり得ない 10 以上の数が現れたときに適用されます。
表示のために余計な信号値を記憶する必要の無い分、 上の方法に比べるとテストが重くならずに済みます。
radix define コマンドを Verilog コードに埋め込んでおく†
表示方法の指定がコードと離れたところにあると、 コードが変更されたときに更新し忘れそうなので、 radix define コマンドをコードに埋め込んでおいて 自動的に抜き出して使うことを考えました。
trimac_tx.v
LANG:verilog
...
localparam stIdle = 0;
localparam stMem = 1;
localparam stTx = 2;
localparam stUdpCrc0 = 3;
localparam stUdpCrc1 = 4;
localparam stUdpCrc2 = 5;
localparam stUdpCrc3 = 6;
localparam stUdpCrc4 = 7;
localparam stDone = 8;
localparam stRewound = 9;
/* include in udo
radix define TrimacTxStates {
0 "Idle" -color white,
1 "Mem" -color yellow,
2 "Tx" -color yellow,
3 "UdpChecksum0" -color yellow,
4 "UdpChecksum1" -color yellow,
5 "UdpChecksum2" -color yellow,
6 "UdpChecksum3" -color yellow,
7 "UdpChecksum4" -color yellow,
8 "Done" -color yellow,
9 "Rewound" -color red
-default decimal
-defaultcolor purple
}
*/
のように radix define コマンドを Verilog コードにコメントとして埋め込んでおいて、
extract.rb
LANG:ruby
#!/usr/bin/ruby -Ks
require "jcode"
# 第1パラメータが検索するタグ名
search = ARGV.shift
regex_open = /\/\*\s*#{search}/
regex_close = /^[ \t]*\*\//
# 残りがファイル名
ARGV.each do |filename|
print "# #{filename}\n\n"
extract = 0;
line_number = 1;
File.readlines(filename).each do |line|
if (extract == 0)
extract = line_number if line =~ regex_open
else
if line =~ regex_close
extract = 0
else
print line
end
end
line_number += 1;
end
if extract != 0
$stderr.print "Error: section starting at line #{extract} was not closed in #{filename}."
exit(false)
end
end
exit(true)
なんてスクリプトを使って、
LANG:console $ mv trimac_test.udo.orig trimac_test.udo $ ruby extract.rb "include in udo" *.v >> trimac_test.udo
とすれば、/* include in udo と */ で囲まれたコメント部分を 全ての .v ファイルから検索して .udo ファイルに抜き出せます。
trimac_test.udo
...
# trimac_tx.v
radix define TrimacTxStates {
0 "Idle" -color white,
1 "Mem" -color yellow,
2 "Tx" -color yellow,
3 "UdpChecksum0" -color yellow,
4 "UdpChecksum1" -color yellow,
5 "UdpChecksum2" -color yellow,
6 "UdpChecksum3" -color yellow,
7 "UdpChecksum4" -color yellow,
8 "Done" -color yellow,
9 "Rewound" -color red
-default decimal
-defaultcolor purple
}