電気回路/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
   }

コメント


Counter: 15034 (from 2010/06/03), today: 1, yesterday: 0