diff options
| author | ragarnoy <[email protected]> | 2025-05-10 10:40:35 +0200 |
|---|---|---|
| committer | ragarnoy <[email protected]> | 2025-05-10 10:40:35 +0200 |
| commit | f28934cb46bd18cf362b988471266e1b7bb9927e (patch) | |
| tree | 68f0226806987fc000dfe85f15a7dc86f6775329 /examples/stm32h755cm7 | |
| parent | 04c0bd84e6043ac35d2a20f1f4a789ccf79bb316 (diff) | |
Rewrite documentation and generally improve it
Diffstat (limited to 'examples/stm32h755cm7')
| -rw-r--r-- | examples/stm32h755cm7/src/bin/intercore.rs | 79 |
1 files changed, 40 insertions, 39 deletions
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 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | //! STM32H7 Primary Core (CM7) Intercore Communication Example | ||
| 5 | //! | ||
| 6 | //! This example demonstrates reliable communication between the Cortex-M7 and | ||
| 7 | //! Cortex-M4 cores using a shared memory region configured as non-cacheable | ||
| 8 | //! via MPU settings. | ||
| 9 | //! | ||
| 10 | //! The CM7 core handles: | ||
| 11 | //! - MPU configuration to make shared memory non-cacheable | ||
| 12 | //! - Clock initialization | ||
| 13 | //! - Toggling LED states in shared memory | ||
| 14 | //! | ||
| 15 | //! Usage: | ||
| 16 | //! 1. Flash the CM4 (secondary) core binary first | ||
| 17 | //! 2. Then flash this CM7 (primary) core binary | ||
| 18 | //! 3. The system will start with CM7 toggling LED states and CM4 responding by | ||
| 19 | //! physically toggling the LEDs | ||
| 20 | |||
| 4 | use core::mem::MaybeUninit; | 21 | use core::mem::MaybeUninit; |
| 5 | 22 | ||
| 6 | use cortex_m::asm; | 23 | use cortex_m::asm; |
| @@ -12,17 +29,15 @@ use embassy_time::Timer; | |||
| 12 | use shared::{SHARED_LED_STATE, SRAM4_BASE_ADDRESS, SRAM4_REGION_NUMBER, SRAM4_SIZE_LOG2}; | 29 | use shared::{SHARED_LED_STATE, SRAM4_BASE_ADDRESS, SRAM4_REGION_NUMBER, SRAM4_SIZE_LOG2}; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 30 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 31 | ||
| 32 | /// Module providing shared memory constructs for intercore communication | ||
| 15 | mod shared { | 33 | mod shared { |
| 16 | use core::sync::atomic::{AtomicU32, Ordering}; | 34 | use core::sync::atomic::{AtomicU32, Ordering}; |
| 17 | 35 | ||
| 18 | /// Shared LED state between CM7 and CM4 cores | 36 | /// State shared between CM7 and CM4 cores for LED control |
| 19 | #[repr(C, align(4))] | 37 | #[repr(C, align(4))] |
| 20 | pub struct SharedLedState { | 38 | pub struct SharedLedState { |
| 21 | // Magic number for validation | ||
| 22 | pub magic: AtomicU32, | 39 | pub magic: AtomicU32, |
| 23 | // Counter for synchronization testing | ||
| 24 | pub counter: AtomicU32, | 40 | pub counter: AtomicU32, |
| 25 | // LED states packed into a single atomic | ||
| 26 | pub led_states: AtomicU32, | 41 | pub led_states: AtomicU32, |
| 27 | } | 42 | } |
| 28 | 43 | ||
| @@ -33,18 +48,16 @@ mod shared { | |||
| 33 | impl SharedLedState { | 48 | impl SharedLedState { |
| 34 | pub const fn new() -> Self { | 49 | pub const fn new() -> Self { |
| 35 | Self { | 50 | Self { |
| 36 | magic: AtomicU32::new(0xDEADBEEF), // Magic number | 51 | magic: AtomicU32::new(0xDEADBEEF), |
| 37 | counter: AtomicU32::new(0), | 52 | counter: AtomicU32::new(0), |
| 38 | led_states: AtomicU32::new(0), | 53 | led_states: AtomicU32::new(0), |
| 39 | } | 54 | } |
| 40 | } | 55 | } |
| 41 | 56 | ||
| 42 | /// Set LED state using safe bit operations | 57 | /// Set LED state by manipulating the appropriate bit in the led_states field |
| 43 | #[inline(never)] | 58 | #[inline(never)] |
| 44 | pub fn set_led(&self, is_green: bool, state: bool) { | 59 | pub fn set_led(&self, is_green: bool, state: bool) { |
| 45 | let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT }; | 60 | let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT }; |
| 46 | |||
| 47 | // Use bit operations to avoid complex atomic operations | ||
| 48 | let current = self.led_states.load(Ordering::SeqCst); | 61 | let current = self.led_states.load(Ordering::SeqCst); |
| 49 | 62 | ||
| 50 | let new_value = if state { | 63 | let new_value = if state { |
| @@ -57,7 +70,7 @@ mod shared { | |||
| 57 | core::sync::atomic::compiler_fence(Ordering::SeqCst); | 70 | core::sync::atomic::compiler_fence(Ordering::SeqCst); |
| 58 | } | 71 | } |
| 59 | 72 | ||
| 60 | /// Get LED state using safe bit operations | 73 | /// Get current LED state |
| 61 | #[inline(never)] | 74 | #[inline(never)] |
| 62 | #[allow(dead_code)] | 75 | #[allow(dead_code)] |
| 63 | pub fn get_led(&self, is_green: bool) -> bool { | 76 | pub fn get_led(&self, is_green: bool) -> bool { |
| @@ -69,7 +82,7 @@ mod shared { | |||
| 69 | (value & (1 << bit)) != 0 | 82 | (value & (1 << bit)) != 0 |
| 70 | } | 83 | } |
| 71 | 84 | ||
| 72 | /// Increment counter safely | 85 | /// Increment counter and return new value |
| 73 | #[inline(never)] | 86 | #[inline(never)] |
| 74 | pub fn increment_counter(&self) -> u32 { | 87 | pub fn increment_counter(&self) -> u32 { |
| 75 | let current = self.counter.load(Ordering::SeqCst); | 88 | let current = self.counter.load(Ordering::SeqCst); |
| @@ -79,7 +92,7 @@ mod shared { | |||
| 79 | new_value | 92 | new_value |
| 80 | } | 93 | } |
| 81 | 94 | ||
| 82 | /// Get counter without incrementing | 95 | /// Get current counter value |
| 83 | #[inline(never)] | 96 | #[inline(never)] |
| 84 | #[allow(dead_code)] | 97 | #[allow(dead_code)] |
| 85 | pub fn get_counter(&self) -> u32 { | 98 | pub fn get_counter(&self) -> u32 { |
| @@ -92,37 +105,29 @@ mod shared { | |||
| 92 | #[link_section = ".ram_d3"] | 105 | #[link_section = ".ram_d3"] |
| 93 | pub static SHARED_LED_STATE: SharedLedState = SharedLedState::new(); | 106 | pub static SHARED_LED_STATE: SharedLedState = SharedLedState::new(); |
| 94 | 107 | ||
| 95 | // SRAM4 memory region constants for MPU configuration | 108 | // Memory region constants for MPU configuration |
| 96 | pub const SRAM4_BASE_ADDRESS: u32 = 0x38000000; | 109 | pub const SRAM4_BASE_ADDRESS: u32 = 0x38000000; |
| 97 | pub const SRAM4_SIZE_LOG2: u32 = 15; // 64KB = 2^(15+1) | 110 | pub const SRAM4_SIZE_LOG2: u32 = 15; // 64KB = 2^(15+1) |
| 98 | pub const SRAM4_REGION_NUMBER: u8 = 0; // MPU region number to use | 111 | pub const SRAM4_REGION_NUMBER: u8 = 0; |
| 99 | } | 112 | } |
| 100 | 113 | ||
| 101 | #[link_section = ".ram_d3"] | 114 | #[link_section = ".ram_d3"] |
| 102 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 115 | static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 103 | 116 | ||
| 104 | // Function to configure MPU with your provided settings | 117 | /// Configure MPU to make SRAM4 region non-cacheable |
| 105 | fn configure_mpu_non_cacheable(mpu: &mut MPU) { | 118 | fn configure_mpu_non_cacheable(mpu: &mut MPU) { |
| 106 | // Ensure all operations complete before reconfiguring MPU/caches | ||
| 107 | asm::dmb(); | 119 | asm::dmb(); |
| 108 | unsafe { | 120 | unsafe { |
| 109 | // Disable MPU | 121 | // Disable MPU |
| 110 | mpu.ctrl.write(0); | 122 | mpu.ctrl.write(0); |
| 111 | 123 | ||
| 112 | // Configure SRAM4 as non-cacheable | 124 | // Configure SRAM4 as non-cacheable |
| 113 | // Set region number (0) | ||
| 114 | mpu.rnr.write(SRAM4_REGION_NUMBER as u32); | 125 | mpu.rnr.write(SRAM4_REGION_NUMBER as u32); |
| 115 | 126 | ||
| 116 | // Set base address (SRAM4 = 0x38000000) with VALID bit and region number | 127 | // Set base address with region number |
| 117 | mpu.rbar.write( | 128 | mpu.rbar.write(SRAM4_BASE_ADDRESS | (1 << 4)); |
| 118 | SRAM4_BASE_ADDRESS | (1 << 4), // Region number = 0 (explicit in RBAR) | ||
| 119 | ); | ||
| 120 | 129 | ||
| 121 | // Configure region attributes: | 130 | // Configure region attributes |
| 122 | // SIZE=15 (64KB = 2^(15+1)) | ||
| 123 | // ENABLE=1 | ||
| 124 | // AP=3 (Full access) | ||
| 125 | // TEX=1, S=1, C=0, B=0 (Normal memory, Non-cacheable, Shareable) | ||
| 126 | let rasr_value: u32 = (SRAM4_SIZE_LOG2 << 1) | // SIZE=15 (64KB) | 131 | let rasr_value: u32 = (SRAM4_SIZE_LOG2 << 1) | // SIZE=15 (64KB) |
| 127 | (1 << 0) | // ENABLE=1 | 132 | (1 << 0) | // ENABLE=1 |
| 128 | (3 << 24) | // AP=3 (Full access) | 133 | (3 << 24) | // AP=3 (Full access) |
| @@ -135,7 +140,6 @@ fn configure_mpu_non_cacheable(mpu: &mut MPU) { | |||
| 135 | mpu.ctrl.write(1 | (1 << 2)); // MPU_ENABLE | PRIVDEFENA | 140 | mpu.ctrl.write(1 | (1 << 2)); // MPU_ENABLE | PRIVDEFENA |
| 136 | } | 141 | } |
| 137 | 142 | ||
| 138 | // Ensure changes are committed | ||
| 139 | asm::dsb(); | 143 | asm::dsb(); |
| 140 | asm::isb(); | 144 | asm::isb(); |
| 141 | 145 | ||
| @@ -144,25 +148,26 @@ fn configure_mpu_non_cacheable(mpu: &mut MPU) { | |||
| 144 | 148 | ||
| 145 | #[embassy_executor::main] | 149 | #[embassy_executor::main] |
| 146 | async fn main(_spawner: Spawner) -> ! { | 150 | async fn main(_spawner: Spawner) -> ! { |
| 147 | // Configure MPU to make SRAM4 non-cacheable | 151 | // Set up MPU and cache configuration |
| 148 | { | 152 | { |
| 149 | let mut cp = cortex_m::Peripherals::take().unwrap(); | 153 | let mut cp = cortex_m::Peripherals::take().unwrap(); |
| 150 | let scb = &mut cp.SCB; | 154 | let scb = &mut cp.SCB; |
| 151 | 155 | ||
| 156 | // First disable caches | ||
| 152 | scb.disable_icache(); | 157 | scb.disable_icache(); |
| 153 | scb.disable_dcache(&mut cp.CPUID); | 158 | scb.disable_dcache(&mut cp.CPUID); |
| 154 | 159 | ||
| 155 | // 2. MPU setup | 160 | // Configure MPU |
| 156 | configure_mpu_non_cacheable(&mut cp.MPU); | 161 | configure_mpu_non_cacheable(&mut cp.MPU); |
| 157 | 162 | ||
| 158 | // 3. re-enable caches | 163 | // Re-enable caches |
| 159 | scb.enable_icache(); | 164 | scb.enable_icache(); |
| 160 | scb.enable_dcache(&mut cp.CPUID); | 165 | scb.enable_dcache(&mut cp.CPUID); |
| 161 | asm::dsb(); | 166 | asm::dsb(); |
| 162 | asm::isb(); | 167 | asm::isb(); |
| 163 | } | 168 | } |
| 164 | 169 | ||
| 165 | // Configure the clocks | 170 | // Configure the clock system |
| 166 | let mut config = Config::default(); | 171 | let mut config = Config::default(); |
| 167 | { | 172 | { |
| 168 | use embassy_stm32::rcc::*; | 173 | use embassy_stm32::rcc::*; |
| @@ -191,42 +196,38 @@ async fn main(_spawner: Spawner) -> ! { | |||
| 191 | let _p = embassy_stm32::init_primary(config, &SHARED_DATA); | 196 | let _p = embassy_stm32::init_primary(config, &SHARED_DATA); |
| 192 | info!("CM7 core initialized with non-cacheable SRAM4!"); | 197 | info!("CM7 core initialized with non-cacheable SRAM4!"); |
| 193 | 198 | ||
| 194 | // Read the magic value to ensure shared memory is accessible | 199 | // Verify shared memory is accessible |
| 195 | let magic = SHARED_LED_STATE.magic.load(core::sync::atomic::Ordering::SeqCst); | 200 | let magic = SHARED_LED_STATE.magic.load(core::sync::atomic::Ordering::SeqCst); |
| 196 | info!("CM7: Magic value = 0x{:X}", magic); | 201 | info!("CM7: Magic value = 0x{:X}", magic); |
| 197 | 202 | ||
| 198 | // Initialize shared memory state | 203 | // Initialize LED states |
| 199 | SHARED_LED_STATE.set_led(true, false); // Green LED off | 204 | SHARED_LED_STATE.set_led(true, false); // Green LED off |
| 200 | SHARED_LED_STATE.set_led(false, false); // Yellow LED off | 205 | SHARED_LED_STATE.set_led(false, false); // Yellow LED off |
| 201 | 206 | ||
| 202 | // Main loop - update shared memory values | 207 | // Main loop - periodically toggle LED states |
| 203 | let mut green_state = false; | 208 | let mut green_state = false; |
| 204 | let mut yellow_state = false; | 209 | let mut yellow_state = false; |
| 205 | let mut loop_count = 0; | 210 | let mut loop_count = 0; |
| 206 | 211 | ||
| 207 | info!("CM7: Starting main loop"); | 212 | info!("CM7: Starting main loop"); |
| 208 | loop { | 213 | loop { |
| 209 | // Update loop counter | ||
| 210 | loop_count += 1; | 214 | loop_count += 1; |
| 211 | |||
| 212 | // Update shared counter | ||
| 213 | let counter = SHARED_LED_STATE.increment_counter(); | 215 | let counter = SHARED_LED_STATE.increment_counter(); |
| 214 | 216 | ||
| 215 | // Every second, toggle green LED state | 217 | // Toggle green LED every second |
| 216 | if loop_count % 10 == 0 { | 218 | if loop_count % 10 == 0 { |
| 217 | green_state = !green_state; | 219 | green_state = !green_state; |
| 218 | SHARED_LED_STATE.set_led(true, green_state); | 220 | SHARED_LED_STATE.set_led(true, green_state); |
| 219 | info!("CM7: Counter = {}, Set green LED to {}", counter, green_state); | 221 | info!("CM7: Counter = {}, Set green LED to {}", counter, green_state); |
| 220 | } | 222 | } |
| 221 | 223 | ||
| 222 | // Every 3 seconds, toggle yellow LED state | 224 | // Toggle yellow LED every 3 seconds |
| 223 | if loop_count % 30 == 0 { | 225 | if loop_count % 30 == 0 { |
| 224 | yellow_state = !yellow_state; | 226 | yellow_state = !yellow_state; |
| 225 | SHARED_LED_STATE.set_led(false, yellow_state); | 227 | SHARED_LED_STATE.set_led(false, yellow_state); |
| 226 | info!("CM7: Counter = {}, Set yellow LED to {}", counter, yellow_state); | 228 | info!("CM7: Counter = {}, Set yellow LED to {}", counter, yellow_state); |
| 227 | } | 229 | } |
| 228 | 230 | ||
| 229 | // Wait 100ms before next cycle | ||
| 230 | Timer::after_millis(100).await; | 231 | Timer::after_millis(100).await; |
| 231 | } | 232 | } |
| 232 | } | 233 | } |
