UARTのモジュールを追加して、xv6の実行時の動作を観察できるようにしよう。 xv6のスタートアップルーチンを見てみると、UARTの初期化をしている。 さらに、uart.cを見てみると、Intel 8250というUARTのモジュールを想定していることが分かった。
そこで、まずはISSにUARTのモジュールを接続してみる。 基本的にこれらのモジュールはアドレスマップに追加されているのだが、残念ながら今のISSには、外部のペリフェラルにアクセスしに行く手段がない。 つまり、LW/SWなどを叩くと、問答無用でmemoryに対してアクセスが発生するようになっている。
そこで、一段かませて、アドレスに従って、メモリに行くか、別のユニットにアクセスが飛ぶかを制御するようにした。 本来はここでもメモリモジュールか、それ以外かをベクタか何かで検索して切り分けるようにしないと汎用性が得られないのだが、とりあえずここでは一緒にしている。これは後で直そう。
MemResult MipsEnv::LoadFromBus (Addr_t addr, Size_t size, Word_t *data) { if (addr >= 0xb0000000 && addr <= 0xb4ffffff) { return m_uart->LoadData (addr, size, data); } else { return LoadMemory (addr, size, data); } } MemResult MipsEnv::StoreToBus (Addr_t addr, Word_t data, Size_t size) { if (addr >= 0xb0000000 && addr <= 0xb4ffffff) { return m_uart->StoreData (addr, data, size); } else { return StoreMemory (addr, data, size); } }
そして、Uartのモジュールを作成して、追加しておく。 システムレジスタあたりは、何も実装されていないが、とりあえずデータアクセスをするとメッセージを出すようにしておく。
MemResult ModuleUart::LoadData (Addr_t addr, Size_t size, Word_t *data) { m_env->DebugPrint ("<UART: Load (%08x)>\n", addr); if (addr == 0xb40003fd) { // *data = 0x00000020; } else { *data = 0x00000000; } return MemNoExcept; } MemResult ModuleUart::StoreData (Addr_t addr, Word_t data, Size_t size) { m_env->DebugPrint ("<UART: Store (%08x)>\n", addr, data, static_cast<char>(data & 0x00FFU)); if (addr == 0xb40003f8) { fprintf (m_uart_fp, "%c", static_cast<char>(data & 0x00FFU)); } return MemNoExcept; }
これで実行してみると、xv6のスタートアップから、UARTのモジュールが起動し、最初のメッセージとして、"xv6..."というメッセージを出力していることが分かる。
$ ~/swimmer_riscv/build_mips/swimmer_mips --binfile ./kernel --max 10000 --init_pc 80100000 --debug --out debug.log --debug_func --debug_gvar xv6...
とりあえずメッセージは表示されるようになったから、デバッグはし易くなったかな? ここらへんのペリフェラルのレジスタとかも、半自動生成できるようなフレームワークを作っておきたいなあ...