Interfaces in QEMU: UART and MMIO Concepts

TL;DR


1. UART on QEMU virt

The virt machine exposes a UART at a fixed MMIO address:

1
UART0 base: 0x10000000

Writing a byte to that address prints a character.

Minimal UART write

1
2
3
4
5
6
7
// src/uart.c (see full file in src/)
#define UART0_BASE 0x10000000u
static volatile u8 *const uart0 = (volatile u8 *)UART0_BASE;

void uart_putc(char c) {
  *uart0 = (u8)c;
}

2. A runnable UART example

1
2
3
4
5
6
7
8
// src/hello_uart.c
#include "types.h"
#include "uart.h"

int main(void) {
  uart_puts("UART is alive!\n");
  return 0;
}

Build and run:

1
2
3
4
5
riscv64-unknown-elf-gcc -O0 -g -ffreestanding -nostdlib \
  -march=rv32im -mabi=ilp32 -T src/link.ld \
  src/start.s src/uart.c src/hello_uart.c -o build/hello_uart.elf

qemu-system-riscv32 -M virt -nographic -bios none -kernel build/hello_uart.elf

3. Conceptual overview: JTAG, SPI, I2C

These are common hardware interfaces, but in this book we only describe them:

Because we run everything in QEMU, we do not require physical wiring, adapters, or boards.


Exercises

  1. Change the UART string and verify the output.
  2. Add a function that prints a hex value with uart_puthex32.
  3. Search the QEMU virt memory map and list one more MMIO device.

Summary