Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

First attempt

Let’s start with our initial attempt following the datasheet:

#![no_std]
#![no_main]
use cortex_m::asm;
use cortex_m_rt::entry;
use defmt::{assert_eq, error, info};
use {defmt_rtt as _, panic_probe as _};

#[unsafe(link_section = ".start_block")]
#[unsafe(no_mangle)]
static BOOT_ROM_INFO: [u8; 44] = [
    0xd3, 0xde, 0xff, 0xff, 0x42, 0x01, 0x21, 0x10, 0xff, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x79, 0x35, 0x12, 0xab, 0xf2, 0xeb, 0x88, 0x71,
    0x6c, 0x93, 0x02, 0x10, 0x7c, 0x93, 0x02, 0x10, 0x70, 0xf6, 0x02, 0x10,
    0x90, 0xa3, 0x1a, 0xe7, 0x1f, 0x01, 0x02, 0x03,
];

fn second_cpu() {
    for _ in 1..10000 {
        asm::nop();
    }

    loop {
        info!(">>>>>>>> cpu 2 is alive");
        for _ in 1..100 {
            asm::nop();
        }
    }
}

#[entry]
fn main() -> ! {
    unsafe {
        let vector_table_address =
            core::ptr::read_volatile(0xE000ED08 as *const usize);
        info!("vector table address: {:#x}", vector_table_address);

        let cmd_sequence: [usize; 6] = [
            0,
            0,
            1,
            vector_table_address,
            0x20040000,
            second_cpu as *const usize as usize,
        ];

        // algorithm in page 377
        'outer: loop {
            for _ in 1..10000 {
                asm::nop();
            }

            // discard data from read FIFO until empty
            // SIO: FIFO_RD Register
            while core::ptr::read_volatile(0xd0000050 as *const usize) & 1 == 1
            {
                let answer =
                    core::ptr::read_volatile(0xd0000058 as *const usize);
                info!("cleaning with answer: {}", answer);
            }

            for cmd in cmd_sequence {
                // validate no FIFO error
                // SIO: FIFO_ST Register
                assert_eq!(
                    core::ptr::read_volatile(0xd0000050 as *const usize)
                        & 0b1110,
                    2
                );

                // SIO: FIFO_WR Register
                core::ptr::write_volatile(0xd0000054 as *mut usize, cmd);
                asm::sev();

                info!("waiting for data");
                while core::ptr::read_volatile(0xd0000050 as *const usize) & 1
                    == 0
                {
                    asm::nop();
                }
                info!("got data");

                let answer =
                    core::ptr::read_volatile(0xd0000058 as *const usize);
                info!("cmd: {} with answer: {}", cmd, answer);

                if answer != cmd {
                    error!("bad answer, will restart");
                    continue 'outer;
                }
            }
            break;
        }

        for _ in 1..10000 {
            asm::nop();
        }

        loop {
            info!(">>>>>>>> cpu 1 is alive");
            for _ in 1..100 {
                asm::nop();
            }
        }
    }
}

This will work on waking up CPU 1 but we will quickly encounter a hardware fault.

The reason for the hardware fault is due defmt printing functionally relies on our configured critical section from the cortex-m-rt crate:

[package]
name = "pico-simple"
version = "0.1.0"
edition = "2024"

[dependencies]
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m-rt = { version = "0.7.0" }
cortex-m-rt-macros = "0.7.5"
panic-probe = { version = "1.0.0", features = ["print-defmt"] }
defmt = "1.0.1"
defmt-rtt = "1.0.0"

But as the documentation (and even the feature name!) of this, explicitly states that it is only suitable for single-core machines, which is not the case as we have two CPUs working here. Take a look at the critical-section create to learn why.