ARM, Carrière, Toolchain

Zero-Cost DMA Abstractions in Embedded Rust with Embassy

Introduction

L’un des bugs les plus insidieux en C embarqué est le buffer aliasing DMA : modifier un buffer pendant qu’un transfert DMA est en cours. Rust élimine cette classe de bugs à la compilation grâce à son système d’ownership.

Le problème en C

uint8_t buffer[64];

void bad_code(void) {
    HAL_UART_Transmit_DMA(&huart1, buffer, 64);
    
    /* BUG : modifie le buffer pendant le transfer DMA ! */
    memset(buffer, 0, 64);
}

La solution Rust — Ownership au service du DMA

#![no_std]
#![no_main]

use embassy_stm32::usart::{self, Uart};
use embassy_executor::Spawner;

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    let p = embassy_stm32::init(Default::default());
    
    let mut uart = Uart::new(
        p.USART1, p.PA10, p.PA9,
        Irqs, p.DMA1_CH1, p.DMA1_CH2,
        usart::Config::default(),
    ).unwrap();

    let buffer: [u8; 64] = [0x41; 64];
    
    // Transfer prend ownership — impossible de modifier buffer
    // pendant le transfer. Le compilateur le garantit.
    uart.write(&buffer).await.unwrap();
}

Transferts DMA parallèles avec join!

use embassy_futures::join::join;

async fn parallel_transfers(
    mut spi: Spi<'static, SPI1, DMA1_CH3, DMA1_CH4>,
    mut uart: Uart<'static, USART1, DMA1_CH1, DMA1_CH2>,
) {
    let spi_data = [0x01u8, 0x02, 0x03, 0x04];
    let uart_data = *b"Hello from Embassy!\r\n";
    
    // Les deux transfers s'exécutent en parallèle
    join(
        spi.write(&spi_data),
        uart.write(&uart_data),
    ).await;
}

Cargo.toml pour STM32

[dependencies]
embassy-stm32 = { version = "0.1", features = [
    "stm32f429zi", "time-driver-any", "exti"
]}
embassy-executor = { version = "0.5", features = [
    "arch-cortex-m", "executor-thread"
]}
embassy-time = { version = "0.3" }
embassy-futures = { version = "0.1" }
defmt = "0.3"
defmt-rtt = "0.4"
panic-probe = { version = "0.3", features = ["print-defmt"] }

Conclusion

Embassy démontre que les garanties de sécurité mémoire de Rust ne sont pas incompatibles avec la programmation embarquée bas-niveau. Le système d’ownership élimine statiquement toute une classe de bugs DMA sans aucun overhead runtime.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *