aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorragarnoy <[email protected]>2025-05-10 10:40:35 +0200
committerragarnoy <[email protected]>2025-05-10 10:40:35 +0200
commitf28934cb46bd18cf362b988471266e1b7bb9927e (patch)
tree68f0226806987fc000dfe85f15a7dc86f6775329
parent04c0bd84e6043ac35d2a20f1f4a789ccf79bb316 (diff)
Rewrite documentation and generally improve it
-rw-r--r--examples/stm32h755cm4/src/bin/intercore.rs53
-rw-r--r--examples/stm32h755cm7/src/bin/intercore.rs79
2 files changed, 71 insertions, 61 deletions
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 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4// IMPORTANT: This must match EXACTLY the definition in CM7! 4//! STM32H7 Secondary Core (CM4) Intercore Communication Example
5//!
6//! This example demonstrates reliable communication between the Cortex-M7 and
7//! Cortex-M4 cores. This secondary core monitors shared memory for LED state
8//! changes and updates the physical LEDs accordingly.
9//!
10//! The CM4 core handles:
11//! - Responding to state changes from CM7
12//! - Controlling the physical green and yellow LEDs
13//! - Providing visual feedback via a heartbeat on the red LED
14//!
15//! Usage:
16//! 1. Flash this CM4 (secondary) core binary first
17//! 2. Then flash the CM7 (primary) core binary
18//! 3. The red LED should blink continuously as a heartbeat
19//! 4. Green and yellow LEDs should toggle according to CM7 core signals
20
21/// Module providing shared memory constructs for intercore communication
5mod shared { 22mod shared {
6 use core::sync::atomic::{AtomicU32, Ordering}; 23 use core::sync::atomic::{AtomicU32, Ordering};
7 24
8 /// Shared LED state between CM7 and CM4 cores 25 /// State shared between CM7 and CM4 cores for LED control
9 #[repr(C, align(4))] 26 #[repr(C, align(4))]
10 pub struct SharedLedState { 27 pub struct SharedLedState {
11 // Magic number for validation
12 pub magic: AtomicU32, 28 pub magic: AtomicU32,
13 // Counter for synchronization testing
14 pub counter: AtomicU32, 29 pub counter: AtomicU32,
15 // LED states packed into a single atomic
16 pub led_states: AtomicU32, 30 pub led_states: AtomicU32,
17 } 31 }
18 32
@@ -23,19 +37,17 @@ mod shared {
23 impl SharedLedState { 37 impl SharedLedState {
24 pub const fn new() -> Self { 38 pub const fn new() -> Self {
25 Self { 39 Self {
26 magic: AtomicU32::new(0xDEADBEEF), // Magic number 40 magic: AtomicU32::new(0xDEADBEEF),
27 counter: AtomicU32::new(0), 41 counter: AtomicU32::new(0),
28 led_states: AtomicU32::new(0), 42 led_states: AtomicU32::new(0),
29 } 43 }
30 } 44 }
31 45
32 /// Set LED state using safe bit operations 46 /// Set LED state by manipulating the appropriate bit in the led_states field
33 #[inline(never)] 47 #[inline(never)]
34 #[allow(dead_code)] 48 #[allow(dead_code)]
35 pub fn set_led(&self, is_green: bool, state: bool) { 49 pub fn set_led(&self, is_green: bool, state: bool) {
36 let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT }; 50 let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT };
37
38 // Use bit operations to avoid complex atomic operations
39 let current = self.led_states.load(Ordering::SeqCst); 51 let current = self.led_states.load(Ordering::SeqCst);
40 52
41 let new_value = if state { 53 let new_value = if state {
@@ -48,7 +60,7 @@ mod shared {
48 core::sync::atomic::fence(Ordering::SeqCst); 60 core::sync::atomic::fence(Ordering::SeqCst);
49 } 61 }
50 62
51 /// Get LED state using safe bit operations 63 /// Get current LED state
52 #[inline(never)] 64 #[inline(never)]
53 pub fn get_led(&self, is_green: bool) -> bool { 65 pub fn get_led(&self, is_green: bool) -> bool {
54 let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT }; 66 let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT };
@@ -59,7 +71,7 @@ mod shared {
59 (value & (1 << bit)) != 0 71 (value & (1 << bit)) != 0
60 } 72 }
61 73
62 /// Increment counter safely 74 /// Increment counter and return new value
63 #[inline(never)] 75 #[inline(never)]
64 #[allow(dead_code)] 76 #[allow(dead_code)]
65 pub fn increment_counter(&self) -> u32 { 77 pub fn increment_counter(&self) -> u32 {
@@ -70,7 +82,7 @@ mod shared {
70 new_value 82 new_value
71 } 83 }
72 84
73 /// Get counter without incrementing 85 /// Get current counter value
74 #[inline(never)] 86 #[inline(never)]
75 pub fn get_counter(&self) -> u32 { 87 pub fn get_counter(&self) -> u32 {
76 let value = self.counter.load(Ordering::SeqCst); 88 let value = self.counter.load(Ordering::SeqCst);
@@ -84,19 +96,18 @@ mod shared {
84} 96}
85 97
86use core::mem::MaybeUninit; 98use core::mem::MaybeUninit;
87
88use defmt::*; 99use defmt::*;
89use embassy_executor::Spawner; 100use embassy_executor::Spawner;
90use embassy_stm32::gpio::{Level, Output, Speed}; 101use embassy_stm32::gpio::{Level, Output, Speed};
91use embassy_stm32::SharedData; 102use embassy_stm32::SharedData;
92use embassy_time::Timer; 103use embassy_time::Timer;
93// Use our shared state from the module
94use shared::SHARED_LED_STATE; 104use shared::SHARED_LED_STATE;
95use {defmt_rtt as _, panic_probe as _}; 105use {defmt_rtt as _, panic_probe as _};
96 106
97#[link_section = ".ram_d3"] 107#[link_section = ".ram_d3"]
98static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); 108static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();
99 109
110/// Task that continuously blinks the red LED as a heartbeat indicator
100#[embassy_executor::task] 111#[embassy_executor::task]
101async fn blink_heartbeat(mut led: Output<'static>) { 112async fn blink_heartbeat(mut led: Output<'static>) {
102 loop { 113 loop {
@@ -112,11 +123,11 @@ async fn main(spawner: Spawner) -> ! {
112 let p = embassy_stm32::init_secondary(&SHARED_DATA); 123 let p = embassy_stm32::init_secondary(&SHARED_DATA);
113 info!("CM4 core initialized!"); 124 info!("CM4 core initialized!");
114 125
115 // Read the magic value to ensure shared memory is accessible 126 // Verify shared memory is accessible
116 let magic = SHARED_LED_STATE.magic.load(core::sync::atomic::Ordering::SeqCst); 127 let magic = SHARED_LED_STATE.magic.load(core::sync::atomic::Ordering::SeqCst);
117 info!("CM4: Magic value = 0x{:X}", magic); 128 info!("CM4: Magic value = 0x{:X}", magic);
118 129
119 // Initialize LEDs 130 // Set up LEDs
120 let mut green_led = Output::new(p.PB0, Level::Low, Speed::Low); // LD1 131 let mut green_led = Output::new(p.PB0, Level::Low, Speed::Low); // LD1
121 let mut yellow_led = Output::new(p.PE1, Level::Low, Speed::Low); // LD2 132 let mut yellow_led = Output::new(p.PE1, Level::Low, Speed::Low); // LD2
122 let red_led = Output::new(p.PB14, Level::Low, Speed::Low); // LD3 (heartbeat) 133 let red_led = Output::new(p.PB14, Level::Low, Speed::Low); // LD3 (heartbeat)
@@ -124,31 +135,30 @@ async fn main(spawner: Spawner) -> ! {
124 // Start heartbeat task 135 // Start heartbeat task
125 unwrap!(spawner.spawn(blink_heartbeat(red_led))); 136 unwrap!(spawner.spawn(blink_heartbeat(red_led)));
126 137
127 // Previous values for detecting changes 138 // Track previous values to detect changes
128 let mut prev_green = false; 139 let mut prev_green = false;
129 let mut prev_yellow = false; 140 let mut prev_yellow = false;
130 let mut prev_counter = 0; 141 let mut prev_counter = 0;
131 142
132 info!("CM4: Starting main loop"); 143 info!("CM4: Starting main loop");
133 loop { 144 loop {
134 // Read values from shared memory 145 // Read current values from shared memory
135 let green_state = SHARED_LED_STATE.get_led(true); 146 let green_state = SHARED_LED_STATE.get_led(true);
136 let yellow_state = SHARED_LED_STATE.get_led(false); 147 let yellow_state = SHARED_LED_STATE.get_led(false);
137 let counter = SHARED_LED_STATE.get_counter(); 148 let counter = SHARED_LED_STATE.get_counter();
138 149
139 // Check for state changes 150 // Detect changes
140 let green_changed = green_state != prev_green; 151 let green_changed = green_state != prev_green;
141 let yellow_changed = yellow_state != prev_yellow; 152 let yellow_changed = yellow_state != prev_yellow;
142 let counter_changed = counter != prev_counter; 153 let counter_changed = counter != prev_counter;
143 154
144 // If any state changed, log it and update LEDs 155 // Update LEDs and logs when values change
145 if green_changed || yellow_changed || counter_changed { 156 if green_changed || yellow_changed || counter_changed {
146 if counter_changed { 157 if counter_changed {
147 info!("CM4: Counter = {}", counter); 158 info!("CM4: Counter = {}", counter);
148 prev_counter = counter; 159 prev_counter = counter;
149 } 160 }
150 161
151 // Update LED states
152 if green_changed { 162 if green_changed {
153 if green_state { 163 if green_state {
154 green_led.set_high(); 164 green_led.set_high();
@@ -172,7 +182,6 @@ async fn main(spawner: Spawner) -> ! {
172 } 182 }
173 } 183 }
174 184
175 // Poll at a reasonable rate
176 Timer::after_millis(10).await; 185 Timer::after_millis(10).await;
177 } 186 }
178} 187}
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
4use core::mem::MaybeUninit; 21use core::mem::MaybeUninit;
5 22
6use cortex_m::asm; 23use cortex_m::asm;
@@ -12,17 +29,15 @@ use embassy_time::Timer;
12use shared::{SHARED_LED_STATE, SRAM4_BASE_ADDRESS, SRAM4_REGION_NUMBER, SRAM4_SIZE_LOG2}; 29use shared::{SHARED_LED_STATE, SRAM4_BASE_ADDRESS, SRAM4_REGION_NUMBER, SRAM4_SIZE_LOG2};
13use {defmt_rtt as _, panic_probe as _}; 30use {defmt_rtt as _, panic_probe as _};
14 31
32/// Module providing shared memory constructs for intercore communication
15mod shared { 33mod 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"]
102static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); 115static 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
105fn configure_mpu_non_cacheable(mpu: &mut MPU) { 118fn 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]
146async fn main(_spawner: Spawner) -> ! { 150async 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}