ModelSim 用 PicoBlaze ディスアセンブラ の変更点

更新


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

#contents

* Xilinx が ModelSim の無償版&廉価版の配布を中止との情報 [#t85996f6]

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

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

* ものすごい無駄骨 [#u85c17e5]

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

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

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

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

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

* ModelSim を用いた PicoBlaze プログラムのデバッグ [#u0c14a4d]

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

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

&attachref(picoblaze_debug.png);

注目は instruction の部分です。

* 結構簡単 [#m7a629a1]

やっているのは、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);

* ディスアセンブラ [#l0b8eae6]

インストラクションを渡すと、ディスアセンブルした結果を文字列として返す 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 のコンパイル方法 [#if52ec61]

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

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

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


* コメント [#r7b96274]

#comment_kcaptcha

Counter: 6976 (from 2010/06/03), today: 2, yesterday: 0