diff --git a/examples/stm32h755cm4/src/bin/intercore.rs b/examples/stm32h755cm4/src/bin/intercore.rs index 3a66a1ecd..715df28d6 100644 --- a/examples/stm32h755cm4/src/bin/intercore.rs +++ b/examples/stm32h755cm4/src/bin/intercore.rs @@ -1,18 +1,32 @@ #![no_std] #![no_main] -// IMPORTANT: This must match EXACTLY the definition in CM7! +//! STM32H7 Secondary Core (CM4) Intercore Communication Example +//! +//! This example demonstrates reliable communication between the Cortex-M7 and +//! Cortex-M4 cores. This secondary core monitors shared memory for LED state +//! changes and updates the physical LEDs accordingly. +//! +//! The CM4 core handles: +//! - Responding to state changes from CM7 +//! - Controlling the physical green and yellow LEDs +//! - Providing visual feedback via a heartbeat on the red LED +//! +//! Usage: +//! 1. Flash this CM4 (secondary) core binary first +//! 2. Then flash the CM7 (primary) core binary +//! 3. The red LED should blink continuously as a heartbeat +//! 4. Green and yellow LEDs should toggle according to CM7 core signals + +/// Module providing shared memory constructs for intercore communication mod shared { use core::sync::atomic::{AtomicU32, Ordering}; - /// Shared LED state between CM7 and CM4 cores + /// State shared between CM7 and CM4 cores for LED control #[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, } @@ -23,19 +37,17 @@ mod shared { impl SharedLedState { pub const fn new() -> Self { Self { - magic: AtomicU32::new(0xDEADBEEF), // Magic number + magic: AtomicU32::new(0xDEADBEEF), counter: AtomicU32::new(0), led_states: AtomicU32::new(0), } } - /// Set LED state using safe bit operations + /// Set LED state by manipulating the appropriate bit in the led_states field #[inline(never)] #[allow(dead_code)] 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 { @@ -48,7 +60,7 @@ mod shared { core::sync::atomic::fence(Ordering::SeqCst); } - /// Get LED state using safe bit operations + /// Get current LED state #[inline(never)] pub fn get_led(&self, is_green: bool) -> bool { let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT }; @@ -59,7 +71,7 @@ mod shared { (value & (1 << bit)) != 0 } - /// Increment counter safely + /// Increment counter and return new value #[inline(never)] #[allow(dead_code)] pub fn increment_counter(&self) -> u32 { @@ -70,7 +82,7 @@ mod shared { new_value } - /// Get counter without incrementing + /// Get current counter value #[inline(never)] pub fn get_counter(&self) -> u32 { let value = self.counter.load(Ordering::SeqCst); @@ -84,19 +96,18 @@ mod shared { } 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 our shared state from the module use shared::SHARED_LED_STATE; use {defmt_rtt as _, panic_probe as _}; #[link_section = ".ram_d3"] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); +/// Task that continuously blinks the red LED as a heartbeat indicator #[embassy_executor::task] async fn blink_heartbeat(mut led: Output<'static>) { loop { @@ -112,11 +123,11 @@ async fn main(spawner: Spawner) -> ! { let p = embassy_stm32::init_secondary(&SHARED_DATA); info!("CM4 core initialized!"); - // Read the magic value to ensure shared memory is accessible + // Verify 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 + // Set up 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) @@ -124,31 +135,30 @@ async fn main(spawner: Spawner) -> ! { // Start heartbeat task unwrap!(spawner.spawn(blink_heartbeat(red_led))); - // Previous values for detecting changes + // Track previous values to detect 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 + // Read current 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 + // Detect 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 + // Update LEDs and logs when values change 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(); @@ -172,7 +182,6 @@ async fn main(spawner: Spawner) -> ! { } } - // Poll at a reasonable rate Timer::after_millis(10).await; } } diff --git a/examples/stm32h755cm7/src/bin/intercore.rs b/examples/stm32h755cm7/src/bin/intercore.rs index f1fbd29bc..530e782ab 100644 --- a/examples/stm32h755cm7/src/bin/intercore.rs +++ b/examples/stm32h755cm7/src/bin/intercore.rs @@ -1,6 +1,23 @@ #![no_std] #![no_main] +//! STM32H7 Primary Core (CM7) Intercore Communication Example +//! +//! This example demonstrates reliable communication between the Cortex-M7 and +//! Cortex-M4 cores using a shared memory region configured as non-cacheable +//! via MPU settings. +//! +//! The CM7 core handles: +//! - MPU configuration to make shared memory non-cacheable +//! - Clock initialization +//! - Toggling LED states in shared memory +//! +//! Usage: +//! 1. Flash the CM4 (secondary) core binary first +//! 2. Then flash this CM7 (primary) core binary +//! 3. The system will start with CM7 toggling LED states and CM4 responding by +//! physically toggling the LEDs + use core::mem::MaybeUninit; use cortex_m::asm; @@ -12,17 +29,15 @@ use embassy_time::Timer; use shared::{SHARED_LED_STATE, SRAM4_BASE_ADDRESS, SRAM4_REGION_NUMBER, SRAM4_SIZE_LOG2}; use {defmt_rtt as _, panic_probe as _}; +/// Module providing shared memory constructs for intercore communication mod shared { use core::sync::atomic::{AtomicU32, Ordering}; - /// Shared LED state between CM7 and CM4 cores + /// State shared between CM7 and CM4 cores for LED control #[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, } @@ -33,18 +48,16 @@ mod shared { impl SharedLedState { pub const fn new() -> Self { Self { - magic: AtomicU32::new(0xDEADBEEF), // Magic number + magic: AtomicU32::new(0xDEADBEEF), counter: AtomicU32::new(0), led_states: AtomicU32::new(0), } } - /// Set LED state using safe bit operations + /// Set LED state by manipulating the appropriate bit in the led_states field #[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 { @@ -57,7 +70,7 @@ mod shared { core::sync::atomic::compiler_fence(Ordering::SeqCst); } - /// Get LED state using safe bit operations + /// Get current LED state #[inline(never)] #[allow(dead_code)] pub fn get_led(&self, is_green: bool) -> bool { @@ -69,7 +82,7 @@ mod shared { (value & (1 << bit)) != 0 } - /// Increment counter safely + /// Increment counter and return new value #[inline(never)] pub fn increment_counter(&self) -> u32 { let current = self.counter.load(Ordering::SeqCst); @@ -79,7 +92,7 @@ mod shared { new_value } - /// Get counter without incrementing + /// Get current counter value #[inline(never)] #[allow(dead_code)] pub fn get_counter(&self) -> u32 { @@ -92,37 +105,29 @@ mod shared { #[link_section = ".ram_d3"] pub static SHARED_LED_STATE: SharedLedState = SharedLedState::new(); - // SRAM4 memory region constants for MPU configuration + // 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 + pub const SRAM4_REGION_NUMBER: u8 = 0; } #[link_section = ".ram_d3"] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); -// Function to configure MPU with your provided settings +/// Configure MPU to make SRAM4 region non-cacheable fn configure_mpu_non_cacheable(mpu: &mut MPU) { - // 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) - ); + // Set base address with region number + mpu.rbar.write(SRAM4_BASE_ADDRESS | (1 << 4)); - // 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) + // Configure region attributes let rasr_value: u32 = (SRAM4_SIZE_LOG2 << 1) | // SIZE=15 (64KB) (1 << 0) | // ENABLE=1 (3 << 24) | // AP=3 (Full access) @@ -135,7 +140,6 @@ fn configure_mpu_non_cacheable(mpu: &mut MPU) { mpu.ctrl.write(1 | (1 << 2)); // MPU_ENABLE | PRIVDEFENA } - // Ensure changes are committed asm::dsb(); asm::isb(); @@ -144,25 +148,26 @@ fn configure_mpu_non_cacheable(mpu: &mut MPU) { #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { - // Configure MPU to make SRAM4 non-cacheable + // Set up MPU and cache configuration { let mut cp = cortex_m::Peripherals::take().unwrap(); let scb = &mut cp.SCB; + // First disable caches scb.disable_icache(); scb.disable_dcache(&mut cp.CPUID); - // 2. MPU setup + // Configure MPU configure_mpu_non_cacheable(&mut cp.MPU); - // 3. re-enable caches + // Re-enable caches scb.enable_icache(); scb.enable_dcache(&mut cp.CPUID); asm::dsb(); asm::isb(); } - // Configure the clocks + // Configure the clock system let mut config = Config::default(); { use embassy_stm32::rcc::*; @@ -191,42 +196,38 @@ async fn main(_spawner: Spawner) -> ! { 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 + // Verify 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 + // Initialize LED states 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 + // Main loop - periodically toggle LED states 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 + // Toggle green LED every second 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 + // Toggle yellow LED every 3 seconds 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; } }