前回少し調査した、テストパタン生成ツールのMicroTESKだが、どのようにして使うのか調査したいので、あらかじめ提供されているモデルを改造してオリジナルのCPUモデルを構築してみようと思う。
関連記事
- オープンソースのマイクロプロセッサ テストプログラム生成ツール MicroTESK 試行
- オープンソースのマイクロプロセッサ テストプログラム生成ツール MicroTESK 試行 (2. NMLファイルを読み解く)
- オープンソースのマイクロプロセッサ テストプログラム生成ツール MicroTESK 試行 (3. オリジナルCPUモデルを作ってパタンを生成する)
最もシンプルなモデル cpu.nml
MicroTESK は NML モデルと呼ばれるモデル記述言語を使ってCPUのモデルを記述している。
これについては例を見てみるほうが速い。 arch/demo/cpu/model/cpu.nml
というファイルが提供されており、それを開いてみる。
中身は大きく分けて以下のように構成されている。
最初にCPUモデルの基本事項を設定する。例えば、以下のように記述されており、レジスタ数は32本、演算の単位は8バイトであることが分かる。 レジスタファイルとしてGPR(8ビット長、16本)を定義し、PCはメモリのサイズに合わせて8ビットだ。
/* * Description: * * Example of a specification of a trivial microprocessor ISA. * Provided to demonstrate various features of nML. */ //////////////////////////////////////////////////////////////////////////////// // Constants let MEM_SIZE = 2 ** 8 // Memory size, 2 to the power of 8 let REG_NUMBER = 16 // Number of general-purpose registers //////////////////////////////////////////////////////////////////////////////// // Types type INDEX = card(8) // 8-bit unsigned integer type NIBBLE = card(4) // 4-bit unsigned integer type BYTE = int(8) // 8-bit signed integer //////////////////////////////////////////////////////////////////////////////// // Registers and Memory // General-purpose registers. reg GPR[REG_NUMBER, BYTE] // Format: <name>[<number>, <type>] // A register for storing the program counter. reg PC[INDEX] // This format means that there is 1 register of type INDEX // A memory line mem M[MEM_SIZE, BYTE]
次に、アドレッシングモードを記述していく。 アドレッシングモードは、主にオペランドに対してどのような値を取ることができるかを設定している。 一般的なRISCマシンであれば、オペランドはレジスタか定数だ。 しかし、x86のようなCISCマシンであればメモリを取ることもできる。このように、アーキテクチャによってサポートしているアドレッシングモードが異なるため、 ここで定義しておく。 また、これらのオペランドが、どの命令でどのように使うことができるかも指定している。
例えば以下のような記述だ。まずは定数として6ビットの定数を定義している。これはオペランドの1つとして定義している。 次にレジスタだ。レジスタは全部で16本のため、レジスタインデックスとしては4ビットを定義している。 最後にメモリアクセスだ。メモリアドレスは6ビット長のアドレスインデックスを定義している。
ここでimage
という値を設定していることに注意してほしい。
これはこれらの記述子が、どのようにオペランドに割り当てられるかを定義したものだ。レジスタは4ビットしか使っていないので、上に2ビットを乗せていることに注意してほしい。
(ランダムテストパタン生成ツールだから、こんな情報いらないじゃないか!?と思うのだが、どうやらパタンを生成するときに内部でシミュレーションを行っているようだ。このために必要なのだろうか?)
ちなみに、このアドレッシングのimage
の値を少しずらしたりすると、実際にパタンを生成するときに思いっきりエラーが吐かれたりするので注意する。
//////////////////////////////////////////////////////////////////////////////// // Addressing Modes // An addressing mode for an immediate value. mode IMM(i: int(6)) = sign_extend(BYTE, i) // Value expression syntax = format("[%d]", i) // Textual format image = format("%6s", i) // Binary format // An addressing mode for a register access. mode REG(i: NIBBLE) = GPR[i] syntax = format("R%d", i) image = format("00%4s", i) // An addressing mode for a memory access. mode MEM(i: card(6)) = M[i] syntax = format("(%d)", i) image = format("%6s", i) // Addressing modes are united into groups. mode OPRNDL = MEM | REG mode OPRNDR = OPRNDL | IMM
次に命令の定義だ。以下のようにして命令のコードと挙動を定義している。
これは説明不要。またimage
がこちらでも定義されていることに注意する。
最後に、ADD
, SUB
, MOV
という3種類の定義した命令をまとめてALU
というオペレーション(コマンド)を定義している。
//////////////////////////////////////////////////////////////////////////////// // Arithmetic and Logic Instructions op ADD() syntax = "add" image = "00" action = { DEST = SRC1 + SRC2; // Function 'trace' prints text messages to the simulator log trace("%d + %d = %d", SRC1, SRC2, DEST); } op SUB() syntax = "sub" image = "01" action = { DEST = SRC1 - SRC2; } op MOV() syntax = "mov" image = "10" action = { DEST = SRC2; } // A common alias for ADD, SUB and MOV op ALU = ADD | SUB | MOV
このALU
コマンドを使用する命令が、以下の定義だ。同様にsyntax
とimage
が定義されていることに注意すること。
これも見ればわかるが、要するにSRC1とSRC2に対してコマンドで定義される演算を実行し、その結果をop1に代入している
(つまりこれは2オペランド命令、 op1 = op1 op op2
の命令系列を定義していることに等しい)
ここで気がつくのは、"%s%s00%s"
という表記だ。コマンドとオペランド2つの組み合わせなわけであるが、上記の通りコマンドは2ビット、オペランドは6ビットであるので、それに00
をくっつけて合計16ビットの命令であることが分かる。
このように命令長もケアしながら定義していかないと、パタン生成時のシミュレーションでエラーを吐いてしまいパタンを生成できない。
// A common specification of all ALU instructions that describes their common // format, performs all common actions and delegates unique responsibilities // to specific operations (or subinstructions). op alu_instr(command: ALU, op1: OPRNDL, op2: OPRNDR) syntax = format("%s %s %s", command.syntax, op1.syntax, op2.syntax) image = format("%s%s00%s", command.image, op1.image, op2.image) action = { SRC1 = op1; SRC2 = op2; command.action; op1 = DEST; PC = PC + 2; }
さらに、同様に制御系の命令の定義が続いている。
//////////////////////////////////////////////////////////////////////////////// // 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) ...
最後に、instruction
を上記のalu_instr
と branch_instr
を組み合わせたものとして定義し、完成だ。
//////////////////////////////////////////////////////////////////////////////// // Entry Point // // By nML conventions, the "instruction" operation is the root of the tree // describing a microprocessor ISA. op instruction = alu_instr | branch_instr