Как написать адрес памяти в Rust?

Я пытаюсь сделать "Blinky" для STM32F1xx в Rust. Я знаю, что для этого существуют библиотеки, но я хочу создать свою собственную "lib" для учебных целей.

Я могу получить доступ к регистрам STM32 по их адресам, подобным этому в C:

*(uint32_t*)(0x40021000 + 0x018) |= 0x10;
*(uint32_t*)(0x40011000 + 0x004) |= 0x33;
*(uint32_t*)(0x40011000 + 0x004) &= ~0xCC;
*(uint32_t*)(0x40011000 + 0x10) |= 0x300;

while(1) {}

Это записывает некоторые биты в регистр RCC_APB2ENR, чтобы включить синхронизацию порта C, настраивает контакты и включает светодиоды в моем обнаружении.

Мне нужно переписать это в Rust, создать consts, fns и начать писать хороший Rusty-код. Возможно ли это в Rust без FFI, вызывающего код C? Могу ли я достичь этого с помощью макроса asm!?

Ответ 1

В C вы должны объявлять свои указатели как volatile при доступе к аппаратным регистрам, чтобы компилятор выполнял обращения точно так же, как вы их программируете. В противном случае он может переупорядочить их или исключить дублирование доступа к одному регистру.

Поскольку Rust 1.9 (благодаря этому RFC), вы можете использовать core::ptr::read_volatile и core::ptr::write_volatile для чтения и записи на такие память.

Если у вас более старая версия Rust, они доступны как volatile_read и volatile_store в core:: intrinsics, которые однако постоянно неустойчивы и, следовательно, требуют ночной версии Rust для доступа к ним.

Ответ 2

Функции read_volatile и write_volatile являются стабильными с версии 1.9, поэтому вы должны использовать их. Заимствование @ker переводило образец для демонстрации:

use std::ptr::{read_volatile, write_volatile};

const A: *mut u32 = (0x40021000 + 0x018) as *mut u32;
const B: *mut u32 = (0x40011000 + 0x004) as *mut u32;
const C: *mut u32 = (0x40011000 + 0x10) as *mut u32;
unsafe {
    write_volatile(A, read_volatile(A) | 0x10);
    write_volatile(B, read_volatile(B) | 0x33);
    write_volatile(B, read_volatile(B) & !0xCC);
    write_volatile(C, read_volatile(C) | 0x300);
}

Кроме того, volatile crate предоставляет типы-оболочки вокруг значений для нестабильного доступа.

use volatile::Volatile;

const A: *mut u32 = (0x40021000 + 0x018) as *mut u32;
const B: *mut u32 = (0x40011000 + 0x004) as *mut u32;
const C: *mut u32 = (0x40011000 + 0x10) as *mut u32;

const volatile_A = A as *mut Volatile<u32>;
const volatile_B = B as *mut Volatile<u32>;
const volatile_C = C as *mut Volatile<u32>;

unsafe {
    (*volatile_A).update(|x| *x | 0x10);
    (*volatile_B).update(|x| *x & !0xCC);
    (*volatile_C).update(|x| *x | 0x300);
}

Ответ 3

rust имеет std::ptr модуль в стандартной библиотеке. Он предлагает такие функции, как ptr::read и ptr::write, которые гораздо более явные, чем разыменование.

Итак, ваш пример будет

const A: *mut u32 = (0x40021000 + 0x018) as *mut u32;
const B: *mut u32 = (0x40011000 + 0x004) as *mut u32;
const C: *mut u32 = (0x40011000 + 0x10) as *mut u32;
unsafe {
    ptr::write(A, ptr::read(A) | 0x10);
    ptr::write(B, ptr::read(B) | 0x33);
    ptr::write(B, ptr::read(B) & !0xCC);
    ptr::write(C, ptr::read(C) | 0x300);
}

Более сжатой версией является использование разыменования, но это работает только для типов Copy:

*A |= 0x10;
*B |= 0x33;
*B &= !0xCC;
*C |= 0x300;