電気回路/HDL/ModelSim の radix define
ステートマシンのステートを記号で表示したい†
ModelSim の wave ウィンドウでは信号線の状態を 2進数や16進数、アナロググラフなど、 さまざまな形式で表示することができます。
しかし、Verilog で書いたステートマシンのステートを、 分かりやすく記号で表示するのは一筋縄では行きません。
こんなのを例として考えます。
LANG:verilog
module trimac_tx (
...
);
...
localparam stIdle = 0;
localparam stMem = 1;
localparam stTx = 2;
localparam stUdpChecksum0 = 3;
localparam stUdpChecksum1 = 4;
localparam stUdpChecksum2 = 5;
localparam stUdpChecksum3 = 6;
localparam stUdpChecksum4 = 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 のテスト時に、インストラクションをアセンブラで表示できます。
ちなみに、このような SystemVerilog のコードをテストベンチで使うには、 コードが SystemVerilog で書かれていることをコンパイラに伝えなければなりません。
一番楽にできるのは、ISE の Processes で右クリックして Process Properties を選択し、Other VLOG Command Line Options のところに -sv を入力することです。
これで、 ModelSim はすべての Verilog ファイルを SystemVerilog として解釈するため、 string その他を使った SystemVerilog 形式のテストベンチを使うことができるようになります。
radix define を使う†
こちらは ModelSim のマニュアルから発見しました。
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 stUdpChecksum0 = 3;
localparam stUdpChecksum1 = 4;
localparam stUdpChecksum2 = 5;
localparam stUdpChecksum3 = 6;
localparam stUdpChecksum4 = 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)
なんてスクリプトを作ります。
もともとの .udo ファイルを別名で取っておくことにして、
LANG:console $ cp trimac_test.udo trimac_test.udo.orig
以下のようにすれば、
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
}
特に便利かもしれない使用法†
これ、PicoBlaze など埋め込み CPU のデバッグに大変有効ですね。
通常、PicoBlaze のデバッグで pc の値を追いかけるためには、めまぐるしく入れ替わる個々の数値が見えるくらい拡大しないといけないのですが、そうすると全体の流れが見づらくなってしまいますし、そうでなくても何百、何千の値の中から特定のアドレスを見つけるのは骨が折れます。
そこで、起点となるいくつかのアドレスに radix define で色を付けておくと、かなり縮小してあっても一目で箇所を特定できるようになり、そこを目印に拡大していけば目的の箇所に素早くたどり着けます。
trimac_test.udo
...
# trimac_control.v
radix define TrimacControllerPC {
'h007 "Idle" -color green,
'h126 "RxArp" -color yellow,
'h143 "RxUdp" -color yellow,
...
-default hex
-defaultcolor white
}
コメント†
- 私はステートのストリングをソースに埋め込んでいます。http://marsee101.blog19.fc2.com/blog-entry-400.html -- [marsee]
- はい、#kb177b67 の方法ですね。radix define だと色が変えられるのが面白いと思います。 -- [武内(管理人)]
- 色が変えられることを大いに生かせる例として、PicoBlaze のデバッグについて追記しました。 -- [武内(管理人)]
- やり方がわかりましたw ありがとうございます! -- [アプロ]
- 役に立って良かったです(^^ 私自身は最近 ISim がメインになってしまっているのですが・・・ -- [武内(管理人)]