Accessing Hardware in Rust

⭐⭐⭐⭐ (4星) 探索时间: 2026-03-20
来源: Ferrous Systems
标签: Rust 嵌入式 裸金属 MMIO

摘要

Ferrous Systems 详述了在 Rust 中访问硬件的三种主要方式:I/O 端口、系统寄存器和内存映射 I/O,以及位字段操作的最佳实践。

核心内容

1. I/O 端口 (x86)

老式 x86 架构使用专用 IN/OUT 指令访问 I/O 空间。

use x86_64::instructions::port::Port;

unsafe {
    let mut port = Port::new(0xf4);
    port.write(exit_code as u32);
}

2. 系统寄存器 (Arm/RISC-V)

系统寄存器是处理器内部特殊功能的寄存器,需要特殊指令访问。

32-bit Arm (协处理器):

core::arch::asm!("mcr p15, 0, {r}, c0, c0, 5", 
    r = out(reg) value, options(nomem, nostack));

AArch64 (命名寄存器):

core::arch::asm!("mrs {r:w}, MPIDR_EL1", 
    r = out(reg) value, options(nomem, nostack));

RISC-V (CSR):

core::arch::asm!("csrrs {r}, 0xF14, x0", 
    r = out(reg) value, options(nomem, nostack));

3. 内存映射 I/O (MMIO)

现代系统将外设映射到内存地址空间。

重要提示: 永远不要对 MMIO 地址使用引用!Rust 编译器假设引用是可解引用的,会导致 LLVM 优化错误。

正确方式 - volatile 写入:

const UART0_TRANSMIT_FIFO: *mut u32 = 0xE020_5000 as *mut u32;
unsafe { UART0_TRANSMIT_FIFO.write_volatile(byte as u32); }

错误方式 - 引用 (Unsound!):

let uart_ref: &Uart = unsafe { &*(0xE020_5000 as *mut Uart) };
// 不要这样做!

4. 位字段操作

使用闭包 API 实现 Zero-Cost Abstraction:

impl Uart {
    pub fn modify_ifls<F>(&mut self, f: F) 
    where F: FnOnce(&mut Uartifls) {
        let mut value = self.read_ifls();
        f(&mut value);
        self.write_ifls(value);
    }
}

关键工具

  • svd2rust - 从 Arm SVD 文件生成 PAC
  • tock-registers - Tock 操作系统的寄存器抽象
  • safe-mmio + bitflags - 安全 MMIO 封装
  • derive-mmio + bitbybit - 派生宏方案

评价

亮点:

  • 实战经验总结
  • 解释 volatile 和引用问题
  • Zero-Cost Abstraction 示例
  • 文档最佳实践

适用人群: 嵌入式 Rust 开发者、操作系统爱好者、硬件接口工程师