2016-01-26 22 views
10

Sto cercando di rendere "Blinky" per STM32F1xx in Rust. So che ci sono lib per questo, ma voglio fare la mia "lib" per scopi di apprendimento.Come scrivere in un indirizzo mappato in memoria in Rust?

posso accedere ai "registri" del STM32 dai loro indirizzi come questo in C:

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

while(1) {} 

Questo scrive alcuni bit al registro RCC_APB2ENR per consentire clock della porta C, configura perni e consente LED sulla mia scoperta.

Ho bisogno di riscriverlo in Rust, per creare consts, fns e iniziare a scrivere un bel codice Rusty. E 'possibile in Rust senza FFI chiamare il codice C? Posso ottenere questo risultato con la macro asm!?

risposta

8

In C si dovrebbe dichiarare i puntatori come volatile quando accedere ai registri hardware, in modo che il compilatore esegua gli accessi esattamente come li programmi. Altrimenti potrebbe riordinarli o eliminare gli accessi duplicati allo stesso registro.

Da Rust 1.9 (grazie a questo RFC) è possibile utilizzare core::ptr::read_volatile e core::ptr::write_volatile per leggere e scrivere in tale memoria.

Se si dispone di una versione precedente di Rust, questi sono disponibili come volatile_read e volatile_store in core::intrinsics, che però sono permanentemente instabile, e quindi richiedono una versione nightly di Rust per accedervi.

+0

Il comportamento del compilatore per i puntatori non volatili è lo stesso per C e Rust? – fevgenym

+0

Sì, il compilatore di Rust può fare le stesse ottimizzazioni. Il backend LLVM che fa questo è usato per entrambe le lingue. – starblue

1

ruggine ha il modulo std::ptr nella libreria standard. Offre funzioni come ptr::read e ptr::write che sono molto più esplicite del dereferenziazione.

Così il vostro esempio può essere

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); 
} 

La versione più concisa è quello di utilizzare dereferenziazione, ma che funziona solo per Copy tipi:

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

Le funzioni read_volatile e write_volatile sono stabili dalla versione 1.9, quindi è consigliabile utilizzarle. campione tradotto Prestito di @ ker per la dimostrazione:

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); 
} 

Inoltre, la cassa volatile fornisce tipi wrapper per i valori per l'accesso volatile.

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); 
} 
+0

ottime notizie! Grazie – fevgenym

Problemi correlati