ModelSim 用 PicoBlaze ディスアセンブラ

(4952d) 更新


公開メモ
電気回路/HDL/ModelSim の radix define

Xilinx が ModelSim の無償版&廉価版の配布を中止との情報

http://marsee101.blog19.fc2.com/blog-entry-1570.html

Xilinx 向けには ModelSim は敷居の高い環境になってしまいました。

ものすごい無駄骨

以下の内容ですが、思いっきり車輪の再発明でした。

PicoBlaze のマニュアルの VHDL Simulation という項に、 ほぼ以下の内容と同じことが可能なコードが始めから KCPSM3 のパッケージに入っていることが書かれています。

Verilog 版でもかなり探しにくいものの、"hexcharacter" という Verilog Task として入っています。 instruction の他、フラグやそれ以外の有用な情報を見やすく表示できますので、 そちらを使うのがお勧めです。

まあ、勉強にはなりましたけどね。色々な意味で・・・(負け惜しみ)

独学だと、こういうわかりきったことを発見するまでに掛かる時間が痛いです。

ModelSim を用いた PicoBlaze プログラムのデバッグ

PicoBlaze が実行中のコマンドを、数値ではなくアセンブラで表示する方法を考えてみました。

以下の手順で、こんな風にデバッグできます。

picoblaze_debug.png

注目は instruction の部分です。

結構簡単

やっているのは、kcpsm3 に繋いだ instruction 信号を SystemVerilog で書いた function でディスアセンブルして、表示用の変数に代入しているだけ。

思った以上に簡単でした。

kcpsm3 と instructions:

LANG:verilog
    /// **** instructions
    
    wire [9:0] pc ;
    reg [17:0] instruction ;
    reg [17:0] instructions[2**10-1:0];
    always @(posedge clk)
        instruction <= instructions[pc];
    initial
        $readmemh("instructions.mem", instructions, 0, 2**10-1);

    /// **** cpu
    
    wire [7:0] addr;
    wire [7:0] idata;
    wire [7:0] odata;
     wire we;
    
    kcpsm3 cpu (
        .clk(clk),
        .reset(rst),
        .interrupt(0),
//      .interrupt_ack(ack),
        .address(pc),
        .instruction(instruction),
        .port_id(addr),
        .write_strobe(we),
        .out_port(odata),
//      .read_strobe(re),
        .in_port(idata)
    );

テストベンチ:

LANG:verilog
    `include "psm_disasm.inc"
    string instruction;
    always @(uut.instruction)
        instruction = psm_disasm(uut.instruction);

ディスアセンブラ

インストラクションを渡すと、ディスアセンブルした結果を文字列として返す function です。

アセンブラは xilinx オリジナルの物ではなく、 よりコンパクトな pBlazIDE のものになっています。

読み替えは簡単だと思いますが、 ディスアセンブラの方を直すのもキーワードの置き換えだけなので簡単にできると思います。

psm_disasm.inc:

LANG:verilog
localparam opJump  = 5'b11010;
localparam opCall  = 5'b11000;
localparam opRet   = 5'b10101;

localparam opLoad  = 5'b00000;
localparam opAnd   = 5'b00101;
localparam opOr    = 5'b00110;
localparam opXor   = 5'b00111;
localparam opTest  = 5'b01001;
localparam opAdd   = 5'b01100;
localparam opAddc  = 5'b01101;
localparam opSub   = 5'b01110;
localparam opSubc  = 5'b01111;
localparam opComp  = 5'b01010;
localparam opOut   = 5'b10110;
localparam opStore = 5'b10111;
localparam opIn    = 5'b00010;
localparam opFetch = 5'b00011;

localparam opRetIE = 17'b11100000000000001;
localparam opRetID = 17'b11100000000000000;
localparam opEInt  = 17'b11110000000000001;
localparam opDInt  = 17'b11110000000000000;

localparam opShift = 6'b100000;

function string psm_disasm;
    input [17:0] inst;
    reg [4:0] code;
    string result, s;
    begin
        code = inst[17:13];

        result = "";
        
		// 通常コマンド
        if ( code == opLoad ) result = "LOAD";
        if ( code == opAnd  ) result = "AND";
        if ( code == opOr   ) result = "OR";
        if ( code == opXor  ) result = "XOR";
        if ( code == opTest ) result = "TEST";
        if ( code == opAdd  ) result = "ADD";
        if ( code == opAddc ) result = "ADDC";
        if ( code == opSub  ) result = "SUB";
        if ( code == opSubc ) result = "SUBC";
        if ( code == opComp ) result = "COMP";
        if ( code == opOut  ) result = "OUT";
        if ( code == opStore) result = "STORE";
        if ( code == opIn   ) result = "IN";
        if ( code == opFetch) result = "FETCH";

		// 分岐
        if ( code == opJump ) result = "JUMP";
        if ( code == opCall ) result = "CALL";
        result = {result, " "};
        if ( code == opRet  ) result = "RET";

		// 特殊なコマンド
        if ( inst == opRetIE) result = "RETI ENABLE";
        if ( inst == opRetID) result = "RETI DISABLE";
        if ( inst == opEInt)  result = "EINT";
        if ( inst == opDInt)  result = "DINT";

		// シフト&ローテート
        if ( inst[17:12] == opShift && inst[7:4] == 4'b0000 ) begin
            case ( inst[3:0] )
            5'b1110:    result = "SR0 ";
            5'b1111:    result = "SR1 ";
            5'b1010:    result = "SRX ";
            5'b1000:    result = "SRA ";
            5'b1100:    result = "RR ";
            5'b0110:    result = "SL0 ";
            5'b0111:    result = "SL1 ";
            5'b0010:    result = "SLX ";
            5'b0000:    result = "SLA ";
            5'b0100:    result = "RL ";
            endcase
            $sformat(s, "s%X", inst[11:8]);
            result = {result, s};
        end

        if (result=="" || result==" ") begin
			// 未定義命令
            $sformat(s, "INST $%04x", inst);
            result = s;
        end else begin
			// 分岐の引数
            if( code == opJump ||
                code == opCall ||
                code == opRet  ) begin
                if ( inst[12] != 0 ) begin
                    if (inst[11:10] == 2'b00) result = {result, "Z"};
                    if (inst[11:10] == 2'b01) result = {result, "NZ"};
                    if (inst[11:10] == 2'b10) result = {result, "C"};
                    if (inst[11:10] == 2'b11) result = {result, "NC"};
                    if( code == opJump || code == opCall )
                        result = {result, ", "};
                end
                if( code == opJump || code == opCall ) begin
                    $sformat(s, "$%03x", inst[9:0]);
                    result = {result, s};
                end
            end

			// 通常命令の引数
            if( code == opLoad ||
                code == opAnd  ||
                code == opOr   ||
                code == opXor  ||
                code == opTest ||
                code == opAdd  ||
                code == opAddc ||
                code == opSub  ||
                code == opSubc ||
                code == opComp ||
                code == opOut  ||
                code == opStore||
                code == opIn   ||
                code == opFetch ) begin
                $sformat(s, "s%X, ", inst[11:8]);
                result = {result, s};
                if ( inst[12] != 0 ) begin
                    $sformat(s, "s%X", inst[7:4]);
                end else begin
                    $sformat(s, "$%02x", inst[7:0]);
                end
                result = {result, s};
            end
        end

        psm_disasm = result;
    end
endfunction

あまりちゃんとテストしていません。
動作がおかしかったら教えて下さい。

SystemVerilog のコンパイル方法

上記コードは System Verilog のキーワードである string を使っていますので、 Verilog 2001 ではコンパイルできません。

ModelSim の verilog コンパイラである vlog に -sv というオプションを渡すことで、.v ファイルを SystemVerilog としてコンパイルしてくれるようになりますので、 そのようにして使うことを想定しています。

Xilinx ISE であれば、Simulate Behavioral Model などの文字の上で右クリックから [Process Properties] を選択し、[Other VLOG command line options] に -sv を記入するのが近道です。

コメント


添付ファイル: filepicoblaze_debug.png 1447件 [詳細]

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