FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

オープンソースのマイクロプロセッサ テストプログラム生成ツール MicroTESK 試行 (3. オリジナルCPUモデルを作ってパタンを生成する)

前回少し調査した、テストパタン生成ツールのMicroTESKだが、簡単なオリジナルCPUモデルを作ってテストパタンを生成してみたい。

関連記事

オリジナルCPUモデルを作る

オリジナルCPUモデルを作るために、簡易モデルであるcpuをコピーして以下のようなディレクトリを作った。simple_riscv とした。まだ全然RISC-Vじゃないけど。

microtesk-2.4.39-beta-180219$ tree arch/simple_riscv/
arch/simple_riscv/
├── model
│   ├── Makefile
│   └── simple_riscv.nml
├── settings.xml
└── templates
    ├── Makefile
    ├── cpu_base.rb
    ├── example.rb
    └── run.sh

2 directories, 7 files

スクリプトファイルrun.shMakefileに記述してあるモデル名"cpu"は、徹底的に"simple_riscv" に変換していく。

ARCH=simple_riscv

all:
        sh $(MICROTESK_HOME)/bin/compile.sh $(ARCH).nml
  • arch/simple_riscv/templates/run.sh
#!/bin/bash
sh $MICROTESK_HOME/bin/generate.sh simple_riscv $1.rb --code-file-prefix $1 --code-file-extension s -v -sd 1>$1.stdout 2>$1.stderr

オリジナルCPUモデル"simple_riscv"の改造

参考にしたCPUモデル"cpu"は、2オペランドで、ソースレジスタ1と書き込みレジスタが共有されているシンプルなモデルだ。 これでは使い物にならないので、3オペランド指定できるモデルに改造したい。

このために、モデルファイル simple_riscv.nml を変更した。 オリジナルのCPUモデルは、2項演算と1項演算(MOV命令)が同じALUカテゴリで定義されているため、これを分離して2オペランド命令と1オペランド命令に分類する。

op alu_instr(command: ALU, dst: OPRNDL, op1: OPRNDR, op2: OPRNDR)
  syntax = format("%s %s %s %s", command.syntax, dst.syntax, op1.syntax, op2.syntax)
  image  = format("%s%s00%s0001000100010001", command.image, op1.image, op2.image)
  action = {
    SRC1 = op1;
    SRC2 = op2;
    command.action;
    op1 = DEST;
    PC = PC + 4;
  }


op op1_instr (command: ALU_op1, dst: OPRNDL, op1: OPRNDR)
  syntax = format("%s %s %s", command.syntax, dst.syntax, op1.syntax)
  image  = format("%s%s00%s0010001000100010", command.image, dst.image, op1.image)
  action = {
    SRC1 = op1;
    command.action;
    dst = DEST;
    PC = PC + 4;
  }

imageフォーマットが、無駄に長くしていることに注意。32ビット分用意している。 これは、オリジナルのCPUモデルは16bit命令長であったものの、最終的なRISC-Vでの発展系を考慮して32ビットに拡張した。 このため、PCの進め方も+2ではなく+4としている。

テストプログラムテンプレート example.rb の変更

3オペランドになったので、テストパタンも変更する。 適当に、レジスタを増やした (基本 op1=op1+op2 で記述してあったので、書き込みレジスタアドレスを、読み込みレジスタアドレス1としてコピーしただけだ。)。

  • example.rb
...
    ############################################################################
    # Data generation

    # All registers are filled with zeros.
    add reg(1), reg(1), reg(2) do situation('zero', :size => 8) end

    # Random registers are filled with random values.
    # add reg(_), reg(_) do situation('random', :size => 8, :min_imm => 0, :max_imm => 15) end
    # newline

    # 'Normal' and 'Overflow' situations for integer addition.
    add reg(3), reg(3), reg(4) do situation('add', :case => 'normal', :size => 8) end
    add reg(5), reg(5), reg(6) do situation('add', :case => 'overflow', :size => 8) end
    newline
...

テストパタンを生成する

まずは、CPUモデルをコンパイルする。結構エラーがでるので、慎重に拡張する必要がある。

sh bin/compile.sh arch/simple_riscv/model/simple_riscv.nml

つぎに、テストパタンを生成する。テストパタンはシミュレーションをしながら生成されるので、シミュレーションに失敗するとパタンを生成せずに停止してしまう。 だけど何故だ?まだログの解析方法が分からないので、うまくパタンを生成できない。

