gombeのブログ

マイコンの電子工作系PIC32/KiCad/C/C++/3D/

QEMUのsoft-mmuで色々なデバイスを実装してみる

QEMUってなに?

QEMUとはかんたんに言えばエミュレータです。様々なアーキテクチャに対応していて、中間コードを経由することで高速に動作するみたいです。 QEMUの中でも大きな2つの動作モードがあるので説明します

  1. soft-MMU
  2. userモード

1はMMUをソフトウェアで再現することで、kernel部分のシミュレーションが可能になります。VMWareなどの仮想技術と似たような感じですが、QEMUのほうが基礎部分のシミュレーションに向いています。また、2のuserモードのエミュレーションより速度は遅いです。

2のuserモードはユーザーアプリケーションを違うアーキテクチャのマシンで実行するために使います。システムコールなどをネイティブに変換することで動作を高速に行うことができます。その代わり、ユーザー特権で行えることに動作が限定されます。カーネルを動かすことができません。逆にカーネルを用意する必要がありません。

QEMUの特徴としては、GDBでデバックもできるのでこれも結構助かります。 今回はsoft-MMURISC-Vアーキテクチャを用いたk210の超基本的なシミュレータの制作について少しだけ触れます。誰かの助けになるかなぁ? 参考にしたのはLiteBSDを作った人と同じ人がリリースしているPIC32のシミュレータです。

github.com

QEMUの基本構造

QEMUは様々なCPUアーキテクチャをサポートしています。ただ、これらすべてフルスクラッチで書いているわけではなく、中間コードを経由することで効率よくエミュレートしています。 代表的なCPUアーキテクチャはほとんどすでにサポートされています。targetディレクトリの中に各アーキテクチャのコードが入っています。新たに独自のCPUアーキテクチャを開発することがなければ触ることが殆ど無いでしょう。 ハードウェア依存の部分はhwディレクトリにあります。このディレクトリのriscvの中に、このアーキテクチャのCPUのいくつかのハードウェア情報があります。

QEMUでデバイスを作る方法

ではCPUのアーキテクチャ以外にデバイスを具体的に実装する方法の一部だけ示します。 1. デバイスを作るもとを複製せよ

ちなみに著作権はよっぽどのことがない限りGPLなのでそれに従えばOKです。もととなるアーキテクチャと一致するといいと思います。

2. スタートアップルーチンをハンドアセンブルで記述せよ

いや、ハンドアセンブルってなによってなると思いますが、機械語をバイナリで書きます。多くの場合はアーキテクチャで定義されるスタートアドレスがメモリにマッピングされていないため、自分でジャンプする必要があります。 RISC-Vの場合は0x1000番地なので、このアドレスからそれぞれのデバイスのメモリにあるstartにジャンプするためのコードを書きます。幸いなことにRISCVではすでに他のデバイスでも似たようなコードがあったので、参考にさせてもらいました。

3. メモリ設定をせよ

メモリのアドレスマップはそれぞれのデバイスにより大きく異なります。それぞれのデバイスに合わせてメモリマップを作成します。

4. ペリフェラルを実装せよ

ペリフェラル、、結構大変です。ポイントは完全なハードウェアのエミュレーションまで必要ではない場合が多く、その点はバッサリと割り切りながら実装します。 これは長くなりそうなので次のセクションで説明します。

ペリフェラルの実装

UARTやSPI、SDカードといったハードウェアをエミュレートします。IRQ(Interrupt ReQuest)も含みます。

UART

UARTは最も基本的なペリフェラルで、本来はボーレートを正しく合わせなければ通信できませんが、エミュレートする上ではシンプルに送信レジスタアドレスに書き込まれた値を画面に出すだけでOKです。 受信はIRQを生成し、レジスタリードに対してその値を返します。

SPI, SD

これ結構大変です。少しだけTipsを書きます。 SPIドライバはFIFOを実装してその書き込みに対する応答をSDカード側で生成します。 SDカードは、ディスクイメージを予め作成しておき、それにブロックごとにリードライトをします。 SDカードのCSピンがHighになることでコマンドの終了を兼ねているため、これらを正確に読み込むことがコマンドの制御に欠かせません。そのためGPIOの監視も行う必要があります。結構厄介ですね。 Highになっても送信関数が呼び出される可能性があるので、それを無視することも必要になります。正確に言うと、立ち下がりでカウンタをリセットすればOKです。

雑でしたが、こんな感じでデバイスを実装するというところで今日は締めたいと思います。