|
| 1 | +//! UART example for QEMU virt machine |
| 2 | +//! |
| 3 | +//! This example demonstrates direct UART output on QEMU's virt machine. |
| 4 | +//! It writes to the NS16550-compatible UART at 0x1000_0000. |
| 5 | +
|
| 6 | +#![no_std] |
| 7 | +#![no_main] |
| 8 | + |
| 9 | +extern crate panic_halt; |
| 10 | + |
| 11 | +use riscv_rt::entry; |
| 12 | +use riscv_semihosting::debug::{self, EXIT_SUCCESS}; |
| 13 | + |
| 14 | +const UART_BASE: usize = 0x1000_0000; |
| 15 | +const UART_THR: usize = UART_BASE; |
| 16 | +const UART_IER: usize = UART_BASE + 1; |
| 17 | +const UART_FCR: usize = UART_BASE + 2; |
| 18 | +const UART_LCR: usize = UART_BASE + 3; |
| 19 | +const UART_LSR: usize = UART_BASE + 5; |
| 20 | +const LCR_DLAB: u8 = 1 << 7; |
| 21 | +const LCR_8N1: u8 = 0x03; |
| 22 | +const LSR_THRE: u8 = 1 << 5; |
| 23 | + |
| 24 | +unsafe fn uart_write_reg(off: usize, v: u8) { |
| 25 | + (off as *mut u8).write_volatile(v); |
| 26 | +} |
| 27 | + |
| 28 | +unsafe fn uart_read_reg(off: usize) -> u8 { |
| 29 | + (off as *const u8).read_volatile() |
| 30 | +} |
| 31 | + |
| 32 | +fn uart_init() { |
| 33 | + unsafe { |
| 34 | + uart_write_reg(UART_LCR, LCR_DLAB); |
| 35 | + uart_write_reg(UART_THR, 0x01); |
| 36 | + uart_write_reg(UART_IER, 0x00); |
| 37 | + uart_write_reg(UART_LCR, LCR_8N1); |
| 38 | + uart_write_reg(UART_FCR, 0x07); |
| 39 | + } |
| 40 | +} |
| 41 | + |
| 42 | +fn uart_write_byte(b: u8) { |
| 43 | + unsafe { |
| 44 | + while (uart_read_reg(UART_LSR) & LSR_THRE) == 0 {} |
| 45 | + uart_write_reg(UART_THR, b); |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +fn uart_write_str(s: &str) { |
| 50 | + for &b in s.as_bytes() { |
| 51 | + uart_write_byte(b); |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +#[entry] |
| 56 | +fn main() -> ! { |
| 57 | + uart_init(); |
| 58 | + uart_write_str("Hello from UART!\n"); |
| 59 | + debug::exit(EXIT_SUCCESS); |
| 60 | + loop {} |
| 61 | +} |
0 commit comments