raspberry_pi_pico/
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 Tock Contributors 2022.
4
5//! Tock kernel for the Raspberry Pi Pico.
6//!
7//! It is based on RP2040SoC SoC (Cortex M0+).
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, addr_of_mut};
16
17use capsules_core::i2c_master::I2CMasterDriver;
18use capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm;
19use components::date_time_component_static;
20use components::gpio::GpioComponent;
21use components::led::LedsComponent;
22use enum_primitive::cast::FromPrimitive;
23use kernel::component::Component;
24use kernel::debug;
25use kernel::hil::gpio::{Configure, FloatingState};
26use kernel::hil::i2c::I2CMaster;
27use kernel::hil::led::LedHigh;
28use kernel::hil::usb::Client;
29use kernel::platform::{KernelResources, SyscallDriverLookup};
30use kernel::scheduler::round_robin::RoundRobinSched;
31use kernel::syscall::SyscallDriver;
32use kernel::{capabilities, create_capability, static_init, Kernel};
33
34use rp2040::adc::{Adc, Channel};
35use rp2040::chip::{Rp2040, Rp2040DefaultPeripherals};
36use rp2040::clocks::{
37    AdcAuxiliaryClockSource, PeripheralAuxiliaryClockSource, PllClock,
38    ReferenceAuxiliaryClockSource, ReferenceClockSource, RtcAuxiliaryClockSource,
39    SystemAuxiliaryClockSource, SystemClockSource, UsbAuxiliaryClockSource,
40};
41use rp2040::gpio::{GpioFunction, RPGpio, RPGpioPin};
42use rp2040::i2c::I2c;
43use rp2040::resets::Peripheral;
44use rp2040::sysinfo;
45use rp2040::timer::RPTimer;
46
47mod io;
48
49mod flash_bootloader;
50
51/// Allocate memory for the stack
52#[no_mangle]
53#[link_section = ".stack_buffer"]
54pub static mut STACK_MEMORY: [u8; 0x1500] = [0; 0x1500];
55
56// Manually setting the boot header section that contains the FCB header
57#[used]
58#[link_section = ".flash_bootloader"]
59static FLASH_BOOTLOADER: [u8; 256] = flash_bootloader::FLASH_BOOTLOADER;
60
61// State for loading and holding applications.
62// How should the kernel respond when a process faults.
63const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
64    capsules_system::process_policies::PanicFaultPolicy {};
65
66// Number of concurrent processes this platform supports.
67const NUM_PROCS: usize = 4;
68
69static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
70    [None; NUM_PROCS];
71
72static mut CHIP: Option<&'static Rp2040<Rp2040DefaultPeripherals>> = None;
73static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
74    None;
75
76type TemperatureRp2040Sensor = components::temperature_rp2040::TemperatureRp2040ComponentType<
77    capsules_core::virtualizers::virtual_adc::AdcDevice<'static, rp2040::adc::Adc<'static>>,
78>;
79type TemperatureDriver = components::temperature::TemperatureComponentType<TemperatureRp2040Sensor>;
80
81/// Supported drivers by the platform
82pub struct RaspberryPiPico {
83    ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
84    console: &'static capsules_core::console::Console<'static>,
85    alarm: &'static capsules_core::alarm::AlarmDriver<
86        'static,
87        VirtualMuxAlarm<'static, rp2040::timer::RPTimer<'static>>,
88    >,
89    gpio: &'static capsules_core::gpio::GPIO<'static, RPGpioPin<'static>>,
90    led: &'static capsules_core::led::LedDriver<'static, LedHigh<'static, RPGpioPin<'static>>, 1>,
91    adc: &'static capsules_core::adc::AdcVirtualized<'static>,
92    temperature: &'static TemperatureDriver,
93    i2c: &'static capsules_core::i2c_master::I2CMasterDriver<'static, I2c<'static, 'static>>,
94
95    date_time:
96        &'static capsules_extra::date_time::DateTimeCapsule<'static, rp2040::rtc::Rtc<'static>>,
97    scheduler: &'static RoundRobinSched<'static>,
98    systick: cortexm0p::systick::SysTick,
99}
100
101impl SyscallDriverLookup for RaspberryPiPico {
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            capsules_core::adc::DRIVER_NUM => f(Some(self.adc)),
113            capsules_extra::temperature::DRIVER_NUM => f(Some(self.temperature)),
114            capsules_core::i2c_master::DRIVER_NUM => f(Some(self.i2c)),
115            capsules_extra::date_time::DRIVER_NUM => f(Some(self.date_time)),
116            _ => f(None),
117        }
118    }
119}
120
121impl KernelResources<Rp2040<'static, Rp2040DefaultPeripherals<'static>>> for RaspberryPiPico {
122    type SyscallDriverLookup = Self;
123    type SyscallFilter = ();
124    type ProcessFault = ();
125    type Scheduler = RoundRobinSched<'static>;
126    type SchedulerTimer = cortexm0p::systick::SysTick;
127    type WatchDog = ();
128    type ContextSwitchCallback = ();
129
130    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
131        self
132    }
133    fn syscall_filter(&self) -> &Self::SyscallFilter {
134        &()
135    }
136    fn process_fault(&self) -> &Self::ProcessFault {
137        &()
138    }
139    fn scheduler(&self) -> &Self::Scheduler {
140        self.scheduler
141    }
142    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
143        &self.systick
144    }
145    fn watchdog(&self) -> &Self::WatchDog {
146        &()
147    }
148    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
149        &()
150    }
151}
152
153#[allow(dead_code)]
154extern "C" {
155    /// Entry point used for debugger
156    ///
157    /// When loaded using gdb, the Raspberry Pi Pico is not reset
158    /// by default. Without this function, gdb sets the PC to the
159    /// beginning of the flash. This is not correct, as the RP2040
160    /// has a more complex boot process.
161    ///
162    /// This function is set to be the entry point for gdb and is used
163    /// to send the RP2040 back in the bootloader so that all the boot
164    /// sequence is performed.
165    fn jump_to_bootloader();
166}
167
168#[cfg(any(doc, all(target_arch = "arm", target_os = "none")))]
169core::arch::global_asm!(
170    "
171    .section .jump_to_bootloader, \"ax\"
172    .global jump_to_bootloader
173    .thumb_func
174  jump_to_bootloader:
175    movs r0, #0
176    ldr r1, =(0xe0000000 + 0x0000ed08)
177    str r0, [r1]
178    ldmia r0!, {{r1, r2}}
179    msr msp, r1
180    bx r2
181    "
182);
183
184fn init_clocks(peripherals: &Rp2040DefaultPeripherals) {
185    // Start tick in watchdog
186    peripherals.watchdog.start_tick(12);
187
188    // Disable the Resus clock
189    peripherals.clocks.disable_resus();
190
191    // Setup the external Oscillator
192    peripherals.xosc.init();
193
194    // disable ref and sys clock aux sources
195    peripherals.clocks.disable_sys_aux();
196    peripherals.clocks.disable_ref_aux();
197
198    peripherals
199        .resets
200        .reset(&[Peripheral::PllSys, Peripheral::PllUsb]);
201    peripherals
202        .resets
203        .unreset(&[Peripheral::PllSys, Peripheral::PllUsb], true);
204
205    // Configure PLLs (from Pico SDK)
206    //                   REF     FBDIV VCO            POSTDIV
207    // PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz
208    // PLL USB: 12 / 1 = 12MHz * 40  = 480 MHz / 5 / 2 =  48MHz
209
210    // It seems that the external oscillator is clocked at 12 MHz
211
212    peripherals
213        .clocks
214        .pll_init(PllClock::Sys, 12, 1, 1500 * 1000000, 6, 2);
215    peripherals
216        .clocks
217        .pll_init(PllClock::Usb, 12, 1, 480 * 1000000, 5, 2);
218
219    // pico-sdk: // CLK_REF = XOSC (12MHz) / 1 = 12MHz
220    peripherals.clocks.configure_reference(
221        ReferenceClockSource::Xosc,
222        ReferenceAuxiliaryClockSource::PllUsb,
223        12000000,
224        12000000,
225    );
226    // pico-sdk: CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
227    peripherals.clocks.configure_system(
228        SystemClockSource::Auxiliary,
229        SystemAuxiliaryClockSource::PllSys,
230        125000000,
231        125000000,
232    );
233    // pico-sdk: CLK USB = PLL USB (48MHz) / 1 = 48MHz
234    peripherals
235        .clocks
236        .configure_usb(UsbAuxiliaryClockSource::PllSys, 48000000, 48000000);
237    // pico-sdk: CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
238    peripherals
239        .clocks
240        .configure_adc(AdcAuxiliaryClockSource::PllUsb, 48000000, 48000000);
241    // pico-sdk: CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
242    peripherals
243        .clocks
244        .configure_rtc(RtcAuxiliaryClockSource::PllSys, 48000000, 46875);
245    // pico-sdk:
246    // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
247    // Normally choose clk_sys or clk_usb
248    peripherals
249        .clocks
250        .configure_peripheral(PeripheralAuxiliaryClockSource::System, 125000000);
251}
252
253/// This is in a separate, inline(never) function so that its stack frame is
254/// removed when this function returns. Otherwise, the stack space used for
255/// these static_inits is wasted.
256#[inline(never)]
257pub unsafe fn start() -> (
258    &'static kernel::Kernel,
259    RaspberryPiPico,
260    &'static rp2040::chip::Rp2040<'static, Rp2040DefaultPeripherals<'static>>,
261) {
262    // Loads relocations and clears BSS
263    rp2040::init();
264
265    let peripherals = static_init!(Rp2040DefaultPeripherals, Rp2040DefaultPeripherals::new());
266    peripherals.resolve_dependencies();
267
268    // Reset all peripherals except QSPI (we might be booting from Flash), PLL USB and PLL SYS
269    peripherals.resets.reset_all_except(&[
270        Peripheral::IOQSpi,
271        Peripheral::PadsQSpi,
272        Peripheral::PllUsb,
273        Peripheral::PllSys,
274    ]);
275
276    // Unreset all the peripherals that do not require clock setup as they run using the sys_clk or ref_clk
277    // Wait for the peripherals to reset
278    peripherals.resets.unreset_all_except(
279        &[
280            Peripheral::Adc,
281            Peripheral::Rtc,
282            Peripheral::Spi0,
283            Peripheral::Spi1,
284            Peripheral::Uart0,
285            Peripheral::Uart1,
286            Peripheral::UsbCtrl,
287        ],
288        true,
289    );
290
291    init_clocks(peripherals);
292
293    // Unreset all peripherals
294    peripherals.resets.unreset_all_except(&[], true);
295
296    // Set the UART used for panic
297    (*addr_of_mut!(io::WRITER)).set_uart(&peripherals.uart0);
298
299    //set RX and TX pins in UART mode
300    let gpio_tx = peripherals.pins.get_pin(RPGpio::GPIO0);
301    let gpio_rx = peripherals.pins.get_pin(RPGpio::GPIO1);
302    gpio_rx.set_function(GpioFunction::UART);
303    gpio_tx.set_function(GpioFunction::UART);
304
305    // Disable IE for pads 26-29 (the Pico SDK runtime does this, not sure why)
306    for pin in 26..30 {
307        peripherals
308            .pins
309            .get_pin(RPGpio::from_usize(pin).unwrap())
310            .deactivate_pads();
311    }
312
313    let chip = static_init!(
314        Rp2040<Rp2040DefaultPeripherals>,
315        Rp2040::new(peripherals, &peripherals.sio)
316    );
317
318    CHIP = Some(chip);
319
320    let board_kernel = static_init!(Kernel, Kernel::new(&*addr_of!(PROCESSES)));
321
322    let process_management_capability =
323        create_capability!(capabilities::ProcessManagementCapability);
324    let memory_allocation_capability = create_capability!(capabilities::MemoryAllocationCapability);
325
326    let mux_alarm = components::alarm::AlarmMuxComponent::new(&peripherals.timer)
327        .finalize(components::alarm_mux_component_static!(RPTimer));
328
329    let alarm = components::alarm::AlarmDriverComponent::new(
330        board_kernel,
331        capsules_core::alarm::DRIVER_NUM,
332        mux_alarm,
333    )
334    .finalize(components::alarm_component_static!(RPTimer));
335
336    // CDC
337    let strings = static_init!(
338        [&str; 3],
339        [
340            "Raspberry Pi",      // Manufacturer
341            "Pico - TockOS",     // Product
342            "00000000000000000", // Serial number
343        ]
344    );
345
346    let cdc = components::cdc::CdcAcmComponent::new(
347        &peripherals.usb,
348        //capsules_extra::usb::cdc::MAX_CTRL_PACKET_SIZE_RP2040,
349        64,
350        peripherals.sysinfo.get_manufacturer_rp2040() as u16,
351        peripherals.sysinfo.get_part() as u16,
352        strings,
353        mux_alarm,
354        None,
355    )
356    .finalize(components::cdc_acm_component_static!(
357        rp2040::usb::UsbCtrl,
358        rp2040::timer::RPTimer
359    ));
360
361    // UART
362    // Create a shared UART channel for kernel debug.
363    let uart_mux = components::console::UartMuxComponent::new(cdc, 115200)
364        .finalize(components::uart_mux_component_static!());
365
366    // Uncomment this to use UART as an output
367    // let uart_mux2 = components::console::UartMuxComponent::new(
368    //     &peripherals.uart0,
369    //     115200,
370    // )
371    // .finalize(components::uart_mux_component_static!());
372
373    // Setup the console.
374    let console = components::console::ConsoleComponent::new(
375        board_kernel,
376        capsules_core::console::DRIVER_NUM,
377        uart_mux,
378    )
379    .finalize(components::console_component_static!());
380    // Create the debugger object that handles calls to `debug!()`.
381    components::debug_writer::DebugWriterComponent::new(uart_mux)
382        .finalize(components::debug_writer_component_static!());
383
384    cdc.enable();
385    cdc.attach();
386
387    let gpio = GpioComponent::new(
388        board_kernel,
389        capsules_core::gpio::DRIVER_NUM,
390        components::gpio_component_helper!(
391            RPGpioPin,
392            // Used for serial communication. Comment them in if you don't use serial.
393            // 0 => peripherals.pins.get_pin(RPGpio::GPIO0),
394            // 1 => peripherals.pins.get_pin(RPGpio::GPIO1),
395            2 => peripherals.pins.get_pin(RPGpio::GPIO2),
396            3 => peripherals.pins.get_pin(RPGpio::GPIO3),
397            // Used for i2c. Comment them in if you don't use i2c.
398            // 4 => peripherals.pins.get_pin(RPGpio::GPIO4),
399            // 5 => peripherals.pins.get_pin(RPGpio::GPIO5),
400            6 => peripherals.pins.get_pin(RPGpio::GPIO6),
401            7 => peripherals.pins.get_pin(RPGpio::GPIO7),
402            8 => peripherals.pins.get_pin(RPGpio::GPIO8),
403            9 => peripherals.pins.get_pin(RPGpio::GPIO9),
404            10 => peripherals.pins.get_pin(RPGpio::GPIO10),
405            11 => peripherals.pins.get_pin(RPGpio::GPIO11),
406            12 => peripherals.pins.get_pin(RPGpio::GPIO12),
407            13 => peripherals.pins.get_pin(RPGpio::GPIO13),
408            14 => peripherals.pins.get_pin(RPGpio::GPIO14),
409            15 => peripherals.pins.get_pin(RPGpio::GPIO15),
410            16 => peripherals.pins.get_pin(RPGpio::GPIO16),
411            17 => peripherals.pins.get_pin(RPGpio::GPIO17),
412            18 => peripherals.pins.get_pin(RPGpio::GPIO18),
413            19 => peripherals.pins.get_pin(RPGpio::GPIO19),
414            20 => peripherals.pins.get_pin(RPGpio::GPIO20),
415            21 => peripherals.pins.get_pin(RPGpio::GPIO21),
416            22 => peripherals.pins.get_pin(RPGpio::GPIO22),
417            23 => peripherals.pins.get_pin(RPGpio::GPIO23),
418            24 => peripherals.pins.get_pin(RPGpio::GPIO24),
419            // LED pin
420            // 25 => peripherals.pins.get_pin(RPGpio::GPIO25),
421
422            // Uncomment to use these as GPIO pins instead of ADC pins
423            // 26 => peripherals.pins.get_pin(RPGpio::GPIO26),
424            // 27 => peripherals.pins.get_pin(RPGpio::GPIO27),
425            // 28 => peripherals.pins.get_pin(RPGpio::GPIO28),
426            // 29 => peripherals.pins.get_pin(RPGpio::GPIO29)
427        ),
428    )
429    .finalize(components::gpio_component_static!(RPGpioPin<'static>));
430
431    let led = LedsComponent::new().finalize(components::led_component_static!(
432        LedHigh<'static, RPGpioPin<'static>>,
433        LedHigh::new(peripherals.pins.get_pin(RPGpio::GPIO25))
434    ));
435
436    peripherals.adc.init();
437
438    let adc_mux = components::adc::AdcMuxComponent::new(&peripherals.adc)
439        .finalize(components::adc_mux_component_static!(Adc));
440
441    let temp_sensor = components::temperature_rp2040::TemperatureRp2040Component::new(
442        adc_mux,
443        Channel::Channel4,
444        1.721,
445        0.706,
446    )
447    .finalize(components::temperature_rp2040_adc_component_static!(
448        rp2040::adc::Adc
449    ));
450
451    // RTC DATE TIME
452
453    match peripherals.rtc.rtc_init() {
454        Ok(()) => {}
455        Err(e) => {
456            debug!("error starting rtc {:?}", e)
457        }
458    }
459
460    let date_time = components::date_time::DateTimeComponent::new(
461        board_kernel,
462        capsules_extra::date_time::DRIVER_NUM,
463        &peripherals.rtc,
464    )
465    .finalize(date_time_component_static!(rp2040::rtc::Rtc<'static>));
466
467    let temp = components::temperature::TemperatureComponent::new(
468        board_kernel,
469        capsules_extra::temperature::DRIVER_NUM,
470        temp_sensor,
471    )
472    .finalize(components::temperature_component_static!(
473        TemperatureRp2040Sensor
474    ));
475
476    let adc_channel_0 = components::adc::AdcComponent::new(adc_mux, Channel::Channel0)
477        .finalize(components::adc_component_static!(Adc));
478
479    let adc_channel_1 = components::adc::AdcComponent::new(adc_mux, Channel::Channel1)
480        .finalize(components::adc_component_static!(Adc));
481
482    let adc_channel_2 = components::adc::AdcComponent::new(adc_mux, Channel::Channel2)
483        .finalize(components::adc_component_static!(Adc));
484
485    let adc_channel_3 = components::adc::AdcComponent::new(adc_mux, Channel::Channel3)
486        .finalize(components::adc_component_static!(Adc));
487
488    let adc_syscall =
489        components::adc::AdcVirtualComponent::new(board_kernel, capsules_core::adc::DRIVER_NUM)
490            .finalize(components::adc_syscall_component_helper!(
491                adc_channel_0,
492                adc_channel_1,
493                adc_channel_2,
494                adc_channel_3,
495            ));
496    // PROCESS CONSOLE
497    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
498        .finalize(components::process_printer_text_component_static!());
499    PROCESS_PRINTER = Some(process_printer);
500
501    let process_console = components::process_console::ProcessConsoleComponent::new(
502        board_kernel,
503        uart_mux,
504        mux_alarm,
505        process_printer,
506        Some(cortexm0p::support::reset),
507    )
508    .finalize(components::process_console_component_static!(RPTimer));
509    let _ = process_console.start();
510
511    let sda_pin = peripherals.pins.get_pin(RPGpio::GPIO4);
512    let scl_pin = peripherals.pins.get_pin(RPGpio::GPIO5);
513
514    sda_pin.set_function(GpioFunction::I2C);
515    scl_pin.set_function(GpioFunction::I2C);
516
517    sda_pin.set_floating_state(FloatingState::PullUp);
518    scl_pin.set_floating_state(FloatingState::PullUp);
519
520    let i2c_master_buffer = static_init!(
521        [u8; capsules_core::i2c_master::BUFFER_LENGTH],
522        [0; capsules_core::i2c_master::BUFFER_LENGTH]
523    );
524    let i2c0 = &peripherals.i2c0;
525    let i2c = static_init!(
526        I2CMasterDriver<I2c<'static, 'static>>,
527        I2CMasterDriver::new(
528            i2c0,
529            i2c_master_buffer,
530            board_kernel.create_grant(
531                capsules_core::i2c_master::DRIVER_NUM,
532                &memory_allocation_capability
533            ),
534        )
535    );
536    i2c0.init(10 * 1000);
537    i2c0.set_master_client(i2c);
538
539    let scheduler = components::sched::round_robin::RoundRobinComponent::new(&*addr_of!(PROCESSES))
540        .finalize(components::round_robin_component_static!(NUM_PROCS));
541
542    let raspberry_pi_pico = RaspberryPiPico {
543        ipc: kernel::ipc::IPC::new(
544            board_kernel,
545            kernel::ipc::DRIVER_NUM,
546            &memory_allocation_capability,
547        ),
548        alarm,
549        gpio,
550        led,
551        console,
552        adc: adc_syscall,
553        temperature: temp,
554        i2c,
555        date_time,
556
557        scheduler,
558        systick: cortexm0p::systick::SysTick::new_with_calibration(125_000_000),
559    };
560
561    let platform_type = match peripherals.sysinfo.get_platform() {
562        sysinfo::Platform::Asic => "ASIC",
563        sysinfo::Platform::Fpga => "FPGA",
564    };
565
566    debug!(
567        "RP2040 Revision {} {}",
568        peripherals.sysinfo.get_revision(),
569        platform_type
570    );
571
572    debug!("Initialization complete. Enter main loop");
573
574    // These symbols are defined in the linker script.
575    extern "C" {
576        /// Beginning of the ROM region containing app images.
577        static _sapps: u8;
578        /// End of the ROM region containing app images.
579        static _eapps: u8;
580        /// Beginning of the RAM region for app memory.
581        static mut _sappmem: u8;
582        /// End of the RAM region for app memory.
583        static _eappmem: u8;
584    }
585
586    kernel::process::load_processes(
587        board_kernel,
588        chip,
589        core::slice::from_raw_parts(
590            core::ptr::addr_of!(_sapps),
591            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
592        ),
593        core::slice::from_raw_parts_mut(
594            core::ptr::addr_of_mut!(_sappmem),
595            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
596        ),
597        &mut *addr_of_mut!(PROCESSES),
598        &FAULT_RESPONSE,
599        &process_management_capability,
600    )
601    .unwrap_or_else(|err| {
602        debug!("Error loading processes!");
603        debug!("{:?}", err);
604    });
605
606    (board_kernel, raspberry_pi_pico, chip)
607}
608
609/// Main function called after RAM initialized.
610#[no_mangle]
611pub unsafe fn main() {
612    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
613
614    let (board_kernel, platform, chip) = start();
615    board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
616}