raspberry_pi_pico_2/
main.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright OxidOS Automotive 2025.
4
5//! Tock kernel for the Raspberry Pi Pico 2.
6//!
7//! It is based on RP2350SoC SoC (Cortex M33).
8
9#![no_std]
10// Disable this attribute when documenting, as a workaround for
11// https://github.com/rust-lang/rust/issues/62184.
12#![cfg_attr(not(doc), no_main)]
13#![deny(missing_docs)]
14
15use core::ptr::addr_of_mut;
16
17use capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm;
18use components::gpio::GpioComponent;
19use components::led::LedsComponent;
20use enum_primitive::cast::FromPrimitive;
21use kernel::component::Component;
22use kernel::hil::led::LedHigh;
23use kernel::platform::{KernelResources, SyscallDriverLookup};
24use kernel::process::ProcessArray;
25use kernel::scheduler::round_robin::RoundRobinSched;
26use kernel::syscall::SyscallDriver;
27use kernel::{capabilities, create_capability, static_init, Kernel};
28
29use rp2350::chip::{Rp2350, Rp2350DefaultPeripherals};
30use rp2350::clocks::{
31    AdcAuxiliaryClockSource, HstxAuxiliaryClockSource, PeripheralAuxiliaryClockSource, PllClock,
32    ReferenceAuxiliaryClockSource, ReferenceClockSource, SystemAuxiliaryClockSource,
33    SystemClockSource, UsbAuxiliaryClockSource,
34};
35use rp2350::gpio::{GpioFunction, RPGpio, RPGpioPin};
36use rp2350::resets::Peripheral;
37use rp2350::timer::RPTimer;
38#[allow(unused)]
39use rp2350::{xosc, BASE_VECTORS};
40
41mod io;
42
43mod flash_bootloader;
44
45/// Allocate memory for the stack
46//
47// When compiling for a macOS host, the `link_section` attribute is elided as
48// it yields the following error: `mach-o section specifier requires a segment
49// and section separated by a comma`.
50#[cfg_attr(not(target_os = "macos"), link_section = ".stack_buffer")]
51#[no_mangle]
52static mut STACK_MEMORY: [u8; 0x3000] = [0; 0x3000];
53
54// Manually setting the boot header section that contains the FCB header
55//
56// When compiling for a macOS host, the `link_section` attribute is elided as
57// it yields the following error: `mach-o section specifier requires a segment
58// and section separated by a comma`.
59#[cfg_attr(not(target_os = "macos"), link_section = ".flash_bootloader")]
60#[used]
61static FLASH_BOOTLOADER: [u8; 256] = flash_bootloader::FLASH_BOOTLOADER;
62
63// When compiling for a macOS host, the `link_section` attribute is elided as
64// it yields the following error: `mach-o section specifier requires a segment
65// and section separated by a comma`.
66#[cfg_attr(not(target_os = "macos"), link_section = ".metadata_block")]
67#[used]
68static METADATA_BLOCK: [u8; 28] = flash_bootloader::METADATA_BLOCK;
69
70// State for loading and holding applications.
71// How should the kernel respond when a process faults.
72const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
73    capsules_system::process_policies::PanicFaultPolicy {};
74
75// Number of concurrent processes this platform supports.
76const NUM_PROCS: usize = 4;
77
78type ChipHw = Rp2350<'static, Rp2350DefaultPeripherals<'static>>;
79
80/// Static variables used by io.rs.
81static mut PROCESSES: Option<&'static ProcessArray<NUM_PROCS>> = None;
82
83static mut CHIP: Option<&'static Rp2350<Rp2350DefaultPeripherals<'static>>> = None;
84static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
85    None;
86
87/// Supported drivers by the platform
88pub struct RaspberryPiPico2 {
89    ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
90    console: &'static capsules_core::console::Console<'static>,
91    scheduler: &'static RoundRobinSched<'static>,
92    systick: cortexm33::systick::SysTick,
93    alarm: &'static capsules_core::alarm::AlarmDriver<
94        'static,
95        VirtualMuxAlarm<'static, rp2350::timer::RPTimer<'static>>,
96    >,
97    gpio: &'static capsules_core::gpio::GPIO<'static, RPGpioPin<'static>>,
98    led: &'static capsules_core::led::LedDriver<'static, LedHigh<'static, RPGpioPin<'static>>, 1>,
99}
100
101impl SyscallDriverLookup for RaspberryPiPico2 {
102    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
103    where
104        F: FnOnce(Option<&dyn SyscallDriver>) -> R,
105    {
106        match driver_num {
107            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
108            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
109            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
110            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
111            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
112            _ => f(None),
113        }
114    }
115}
116
117impl KernelResources<Rp2350<'static, Rp2350DefaultPeripherals<'static>>> for RaspberryPiPico2 {
118    type SyscallDriverLookup = Self;
119    type SyscallFilter = ();
120    type ProcessFault = ();
121    type Scheduler = RoundRobinSched<'static>;
122    type SchedulerTimer = cortexm33::systick::SysTick;
123    type WatchDog = ();
124    type ContextSwitchCallback = ();
125
126    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
127        self
128    }
129    fn syscall_filter(&self) -> &Self::SyscallFilter {
130        &()
131    }
132    fn process_fault(&self) -> &Self::ProcessFault {
133        &()
134    }
135    fn scheduler(&self) -> &Self::Scheduler {
136        self.scheduler
137    }
138    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
139        &self.systick
140    }
141    fn watchdog(&self) -> &Self::WatchDog {
142        &()
143    }
144    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
145        &()
146    }
147}
148
149#[allow(dead_code)]
150extern "C" {
151    /// Entry point used for debugger
152    ///
153    /// When loaded using gdb, the Raspberry Pi Pico 2 is not reset
154    /// by default. Without this function, gdb sets the PC to the
155    /// beginning of the flash. This is not correct, as the RP2350
156    /// has a more complex boot process.
157    ///
158    /// This function is set to be the entry point for gdb and is used
159    /// to send the RP2350 back in the bootloader so that all the boot
160    /// sequence is performed.
161    fn jump_to_bootloader();
162}
163
164#[cfg(any(doc, all(target_arch = "arm", target_os = "none")))]
165core::arch::global_asm!(
166    "
167    .section .jump_to_bootloader, \"ax\"
168    .global jump_to_bootloader
169    .thumb_func
170  jump_to_bootloader:
171    movs r0, #0
172    ldr r1, =(0xe0000000 + 0x0000ed08)
173    str r0, [r1]
174    ldmia r0!, {{r1, r2}}
175    msr msp, r1
176    bx r2
177    "
178);
179
180fn init_clocks(peripherals: &Rp2350DefaultPeripherals) {
181    // // Start tick in watchdog
182    // peripherals.watchdog.start_tick(12);
183    //
184    // Disable the Resus clock
185    peripherals.clocks.disable_resus();
186
187    // Setup the external Oscillator
188    peripherals.xosc.init();
189
190    // disable ref and sys clock aux sources
191    peripherals.clocks.disable_sys_aux();
192    peripherals.clocks.disable_ref_aux();
193
194    peripherals
195        .resets
196        .reset(&[Peripheral::PllSys, Peripheral::PllUsb]);
197    peripherals
198        .resets
199        .unreset(&[Peripheral::PllSys, Peripheral::PllUsb], true);
200
201    // Configure PLLs (from Pico SDK)
202    //                   REF     FBDIV VCO            POSTDIV
203    // PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz
204    // PLL USB: 12 / 1 = 12MHz * 40  = 480 MHz / 5 / 2 =  48MHz
205
206    // It seems that the external oscillator is clocked at 12 MHz
207
208    peripherals
209        .clocks
210        .pll_init(PllClock::Sys, 12, 1, 1500 * 1000000, 6, 2);
211    peripherals
212        .clocks
213        .pll_init(PllClock::Usb, 12, 1, 480 * 1000000, 5, 2);
214
215    // pico-sdk: // CLK_REF = XOSC (12MHz) / 1 = 12MHz
216    peripherals.clocks.configure_reference(
217        ReferenceClockSource::Xosc,
218        ReferenceAuxiliaryClockSource::PllUsb,
219        12000000,
220        12000000,
221    );
222    // pico-sdk: CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
223    peripherals.clocks.configure_system(
224        SystemClockSource::Auxiliary,
225        SystemAuxiliaryClockSource::PllSys,
226        125000000,
227        125000000,
228    );
229
230    // pico-sdk: CLK USB = PLL USB (48MHz) / 1 = 48MHz
231    peripherals
232        .clocks
233        .configure_usb(UsbAuxiliaryClockSource::PllSys, 48000000, 48000000);
234    // pico-sdk: CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
235    peripherals
236        .clocks
237        .configure_adc(AdcAuxiliaryClockSource::PllUsb, 48000000, 48000000);
238    // pico-sdk: CLK HSTX = PLL USB (48MHz) / 1024 = 46875Hz
239    peripherals
240        .clocks
241        .configure_hstx(HstxAuxiliaryClockSource::PllSys, 48000000, 46875);
242    // pico-sdk:
243    // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
244    // Normally choose clk_sys or clk_usb
245    peripherals
246        .clocks
247        .configure_peripheral(PeripheralAuxiliaryClockSource::System, 125000000);
248}
249
250unsafe fn get_peripherals() -> &'static mut Rp2350DefaultPeripherals<'static> {
251    static_init!(Rp2350DefaultPeripherals, Rp2350DefaultPeripherals::new())
252}
253
254/// Main function called after RAM initialized.
255#[no_mangle]
256pub unsafe fn main() {
257    rp2350::init();
258
259    let peripherals = get_peripherals();
260    peripherals.resolve_dependencies();
261
262    peripherals.resets.reset_all_except(&[
263        Peripheral::IOQSpi,
264        Peripheral::PadsQSpi,
265        Peripheral::PllUsb,
266        Peripheral::PllSys,
267    ]);
268
269    init_clocks(peripherals);
270
271    peripherals.resets.unreset_all_except(&[], true);
272
273    // Set the UART used for panic
274    (*addr_of_mut!(io::WRITER)).set_uart(&peripherals.uart0);
275
276    let gpio_tx = peripherals.pins.get_pin(RPGpio::GPIO0);
277    let gpio_rx = peripherals.pins.get_pin(RPGpio::GPIO1);
278    gpio_rx.set_function(GpioFunction::UART);
279    gpio_tx.set_function(GpioFunction::UART);
280
281    //// Disable IE for pads 26-29 (the Pico SDK runtime does this, not sure why)
282    for pin in 26..30 {
283        peripherals
284            .pins
285            .get_pin(RPGpio::from_usize(pin).unwrap())
286            .deactivate_pads();
287    }
288
289    let chip = static_init!(
290        Rp2350<Rp2350DefaultPeripherals>,
291        Rp2350::new(peripherals, &peripherals.sio)
292    );
293
294    CHIP = Some(chip);
295
296    // Create an array to hold process references.
297    let processes = components::process_array::ProcessArrayComponent::new()
298        .finalize(components::process_array_component_static!(NUM_PROCS));
299    PROCESSES = Some(processes);
300
301    let board_kernel = static_init!(Kernel, Kernel::new(processes.as_slice()));
302
303    let process_management_capability =
304        create_capability!(capabilities::ProcessManagementCapability);
305    let memory_allocation_capability = create_capability!(capabilities::MemoryAllocationCapability);
306
307    let mux_alarm = components::alarm::AlarmMuxComponent::new(&peripherals.timer0)
308        .finalize(components::alarm_mux_component_static!(RPTimer));
309
310    let alarm = components::alarm::AlarmDriverComponent::new(
311        board_kernel,
312        capsules_core::alarm::DRIVER_NUM,
313        mux_alarm,
314    )
315    .finalize(components::alarm_component_static!(RPTimer));
316
317    let uart_mux = components::console::UartMuxComponent::new(&peripherals.uart0, 115200)
318        .finalize(components::uart_mux_component_static!());
319
320    // Setup the console.
321    let console = components::console::ConsoleComponent::new(
322        board_kernel,
323        capsules_core::console::DRIVER_NUM,
324        uart_mux,
325    )
326    .finalize(components::console_component_static!());
327
328    let gpio = GpioComponent::new(
329        board_kernel,
330        capsules_core::gpio::DRIVER_NUM,
331        components::gpio_component_helper!(
332            RPGpioPin,
333            // Used for serial communication. Comment them in if you don't use serial.
334            // 0 => peripherals.pins.get_pin(RPGpio::GPIO0),
335            // 1 => peripherals.pins.get_pin(RPGpio::GPIO1),
336            2 => peripherals.pins.get_pin(RPGpio::GPIO2),
337            3 => peripherals.pins.get_pin(RPGpio::GPIO3),
338            4 => peripherals.pins.get_pin(RPGpio::GPIO4),
339            5 => peripherals.pins.get_pin(RPGpio::GPIO5),
340            6 => peripherals.pins.get_pin(RPGpio::GPIO6),
341            7 => peripherals.pins.get_pin(RPGpio::GPIO7),
342            8 => peripherals.pins.get_pin(RPGpio::GPIO8),
343            9 => peripherals.pins.get_pin(RPGpio::GPIO9),
344            10 => peripherals.pins.get_pin(RPGpio::GPIO10),
345            11 => peripherals.pins.get_pin(RPGpio::GPIO11),
346            12 => peripherals.pins.get_pin(RPGpio::GPIO12),
347            13 => peripherals.pins.get_pin(RPGpio::GPIO13),
348            14 => peripherals.pins.get_pin(RPGpio::GPIO14),
349            15 => peripherals.pins.get_pin(RPGpio::GPIO15),
350            16 => peripherals.pins.get_pin(RPGpio::GPIO16),
351            17 => peripherals.pins.get_pin(RPGpio::GPIO17),
352            18 => peripherals.pins.get_pin(RPGpio::GPIO18),
353            19 => peripherals.pins.get_pin(RPGpio::GPIO19),
354            20 => peripherals.pins.get_pin(RPGpio::GPIO20),
355            21 => peripherals.pins.get_pin(RPGpio::GPIO21),
356            22 => peripherals.pins.get_pin(RPGpio::GPIO22),
357            23 => peripherals.pins.get_pin(RPGpio::GPIO23),
358            24 => peripherals.pins.get_pin(RPGpio::GPIO24),
359            // LED pin
360            // 25 => peripherals.pins.get_pin(RPGpio::GPIO25),
361            26 => peripherals.pins.get_pin(RPGpio::GPIO26),
362            27 => peripherals.pins.get_pin(RPGpio::GPIO27),
363            28 => peripherals.pins.get_pin(RPGpio::GPIO28),
364            29 => peripherals.pins.get_pin(RPGpio::GPIO29)
365        ),
366    )
367    .finalize(components::gpio_component_static!(RPGpioPin<'static>));
368
369    let led = LedsComponent::new().finalize(components::led_component_static!(
370        LedHigh<'static, RPGpioPin<'static>>,
371        LedHigh::new(peripherals.pins.get_pin(RPGpio::GPIO25))
372    ));
373
374    // Create the debugger object that handles calls to `debug!()`.
375    components::debug_writer::DebugWriterComponent::new::<
376        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
377    >(
378        uart_mux,
379        create_capability!(capabilities::SetDebugWriterCapability),
380    )
381    .finalize(components::debug_writer_component_static!());
382
383    // PROCESS CONSOLE
384    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
385        .finalize(components::process_printer_text_component_static!());
386    PROCESS_PRINTER = Some(process_printer);
387
388    let process_console = components::process_console::ProcessConsoleComponent::new(
389        board_kernel,
390        uart_mux,
391        mux_alarm,
392        process_printer,
393        Some(cortexm33::support::reset),
394    )
395    .finalize(components::process_console_component_static!(RPTimer));
396    let _ = process_console.start();
397
398    let scheduler = components::sched::round_robin::RoundRobinComponent::new(processes)
399        .finalize(components::round_robin_component_static!(NUM_PROCS));
400
401    let raspberry_pi_pico = RaspberryPiPico2 {
402        ipc: kernel::ipc::IPC::new(
403            board_kernel,
404            kernel::ipc::DRIVER_NUM,
405            &memory_allocation_capability,
406        ),
407        console,
408        alarm,
409        gpio,
410        led,
411        scheduler,
412        systick: cortexm33::systick::SysTick::new_with_calibration(125_000_000),
413    };
414
415    kernel::debug!("Initialization complete. Enter main loop");
416
417    // These symbols are defined in the linker script.
418    extern "C" {
419        /// Beginning of the ROM region containing app images.
420        static _sapps: u8;
421        /// End of the ROM region containing app images.
422        static _eapps: u8;
423        /// Beginning of the RAM region for app memory.
424        static mut _sappmem: u8;
425        /// End of the RAM region for app memory.
426        static _eappmem: u8;
427    }
428
429    kernel::process::load_processes(
430        board_kernel,
431        chip,
432        core::slice::from_raw_parts(
433            core::ptr::addr_of!(_sapps),
434            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
435        ),
436        core::slice::from_raw_parts_mut(
437            core::ptr::addr_of_mut!(_sappmem),
438            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
439        ),
440        &FAULT_RESPONSE,
441        &process_management_capability,
442    )
443    .unwrap_or_else(|err| {
444        kernel::debug!("Error loading processes!");
445        kernel::debug!("{:?}", err);
446    });
447
448    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
449
450    board_kernel.kernel_loop(
451        &raspberry_pi_pico,
452        chip,
453        Some(&raspberry_pi_pico.ipc),
454        &main_loop_capability,
455    );
456}