Rewrite documentation and generally improve it

This commit is contained in:
ragarnoy 2025-05-10 10:40:35 +02:00
parent 04c0bd84e6
commit f28934cb46
No known key found for this signature in database
GPG Key ID: 7A8FB9858346157A
2 changed files with 71 additions and 61 deletions

View File

@ -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<SharedData> = 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;
}
}

View File

@ -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<SharedData> = 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;
}
}