如何确保对内存映射外设的内存访问是不稳定的?

时间:2017-06-12 23:22:08

标签: rust

在嵌入式编程中,可以通过放置在正确地址的结构访问存储器映射的外设。如何确保在Rust中的总线上确实执行了对外设的存储器访问?在C中,这可以通过创建变量或字段volatile来完成。

考虑这个例子:

#[repr(C)]
struct Periph {
    sr: u32, /* Status  */
    cr: u32, /* Control */
    dr: u32, /* Data    */
}

const PERIPH1: *mut Periph = 0x10001200 as *mut Periph;
const PERIPH2: *mut Periph = 0x10001400 as *mut Periph;

fn useperiph(p: &mut Periph) -> i32 {
    p.cr = CR_CONSTANT;
    if p.cr != CR_CONSTANT {
        /* Peripheral was not enabled */
        return -1;
    }

    /* Loop a few micro seconds until ready */
    while p.sr != SR_CONSTANT {}

    /* Write values to hardware FIFO */
    p.dr = DATA1;
    p.dr = DATA2;
    p.dr = DATA3;
    0
}

我需要确保编译器不会删除对控制寄存器的检查,将保留状态寄存器的所有负载,并且不会将三个存储器折叠到数据寄存器。我该怎么做?

1 个答案:

答案 0 :(得分:0)

我所知道的最好的方法是使用类似于Zinc的volatile_cell库的东西。内存映射外设寄存器的结构具有VolatileCell<u32>类型的寄存器,而不是普通的u32VolatileCell包含一个值,具有相同的大小,并提供使用core::ptr::read_volatilecore::ptr::write_volatile的访问者。简而言之,VolatileCell看起来像这样:

#[repr(C)]
pub struct VolatileCell<T> {
    value: T,
}

impl<T> VolatileCell<T> {

    /// Get register value.
    #[inline]
    pub fn get(&self) -> T {
        unsafe {
            read_volatile(&self.value)
        }
    }

    /// Set register value.
    #[inline]
    pub fn set(&self, value: T) {
        unsafe {
            write_volatile(&self.value as *const T as *mut T, value)
        }
    }
}

然后使用VolatileCell<u32>字段写入外设寄存器结构:

#[repr(C)]
struct Periph {
    sr: VolatileCell<u32>, /* Status  */
    cr: VolatileCell<u32>, /* Control */
    dr: VolatileCell<u32>, /* Data    */
}

const PERIPH1: *mut Periph = 0x10001200 as *mut Periph;
const PERIPH2: *mut Periph = 0x10001400 as *mut Periph;

fn useperiph(p: &mut Periph) -> i32 {
    p.cr.set(CR_CONSTANT);
    if p.cr.get() != CR_CONSTANT {
        /* Peripheral was not enabled */
        return -1;
    }

    /* Loop a few micro seconds until ready */
    while p.sr.get() != SR_CONSTANT {}

    p.dr.set(DATA1);
    p.dr.set(DATA2);
    p.dr.set(DATA3);
    0
}

生成的代码具有正确的语义,并且比直接使用u32稍微冗长一些。显而易见可以说是一个好处。