embassy-rs / embassy

Modern embedded framework, using Rust and async.
https://embassy.dev
Apache License 2.0
5.16k stars 713 forks source link

[embassy-stm32] Help enabling OCTOSPI1 memory mapped mode #3149

Open Luctins opened 2 months ago

Luctins commented 2 months ago

I'm trying to use the memory mapped mode of the OCTOSPI available on the CPU (stm32h563zi) at memory address 0x9000_0000 with a 64mbit QuadSPI (APM6404L) but trying to access this memory range trows a hardfault.

My current attempt is this:

use embassy_stm32::pac::octospi::{Octospi, vals::FunctionalMode};
fn test_ram_raw() {
    const OCTOSPI1_BASE: *mut u8 = 0x9000_0000 as *mut u8;

    // create safe slice from raw memory address and size
    let sram_mem: &mut [_] = unsafe {
        core::slice::from_raw_parts_mut(OCTOSPI1_BASE, 0x80_0000)
    };

    // create inner instance from raw address (obtained from CUBEMX generated code)
    let ospi_base = (0x1001400 + 0x06000000 + 0x40000000) as *mut ();
    let instance = unsafe { Octospi::from_ptr(ospi_base) } ;
    instance.cr().modify(|w| {
        w.set_fmode(FunctionalMode::MEMORYMAPPED);
    });

    // read config reg (cr) to confirm value is correct
    let reg_val = unsafe {
        *(ospi_base as *const u32)
    };
    info!("ospi_reg: {:032b}", reg_val);

    block_on(async { Timer::after_millis(100); });

    let val =  unsafe { *(OCTOSPI1_BASE.wrapping_add(0x10) ) };
    info!("first value: {:02x}", val);

    // for (addr, b) in sram_mem.into_iter().enumerate() {
    //     if addr % 0x1000 == 0 {
    //         debug_print!("\n{:08x}:", OCTOSPI1_BASE as u32 + addr as u32)
    //     }
    //     debug_print!(" {:02x}", *b);
    // }
}

#[embassy_executor::main]
async fn main(spawner: Spawner) {
    let p = hal_setup();

    let mut config = ospi::Config::default();
    config.memory_type = MemoryType::Micron;
    config.device_size = MemorySize::_64MiB;
    config.clock_prescaler = 20; // input clock is 200 Mhz, so 10Mhz
    let mut ospi_dev = ospi::Ospi::new_blocking_quadspi(
        p.OCTOSPI1,
        p.PF10,
        p.PF8,
        p.PF9,
        p.PF7,
        p.PF6,
        p.PB6,
        config
    );
    let t = ospi::TransferConfig {
        iwidth: ospi::OspiWidth::SING,
        adwidth: ospi::OspiWidth::NONE,
        dwidth: ospi::OspiWidth::NONE,
        instruction: Some(0x35),
        address: None,
        dummy: DummyCycles::_0,
        ..Default::default()
    };
    unwrap!(ospi_dev.command(&t).await);
    info!("flash enter quad mode done");
    test_ram_raw();
}

fn hal_setup() -> Peripherals {
    let mut hal_config = embassy_stm32::Config::default();

    hal_config.rcc.hsi = None;
    hal_config.rcc.hsi48 = Some(Default::default()); // needed for RNG
    hal_config.rcc.hse = Some(Hse {
        freq: Hertz(25_000_000),
        mode: embassy_stm32::rcc::HseMode::Oscillator,
    });
    hal_config.rcc.pll2 = Some(Pll {
        source: embassy_stm32::rcc::PllSource::HSE,
        prediv: embassy_stm32::rcc::PllPreDiv::DIV2,
        mul: embassy_stm32::rcc::PllMul::MUL40,
        divr: Some(PllDiv::DIV2),
        divq: Some(PllDiv::DIV2),
        divp: Some(PllDiv::DIV2),
    });
    // hal_config.rcc.mux.
    hal_config.rcc.mux.usart3sel = Usartsel::PLL2_Q;
    hal_config.rcc.mux.octospi1sel = Octospisel::PLL1_Q;
    hal_config.rcc.pll1 = Some(Pll {
        source: embassy_stm32::rcc::PllSource::HSE,
        prediv: embassy_stm32::rcc::PllPreDiv::DIV2,
        mul: embassy_stm32::rcc::PllMul::MUL40,
        divp: Some(PllDiv::DIV2),
        divq: Some(PllDiv::DIV2),
        divr: Some(PllDiv::DIV2),
    });
    use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Sysclk};
    hal_config.rcc.ahb_pre = AHBPrescaler::DIV1;
    hal_config.rcc.apb1_pre = APBPrescaler::DIV1;
    hal_config.rcc.apb2_pre = APBPrescaler::DIV1;
    hal_config.rcc.apb3_pre = APBPrescaler::DIV1;

    hal_config.rcc.sys = Sysclk::PLL1_P;
    hal_config.rcc.voltage_scale = embassy_stm32::rcc::VoltageScale::Scale0;

    embassy_stm32::init(hal_config)
}

Currently I can read and write to the memory chip using command, [blocking_]write and [blocking_]read without problem.

I'm accessing the registers by myself because I couldn't find a high level config to enable memory mapped mode, and most examples I found online were C and accessed the registers directly.

Am I missing something to make this work, like a linker script?

Luctins commented 2 months ago

Update: I got memory mapped mode to work with the following register changes:

const OSPI_BASE: *mut () = (0x1001400 + 0x06000000 + 0x40000000) as *mut ();
const OCTOSPI1_DEV: Octospi = unsafe { Octospi::from_ptr(OSPI_BASE) };

fn test_ram() {
    //...
    while OCTOSPI1_DEV.sr().read().busy() {
        info!("wait ospi busy");
    }

    OCTOSPI1_DEV.ccr().modify(|r| {
        r.set_isize(embassy_stm32::pac::octospi::vals::SizeInBits::_8BIT);
        r.set_adsize(embassy_stm32::pac::octospi::vals::SizeInBits::_24BIT);
        r.set_admode(embassy_stm32::pac::octospi::vals::PhaseMode::FOURLINES);
        r.set_imode(embassy_stm32::pac::octospi::vals::PhaseMode::FOURLINES);
        r.set_dmode(embassy_stm32::pac::octospi::vals::PhaseMode::FOURLINES);
    });

    OCTOSPI1_DEV.cr().modify(|r| {
        r.set_fmode(FunctionalMode::MEMORYMAPPED);
        r.set_dmaen(false);
        r.set_en(true);
    });

    OCTOSPI1_DEV.tcr().modify(|r| {
        r.set_dcyc(6);
    });
    //...
}

But I would really like to know if there's an officially supported way to do this.