From 90404a8e524a0d06ce35230dea7ff4d3e4d0375a Mon Sep 17 00:00:00 2001 From: ragarnoy Date: Sat, 10 May 2025 02:17:54 +0200 Subject: Add intercore communication examples for STM32H755CM4 and CM7, does not work in release for now (for some reason) --- examples/stm32h755cm4/Cargo.toml | 17 +-- examples/stm32h755cm4/src/bin/intercore.rs | 181 +++++++++++++++++++++++ examples/stm32h755cm7/Cargo.toml | 15 +- examples/stm32h755cm7/src/bin/intercore.rs | 226 +++++++++++++++++++++++++++++ 4 files changed, 415 insertions(+), 24 deletions(-) create mode 100644 examples/stm32h755cm4/src/bin/intercore.rs create mode 100644 examples/stm32h755cm7/src/bin/intercore.rs diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index 7c17bc766..c6d4996f1 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml @@ -15,8 +15,8 @@ embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defm embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -defmt = "0.3" -defmt-rtt = "0.4" +defmt = "1.0.1" +defmt-rtt = "1.0.0" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" @@ -25,7 +25,7 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = { version = "1.0" } embedded-nal-async = "0.8.0" embedded-io-async = { version = "0.6.1" } -panic-probe = { version = "0.3", features = ["print-defmt"] } +panic-probe = { version = "1.0.0", features = ["print-defmt"] } heapless = { version = "0.8", default-features = false } rand_core = "0.6.3" critical-section = "1.1" @@ -37,13 +37,6 @@ chrono = { version = "^0.4", default-features = false } grounded = "0.2.0" # cargo build/run -[profile.dev] -codegen-units = 1 -debug = 2 -debug-assertions = true # <- -incremental = false -opt-level = 3 # <- -overflow-checks = true # <- # cargo test [profile.test] @@ -60,8 +53,8 @@ codegen-units = 1 debug = 2 debug-assertions = false # <- incremental = false -lto = 'fat' -opt-level = 3 # <- +#lto = 'fat' +#opt-level = 3 # <- overflow-checks = false # <- # cargo test --release diff --git a/examples/stm32h755cm4/src/bin/intercore.rs b/examples/stm32h755cm4/src/bin/intercore.rs new file mode 100644 index 000000000..08cf6c7b9 --- /dev/null +++ b/examples/stm32h755cm4/src/bin/intercore.rs @@ -0,0 +1,181 @@ +#![no_std] +#![no_main] + +// IMPORTANT: This must match EXACTLY the definition in CM7! +mod shared { + use core::sync::atomic::{AtomicU32, Ordering}; + + /// Shared LED state between CM7 and CM4 cores + #[repr(C, align(4))] + pub struct SharedLedState { + // Magic number for validation + pub magic: AtomicU32, + // Counter for synchronization testing + pub counter: AtomicU32, + // LED states packed into a single atomic + pub led_states: AtomicU32, + } + + // Bit positions in led_states + pub const GREEN_LED_BIT: u32 = 0; + pub const YELLOW_LED_BIT: u32 = 1; + + impl SharedLedState { + pub const fn new() -> Self { + Self { + magic: AtomicU32::new(0xDEADBEEF), // Magic number + counter: AtomicU32::new(0), + led_states: AtomicU32::new(0), + } + } + + /// Set LED state using safe bit operations + #[inline(never)] + pub fn set_led(&self, is_green: bool, state: bool) { + let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT }; + + // Use bit operations to avoid complex atomic operations + let current = self.led_states.load(Ordering::SeqCst); + + let new_value = if state { + current | (1 << bit) // Set bit + } else { + current & !(1 << bit) // Clear bit + }; + + self.led_states.store(new_value, Ordering::SeqCst); + core::sync::atomic::compiler_fence(Ordering::SeqCst); + } + + /// Get LED state using safe bit operations + #[inline(never)] + pub fn get_led(&self, is_green: bool) -> bool { + let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT }; + + let value = self.led_states.load(Ordering::SeqCst); + core::sync::atomic::compiler_fence(Ordering::SeqCst); + + (value & (1 << bit)) != 0 + } + + /// Increment counter safely + #[inline(never)] + pub fn increment_counter(&self) -> u32 { + let current = self.counter.load(Ordering::SeqCst); + let new_value = current.wrapping_add(1); + self.counter.store(new_value, Ordering::SeqCst); + core::sync::atomic::compiler_fence(Ordering::SeqCst); + new_value + } + + /// Get counter without incrementing + #[inline(never)] + pub fn get_counter(&self) -> u32 { + let value = self.counter.load(Ordering::SeqCst); + core::sync::atomic::compiler_fence(Ordering::SeqCst); + value + } + } + + #[link_section = ".ram_d3"] + pub static SHARED_LED_STATE: SharedLedState = SharedLedState::new(); + + // SRAM4 memory region constants for MPU configuration + pub const SRAM4_BASE_ADDRESS: u32 = 0x38000000; + pub const SRAM4_SIZE_LOG2: u32 = 15; // 64KB = 2^(15+1) + pub const SRAM4_REGION_NUMBER: u8 = 0; // MPU region number to use +} + +use core::mem::MaybeUninit; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::SharedData; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +// Use our shared state from the module +use shared::SHARED_LED_STATE; + +#[link_section = ".ram_d3"] +static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); + +#[embassy_executor::task] +async fn blink_heartbeat(mut led: Output<'static>) { + loop { + led.toggle(); + info!("CM4 heartbeat"); + Timer::after_millis(500).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) -> ! { + // Initialize the secondary core + let p = embassy_stm32::init_secondary(&SHARED_DATA); + info!("CM4 core initialized!"); + + // Read the magic value to ensure shared memory is accessible + let magic = SHARED_LED_STATE.magic.load(core::sync::atomic::Ordering::SeqCst); + info!("CM4: Magic value = 0x{:X}", magic); + + // Initialize LEDs + let mut green_led = Output::new(p.PB0, Level::Low, Speed::Low); // LD1 + let mut yellow_led = Output::new(p.PE1, Level::Low, Speed::Low); // LD2 + let red_led = Output::new(p.PB14, Level::Low, Speed::Low); // LD3 (heartbeat) + + // Start heartbeat task + unwrap!(spawner.spawn(blink_heartbeat(red_led))); + + // Previous values for detecting changes + let mut prev_green = false; + let mut prev_yellow = false; + let mut prev_counter = 0; + + info!("CM4: Starting main loop"); + loop { + // Read values from shared memory + let green_state = SHARED_LED_STATE.get_led(true); + let yellow_state = SHARED_LED_STATE.get_led(false); + let counter = SHARED_LED_STATE.get_counter(); + + // Check for state changes + let green_changed = green_state != prev_green; + let yellow_changed = yellow_state != prev_yellow; + let counter_changed = counter != prev_counter; + + // If any state changed, log it and update LEDs + if green_changed || yellow_changed || counter_changed { + if counter_changed { + info!("CM4: Counter = {}", counter); + prev_counter = counter; + } + + // Update LED states + if green_changed { + if green_state { + green_led.set_high(); + info!("CM4: Green LED ON"); + } else { + green_led.set_low(); + info!("CM4: Green LED OFF"); + } + prev_green = green_state; + } + + if yellow_changed { + if yellow_state { + yellow_led.set_high(); + info!("CM4: Yellow LED ON"); + } else { + yellow_led.set_low(); + info!("CM4: Yellow LED OFF"); + } + prev_yellow = yellow_state; + } + } + + // Poll at a reasonable rate + Timer::after_millis(10).await; + } +} \ No newline at end of file diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index 3186929a8..06a3b06af 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml @@ -15,8 +15,8 @@ embassy-net = { version = "0.7.0", path = "../../embassy-net", features = ["defm embassy-usb = { version = "0.4.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -defmt = "0.3" -defmt-rtt = "0.4" +defmt = "1.0.1" +defmt-rtt = "1.0.0" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" @@ -25,7 +25,7 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = { version = "1.0" } embedded-nal-async = "0.8.0" embedded-io-async = { version = "0.6.1" } -panic-probe = { version = "0.3", features = ["print-defmt"] } +panic-probe = { version = "1.0.0", features = ["print-defmt"] } heapless = { version = "0.8", default-features = false } rand_core = "0.6.3" critical-section = "1.1" @@ -36,15 +36,6 @@ static_cell = "2" chrono = { version = "^0.4", default-features = false } grounded = "0.2.0" -# cargo build/run -[profile.dev] -codegen-units = 1 -debug = 2 -debug-assertions = true # <- -incremental = false -opt-level = 3 # <- -overflow-checks = true # <- - # cargo test [profile.test] codegen-units = 1 diff --git a/examples/stm32h755cm7/src/bin/intercore.rs b/examples/stm32h755cm7/src/bin/intercore.rs new file mode 100644 index 000000000..154b1682b --- /dev/null +++ b/examples/stm32h755cm7/src/bin/intercore.rs @@ -0,0 +1,226 @@ +#![no_std] +#![no_main] + +mod shared { + use core::sync::atomic::{AtomicU32, Ordering}; + + /// Shared LED state between CM7 and CM4 cores + #[repr(C, align(4))] + pub struct SharedLedState { + // Magic number for validation + pub magic: AtomicU32, + // Counter for synchronization testing + pub counter: AtomicU32, + // LED states packed into a single atomic + pub led_states: AtomicU32, + } + + // Bit positions in led_states + pub const GREEN_LED_BIT: u32 = 0; + pub const YELLOW_LED_BIT: u32 = 1; + + impl SharedLedState { + pub const fn new() -> Self { + Self { + magic: AtomicU32::new(0xDEADBEEF), // Magic number + counter: AtomicU32::new(0), + led_states: AtomicU32::new(0), + } + } + + /// Set LED state using safe bit operations + #[inline(never)] + pub fn set_led(&self, is_green: bool, state: bool) { + let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT }; + + // Use bit operations to avoid complex atomic operations + let current = self.led_states.load(Ordering::SeqCst); + + let new_value = if state { + current | (1 << bit) // Set bit + } else { + current & !(1 << bit) // Clear bit + }; + + self.led_states.store(new_value, Ordering::SeqCst); + core::sync::atomic::compiler_fence(Ordering::SeqCst); + } + + /// Get LED state using safe bit operations + #[inline(never)] + pub fn get_led(&self, is_green: bool) -> bool { + let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT }; + + let value = self.led_states.load(Ordering::SeqCst); + core::sync::atomic::compiler_fence(Ordering::SeqCst); + + (value & (1 << bit)) != 0 + } + + /// Increment counter safely + #[inline(never)] + pub fn increment_counter(&self) -> u32 { + let current = self.counter.load(Ordering::SeqCst); + let new_value = current.wrapping_add(1); + self.counter.store(new_value, Ordering::SeqCst); + core::sync::atomic::compiler_fence(Ordering::SeqCst); + new_value + } + + /// Get counter without incrementing + #[inline(never)] + pub fn get_counter(&self) -> u32 { + let value = self.counter.load(Ordering::SeqCst); + core::sync::atomic::compiler_fence(Ordering::SeqCst); + value + } + } + + #[link_section = ".ram_d3"] + pub static SHARED_LED_STATE: SharedLedState = SharedLedState::new(); + + // SRAM4 memory region constants for MPU configuration + pub const SRAM4_BASE_ADDRESS: u32 = 0x38000000; + pub const SRAM4_SIZE_LOG2: u32 = 15; // 64KB = 2^(15+1) + pub const SRAM4_REGION_NUMBER: u8 = 0; // MPU region number to use +} + +use core::mem::MaybeUninit; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::{Config, SharedData}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +// Import cortex_m for MPU configuration +use cortex_m::peripheral::{MPU, SCB}; +use cortex_m::asm; + +// Use our shared state from the module +use shared::{SHARED_LED_STATE, SRAM4_BASE_ADDRESS, SRAM4_REGION_NUMBER, SRAM4_SIZE_LOG2}; + +#[link_section = ".ram_d3"] +static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); + +// Function to configure MPU with your provided settings +fn configure_mpu_non_cacheable(mpu: &mut MPU, _scb: &mut SCB) { + // Ensure all operations complete before reconfiguring MPU/caches + asm::dmb(); + unsafe { + // Disable MPU + mpu.ctrl.write(0); + + // Configure SRAM4 as non-cacheable + // Set region number (0) + mpu.rnr.write(SRAM4_REGION_NUMBER as u32); + + // Set base address (SRAM4 = 0x38000000) with VALID bit and region number + mpu.rbar.write( + SRAM4_BASE_ADDRESS | + (1 << 4) // Region number = 0 (explicit in RBAR) + ); + + // Configure region attributes: + // SIZE=15 (64KB = 2^(15+1)) + // ENABLE=1 + // AP=3 (Full access) + // TEX=1, S=1, C=0, B=0 (Normal memory, Non-cacheable, Shareable) + let rasr_value: u32 = (SRAM4_SIZE_LOG2 << 1) | // SIZE=15 (64KB) + (1 << 0) | // ENABLE=1 + (3 << 24) | // AP=3 (Full access) + (1 << 19) | // TEX=1 + (1 << 18); // S=1 (Shareable) + + mpu.rasr.write(rasr_value); + + // Enable MPU with default memory map as background + mpu.ctrl.write(1 | (1 << 2)); // MPU_ENABLE | PRIVDEFENA + } + + // Ensure changes are committed + asm::dsb(); + asm::isb(); + + info!("MPU configured - SRAM4 set as non-cacheable"); +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) -> ! { + // Configure MPU to make SRAM4 non-cacheable + { + let mut cp = cortex_m::Peripherals::take().unwrap(); + let mpu = &mut cp.MPU; + let scb = &mut cp.SCB; + + // Configure MPU without disabling caches + configure_mpu_non_cacheable(mpu, scb); + } + + // Configure the clocks + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(HSIPrescaler::DIV1); + config.rcc.csi = true; + config.rcc.hsi48 = Some(Default::default()); + config.rcc.pll1 = Some(Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL50, + divp: Some(PllDiv::DIV2), + divq: Some(PllDiv::DIV8), + divr: None, + }); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV2; + config.rcc.apb1_pre = APBPrescaler::DIV2; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.apb3_pre = APBPrescaler::DIV2; + config.rcc.apb4_pre = APBPrescaler::DIV2; + config.rcc.voltage_scale = VoltageScale::Scale1; + config.rcc.supply_config = SupplyConfig::DirectSMPS; + } + + // Initialize the CM7 core + let _p = embassy_stm32::init_primary(config, &SHARED_DATA); + info!("CM7 core initialized with non-cacheable SRAM4!"); + + // Read the magic value to ensure shared memory is accessible + let magic = SHARED_LED_STATE.magic.load(core::sync::atomic::Ordering::SeqCst); + info!("CM7: Magic value = 0x{:X}", magic); + + // Initialize shared memory state + SHARED_LED_STATE.set_led(true, false); // Green LED off + SHARED_LED_STATE.set_led(false, false); // Yellow LED off + + // Main loop - update shared memory values + let mut green_state = false; + let mut yellow_state = false; + let mut loop_count = 0; + + info!("CM7: Starting main loop"); + loop { + // Update loop counter + loop_count += 1; + + // Update shared counter + let counter = SHARED_LED_STATE.increment_counter(); + + // Every second, toggle green LED state + if loop_count % 10 == 0 { + green_state = !green_state; + SHARED_LED_STATE.set_led(true, green_state); + info!("CM7: Counter = {}, Set green LED to {}", counter, green_state); + } + + // Every 3 seconds, toggle yellow LED state + if loop_count % 30 == 0 { + yellow_state = !yellow_state; + SHARED_LED_STATE.set_led(false, yellow_state); + info!("CM7: Counter = {}, Set yellow LED to {}", counter, yellow_state); + } + + // Wait 100ms before next cycle + Timer::after_millis(100).await; + } +} \ No newline at end of file -- cgit