ModelSim 用 PicoBlaze ディスアセンブラ のバックアップ(No.4)

更新


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

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

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

コメント


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