#volatile #pointers #memory #hardware #memory-mapped #raw-pointers

nightly no-std volatile-ptr

I/O设备访问的volatile指针实现

2个版本

使用旧的Rust 2015

0.1.1 2017年3月29日
0.1.0 2017年3月29日

#13 in #volatile

GPL-3.0 许可证

22KB
377

volatile内存操作。

此库包含原始指针的包装以执行volatile内存操作。这对于内存映射I/O非常有用。写入外围内存地址通常会被编译器优化掉。类型Volatile通过包装内存地址以执行volatile操作来强制编译器保持这些内存访问和存储。

Volatile指针的创建通常是不安全的,但您可以在其上执行的实际操作被认为是安全的。这是因为实际的内存操作是通过定义为由Rust标准定义的safe方法的DerefDerefMut特质来执行的。重要的是要记住,Volatile指针几乎与原始指针相同,因此对它的所有解引用操作都应被视为不安全的(即使编译器没有强制执行)。

示例

use volatile::Volatile;

const IO_ADDR: *const u32 = 0x4000_4400 as *const _;

unsafe {
    let mut io_ptr = Volatile::new(IO_ADDR);
    // Some bit that we need to set for an IO operation
    *io_ptr |= 0b1 << 5;
}

在某些嵌入式设备上,您可能希望执行某些操作,例如等待由某些tick计数的量度经过的一定时间。

// Some tick counter that may be updated by a hardware interrupt
static mut TICKS: usize = 0;

while unsafe { TICKS < 10 } {/* wait for ticks to change */}

通常,Rust编译器会将这种类型的操作优化为无限循环loop,因为在单线程环境中,TICKS的值不能改变,但TICKS可能会被某些硬件中断更新,所以我们希望不断重新加载该值以检查它。因此,为了解决这个问题,我们可以使用Volatile指针来强制编译器在每次循环中重新加载该值。

use volatile::Volatile;

static mut TICKS: usize = 0;

unsafe {
    let ticks_ptr = Volatile::new(&TICKS);
    while *ticks_ptr < 10 {/* wait for ticks to change */}
}

现在,在每次循环中都会重新加载TICKS的值。

当与内存映射外围设备一起工作时,您通常有一个包含控制、状态和数据寄存器的内存块,这些寄存器用于某些硬件外围设备。这些通常最好表示为具有每个寄存器作为字段的struct,但在没有volatile操作的情况下,对这些内存地址的加载和存储通常会被编译器优化掉。为了解决这个问题,您可以使用Volatile指针指向映射的地址,并将其表示为正确的类型的struct。

use volatile::Volatile;

const USART_ADDR: *const Usart = 0x4000_4400 as *const _;
// For transmitting and receiving data over serial
#[repr(C)]
struct Usart {
    control_reg: u32,
    status_reg: u32,
    tx_data_reg: u32,
    rx_data_reg: u32,
}

let recieved = unsafe {
    let mut usart_block = Volatile::new(USART_ADDR);
    // Set some bits, these will be hardware specific
    usart_block.control_reg |= 0b11 << 5;

    while usart_block.status_reg & 0b1 << 7 == 0 {/*wait for hardware to set a bit*/}

    // Transmit some data
    usart_block.tx_data_reg = 100;

    while usart_block.status_reg & 0b1 << 6 == 0 {/*wait for hardware to set some other bit*/}

    // Receive some data
    usart_block.rx_data_reg
};

指向结构的每个字段访问都将被视为易变,因此编译器不会优化掉。

与原始指针一样,可以从有效的引用安全地创建Volatile指针,尽管它们的使用仍然应该被视为不安全的。

use volatile::Volatile;

let x: u32 = 0;
let ptr = Volatile::from(&x);

没有运行时依赖