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