nrf52840_dongle/
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 Nordic Semiconductor nRF52840 dongle.
6//!
7//! It is based on nRF52840 SoC (Cortex M4 core with a BLE transceiver) with
8//! many exported I/O and peripherals.
9
10#![no_std]
11#![no_main]
12#![deny(missing_docs)]
13
14use core::ptr::addr_of;
15
16use capsules_core::virtualizers::virtual_aes_ccm::MuxAES128CCM;
17use capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm;
18use kernel::component::Component;
19use kernel::deferred_call::DeferredCallClient;
20use kernel::hil::led::LedLow;
21use kernel::hil::symmetric_encryption::AES128;
22use kernel::hil::time::Counter;
23use kernel::platform::{KernelResources, SyscallDriverLookup};
24use kernel::process::ProcessArray;
25use kernel::scheduler::round_robin::RoundRobinSched;
26#[allow(unused_imports)]
27use kernel::{capabilities, create_capability, debug, debug_gpio, debug_verbose, static_init};
28use nrf52840::gpio::Pin;
29use nrf52840::interrupt_service::Nrf52840DefaultPeripherals;
30use nrf52_components::{UartChannel, UartPins};
31
32// The nRF52840 Dongle LEDs
33const LED1_PIN: Pin = Pin::P0_06;
34const LED2_R_PIN: Pin = Pin::P0_08;
35const LED2_G_PIN: Pin = Pin::P1_09;
36const LED2_B_PIN: Pin = Pin::P0_12;
37
38// The nRF52840 Dongle button
39const BUTTON_PIN: Pin = Pin::P1_06;
40const BUTTON_RST_PIN: Pin = Pin::P0_18;
41
42const UART_RTS: Option<Pin> = Some(Pin::P0_13);
43const UART_TXD: Pin = Pin::P0_15;
44const UART_CTS: Option<Pin> = Some(Pin::P0_17);
45const UART_RXD: Pin = Pin::P0_20;
46
47// SPI pins not currently in use, but left here for convenience
48const _SPI_MOSI: Pin = Pin::P1_01;
49const _SPI_MISO: Pin = Pin::P1_02;
50const _SPI_CLK: Pin = Pin::P1_04;
51
52// Constants related to the configuration of the 15.4 network stack; DEFAULT_EXT_SRC_MAC
53// should be replaced by an extended src address generated from device serial number
54const SRC_MAC: u16 = 0xf00f;
55const PAN_ID: u16 = 0xABCD;
56const DEFAULT_EXT_SRC_MAC: [u8; 8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77];
57
58/// UART Writer
59pub mod io;
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 = 8;
68
69/// Static variables used by io.rs.
70static mut PROCESSES: Option<&'static ProcessArray<NUM_PROCS>> = None;
71// Static reference to chip for panic dumps
72static mut CHIP: Option<&'static nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>> = None;
73// Static reference to process printer for panic dumps
74static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
75    None;
76
77/// Dummy buffer that causes the linker to reserve enough space for the stack.
78#[no_mangle]
79#[link_section = ".stack_buffer"]
80static mut STACK_MEMORY: [u8; 0x1000] = [0; 0x1000];
81
82type TemperatureDriver =
83    components::temperature::TemperatureComponentType<nrf52840::temperature::Temp<'static>>;
84type RngDriver = components::rng::RngComponentType<nrf52840::trng::Trng<'static>>;
85
86type Ieee802154Driver = components::ieee802154::Ieee802154ComponentType<
87    nrf52840::ieee802154_radio::Radio<'static>,
88    nrf52840::aes::AesECB<'static>,
89>;
90
91/// Supported drivers by the platform
92pub struct Platform {
93    ble_radio: &'static capsules_extra::ble_advertising_driver::BLE<
94        'static,
95        nrf52840::ble_radio::Radio<'static>,
96        VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
97    >,
98    ieee802154_radio: &'static Ieee802154Driver,
99    button: &'static capsules_core::button::Button<'static, nrf52840::gpio::GPIOPin<'static>>,
100    pconsole: &'static capsules_core::process_console::ProcessConsole<
101        'static,
102        { capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
103        VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
104        components::process_console::Capability,
105    >,
106    console: &'static capsules_core::console::Console<'static>,
107    gpio: &'static capsules_core::gpio::GPIO<'static, nrf52840::gpio::GPIOPin<'static>>,
108    led: &'static capsules_core::led::LedDriver<
109        'static,
110        LedLow<'static, nrf52840::gpio::GPIOPin<'static>>,
111        4,
112    >,
113    rng: &'static RngDriver,
114    temp: &'static TemperatureDriver,
115    ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
116    analog_comparator: &'static capsules_extra::analog_comparator::AnalogComparator<
117        'static,
118        nrf52840::acomp::Comparator<'static>,
119    >,
120    alarm: &'static capsules_core::alarm::AlarmDriver<
121        'static,
122        capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm<
123            'static,
124            nrf52840::rtc::Rtc<'static>,
125        >,
126    >,
127    scheduler: &'static RoundRobinSched<'static>,
128    systick: cortexm4::systick::SysTick,
129}
130
131impl SyscallDriverLookup for Platform {
132    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
133    where
134        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
135    {
136        match driver_num {
137            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
138            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
139            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
140            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
141            capsules_core::button::DRIVER_NUM => f(Some(self.button)),
142            capsules_core::rng::DRIVER_NUM => f(Some(self.rng)),
143            capsules_extra::ble_advertising_driver::DRIVER_NUM => f(Some(self.ble_radio)),
144            capsules_extra::ieee802154::DRIVER_NUM => f(Some(self.ieee802154_radio)),
145            capsules_extra::temperature::DRIVER_NUM => f(Some(self.temp)),
146            capsules_extra::analog_comparator::DRIVER_NUM => f(Some(self.analog_comparator)),
147            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
148            _ => f(None),
149        }
150    }
151}
152
153impl KernelResources<nrf52840::chip::NRF52<'static, Nrf52840DefaultPeripherals<'static>>>
154    for Platform
155{
156    type SyscallDriverLookup = Self;
157    type SyscallFilter = ();
158    type ProcessFault = ();
159    type Scheduler = RoundRobinSched<'static>;
160    type SchedulerTimer = cortexm4::systick::SysTick;
161    type WatchDog = ();
162    type ContextSwitchCallback = ();
163
164    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
165        self
166    }
167    fn syscall_filter(&self) -> &Self::SyscallFilter {
168        &()
169    }
170    fn process_fault(&self) -> &Self::ProcessFault {
171        &()
172    }
173    fn scheduler(&self) -> &Self::Scheduler {
174        self.scheduler
175    }
176    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
177        &self.systick
178    }
179    fn watchdog(&self) -> &Self::WatchDog {
180        &()
181    }
182    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
183        &()
184    }
185}
186
187/// This is in a separate, inline(never) function so that its stack frame is
188/// removed when this function returns. Otherwise, the stack space used for
189/// these static_inits is wasted.
190#[inline(never)]
191pub unsafe fn start() -> (
192    &'static kernel::Kernel,
193    Platform,
194    &'static nrf52840::chip::NRF52<'static, Nrf52840DefaultPeripherals<'static>>,
195) {
196    nrf52840::init();
197
198    let ieee802154_ack_buf = static_init!(
199        [u8; nrf52840::ieee802154_radio::ACK_BUF_SIZE],
200        [0; nrf52840::ieee802154_radio::ACK_BUF_SIZE]
201    );
202    // Initialize chip peripheral drivers
203    let nrf52840_peripherals = static_init!(
204        Nrf52840DefaultPeripherals,
205        Nrf52840DefaultPeripherals::new(ieee802154_ack_buf)
206    );
207
208    // set up circular peripheral dependencies
209    nrf52840_peripherals.init();
210    let base_peripherals = &nrf52840_peripherals.nrf52;
211
212    // Create an array to hold process references.
213    let processes = components::process_array::ProcessArrayComponent::new()
214        .finalize(components::process_array_component_static!(NUM_PROCS));
215    PROCESSES = Some(processes);
216
217    // Setup space to store the core kernel data structure.
218    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
219
220    // GPIOs
221    let gpio = components::gpio::GpioComponent::new(
222        board_kernel,
223        capsules_core::gpio::DRIVER_NUM,
224        components::gpio_component_helper!(
225            nrf52840::gpio::GPIOPin,
226            // left side of the USB plug
227            0 => &nrf52840_peripherals.gpio_port[Pin::P0_13],
228            1 => &nrf52840_peripherals.gpio_port[Pin::P0_15],
229            2 => &nrf52840_peripherals.gpio_port[Pin::P0_17],
230            3 => &nrf52840_peripherals.gpio_port[Pin::P0_20],
231            4 => &nrf52840_peripherals.gpio_port[Pin::P0_22],
232            5 => &nrf52840_peripherals.gpio_port[Pin::P0_24],
233            6 => &nrf52840_peripherals.gpio_port[Pin::P1_00],
234            7 => &nrf52840_peripherals.gpio_port[Pin::P0_09],
235            8 => &nrf52840_peripherals.gpio_port[Pin::P0_10],
236            // right side of the USB plug
237            9 => &nrf52840_peripherals.gpio_port[Pin::P0_31],
238            10 => &nrf52840_peripherals.gpio_port[Pin::P0_29],
239            11 => &nrf52840_peripherals.gpio_port[Pin::P0_02],
240            12 => &nrf52840_peripherals.gpio_port[Pin::P1_15],
241            13 => &nrf52840_peripherals.gpio_port[Pin::P1_13],
242            14 => &nrf52840_peripherals.gpio_port[Pin::P1_10],
243            // Below the PCB
244            15 => &nrf52840_peripherals.gpio_port[Pin::P0_26],
245            16 => &nrf52840_peripherals.gpio_port[Pin::P0_04],
246            17 => &nrf52840_peripherals.gpio_port[Pin::P0_11],
247            18 => &nrf52840_peripherals.gpio_port[Pin::P0_14],
248            19 => &nrf52840_peripherals.gpio_port[Pin::P1_11],
249            20 => &nrf52840_peripherals.gpio_port[Pin::P1_07],
250            21 => &nrf52840_peripherals.gpio_port[Pin::P1_01],
251            22 => &nrf52840_peripherals.gpio_port[Pin::P1_04],
252            23 => &nrf52840_peripherals.gpio_port[Pin::P1_02]
253        ),
254    )
255    .finalize(components::gpio_component_static!(nrf52840::gpio::GPIOPin));
256
257    let button = components::button::ButtonComponent::new(
258        board_kernel,
259        capsules_core::button::DRIVER_NUM,
260        components::button_component_helper!(
261            nrf52840::gpio::GPIOPin,
262            (
263                &nrf52840_peripherals.gpio_port[BUTTON_PIN],
264                kernel::hil::gpio::ActivationMode::ActiveLow,
265                kernel::hil::gpio::FloatingState::PullUp
266            )
267        ),
268    )
269    .finalize(components::button_component_static!(
270        nrf52840::gpio::GPIOPin
271    ));
272
273    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
274        LedLow<'static, nrf52840::gpio::GPIOPin>,
275        LedLow::new(&nrf52840_peripherals.gpio_port[LED1_PIN]),
276        LedLow::new(&nrf52840_peripherals.gpio_port[LED2_R_PIN]),
277        LedLow::new(&nrf52840_peripherals.gpio_port[LED2_G_PIN]),
278        LedLow::new(&nrf52840_peripherals.gpio_port[LED2_B_PIN]),
279    ));
280
281    let chip = static_init!(
282        nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>,
283        nrf52840::chip::NRF52::new(nrf52840_peripherals)
284    );
285    CHIP = Some(chip);
286
287    nrf52_components::startup::NrfStartupComponent::new(
288        false,
289        BUTTON_RST_PIN,
290        nrf52840::uicr::Regulator0Output::V3_0,
291        &base_peripherals.nvmc,
292    )
293    .finalize(());
294
295    // Create capabilities that the board needs to call certain protected kernel
296    // functions.
297    let process_management_capability =
298        create_capability!(capabilities::ProcessManagementCapability);
299    let memory_allocation_capability = create_capability!(capabilities::MemoryAllocationCapability);
300
301    let gpio_port = &nrf52840_peripherals.gpio_port;
302
303    // Configure kernel debug gpios as early as possible
304    kernel::debug::assign_gpios(
305        Some(&gpio_port[LED2_R_PIN]),
306        Some(&gpio_port[LED2_G_PIN]),
307        Some(&gpio_port[LED2_B_PIN]),
308    );
309
310    let rtc = &base_peripherals.rtc;
311    let _ = rtc.start();
312    let mux_alarm = components::alarm::AlarmMuxComponent::new(rtc)
313        .finalize(components::alarm_mux_component_static!(nrf52840::rtc::Rtc));
314    let alarm = components::alarm::AlarmDriverComponent::new(
315        board_kernel,
316        capsules_core::alarm::DRIVER_NUM,
317        mux_alarm,
318    )
319    .finalize(components::alarm_component_static!(nrf52840::rtc::Rtc));
320    let uart_channel = UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD));
321    let channel = nrf52_components::UartChannelComponent::new(
322        uart_channel,
323        mux_alarm,
324        &base_peripherals.uarte0,
325    )
326    .finalize(nrf52_components::uart_channel_component_static!(
327        nrf52840::rtc::Rtc
328    ));
329
330    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
331        .finalize(components::process_printer_text_component_static!());
332    PROCESS_PRINTER = Some(process_printer);
333
334    // Create a shared UART channel for the console and for kernel debug.
335    let uart_mux = components::console::UartMuxComponent::new(channel, 115200)
336        .finalize(components::uart_mux_component_static!());
337
338    let pconsole = components::process_console::ProcessConsoleComponent::new(
339        board_kernel,
340        uart_mux,
341        mux_alarm,
342        process_printer,
343        Some(cortexm4::support::reset),
344    )
345    .finalize(components::process_console_component_static!(
346        nrf52840::rtc::Rtc<'static>
347    ));
348
349    // Setup the console.
350    let console = components::console::ConsoleComponent::new(
351        board_kernel,
352        capsules_core::console::DRIVER_NUM,
353        uart_mux,
354    )
355    .finalize(components::console_component_static!());
356    // Create the debugger object that handles calls to `debug!()`.
357    components::debug_writer::DebugWriterComponent::new(
358        uart_mux,
359        create_capability!(capabilities::SetDebugWriterCapability),
360    )
361    .finalize(components::debug_writer_component_static!());
362
363    let ble_radio = components::ble::BLEComponent::new(
364        board_kernel,
365        capsules_extra::ble_advertising_driver::DRIVER_NUM,
366        &base_peripherals.ble_radio,
367        mux_alarm,
368    )
369    .finalize(components::ble_component_static!(
370        nrf52840::rtc::Rtc,
371        nrf52840::ble_radio::Radio
372    ));
373
374    let aes_mux = static_init!(
375        MuxAES128CCM<'static, nrf52840::aes::AesECB>,
376        MuxAES128CCM::new(&base_peripherals.ecb,)
377    );
378    aes_mux.register();
379    base_peripherals.ecb.set_client(aes_mux);
380
381    let (ieee802154_radio, _mux_mac) = components::ieee802154::Ieee802154Component::new(
382        board_kernel,
383        capsules_extra::ieee802154::DRIVER_NUM,
384        &nrf52840_peripherals.ieee802154_radio,
385        aes_mux,
386        PAN_ID,
387        SRC_MAC,
388        DEFAULT_EXT_SRC_MAC,
389    )
390    .finalize(components::ieee802154_component_static!(
391        nrf52840::ieee802154_radio::Radio,
392        nrf52840::aes::AesECB<'static>
393    ));
394
395    let temp = components::temperature::TemperatureComponent::new(
396        board_kernel,
397        capsules_extra::temperature::DRIVER_NUM,
398        &base_peripherals.temp,
399    )
400    .finalize(components::temperature_component_static!(
401        nrf52840::temperature::Temp
402    ));
403
404    let rng = components::rng::RngComponent::new(
405        board_kernel,
406        capsules_core::rng::DRIVER_NUM,
407        &base_peripherals.trng,
408    )
409    .finalize(components::rng_component_static!(nrf52840::trng::Trng));
410
411    // Initialize AC using AIN5 (P0.29) as VIN+ and VIN- as AIN0 (P0.02)
412    // These are hardcoded pin assignments specified in the driver
413    let analog_comparator = components::analog_comparator::AnalogComparatorComponent::new(
414        &base_peripherals.acomp,
415        components::analog_comparator_component_helper!(
416            nrf52840::acomp::Channel,
417            &*addr_of!(nrf52840::acomp::CHANNEL_AC0)
418        ),
419        board_kernel,
420        capsules_extra::analog_comparator::DRIVER_NUM,
421    )
422    .finalize(components::analog_comparator_component_static!(
423        nrf52840::acomp::Comparator
424    ));
425
426    nrf52_components::NrfClockComponent::new(&base_peripherals.clock).finalize(());
427
428    let scheduler = components::sched::round_robin::RoundRobinComponent::new(processes)
429        .finalize(components::round_robin_component_static!(NUM_PROCS));
430
431    let platform = Platform {
432        button,
433        ble_radio,
434        ieee802154_radio,
435        pconsole,
436        console,
437        led,
438        gpio,
439        rng,
440        temp,
441        alarm,
442        analog_comparator,
443        ipc: kernel::ipc::IPC::new(
444            board_kernel,
445            kernel::ipc::DRIVER_NUM,
446            &memory_allocation_capability,
447        ),
448        scheduler,
449        systick: cortexm4::systick::SysTick::new_with_calibration(64000000),
450    };
451
452    let _ = platform.pconsole.start();
453    debug!("Initialization complete. Entering main loop\r");
454    debug!("{}", &*addr_of!(nrf52840::ficr::FICR_INSTANCE));
455
456    // These symbols are defined in the linker script.
457    extern "C" {
458        /// Beginning of the ROM region containing app images.
459        static _sapps: u8;
460        /// End of the ROM region containing app images.
461        static _eapps: u8;
462        /// Beginning of the RAM region for app memory.
463        static mut _sappmem: u8;
464        /// End of the RAM region for app memory.
465        static _eappmem: u8;
466    }
467
468    kernel::process::load_processes(
469        board_kernel,
470        chip,
471        core::slice::from_raw_parts(
472            core::ptr::addr_of!(_sapps),
473            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
474        ),
475        core::slice::from_raw_parts_mut(
476            core::ptr::addr_of_mut!(_sappmem),
477            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
478        ),
479        &FAULT_RESPONSE,
480        &process_management_capability,
481    )
482    .unwrap_or_else(|err| {
483        debug!("Error loading processes!");
484        debug!("{:?}", err);
485    });
486
487    (board_kernel, platform, chip)
488}
489
490/// Main function called after RAM initialized.
491#[no_mangle]
492pub unsafe fn main() {
493    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
494
495    let (board_kernel, platform, chip) = start();
496    board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
497}