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