電気回路/HDL/ModelSim の radix define の履歴(No.5)
更新ステートマシンのステートを記号で表示したい†
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 のテスト時に、インストラクションをアセンブラで表示できます。
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 }