sh bin/generate.sh simple_riscv arch/simple_riscv/templates/example.rb
Loaded template CpuBaseTemplate defined in /home/msyksphinz/work/training/program/microtesk-2.4.39-beta-180219/arch/simple_riscv/templates/cpu_base.rb
Loaded template ExampleTemplate defined in /home/msyksphinz/work/training/program/microtesk-2.4.39-beta-180219/arch/simple_riscv/templates/example.rb
Processing template ExampleTemplate defined in /home/msyksphinz/work/training/program/microtesk-2.4.39-beta-180219/arch/simple_riscv/templates/example.rb...
Instance number: 1
Warning: Failed to load the MMU model. Physical memory will be accessed directly.
Error: Simulation error. No executable code at 0x0000000000000000.
Generation was aborted.
Generation Statistics
Generation time: 0.186 seconds
Generation rate: 381 instructions/second
Programs/stimuli/instructions: 0/4/71

いろんなオプションでデバッグをすることができる。

  • -ns シミュレーションを実行しない。とりあえずテストパタンを生成できる。
  • -g -dp テストパタンログを生成する。基本的にテストパタンのデバッグはこれで行う?
  • -gb バイナリデータを生成する。

一応、バイナリデータ的には問題無さそうなんだけどな... なんでだろう?引き続き解析する。

$ hexdump test_0000.bin
0000000 2222 8c0f 2222 800f 2222 8100 2222 820f
0000010 2222 8302 2222 8a05 1111 040a 1111 050a
0000020 1111 0102 1111 0304 1111 0506 1111 4708
0000030 1111 470a 2222 8b05 2222 8c01 4444 cb00
0000040 1111 4b0c 1333 0080 2222 8c00 2222 8628
0000050 1111 0606 1111 0606 1111 0603 2222 852f
0000060 1111 0505 1111 0505 1111 0503 2222 8327
0000070 2222 8428 1111 4605 1111 0304 2222 8338
0000080 1111 0303 1111 0303 1111 0300 2222 841c
0000090 1111 0404 1111 0404 1111 0400 2222 8632
00000a0 1111 0606 1111 0606 1111 0601 2222 852a
00000b0 1111 0505 1111 0505 1111 0501 1111 4304
00000c0 1111 4605 2222 8613 1111 0606 1111 0606
00000d0 1111 0602 2222 852b 1111 0505 1111 0505
00000e0 1111 0502 2222 832c 1111 4605 2222 8403
00000f0 2222 8631 1111 0606 1111 0606 1111 0602
0000100 2222 8329 1111 0303 1111 0303 1111 0303
0000110 2222 842a 1111 0404 1111 0404 1111 0400
0000120 2222 8706 1111 0304 2222 862d 1111 0606
0000130 1111 0606 1111 0602 2222 831a 1111 0303
0000140 1111 0303 1111 0300 2222 8435 1111 0404
0000150 1111 0404 1111 0400 2222 8706 1111 4304
0000160 2222 8338 1111 0303 1111 0303 1111 0300
0000170 2222 8612 1111 0606 1111 0606 1111 0601
0000180 2222 8403 2222 8706 2222 9717
000018c

追記。ちゃんとジャンプアドレスについても、メモリサイズの拡張を行った分だけアドレスを拡張しなければならないらしい。 ちゃんと作りこまなければならないので大変。

////////////////////////////////////////////////////////////////////////////////
// Control Transfer Instructions

// Transfers control to the specified address.
//
// IMPORTANT NOTE: The target address can be specified via a label. In order to
// use the label name rather than an address constant in the generated test
// program, special label-based addressing modes must be described in
// the specifications.

label mode J_LABEL(value: INDEX) = value
  syntax = ""
  image  = format("%s", value)

mode J_IMM(value: INDEX) = value
  syntax = format("0x%X", value)
  image  = format("%s", value)

mode J_ADDR = J_LABEL | J_IMM

op J (target : J_ADDR)
  syntax = format("j %s", target.syntax)
  image  = format("01%s0110011", target.image)    # ここのオペランドを縮める。
  action = { PC = target; }

// Transfers control to the specified address if source equals 0.
op JZ (source: OPRNDL, target: J_ADDR)
  syntax = format("jz %s", target.syntax)
  image  = format("11%s%s01000100", source.image, target.image)  # ここのオペランドを縮める。
  action = {
    if source == 0 then
      PC = target;
    else
      PC = PC + 4;
    endif;
  }

op branch_instr = J | JZ

実行結果:

sh bin/compile.sh arch/simple_riscv/model/simple_riscv.nml
sh bin/generate.sh -dp  simple_riscv arch/simple_riscv/templates/simple.rb