Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

safe example with spin #72

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

Dajamante
Copy link

We would like to add an example using spin to make sure the heap is initialized once :).

@Dajamante Dajamante requested a review from a team as a code owner October 3, 2023 13:20
Cargo.toml Outdated
@@ -25,6 +25,7 @@ version = "0.5.0"

[dependencies]
critical-section = "1.0"
spin = "0.9.8"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should go under dev-dependencies for an example.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok!

Copy link
Member

@Dirbaio Dirbaio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using spin is always a bug in bare-metal.

For example, if you use it to sync data between main and interrupt (as a substitute of std::sync) you WILL get deadlocks if, while main holds the lock, an interrupt fires. The interrupt will spin waiting on the already-acquired lock, which should be released by main, but main will never contine running until the interrupt returns.

Same for all the other sync primitives spin has, including spin::Once.

We really shouldn't promote it in embedded contexts, or people will see "ooh, nostd mutex, shiny!" and use it and shoot themselves in the foot.

@thejpster
Copy link
Contributor

@Dirbaio what do you propose instead for ensuring the heap is only initialised once?

@trueb2
Copy link
Contributor

trueb2 commented Jan 29, 2024

Using spin is always a bug in bare-metal.

For example, if you use it to sync data between main and interrupt (as a substitute of std::sync) you WILL get deadlocks if, while main holds the lock, an interrupt fires. The interrupt will spin waiting on the already-acquired lock, which should be released by main, but main will never contine running until the interrupt returns.

Same for all the other sync primitives spin has, including spin::Once.

We really shouldn't promote it in embedded contexts, or people will see "ooh, nostd mutex, shiny!" and use it and shoot themselves in the foot.

I agree. Spinning is an easy way to deadlock when there are interrupts handled. It is important to know if you are in cooperative or preemptive execution.

I use the heap with the nrf-softdevice, spawning cooperative executors from the preemptive executor after the heap is initialized. Just initialize the heap first thing when nothing is spawned that can allocate. Nothing is going to be allocating before you have an uncontested chance to initialize.

const HEAP_SIZE: usize = 192 * 1024;
#[repr(align(1024))]
struct HeapBuffer([u8; HEAP_SIZE]);
static mut HEAP_BUFFER: HeapBuffer = HeapBuffer([0; HEAP_SIZE]);
#[global_allocator]
static HEAP: Heap = Heap::empty();

///
/// SoftDevice gets 0,1,and 4 priorities
/// GPIO and Timer get 2 priorities
/// SAADC gets 3 priority
///
/// SoftDevice uses:
///
/// TIMER0
/// SWI1_EGU1 is priority 6 Radio Notification
/// SWI2_EGU2 is priority 6 SoftDevice Event Notification
/// SWI4_EGU4 is reserved for future use
/// SWI5_EGU5 is priority 4 SoftDevice Processing
/// PPI 20 and up ... and maybe more
/// check at https://infocenter.nordicsemi.com/pdf/S140_SDS_v2.1.pdf
///
///
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
    // Configure for low-power SoftDevice operation
    let mut config = embassy_nrf::config::Config::default();
    config.gpiote_interrupt_priority = Priority::P2;
    config.time_interrupt_priority = Priority::P2;
    config.lfclk_source = embassy_nrf::config::LfclkSource::ExternalXtal;
    let mut p = embassy_nrf::init(config);

    // Confirm to the bootloader that we are alive by consuming the watchdog
    let wdt_config = wdt::Config::try_new(&p.WDT).unwrap();
    let (_wdt, [wdt_handle]) = match Watchdog::try_new(p.WDT, wdt_config) {
        Ok(x) => x,
        Err(_) => {
            // Watchdog already active with the wrong number of handles, waiting for it to timeout...
            loop {
                cortex_m::asm::wfe();
            }
        }
    };

    // Okay, we are booted.
    info!("Hello World!");

    // Before anything can attempt to allocate from the HEAP, initialize the heap memory
    // SAFETY: No tasks that can touch the heap may be spawned before this initialization
    unsafe {
        HEAP.init(HEAP_BUFFER.0.as_mut_ptr() as usize, HEAP_BUFFER.0.len());
    }
    
    // Configuration ...
    
    // High-priority executor: SWI0_EGU0
    info!("Starting SWI0 executor and task");
    interrupt::SWI0_EGU0.set_priority(Priority::P5);
    let swi_spawner = EXECUTOR_SWI0.start(interrupt::SWI0_EGU0);
    unwrap!(swi_spawner.spawn(run_watchdog(wdt_handle)));
    unwrap!(swi_spawner.spawn(run_ble()));

    // Medium-priority executor: SWI3_EGU3
    info!("Starting SWI3 executor and task");
    interrupt::SWI3_EGU3.set_priority(Priority::P6);
    let swi_spawner = EXECUTOR_SWI3.start(interrupt::SWI3_EGU3);
    unwrap!(swi_spawner.spawn(run_battery_check(saadc, p.P0_02.degrade())));
    unwrap!(swi_spawner.spawn(run_imu(imu_spim, imu_spi_cs, imu_spi_int)));
    unwrap!(swi_spawner.spawn(run_pdm(pdm)));

    // Run low priority, expensive/blocking work in thread mode
    unwrap!(spawner.spawn(run_cpu_imu()));
    unwrap!(spawner.spawn(run_cpu_pdm()));
    unwrap!(spawner.spawn(run_cpu_heartbeat()));
    unwrap!(spawner.spawn(run_cpu_temperature()));
    unwrap!(spawner.spawn(run_cpu_nand(nand_spim, nand_spi_cs, nand_wp, nand_hold)));
    unwrap!(spawner.spawn(run_dfu()));
   
    ...
    ```

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants