LPC812(Cortex-M0+,V6Mアーキテクチャ)のエミュレータを試作してみました。
とりあえず、mbed_blinky(Lチカ)のバイナリを動かすまでは出来ました。
mbed LPC1768で動作します:https://developer.mbed.org/users/va009039/code/emu812/
pythonでの検証
まずは、動作速度は関係なく、V6Mアーキテクチャを理解するためにpythonで書き初めました。
ステップ実行のレジスタやメモリアクセスの様子をログに残すようにして、他の言語で書いたものと機械的に比較して間違いを検出しやすくするようにします。
大まかな動作は次のとおりです。
プログラムカウンタが指すアドレスから命令を取り出す。
ジャンプテーブルで命令の上位バイトから256通りに分岐します。
命令をデコードしてレジスタにアクセスするための添字を求めます。
演算(データ処理)またはデータ転送をします。
ゼロフラグ(Z)、符号フラグ(N)は演算結果から求める事はできる。
キャリーフラグ(C )、オーバーフローフラグ(V)はどうするのだろうと調べてみたら、演算前の最上位ビットと演算後の最上位ビットから求める事ができるのがわかりました。
ADD Rd,Rn,Rm のそれぞれRd,Rn,Rmの最上位ビットからテーブルを引いています。
ADD Rd,#imm レジスタとイミディエートとの演算、ADD Rd,Rn レジスタとレジスタの演算を共通化するためにイミディエート用の仮のレジスタを作りました。
命令をデコードしたときにイミディエートの値をR[17]に入れます。
つまり、 R0 = R1 + #imm は R0 = R1 + R17 に換えて演算処理に渡します。
演算結果を求めるのでなくてフラグの結果だけを知りたい比較命令CMPと減算命令SUBを共通化します。
比較命令を Rd = Rn - Rm に共通化するために 命令のデコードでRd をR[18]に書き換えて演算結果を捨てるようにします。
pythonでのLPC810エミュレータです:https://bitbucket.org/va009039/emu810
C++への半自動変換
pythonで書いたプログラムをC++に書きなおすのですが同じような作業の繰り返しなので簡単な文字列置換のプログラムを作ってC++へまとめて変換します。
pythonでは冗長なselfでのメソッド変数へのアクセスは文字列置換には好都合です。
ジャンプテーブルの自動作成のために命令範囲をコメントとして書いています。同じ形式の関数名にして抽出しやすいようにしています。
変換に作ったプログラムです: conv.py
ペリフェラル
初期設定を進めるためにPLLがロックしたとみなすステータスを返すようにします。
wait()タイマーはMRTを使っているので適当な間隔でTIMER0を進めます。
GPIOはDIRレジスタをみて出力だけをホストMCUに渡すようにしています。
LPC1114版も作ってみました: https://developer.mbed.org/users/va009039/code/emu1114/
参考文献
- ARMプロセッサ―32ビットRISCのシステム・アーキテクチャ (DesignWaveBooksシリーズ)
- 第3回ローレイヤー勉強会 : FPGAでコンピュータを作ってみた
- Cortex-M0 emulator on ATTiny85
---