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